UNPKG

8.01 kBJavaScriptView Raw
1
2var debug = require('debug');
3var lg = debug('vash:codegen');
4
5var gens = {}
6
7gens.VashProgram = function(node, opts, generate) {
8 return node.body.map(generate).join('');
9}
10
11gens.VashExplicitExpression = function(node, opts, generate) {
12 var str = node.values.map(generate).join('');
13 str = '(' + maybeHTMLEscape(node, opts, str) + ')';
14 if (parentIsContent(node)) {
15 str = bewrap(str);
16 }
17 return str;
18}
19
20gens.VashExpression = function(node, opts, generate) {
21 var str = node.values.map(generate).join('');
22 str = bewrap(maybeHTMLEscape(node, opts, str));
23 return str;
24}
25
26gens.VashRegex = function(node, opts, generate) {
27 var str = node.values.map(generate).join('');
28 str = maybeHTMLEscape(node, opts, str);
29 if (parentIsContent(node)) {
30 str = bewrap(str);
31 }
32 return str;
33}
34
35gens.VashMarkup = function(node, opts, generate) {
36 var isText = node.name === 'text';
37 var name = node.name ? bcwrap(node.name) : '';
38 var tagNameValue = name
39 + (node.expression ? generate(node.expression) : '');
40
41 var tagOpen = ''
42 + bcwrap('<')
43 + tagNameValue
44 + bcwrap(node.attributes.length ? ' ' : '')
45 + node.attributes.map(generate).join(bcwrap(' '))
46
47 var values;
48 var tagClose;
49
50 if (node.isVoid) {
51 tagOpen += bcwrap(node.voidClosed ? ' />' : '>');
52 values = '';
53 tagClose = '';
54 } else {
55 tagOpen += bcwrap('>');
56 values = node.values.map(generate).join('');
57 tagClose = node.isClosed ? bcwrap('</') + tagNameValue + bcwrap('>') : '';
58 }
59
60 if (isText) {
61 tagOpen = tagClose = '';
62 }
63
64 return ''
65 + (parentIsExpression(node) ? '(function () {' : '')
66 + dbgstart(node, opts)
67 + tagOpen
68 + values
69 + tagClose
70 + dbgend(node, opts)
71 + (parentIsExpression(node) ? '}())' : '')
72}
73
74gens.VashMarkupAttribute = function(node, opts, generate) {
75 var quote = node.rightIsQuoted || '';
76 quote = escapeMarkupContent(quote);
77 return ''
78 + dbgstart(node, opts)
79 + node.left.map(generate).join('')
80 + (node.right.length || node.rightIsQuoted
81 ? bcwrap('=' + quote)
82 + node.right.map(generate).join('')
83 + bcwrap(quote)
84 : '')
85 + dbgend(node, opts);
86}
87
88gens.VashMarkupContent = function(node, opts, generate) {
89 return ''
90 + dbgstart(node, opts)
91 + node.values.map(generate).join('')
92 + dbgend(node, opts);
93}
94
95gens.VashMarkupComment = function(node, opts, generate) {
96 return ''
97 + bcwrap('<!--')
98 + dbgstart(node, opts)
99 + node.values.map(generate).join('')
100 + dbgend(node, opts)
101 + bcwrap('-->');
102}
103
104gens.VashBlock = function(node, opts, generate) {
105 var hasValues = node.values.length > 0;
106 var unsafeForDbg = node.keyword === 'switch'
107 || !node.name
108 || !hasValues;
109 var openBrace = hasValues || node.hasBraces
110 ? '{' + (unsafeForDbg ? '' : dbgstart(node, opts))
111 : '';
112 var closeBrace = hasValues || node.hasBraces
113 ? (unsafeForDbg ? '' : dbgend(node, opts)) + '}'
114 : '';
115 return ''
116 + (node.keyword ? node.keyword : '')
117 + node.head.map(generate).join('')
118 + openBrace
119 + node.values.map(generate).join('')
120 + closeBrace
121 + node.tail.map(generate).join('');
122}
123
124gens.VashIndexExpression = function(node, opts, generate) {
125 var str = node.values.map(generate).join('');
126 return '[' + str + ']';
127}
128
129gens.VashText = function(node, opts, generate) {
130 if (!node.value.length) return '';
131 return parentIsContent(node)
132 ? ''
133 + dbgstart(node, opts)
134 + bcwrap(escapeMarkupContent(node.value))
135 + dbgend(node, opts)
136 : node.value;
137}
138
139gens.VashComment = function(node, opts, generate) {
140 return '';
141}
142
143var reQuote = /(['"])/g;
144var reEscapedQuote = /\\+(["'])/g;
145var reLineBreak = /\n/g;
146var reHelpersName = /HELPERSNAME/g;
147var reModelName = /MODELNAME/g;
148var reOriginalMarkup = /ORIGINALMARKUP/g;
149
150function escapeMarkupContent(str) {
151 return str
152 .replace(/\\/g, '\\\\')
153 .replace(reQuote, '\\$1')
154 .replace(reLineBreak, '\\n');
155}
156
157var BUFFER_HEAD = '\n__vbuffer.push(';
158var BUFFER_TAIL = ');\n';
159
160// buffer content wrap
161function bcwrap(str) {
162 return BUFFER_HEAD + '\'' + str.replace(/\n/, '\\n') + '\'' + BUFFER_TAIL;
163}
164
165// buffer expression wrap
166function bewrap(str) {
167 return BUFFER_HEAD + str + BUFFER_TAIL;
168}
169
170function parentIsContent(node) {
171 return node.parent.type === 'VashMarkup'
172 || node.parent.type === 'VashMarkupContent'
173 || node.parent.type === 'VashMarkupComment'
174 || node.parent.type === 'VashMarkupAttribute'
175 || node.parent.type === 'VashProgram';
176}
177
178function parentIsExpression(node) {
179 return node.parent.type === 'VashExpression'
180 || node.parent.type === 'VashExplicitExpression'
181 || node.parent.type === 'VashIndexExpression';
182}
183
184function dbgstart(node, opts) {
185 return opts.debug
186 ? ''
187 + opts.helpersName + '.vl = ' + node.startloc.line + ', '
188 + opts.helpersName + '.vc = ' + node.startloc.column + '; \n'
189 : '';
190}
191
192function dbgend(node, opts) {
193 return opts.debug
194 ? ''
195 + opts.helpersName + '.vl = ' + node.endloc.line + ', '
196 + opts.helpersName + '.vc = ' + node.endloc.column + '; \n'
197 : '';
198}
199
200function maybeHTMLEscape(node, opts, str) {
201 if (parentIsContent(node) && opts.htmlEscape) {
202 return opts.helpersName + '.escape(' + str + ').toHtmlString()';
203 } else {
204 return str;
205 }
206}
207
208function replaceDevTokens(str, opts){
209 return str
210 .replace( reHelpersName, opts.helpersName )
211 .replace( reModelName, opts.modelName );
212}
213
214function head(opts){
215 var str = ''
216 + (opts.debug ? 'try { \n' : '')
217 + 'var __vbuffer = HELPERSNAME.buffer; \n'
218 + 'HELPERSNAME.options = __vopts; \n'
219 + 'MODELNAME = MODELNAME || {}; \n'
220 + (opts.useWith ? 'with( MODELNAME ){ \n' : '');
221
222 str = replaceDevTokens(str, opts);
223 return str;
224}
225
226function helperHead(opts){
227 var str = ''
228 + (opts.debug ? 'try { \n' : '')
229 + 'var __vbuffer = this.buffer; \n'
230 + 'var MODELNAME = this.model; \n'
231 + 'var HELPERSNAME = this; \n';
232
233 str = replaceDevTokens(str, opts);
234 return str;
235}
236
237function tail(opts){
238 var str = ''
239 + (opts.simple
240 ? 'return HELPERSNAME.buffer.join(""); \n'
241 : ';(__vopts && __vopts.onRenderEnd && __vopts.onRenderEnd(null, HELPERSNAME)); \n'
242 + 'return (__vopts && __vopts.asContext) \n'
243 + ' ? HELPERSNAME \n'
244 + ' : HELPERSNAME.toString(); \n' )
245 + (opts.useWith ? '} \n' : '')
246 + (opts.debug ? '} catch( e ){ \n'
247 + ' HELPERSNAME.reportError( e, HELPERSNAME.vl, HELPERSNAME.vc, "ORIGINALMARKUP", "!LB!", true ); \n'
248 + '} \n' : '');
249
250 str = replaceDevTokens(str, opts)
251 .replace(reOriginalMarkup, escapeForDebug(opts.source));
252
253 return str;
254}
255
256 function helperTail(opts){
257 var str = ''
258 + (opts.debug ? '} catch( e ){ \n'
259 + ' HELPERSNAME.reportError( e, HELPERSNAME.vl, HELPERSNAME.vc, "ORIGINALMARKUP", "!LB!", true ); \n'
260 + '} \n' : '');
261
262 str = replaceDevTokens(str, opts)
263 .replace(reOriginalMarkup, escapeForDebug(opts.source));
264
265 return str;
266}
267
268function escapeForDebug( str ){
269 return str
270 .replace(reLineBreak, '!LB!')
271 .replace(reQuote, '\\$1')
272 .replace(reEscapedQuote, '\\$1')
273}
274
275// Not necessary, but provides faster execution when not in debug mode
276// and looks nicer.
277function condenseContent(str) {
278 return str
279 .replace(/'\);\n+__vbuffer.push\('/g, '')
280 .replace(/\n+/g, '\n');
281}
282
283function generate(node, opts) {
284
285 function gen(opts, node) {
286 lg('Entering ' + node.type);
287 var str = gens[node.type](node, opts, genChild);
288 lg('Leaving ' + node.type);
289 return str;
290
291 function genChild(child) {
292 if (!child.parent) child.parent = node;
293 lg('Generating child type %s of parent type %s', child.type, node.type)
294 return gen(opts, child);
295 }
296 }
297
298 var generated = gen(opts, node);
299
300 var body;
301 if(!opts.asHelper){
302 body = head(opts) + generated + tail(opts);
303 } else {
304 body = helperHead(opts) + generated + helperTail(opts);
305 }
306
307 return opts.debug
308 ? body
309 : condenseContent(body);
310}
311
312module.exports = generate;