mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Restored most of the looping functionality
This commit is contained in:
parent
6ecb5b32dd
commit
c78076f877
@ -6,6 +6,7 @@ var Literal = require('./ast/Literal');
|
|||||||
var Identifier = require('./ast/Identifier');
|
var Identifier = require('./ast/Identifier');
|
||||||
var ok = require('assert').ok;
|
var ok = require('assert').ok;
|
||||||
var Container = require('./ast/Container');
|
var Container = require('./ast/Container');
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
class Slot {
|
class Slot {
|
||||||
constructor(generator) {
|
constructor(generator) {
|
||||||
@ -112,7 +113,7 @@ class Generator {
|
|||||||
if (!generateCodeFunc) {
|
if (!generateCodeFunc) {
|
||||||
throw new Error('Missing generator method for node of type "' +
|
throw new Error('Missing generator method for node of type "' +
|
||||||
node.type +
|
node.type +
|
||||||
'". Node: ' + node);
|
'". Node: ' + util.inspect(node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,51 +7,7 @@ var charProps = require('char-props');
|
|||||||
var deresolve = require('./util/deresolve');
|
var deresolve = require('./util/deresolve');
|
||||||
var UniqueVars = require('./util/UniqueVars');
|
var UniqueVars = require('./util/UniqueVars');
|
||||||
var PosInfo = require('./util/PosInfo');
|
var PosInfo = require('./util/PosInfo');
|
||||||
|
var CompileError = require('./CompileError');
|
||||||
class CompileError {
|
|
||||||
constructor(errorInfo, context) {
|
|
||||||
this.context = context;
|
|
||||||
this.node = errorInfo.node;
|
|
||||||
this.message = errorInfo.message;
|
|
||||||
this.code = errorInfo.code;
|
|
||||||
|
|
||||||
var pos = errorInfo.pos;
|
|
||||||
var endPos = errorInfo.endPos;
|
|
||||||
|
|
||||||
if (pos == null) {
|
|
||||||
pos = this.node && this.node.pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endPos == null) {
|
|
||||||
endPos = this.node && this.node.endPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos != null) {
|
|
||||||
pos = context.getPosInfo(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endPos != null) {
|
|
||||||
endPos = context.getPosInfo(endPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pos = pos;
|
|
||||||
this.endPos = endPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
var pos = this.pos;
|
|
||||||
if (pos) {
|
|
||||||
pos = '[' + pos + '] ';
|
|
||||||
} else {
|
|
||||||
pos = '';
|
|
||||||
}
|
|
||||||
var str = pos + this.message;
|
|
||||||
if (this.node) {
|
|
||||||
str += ' (' + this.node.toString() + ')';
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CompileContext {
|
class CompileContext {
|
||||||
constructor(src, filename, builder) {
|
constructor(src, filename, builder) {
|
||||||
|
|||||||
48
compiler/CompileError.js
Normal file
48
compiler/CompileError.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
class CompileError {
|
||||||
|
constructor(errorInfo, context) {
|
||||||
|
this.context = context;
|
||||||
|
this.node = errorInfo.node;
|
||||||
|
this.message = errorInfo.message;
|
||||||
|
this.code = errorInfo.code;
|
||||||
|
|
||||||
|
var pos = errorInfo.pos;
|
||||||
|
var endPos = errorInfo.endPos;
|
||||||
|
|
||||||
|
if (pos == null) {
|
||||||
|
pos = this.node && this.node.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endPos == null) {
|
||||||
|
endPos = this.node && this.node.endPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != null) {
|
||||||
|
pos = context.getPosInfo(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endPos != null) {
|
||||||
|
endPos = context.getPosInfo(endPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pos = pos;
|
||||||
|
this.endPos = endPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
var pos = this.pos;
|
||||||
|
if (pos) {
|
||||||
|
pos = '[' + pos + '] ';
|
||||||
|
} else {
|
||||||
|
pos = '';
|
||||||
|
}
|
||||||
|
var str = pos + this.message;
|
||||||
|
if (this.node) {
|
||||||
|
str += ' (' + this.node.toString() + ')';
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CompileError;
|
||||||
@ -159,6 +159,8 @@ class Parser {
|
|||||||
|
|
||||||
node.pos = el.pos;
|
node.pos = el.pos;
|
||||||
|
|
||||||
|
var foundAttrs = {};
|
||||||
|
|
||||||
// Validate the attributes
|
// Validate the attributes
|
||||||
attributes.forEach((attr) => {
|
attributes.forEach((attr) => {
|
||||||
let attrName = attr.name;
|
let attrName = attr.name;
|
||||||
@ -175,8 +177,36 @@ class Parser {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attr.def = attrDef;
|
||||||
|
|
||||||
|
foundAttrs[attrName] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (tagDef) {
|
||||||
|
// Add default values for any attributes. If an attribute has a declared
|
||||||
|
// default value and the attribute was not found on the element
|
||||||
|
// then add the attribute with the specified default value
|
||||||
|
tagDef.forEachAttribute(function (attrDef) {
|
||||||
|
var attrName = attrDef.name;
|
||||||
|
|
||||||
|
if (attrDef.hasOwnProperty('defaultValue') && !foundAttrs.hasOwnProperty(attrName)) {
|
||||||
|
attributes.push({
|
||||||
|
name: attrName,
|
||||||
|
value: builder.literal(attrDef.defaultValue)
|
||||||
|
});
|
||||||
|
} else if (attrDef.required === true) {
|
||||||
|
// TODO Only throw an error if there is no data argument provided (just HTML attributes)
|
||||||
|
if (!foundAttrs.hasOwnProperty(attrName)) {
|
||||||
|
context.addError({
|
||||||
|
node: node,
|
||||||
|
message: 'The "' + attrName + '" attribute is required for tag "' + tagName + '" in taglib "' + getTaglibPath(tagDef.taglibId) + '".'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.parentNode.appendChild(node);
|
this.parentNode.appendChild(node);
|
||||||
|
|
||||||
this.stack.push({
|
this.stack.push({
|
||||||
@ -196,6 +226,8 @@ class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleComment(comment) {
|
handleComment(comment) {
|
||||||
|
this.prevTextNode = null;
|
||||||
|
|
||||||
var builder = this.context.builder;
|
var builder = this.context.builder;
|
||||||
|
|
||||||
var compilerOptions = this.compilerOptions;
|
var compilerOptions = this.compilerOptions;
|
||||||
@ -209,6 +241,8 @@ class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleBodyTextPlaceholder(expression, escape) {
|
handleBodyTextPlaceholder(expression, escape) {
|
||||||
|
this.prevTextNode = null;
|
||||||
|
|
||||||
var builder = this.context.builder;
|
var builder = this.context.builder;
|
||||||
|
|
||||||
var textOutput = builder.textOutput(expression, escape);
|
var textOutput = builder.textOutput(expression, escape);
|
||||||
|
|||||||
@ -13,16 +13,52 @@ function removeExt(filename) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildInputProps(node, builder) {
|
function buildInputProps(node, context) {
|
||||||
var inputProps = {};
|
var inputProps = {};
|
||||||
|
|
||||||
node.forEachAttribute((attr) => {
|
node.forEachAttribute((attr) => {
|
||||||
var attrName = attr.name;
|
var attrName = attr.name;
|
||||||
var propName = removeDashes(attrName);
|
|
||||||
inputProps[propName] = attr.value;
|
var attrDef = attr.def || context.taglibLookup.getAttribute(node.tagName, attr.name);
|
||||||
|
|
||||||
|
var propName;
|
||||||
|
var parentPropName;
|
||||||
|
|
||||||
|
if (attrDef.dynamicAttribute) {
|
||||||
|
// Dynamic attributes are allowed attributes
|
||||||
|
// that are not declared (i.e. "*" attributes)
|
||||||
|
//
|
||||||
|
if (attrDef.preserveName === false) {
|
||||||
|
propName = removeDashes(attrName);
|
||||||
|
} else {
|
||||||
|
propName = attrName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrDef.targetProperty) {
|
||||||
|
parentPropName = attrDef.targetProperty;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Attributes map to properties and we allow the taglib
|
||||||
|
// author to control how an attribute name resolves
|
||||||
|
// to a property name.
|
||||||
|
if (attrDef.targetProperty) {
|
||||||
|
propName = attrDef.targetProperty;
|
||||||
|
} else if (attrDef.preserveName) {
|
||||||
|
propName = attr.name;
|
||||||
|
} else {
|
||||||
|
propName = removeDashes(attr.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentPropName) {
|
||||||
|
let parent = inputProps[parentPropName] = (inputProps[parentPropName] = {});
|
||||||
|
parent[propName] = attr.value;
|
||||||
|
} else {
|
||||||
|
inputProps[propName] = attr.value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return builder.literal(inputProps);
|
return context.builder.literal(inputProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomTag extends HtmlElement {
|
class CustomTag extends HtmlElement {
|
||||||
@ -48,7 +84,7 @@ class CustomTag extends HtmlElement {
|
|||||||
let loadRendererFunctionCall = builder.functionCall(loadRendererVar, [ requireRendererFunctionCall ]);
|
let loadRendererFunctionCall = builder.functionCall(loadRendererVar, [ requireRendererFunctionCall ]);
|
||||||
|
|
||||||
let rendererVar = generator.addStaticVar(removeExt(rendererPath), loadRendererFunctionCall);
|
let rendererVar = generator.addStaticVar(removeExt(rendererPath), loadRendererFunctionCall);
|
||||||
var inputProps = buildInputProps(this, builder);
|
var inputProps = buildInputProps(this, context);
|
||||||
var tagArgs = [ 'out', rendererVar, inputProps ];
|
var tagArgs = [ 'out', rendererVar, inputProps ];
|
||||||
var tagFunctionCall = builder.functionCall(tagVar, tagArgs);
|
var tagFunctionCall = builder.functionCall(tagVar, tagArgs);
|
||||||
return tagFunctionCall;
|
return tagFunctionCall;
|
||||||
|
|||||||
@ -8,6 +8,11 @@ class ForEach extends Node {
|
|||||||
this.varName = def.varName;
|
this.varName = def.varName;
|
||||||
this.target = def.target;
|
this.target = def.target;
|
||||||
this.body = this.makeContainer(def.body);
|
this.body = this.makeContainer(def.body);
|
||||||
|
this.separator = def.separator;
|
||||||
|
this.statusVarName = def.statusVarName;
|
||||||
|
this.from = def.from;
|
||||||
|
this.to = def.to;
|
||||||
|
this.step = def.step;
|
||||||
|
|
||||||
ok(this.varName, '"varName" is required');
|
ok(this.varName, '"varName" is required');
|
||||||
ok(this.target, '"target" is required');
|
ok(this.target, '"target" is required');
|
||||||
@ -16,15 +21,76 @@ class ForEach extends Node {
|
|||||||
generateCode(generator) {
|
generateCode(generator) {
|
||||||
var varName = this.varName;
|
var varName = this.varName;
|
||||||
var target = this.target;
|
var target = this.target;
|
||||||
|
var separator = this.separator;
|
||||||
|
var statusVarName = this.statusVarName;
|
||||||
|
|
||||||
var builder = generator.builder;
|
var builder = generator.builder;
|
||||||
|
|
||||||
generator.addStaticVar('forEach', '__helpers.f');
|
if (this.from) {
|
||||||
|
// This is a range loop
|
||||||
|
var from = This.from;
|
||||||
|
var to = this.to;
|
||||||
|
var step = this.step;
|
||||||
|
var comparison = '<=';
|
||||||
|
|
||||||
|
if (typeof step === 'number') {
|
||||||
|
if (step < 0) {
|
||||||
|
comparison = '>=';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step === 1) {
|
||||||
|
step = varName + '++';
|
||||||
|
} else if (step === -1) {
|
||||||
|
step = varName + '--';
|
||||||
|
} else if (step > 0) {
|
||||||
|
step = varName + '+=' + step;
|
||||||
|
} else if (step === 0) {
|
||||||
|
throw new Error('Invalid step of 0');
|
||||||
|
} else if (step < 0) {
|
||||||
|
step = 0-step; // Make the step positive and switch to -=
|
||||||
|
step = varName + '-=' + step;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
step = varName + '+=' + step;
|
||||||
|
}
|
||||||
|
|
||||||
|
// template.statement('(function() {').indent(function () {
|
||||||
|
// template.statement('for (var ' + nameVar + '=' + from + '; ' + nameVar + comparison + to + '; ' + step + ') {').indent(function () {
|
||||||
|
// this.generateCodeForChildren(template);
|
||||||
|
// }, this).line('}');
|
||||||
|
// }, this).line('}());');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (separator && !statusVarName) {
|
||||||
|
statusVarName = '__loop';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusVarName) {
|
||||||
|
let forEachVarName = generator.addStaticVar('forEachWithStatusVar', '__helpers.fv');
|
||||||
|
let body = this.body;
|
||||||
|
|
||||||
|
if (separator) {
|
||||||
|
body = body.items.concat([
|
||||||
|
builder.ifStatement('!' + statusVarName + '.isLast()', [
|
||||||
|
builder.textOutput(separator)
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.functionCall(forEachVarName, [
|
||||||
|
target,
|
||||||
|
builder.functionDeclaration(null, [varName, statusVarName], body)
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
let forEachVarName = generator.addStaticVar('forEach', '__helpers.f');
|
||||||
|
|
||||||
|
return builder.functionCall(forEachVarName, [
|
||||||
|
target,
|
||||||
|
builder.functionDeclaration(null, [varName], this.body)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return builder.functionCall('forEach', [
|
|
||||||
target,
|
|
||||||
builder.functionDeclaration(null, [varName], this.body)
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ class HtmlAttribute {
|
|||||||
this.name = def.name.toLowerCase();
|
this.name = def.name.toLowerCase();
|
||||||
this.value = def.value;
|
this.value = def.value;
|
||||||
this.argument = def.argument;
|
this.argument = def.argument;
|
||||||
|
this.def = def.def; // The attribute definition loaded from the taglib (if any)
|
||||||
}
|
}
|
||||||
|
|
||||||
isLiteralValue() {
|
isLiteralValue() {
|
||||||
|
|||||||
@ -111,6 +111,17 @@ class HtmlElement extends Node {
|
|||||||
this._dynamicAttributesExpressionArray.push(expression);
|
this._dynamicAttributesExpressionArray.push(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAttribute(name) {
|
||||||
|
return this._attributes != null && this._attributes.getAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAttributeValue(name) {
|
||||||
|
var attr = this._attributes != null && this._attributes.getAttribute(name);
|
||||||
|
if (attr) {
|
||||||
|
return attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeAttribute(name) {
|
removeAttribute(name) {
|
||||||
if (this._attributes) {
|
if (this._attributes) {
|
||||||
this._attributes.removeAttribute(name);
|
this._attributes.removeAttribute(name);
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
var Node = require('./Node');
|
var Node = require('./Node');
|
||||||
var Literal = require('./Literal');
|
var Literal = require('./Literal');
|
||||||
var ok = require('assert').ok;
|
|
||||||
|
|
||||||
function trim(textOutputNode) {
|
function trim(textOutputNode) {
|
||||||
var text = textOutputNode.argument.value;
|
var text = textOutputNode.argument.value;
|
||||||
@ -86,8 +85,6 @@ class TextOutput extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (curChild.type === 'TextOutput' && curChild.isLiteral()) {
|
if (curChild.type === 'TextOutput' && curChild.isLiteral()) {
|
||||||
|
|
||||||
|
|
||||||
if (currentTextLiteral) {
|
if (currentTextLiteral) {
|
||||||
currentTextLiteral.argument.value += curChild.argument.value;
|
currentTextLiteral.argument.value += curChild.argument.value;
|
||||||
curChild.detach();
|
curChild.detach();
|
||||||
@ -111,6 +108,8 @@ class TextOutput extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
literalTextNodes.forEach(trim);
|
literalTextNodes.forEach(trim);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isWhitespace() {
|
isWhitespace() {
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Builder = require('./Builder');
|
var Builder = require('./Builder');
|
||||||
var CodeGenerator = require('./CodeGenerator');
|
|
||||||
var Compiler = require('./Compiler');
|
var Compiler = require('./Compiler');
|
||||||
var Walker = require('./Walker');
|
var Walker = require('./Walker');
|
||||||
var Parser = require('./Parser');
|
var Parser = require('./Parser');
|
||||||
var HtmlJsParser = require('./HtmlJsParser');
|
var HtmlJsParser = require('./HtmlJsParser');
|
||||||
var CompileContext = require('./CompileContext');
|
|
||||||
var defaultBuilder = new Builder();
|
var defaultBuilder = new Builder();
|
||||||
var defaultParser = new Parser(new HtmlJsParser());
|
var defaultParser = new Parser(new HtmlJsParser());
|
||||||
|
|
||||||
|
|||||||
10
compiler/util/isValidJavaScriptVarName.js
Normal file
10
compiler/util/isValidJavaScriptVarName.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
var reservedWords = require('./javaScriptReservedWords');
|
||||||
|
var varNameRegExp = /^[$A-Z_][0-9A-Z_$]*$/i;
|
||||||
|
|
||||||
|
module.exports = function isValidJavaScriptVarName(varName) {
|
||||||
|
if (reservedWords[varName]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return varNameRegExp.test(varName);
|
||||||
|
};
|
||||||
65
compiler/util/javaScriptReservedWords.js
Normal file
65
compiler/util/javaScriptReservedWords.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
module.exports = {
|
||||||
|
'abstract': true,
|
||||||
|
'arguments': true,
|
||||||
|
'boolean': true,
|
||||||
|
'break': true,
|
||||||
|
'byte': true,
|
||||||
|
'case': true,
|
||||||
|
'catch': true,
|
||||||
|
'char': true,
|
||||||
|
'class': true,
|
||||||
|
'const': true,
|
||||||
|
'continue': true,
|
||||||
|
'debugger': true,
|
||||||
|
'default': true,
|
||||||
|
'delete': true,
|
||||||
|
'do': true,
|
||||||
|
'double': true,
|
||||||
|
'else': true,
|
||||||
|
'enum*': true,
|
||||||
|
'eval': true,
|
||||||
|
'export': true,
|
||||||
|
'extends': true,
|
||||||
|
'false': true,
|
||||||
|
'final': true,
|
||||||
|
'finally': true,
|
||||||
|
'float': true,
|
||||||
|
'for': true,
|
||||||
|
'function': true,
|
||||||
|
'goto': true,
|
||||||
|
'if': true,
|
||||||
|
'implements': true,
|
||||||
|
'import': true,
|
||||||
|
'in': true,
|
||||||
|
'instanceof': true,
|
||||||
|
'int': true,
|
||||||
|
'interface': true,
|
||||||
|
'let': true,
|
||||||
|
'long': true,
|
||||||
|
'native': true,
|
||||||
|
'new': true,
|
||||||
|
'null': true,
|
||||||
|
'package': true,
|
||||||
|
'private': true,
|
||||||
|
'protected': true,
|
||||||
|
'public': true,
|
||||||
|
'return': true,
|
||||||
|
'short': true,
|
||||||
|
'static': true,
|
||||||
|
'super': true,
|
||||||
|
'switch': true,
|
||||||
|
'synchronized': true,
|
||||||
|
'this': true,
|
||||||
|
'throw': true,
|
||||||
|
'throws': true,
|
||||||
|
'transient': true,
|
||||||
|
'true': true,
|
||||||
|
'try': true,
|
||||||
|
'typeof': true,
|
||||||
|
'var': true,
|
||||||
|
'void': true,
|
||||||
|
'volatile': true,
|
||||||
|
'while': true,
|
||||||
|
'with': true,
|
||||||
|
'yield': true
|
||||||
|
};
|
||||||
137
package.json
137
package.json
@ -1,70 +1,71 @@
|
|||||||
{
|
{
|
||||||
"name": "marko",
|
"name": "marko",
|
||||||
"description": "Marko is an extensible, streaming, asynchronous, high performance, HTML-based templating language that can be used in Node.js or in the browser.",
|
"description": "Marko is an extensible, streaming, asynchronous, high performance, HTML-based templating language that can be used in Node.js or in the browser.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"templating",
|
"templating",
|
||||||
"template",
|
"template",
|
||||||
"async",
|
"async",
|
||||||
"streaming"
|
"streaming"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/marko-js/marko.git"
|
"url": "https://github.com/marko-js/marko.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"init-tests": "./test/init-tests.sh",
|
"init-tests": "./test/init-tests.sh",
|
||||||
"test": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test && node_modules/.bin/jshint compiler/ runtime/ taglibs/",
|
"test": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test && node_modules/.bin/jshint compiler/ runtime/ taglibs/",
|
||||||
"test-fast": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-test",
|
"test-fast": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-test",
|
||||||
"test-async": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-async-test",
|
"test-async": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test/render-async-test",
|
||||||
"test-taglib-loader": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test/taglib-loader-test",
|
"test-taglib-loader": "npm run init-tests && node_modules/.bin/mocha --ui bdd --reporter spec ./test/taglib-loader-test",
|
||||||
"jshint": "node_modules/.bin/jshint compiler/ runtime/ taglibs/"
|
"jshint": "node_modules/.bin/jshint compiler/ runtime/ taglibs/"
|
||||||
},
|
},
|
||||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
"Patrick Steele-Idem <pnidem@gmail.com>"
|
"Patrick Steele-Idem <pnidem@gmail.com>"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-module-path": "^1.0.0",
|
"app-module-path": "^1.0.0",
|
||||||
"async-writer": "^1.4.0",
|
"async-writer": "^1.4.0",
|
||||||
"browser-refresh-client": "^1.0.0",
|
"browser-refresh-client": "^1.0.0",
|
||||||
"char-props": "~0.1.5",
|
"char-props": "~0.1.5",
|
||||||
"events": "^1.0.2",
|
"esprima": "^2.7.0",
|
||||||
"jsonminify": "^0.2.3",
|
"events": "^1.0.2",
|
||||||
"minimatch": "^0.2.14",
|
"jsonminify": "^0.2.3",
|
||||||
"property-handlers": "^1.0.0",
|
"minimatch": "^0.2.14",
|
||||||
"raptor-args": "^1.0.0",
|
"property-handlers": "^1.0.0",
|
||||||
"raptor-json": "^1.0.1",
|
"raptor-args": "^1.0.0",
|
||||||
"raptor-logging": "^1.0.1",
|
"raptor-json": "^1.0.1",
|
||||||
"raptor-modules": "^1.0.5",
|
"raptor-logging": "^1.0.1",
|
||||||
"raptor-polyfill": "^1.0.0",
|
"raptor-modules": "^1.0.5",
|
||||||
"raptor-promises": "^1.0.1",
|
"raptor-polyfill": "^1.0.0",
|
||||||
"raptor-regexp": "^1.0.0",
|
"raptor-promises": "^1.0.1",
|
||||||
"raptor-strings": "^1.0.0",
|
"raptor-regexp": "^1.0.0",
|
||||||
"raptor-util": "^1.0.0",
|
"raptor-strings": "^1.0.0",
|
||||||
"resolve-from": "^1.0.0",
|
"raptor-util": "^1.0.0",
|
||||||
"try-require": "^1.2.1"
|
"resolve-from": "^1.0.0",
|
||||||
},
|
"try-require": "^1.2.1"
|
||||||
"devDependencies": {
|
},
|
||||||
"bluebird": "^2.9.30",
|
"devDependencies": {
|
||||||
"chai": "^3.3.0",
|
"bluebird": "^2.9.30",
|
||||||
"jshint": "^2.5.0",
|
"chai": "^3.3.0",
|
||||||
"mocha": "^2.3.3",
|
"jshint": "^2.5.0",
|
||||||
"through": "^2.3.4"
|
"mocha": "^2.3.3",
|
||||||
},
|
"through": "^2.3.4"
|
||||||
"license": "Apache-2.0",
|
},
|
||||||
"bin": {
|
"license": "Apache-2.0",
|
||||||
"markoc": "bin/markoc"
|
"bin": {
|
||||||
},
|
"markoc": "bin/markoc"
|
||||||
"main": "runtime/marko-runtime.js",
|
},
|
||||||
"publishConfig": {
|
"main": "runtime/marko-runtime.js",
|
||||||
"registry": "https://registry.npmjs.org/"
|
"publishConfig": {
|
||||||
},
|
"registry": "https://registry.npmjs.org/"
|
||||||
"browser": {
|
},
|
||||||
"./node-require.js": "./node-require-browser.js"
|
"browser": {
|
||||||
},
|
"./node-require.js": "./node-require-browser.js"
|
||||||
"homepage": "http://markojs.com/",
|
},
|
||||||
"version": "3.0.0-beta.1",
|
"homepage": "http://markojs.com/",
|
||||||
"logo": {
|
"version": "3.0.0-beta.1",
|
||||||
"url": "https://raw.githubusercontent.com/marko-js/branding/master/marko-logo-small.png"
|
"logo": {
|
||||||
}
|
"url": "https://raw.githubusercontent.com/marko-js/branding/master/marko-logo-small.png"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
var extend = require('raptor-util/extend');
|
var extend = require('raptor-util/extend');
|
||||||
var parseComplexAttribute = require('./util/parseComplexAttribute');
|
var parseComplexAttribute = require('./util/parseComplexAttribute');
|
||||||
var parseForEach = require('./util/parseForEach');
|
var parseForEach = require('./util/parseForEach');
|
||||||
|
var parseJavaScriptIdentifier = require('./util/parseJavaScriptIdentifier');
|
||||||
|
|
||||||
var coreAttrHandlers = [
|
var coreAttrHandlers = [
|
||||||
[
|
[
|
||||||
@ -13,8 +14,8 @@ var coreAttrHandlers = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
var forEachProps = parseComplexAttribute(forArgument, {
|
var forEachProps = parseComplexAttribute(forArgument, {
|
||||||
each: true,
|
'each': true,
|
||||||
separator: true,
|
'separator': true,
|
||||||
'iterator': true,
|
'iterator': true,
|
||||||
'status-var': true,
|
'status-var': true,
|
||||||
'for-loop': true
|
'for-loop': true
|
||||||
@ -31,11 +32,26 @@ var coreAttrHandlers = [
|
|||||||
this.addError('Invalid "for" attribute.');
|
this.addError('Invalid "for" attribute.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var statusVarName = forEachProps.statusVar;
|
||||||
|
|
||||||
|
if (statusVarName) {
|
||||||
|
// statusVar is expected to be a String literal expression
|
||||||
|
// For example: statusVar: "'foo'"
|
||||||
|
// We need to parse it into an actual string such as "foo"
|
||||||
|
statusVarName = parseJavaScriptIdentifier(statusVarName);
|
||||||
|
if (!statusVarName) {
|
||||||
|
this.addError('Invalid "status-var": ' + forEachProps.statusVar);
|
||||||
|
}
|
||||||
|
delete forEachProps.statusVar;
|
||||||
|
forEachProps.statusVarName = statusVarName;
|
||||||
|
}
|
||||||
|
|
||||||
var parsedForEach = parseForEach(forEachProps.each);
|
var parsedForEach = parseForEach(forEachProps.each);
|
||||||
delete forEachProps.each;
|
delete forEachProps.each;
|
||||||
extend(forEachProps, parsedForEach);
|
extend(forEachProps, parsedForEach);
|
||||||
|
|
||||||
forEachProps.pos = node.pos;
|
forEachProps.pos = node.pos;
|
||||||
|
|
||||||
//Copy the position property
|
//Copy the position property
|
||||||
var forEachNode = this.builder.forEach(forEachProps);
|
var forEachNode = this.builder.forEach(forEachProps);
|
||||||
//Surround the existing node with a "forEach" node
|
//Surround the existing node with a "forEach" node
|
||||||
@ -108,6 +124,7 @@ var coreAttrHandlers = [
|
|||||||
|
|
||||||
class AttributeTransformer {
|
class AttributeTransformer {
|
||||||
constructor(context, el) {
|
constructor(context, el) {
|
||||||
|
this.context = context;
|
||||||
this.builder = context.builder;
|
this.builder = context.builder;
|
||||||
this.el = el;
|
this.el = el;
|
||||||
}
|
}
|
||||||
@ -131,8 +148,11 @@ class AttributeTransformer {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
addError(error) {
|
addError(message) {
|
||||||
this.compiler.addError(this.el, error);
|
this.context.addError({
|
||||||
|
node: this.el,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,19 @@ exports.generateCode = function(generator) {
|
|||||||
var argument = this.argument;
|
var argument = this.argument;
|
||||||
if (!argument) {
|
if (!argument) {
|
||||||
generator.addError('Invalid <for> tag. Argument is missing. Example; <for(color in colors)>');
|
generator.addError('Invalid <for> tag. Argument is missing. Example; <for(color in colors)>');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var forEachProps = parseForEach(argument);
|
var forEachProps = parseForEach(argument);
|
||||||
forEachProps.body = this.body;
|
forEachProps.body = this.body;
|
||||||
|
|
||||||
|
if (this.hasAttribute('separator')) {
|
||||||
|
forEachProps.separator = this.getAttributeValue('separator');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('status-var')) {
|
||||||
|
forEachProps.statusVarName = this.getAttributeValue('status-var');
|
||||||
|
}
|
||||||
|
|
||||||
return generator.builder.forEach(forEachProps);
|
return generator.builder.forEach(forEachProps);
|
||||||
};
|
};
|
||||||
23
taglibs/core/util/parseJavaScriptIdentifier.js
Normal file
23
taglibs/core/util/parseJavaScriptIdentifier.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
var esprima = require('esprima');
|
||||||
|
|
||||||
|
function parseJavaScriptIdentifier(src, requireQuotes) {
|
||||||
|
var program = esprima.parse(src);
|
||||||
|
if (program.body.length === 1) {
|
||||||
|
var expressionStatement = program.body[0];
|
||||||
|
if (expressionStatement.type === 'ExpressionStatement') {
|
||||||
|
var expression = expressionStatement.expression;
|
||||||
|
if (expression.type === 'Literal') {
|
||||||
|
var value = expression.value;
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
} else if (requireQuotes !== true && expression.type === 'Identifier') {
|
||||||
|
return expression.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = parseJavaScriptIdentifier;
|
||||||
11
test/fixtures/marko-taglib.json
vendored
11
test/fixtures/marko-taglib.json
vendored
@ -3,7 +3,18 @@
|
|||||||
"renderer": "./taglib/test-declared-attributes/renderer.js",
|
"renderer": "./taglib/test-declared-attributes/renderer.js",
|
||||||
"@name": "string"
|
"@name": "string"
|
||||||
},
|
},
|
||||||
|
"<test-target-property>": {
|
||||||
|
"renderer": "./taglib/test-target-property/renderer.js",
|
||||||
|
"@name-foo": {
|
||||||
|
"target-property": "name"
|
||||||
|
}
|
||||||
|
},
|
||||||
"<test-invalid-attr>": {
|
"<test-invalid-attr>": {
|
||||||
"@foo": "string"
|
"@foo": "string"
|
||||||
|
},
|
||||||
|
"<test-missing-required-attr>": {
|
||||||
|
"@name": {
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user