UNPKG

16.8 kBJavaScriptView Raw
1"use strict";
2var isArray = Array.isArray;
3var ok = require("assert").ok;
4
5var Node = require("./ast/Node");
6var Program = require("./ast/Program");
7var TemplateRoot = require("./ast/TemplateRoot");
8var FunctionDeclaration = require("./ast/FunctionDeclaration");
9var FunctionCall = require("./ast/FunctionCall");
10var Literal = require("./ast/Literal");
11var Identifier = require("./ast/Identifier");
12var Comment = require("./ast/Comment");
13var If = require("./ast/If");
14var ElseIf = require("./ast/ElseIf");
15var Else = require("./ast/Else");
16var Assignment = require("./ast/Assignment");
17var BinaryExpression = require("./ast/BinaryExpression");
18var LogicalExpression = require("./ast/LogicalExpression");
19var Vars = require("./ast/Vars");
20var Return = require("./ast/Return");
21var HtmlElement = require("./ast/HtmlElement");
22var Html = require("./ast/Html");
23var Text = require("./ast/Text");
24var ForEach = require("./ast/ForEach");
25var ForEachProp = require("./ast/ForEachProp");
26var ForRange = require("./ast/ForRange");
27var HtmlComment = require("./ast/HtmlComment");
28var SelfInvokingFunction = require("./ast/SelfInvokingFunction");
29var ForStatement = require("./ast/ForStatement");
30var UpdateExpression = require("./ast/UpdateExpression");
31var UnaryExpression = require("./ast/UnaryExpression");
32var MemberExpression = require("./ast/MemberExpression");
33var Code = require("./ast/Code");
34var Macro = require("./ast/Macro");
35var ConditionalExpression = require("./ast/ConditionalExpression");
36var NewExpression = require("./ast/NewExpression");
37var ObjectExpression = require("./ast/ObjectExpression");
38var ArrayExpression = require("./ast/ArrayExpression");
39var Property = require("./ast/Property");
40var VariableDeclarator = require("./ast/VariableDeclarator");
41var ThisExpression = require("./ast/ThisExpression");
42var TemplateLiteral = require("./ast/TemplateLiteral");
43var Expression = require("./ast/Expression");
44var Scriptlet = require("./ast/Scriptlet");
45var ContainerNode = require("./ast/ContainerNode");
46var WhileStatement = require("./ast/WhileStatement");
47var DocumentType = require("./ast/DocumentType");
48var Declaration = require("./ast/Declaration");
49var SequenceExpression = require("./ast/SequenceExpression");
50var CustomTag = require("./ast/CustomTag");
51
52var parseExpression = require("./util/parseExpression");
53var parseStatement = require("./util/parseStatement");
54var parseJavaScriptArgs = require("./util/parseJavaScriptArgs");
55var parseJavaScriptParams = require("./util/parseJavaScriptParams");
56var isValidJavaScriptIdentifier = require("./util/isValidJavaScriptIdentifier");
57
58var DEFAULT_BUILDER;
59
60function makeNode(arg) {
61 if (typeof arg === "string") {
62 return parseExpression(arg, DEFAULT_BUILDER);
63 } else if (arg instanceof Node) {
64 return arg;
65 } else if (arg == null) {
66 return undefined;
67 } else if (Array.isArray(arg)) {
68 return arg.map(arg => {
69 return makeNode(arg);
70 });
71 } else {
72 throw new Error(
73 "Argument should be a string or Node or null. Actual: " + arg
74 );
75 }
76}
77
78var literalNull = new Literal({ value: null });
79var literalUndefined = new Literal({ value: undefined });
80var literalTrue = new Literal({ value: true });
81var literalFalse = new Literal({ value: false });
82var identifierOut = new Identifier({ name: "out" });
83var identifierRequire = new Identifier({ name: "require" });
84
85class Builder {
86 arrayExpression(elements) {
87 if (elements) {
88 if (!isArray(elements)) {
89 elements = [elements];
90 }
91
92 for (var i = 0; i < elements.length; i++) {
93 elements[i] = makeNode(elements[i]);
94 }
95 } else {
96 elements = [];
97 }
98
99 return new ArrayExpression({ elements });
100 }
101
102 assignment(left, right, operator) {
103 if (operator == null) {
104 operator = "=";
105 }
106 left = makeNode(left);
107 right = makeNode(right);
108 return new Assignment({ left, right, operator });
109 }
110
111 binaryExpression(left, operator, right) {
112 left = makeNode(left);
113 right = makeNode(right);
114 return new BinaryExpression({ left, operator, right });
115 }
116
117 sequenceExpression(expressions) {
118 expressions = makeNode(expressions);
119 return new SequenceExpression({ expressions });
120 }
121
122 code(value) {
123 return new Code({ value });
124 }
125
126 computedMemberExpression(object, property) {
127 object = makeNode(object);
128 property = makeNode(property);
129 let computed = true;
130
131 return new MemberExpression({ object, property, computed });
132 }
133
134 /**
135 * Generates code that joins all of the arguments using `+` (BinaryExpression)
136 *
137 * @param {Array} args If the args object is not an array then `arguments` is used
138 * @return {Node} The resulting Node
139 */
140 concat(args) {
141 var prev;
142 let operator = "+";
143 args = Array.isArray(args)
144 ? args
145 : Array.prototype.slice.call(arguments, 0);
146
147 for (var i = 1; i < args.length; i++) {
148 var left;
149 var right = makeNode(args[i]);
150 if (i === 1) {
151 left = makeNode(args[i - 1]);
152 } else {
153 left = prev;
154 }
155
156 prev = new BinaryExpression({ left, operator, right });
157 }
158
159 return prev;
160 }
161
162 conditionalExpression(test, consequent, alternate) {
163 return new ConditionalExpression({ test, consequent, alternate });
164 }
165
166 containerNode(type, codeGenerator) {
167 if (typeof type === "function") {
168 codeGenerator = arguments[0];
169 type = "ContainerNode";
170 }
171
172 var node = new ContainerNode(type);
173 if (codeGenerator) {
174 node.setCodeGenerator(codeGenerator);
175 }
176 return node;
177 }
178
179 customTag(el, tagDef) {
180 return new CustomTag(el, tagDef);
181 }
182
183 declaration(declaration) {
184 return new Declaration({ declaration });
185 }
186
187 documentType(documentType) {
188 return new DocumentType({ documentType });
189 }
190
191 elseStatement(body) {
192 return new Else({ body });
193 }
194
195 elseIfStatement(test, body, elseStatement) {
196 test = makeNode(test);
197
198 return new ElseIf({ test, body, else: elseStatement });
199 }
200
201 expression(value, ast) {
202 return new Expression({ value, ast });
203 }
204
205 forEach(params, ofExpression, body) {
206 return new ForEach(
207 arguments.length === 1
208 ? params
209 : {
210 params: makeNode(params),
211 of: makeNode(ofExpression),
212 body
213 }
214 );
215 }
216
217 forEachProp(params, inExpression, body) {
218 return new ForEachProp(
219 arguments.length === 1
220 ? params
221 : {
222 params: makeNode(params),
223 in: makeNode(inExpression),
224 body
225 }
226 );
227 }
228
229 forRange(params, from, to, step, body) {
230 return new ForRange(
231 arguments.length === 1
232 ? params
233 : {
234 params: makeNode(params),
235 from: makeNode(from),
236 to: makeNode(to),
237 step: makeNode(step),
238 body
239 }
240 );
241 }
242
243 forStatement(init, test, update, body) {
244 if (arguments.length === 1) {
245 var def = arguments[0];
246 return new ForStatement(def);
247 } else {
248 init = makeNode(init);
249 test = makeNode(test);
250 update = makeNode(update);
251 return new ForStatement({ init, test, update, body });
252 }
253 }
254
255 functionCall(callee, args) {
256 callee = makeNode(callee);
257
258 if (args) {
259 if (!isArray(args)) {
260 throw new Error('"args" should be an array');
261 }
262
263 for (var i = 0; i < args.length; i++) {
264 args[i] = makeNode(args[i]);
265 }
266 } else {
267 args = [];
268 }
269
270 return new FunctionCall({ callee, args });
271 }
272
273 functionDeclaration(name, params, body) {
274 return new FunctionDeclaration({ name, params, body });
275 }
276
277 html(argument) {
278 argument = makeNode(argument);
279
280 return new Html({ argument });
281 }
282
283 htmlComment(comment) {
284 return new HtmlComment({ comment });
285 }
286
287 comment(comment) {
288 return new Comment({ comment });
289 }
290
291 htmlElement(tagName, attributes, body, argument, openTagOnly, selfClosed) {
292 if (typeof tagName === "object" && !(tagName instanceof Node)) {
293 let def = arguments[0];
294 return new HtmlElement(def);
295 } else {
296 return new HtmlElement({
297 tagName,
298 attributes,
299 body,
300 argument,
301 openTagOnly,
302 selfClosed
303 });
304 }
305 }
306
307 htmlLiteral(htmlCode) {
308 var argument = new Literal({ value: htmlCode });
309 return new Html({ argument });
310 }
311
312 identifier(name) {
313 ok(typeof name === "string", '"name" should be a string');
314
315 if (!isValidJavaScriptIdentifier(name)) {
316 var error = new Error("Invalid JavaScript identifier: " + name);
317 error.code = "INVALID_IDENTIFIER";
318 throw error;
319 }
320 return new Identifier({ name });
321 }
322
323 identifierOut() {
324 return identifierOut;
325 }
326
327 ifStatement(test, body, elseStatement) {
328 test = makeNode(test);
329
330 return new If({ test, body, else: elseStatement });
331 }
332
333 literal(value) {
334 return new Literal({ value });
335 }
336
337 literalFalse() {
338 return literalFalse;
339 }
340
341 literalNull() {
342 return literalNull;
343 }
344
345 literalTrue() {
346 return literalTrue;
347 }
348
349 literalUndefined() {
350 return literalUndefined;
351 }
352
353 logicalExpression(left, operator, right) {
354 left = makeNode(left);
355 right = makeNode(right);
356 return new LogicalExpression({ left, operator, right });
357 }
358
359 macro(name, params, body) {
360 return new Macro({ name, params, body });
361 }
362
363 memberExpression(object, property, computed) {
364 object = makeNode(object);
365 property = makeNode(property);
366
367 return new MemberExpression({ object, property, computed });
368 }
369
370 moduleExports(value) {
371 let object = new Identifier({ name: "module" });
372 let property = new Identifier({ name: "exports" });
373
374 var moduleExports = new MemberExpression({ object, property });
375
376 if (value) {
377 return new Assignment({
378 left: moduleExports,
379 right: value,
380 operator: "="
381 });
382 } else {
383 return moduleExports;
384 }
385 }
386
387 negate(argument) {
388 argument = makeNode(argument);
389
390 var operator = "!";
391 var prefix = true;
392 return new UnaryExpression({ argument, operator, prefix });
393 }
394
395 newExpression(callee, args) {
396 callee = makeNode(callee);
397
398 if (args) {
399 if (!isArray(args)) {
400 args = [args];
401 }
402
403 for (var i = 0; i < args.length; i++) {
404 args[i] = makeNode(args[i]);
405 }
406 } else {
407 args = [];
408 }
409
410 return new NewExpression({ callee, args });
411 }
412
413 node(type, generateCode) {
414 if (typeof type === "function") {
415 generateCode = arguments[0];
416 type = "Node";
417 }
418
419 var node = new Node(type);
420 if (generateCode) {
421 node.setCodeGenerator(generateCode);
422 }
423 return node;
424 }
425
426 objectExpression(properties) {
427 if (properties) {
428 if (isArray(properties)) {
429 for (var i = 0; i < properties.length; i++) {
430 let prop = properties[i];
431 prop.value = makeNode(prop.value);
432 }
433 } else {
434 let propertiesObject = properties;
435 properties = Object.keys(propertiesObject).map(key => {
436 let value = propertiesObject[key];
437 if (!(value instanceof Node)) {
438 value = value = new Literal({ value });
439 }
440
441 key = new Literal({ value: key });
442
443 let property = new Property({ key, value });
444 return property;
445 });
446 }
447 } else {
448 properties = [];
449 }
450
451 return new ObjectExpression({ properties });
452 }
453
454 parseExpression(str) {
455 ok(typeof str === "string", '"str" should be a string expression');
456 var parsed = parseExpression(str, DEFAULT_BUILDER);
457 return parsed;
458 }
459
460 parseJavaScriptArgs(args) {
461 ok(typeof args === "string", '"args" should be a string');
462 return parseJavaScriptArgs(args, DEFAULT_BUILDER);
463 }
464
465 parseJavaScriptParams(params) {
466 ok(typeof params === "string", '"params" should be a string');
467 return parseJavaScriptParams(params, DEFAULT_BUILDER);
468 }
469
470 parseStatement(str) {
471 ok(typeof str === "string", '"str" should be a string expression');
472 var parsed = parseStatement(str, DEFAULT_BUILDER);
473 return parsed;
474 }
475
476 replacePlaceholderEscapeFuncs(node) {
477 return node;
478 }
479
480 program(body) {
481 return new Program({ body });
482 }
483
484 property(key, value, computed) {
485 key = makeNode(key);
486 value = makeNode(value);
487 computed = computed === true;
488
489 return new Property({ key, value, computed });
490 }
491
492 renderBodyFunction(body, params) {
493 if (!params) {
494 params = [new Identifier({ name: "out" })];
495 }
496 return new FunctionDeclaration({ name: null, params, body });
497 }
498
499 require(path) {
500 path = makeNode(path);
501
502 let callee = identifierRequire;
503 let args = [path];
504 return new FunctionCall({ callee, args });
505 }
506
507 requireResolve(path) {
508 path = makeNode(path);
509
510 let callee = new MemberExpression({
511 object: new Identifier({ name: "require" }),
512 property: new Identifier({ name: "resolve" })
513 });
514
515 let args = [path];
516 return new FunctionCall({ callee, args });
517 }
518
519 returnStatement(argument) {
520 argument = makeNode(argument);
521
522 return new Return({ argument });
523 }
524
525 scriptlet(scriptlet) {
526 return new Scriptlet({
527 code: scriptlet.value,
528 tag: scriptlet.tag,
529 block: scriptlet.block
530 });
531 }
532
533 selfInvokingFunction(params, args, body) {
534 if (arguments.length === 1) {
535 body = arguments[0];
536 params = null;
537 args = null;
538 }
539
540 return new SelfInvokingFunction({ params, args, body });
541 }
542
543 strictEquality(left, right) {
544 left = makeNode(left);
545 right = makeNode(right);
546
547 var operator = "===";
548 return new BinaryExpression({ left, right, operator });
549 }
550
551 templateLiteral(quasis, expressions) {
552 return new TemplateLiteral({ quasis, expressions });
553 }
554
555 templateRoot(body) {
556 return new TemplateRoot({ body });
557 }
558
559 text(argument, escape, preserveWhitespace) {
560 if (typeof argument === "object" && !(argument instanceof Node)) {
561 var def = arguments[0];
562 return new Text(def);
563 }
564 argument = makeNode(argument);
565
566 return new Text({ argument, escape, preserveWhitespace });
567 }
568
569 thisExpression() {
570 return new ThisExpression();
571 }
572
573 unaryExpression(argument, operator, prefix) {
574 argument = makeNode(argument);
575
576 return new UnaryExpression({ argument, operator, prefix });
577 }
578
579 updateExpression(argument, operator, prefix) {
580 argument = makeNode(argument);
581 return new UpdateExpression({ argument, operator, prefix });
582 }
583
584 variableDeclarator(id, init) {
585 if (typeof id === "string") {
586 id = new Identifier({ name: id });
587 }
588 if (init) {
589 init = makeNode(init);
590 }
591
592 return new VariableDeclarator({ id, init });
593 }
594
595 var(id, init, kind) {
596 if (!kind) {
597 kind = "var";
598 }
599
600 id = makeNode(id);
601 init = makeNode(init);
602
603 var declarations = [new VariableDeclarator({ id, init })];
604
605 return new Vars({ declarations, kind });
606 }
607
608 vars(declarations, kind) {
609 if (declarations) {
610 if (Array.isArray(declarations)) {
611 for (let i = 0; i < declarations.length; i++) {
612 var declaration = declarations[i];
613 if (!declaration) {
614 throw new Error("Invalid variable declaration");
615 }
616 if (typeof declaration === "string") {
617 declarations[i] = new VariableDeclarator({
618 id: new Identifier({ name: declaration })
619 });
620 } else if (declaration instanceof Identifier) {
621 declarations[i] = new VariableDeclarator({
622 id: declaration
623 });
624 } else if (typeof declaration === "object") {
625 if (!(declaration instanceof VariableDeclarator)) {
626 let id = declaration.id;
627 let init = declaration.init;
628
629 if (typeof id === "string") {
630 id = new Identifier({ name: id });
631 }
632
633 if (!id) {
634 throw new Error("Invalid variable declaration");
635 }
636
637 if (init) {
638 init = makeNode(init);
639 }
640
641 declarations[i] = new VariableDeclarator({
642 id,
643 init
644 });
645 }
646 }
647 }
648 } else if (typeof declarations === "object") {
649 // Convert the object into an array of variables
650 declarations = Object.keys(declarations).map(key => {
651 let id = new Identifier({ name: key });
652 let init = makeNode(declarations[key]);
653 return new VariableDeclarator({ id, init });
654 });
655 }
656 }
657
658 return new Vars({ declarations, kind });
659 }
660
661 whileStatement(test, body) {
662 return new WhileStatement({ test, body });
663 }
664}
665
666DEFAULT_BUILDER = new Builder();
667
668Builder.DEFAULT_BUILDER = DEFAULT_BUILDER;
669
670module.exports = Builder;