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 Assignment = require('./ast/Assignment');
|
||||
var BinaryExpression = require('./ast/BinaryExpression');
|
||||
var LogicalExpression = require('./ast/LogicalExpression');
|
||||
var Vars = require('./ast/Vars');
|
||||
var Return = require('./ast/Return');
|
||||
var HtmlElement = require('./ast/HtmlElement');
|
||||
@ -32,6 +33,13 @@ var MemberExpression = require('./ast/MemberExpression');
|
||||
var Code = require('./ast/Code');
|
||||
var InvokeMacro = require('./ast/InvokeMacro');
|
||||
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');
|
||||
|
||||
@ -46,6 +54,22 @@ function makeNode(arg) {
|
||||
}
|
||||
|
||||
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) {
|
||||
if (operator == null) {
|
||||
operator = '=';
|
||||
@ -65,6 +89,10 @@ class Builder {
|
||||
return new Code({value});
|
||||
}
|
||||
|
||||
conditionalExpression(test, consequent, alternate) {
|
||||
return new ConditionalExpression({test, consequent, alternate});
|
||||
}
|
||||
|
||||
elseStatement(body) {
|
||||
return new Else({body});
|
||||
}
|
||||
@ -190,6 +218,12 @@ class Builder {
|
||||
return new Literal({value});
|
||||
}
|
||||
|
||||
logicalExpression(left, operator, right) {
|
||||
left = makeNode(left);
|
||||
right = makeNode(right);
|
||||
return new LogicalExpression({left, operator, right});
|
||||
}
|
||||
|
||||
macro(name, params, body) {
|
||||
return new Macro({name, params, body});
|
||||
}
|
||||
@ -209,6 +243,24 @@ class Builder {
|
||||
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) {
|
||||
if (typeof type === 'function') {
|
||||
generateCode = arguments[0];
|
||||
@ -222,10 +274,34 @@ class Builder {
|
||||
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) {
|
||||
return new Program({body});
|
||||
}
|
||||
|
||||
property(key, value) {
|
||||
key = makeNode(key);
|
||||
value = makeNode(value);
|
||||
|
||||
return new Property({key, value});
|
||||
}
|
||||
|
||||
renderBodyFunction(body) {
|
||||
let name = 'renderBody';
|
||||
let params = [new Identifier({name: 'out'})];
|
||||
@ -278,6 +354,10 @@ class Builder {
|
||||
return new Text({argument, escape});
|
||||
}
|
||||
|
||||
thisExpression() {
|
||||
return new ThisExpression();
|
||||
}
|
||||
|
||||
unaryExpression(argument, operator, prefix) {
|
||||
argument = makeNode(argument);
|
||||
|
||||
@ -289,19 +369,53 @@ class Builder {
|
||||
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) {
|
||||
if (declarations) {
|
||||
if (Array.isArray(declarations)) {
|
||||
for (let i=0; i<declarations.length; i++) {
|
||||
var declaration = declarations[i];
|
||||
if (!declaration) {
|
||||
throw new Error('Invalid variable declaration');
|
||||
}
|
||||
if (typeof declaration === 'string') {
|
||||
declarations[i] = {
|
||||
id: makeNode(declaration)
|
||||
};
|
||||
declarations[i] = new VariableDeclarator({
|
||||
id: new Identifier({name: declaration})
|
||||
});
|
||||
} else if (declaration instanceof Identifier) {
|
||||
declarations[i] = {
|
||||
declarations[i] = new VariableDeclarator({
|
||||
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') {
|
||||
@ -309,7 +423,7 @@ class Builder {
|
||||
declarations = Object.keys(declarations).map((key) => {
|
||||
let id = new Identifier({name: key});
|
||||
let init = makeNode(declarations[key]);
|
||||
return { id, init };
|
||||
return new VariableDeclarator({ id, init });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,6 +342,7 @@ class Generator {
|
||||
}
|
||||
|
||||
addWrite(output) {
|
||||
ok(output, '"output" is required');
|
||||
if (output instanceof Literal) {
|
||||
let lastWrite = this._bufferedWrites ?
|
||||
this._bufferedWrites[this._bufferedWrites.length-1] :
|
||||
|
||||
@ -19,6 +19,11 @@ class HtmlJsParser {
|
||||
|
||||
onattributeplaceholder(event) {
|
||||
// placeholder within attribute
|
||||
if (event.escape) {
|
||||
event.expression = 'escapeXml(' + event.expression + ')';
|
||||
} else {
|
||||
event.expression = 'noEscapeXml(' + event.expression + ')';
|
||||
}
|
||||
},
|
||||
|
||||
oncdata(event) {
|
||||
|
||||
@ -82,7 +82,7 @@ class Parser {
|
||||
|
||||
var tagName = el.tagName;
|
||||
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') {
|
||||
var compilerOptions = this.compilerOptions;
|
||||
|
||||
@ -2,41 +2,121 @@
|
||||
var isArray = Array.isArray;
|
||||
var Container = require('./ast/Container');
|
||||
|
||||
function noop() {}
|
||||
|
||||
class Walker {
|
||||
constructor(options) {
|
||||
this._visit = options.visit;
|
||||
this.transform = options.transform !== false;
|
||||
this._enter = options.enter || noop;
|
||||
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) {
|
||||
if (node == null) {
|
||||
return;
|
||||
if (!node || this._stopped || typeof node === 'string') {
|
||||
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)) {
|
||||
let nodes = node;
|
||||
let len = nodes.length;
|
||||
|
||||
for (var i=0; i<len; i++) {
|
||||
this.walk(nodes[i]);
|
||||
}
|
||||
let array = node;
|
||||
let newArray = this._walkArray(array);
|
||||
this._stack.pop();
|
||||
return newArray;
|
||||
} else if (node instanceof Container) {
|
||||
let container = node;
|
||||
if (this.transform) {
|
||||
container.safeForEach(this.walk, this);
|
||||
} else {
|
||||
container.forEach(this.walk, this);
|
||||
}
|
||||
|
||||
this._walkContainer(container);
|
||||
this._stack.pop();
|
||||
return container;
|
||||
} else {
|
||||
this._visit(node);
|
||||
|
||||
if (node.walkChildren) {
|
||||
node.walkChildren(this);
|
||||
} else if (node.forEachChild) {
|
||||
node.forEachChild(this.walk, this);
|
||||
if (node.walk) {
|
||||
node.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 {
|
||||
constructor(node, array) {
|
||||
super(node);
|
||||
if (array) {
|
||||
ok(isArray(array), 'Invalid array');
|
||||
|
||||
for (let i=0; i<array.length; i++) {
|
||||
array[i].container = this;
|
||||
}
|
||||
}
|
||||
this.array = array || [];
|
||||
this.items = 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) {
|
||||
var array = this.array.concat([]);
|
||||
for (var i=0; i<array.length; i++) {
|
||||
@ -104,6 +85,17 @@ class ArrayContainer extends Container {
|
||||
get items() {
|
||||
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;
|
||||
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() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -53,6 +53,11 @@ class BinaryExpression extends Node {
|
||||
right: this.right
|
||||
};
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.left = walker.walk(this.left);
|
||||
this.right = walker.walk(this.right);
|
||||
}
|
||||
}
|
||||
|
||||
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.write('\n');
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.body = walker.walk(this.body);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Else;
|
||||
@ -21,6 +21,12 @@ class ElseIf extends Node {
|
||||
codegen.write('else ');
|
||||
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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -90,7 +90,14 @@ class ForRange extends Node {
|
||||
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');
|
||||
}
|
||||
|
||||
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;
|
||||
@ -37,6 +37,11 @@ class FunctionCall extends Node {
|
||||
|
||||
codegen.write(')');
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.callee = walker.walk(this.callee);
|
||||
this.args = walker.walk(this.args);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FunctionCall;
|
||||
@ -61,6 +61,12 @@ class FunctionDeclaration extends Node {
|
||||
isCompoundExpression() {
|
||||
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;
|
||||
@ -16,6 +16,10 @@ class Html extends Node {
|
||||
let argument = this.argument;
|
||||
codegen.addWrite(argument);
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.argument = walker.walk(this.argument);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Html;
|
||||
@ -1,14 +1,160 @@
|
||||
'use strict';
|
||||
var Node = require('./Node');
|
||||
var Literal = require('./Literal');
|
||||
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) {
|
||||
ok(def, 'Invalid attribute definition');
|
||||
super('HtmlAttribute');
|
||||
|
||||
ok(def, 'Invalid attribute definition');
|
||||
this.type = 'HtmlAttribute';
|
||||
this.name = def.name.toLowerCase();
|
||||
this.value = def.value;
|
||||
|
||||
if (typeof this.value === 'string') {
|
||||
this.value = parseExpression(this.value);
|
||||
}
|
||||
|
||||
this.argument = def.argument;
|
||||
|
||||
this.def = def.def; // The attribute definition loaded from the taglib (if any)
|
||||
}
|
||||
|
||||
@ -25,6 +171,39 @@ class HtmlAttribute {
|
||||
return this.isLiteralValue() &&
|
||||
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) {
|
||||
|
||||
@ -7,35 +7,7 @@ var Node = require('./Node');
|
||||
|
||||
class HtmlAttributeCollection {
|
||||
constructor(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setAttributes(attributes);
|
||||
}
|
||||
|
||||
addAttribute(newAttr) {
|
||||
@ -135,6 +107,43 @@ class HtmlAttributeCollection {
|
||||
toString() {
|
||||
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;
|
||||
@ -16,6 +16,10 @@ class HtmlComment extends Node {
|
||||
codegen.addWrite(comment);
|
||||
codegen.addWrite(literal('-->'));
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.comment = walker.walk(this.comment);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HtmlComment;
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
var Node = require('./Node');
|
||||
var Literal = require('./Literal');
|
||||
var escapeXmlAttr = require('raptor-util/escapeXml').attr;
|
||||
var HtmlAttributeCollection = require('./HtmlAttributeCollection');
|
||||
|
||||
class StartTag extends Node {
|
||||
@ -33,31 +32,7 @@ class StartTag extends Node {
|
||||
if (attributes) {
|
||||
for (let i=0; i<attributes.length; i++) {
|
||||
let attr = attributes[i];
|
||||
let attrName = attr.name;
|
||||
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(')');
|
||||
}
|
||||
codegen.generateCode(attr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,12 +73,13 @@ class HtmlElement extends Node {
|
||||
this.tagNameExpression = null;
|
||||
this.setTagName(def.tagName);
|
||||
this._attributes = def.attributes;
|
||||
this.body = this.makeContainer(def.body);
|
||||
this.argument = def.argument;
|
||||
|
||||
if (!(this._attributes instanceof HtmlAttributeCollection)) {
|
||||
this._attributes = new HtmlAttributeCollection(this._attributes);
|
||||
}
|
||||
this.body = this.makeContainer(def.body);
|
||||
this.argument = def.argument;
|
||||
|
||||
this.allowSelfClosing = false;
|
||||
this.startTagOnly = false;
|
||||
this.dynamicAttributes = undefined;
|
||||
@ -190,7 +166,6 @@ class HtmlElement extends Node {
|
||||
codegen.generateCode(body);
|
||||
codegen.generateCode(endTag);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +229,8 @@ class HtmlElement extends Node {
|
||||
} else {
|
||||
this.tagNameExpression = tagName;
|
||||
}
|
||||
} else if (typeof tagName === 'string'){
|
||||
} else if (typeof tagName === 'string') {
|
||||
this.tagNameExpression = new Literal({value: tagName});
|
||||
this.tagName = tagName;
|
||||
}
|
||||
}
|
||||
@ -274,6 +250,12 @@ class HtmlElement extends Node {
|
||||
setBodyOnlyIf(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;
|
||||
@ -77,6 +77,12 @@ class If extends Node {
|
||||
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;
|
||||
@ -110,6 +110,13 @@ class InvokeMacro extends Node {
|
||||
|
||||
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;
|
||||
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;
|
||||
return builder.functionDeclaration(functionName, macroDef.params, body);
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.body = walker.walk(this.body);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Macro;
|
||||
@ -35,6 +35,11 @@ class MemberExpression extends Node {
|
||||
computed: this.computed
|
||||
};
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.object = walker.walk(this.object);
|
||||
this.property = walker.walk(this.property);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.body = walker.walk(this.body);
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.argument = walker.walk(this.argument);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Return;
|
||||
@ -22,6 +22,12 @@ class SelfInvokingFunction extends Node {
|
||||
|
||||
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;
|
||||
@ -64,6 +64,10 @@ class TemplateRoot extends Node {
|
||||
body: this.body
|
||||
};
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.body = walker.walk(this.body);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.argument = walker.walk(this.argument);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UnaryExpression;
|
||||
@ -49,6 +49,10 @@ class UpdateExpression extends Node {
|
||||
prefix: this.prefix
|
||||
};
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.argument = walker.walk(this.argument);
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
var Node = require('./Node');
|
||||
var Identifier = require('./Identifier');
|
||||
|
||||
class Vars extends Node {
|
||||
constructor(def) {
|
||||
@ -29,7 +28,7 @@ class Vars extends Node {
|
||||
}
|
||||
|
||||
for (let i=0; i<declarations.length; i++) {
|
||||
var declaration = declarations[i];
|
||||
var declarator = declarations[i];
|
||||
|
||||
if (i === 0) {
|
||||
codegen.write(kind + ' ');
|
||||
@ -38,26 +37,7 @@ class Vars extends Node {
|
||||
codegen.writeLineIndent();
|
||||
}
|
||||
|
||||
var varId = declaration.id || declaration.name;
|
||||
|
||||
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);
|
||||
}
|
||||
codegen.generateCode(declarator);
|
||||
|
||||
if (i !== 0) {
|
||||
codegen.decIndent(4);
|
||||
@ -75,6 +55,10 @@ class Vars extends Node {
|
||||
codegen.generateCode(body);
|
||||
}
|
||||
}
|
||||
|
||||
walk(walker) {
|
||||
this.argument = walker.walk(this.argument);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Vars;
|
||||
@ -7,42 +7,25 @@ var compiler = require('../');
|
||||
function convert(node) {
|
||||
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) {
|
||||
case 'Program': {
|
||||
if (node.body && node.body.length === 1) {
|
||||
return convert(node.body[0]);
|
||||
}
|
||||
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) {
|
||||
case 'ArrayExpression': {
|
||||
let elements = convert(node.elements);
|
||||
if (!elements) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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);
|
||||
return builder.arrayExpression(elements);
|
||||
}
|
||||
case 'AssignmentExpression': {
|
||||
let left = convert(node.left);
|
||||
@ -57,6 +40,99 @@ function convert(node) {
|
||||
|
||||
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': {
|
||||
let object = convert(node.object);
|
||||
if (!object) {
|
||||
@ -70,13 +146,62 @@ function convert(node) {
|
||||
|
||||
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:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseExpression(src) {
|
||||
let jsAST = esprima.parse(src);
|
||||
let jsAST = esprima.parse('(' + src + ')');
|
||||
var converted = convert(jsAST);
|
||||
if (converted == null) {
|
||||
converted = new Expression({value: src});
|
||||
|
||||
@ -63,6 +63,8 @@ if (this.container) {
|
||||
|
||||
## methods
|
||||
|
||||
### arrayExpression(elements)
|
||||
|
||||
### assignment(left, right)
|
||||
|
||||
Returns a node that generates the following code:
|
||||
@ -87,7 +89,7 @@ foo = '123';
|
||||
Returns a node that generates the following code:
|
||||
|
||||
```javascript
|
||||
<left> <operator> <right>;
|
||||
<left> <operator> <right>
|
||||
```
|
||||
|
||||
For example:
|
||||
@ -121,6 +123,26 @@ var b = 2;
|
||||
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)
|
||||
|
||||
Returns a node that generates the following code:
|
||||
@ -499,6 +521,8 @@ var aString = "abc",
|
||||
]
|
||||
```
|
||||
|
||||
### logicalExpression(left, operator, right)
|
||||
|
||||
### 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.
|
||||
@ -520,6 +544,8 @@ builder.negate(builder.identifier('foo'))
|
||||
!foo
|
||||
```
|
||||
|
||||
### newExpression(callee, args)
|
||||
|
||||
### 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.
|
||||
@ -536,6 +562,8 @@ builder.node(function(node, generator) {
|
||||
out.w("Hello World!");
|
||||
```
|
||||
|
||||
### objectExpression(properties)
|
||||
|
||||
### program(body)
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
### property(key, value)
|
||||
|
||||
### require(path)
|
||||
|
||||
Returns a node that generates the following code:
|
||||
@ -701,10 +731,14 @@ a === b
|
||||
|
||||
### text(argument, escape)
|
||||
|
||||
### thisExpression()
|
||||
|
||||
### unaryExpression(argument, operator, prefix)
|
||||
|
||||
### updateExpression(argument, operator, prefix)
|
||||
|
||||
### variableDeclarator(id, init)
|
||||
|
||||
### vars(declarations, kind)
|
||||
|
||||
# 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 (!(!data.url)) {
|
||||
out.w("<a href=\"" +
|
||||
data.url +
|
||||
"\">");
|
||||
out.w("<a" +
|
||||
attr("href", data.url) +
|
||||
">");
|
||||
}
|
||||
|
||||
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"
|
||||
},
|
||||
"right": {
|
||||
"type": "BinaryExpression",
|
||||
"type": "LogicalExpression",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "a"
|
||||
"type": "BinaryExpression",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "a"
|
||||
},
|
||||
"operator": "+",
|
||||
"right": {
|
||||
"type": "Identifier",
|
||||
"name": "b"
|
||||
}
|
||||
},
|
||||
"operator": "+",
|
||||
"operator": ">",
|
||||
"right": {
|
||||
"type": "Identifier",
|
||||
"name": "b"
|
||||
"type": "Assignment",
|
||||
"left": {
|
||||
"type": "Identifier",
|
||||
"name": "c"
|
||||
},
|
||||
"right": {
|
||||
"type": "Literal",
|
||||
"value": 2
|
||||
},
|
||||
"operator": "+="
|
||||
}
|
||||
},
|
||||
"operator": ">",
|
||||
"operator": "||",
|
||||
"right": {
|
||||
"type": "Assignment",
|
||||
"left": {
|
||||
"type": "ConditionalExpression",
|
||||
"test": {
|
||||
"type": "Identifier",
|
||||
"name": "c"
|
||||
"name": "isHidden"
|
||||
},
|
||||
"right": {
|
||||
"consequent": {
|
||||
"type": "Literal",
|
||||
"value": 2
|
||||
"value": "hidden"
|
||||
},
|
||||
"operator": "+="
|
||||
"alternate": {
|
||||
"type": "Literal",
|
||||
"value": "visible"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"declarations": [
|
||||
{
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "x"
|
||||
@ -16,6 +17,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "y"
|
||||
@ -26,6 +28,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "z"
|
||||
@ -44,6 +47,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "unusedvar"
|
||||
|
||||
@ -13,16 +13,61 @@
|
||||
"kind": "var",
|
||||
"declarations": [
|
||||
{
|
||||
"id": "str",
|
||||
"init": "__helpers.s"
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "str"
|
||||
},
|
||||
"init": {
|
||||
"type": "MemberExpression",
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"name": "__helpers"
|
||||
},
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"name": "s"
|
||||
},
|
||||
"computed": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "empty",
|
||||
"init": "__helpers.e"
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "empty"
|
||||
},
|
||||
"init": {
|
||||
"type": "MemberExpression",
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"name": "__helpers"
|
||||
},
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"name": "e"
|
||||
},
|
||||
"computed": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "notEmpty",
|
||||
"init": "__helpers.ne"
|
||||
"type": "VariableDeclarator",
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"name": "notEmpty"
|
||||
},
|
||||
"init": {
|
||||
"type": "MemberExpression",
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"name": "__helpers"
|
||||
},
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"name": "ne"
|
||||
},
|
||||
"computed": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"body": []
|
||||
@ -95,6 +140,7 @@
|
||||
"tagName": "ul",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "HtmlAttribute",
|
||||
"name": "class",
|
||||
"value": {
|
||||
"type": "Literal",
|
||||
@ -134,6 +180,7 @@
|
||||
"tagName": "li",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "HtmlAttribute",
|
||||
"name": "class",
|
||||
"value": {
|
||||
"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 = {
|
||||
"myAttrs": {
|
||||
"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({
|
||||
visit: function(node) {
|
||||
enter(node) {
|
||||
if (node.type === 'HtmlElement') {
|
||||
if (node.hasAttribute('for')) {
|
||||
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