Various cleanup

This commit is contained in:
Patrick Steele-Idem 2015-12-11 09:08:27 -07:00
parent eb77d3265d
commit d621ef13df
43 changed files with 509 additions and 170 deletions

0
bin/markoc Normal file → Executable file
View File

View File

@ -1,6 +1,7 @@
'use strict';
var isArray = Array.isArray;
var Node = require('./ast/Node');
var Program = require('./ast/Program');
var TemplateRoot = require('./ast/TemplateRoot');
var FunctionDeclaration = require('./ast/FunctionDeclaration');
@ -15,19 +16,24 @@ var BinaryExpression = require('./ast/BinaryExpression');
var Vars = require('./ast/Vars');
var Return = require('./ast/Return');
var HtmlElement = require('./ast/HtmlElement');
var HtmlOutput = require('./ast/HtmlOutput');
var TextOutput = require('./ast/TextOutput');
var Html = require('./ast/Html');
var Text = require('./ast/Text');
var ForEach = require('./ast/ForEach');
var Slot = require('./ast/Slot');
var HtmlComment = require('./ast/HtmlComment');
var SelfInvokingFunction = require('./ast/SelfInvokingFunction');
var ForStatement = require('./ast/ForStatement');
var BinaryExpression = require('./ast/BinaryExpression');
class Builder {
program(body) {
return new Program({body});
}
node(type) {
return new Node(type);
}
templateRoot(body) {
return new TemplateRoot({body});
}
@ -109,12 +115,12 @@ class Builder {
return new HtmlElement({tagName, attributes, body, argument});
}
htmlOutput(argument) {
return new HtmlOutput({argument});
html(argument) {
return new Html({argument});
}
textOutput(argument, escape) {
return new TextOutput({argument, escape});
text(argument, escape) {
return new Text({argument, escape});
}
htmlComment(comment) {
@ -148,6 +154,10 @@ class Builder {
return new ForStatement({init, test, update, body});
}
}
binaryExpression(left, operator, right) {
return new BinaryExpression({left, operator, right});
}
}
module.exports = Builder;

View File

@ -86,7 +86,7 @@ class Generator {
ok(this.builder, '"this.builder" is required');
this._generatorCodeFuncName = 'generate' +
this._generatorCodeMethodName = 'generate' +
this.outputType.charAt(0).toUpperCase() +
this.outputType.substring(1) +
'Code';
@ -121,25 +121,48 @@ class Generator {
return;
}
var generateCodeFunc = node.generateCode;
if (!generateCodeFunc) {
generateCodeFunc = node[this._generatorCodeFuncName];
if (!generateCodeFunc) {
throw new Error('Missing generator method for node of type "' +
node.type +
'". Node: ' + util.inspect(node));
}
}
var oldCurrentNode = this._currentNode;
this._currentNode = node;
var resultNode = generateCodeFunc.call(node, this);
if (resultNode) {
// The generateCode function can optionally return either of the following:
// - An AST node
// - An array/cointainer of AST nodes
this.generateCode(resultNode);
var finalNode;
if (node.getCodeGenerator) {
let generateCodeFunc = node.getCodeGenerator(this.outputType);
if (generateCodeFunc) {
finalNode = generateCodeFunc(node, this);
}
}
if (finalNode && finalNode !== node) {
this.generateCode(finalNode);
} else {
let generateCodeMethod = node.generateCode;
if (!generateCodeMethod) {
generateCodeMethod = node[this._generatorCodeMethodName];
if (!generateCodeMethod) {
throw new Error('Missing code for node of type "' +
node.type +
'" (output type: "' + this.outputType + '"). Node: ' + util.inspect(node));
}
}
finalNode = generateCodeMethod.call(node, this);
if (finalNode != null) {
if (finalNode === node) {
throw new Error('Invalid node returned. Same node returned: ' + util.inspect(node));
}
// The generateCode function can optionally return either of the following:
// - An AST node
// - An array/cointainer of AST nodes
this.generateCode(finalNode);
}
}
this._currentNode = oldCurrentNode;

View File

@ -77,7 +77,8 @@ class Compiler {
var context = new CompileContext(src, filename, this.builder);
var ast = this.parser.parse(src, context);
// console.log('ROOT', JSON.stringify(ast, null, 2));
console.log('ROOT', JSON.stringify(ast, null, 2));
var transformedAST = transformTree(ast, context);
// console.log('transformedAST', JSON.stringify(ast, null, 2));

View File

@ -1,6 +1,7 @@
'use strict';
var ok = require('assert').ok;
var path = require('path');
var Node = require('./ast/Node');
var COMPILER_ATTRIBUTE_HANDLERS = {
whitespace: function(attr, compilerOptions) {
@ -83,7 +84,7 @@ class Parser {
if (this.prevTextNode && this.prevTextNode.isLiteral()) {
this.prevTextNode.appendText(text);
} else {
this.prevTextNode = builder.textOutput(builder.literal(text));
this.prevTextNode = builder.text(builder.literal(text));
this.prevTextNode.pos = text.pos;
this.parentNode.appendChild(this.prevTextNode);
}
@ -152,6 +153,9 @@ class Parser {
var nodeFactoryFunc = tagDef.getNodeFactory();
if (nodeFactoryFunc) {
node = nodeFactoryFunc(elNode, context);
if (!(node instanceof Node)) {
throw new Error('Invalid node returned from node factory for tag "' + tagName + '".');
}
}
}
@ -247,8 +251,8 @@ class Parser {
var builder = this.context.builder;
var textOutput = builder.textOutput(expression, escape);
this.parentNode.appendChild(textOutput);
var text = builder.text(expression, escape);
this.parentNode.appendChild(text);
}
get parentNode() {

View File

@ -6,18 +6,46 @@ class BinaryExpression extends Node {
constructor(def) {
super('BinaryExpression');
this.left = def.left;
this.right = def.right;
this.operator = def.operator;
this.right = def.right;
this.parens = def.parens === true;
}
generateCode(generator) {
var left = this.left;
var right = this.right;
var operator = this.operator;
var parens = this.parens || this.data.isSubExpression;
generator.generateCode(left);
generator.write(' ' + operator + ' ');
generator.generateCode(right);
if (left instanceof Node) {
left.data.isSubExpression = true;
}
if (right instanceof Node) {
right.data.isSubExpression = true;
}
if (parens) {
generator.write('(');
}
generator.generateCode(this.left);
generator.write(' ');
generator.generateCode(this.operator);
generator.write(' ');
generator.generateCode(this.right);
if (parens) {
generator.write(')');
}
}
toJSON() {
return {
type: 'BinaryExpression',
left: this.left,
operator: this.operator,
right: this.right
};
}
}

View File

@ -77,7 +77,7 @@ class ForEach extends Node {
if (separator) {
body = body.items.concat([
builder.ifStatement('!' + statusVarName + '.isLast()', [
builder.textOutput(separator)
builder.text(separator)
])
]);
}

View File

@ -2,9 +2,9 @@
var Node = require('./Node');
class HtmlOutput extends Node {
class Html extends Node {
constructor(def) {
super('HtmlOutput');
super('Html');
this.argument = def.argument;
}
@ -19,4 +19,4 @@ class HtmlOutput extends Node {
}
}
module.exports = HtmlOutput;
module.exports = Html;

View File

@ -112,6 +112,18 @@ class HtmlAttributeCollection {
return this.lookup[name];
}
setAttributeValue(name, value) {
var attr = this.getAttribute(name);
if (attr) {
attr.value = value;
} else {
this.addAttribute({
name: name,
value: value
});
}
}
getAttributes() {
return this.all;
}

View File

@ -122,6 +122,10 @@ class HtmlElement extends Node {
}
}
setAttributeValue(name, value) {
this._attributes.setAttributeValue(name, value);
}
removeAttribute(name) {
if (this._attributes) {
this._attributes.removeAttribute(name);
@ -152,6 +156,15 @@ class HtmlElement extends Node {
var tagName = this.tagName;
return '<' + tagName + '>';
}
toJSON() {
return {
type: this.type,
tagName: this.tagName,
attributes: this._attributes,
argument: this.argument
};
}
}
module.exports = HtmlElement;

View File

@ -45,7 +45,7 @@ class If extends Node {
previous = curNode;
curNode.matched = true;
return true; // Keep searching since they may be more ElseIf/Else nodes...
} else if (curNode.type === 'TextOutput') {
} else if (curNode.type === 'Text') {
if (curNode.isWhitespace()) {
whitespaceNodes.push(curNode);
return true; // Just whitespace... keep searching

View File

@ -11,7 +11,10 @@ class Node {
this.statement = false;
this.container = null;
this.pos = null; // The character index of the node in the original source file
this.transformersApplied = {};
this._codeGeneratorFuncs = null;
this._flags = {};
this._transformersApplied = {};
this.data = {};
}
wrap(wrapperNode) {
@ -49,11 +52,11 @@ class Node {
}
isTransformerApplied(transformer) {
return this.transformersApplied[transformer.id] === true;
return this._transformersApplied[transformer.id] === true;
}
setTransformerApplied(transformer) {
this.transformersApplied[transformer.id] = true;
this._transformersApplied[transformer.id] = true;
}
toString() {
@ -65,7 +68,10 @@ class Node {
delete result.container;
delete result.statement;
delete result.pos;
delete result.transformersApplied;
delete result._transformersApplied;
delete result._codeGeneratorFuncs;
delete result._flags;
delete result.data;
return result;
}
@ -87,6 +93,46 @@ class Node {
// We inspect in the simplified version of this object t
return this.toJSON();
}
setType(newType) {
this.type = newType;
}
setCodeGenerator(mode, codeGeneratorFunc) {
if (arguments.length === 1) {
codeGeneratorFunc = arguments[0];
mode = null;
}
if (!this._codeGeneratorFuncs) {
this._codeGeneratorFuncs = {};
}
this._codeGeneratorFuncs[mode || 'DEFAULT'] = codeGeneratorFunc;
}
getCodeGenerator(mode) {
if (this._codeGeneratorFuncs) {
return this._codeGeneratorFuncs[mode] || this._codeGeneratorFuncs.DEFAULT;
} else {
return undefined;
}
}
setFlag(name) {
this._flags[name] = true;
}
clearFlag(name) {
delete this._flags[name];
}
isFlagSet(name) {
return this._flags.hasOwnProperty(name);
}
get parentNode() {
return this.container && this.container.node;
}
}
module.exports = Node;

View File

@ -3,10 +3,10 @@
var Node = require('./Node');
var Literal = require('./Literal');
function trim(textOutputNode) {
var text = textOutputNode.argument.value;
var isFirst = textOutputNode.isFirst;
var isLast = textOutputNode.isLast;
function trim(textNode) {
var text = textNode.argument.value;
var isFirst = textNode.isFirst;
var isLast = textNode.isLast;
if (isFirst) {
//First child
@ -21,12 +21,12 @@ function trim(textOutputNode) {
text = '';
}
text = text.replace(/\s+/g, ' ');
textOutputNode.argument.value = text;
textNode.argument.value = text;
}
class TextOutput extends Node {
class Text extends Node {
constructor(def) {
super('TextOutput');
super('Text');
this.argument = def.argument;
this.escape = def.escape;
this.normalized = false;
@ -80,11 +80,11 @@ class TextOutput extends Node {
return;
}
if (curChild.type === 'TextOutput') {
if (curChild.type === 'Text') {
curChild.normalized = true;
}
if (curChild.type === 'TextOutput' && curChild.isLiteral()) {
if (curChild.type === 'Text' && curChild.isLiteral()) {
if (currentTextLiteral) {
currentTextLiteral.argument.value += curChild.argument.value;
curChild.detach();
@ -121,7 +121,7 @@ class TextOutput extends Node {
appendText(text) {
if (!this.isLiteral()) {
throw new Error('Text cannot be appended to a non-literal TextOutput node');
throw new Error('Text cannot be appended to a non-literal Text node');
}
this.argument.value += text;
@ -135,4 +135,4 @@ class TextOutput extends Node {
}
}
module.exports = TextOutput;
module.exports = Text;

View File

@ -15,9 +15,7 @@
*/
'use strict';
var forEachEntry = require('raptor-util/forEachEntry');
var extend = require('raptor-util/extend');
var ok = require('assert').ok;
var HtmlElement = require('../../ast/HtmlElement');
var CustomTag = require('../../ast/CustomTag');
function inheritProps(sub, sup) {
@ -28,25 +26,6 @@ function inheritProps(sub, sup) {
});
}
function createNodeFactory(codeGenerator, typeName) {
class CustomNode extends HtmlElement {
constructor(options) {
super(options);
this.type = typeName;
if (this.init) {
this.init(options);
}
}
}
extend(CustomNode.prototype, codeGenerator);
return function nodeFactory(el) {
return new CustomNode(el);
};
}
function createCustomTagNodeFactory(tag) {
return function nodeFactory(el) {
return new CustomTag(el, tag);
@ -224,9 +203,15 @@ class Tag{
return nodeFactory;
}
let codeGeneratorModulePath = this.codeGeneratorModulePath;
if (this.codeGeneratorModulePath) {
var loadedCodeGeneratorModule = require(this.codeGeneratorModulePath);
nodeFactory = createNodeFactory(loadedCodeGeneratorModule, this.codeGeneratorModulePath);
var loadedCodeGenerator = require(this.codeGeneratorModulePath);
nodeFactory = function(elNode) {
elNode.setType(codeGeneratorModulePath);
elNode.setCodeGenerator(loadedCodeGenerator);
return elNode;
};
} else if (this.nodeFactoryPath) {
nodeFactory = require(this.nodeFactoryPath);
if (typeof nodeFactory !== 'function') {
@ -240,6 +225,10 @@ class Tag{
return (this._nodeFactory = nodeFactory);
}
toJSON() {
return this;
}
}
module.exports = Tag;

View File

@ -1,12 +1,12 @@
/*
* Copyright 2011 eBay Software Foundation
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -71,6 +71,8 @@ module.exports = function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, ta
var rendererFile = nodePath.join(dir, childFilename, 'renderer.js');
var indexFile = nodePath.join(dir, childFilename, 'index.js');
var templateFile = nodePath.join(dir, childFilename, 'template.marko');
var codeGeneratorFile = nodePath.join(dir, childFilename, 'code-generator.js');
var nodeFactoryFile = nodePath.join(dir, childFilename, 'node-factory.js');
var tagDef = null;
// Record dependencies so that we can check if a template is up-to-date
@ -86,7 +88,7 @@ module.exports = function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, ta
throw new Error('Unable to parse JSON file at path "' + tagFile + '". Error: ' + e);
}
if (!tagDef.renderer && !tagDef.template) {
if (!tagDef.renderer && !tagDef.template && !tagDef['code-generator'] && !tagDef['node-factory']) {
if (fs.existsSync(rendererFile)) {
tagDef.renderer = rendererFile;
} else if (fs.existsSync(indexFile)) {
@ -95,8 +97,12 @@ module.exports = function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, ta
tagDef.template = templateFile;
} else if (fs.existsSync(templateFile + ".html")) {
tagDef.template = templateFile + ".html";
} else if (fs.existsSync(codeGeneratorFile)) {
tagDef['code-generator'] = codeGeneratorFile;
} else if (fs.existsSync(nodeFactoryFile)) {
tagDef['node-factory'] = nodeFactoryFile;
} else {
throw new Error('Invalid tag file: ' + tagFile + '. Neither a renderer or a template was found for tag.');
throw new Error('Invalid tag file: ' + tagFile + '. Neither a renderer or a template was found for tag. ' + JSON.stringify(tagDef, null, 2));
}
}

View File

@ -19,7 +19,7 @@ var ok = require('assert').ok;
var Taglib = require('../taglib-loader/Taglib');
var extend = require('raptor-util/extend');
var HtmlElement = require('../ast/HtmlElement');
var TextOutput = require('../ast/TextOutput');
var Text = require('../ast/Text');
function transformerComparator(a, b) {
a = a.priority;
@ -223,7 +223,7 @@ class TaglibLookup {
*/
if (node instanceof HtmlElement) {
this.forEachTagTransformer(node, callback, thisObj);
} else if (node instanceof TextOutput) {
} else if (node instanceof Text) {
this.forEachTextTransformer(callback, thisObj);
}
}

66
docs/compile-time-tags.md Normal file
View File

@ -0,0 +1,66 @@
Compile-time Tags
=================
Marko allows developers to control how a custom tag generates JavaScript code at compile-time. A custom compile-time tag developer can provide a "code generator" function for a custom tag.
# Custom tag code generator
Let's take a look at how to create a compile-time tag by providing our own code generator. The first step is to register the custom tag in a `marko-taglib.json` file. We will use the `code-generator` property to associate the custom tag with a function that will be used to generate the code for the element node at compile-time:
```json
{
"<greeting>": {
"code-generator": "./greeting-tag"
}
}
```
A code generator module should export a function with the following signature:
```javascript
function generateCode(elNode, generator)
```
Continuing with the `<greeting>`, let's assume we have the following template:
```xml
<greeting name="Frank"/>
```
Let's implement the greeting tag that generates code by return an array of output AST nodes.
```javascript
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
var nameValue = elNode.getAttributeValue('name');
return builder.functionCall('console.log', nameValue);
};
```
The above code results in the following compiled code:
```javascript
out.w("Hello FrankHello " +
escapeXml(data.name));
```
```javascript
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return [
builder.text(builder.literal('Hello Frank')),
builder.text(elNode.getAttributeValue('name'))
];
};
```
So when should you a custom compile-time tag and when should use use a compile-time transformer?
Utilize custom compile-time tags when you need to create new custom tags that are capable of generating JavaScript code at compile-time. Examples of custom compile-time tags:
-
Compile-time transformers are useful for mod

140
docs/compiler-advanced.md Normal file
View File

@ -0,0 +1,140 @@
Compiler Advanced
====================
Marko allows developers to control how templates generate JavaScript code at compile-time. Developers can create custom compile-time tags and it is also possible to transform the intermediate parse tree by adding, removing, modifying or rearranging nodes at compilation time.
# Overview
The three primary stages of the Marko compiler are parse, transform, generate. Each of these stages is described in more detail below:
## Parse stage
The first stage of the Marko compiler takes the source template string and produces an Abstract Syntax Tree (AST).
For example, given the following input template:
```xml
Hello ${data.name}!
<ul if(notEmpty(data.colors))>
<li for(color in data.colors)>
${color}
</li>
</ul>
<div else>
No colors!
</div>
```
The following AST will be produced:
```json
{
"type": "TemplateRoot",
"body": [
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "Hello "
}
},
{
"type": "Text",
"argument": "data.name"
},
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "!\n\n"
}
},
{
"type": "HtmlElement",
"tagName": "ul",
"attributes": [
{
"name": "if",
"argument": "notEmpty(data.colors)"
}
]
},
{
"type": "Text",
"argument": {
"type": "Literal",
"value": "\n"
}
},
{
"type": "HtmlElement",
"tagName": "div",
"attributes": [
{
"name": "else"
}
]
}
]
}
```
# Creating a compile-time tag
Let's take a look at how to create a compile-time tag by providing our own code generator. The first step is to register the custom tag in a `marko-taglib.json` file. We will use the `code-generator` property to associate the custom tag with a function that will be used to generate the code for the element node at compile-time:
```json
{
"<greeting>": {
"code-generator": "./greeting-tag"
}
}
```
The code generator module should export a function with the following signature:
```javascript
function generateCode(elNode, generator) : Node
```
Continuing with the `<greeting>`, let's assume we have the following template:
```xml
<greeting name="Frank"/>
```
Let's implement the greeting tag that generates code that uses `console.log` to output `Hello <NAME>` as shown below:
```javascript
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return [
builder.text(builder.literal('Hello Frank')),
builder.text(elNode.getAttributeValue('name'))
];
};
```
```javascript
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return [
builder.text(builder.literal('Hello Frank')),
builder.text(elNode.getAttributeValue('name'))
];
};
```
So when should you a custom compile-time tag and when should use use a compile-time transformer?
Utilize custom compile-time tags when you need to create new custom tags that are capable of generating JavaScript code at compile-time. Examples of custom compile-time tags:
-
Compile-time transformers are useful for mod

View File

@ -1,22 +1,22 @@
var parseForEach = require('./util/parseForEach');
exports.generateCode = function(generator) {
var argument = this.argument;
module.exports = function nodeFactory(elNode, context) {
var argument = elNode.argument;
if (!argument) {
generator.addError('Invalid <for> tag. Argument is missing. Example; <for(color in colors)>');
return;
context.addError(elNode, 'Invalid <for> tag. Argument is missing. Example; <for(color in colors)>');
return elNode;
}
var forEachProps = parseForEach(argument);
forEachProps.body = this.body;
forEachProps.body = elNode.body;
if (this.hasAttribute('separator')) {
forEachProps.separator = this.getAttributeValue('separator');
if (elNode.hasAttribute('separator')) {
forEachProps.separator = elNode.getAttributeValue('separator');
}
if (this.hasAttribute('status-var')) {
forEachProps.statusVarName = this.getAttributeValue('status-var');
if (elNode.hasAttribute('status-var')) {
forEachProps.statusVarName = elNode.getAttributeValue('status-var');
}
return generator.builder.forEach(forEachProps);
return context.builder.forEach(forEachProps);
};

View File

@ -1,16 +1,17 @@
module.exports = function nodeFactory(el, context) {
var argument = el.argument;
var attributes = el.attributes;
var ifStatement = context.builder.ifStatement(argument || 'INVALID');
module.exports = function nodeFactory(elNode, context) {
var argument = elNode.argument;
if (!argument) {
context.addError(ifStatement, 'Invalid <if> tag. Argument is missing. Example; <if(foo === true)>');
context.addError(elNode, 'Invalid <if> tag. Argument is missing. Example; <if(foo === true)>');
return elNode;
}
var attributes = elNode.attributes;
if (attributes.length) {
context.addError(ifStatement, 'Invalid <if> tag. Attributes not allowed.');
context.addError(elNode, 'Invalid <if> tag. Attributes not allowed.');
return;
}
return ifStatement;
return context.builder.ifStatement(argument);
};

View File

@ -1,6 +1,6 @@
{
"<for>": {
"code-generator": "./for-tag"
"node-factory": "./for-tag"
},
"<if>": {
"node-factory": "./if-tag"

View File

@ -2,14 +2,9 @@
module.exports = function(builder) {
var templateRoot = builder.templateRoot;
var literal = builder.literal;
var functionDeclaration = builder.functionDeclaration;
var returnStatement = builder.returnStatement;
var functionCall = builder.functionCall;
var ifStatement = builder.ifStatement;
var vars = builder.vars;
var textOutput = builder.textOutput;
var htmlOutput = builder.htmlOutput;
var text = builder.text;
var htmlElement = builder.htmlElement;
return templateRoot([
@ -25,7 +20,7 @@ module.exports = function(builder) {
'li',
[],
[
textOutput('color')
text('color')
]),
functionCall('bar')
])

View File

@ -3,11 +3,11 @@
module.exports = function(builder) {
var templateRoot = builder.templateRoot;
var forEach = builder.forEach;
var textOutput = builder.textOutput;
var text = builder.text;
return templateRoot([
forEach('color', 'data.colors', [
textOutput('color')
text('color')
])
]);
};

View File

@ -6,14 +6,14 @@ module.exports = function(builder) {
var functionDeclaration = builder.functionDeclaration;
var functionCall = builder.functionCall;
var ifStatement = builder.ifStatement;
var textOutput = builder.textOutput;
var htmlOutput = builder.htmlOutput;
var text = builder.text;
var html = builder.html;
var htmlElement = builder.htmlElement;
var rootNode = templateRoot([
textOutput(literal('Hello')),
htmlOutput('data.name'),
textOutput(literal('!')),
text(literal('Hello')),
html('data.name'),
text(literal('!')),
ifStatement(
functionCall('notEmpty', 'data.colors'),
[
@ -28,7 +28,7 @@ module.exports = function(builder) {
htmlElement('li',
{ 'class': literal('color')},
[
textOutput('color')
text('color')
])
])
]),

View File

@ -16,5 +16,6 @@
"@name": {
"required": true
}
}
},
"tags-dir": "./taglib/scanned-tags"
}

View File

@ -37,18 +37,18 @@
],
"body": [
{
"type": "TextOutput",
"type": "Text",
"argument": {
"type": "Literal",
"value": "Hello"
}
},
{
"type": "HtmlOutput",
"type": "Html",
"argument": "data.name"
},
{
"type": "TextOutput",
"type": "Text",
"argument": {
"type": "Literal",
"value": "!"
@ -67,7 +67,7 @@
{
"type": "HtmlElement",
"tagName": "ul",
"_attributes": [
"attributes": [
{
"name": "class",
"value": {
@ -75,48 +75,7 @@
"value": "colors"
}
}
],
"body": [
{
"type": "FunctionCall",
"callee": "forEach",
"args": [
"data.colors",
{
"type": "FunctionDeclaration",
"name": null,
"params": [
"color"
],
"body": [
{
"type": "HtmlElement",
"tagName": "li",
"_attributes": [
{
"name": "class",
"value": {
"type": "Literal",
"value": "color"
}
}
],
"body": [
{
"type": "TextOutput",
"argument": "color"
}
],
"allowSelfClosing": false,
"startTagOnly": false
}
]
}
]
}
],
"allowSelfClosing": false,
"startTagOnly": false
]
}
]
}

View File

@ -6,8 +6,8 @@ module.exports = function(builder) {
var functionCall = builder.functionCall;
var ifStatement = builder.ifStatement;
var vars = builder.vars;
var textOutput = builder.textOutput;
var htmlOutput = builder.htmlOutput;
var text = builder.text;
var html = builder.html;
var htmlElement = builder.htmlElement;
return templateRoot([
@ -19,9 +19,9 @@ module.exports = function(builder) {
]),
returnStatement(functionDeclaration('render', ['data', 'out'], [
textOutput(literal('Hello')),
htmlOutput('data.name'),
textOutput(literal('!')),
text(literal('Hello')),
html('data.name'),
text(literal('!')),
ifStatement(
functionCall('notEmpty', 'data.colors'),
[
@ -39,7 +39,7 @@ module.exports = function(builder) {
'li',
{ 'class': literal('color')},
[
textOutput('color')
text('color')
])
])
]),

View File

@ -0,0 +1 @@
Hello FrankHello John

View File

@ -0,0 +1,2 @@
<test-tag-code-generator-return-array name="Frank"/>
<test-tag-code-generator-return-array name=data.name/>

View File

@ -0,0 +1,3 @@
exports.templateData = {
name: 'John'
};

View File

@ -0,0 +1 @@
Hello FrankHello John

View File

@ -0,0 +1,2 @@
<test-tag-code-generator-return-node name="Frank"/>
<test-tag-code-generator-return-node name=data.name/>

View File

@ -0,0 +1,3 @@
exports.templateData = {
name: 'John'
};

View File

@ -0,0 +1 @@
<test-tag-code-generator-return-self name="Frank" foo="bar"></test-tag-code-generator-return-self><test-tag-code-generator-return-self name="John" foo="bar"></test-tag-code-generator-return-self>

View File

@ -0,0 +1,2 @@
<test-tag-code-generator-return-self name="Frank"/>
<test-tag-code-generator-return-self name=data.name/>

View File

@ -0,0 +1,3 @@
exports.templateData = {
name: 'John'
};

View File

@ -0,0 +1,7 @@
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return [
builder.text(builder.literal('Hello ')),
builder.text(elNode.getAttributeValue('name'))
];
};

View File

@ -0,0 +1,3 @@
{
"@name": "string"
}

View File

@ -0,0 +1,6 @@
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
return builder.functionCall('out.write', [
builder.binaryExpression('"Hello "', '+', elNode.getAttributeValue('name'))
]);
};

View File

@ -0,0 +1,3 @@
{
"@name": "string"
}

View File

@ -0,0 +1,5 @@
module.exports = function generateCode(elNode, generator) {
var builder = generator.builder;
elNode.setAttributeValue('foo', builder.literal('bar'));
return elNode;
};

View File

@ -0,0 +1,3 @@
{
"@name": "string"
}

View File

@ -3,7 +3,7 @@
module.exports = function(compiler) {
let builder = compiler.createBuilder();
let textOutput = builder.textOutput;
let text = builder.text;
let templateRoot = builder.templateRoot;
let htmlElement = builder.htmlElement;
let ifStatement = builder.ifStatement;
@ -29,7 +29,7 @@ module.exports = function(compiler) {
}
},
[
textOutput('color')
text('color')
])
])
]);