Reintroduced attrs support

This commit is contained in:
Patrick Steele-Idem 2015-12-01 09:36:44 -07:00
parent a4dee1bbf3
commit 66f9be0d4e
16 changed files with 314 additions and 31 deletions

View File

@ -91,6 +91,7 @@ class Generator {
throw new Error('Root node is not of type "TemplateRoot". Actual ' + JSON.stringify(templateRoot));
}
templateRoot.addStaticVar(name, value);
return name;
}
generateCode(node, options) {

View File

@ -30,7 +30,7 @@ class ArrayContainer extends Container {
// }
forEach(callback, thisObj) {
var array = this.array;
var array = this.array.concat([]);
for (var i=0; i<array.length; i++) {
var item = array[i];
if (item.container === this) {

17
compiler/ast/CustomTag.js Normal file
View File

@ -0,0 +1,17 @@
'use strict';
var HtmlElement = require('./HtmlElement');
class CustomTag extends HtmlElement {
constructor(def) {
super('Identifier');
this.name = def.name;
}
generateCode(generator) {
var name = this.name;
generator.write(name);
}
}
module.exports = Identifier;

View File

@ -17,6 +17,7 @@ class HtmlElement extends Node {
this.argument = def.argument;
this.allowSelfClosing = false;
this.startTagOnly = false;
this._dynamicAttributesExpressionArray = undefined;
}
generateHtmlCode(generator) {
@ -25,6 +26,7 @@ class HtmlElement extends Node {
var startTagOnly = this.startTagOnly;
var allowSelfClosing = this.allowSelfClosing;
var hasBody = body && body.length;
var builder = generator.builder;
// Starting tag
generator.addWriteLiteral('<' + tagName);
@ -63,6 +65,14 @@ class HtmlElement extends Node {
}
}
if (this._dynamicAttributesExpressionArray) {
this._dynamicAttributesExpressionArray.forEach(function(attrsExpression) {
generator.addStaticVar('attrs', '__helpers.as');
let attrsFunctionCall = builder.functionCall('attrs', [attrsExpression]);
generator.addWrite(attrsFunctionCall);
});
}
if (hasBody) {
generator.addWriteLiteral('>');
} else {
@ -94,6 +104,14 @@ class HtmlElement extends Node {
}
}
addDynamicAttributes(expression) {
if (!this._dynamicAttributesExpressionArray) {
this._dynamicAttributesExpressionArray = [];
}
this._dynamicAttributesExpressionArray.push(expression);
}
removeAttribute(name) {
if (this._attributes) {
this._attributes.removeAttribute(name);
@ -111,6 +129,14 @@ class HtmlElement extends Node {
get attributes() {
return this._attributes.all;
}
forEachAttribute(callback, thisObj) {
var attributes = this._attributes.all.concat([]);
for (let i=0, len=attributes.length; i<len; i++) {
callback.call(thisObj, attributes[i]);
}
}
}
module.exports = HtmlElement;

View File

@ -94,6 +94,15 @@ var coreAttrHandlers = [
el.setStripExpression(attr);
}
],
[
'attrs', function(attr, node) {
if (!node.addDynamicAttributes) {
node.addError('Node does not support the "attrs" attribute');
} else {
node.addDynamicAttributes(attr.value);
}
}
]
];
@ -139,20 +148,16 @@ var attributeTransformers = AttributeTransformer.prototype;
module.exports = function transform(el, compiler) {
var attributeTransfomer;
var attributes = el.getAttributes();
if (attributes) {
for (let i=0, len=attributes.length; i<len; i++) {
let attr = attributes[i];
let attrName = attr.name;
var attrTransformerFunc = attributeTransformers[attrName];
if (attrTransformerFunc) {
el.removeAttribute(attrName);
el.forEachAttribute((attr) => {
let attrName = attr.name;
var attrTransformerFunc = attributeTransformers[attrName];
if (attrTransformerFunc) {
el.removeAttribute(attrName);
if (!attributeTransfomer) {
attributeTransfomer = new AttributeTransformer(compiler, el);
}
attributeTransfomer[attrName](attr, el);
if (!attributeTransfomer) {
attributeTransfomer = new AttributeTransformer(compiler, el);
}
attributeTransfomer[attrName](attr, el);
}
}
});
};

View File

@ -1,30 +1,47 @@
var fs = require('fs');
var enabledTest = process.env.TEST;
var path = require('path');
var assert = require('assert');
function autoTest(name, dir, run, options) {
var compareExtension = (options && options.compareExtension) || '.js';
var isJSON = compareExtension === '.json';
var actualPath = path.join(dir, 'actual' + compareExtension);
var expectedPath = path.join(dir, 'expected' + compareExtension);
var actual = run(dir);
var actualJSON = isJSON ? JSON.stringify(actual, null, 2) : null;
fs.writeFileSync(actualPath, actual, {encoding: 'utf8'});
fs.writeFileSync(
actualPath,
isJSON ? actualJSON : actual,
{encoding: 'utf8'});
var expected;
try {
expected = fs.readFileSync(expectedPath, { encoding: 'utf8' });
} catch(e) {
expected = 'TBD';
expected = isJSON ? '"TBD"' : 'TBD';
fs.writeFileSync(expectedPath, expected, {encoding: 'utf8'});
}
if (actual !== expected) {
throw new Error('Unexpected output for "' + name + '":\nEXPECTED (' + expectedPath + '):\n---------\n' + expected +
'\n---------\nACTUAL (' + actualPath + '):\n---------\n' + actual + '\n---------');
var expectedJSON;
if (isJSON) {
expectedJSON = expected;
expected = JSON.parse(expectedJSON);
}
assert.deepEqual(
(isJSON ? JSON.parse(actualJSON) : actual),
expected,
'Unexpected output for "' + name + '":\nEXPECTED (' + expectedPath + '):\n---------\n' +
(isJSON ? expectedJSON : expected) +
'\n---------\nACTUAL (' + actualPath + '):\n---------\n' +
(isJSON ? actualJSON : actual) +
'\n---------');
}
exports.scanDir = function(autoTestDir, run, options) {

View File

@ -1 +1,42 @@
TBD
function create(__helpers) {
var str = __helpers.s,
empty = __helpers.e,
notEmpty = __helpers.ne,
escapeXml = __helpers.x,
forEach = __helpers.f;
return function render(data, out) {
out.w("Hello ! " +
escapeXml(data.name));
if (notEmpty(data.colors)) {
out.w("<ul>");
forEach(data.colors, function(color) {
out.w("<li>" +
escapeXml(color) +
"</li>");
});
out.w("</ul>");
} else {
out.w("<div>No colors!</div>");
}
if (notEmpty(data.colors)) {
out.w("<ul>");
forEach(data.colors, function(color) {
out.w("<li>" +
escapeXml(color) +
"</li>");
});
out.w("</ul>");
} else {
out.w("<div>No colors!</div>");
}
};
}
(module.exports = require("marko").c(__filename)).c(create);

View File

@ -0,0 +1,16 @@
{
"type": "TemplateRoot",
"body": [
{
"type": "Assignment",
"left": "a",
"right": "1"
}
],
"staticVars": {
"str": "__helpers.s",
"empty": "__helpers.e",
"notEmpty": "__helpers.ne",
"escapeXml": "__helpers.x"
}
}

View File

@ -0,0 +1,16 @@
{
"type": "TemplateRoot",
"body": [
{
"type": "Assignment",
"left": "a",
"right": "1"
}
],
"staticVars": {
"str": "__helpers.s",
"empty": "__helpers.e",
"notEmpty": "__helpers.ne",
"escapeXml": "__helpers.x"
}
}

View File

@ -67,7 +67,7 @@
{
"type": "HtmlElement",
"tagName": "ul",
"attributes": [
"_attributes": [
{
"name": "class",
"value": {
@ -92,7 +92,7 @@
{
"type": "HtmlElement",
"tagName": "li",
"attributes": [
"_attributes": [
{
"name": "class",
"value": {

View File

@ -0,0 +1,135 @@
{
"type": "TemplateRoot",
"body": [
{
"type": "FunctionDeclaration",
"name": "create",
"params": [
"__helpers"
],
"body": [
{
"type": "Vars",
"kind": "var",
"declarations": [
{
"id": "str",
"init": "__helpers.s"
},
{
"id": "empty",
"init": "__helpers.e"
},
{
"id": "notEmpty",
"init": "__helpers.ne"
}
]
},
{
"type": "Return",
"argument": {
"type": "FunctionDeclaration",
"name": "render",
"params": [
"data",
"out"
],
"body": [
{
"type": "TextOutput",
"argument": {
"type": "Literal",
"value": "Hello"
}
},
{
"type": "HtmlOutput",
"argument": "data.name"
},
{
"type": "TextOutput",
"argument": {
"type": "Literal",
"value": "!"
}
},
{
"type": "If",
"test": {
"type": "FunctionCall",
"callee": "notEmpty",
"args": [
"data.colors"
]
},
"body": [
{
"type": "HtmlElement",
"tagName": "ul",
"_attributes": [
{
"name": "class",
"value": {
"type": "Literal",
"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
}
]
}
]
}
}
]
}
],
"staticVars": {
"str": "__helpers.s",
"empty": "__helpers.e",
"notEmpty": "__helpers.ne",
"escapeXml": "__helpers.x"
}
}

View File

@ -0,0 +1 @@
<div foo="&quot;hello&quot;">Hello World!</div>

View File

@ -0,0 +1,3 @@
<div foo='\"hello\"'>
Hello World!
</div>

View File

@ -0,0 +1 @@
exports.templateData = {};

View File

@ -1,5 +1,3 @@
<var name="myAttrs" value="data.myAttrs"/>
<div attrs="myAttrs" data-encoding="&quot;hello&quot;">
<div attrs=data.myAttrs data-encoding='"hello"'>
Hello World!
</div>

View File

@ -12,9 +12,15 @@ var autotest = require('./autotest');
describe('compiler/pretty-print', function() {
var autoTestDir = path.join(__dirname, 'fixtures/pretty-print/autotest');
autotest.scanDir(autoTestDir, function run(dir) {
var getAST = require(path.join(dir, 'index.js'));
var ast = getAST(builder);
return JSON.stringify(ast, null, 2);
});
autotest.scanDir(
autoTestDir,
function run(dir) {
var getAST = require(path.join(dir, 'index.js'));
var ast = getAST(builder);
return ast;
},
{
deepEqual: true,
compareExtension: '.json'
});
});