mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Fixes #177 - body-only-if support in Marko v3
This commit is contained in:
parent
39c5fa1e0a
commit
70d47e6d5b
@ -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;
|
||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
13
test/fixtures/codegen/autotest/body-only-if/expected.js
vendored
Normal file
13
test/fixtures/codegen/autotest/body-only-if/expected.js
vendored
Normal 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>");
|
||||||
|
}
|
||||||
|
}
|
||||||
18
test/fixtures/codegen/autotest/body-only-if/index.js
vendored
Normal file
18
test/fixtures/codegen/autotest/body-only-if/index.js
vendored
Normal 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
|
||||||
|
]);
|
||||||
|
};
|
||||||
20
test/fixtures/codegen/autotest/if-write-if/expected.js
vendored
Normal file
20
test/fixtures/codegen/autotest/if-write-if/expected.js
vendored
Normal 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);
|
||||||
17
test/fixtures/codegen/autotest/if-write-if/index.js
vendored
Normal file
17
test/fixtures/codegen/autotest/if-write-if/index.js
vendored
Normal 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
|
||||||
|
]);
|
||||||
|
};
|
||||||
@ -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>
|
|
||||||
6
test/fixtures/render/autotest/body-only-if/template.marko
vendored
Normal file
6
test/fixtures/render/autotest/body-only-if/template.marko
vendored
Normal 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user