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 ok = require('assert').ok;
|
||||
var Container = require('./ast/Container');
|
||||
var util = require('util');
|
||||
|
||||
class Slot {
|
||||
constructor(generator) {
|
||||
@ -112,7 +113,7 @@ class Generator {
|
||||
if (!generateCodeFunc) {
|
||||
throw new Error('Missing generator method for node of type "' +
|
||||
node.type +
|
||||
'". Node: ' + node);
|
||||
'". Node: ' + util.inspect(node));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,51 +7,7 @@ var charProps = require('char-props');
|
||||
var deresolve = require('./util/deresolve');
|
||||
var UniqueVars = require('./util/UniqueVars');
|
||||
var PosInfo = require('./util/PosInfo');
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
var CompileError = require('./CompileError');
|
||||
|
||||
class CompileContext {
|
||||
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;
|
||||
|
||||
var foundAttrs = {};
|
||||
|
||||
// Validate the attributes
|
||||
attributes.forEach((attr) => {
|
||||
let attrName = attr.name;
|
||||
@ -175,8 +177,36 @@ class Parser {
|
||||
}
|
||||
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.stack.push({
|
||||
@ -196,6 +226,8 @@ class Parser {
|
||||
}
|
||||
|
||||
handleComment(comment) {
|
||||
this.prevTextNode = null;
|
||||
|
||||
var builder = this.context.builder;
|
||||
|
||||
var compilerOptions = this.compilerOptions;
|
||||
@ -209,6 +241,8 @@ class Parser {
|
||||
}
|
||||
|
||||
handleBodyTextPlaceholder(expression, escape) {
|
||||
this.prevTextNode = null;
|
||||
|
||||
var builder = this.context.builder;
|
||||
|
||||
var textOutput = builder.textOutput(expression, escape);
|
||||
|
||||
@ -13,16 +13,52 @@ function removeExt(filename) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildInputProps(node, builder) {
|
||||
function buildInputProps(node, context) {
|
||||
var inputProps = {};
|
||||
|
||||
node.forEachAttribute((attr) => {
|
||||
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 {
|
||||
@ -48,7 +84,7 @@ class CustomTag extends HtmlElement {
|
||||
let loadRendererFunctionCall = builder.functionCall(loadRendererVar, [ requireRendererFunctionCall ]);
|
||||
|
||||
let rendererVar = generator.addStaticVar(removeExt(rendererPath), loadRendererFunctionCall);
|
||||
var inputProps = buildInputProps(this, builder);
|
||||
var inputProps = buildInputProps(this, context);
|
||||
var tagArgs = [ 'out', rendererVar, inputProps ];
|
||||
var tagFunctionCall = builder.functionCall(tagVar, tagArgs);
|
||||
return tagFunctionCall;
|
||||
|
||||
@ -8,6 +8,11 @@ class ForEach extends Node {
|
||||
this.varName = def.varName;
|
||||
this.target = def.target;
|
||||
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.target, '"target" is required');
|
||||
@ -16,15 +21,76 @@ class ForEach extends Node {
|
||||
generateCode(generator) {
|
||||
var varName = this.varName;
|
||||
var target = this.target;
|
||||
var separator = this.separator;
|
||||
var statusVarName = this.statusVarName;
|
||||
|
||||
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)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,11 @@ var ok = require('assert').ok;
|
||||
class HtmlAttribute {
|
||||
constructor(def) {
|
||||
ok(def, 'Invalid attribute definition');
|
||||
|
||||
|
||||
this.name = def.name.toLowerCase();
|
||||
this.value = def.value;
|
||||
this.argument = def.argument;
|
||||
this.def = def.def; // The attribute definition loaded from the taglib (if any)
|
||||
}
|
||||
|
||||
isLiteralValue() {
|
||||
|
||||
@ -111,6 +111,17 @@ class HtmlElement extends Node {
|
||||
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) {
|
||||
if (this._attributes) {
|
||||
this._attributes.removeAttribute(name);
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
var Node = require('./Node');
|
||||
var Literal = require('./Literal');
|
||||
var ok = require('assert').ok;
|
||||
|
||||
function trim(textOutputNode) {
|
||||
var text = textOutputNode.argument.value;
|
||||
@ -86,8 +85,6 @@ class TextOutput extends Node {
|
||||
}
|
||||
|
||||
if (curChild.type === 'TextOutput' && curChild.isLiteral()) {
|
||||
|
||||
|
||||
if (currentTextLiteral) {
|
||||
currentTextLiteral.argument.value += curChild.argument.value;
|
||||
curChild.detach();
|
||||
@ -111,6 +108,8 @@ class TextOutput extends Node {
|
||||
}
|
||||
|
||||
literalTextNodes.forEach(trim);
|
||||
|
||||
|
||||
}
|
||||
|
||||
isWhitespace() {
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
var Builder = require('./Builder');
|
||||
var CodeGenerator = require('./CodeGenerator');
|
||||
var Compiler = require('./Compiler');
|
||||
var Walker = require('./Walker');
|
||||
var Parser = require('./Parser');
|
||||
var HtmlJsParser = require('./HtmlJsParser');
|
||||
var CompileContext = require('./CompileContext');
|
||||
var defaultBuilder = new Builder();
|
||||
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",
|
||||
"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": [
|
||||
"templating",
|
||||
"template",
|
||||
"async",
|
||||
"streaming"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/marko-js/marko.git"
|
||||
},
|
||||
"scripts": {
|
||||
"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-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-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/"
|
||||
},
|
||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
||||
"maintainers": [
|
||||
"Patrick Steele-Idem <pnidem@gmail.com>"
|
||||
],
|
||||
"dependencies": {
|
||||
"app-module-path": "^1.0.0",
|
||||
"async-writer": "^1.4.0",
|
||||
"browser-refresh-client": "^1.0.0",
|
||||
"char-props": "~0.1.5",
|
||||
"events": "^1.0.2",
|
||||
"jsonminify": "^0.2.3",
|
||||
"minimatch": "^0.2.14",
|
||||
"property-handlers": "^1.0.0",
|
||||
"raptor-args": "^1.0.0",
|
||||
"raptor-json": "^1.0.1",
|
||||
"raptor-logging": "^1.0.1",
|
||||
"raptor-modules": "^1.0.5",
|
||||
"raptor-polyfill": "^1.0.0",
|
||||
"raptor-promises": "^1.0.1",
|
||||
"raptor-regexp": "^1.0.0",
|
||||
"raptor-strings": "^1.0.0",
|
||||
"raptor-util": "^1.0.0",
|
||||
"resolve-from": "^1.0.0",
|
||||
"try-require": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "^2.9.30",
|
||||
"chai": "^3.3.0",
|
||||
"jshint": "^2.5.0",
|
||||
"mocha": "^2.3.3",
|
||||
"through": "^2.3.4"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"markoc": "bin/markoc"
|
||||
},
|
||||
"main": "runtime/marko-runtime.js",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"browser": {
|
||||
"./node-require.js": "./node-require-browser.js"
|
||||
},
|
||||
"homepage": "http://markojs.com/",
|
||||
"version": "3.0.0-beta.1",
|
||||
"logo": {
|
||||
"url": "https://raw.githubusercontent.com/marko-js/branding/master/marko-logo-small.png"
|
||||
}
|
||||
"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.",
|
||||
"keywords": [
|
||||
"templating",
|
||||
"template",
|
||||
"async",
|
||||
"streaming"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/marko-js/marko.git"
|
||||
},
|
||||
"scripts": {
|
||||
"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-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-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/"
|
||||
},
|
||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
||||
"maintainers": [
|
||||
"Patrick Steele-Idem <pnidem@gmail.com>"
|
||||
],
|
||||
"dependencies": {
|
||||
"app-module-path": "^1.0.0",
|
||||
"async-writer": "^1.4.0",
|
||||
"browser-refresh-client": "^1.0.0",
|
||||
"char-props": "~0.1.5",
|
||||
"esprima": "^2.7.0",
|
||||
"events": "^1.0.2",
|
||||
"jsonminify": "^0.2.3",
|
||||
"minimatch": "^0.2.14",
|
||||
"property-handlers": "^1.0.0",
|
||||
"raptor-args": "^1.0.0",
|
||||
"raptor-json": "^1.0.1",
|
||||
"raptor-logging": "^1.0.1",
|
||||
"raptor-modules": "^1.0.5",
|
||||
"raptor-polyfill": "^1.0.0",
|
||||
"raptor-promises": "^1.0.1",
|
||||
"raptor-regexp": "^1.0.0",
|
||||
"raptor-strings": "^1.0.0",
|
||||
"raptor-util": "^1.0.0",
|
||||
"resolve-from": "^1.0.0",
|
||||
"try-require": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "^2.9.30",
|
||||
"chai": "^3.3.0",
|
||||
"jshint": "^2.5.0",
|
||||
"mocha": "^2.3.3",
|
||||
"through": "^2.3.4"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"markoc": "bin/markoc"
|
||||
},
|
||||
"main": "runtime/marko-runtime.js",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"browser": {
|
||||
"./node-require.js": "./node-require-browser.js"
|
||||
},
|
||||
"homepage": "http://markojs.com/",
|
||||
"version": "3.0.0-beta.1",
|
||||
"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 parseComplexAttribute = require('./util/parseComplexAttribute');
|
||||
var parseForEach = require('./util/parseForEach');
|
||||
var parseJavaScriptIdentifier = require('./util/parseJavaScriptIdentifier');
|
||||
|
||||
var coreAttrHandlers = [
|
||||
[
|
||||
@ -13,8 +14,8 @@ var coreAttrHandlers = [
|
||||
}
|
||||
|
||||
var forEachProps = parseComplexAttribute(forArgument, {
|
||||
each: true,
|
||||
separator: true,
|
||||
'each': true,
|
||||
'separator': true,
|
||||
'iterator': true,
|
||||
'status-var': true,
|
||||
'for-loop': true
|
||||
@ -31,11 +32,26 @@ var coreAttrHandlers = [
|
||||
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);
|
||||
delete forEachProps.each;
|
||||
extend(forEachProps, parsedForEach);
|
||||
|
||||
forEachProps.pos = node.pos;
|
||||
|
||||
//Copy the position property
|
||||
var forEachNode = this.builder.forEach(forEachProps);
|
||||
//Surround the existing node with a "forEach" node
|
||||
@ -108,6 +124,7 @@ var coreAttrHandlers = [
|
||||
|
||||
class AttributeTransformer {
|
||||
constructor(context, el) {
|
||||
this.context = context;
|
||||
this.builder = context.builder;
|
||||
this.el = el;
|
||||
}
|
||||
@ -131,8 +148,11 @@ class AttributeTransformer {
|
||||
return node;
|
||||
}
|
||||
|
||||
addError(error) {
|
||||
this.compiler.addError(this.el, error);
|
||||
addError(message) {
|
||||
this.context.addError({
|
||||
node: this.el,
|
||||
message: message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,19 @@ exports.generateCode = function(generator) {
|
||||
var argument = this.argument;
|
||||
if (!argument) {
|
||||
generator.addError('Invalid <for> tag. Argument is missing. Example; <for(color in colors)>');
|
||||
return;
|
||||
}
|
||||
|
||||
var forEachProps = parseForEach(argument);
|
||||
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);
|
||||
};
|
||||
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",
|
||||
"@name": "string"
|
||||
},
|
||||
"<test-target-property>": {
|
||||
"renderer": "./taglib/test-target-property/renderer.js",
|
||||
"@name-foo": {
|
||||
"target-property": "name"
|
||||
}
|
||||
},
|
||||
"<test-invalid-attr>": {
|
||||
"@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