Fixes #177 - body-only-if support in Marko v3

This commit is contained in:
Patrick Steele-Idem 2016-01-04 11:44:04 -07:00
parent 39c5fa1e0a
commit 70d47e6d5b
10 changed files with 229 additions and 98 deletions

View File

@ -5,51 +5,22 @@ var Literal = require('./Literal');
var escapeXmlAttr = require('raptor-util/escapeXml').attr; var escapeXmlAttr = require('raptor-util/escapeXml').attr;
var HtmlAttributeCollection = require('./HtmlAttributeCollection'); var HtmlAttributeCollection = require('./HtmlAttributeCollection');
class HtmlElement extends Node { class StartTag extends Node {
constructor(def) { constructor(def) {
super('HtmlElement'); super('StartTag');
var tagName = def.tagName; this.tagName = def.tagName;
this.attributes = def.attributes;
this.tagName = null;
this.dynamicTagName = null;
if (tagName instanceof Node) {
if (tagName instanceof Literal) {
this.tagName = tagName.value;
} else {
this.dynamicTagName = tagName;
}
} else if (typeof tagName === 'string'){
this.tagName = tagName;
}
this._attributes = def.attributes;
if (!(this._attributes instanceof HtmlAttributeCollection)) {
this._attributes = new HtmlAttributeCollection(this._attributes);
}
this.body = this.makeContainer(def.body);
this.argument = def.argument; this.argument = def.argument;
this.allowSelfClosing = false; this.selfClosing = def.selfClosing;
this.startTagOnly = false; this.dynamicAttributes = def.dynamicAttributes;
this._dynamicAttributesExpressionArray = undefined;
} }
generateHtmlCode(codegen) { generateCode(codegen) {
var tagName = this.tagName; var tagName = this.tagName;
var selfClosing = this.selfClosing;
var dynamicAttributes = this.dynamicAttributes;
// Convert the tag name into a Node so that we generate the code correctly
if (tagName) {
tagName = codegen.builder.literal(tagName);
} else {
tagName = this.dynamicTagName;
}
var body = this.body;
var startTagOnly = this.startTagOnly;
var allowSelfClosing = this.allowSelfClosing;
var hasBody = body && body.length;
var builder = codegen.builder; var builder = codegen.builder;
// Starting tag // Starting tag
@ -57,7 +28,7 @@ class HtmlElement extends Node {
codegen.addWrite(tagName); codegen.addWrite(tagName);
var attributes = this._attributes && this._attributes.all; var attributes = this.attributes;
if (attributes) { if (attributes) {
for (let i=0; i<attributes.length; i++) { for (let i=0; i<attributes.length; i++) {
@ -90,40 +61,149 @@ class HtmlElement extends Node {
} }
} }
if (this._dynamicAttributesExpressionArray) { if (dynamicAttributes) {
this._dynamicAttributesExpressionArray.forEach(function(attrsExpression) { dynamicAttributes.forEach(function(attrsExpression) {
codegen.addStaticVar('attrs', '__helpers.as'); codegen.addStaticVar('attrs', '__helpers.as');
let attrsFunctionCall = builder.functionCall('attrs', [attrsExpression]); let attrsFunctionCall = builder.functionCall('attrs', [attrsExpression]);
codegen.addWrite(attrsFunctionCall); codegen.addWrite(attrsFunctionCall);
}); });
} }
// Body if (selfClosing) {
if (hasBody) { codegen.addWriteLiteral('/>');
codegen.addWriteLiteral('>'); } else {
codegen.generateStatements(body);
codegen.addWriteLiteral('</');
codegen.addWrite(tagName);
codegen.addWriteLiteral('>'); codegen.addWriteLiteral('>');
}
}
}
class EndTag extends Node {
constructor(def) {
super('EndTag');
this.tagName = def.tagName;
}
generateCode(codegen) {
var tagName = this.tagName;
codegen.addWriteLiteral('</');
codegen.addWrite(tagName);
codegen.addWriteLiteral('>');
}
}
class HtmlElement extends Node {
constructor(def) {
super('HtmlElement');
var tagName = def.tagName;
this.tagName = null;
this.dynamicTagName = null;
if (tagName instanceof Node) {
if (tagName instanceof Literal) {
this.tagName = tagName.value;
} else {
this.dynamicTagName = tagName;
}
} else if (typeof tagName === 'string'){
this.tagName = tagName;
}
this._attributes = def.attributes;
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;
this.bodyOnlyIf = undefined;
}
generateHtmlCode(codegen) {
var tagName = this.tagName;
// Convert the tag name into a Node so that we generate the code correctly
if (tagName) {
tagName = codegen.builder.literal(tagName);
} else {
tagName = this.dynamicTagName;
}
var attributes = this._attributes && this._attributes.all;
var body = this.body;
var argument = this.argument;
var hasBody = body && body.length;
var startTagOnly = this.startTagOnly;
var allowSelfClosing = this.allowSelfClosing === true;
var bodyOnlyIf = this.bodyOnlyIf;
var dynamicAttributes = this.dynamicAttributes;
var selfClosing = false;
var builder = codegen.builder;
if (hasBody || bodyOnlyIf) {
startTagOnly = false;
selfClosing = false;
} else {
if (allowSelfClosing) {
selfClosing = true;
startTagOnly = true;
}
}
var startTag = new StartTag({
tagName: tagName,
attributes: attributes,
argument: argument,
selfClosing: selfClosing,
dynamicAttributes: dynamicAttributes
});
var endTag;
if (!startTagOnly) {
endTag = new EndTag({
tagName: tagName
});
}
if (bodyOnlyIf) {
var startIf = builder.ifStatement(builder.negate(bodyOnlyIf), [
startTag
]);
var endIf = builder.ifStatement(builder.negate(bodyOnlyIf), [
endTag
]);
return [
startIf,
body,
endIf
];
} else { } else {
if (startTagOnly) { if (startTagOnly) {
codegen.addWriteLiteral('>'); codegen.generateCode(startTag);
} else if (allowSelfClosing) {
codegen.addWriteLiteral('/>');
} else { } else {
codegen.addWriteLiteral('></'); codegen.generateCode(startTag);
codegen.addWrite(tagName); codegen.generateCode(body);
codegen.addWriteLiteral('>'); codegen.generateCode(endTag);
} }
} }
} }
addDynamicAttributes(expression) { addDynamicAttributes(expression) {
if (!this._dynamicAttributesExpressionArray) { if (!this.dynamicAttributes) {
this._dynamicAttributesExpressionArray = []; this.dynamicAttributes = [];
} }
this._dynamicAttributesExpressionArray.push(expression); this.dynamicAttributes.push(expression);
} }
getAttribute(name) { getAttribute(name) {
@ -177,20 +257,21 @@ class HtmlElement extends Node {
this.dynamicTagName = dynamicTagName; this.dynamicTagName = dynamicTagName;
} }
toString() {
var tagName = this.tagName;
return '<' + tagName + '>';
}
toJSON() { toJSON() {
return { return {
type: this.type, type: this.type,
tagName: this.tagName, tagName: this.tagName,
attributes: this._attributes, attributes: this._attributes,
argument: this.argument, argument: this.argument,
body: this.body body: this.body,
bodyOnlyIf: this.bodyOnlyIf,
dynamicAttributes: this.dynamicAttributes
}; };
} }
setBodyOnlyIf(condition) {
this.bodyOnlyIf = condition;
}
} }
module.exports = HtmlElement; module.exports = HtmlElement;

View File

@ -61,16 +61,12 @@ var coreAttrHandlers = [
], ],
[ [
'body-only-if', function(attr, node, el) { 'body-only-if', function(attr, node, el) {
throw new Error('body-only-if Not Implemented'); var condition = attr.argument;
// var condition = attr.argument; if (!condition) {
// if (!condition) { return;
// this.addError('Invalid "body-only-if" attribute'); }
// return;
// } el.setBodyOnlyIf(condition);
//
// if (el.nodeType !== '')
//
// el.setStripExpression(attr);
} }
], ],
[ [
@ -91,25 +87,6 @@ class AttributeTransformer {
this.el = el; this.el = el;
} }
transformNode(el) {
var node = el;
for (var i=0, len=coreAttrHandlers.length; i<len; i++) {
var attrHandler = coreAttrHandlers[i];
var name = attrHandler[0];
var attr = node.getAttribute(name);
if (attr != null) {
node.removeAttribute(name);
var newNode = this[name](attr, node, el);
if (newNode) {
newNode.pos = node.pos;
node = newNode;
}
}
}
return node;
}
addError(message) { addError(message) {
this.context.addError({ this.context.addError({
node: this.el, node: this.el,
@ -128,6 +105,7 @@ var attributeTransformers = AttributeTransformer.prototype;
module.exports = function transform(el, context) { module.exports = function transform(el, context) {
var attributeTransfomer; var attributeTransfomer;
var node = el;
el.forEachAttribute((attr) => { el.forEachAttribute((attr) => {
let attrName = attr.name; let attrName = attr.name;
@ -138,7 +116,11 @@ module.exports = function transform(el, context) {
if (!attributeTransfomer) { if (!attributeTransfomer) {
attributeTransfomer = new AttributeTransformer(context, el); attributeTransfomer = new AttributeTransformer(context, el);
} }
attributeTransfomer[attrName](attr, el); var newNode = attributeTransfomer[attrName](attr, node, el);
if (newNode) {
newNode.pos = node.pos;
node = newNode;
}
} }
}); });
}; };

View File

@ -0,0 +1,13 @@
if (true) {
if (!(!data.url)) {
out.w("<a href=\"" +
data.url +
"\">");
}
out.w("Hello World");
if (!(!data.url)) {
out.w("</a>");
}
}

View File

@ -0,0 +1,18 @@
'use strict';
module.exports = function(builder) {
var anchor = builder.htmlElement(
'a',
{
href: 'data.url'
},
[
builder.text(builder.literal('Hello World'))
]);
anchor.setBodyOnlyIf('!data.url');
return builder.ifStatement(builder.literal(true), [
anchor
]);
};

View File

@ -0,0 +1,20 @@
function create(__helpers) {
var str = __helpers.s,
empty = __helpers.e,
notEmpty = __helpers.ne,
escapeXml = __helpers.x;
return function render(data, out) {
if (true) {
out.w("A");
}
out.w("B");
if (true) {
out.w("C");
}
};
}
(module.exports = require("marko").c(__filename)).c(create);

View File

@ -0,0 +1,17 @@
'use strict';
module.exports = function(builder) {
var startIf = builder.ifStatement(builder.literal(true), [
builder.text(builder.literal('A'))
]);
var endIf = builder.ifStatement(builder.literal(true), [
builder.text(builder.literal('C'))
]);
return builder.templateRoot([
startIf,
builder.text(builder.literal('B')),
endIf
]);
};

View File

@ -1,6 +0,0 @@
<a href="${data.url}" body-only-if="!data.url">
Some Link
</a>
<a href="${data.invalidUrl}" body-only-if="!data.invalidUrl">
Another Link
</a>

View File

@ -0,0 +1,6 @@
<a href=data.url body-only-if(!data.url)>
Some Link
</a>
<a href=data.invalidUrl body-only-if(!data.invalidUrl)>
Another Link
</a>