mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Fixes #197 - Better attribute code generation
Use attr helper and handle attribute escaping Also improved AST and added walking capability
This commit is contained in:
parent
c51362e793
commit
8c96302550
@ -13,6 +13,7 @@ var ElseIf = require('./ast/ElseIf');
|
|||||||
var Else = require('./ast/Else');
|
var Else = require('./ast/Else');
|
||||||
var Assignment = require('./ast/Assignment');
|
var Assignment = require('./ast/Assignment');
|
||||||
var BinaryExpression = require('./ast/BinaryExpression');
|
var BinaryExpression = require('./ast/BinaryExpression');
|
||||||
|
var LogicalExpression = require('./ast/LogicalExpression');
|
||||||
var Vars = require('./ast/Vars');
|
var Vars = require('./ast/Vars');
|
||||||
var Return = require('./ast/Return');
|
var Return = require('./ast/Return');
|
||||||
var HtmlElement = require('./ast/HtmlElement');
|
var HtmlElement = require('./ast/HtmlElement');
|
||||||
@ -32,6 +33,13 @@ var MemberExpression = require('./ast/MemberExpression');
|
|||||||
var Code = require('./ast/Code');
|
var Code = require('./ast/Code');
|
||||||
var InvokeMacro = require('./ast/InvokeMacro');
|
var InvokeMacro = require('./ast/InvokeMacro');
|
||||||
var Macro = require('./ast/Macro');
|
var Macro = require('./ast/Macro');
|
||||||
|
var ConditionalExpression = require('./ast/ConditionalExpression');
|
||||||
|
var NewExpression = require('./ast/NewExpression');
|
||||||
|
var ObjectExpression = require('./ast/ObjectExpression');
|
||||||
|
var ArrayExpression = require('./ast/ArrayExpression');
|
||||||
|
var Property = require('./ast/Property');
|
||||||
|
var VariableDeclarator = require('./ast/VariableDeclarator');
|
||||||
|
var ThisExpression = require('./ast/ThisExpression');
|
||||||
|
|
||||||
var parseExpression = require('./util/parseExpression');
|
var parseExpression = require('./util/parseExpression');
|
||||||
|
|
||||||
@ -46,6 +54,22 @@ function makeNode(arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
|
arrayExpression(elements) {
|
||||||
|
if (elements) {
|
||||||
|
if (!isArray(elements)) {
|
||||||
|
elements = [elements];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i=0; i<elements.length; i++) {
|
||||||
|
elements[i] = makeNode(elements[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
elements = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayExpression({elements});
|
||||||
|
}
|
||||||
|
|
||||||
assignment(left, right, operator) {
|
assignment(left, right, operator) {
|
||||||
if (operator == null) {
|
if (operator == null) {
|
||||||
operator = '=';
|
operator = '=';
|
||||||
@ -65,6 +89,10 @@ class Builder {
|
|||||||
return new Code({value});
|
return new Code({value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conditionalExpression(test, consequent, alternate) {
|
||||||
|
return new ConditionalExpression({test, consequent, alternate});
|
||||||
|
}
|
||||||
|
|
||||||
elseStatement(body) {
|
elseStatement(body) {
|
||||||
return new Else({body});
|
return new Else({body});
|
||||||
}
|
}
|
||||||
@ -190,6 +218,12 @@ class Builder {
|
|||||||
return new Literal({value});
|
return new Literal({value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logicalExpression(left, operator, right) {
|
||||||
|
left = makeNode(left);
|
||||||
|
right = makeNode(right);
|
||||||
|
return new LogicalExpression({left, operator, right});
|
||||||
|
}
|
||||||
|
|
||||||
macro(name, params, body) {
|
macro(name, params, body) {
|
||||||
return new Macro({name, params, body});
|
return new Macro({name, params, body});
|
||||||
}
|
}
|
||||||
@ -209,6 +243,24 @@ class Builder {
|
|||||||
return new UnaryExpression({argument, operator, prefix});
|
return new UnaryExpression({argument, operator, prefix});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newExpression(callee, args) {
|
||||||
|
callee = makeNode(callee);
|
||||||
|
|
||||||
|
if (args) {
|
||||||
|
if (!isArray(args)) {
|
||||||
|
args = [args];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i=0; i<args.length; i++) {
|
||||||
|
args[i] = makeNode(args[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NewExpression({callee, args});
|
||||||
|
}
|
||||||
|
|
||||||
node(type, generateCode) {
|
node(type, generateCode) {
|
||||||
if (typeof type === 'function') {
|
if (typeof type === 'function') {
|
||||||
generateCode = arguments[0];
|
generateCode = arguments[0];
|
||||||
@ -222,10 +274,34 @@ class Builder {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objectExpression(properties) {
|
||||||
|
if (properties) {
|
||||||
|
if (!isArray(properties)) {
|
||||||
|
properties = [properties];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i=0; i<properties.length; i++) {
|
||||||
|
let prop = properties[i];
|
||||||
|
prop.value = makeNode(prop.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
properties = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectExpression({properties});
|
||||||
|
}
|
||||||
|
|
||||||
program(body) {
|
program(body) {
|
||||||
return new Program({body});
|
return new Program({body});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property(key, value) {
|
||||||
|
key = makeNode(key);
|
||||||
|
value = makeNode(value);
|
||||||
|
|
||||||
|
return new Property({key, value});
|
||||||
|
}
|
||||||
|
|
||||||
renderBodyFunction(body) {
|
renderBodyFunction(body) {
|
||||||
let name = 'renderBody';
|
let name = 'renderBody';
|
||||||
let params = [new Identifier({name: 'out'})];
|
let params = [new Identifier({name: 'out'})];
|
||||||
@ -278,6 +354,10 @@ class Builder {
|
|||||||
return new Text({argument, escape});
|
return new Text({argument, escape});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thisExpression() {
|
||||||
|
return new ThisExpression();
|
||||||
|
}
|
||||||
|
|
||||||
unaryExpression(argument, operator, prefix) {
|
unaryExpression(argument, operator, prefix) {
|
||||||
argument = makeNode(argument);
|
argument = makeNode(argument);
|
||||||
|
|
||||||
@ -289,19 +369,53 @@ class Builder {
|
|||||||
return new UpdateExpression({argument, operator, prefix});
|
return new UpdateExpression({argument, operator, prefix});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variableDeclarator(id, init) {
|
||||||
|
if (typeof id === 'string') {
|
||||||
|
id = new Identifier({name: id});
|
||||||
|
}
|
||||||
|
if (init) {
|
||||||
|
init = makeNode(init);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VariableDeclarator({id, init});
|
||||||
|
}
|
||||||
|
|
||||||
vars(declarations, kind) {
|
vars(declarations, kind) {
|
||||||
if (declarations) {
|
if (declarations) {
|
||||||
if (Array.isArray(declarations)) {
|
if (Array.isArray(declarations)) {
|
||||||
for (let i=0; i<declarations.length; i++) {
|
for (let i=0; i<declarations.length; i++) {
|
||||||
var declaration = declarations[i];
|
var declaration = declarations[i];
|
||||||
|
if (!declaration) {
|
||||||
|
throw new Error('Invalid variable declaration');
|
||||||
|
}
|
||||||
if (typeof declaration === 'string') {
|
if (typeof declaration === 'string') {
|
||||||
declarations[i] = {
|
declarations[i] = new VariableDeclarator({
|
||||||
id: makeNode(declaration)
|
id: new Identifier({name: declaration})
|
||||||
};
|
});
|
||||||
} else if (declaration instanceof Identifier) {
|
} else if (declaration instanceof Identifier) {
|
||||||
declarations[i] = {
|
declarations[i] = new VariableDeclarator({
|
||||||
id: declaration
|
id: declaration
|
||||||
};
|
});
|
||||||
|
} else if (typeof declaration === 'object') {
|
||||||
|
if (!(declaration instanceof VariableDeclarator)) {
|
||||||
|
let id = declaration.id;
|
||||||
|
let init = declaration.init;
|
||||||
|
|
||||||
|
if (typeof id === 'string') {
|
||||||
|
id = new Identifier({name: id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
throw new Error('Invalid variable declaration');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init) {
|
||||||
|
init = makeNode(init);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
declarations[i] = new VariableDeclarator({id, init});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (typeof declarations === 'object') {
|
} else if (typeof declarations === 'object') {
|
||||||
@ -309,7 +423,7 @@ class Builder {
|
|||||||
declarations = Object.keys(declarations).map((key) => {
|
declarations = Object.keys(declarations).map((key) => {
|
||||||
let id = new Identifier({name: key});
|
let id = new Identifier({name: key});
|
||||||
let init = makeNode(declarations[key]);
|
let init = makeNode(declarations[key]);
|
||||||
return { id, init };
|
return new VariableDeclarator({ id, init });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -342,6 +342,7 @@ class Generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addWrite(output) {
|
addWrite(output) {
|
||||||
|
ok(output, '"output" is required');
|
||||||
if (output instanceof Literal) {
|
if (output instanceof Literal) {
|
||||||
let lastWrite = this._bufferedWrites ?
|
let lastWrite = this._bufferedWrites ?
|
||||||
this._bufferedWrites[this._bufferedWrites.length-1] :
|
this._bufferedWrites[this._bufferedWrites.length-1] :
|
||||||
|
|||||||
@ -19,6 +19,11 @@ class HtmlJsParser {
|
|||||||
|
|
||||||
onattributeplaceholder(event) {
|
onattributeplaceholder(event) {
|
||||||
// placeholder within attribute
|
// placeholder within attribute
|
||||||
|
if (event.escape) {
|
||||||
|
event.expression = 'escapeXml(' + event.expression + ')';
|
||||||
|
} else {
|
||||||
|
event.expression = 'noEscapeXml(' + event.expression + ')';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
oncdata(event) {
|
oncdata(event) {
|
||||||
|
|||||||
@ -82,7 +82,7 @@ class Parser {
|
|||||||
|
|
||||||
var tagName = el.tagName;
|
var tagName = el.tagName;
|
||||||
var attributes = el.attributes;
|
var attributes = el.attributes;
|
||||||
var argument = el.argument; // e.g. For <for(color in colors)>, args will be "color in colors"
|
var argument = el.argument; // e.g. For <for(color in colors)>, argument will be "color in colors"
|
||||||
|
|
||||||
if (tagName === 'compiler-options') {
|
if (tagName === 'compiler-options') {
|
||||||
var compilerOptions = this.compilerOptions;
|
var compilerOptions = this.compilerOptions;
|
||||||
|
|||||||
@ -2,41 +2,121 @@
|
|||||||
var isArray = Array.isArray;
|
var isArray = Array.isArray;
|
||||||
var Container = require('./ast/Container');
|
var Container = require('./ast/Container');
|
||||||
|
|
||||||
|
function noop() {}
|
||||||
|
|
||||||
class Walker {
|
class Walker {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this._visit = options.visit;
|
this._enter = options.enter || noop;
|
||||||
this.transform = options.transform !== false;
|
this._exit = options.exit || noop;
|
||||||
|
this._stopped = false;
|
||||||
|
this._reset();
|
||||||
|
this._stack = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_reset() {
|
||||||
|
this._skipped = false;
|
||||||
|
this._replaced = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
this._skipped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this._stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
replace(newNode) {
|
||||||
|
this._replaced = newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
_walkArray(array) {
|
||||||
|
var hasRemoval = false;
|
||||||
|
|
||||||
|
array.forEach((node, i) => {
|
||||||
|
var transformed = this.walk(node);
|
||||||
|
if (transformed == null) {
|
||||||
|
array[i] = null;
|
||||||
|
hasRemoval = true;
|
||||||
|
} else if (transformed !== node) {
|
||||||
|
array[i] = transformed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasRemoval) {
|
||||||
|
for (let i=array.length-1; i>=0; i--) {
|
||||||
|
if (array[i] == null) {
|
||||||
|
array.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
_walkContainer(nodes) {
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
var transformed = this.walk(node);
|
||||||
|
if (transformed == null) {
|
||||||
|
node.container.removeChild(node);
|
||||||
|
} else if (transformed !== node) {
|
||||||
|
node.container.replaceChild(transformed, node);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
walk(node) {
|
walk(node) {
|
||||||
if (node == null) {
|
if (!node || this._stopped || typeof node === 'string') {
|
||||||
return;
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._reset();
|
||||||
|
|
||||||
|
var parent = this._stack.length ? this._stack[this._stack.length - 1] : undefined;
|
||||||
|
|
||||||
|
this._stack.push(node);
|
||||||
|
|
||||||
|
var replaced = this._enter(node, parent) || this._replaced;
|
||||||
|
if (replaced) {
|
||||||
|
this._stack.pop();
|
||||||
|
return replaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._skipped || this._stopped) {
|
||||||
|
this._stack.pop();
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isArray(node)) {
|
if (isArray(node)) {
|
||||||
let nodes = node;
|
let array = node;
|
||||||
let len = nodes.length;
|
let newArray = this._walkArray(array);
|
||||||
|
this._stack.pop();
|
||||||
for (var i=0; i<len; i++) {
|
return newArray;
|
||||||
this.walk(nodes[i]);
|
|
||||||
}
|
|
||||||
} else if (node instanceof Container) {
|
} else if (node instanceof Container) {
|
||||||
let container = node;
|
let container = node;
|
||||||
if (this.transform) {
|
this._walkContainer(container);
|
||||||
container.safeForEach(this.walk, this);
|
this._stack.pop();
|
||||||
} else {
|
return container;
|
||||||
container.forEach(this.walk, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this._visit(node);
|
if (node.walk) {
|
||||||
|
node.walk(this);
|
||||||
if (node.walkChildren) {
|
|
||||||
node.walkChildren(this);
|
|
||||||
} else if (node.forEachChild) {
|
|
||||||
node.forEachChild(this.walk, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._stopped) {
|
||||||
|
this._stack.pop();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._reset();
|
||||||
|
replaced = this._exit(node, parent) || this._replaced;
|
||||||
|
if (replaced) {
|
||||||
|
this._stack.pop();
|
||||||
|
return replaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stack.pop();
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,28 +7,9 @@ var Container = require('./Container');
|
|||||||
class ArrayContainer extends Container {
|
class ArrayContainer extends Container {
|
||||||
constructor(node, array) {
|
constructor(node, array) {
|
||||||
super(node);
|
super(node);
|
||||||
if (array) {
|
this.items = array;
|
||||||
ok(isArray(array), 'Invalid array');
|
|
||||||
|
|
||||||
for (let i=0; i<array.length; i++) {
|
|
||||||
array[i].container = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.array = array || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// forEach(callback, thisObj) {
|
|
||||||
// var array = this.array;
|
|
||||||
//
|
|
||||||
// for (var i=0; i<array.length; i++) {
|
|
||||||
// var item = array[i];
|
|
||||||
// if (item == null) {
|
|
||||||
// throw new Error('Invalid node in container at index ' + i + '. Array: ' + JSON.stringify(array, null, 2));
|
|
||||||
// }
|
|
||||||
// callback.call(thisObj, item, i);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
forEach(callback, thisObj) {
|
forEach(callback, thisObj) {
|
||||||
var array = this.array.concat([]);
|
var array = this.array.concat([]);
|
||||||
for (var i=0; i<array.length; i++) {
|
for (var i=0; i<array.length; i++) {
|
||||||
@ -104,6 +85,17 @@ class ArrayContainer extends Container {
|
|||||||
get items() {
|
get items() {
|
||||||
return this.array;
|
return this.array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set items(newItems) {
|
||||||
|
if (newItems) {
|
||||||
|
ok(isArray(newItems), 'Invalid array');
|
||||||
|
|
||||||
|
for (let i=0; i<newItems.length; i++) {
|
||||||
|
newItems[i].container = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.array = newItems || [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ArrayContainer;
|
module.exports = ArrayContainer;
|
||||||
50
compiler/ast/ArrayExpression.js
Normal file
50
compiler/ast/ArrayExpression.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
|
||||||
|
class ArrayExpression extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('ArrayExpression');
|
||||||
|
this.elements = def.elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var elements = this.elements;
|
||||||
|
|
||||||
|
if (!elements || !elements.length) {
|
||||||
|
this.write('[]');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.write('[\n');
|
||||||
|
codegen.incIndent();
|
||||||
|
|
||||||
|
elements.forEach((element, i) => {
|
||||||
|
codegen.writeLineIndent();
|
||||||
|
codegen.generateCode(element);
|
||||||
|
|
||||||
|
if (i < elements.length - 1) {
|
||||||
|
codegen.write(',\n');
|
||||||
|
} else {
|
||||||
|
codegen.write('\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
codegen.decIndent();
|
||||||
|
codegen.writeLineIndent();
|
||||||
|
codegen.write(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.elements = walker.walk(this.elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
type: 'ArrayExpression',
|
||||||
|
elements: this.elements
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ArrayExpression;
|
||||||
@ -31,6 +31,11 @@ class Assignment extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.left = walker.walk(this.left);
|
||||||
|
this.right = walker.walk(this.right);
|
||||||
|
}
|
||||||
|
|
||||||
isCompoundExpression() {
|
isCompoundExpression() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,11 @@ class BinaryExpression extends Node {
|
|||||||
right: this.right
|
right: this.right
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.left = walker.walk(this.left);
|
||||||
|
this.right = walker.walk(this.right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BinaryExpression;
|
module.exports = BinaryExpression;
|
||||||
46
compiler/ast/ConditionalExpression.js
Normal file
46
compiler/ast/ConditionalExpression.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
|
||||||
|
class ConditionalExpression extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('ConditionalExpression');
|
||||||
|
this.test = def.test;
|
||||||
|
this.consequent = def.consequent;
|
||||||
|
this.alternate = def.alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var test = this.test;
|
||||||
|
var consequent = this.consequent;
|
||||||
|
var alternate = this.alternate;
|
||||||
|
|
||||||
|
|
||||||
|
codegen.generateCode(test);
|
||||||
|
codegen.write(' ? ');
|
||||||
|
codegen.generateCode(consequent);
|
||||||
|
codegen.write(' : ');
|
||||||
|
codegen.generateCode(alternate);
|
||||||
|
}
|
||||||
|
|
||||||
|
isCompoundExpression() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
type: 'ConditionalExpression',
|
||||||
|
test: this.test,
|
||||||
|
consequent: this.consequent,
|
||||||
|
alternate: this.alternate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.test = walker.walk(this.test);
|
||||||
|
this.consequent = walker.walk(this.consequent);
|
||||||
|
this.alternate = walker.walk(this.alternate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ConditionalExpression;
|
||||||
@ -20,6 +20,10 @@ class Else extends Node {
|
|||||||
codegen.generateBlock(body);
|
codegen.generateBlock(body);
|
||||||
codegen.write('\n');
|
codegen.write('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Else;
|
module.exports = Else;
|
||||||
@ -21,6 +21,12 @@ class ElseIf extends Node {
|
|||||||
codegen.write('else ');
|
codegen.write('else ');
|
||||||
codegen.generateCode(ifStatement);
|
codegen.generateCode(ifStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.test = walker.walk(this.test);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
this.else = walker.walk(this.else);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ElseIf;
|
module.exports = ElseIf;
|
||||||
@ -72,6 +72,15 @@ class ForEach extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.varName = walker.walk(this.varName);
|
||||||
|
this.in = walker.walk(this.in);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
this.separator = walker.walk(this.separator);
|
||||||
|
this.statusVarName = walker.walk(this.statusVarName);
|
||||||
|
this.iterator = walker.walk(this.iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ForEach;
|
module.exports = ForEach;
|
||||||
@ -31,6 +31,13 @@ class ForEachProp extends Node {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.nameVarName = walker.walk(this.nameVarName);
|
||||||
|
this.valueVarName = walker.walk(this.valueVarName);
|
||||||
|
this.in = walker.walk(this.in);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ForEachProp;
|
module.exports = ForEachProp;
|
||||||
@ -90,7 +90,14 @@ class ForRange extends Node {
|
|||||||
body: this.body
|
body: this.body
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.varName = walker.walk(this.varName);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
this.from = walker.walk(this.from);
|
||||||
|
this.to = walker.walk(this.to);
|
||||||
|
this.step = walker.walk(this.step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,13 @@ class ForStatement extends Node {
|
|||||||
|
|
||||||
codegen.write('\n');
|
codegen.write('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.init = walker.walk(this.init);
|
||||||
|
this.test = walker.walk(this.test);
|
||||||
|
this.update = walker.walk(this.update);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ForStatement;
|
module.exports = ForStatement;
|
||||||
@ -37,6 +37,11 @@ class FunctionCall extends Node {
|
|||||||
|
|
||||||
codegen.write(')');
|
codegen.write(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.callee = walker.walk(this.callee);
|
||||||
|
this.args = walker.walk(this.args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FunctionCall;
|
module.exports = FunctionCall;
|
||||||
@ -61,6 +61,12 @@ class FunctionDeclaration extends Node {
|
|||||||
isCompoundExpression() {
|
isCompoundExpression() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.name = walker.walk(this.name);
|
||||||
|
this.params = walker.walk(this.params);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FunctionDeclaration;
|
module.exports = FunctionDeclaration;
|
||||||
@ -16,6 +16,10 @@ class Html extends Node {
|
|||||||
let argument = this.argument;
|
let argument = this.argument;
|
||||||
codegen.addWrite(argument);
|
codegen.addWrite(argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.argument = walker.walk(this.argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Html;
|
module.exports = Html;
|
||||||
@ -1,14 +1,160 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
var Node = require('./Node');
|
||||||
var Literal = require('./Literal');
|
var Literal = require('./Literal');
|
||||||
var ok = require('assert').ok;
|
var ok = require('assert').ok;
|
||||||
|
var escapeXmlAttr = require('raptor-util/escapeXml').attr;
|
||||||
|
var compiler = require('../');
|
||||||
|
var parseExpression = require('../util/parseExpression');
|
||||||
|
|
||||||
class HtmlAttribute {
|
function isStringLiteral(node) {
|
||||||
|
return node.type === 'Literal' && typeof node.value === 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNoEscapeXml(node) {
|
||||||
|
return node.type === 'FunctionCall' &&
|
||||||
|
node.callee.type === 'Identifier' &&
|
||||||
|
node.callee.name === 'noEscapeXml';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEscapeXml(node) {
|
||||||
|
return node.type === 'FunctionCall' &&
|
||||||
|
node.callee.type === 'Identifier' &&
|
||||||
|
node.callee.name === 'escapeXml';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStringExpression(node) {
|
||||||
|
return node.type === 'FunctionCall' && node.callee.type === 'Identifier' &&
|
||||||
|
(node.callee.name === 'noEscapeXml' || node.callee.name === 'escapeXml');
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenAttrConcats(node) {
|
||||||
|
// return [node];
|
||||||
|
|
||||||
|
function flattenHelper(node) {
|
||||||
|
if (node.type === 'BinaryExpression' && node.operator === '+') {
|
||||||
|
let left = flattenHelper(node.left);
|
||||||
|
let right = flattenHelper(node.right);
|
||||||
|
|
||||||
|
var isString = left.isString || right.isString;
|
||||||
|
|
||||||
|
if (isString) {
|
||||||
|
return {
|
||||||
|
isString: true,
|
||||||
|
concats: left.concats.concat(right.concats)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
isString: false,
|
||||||
|
concats: [node]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isString: isStringLiteral(node) || isStringExpression(node),
|
||||||
|
concats: [node]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var final = flattenHelper(node);
|
||||||
|
return final.concats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function handleEscaping(node) {
|
||||||
|
//
|
||||||
|
// function handleEscapingHelper(node, escaping) {
|
||||||
|
// if (node.type === 'Literal') {
|
||||||
|
// } else if (isEscapeXml(node)) {
|
||||||
|
// return handleEscapingHelper(node.arguments[0], true);
|
||||||
|
// } else if (isNoEscapeXml(node)) {
|
||||||
|
// return handleEscapingHelper(node.arguments[0], escaping false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var finalNode = handleEscapingHelper(node, true /* default to escaping */);
|
||||||
|
// return finalNode;
|
||||||
|
// }
|
||||||
|
|
||||||
|
function removeEscapeFunctions(node) {
|
||||||
|
var walker = compiler.createWalker({
|
||||||
|
enter: function(node, parent) {
|
||||||
|
if (isNoEscapeXml(node) || isEscapeXml(node)) {
|
||||||
|
return node.args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return walker.walk(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCodeForExpressionAttr(name, value, codegen) {
|
||||||
|
var flattenedConcats = flattenAttrConcats(value);
|
||||||
|
var hasLiteral = false;
|
||||||
|
|
||||||
|
for (let i=0; i<flattenedConcats.length; i++) {
|
||||||
|
if (flattenedConcats[i].type === 'Literal') {
|
||||||
|
hasLiteral = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLiteral) {
|
||||||
|
codegen.addWriteLiteral(' ' + name + '="');
|
||||||
|
for (let i=0; i<flattenedConcats.length; i++) {
|
||||||
|
var part = flattenedConcats[i];
|
||||||
|
if (isStringLiteral(part)) {
|
||||||
|
part.value = escapeXmlAttr(part.value);
|
||||||
|
} else if (part.type === 'Literal') {
|
||||||
|
|
||||||
|
} else if (isNoEscapeXml(part)) {
|
||||||
|
part = removeEscapeFunctions(part);
|
||||||
|
} else {
|
||||||
|
var escapeXmlAttrVar = codegen.addStaticVar('escapeXmlAttr', '__helpers.xa');
|
||||||
|
part = removeEscapeFunctions(part);
|
||||||
|
part = codegen.builder.functionCall(escapeXmlAttrVar, [part]);
|
||||||
|
}
|
||||||
|
codegen.addWrite(part);
|
||||||
|
}
|
||||||
|
codegen.addWriteLiteral('"');
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// let builder = codegen.builder;
|
||||||
|
// let valueWithEscaping = handleEscaping(value);
|
||||||
|
let attrVar = codegen.addStaticVar('attr', '__helpers.a');
|
||||||
|
|
||||||
|
var escape = true;
|
||||||
|
|
||||||
|
if (isNoEscapeXml(value)) {
|
||||||
|
escape = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = removeEscapeFunctions(value);
|
||||||
|
let attrArgs = [codegen.builder.literal(name), value];
|
||||||
|
|
||||||
|
if (escape === false) {
|
||||||
|
attrArgs.push(codegen.builder.literal(false));
|
||||||
|
}
|
||||||
|
codegen.addWrite(codegen.builder.functionCall(attrVar, attrArgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HtmlAttribute extends Node {
|
||||||
constructor(def) {
|
constructor(def) {
|
||||||
ok(def, 'Invalid attribute definition');
|
super('HtmlAttribute');
|
||||||
|
|
||||||
|
ok(def, 'Invalid attribute definition');
|
||||||
|
this.type = 'HtmlAttribute';
|
||||||
this.name = def.name.toLowerCase();
|
this.name = def.name.toLowerCase();
|
||||||
this.value = def.value;
|
this.value = def.value;
|
||||||
|
|
||||||
|
if (typeof this.value === 'string') {
|
||||||
|
this.value = parseExpression(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
this.argument = def.argument;
|
this.argument = def.argument;
|
||||||
|
|
||||||
this.def = def.def; // The attribute definition loaded from the taglib (if any)
|
this.def = def.def; // The attribute definition loaded from the taglib (if any)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +171,39 @@ class HtmlAttribute {
|
|||||||
return this.isLiteralValue() &&
|
return this.isLiteralValue() &&
|
||||||
typeof this.value.value === 'boolean';
|
typeof this.value.value === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateHtmlCode(codegen) {
|
||||||
|
let name = this.name;
|
||||||
|
let value = this.value;
|
||||||
|
let argument = this.argument;
|
||||||
|
|
||||||
|
if (this.isLiteralValue()) {
|
||||||
|
var literalValue = value.value;
|
||||||
|
if (typeof literalValue === 'boolean') {
|
||||||
|
if (literalValue === true) {
|
||||||
|
codegen.addWriteLiteral(' ' + name);
|
||||||
|
}
|
||||||
|
} else if (literalValue != null) {
|
||||||
|
codegen.addWriteLiteral(' ' + name + '="' + escapeXmlAttr(literalValue) + '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value != null) {
|
||||||
|
codegen.isInAttribute = true;
|
||||||
|
generateCodeForExpressionAttr(name, value, codegen);
|
||||||
|
codegen.isInAttribute = false;
|
||||||
|
} else if (argument) {
|
||||||
|
codegen.addWriteLiteral(' ' + name + '(');
|
||||||
|
codegen.addWriteLiteral(argument);
|
||||||
|
codegen.addWriteLiteral(')');
|
||||||
|
} else {
|
||||||
|
// Attribute with no value is a boolean attribute
|
||||||
|
codegen.addWriteLiteral(' ' + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.value = walker.walk(this.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HtmlAttribute.isHtmlAttribute = function(attr) {
|
HtmlAttribute.isHtmlAttribute = function(attr) {
|
||||||
|
|||||||
@ -7,35 +7,7 @@ var Node = require('./Node');
|
|||||||
|
|
||||||
class HtmlAttributeCollection {
|
class HtmlAttributeCollection {
|
||||||
constructor(attributes) {
|
constructor(attributes) {
|
||||||
this.all = [];
|
this.setAttributes(attributes);
|
||||||
this.lookup = {};
|
|
||||||
|
|
||||||
if (attributes) {
|
|
||||||
if (Array.isArray(attributes)) {
|
|
||||||
attributes.forEach((attr) => {
|
|
||||||
this.addAttribute(attr);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (var attrName in attributes) {
|
|
||||||
if (attributes.hasOwnProperty(attrName)) {
|
|
||||||
let attrValue = attributes[attrName];
|
|
||||||
let attrDef;
|
|
||||||
|
|
||||||
if (typeof attrValue === 'object' && !(attrValue instanceof Node)) {
|
|
||||||
attrDef = attrValue;
|
|
||||||
attrDef.name = attrName;
|
|
||||||
} else {
|
|
||||||
attrDef = {
|
|
||||||
name: attrName,
|
|
||||||
value: attrValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.addAttribute(attrDef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addAttribute(newAttr) {
|
addAttribute(newAttr) {
|
||||||
@ -135,6 +107,43 @@ class HtmlAttributeCollection {
|
|||||||
toString() {
|
toString() {
|
||||||
return JSON.stringify(this.all);
|
return JSON.stringify(this.all);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAttributes(attributes) {
|
||||||
|
this.all = [];
|
||||||
|
this.lookup = {};
|
||||||
|
|
||||||
|
if (attributes) {
|
||||||
|
if (Array.isArray(attributes)) {
|
||||||
|
attributes.forEach((attr) => {
|
||||||
|
this.addAttribute(attr);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (var attrName in attributes) {
|
||||||
|
if (attributes.hasOwnProperty(attrName)) {
|
||||||
|
let attrValue = attributes[attrName];
|
||||||
|
let attrDef;
|
||||||
|
|
||||||
|
if (typeof attrValue === 'object' && !(attrValue instanceof Node)) {
|
||||||
|
attrDef = attrValue;
|
||||||
|
attrDef.name = attrName;
|
||||||
|
} else {
|
||||||
|
attrDef = {
|
||||||
|
name: attrName,
|
||||||
|
value: attrValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addAttribute(attrDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
var newAttributes = walker.walk(this.all);
|
||||||
|
this.setAttributes(newAttributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlAttributeCollection;
|
module.exports = HtmlAttributeCollection;
|
||||||
@ -16,6 +16,10 @@ class HtmlComment extends Node {
|
|||||||
codegen.addWrite(comment);
|
codegen.addWrite(comment);
|
||||||
codegen.addWrite(literal('-->'));
|
codegen.addWrite(literal('-->'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.comment = walker.walk(this.comment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlComment;
|
module.exports = HtmlComment;
|
||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
var Node = require('./Node');
|
var Node = require('./Node');
|
||||||
var Literal = require('./Literal');
|
var Literal = require('./Literal');
|
||||||
var escapeXmlAttr = require('raptor-util/escapeXml').attr;
|
|
||||||
var HtmlAttributeCollection = require('./HtmlAttributeCollection');
|
var HtmlAttributeCollection = require('./HtmlAttributeCollection');
|
||||||
|
|
||||||
class StartTag extends Node {
|
class StartTag extends Node {
|
||||||
@ -33,31 +32,7 @@ class StartTag extends Node {
|
|||||||
if (attributes) {
|
if (attributes) {
|
||||||
for (let i=0; i<attributes.length; i++) {
|
for (let i=0; i<attributes.length; i++) {
|
||||||
let attr = attributes[i];
|
let attr = attributes[i];
|
||||||
let attrName = attr.name;
|
codegen.generateCode(attr);
|
||||||
let attrValue = attr.value;
|
|
||||||
|
|
||||||
if (attr.isLiteralValue()) {
|
|
||||||
var literalValue = attrValue.value;
|
|
||||||
if (typeof literalValue === 'boolean') {
|
|
||||||
if (literalValue === true) {
|
|
||||||
codegen.addWriteLiteral(' ' + attrName);
|
|
||||||
}
|
|
||||||
} else if (literalValue != null) {
|
|
||||||
codegen.addWriteLiteral(' ' + attrName + '="' + escapeXmlAttr(literalValue) + '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (attrValue) {
|
|
||||||
codegen.addWriteLiteral(' ' + attrName + '="');
|
|
||||||
codegen.isInAttribute = true;
|
|
||||||
// TODO Deal with escaping dynamic HTML attribute expression
|
|
||||||
codegen.addWrite(attrValue);
|
|
||||||
codegen.isInAttribute = false;
|
|
||||||
codegen.addWriteLiteral('"');
|
|
||||||
} else if (attr.argument) {
|
|
||||||
codegen.addWriteLiteral(' ' + attrName + '(');
|
|
||||||
codegen.addWriteLiteral(attr.argument);
|
|
||||||
codegen.addWriteLiteral(')');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,12 +73,13 @@ class HtmlElement extends Node {
|
|||||||
this.tagNameExpression = null;
|
this.tagNameExpression = null;
|
||||||
this.setTagName(def.tagName);
|
this.setTagName(def.tagName);
|
||||||
this._attributes = def.attributes;
|
this._attributes = def.attributes;
|
||||||
|
this.body = this.makeContainer(def.body);
|
||||||
|
this.argument = def.argument;
|
||||||
|
|
||||||
if (!(this._attributes instanceof HtmlAttributeCollection)) {
|
if (!(this._attributes instanceof HtmlAttributeCollection)) {
|
||||||
this._attributes = new HtmlAttributeCollection(this._attributes);
|
this._attributes = new HtmlAttributeCollection(this._attributes);
|
||||||
}
|
}
|
||||||
this.body = this.makeContainer(def.body);
|
|
||||||
this.argument = def.argument;
|
|
||||||
this.allowSelfClosing = false;
|
this.allowSelfClosing = false;
|
||||||
this.startTagOnly = false;
|
this.startTagOnly = false;
|
||||||
this.dynamicAttributes = undefined;
|
this.dynamicAttributes = undefined;
|
||||||
@ -190,7 +166,6 @@ class HtmlElement extends Node {
|
|||||||
codegen.generateCode(body);
|
codegen.generateCode(body);
|
||||||
codegen.generateCode(endTag);
|
codegen.generateCode(endTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +229,8 @@ class HtmlElement extends Node {
|
|||||||
} else {
|
} else {
|
||||||
this.tagNameExpression = tagName;
|
this.tagNameExpression = tagName;
|
||||||
}
|
}
|
||||||
} else if (typeof tagName === 'string'){
|
} else if (typeof tagName === 'string') {
|
||||||
|
this.tagNameExpression = new Literal({value: tagName});
|
||||||
this.tagName = tagName;
|
this.tagName = tagName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,6 +250,12 @@ class HtmlElement extends Node {
|
|||||||
setBodyOnlyIf(condition) {
|
setBodyOnlyIf(condition) {
|
||||||
this.bodyOnlyIf = condition;
|
this.bodyOnlyIf = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.setTagName(walker.walk(this.tagNameExpression));
|
||||||
|
this._attributes.walk(walker);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlElement;
|
module.exports = HtmlElement;
|
||||||
@ -77,6 +77,12 @@ class If extends Node {
|
|||||||
appendChild(newChild) {
|
appendChild(newChild) {
|
||||||
this.body.appendChild(newChild);
|
this.body.appendChild(newChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.test = walker.walk(this.test);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
this.else = walker.walk(this.else);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = If;
|
module.exports = If;
|
||||||
@ -110,6 +110,13 @@ class InvokeMacro extends Node {
|
|||||||
|
|
||||||
return builder.functionCall(builder.identifier(macroDef.functionName), args);
|
return builder.functionCall(builder.identifier(macroDef.functionName), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.el = walker.walk(this.el);
|
||||||
|
this.name = walker.walk(this.name);
|
||||||
|
this.args = walker.walk(this.args);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = InvokeMacro;
|
module.exports = InvokeMacro;
|
||||||
63
compiler/ast/LogicalExpression.js
Normal file
63
compiler/ast/LogicalExpression.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
var isCompoundExpression = require('../util/isCompoundExpression');
|
||||||
|
|
||||||
|
function generateCodeForOperand(node, codegen) {
|
||||||
|
var wrap = isCompoundExpression(node);
|
||||||
|
|
||||||
|
if (wrap) {
|
||||||
|
codegen.write('(');
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.generateCode(node);
|
||||||
|
|
||||||
|
if (wrap) {
|
||||||
|
codegen.write(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogicalExpression extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('LogicalExpression');
|
||||||
|
this.left = def.left;
|
||||||
|
this.operator = def.operator;
|
||||||
|
this.right = def.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var left = this.left;
|
||||||
|
var operator = this.operator;
|
||||||
|
var right = this.right;
|
||||||
|
|
||||||
|
if (!left || !right) {
|
||||||
|
throw new Error('Invalid LogicalExpression: ' + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCodeForOperand(left, codegen);
|
||||||
|
codegen.write(' ');
|
||||||
|
codegen.generateCode(operator);
|
||||||
|
codegen.write(' ');
|
||||||
|
generateCodeForOperand(right, codegen);
|
||||||
|
}
|
||||||
|
|
||||||
|
isCompoundExpression() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
type: 'LogicalExpression',
|
||||||
|
left: this.left,
|
||||||
|
operator: this.operator,
|
||||||
|
right: this.right
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.left = walker.walk(this.left);
|
||||||
|
this.right = walker.walk(this.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = LogicalExpression;
|
||||||
@ -29,6 +29,10 @@ class Macro extends Node {
|
|||||||
var functionName = macroDef.functionName;
|
var functionName = macroDef.functionName;
|
||||||
return builder.functionDeclaration(functionName, macroDef.params, body);
|
return builder.functionDeclaration(functionName, macroDef.params, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Macro;
|
module.exports = Macro;
|
||||||
@ -35,6 +35,11 @@ class MemberExpression extends Node {
|
|||||||
computed: this.computed
|
computed: this.computed
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.object = walker.walk(this.object);
|
||||||
|
this.property = walker.walk(this.property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MemberExpression;
|
module.exports = MemberExpression;
|
||||||
70
compiler/ast/NewExpression.js
Normal file
70
compiler/ast/NewExpression.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
var isCompoundExpression = require('../util/isCompoundExpression');
|
||||||
|
|
||||||
|
class NewExpression extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('NewExpression');
|
||||||
|
this.callee = def.callee;
|
||||||
|
this.args = def.args;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var callee = this.callee;
|
||||||
|
var args = this.args;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
codegen.write('new ');
|
||||||
|
|
||||||
|
var wrap = isCompoundExpression(callee);
|
||||||
|
|
||||||
|
if (wrap) {
|
||||||
|
codegen.write('(');
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.generateCode(callee);
|
||||||
|
|
||||||
|
if (wrap) {
|
||||||
|
codegen.write(')');
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.write('(');
|
||||||
|
|
||||||
|
if (args && args.length) {
|
||||||
|
for (let i=0, argsLen = args.length; i<argsLen; i++) {
|
||||||
|
if (i !== 0) {
|
||||||
|
codegen.write(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg = args[i];
|
||||||
|
if (!arg) {
|
||||||
|
throw new Error('Arg ' + i + ' is not valid for new expression: ' + JSON.stringify(this.toJSON()));
|
||||||
|
}
|
||||||
|
codegen.generateCode(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.write(')');
|
||||||
|
}
|
||||||
|
|
||||||
|
isCompoundExpression() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
type: 'NewExpression',
|
||||||
|
callee: this.callee,
|
||||||
|
args: this.args
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.callee = walker.walk(this.callee);
|
||||||
|
this.args = walker.walk(this.args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = NewExpression;
|
||||||
@ -83,7 +83,9 @@ class Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detach() {
|
detach() {
|
||||||
this.container.removeChild(this);
|
if (this.container) {
|
||||||
|
this.container.removeChild(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
50
compiler/ast/ObjectExpression.js
Normal file
50
compiler/ast/ObjectExpression.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
|
||||||
|
class ObjectExpression extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('ObjectExpression');
|
||||||
|
this.properties = def.properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var properties = this.properties;
|
||||||
|
|
||||||
|
if (!properties || !properties.length) {
|
||||||
|
this.write('{}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.write('{\n');
|
||||||
|
codegen.incIndent();
|
||||||
|
|
||||||
|
properties.forEach((prop, i) => {
|
||||||
|
codegen.writeLineIndent();
|
||||||
|
codegen.generateCode(prop);
|
||||||
|
|
||||||
|
if (i < properties.length - 1) {
|
||||||
|
codegen.write(',\n');
|
||||||
|
} else {
|
||||||
|
codegen.write('\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
codegen.decIndent();
|
||||||
|
codegen.writeLineIndent();
|
||||||
|
codegen.write('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: this.properties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.properties = walker.walk(this.properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ObjectExpression;
|
||||||
@ -15,6 +15,10 @@ class Program extends Node {
|
|||||||
codegen._flushBufferedWrites();
|
codegen._flushBufferedWrites();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Program;
|
module.exports = Program;
|
||||||
35
compiler/ast/Property.js
Normal file
35
compiler/ast/Property.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
|
||||||
|
class Property extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('Property');
|
||||||
|
this.key = def.key;
|
||||||
|
this.value = def.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var key = this.key;
|
||||||
|
var value = this.value;
|
||||||
|
|
||||||
|
codegen.generateCode(key);
|
||||||
|
codegen.write(': ');
|
||||||
|
codegen.generateCode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
type: 'Property',
|
||||||
|
key: this.key,
|
||||||
|
value: this.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.key = walker.walk(this.key);
|
||||||
|
this.value = walker.walk(this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Property;
|
||||||
@ -22,6 +22,10 @@ class Return extends Node {
|
|||||||
codegen.write('return');
|
codegen.write('return');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.argument = walker.walk(this.argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Return;
|
module.exports = Return;
|
||||||
@ -22,6 +22,12 @@ class SelfInvokingFunction extends Node {
|
|||||||
|
|
||||||
codegen.write(')');
|
codegen.write(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.params = walker.walk(this.params);
|
||||||
|
this.args = walker.walk(this.args);
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SelfInvokingFunction;
|
module.exports = SelfInvokingFunction;
|
||||||
@ -64,6 +64,10 @@ class TemplateRoot extends Node {
|
|||||||
body: this.body
|
body: this.body
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.body = walker.walk(this.body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TemplateRoot;
|
module.exports = TemplateRoot;
|
||||||
15
compiler/ast/ThisExpression.js
Normal file
15
compiler/ast/ThisExpression.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
|
||||||
|
class ThisExpression extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('ThisExpression');
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
codegen.write('this');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ThisExpression;
|
||||||
@ -53,6 +53,10 @@ class UnaryExpression extends Node {
|
|||||||
prefix: this.prefix
|
prefix: this.prefix
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.argument = walker.walk(this.argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UnaryExpression;
|
module.exports = UnaryExpression;
|
||||||
@ -49,6 +49,10 @@ class UpdateExpression extends Node {
|
|||||||
prefix: this.prefix
|
prefix: this.prefix
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.argument = walker.walk(this.argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = UpdateExpression;
|
module.exports = UpdateExpression;
|
||||||
35
compiler/ast/VariableDeclarator.js
Normal file
35
compiler/ast/VariableDeclarator.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Node = require('./Node');
|
||||||
|
var Identifier = require('./Identifier');
|
||||||
|
|
||||||
|
class VariableDeclarator extends Node {
|
||||||
|
constructor(def) {
|
||||||
|
super('VariableDeclarator');
|
||||||
|
this.id = def.id;
|
||||||
|
this.init = def.init;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateCode(codegen) {
|
||||||
|
var id = this.id;
|
||||||
|
var init = this.init;
|
||||||
|
|
||||||
|
if (!(id instanceof Identifier) && typeof id !== 'string') {
|
||||||
|
throw new Error('Invalid variable name: ' + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen.generateCode(id);
|
||||||
|
|
||||||
|
if (init != null) {
|
||||||
|
codegen.write(' = ');
|
||||||
|
codegen.generateCode(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.id = walker.walk(this.id);
|
||||||
|
this.init = walker.walk(this.init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = VariableDeclarator;
|
||||||
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Node = require('./Node');
|
var Node = require('./Node');
|
||||||
var Identifier = require('./Identifier');
|
|
||||||
|
|
||||||
class Vars extends Node {
|
class Vars extends Node {
|
||||||
constructor(def) {
|
constructor(def) {
|
||||||
@ -29,7 +28,7 @@ class Vars extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i=0; i<declarations.length; i++) {
|
for (let i=0; i<declarations.length; i++) {
|
||||||
var declaration = declarations[i];
|
var declarator = declarations[i];
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
codegen.write(kind + ' ');
|
codegen.write(kind + ' ');
|
||||||
@ -38,26 +37,7 @@ class Vars extends Node {
|
|||||||
codegen.writeLineIndent();
|
codegen.writeLineIndent();
|
||||||
}
|
}
|
||||||
|
|
||||||
var varId = declaration.id || declaration.name;
|
codegen.generateCode(declarator);
|
||||||
|
|
||||||
if (!(varId instanceof Identifier) && typeof varId !== 'string') {
|
|
||||||
throw new Error('Invalid variable name: ' + varId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Validate the variable name
|
|
||||||
codegen.generateCode(varId);
|
|
||||||
|
|
||||||
var initValue;
|
|
||||||
if (declaration.hasOwnProperty('init')) {
|
|
||||||
initValue = declaration.init;
|
|
||||||
} else if (declaration.hasOwnProperty('value')) {
|
|
||||||
initValue = declaration.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initValue != null) {
|
|
||||||
codegen.write(' = ');
|
|
||||||
codegen.generateCode(initValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i !== 0) {
|
if (i !== 0) {
|
||||||
codegen.decIndent(4);
|
codegen.decIndent(4);
|
||||||
@ -75,6 +55,10 @@ class Vars extends Node {
|
|||||||
codegen.generateCode(body);
|
codegen.generateCode(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walk(walker) {
|
||||||
|
this.argument = walker.walk(this.argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Vars;
|
module.exports = Vars;
|
||||||
@ -7,42 +7,25 @@ var compiler = require('../');
|
|||||||
function convert(node) {
|
function convert(node) {
|
||||||
var builder = compiler.defaultBuilder;
|
var builder = compiler.defaultBuilder;
|
||||||
|
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
let nodes = node;
|
||||||
|
for (let i=0; i<nodes.length; i++) {
|
||||||
|
var converted = convert(nodes[i]);
|
||||||
|
if (converted == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
nodes[i] = converted;
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
switch(node.type) {
|
switch(node.type) {
|
||||||
case 'Program': {
|
case 'ArrayExpression': {
|
||||||
if (node.body && node.body.length === 1) {
|
let elements = convert(node.elements);
|
||||||
return convert(node.body[0]);
|
if (!elements) {
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case 'ExpressionStatement': {
|
|
||||||
return convert(node.expression);
|
|
||||||
}
|
|
||||||
case 'Identifier': {
|
|
||||||
return builder.identifier(node.name);
|
|
||||||
}
|
|
||||||
case 'Literal': {
|
|
||||||
return builder.literal(node.value);
|
|
||||||
}
|
|
||||||
case 'BinaryExpression': {
|
|
||||||
let left = convert(node.left);
|
|
||||||
if (!left) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return builder.arrayExpression(elements);
|
||||||
let right = convert(node.right);
|
|
||||||
if (!right) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.binaryExpression(left, node.operator, right);
|
|
||||||
}
|
|
||||||
case 'UnaryExpression': {
|
|
||||||
let argument = convert(node.argument);
|
|
||||||
if (!argument) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.unaryExpression(argument, node.operator, node.prefix);
|
|
||||||
}
|
}
|
||||||
case 'AssignmentExpression': {
|
case 'AssignmentExpression': {
|
||||||
let left = convert(node.left);
|
let left = convert(node.left);
|
||||||
@ -57,6 +40,99 @@ function convert(node) {
|
|||||||
|
|
||||||
return builder.assignment(left, right, node.operator);
|
return builder.assignment(left, right, node.operator);
|
||||||
}
|
}
|
||||||
|
case 'BinaryExpression': {
|
||||||
|
let left = convert(node.left);
|
||||||
|
if (!left) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let right = convert(node.right);
|
||||||
|
if (!right) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.binaryExpression(left, node.operator, right);
|
||||||
|
}
|
||||||
|
case 'CallExpression': {
|
||||||
|
let callee = convert(node.callee);
|
||||||
|
|
||||||
|
if (!callee) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = convert(node.arguments);
|
||||||
|
if (!args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.functionCall(callee, args);
|
||||||
|
}
|
||||||
|
case 'ConditionalExpression': {
|
||||||
|
let test = convert(node.test);
|
||||||
|
|
||||||
|
if (!test) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let consequent = convert(node.consequent);
|
||||||
|
|
||||||
|
if (!consequent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alternate = convert(node.alternate);
|
||||||
|
|
||||||
|
if (!alternate) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.conditionalExpression(test, consequent, alternate);
|
||||||
|
}
|
||||||
|
case 'ExpressionStatement': {
|
||||||
|
return convert(node.expression);
|
||||||
|
}
|
||||||
|
case 'FunctionDeclaration':
|
||||||
|
case 'FunctionExpression': {
|
||||||
|
let name = null;
|
||||||
|
|
||||||
|
if (node.id) {
|
||||||
|
name = convert(node.id);
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = convert(node.params);
|
||||||
|
if (!params) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = convert(node.body);
|
||||||
|
if (!body) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.functionDeclaration(name, params, body);
|
||||||
|
}
|
||||||
|
case 'Identifier': {
|
||||||
|
return builder.identifier(node.name);
|
||||||
|
}
|
||||||
|
case 'Literal': {
|
||||||
|
return builder.literal(node.value);
|
||||||
|
}
|
||||||
|
case 'LogicalExpression': {
|
||||||
|
let left = convert(node.left);
|
||||||
|
if (!left) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let right = convert(node.right);
|
||||||
|
if (!right) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.logicalExpression(left, node.operator, right);
|
||||||
|
}
|
||||||
case 'MemberExpression': {
|
case 'MemberExpression': {
|
||||||
let object = convert(node.object);
|
let object = convert(node.object);
|
||||||
if (!object) {
|
if (!object) {
|
||||||
@ -70,13 +146,62 @@ function convert(node) {
|
|||||||
|
|
||||||
return builder.memberExpression(object, property, node.computed);
|
return builder.memberExpression(object, property, node.computed);
|
||||||
}
|
}
|
||||||
|
case 'NewExpression': {
|
||||||
|
let callee = convert(node.callee);
|
||||||
|
|
||||||
|
if (!callee) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = convert(node.arguments);
|
||||||
|
if (!args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.newExpression(callee, args);
|
||||||
|
}
|
||||||
|
case 'Program': {
|
||||||
|
if (node.body && node.body.length === 1) {
|
||||||
|
return convert(node.body[0]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case 'ObjectExpression': {
|
||||||
|
let properties = convert(node.properties);
|
||||||
|
if (!properties) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return builder.objectExpression(properties);
|
||||||
|
}
|
||||||
|
case 'Property': {
|
||||||
|
let key = convert(node.key);
|
||||||
|
if (!key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let value = convert(node.value);
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return builder.property(key, value);
|
||||||
|
}
|
||||||
|
case 'ThisExpression': {
|
||||||
|
return builder.thisExpression();
|
||||||
|
}
|
||||||
|
case 'UnaryExpression': {
|
||||||
|
let argument = convert(node.argument);
|
||||||
|
if (!argument) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.unaryExpression(argument, node.operator, node.prefix);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseExpression(src) {
|
function parseExpression(src) {
|
||||||
let jsAST = esprima.parse(src);
|
let jsAST = esprima.parse('(' + src + ')');
|
||||||
var converted = convert(jsAST);
|
var converted = convert(jsAST);
|
||||||
if (converted == null) {
|
if (converted == null) {
|
||||||
converted = new Expression({value: src});
|
converted = new Expression({value: src});
|
||||||
|
|||||||
@ -63,6 +63,8 @@ if (this.container) {
|
|||||||
|
|
||||||
## methods
|
## methods
|
||||||
|
|
||||||
|
### arrayExpression(elements)
|
||||||
|
|
||||||
### assignment(left, right)
|
### assignment(left, right)
|
||||||
|
|
||||||
Returns a node that generates the following code:
|
Returns a node that generates the following code:
|
||||||
@ -87,7 +89,7 @@ foo = '123';
|
|||||||
Returns a node that generates the following code:
|
Returns a node that generates the following code:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
<left> <operator> <right>;
|
<left> <operator> <right>
|
||||||
```
|
```
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -121,6 +123,26 @@ var b = 2;
|
|||||||
b = 3;
|
b = 3;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### conditionalExpression(test, consequent, alternate)
|
||||||
|
|
||||||
|
Returns a node that generates the following code:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
<test> ? <consequent> : <alternate>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
builder.conditionalExpression(
|
||||||
|
builder.identifier('isHidden'),
|
||||||
|
builder.literal('hidden'),
|
||||||
|
builder.literal('visible'));
|
||||||
|
|
||||||
|
// Output code:
|
||||||
|
isHidden ? "hidden" : "visible"
|
||||||
|
```
|
||||||
|
|
||||||
### elseStatement(body)
|
### elseStatement(body)
|
||||||
|
|
||||||
Returns a node that generates the following code:
|
Returns a node that generates the following code:
|
||||||
@ -499,6 +521,8 @@ var aString = "abc",
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### logicalExpression(left, operator, right)
|
||||||
|
|
||||||
### macro(name, params, body)
|
### macro(name, params, body)
|
||||||
|
|
||||||
Returns a node that generates a macro function with the given name, params and body content. The `InvokeMacro` node should be used to generate the code to invoke the macro.
|
Returns a node that generates a macro function with the given name, params and body content. The `InvokeMacro` node should be used to generate the code to invoke the macro.
|
||||||
@ -520,6 +544,8 @@ builder.negate(builder.identifier('foo'))
|
|||||||
!foo
|
!foo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### newExpression(callee, args)
|
||||||
|
|
||||||
### node([type, ]generatCode)
|
### node([type, ]generatCode)
|
||||||
|
|
||||||
Returns a generic `Node` instance with the given node type (optional) and a `generateCode(node, generator)` function that should be used to generate the code for the node. If a `generateCode(node, generator)` function is not provided the node bust be monkey-patched to add a `generateCode(generator)` method.
|
Returns a generic `Node` instance with the given node type (optional) and a `generateCode(node, generator)` function that should be used to generate the code for the node. If a `generateCode(node, generator)` function is not provided the node bust be monkey-patched to add a `generateCode(generator)` method.
|
||||||
@ -536,6 +562,8 @@ builder.node(function(node, generator) {
|
|||||||
out.w("Hello World!");
|
out.w("Hello World!");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### objectExpression(properties)
|
||||||
|
|
||||||
### program(body)
|
### program(body)
|
||||||
|
|
||||||
Returns a node to generate the code for the root statements of a JavaScript code.
|
Returns a node to generate the code for the root statements of a JavaScript code.
|
||||||
@ -559,6 +587,8 @@ var name = "Frank";
|
|||||||
console.log("Hello", name);
|
console.log("Hello", name);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### property(key, value)
|
||||||
|
|
||||||
### require(path)
|
### require(path)
|
||||||
|
|
||||||
Returns a node that generates the following code:
|
Returns a node that generates the following code:
|
||||||
@ -701,10 +731,14 @@ a === b
|
|||||||
|
|
||||||
### text(argument, escape)
|
### text(argument, escape)
|
||||||
|
|
||||||
|
### thisExpression()
|
||||||
|
|
||||||
### unaryExpression(argument, operator, prefix)
|
### unaryExpression(argument, operator, prefix)
|
||||||
|
|
||||||
### updateExpression(argument, operator, prefix)
|
### updateExpression(argument, operator, prefix)
|
||||||
|
|
||||||
|
### variableDeclarator(id, init)
|
||||||
|
|
||||||
### vars(declarations, kind)
|
### vars(declarations, kind)
|
||||||
|
|
||||||
# CodeGenerator
|
# CodeGenerator
|
||||||
3
test/.gitignore
vendored
3
test/.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
|
/scratch.js
|
||||||
@ -1,8 +1,8 @@
|
|||||||
if (true) {
|
if (true) {
|
||||||
if (!(!data.url)) {
|
if (!(!data.url)) {
|
||||||
out.w("<a href=\"" +
|
out.w("<a" +
|
||||||
data.url +
|
attr("href", data.url) +
|
||||||
"\">");
|
">");
|
||||||
}
|
}
|
||||||
|
|
||||||
out.w("Hello World");
|
out.w("Hello World");
|
||||||
|
|||||||
1
test/fixtures/codegen/autotest/conditionalExpression/expected.js
vendored
Normal file
1
test/fixtures/codegen/autotest/conditionalExpression/expected.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
isHidden ? "hidden" : "visible"
|
||||||
8
test/fixtures/codegen/autotest/conditionalExpression/index.js
vendored
Normal file
8
test/fixtures/codegen/autotest/conditionalExpression/index.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(builder) {
|
||||||
|
return builder.conditionalExpression(
|
||||||
|
builder.identifier('isHidden'),
|
||||||
|
builder.literal('hidden'),
|
||||||
|
builder.literal('visible'));
|
||||||
|
};
|
||||||
3
test/fixtures/codegen/autotest/htmlElement-attr-escape/expected.js
vendored
Normal file
3
test/fixtures/codegen/autotest/htmlElement-attr-escape/expected.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
out.w("<div class=\"greeting\"" +
|
||||||
|
attr("foo", bar) +
|
||||||
|
">Hello World</div>");
|
||||||
18
test/fixtures/codegen/autotest/htmlElement-attr-escape/index.js
vendored
Normal file
18
test/fixtures/codegen/autotest/htmlElement-attr-escape/index.js
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
module.exports = function(builder) {
|
||||||
|
return builder.htmlElement(
|
||||||
|
'div',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'class',
|
||||||
|
value: builder.literal('greeting')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
value: builder.identifier('bar')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
builder.text(builder.literal('Hello World'))
|
||||||
|
]);
|
||||||
|
};
|
||||||
1
test/fixtures/codegen/autotest/new/expected.js
vendored
Normal file
1
test/fixtures/codegen/autotest/new/expected.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
new Foo("Frank", "human")
|
||||||
10
test/fixtures/codegen/autotest/new/index.js
vendored
Normal file
10
test/fixtures/codegen/autotest/new/index.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(builder) {
|
||||||
|
return builder.newExpression(
|
||||||
|
builder.identifier('Foo'),
|
||||||
|
[
|
||||||
|
builder.literal('Frank'),
|
||||||
|
builder.literal('human')
|
||||||
|
]);
|
||||||
|
};
|
||||||
25
test/fixtures/compiler/autotest/attr-escape/expected.js
vendored
Normal file
25
test/fixtures/compiler/autotest/attr-escape/expected.js
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
function create(__helpers) {
|
||||||
|
var str = __helpers.s,
|
||||||
|
empty = __helpers.e,
|
||||||
|
notEmpty = __helpers.ne,
|
||||||
|
escapeXml = __helpers.x,
|
||||||
|
attr = __helpers.a,
|
||||||
|
escapeXmlAttr = __helpers.xa;
|
||||||
|
|
||||||
|
return function render(data, out) {
|
||||||
|
out.w("<div" +
|
||||||
|
attr("class", data.className) +
|
||||||
|
attr("class2", data.className, false) +
|
||||||
|
" foo=\"a" +
|
||||||
|
escapeXmlAttr(data.foo) +
|
||||||
|
"b\" bar=\"a " +
|
||||||
|
escapeXmlAttr(data.foo) +
|
||||||
|
" b\" baz=\"a " +
|
||||||
|
data.foo +
|
||||||
|
" b\" nested=\"a " +
|
||||||
|
data.foo + ("nested " + data.bar) +
|
||||||
|
" b\"></div>");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
(module.exports = require("marko").c(__filename)).c(create);
|
||||||
7
test/fixtures/compiler/autotest/attr-escape/template.marko
vendored
Normal file
7
test/fixtures/compiler/autotest/attr-escape/template.marko
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<div class=data.className
|
||||||
|
class2="$!{data.className}"
|
||||||
|
foo=('a' + data.foo + 'b')
|
||||||
|
bar="a ${data.foo} b"
|
||||||
|
baz="a $!{data.foo} b"
|
||||||
|
nested="a $!{data.foo + 'nested ${data.bar}'} b">
|
||||||
|
</div>
|
||||||
17
test/fixtures/parseExpression/autotest/array/expected.json
vendored
Normal file
17
test/fixtures/parseExpression/autotest/array/expected.json
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 123
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
test/fixtures/parseExpression/autotest/array/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/array/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
['a', 'b', 123]
|
||||||
@ -11,31 +11,50 @@
|
|||||||
"name": "b"
|
"name": "b"
|
||||||
},
|
},
|
||||||
"right": {
|
"right": {
|
||||||
"type": "BinaryExpression",
|
"type": "LogicalExpression",
|
||||||
"left": {
|
"left": {
|
||||||
"type": "BinaryExpression",
|
"type": "BinaryExpression",
|
||||||
"left": {
|
"left": {
|
||||||
"type": "Identifier",
|
"type": "BinaryExpression",
|
||||||
"name": "a"
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "a"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "b"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"operator": "+",
|
"operator": ">",
|
||||||
"right": {
|
"right": {
|
||||||
"type": "Identifier",
|
"type": "Assignment",
|
||||||
"name": "b"
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "c"
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
"operator": "+="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"operator": ">",
|
"operator": "||",
|
||||||
"right": {
|
"right": {
|
||||||
"type": "Assignment",
|
"type": "ConditionalExpression",
|
||||||
"left": {
|
"test": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"name": "c"
|
"name": "isHidden"
|
||||||
},
|
},
|
||||||
"right": {
|
"consequent": {
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"value": 2
|
"value": "hidden"
|
||||||
},
|
},
|
||||||
"operator": "+="
|
"alternate": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "visible"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"operator": "="
|
"operator": "="
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
a = b = (a+b)>(c+=2)
|
a = b = (a+b)>(c+=2) || (isHidden ? 'hidden' : 'visible')
|
||||||
13
test/fixtures/parseExpression/autotest/new-args/expected.json
vendored
Normal file
13
test/fixtures/parseExpression/autotest/new-args/expected.json
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"type": "NewExpression",
|
||||||
|
"callee": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "Foo"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "abc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
test/fixtures/parseExpression/autotest/new-args/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/new-args/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
new Foo('abc')
|
||||||
16
test/fixtures/parseExpression/autotest/new-compound/expected.json
vendored
Normal file
16
test/fixtures/parseExpression/autotest/new-compound/expected.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"type": "NewExpression",
|
||||||
|
"callee": {
|
||||||
|
"type": "LogicalExpression",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "A"
|
||||||
|
},
|
||||||
|
"operator": "||",
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "B"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
1
test/fixtures/parseExpression/autotest/new-compound/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/new-compound/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
new (A || B)()
|
||||||
8
test/fixtures/parseExpression/autotest/new/expected.json
vendored
Normal file
8
test/fixtures/parseExpression/autotest/new/expected.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"type": "NewExpression",
|
||||||
|
"callee": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "Foo"
|
||||||
|
},
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
1
test/fixtures/parseExpression/autotest/new/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/new/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
new Foo()
|
||||||
27
test/fixtures/parseExpression/autotest/object/expected.json
vendored
Normal file
27
test/fixtures/parseExpression/autotest/object/expected.json
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"type": "ObjectExpression",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"type": "Property",
|
||||||
|
"key": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "hello"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "world"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Property",
|
||||||
|
"key": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "class"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
4
test/fixtures/parseExpression/autotest/object/input.txt
vendored
Normal file
4
test/fixtures/parseExpression/autotest/object/input.txt
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
hello: 'world',
|
||||||
|
"class": "foo"
|
||||||
|
}
|
||||||
11
test/fixtures/parseExpression/autotest/this/expected.json
vendored
Normal file
11
test/fixtures/parseExpression/autotest/this/expected.json
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"object": {
|
||||||
|
"type": "ThisExpression"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "foo"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
}
|
||||||
1
test/fixtures/parseExpression/autotest/this/input.txt
vendored
Normal file
1
test/fixtures/parseExpression/autotest/this/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
this.foo
|
||||||
@ -6,6 +6,7 @@
|
|||||||
"kind": "var",
|
"kind": "var",
|
||||||
"declarations": [
|
"declarations": [
|
||||||
{
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
"id": {
|
"id": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"name": "x"
|
"name": "x"
|
||||||
@ -16,6 +17,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
"id": {
|
"id": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"name": "y"
|
"name": "y"
|
||||||
@ -26,6 +28,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
"id": {
|
"id": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"name": "z"
|
"name": "z"
|
||||||
@ -44,6 +47,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
"id": {
|
"id": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"name": "unusedvar"
|
"name": "unusedvar"
|
||||||
|
|||||||
@ -13,16 +13,61 @@
|
|||||||
"kind": "var",
|
"kind": "var",
|
||||||
"declarations": [
|
"declarations": [
|
||||||
{
|
{
|
||||||
"id": "str",
|
"type": "VariableDeclarator",
|
||||||
"init": "__helpers.s"
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "str"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "__helpers"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "s"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "empty",
|
"type": "VariableDeclarator",
|
||||||
"init": "__helpers.e"
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "empty"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "__helpers"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "e"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "notEmpty",
|
"type": "VariableDeclarator",
|
||||||
"init": "__helpers.ne"
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "notEmpty"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "__helpers"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "ne"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"body": []
|
"body": []
|
||||||
@ -95,6 +140,7 @@
|
|||||||
"tagName": "ul",
|
"tagName": "ul",
|
||||||
"attributes": [
|
"attributes": [
|
||||||
{
|
{
|
||||||
|
"type": "HtmlAttribute",
|
||||||
"name": "class",
|
"name": "class",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
@ -134,6 +180,7 @@
|
|||||||
"tagName": "li",
|
"tagName": "li",
|
||||||
"attributes": [
|
"attributes": [
|
||||||
{
|
{
|
||||||
|
"type": "HtmlAttribute",
|
||||||
"name": "class",
|
"name": "class",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
|
|||||||
1
test/fixtures/render/autotest/attr-boolean-dynamic/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/attr-boolean-dynamic/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<select multiple><option value="red">Red</option><option value="green" selected>Green</option><option value="blue">Blue</option></select>
|
||||||
5
test/fixtures/render/autotest/attr-boolean-dynamic/template.marko
vendored
Normal file
5
test/fixtures/render/autotest/attr-boolean-dynamic/template.marko
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<select multiple>
|
||||||
|
<option value="red">Red</option>
|
||||||
|
<option value="green" selected=(data.isGreenSelected)>Green</option>
|
||||||
|
<option value="blue" selected=(data.isBlueSelected)>Blue</option>
|
||||||
|
</select>
|
||||||
4
test/fixtures/render/autotest/attr-boolean-dynamic/test.js
vendored
Normal file
4
test/fixtures/render/autotest/attr-boolean-dynamic/test.js
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
exports.templateData = {
|
||||||
|
isGreenSelected: true,
|
||||||
|
isBlueSelected: false
|
||||||
|
};
|
||||||
1
test/fixtures/render/autotest/attr-boolean-static/expected.html
vendored
Normal file
1
test/fixtures/render/autotest/attr-boolean-static/expected.html
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<select multiple><option value="red">Red</option><option value="green" selected>Green</option><option value="blue">Blue</option></select>
|
||||||
5
test/fixtures/render/autotest/attr-boolean-static/template.marko
vendored
Normal file
5
test/fixtures/render/autotest/attr-boolean-static/template.marko
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<select multiple>
|
||||||
|
<option value="red">Red</option>
|
||||||
|
<option value="green" selected=true>Green</option>
|
||||||
|
<option value="blue">Blue</option>
|
||||||
|
</select>
|
||||||
1
test/fixtures/render/autotest/attr-boolean-static/test.js
vendored
Normal file
1
test/fixtures/render/autotest/attr-boolean-static/test.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
exports.templateData = {};
|
||||||
@ -1 +1 @@
|
|||||||
<div data-encoding=""hello"" style="background-color: #FF0000; <test>" class="my-div">Hello World!</div>
|
<div data-encoding=""hello"" style="background-color: #FF0000; <test>" class="my-div" checked>Hello World!</div>
|
||||||
3
test/fixtures/render/autotest/attrs/test.js
vendored
3
test/fixtures/render/autotest/attrs/test.js
vendored
@ -1,6 +1,7 @@
|
|||||||
exports.templateData = {
|
exports.templateData = {
|
||||||
"myAttrs": {
|
"myAttrs": {
|
||||||
"style": "background-color: #FF0000; <test>",
|
"style": "background-color: #FF0000; <test>",
|
||||||
"class": "my-div"
|
"class": "my-div",
|
||||||
|
"checked": true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1 +1 @@
|
|||||||
<div class="null">Hello Frank! You are 30 years old.</div><div class="hidden">Hello John! You are 10 years old.</div><div class="hidden">Hello John! You are 10 years old.</div><div class="null">Hello John! You are 10 years old.</div>
|
<div>Hello Frank! You are 30 years old.</div><div class="hidden">Hello John! You are 10 years old.</div><div class="hidden">Hello John! You are 10 years old.</div><div>Hello John! You are 10 years old.</div>
|
||||||
@ -1,23 +0,0 @@
|
|||||||
function create(__helpers) {
|
|
||||||
var str = __helpers.s,
|
|
||||||
empty = __helpers.e,
|
|
||||||
notEmpty = __helpers.ne,
|
|
||||||
escapeXml = __helpers.x,
|
|
||||||
forEach = __helpers.f;
|
|
||||||
|
|
||||||
return function render(data, out) {
|
|
||||||
if (notEmpty(data.colors)) {
|
|
||||||
out.w("<ul class=\"colors\">");
|
|
||||||
|
|
||||||
forEach(data.colors, function(color) {
|
|
||||||
out.w("<li class=\"color\">" +
|
|
||||||
escapeXml(color) +
|
|
||||||
"</li>");
|
|
||||||
});
|
|
||||||
|
|
||||||
out.w("</ul>");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
(module.exports = require("marko").c(__filename)).c(create);
|
|
||||||
15
test/fixtures/transformAttrExpression/autotest/id-plus-id-plus-literal/expected.json
vendored
Normal file
15
test/fixtures/transformAttrExpression/autotest/id-plus-id-plus-literal/expected.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"suffix": "12",
|
||||||
|
"node": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "foo"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/fixtures/transformAttrExpression/autotest/id-plus-id-plus-literal/input.txt
vendored
Normal file
1
test/fixtures/transformAttrExpression/autotest/id-plus-id-plus-literal/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo+bar+'12'
|
||||||
7
test/fixtures/transformAttrExpression/autotest/id-plus-literal/expected.json
vendored
Normal file
7
test/fixtures/transformAttrExpression/autotest/id-plus-literal/expected.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"suffix": "12",
|
||||||
|
"node": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/fixtures/transformAttrExpression/autotest/id-plus-literal/input.txt
vendored
Normal file
1
test/fixtures/transformAttrExpression/autotest/id-plus-literal/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo+'12'
|
||||||
15
test/fixtures/transformAttrExpression/autotest/literal-plus-id-plus-id/expected.json
vendored
Normal file
15
test/fixtures/transformAttrExpression/autotest/literal-plus-id-plus-id/expected.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"prefix": "12",
|
||||||
|
"node": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "foo"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/fixtures/transformAttrExpression/autotest/literal-plus-id-plus-id/input.txt
vendored
Normal file
1
test/fixtures/transformAttrExpression/autotest/literal-plus-id-plus-id/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
'12'+foo+bar
|
||||||
7
test/fixtures/transformAttrExpression/autotest/literal-plus-id/expected.json
vendored
Normal file
7
test/fixtures/transformAttrExpression/autotest/literal-plus-id/expected.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"prefix": "12",
|
||||||
|
"node": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/fixtures/transformAttrExpression/autotest/literal-plus-id/input.txt
vendored
Normal file
1
test/fixtures/transformAttrExpression/autotest/literal-plus-id/input.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
'12'+foo
|
||||||
71
test/fixtures/walker/autotest/remove-attrs/expected.json
vendored
Normal file
71
test/fixtures/walker/autotest/remove-attrs/expected.json
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"type": "TemplateRoot",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "If",
|
||||||
|
"test": "notEmpty(data.colors)",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "HtmlElement",
|
||||||
|
"tagName": "ul",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"type": "HtmlAttribute",
|
||||||
|
"name": "class",
|
||||||
|
"value": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "colors"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ForEach",
|
||||||
|
"varName": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "color"
|
||||||
|
},
|
||||||
|
"in": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "data"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "colors"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "HtmlElement",
|
||||||
|
"tagName": "li",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"type": "HtmlAttribute",
|
||||||
|
"name": "class",
|
||||||
|
"value": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "color"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "Text",
|
||||||
|
"argument": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "color"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -35,7 +35,7 @@ module.exports = function(compiler) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let walker = compiler.createWalker({
|
let walker = compiler.createWalker({
|
||||||
visit: function(node) {
|
enter(node) {
|
||||||
if (node.type === 'HtmlElement') {
|
if (node.type === 'HtmlElement') {
|
||||||
if (node.hasAttribute('for')) {
|
if (node.hasAttribute('for')) {
|
||||||
node.wrap(forEach('color', 'data.colors'));
|
node.wrap(forEach('color', 'data.colors'));
|
||||||
52
test/fixtures/walker/autotest/replace/expected.json
vendored
Normal file
52
test/fixtures/walker/autotest/replace/expected.json
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"type": "TemplateRoot",
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "HtmlElement",
|
||||||
|
"tagName": "ul",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"type": "HtmlAttribute",
|
||||||
|
"name": "class",
|
||||||
|
"value": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "data"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "foo"
|
||||||
|
},
|
||||||
|
"computed": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "HtmlElement",
|
||||||
|
"tagName": "li",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"type": "HtmlAttribute",
|
||||||
|
"name": "class",
|
||||||
|
"value": {
|
||||||
|
"type": "Literal",
|
||||||
|
"value": "color"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "Text",
|
||||||
|
"argument": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "color"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
36
test/fixtures/walker/autotest/replace/index.js
vendored
Normal file
36
test/fixtures/walker/autotest/replace/index.js
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(compiler) {
|
||||||
|
let builder = compiler.createBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
let rootNode = builder.templateRoot([
|
||||||
|
builder.htmlElement(
|
||||||
|
'ul',
|
||||||
|
{
|
||||||
|
'class': 'escapeXml(data.foo)'
|
||||||
|
},
|
||||||
|
[
|
||||||
|
builder.htmlElement(
|
||||||
|
'li',
|
||||||
|
{
|
||||||
|
'class': builder.literal('color')
|
||||||
|
},
|
||||||
|
[
|
||||||
|
builder.text('color')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
let walker = compiler.createWalker({
|
||||||
|
enter(node) {
|
||||||
|
if (node.type === 'FunctionCall' && node.callee.type === 'Identifier' && node.callee.name === 'escapeXml') {
|
||||||
|
return node.args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
walker.walk(rootNode);
|
||||||
|
|
||||||
|
return rootNode;
|
||||||
|
};
|
||||||
@ -1,30 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var chai = require('chai');
|
|
||||||
chai.config.includeStack = true;
|
|
||||||
|
|
||||||
var path = require('path');
|
|
||||||
var compiler = require('../compiler');
|
|
||||||
var autotest = require('./autotest');
|
|
||||||
var builder = compiler.createBuilder();
|
|
||||||
var CompileContext = require('../compiler/CompileContext');
|
|
||||||
var CodeGenerator = require('../compiler/CodeGenerator');
|
|
||||||
|
|
||||||
function createGenerator() {
|
|
||||||
var context = new CompileContext('dummy', 'dummy.marko', builder);
|
|
||||||
return new CodeGenerator(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('compiler/transform', function() {
|
|
||||||
var autoTestDir = path.join(__dirname, 'fixtures/transform/autotest');
|
|
||||||
|
|
||||||
autotest.scanDir(autoTestDir, function run(dir) {
|
|
||||||
var getAST = require(path.join(dir, 'index.js'));
|
|
||||||
var ast = getAST(compiler);
|
|
||||||
|
|
||||||
var codeGenerator = createGenerator();
|
|
||||||
codeGenerator.generateCode(ast);
|
|
||||||
return codeGenerator.getCode();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
22
test/walker-test.js
Normal file
22
test/walker-test.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = require('chai');
|
||||||
|
chai.config.includeStack = true;
|
||||||
|
|
||||||
|
var path = require('path');
|
||||||
|
var compiler = require('../compiler');
|
||||||
|
var autotest = require('./autotest');
|
||||||
|
|
||||||
|
describe('compiler/walker', function() {
|
||||||
|
var autoTestDir = path.join(__dirname, 'fixtures/walker/autotest');
|
||||||
|
|
||||||
|
autotest.scanDir(autoTestDir, function run(dir) {
|
||||||
|
var getAST = require(path.join(dir, 'index.js'));
|
||||||
|
return getAST(compiler);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deepEqual: true,
|
||||||
|
compareExtension: '.json'
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user