Fixes #211 - Marko v3: Support concise (Jade-like) syntax

This commit is contained in:
Patrick Steele-Idem 2016-02-01 16:37:13 -07:00
parent 8b51e818a3
commit 266e15fa9c
55 changed files with 280 additions and 113 deletions

View File

@ -4,71 +4,75 @@ var htmljs = require('htmljs-parser');
class HtmlJsParser {
parse(src, handlers) {
var listeners = {
ontext(event) {
handlers.handleCharacters(event.text);
onText(event) {
handlers.handleCharacters(event.value);
},
oncontentplaceholder(event) {
// placeholder within content
handlers.handleBodyTextPlaceholder(event.expression, event.escape);
},
onnestedcontentplaceholder(event) {
// placeholder within string that is within content placeholder
},
onattributeplaceholder(event) {
// placeholder within attribute
if (event.escape) {
event.expression = '$escapeXml(' + event.expression + ')';
onPlaceholder(event) {
if (event.withinBody) {
if (!event.withinString) {
handlers.handleBodyTextPlaceholder(event.value, event.escape);
}
} else if (event.withinOpenTag) {
// Don't escape placeholder for dynamic attributes. For example: <div ${data.myAttrs}></div>
} else {
event.expression = '$noEscapeXml(' + event.expression + ')';
// placeholder within attribute
if (event.escape) {
event.value = '$escapeXml(' + event.value + ')';
} else {
event.value = '$noEscapeXml(' + event.value + ')';
}
}
// placeholder within content
},
onCDATA(event) {
handlers.handleCharacters(event.value);
},
onOpenTag(event, parser) {
event.selfClosed = false; // Don't allow self-closed tags
handlers.handleStartElement(event);
var newParserState = handlers.getParserStateForTag(event);
if (newParserState) {
if (newParserState === 'parsed-text') {
parser.enterParsedTextContentState();
} else if (newParserState === 'static-text') {
parser.enterStaticTextContentState();
}
}
},
oncdata(event) {
handlers.handleCharacters(event.text);
},
onopentag(event) {
handlers.handleStartElement(event);
},
onclosetag(event) {
onCloseTag(event) {
var tagName = event.tagName;
handlers.handleEndElement(tagName);
},
ondtd(event) {
// DTD (e.g. <DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN">)
handlers.handleCharacters(event.dtd);
onDocumentType(event) {
// Document type: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
// NOTE: The value will be all of the text between "<!" and ">""
handlers.handleCharacters('<!' + event.value + '>');
},
ondeclaration(event) {
onDeclaration(event) {
// Declaration (e.g. <?xml version="1.0" encoding="UTF-8" ?>)
handlers.handleCharacters(event.declaration);
handlers.handleCharacters('<?' + event.value + '?>');
},
oncomment(event) {
onComment(event) {
// Text within XML comment
handlers.handleComment(event.comment);
handlers.handleComment(event.value);
},
onerror(event) {
onError(event) {
handlers.handleError(event);
}
};
var options = {
parserStateProvider(event) {
if (event.type === 'opentag') {
return handlers.getParserStateForTag(event);
}
}
};
var parser = this.parser = htmljs.createParser(listeners, options);
var parser = this.parser = htmljs.createParser(listeners);
parser.parse(src);
}
}

View File

@ -64,7 +64,6 @@ class Parser {
} else {
var escape = false;
this.prevTextNode = builder.text(builder.literal(text), escape);
this.prevTextNode.pos = text.pos;
this.parentNode.appendChild(this.prevTextNode);
}
}
@ -77,6 +76,10 @@ class Parser {
var attributes = el.attributes;
var argument = el.argument; // e.g. For <for(color in colors)>, argument will be "color in colors"
if (argument) {
argument = argument.value;
}
if (tagName === 'compiler-options') {
attributes.forEach(function (attr) {
let attrName = attr.name;
@ -117,11 +120,12 @@ class Parser {
name: attr.name,
value: isLiteral ?
builder.literal(attr.literalValue) :
attr.expression == null ? undefined : builder.parseExpression(attr.expression)
attr.value == null ? undefined : builder.parseExpression(attr.value)
};
if (attr.argument) {
attrDef.argument = attr.argument;
// TODO Do something with the argument pos
attrDef.argument = attr.argument.value;
}
return attrDef;

View File

@ -30,7 +30,7 @@
"char-props": "~0.1.5",
"esprima": "^2.7.0",
"events": "^1.0.2",
"htmljs-parser": "^1.0.6",
"htmljs-parser": "^1.3.0",
"jsonminify": "^0.2.3",
"minimatch": "^0.2.14",
"property-handlers": "^1.0.0",

View File

@ -1 +0,0 @@
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div><div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>

View File

@ -1,29 +0,0 @@
<var name="showConditionalTab" value="data.showConditionalTab"/>
<test-tabs>
<test-tab title="Tab 1">
Tab 1 content
</test-tab>
<test-tab title="Tab 2">
Tab 2 content
</test-tab>
<test-tab title="Tab 3" if="showConditionalTab">
Tab 3 content
</test-tab>
</test-tabs>
<test-tabs-new>
<test-tab-new title="Tab 1">
Tab 1 content
</test-tab-new>
<test-tab-new title="Tab 2">
Tab 2 content
</test-tab-new>
<test-tab-new title="Tab 3" if="showConditionalTab">
Tab 3 content
</test-tab-new>
</test-tabs-new>

View File

@ -1 +1 @@
A ${'B'} C
- A ${'B'} C

View File

@ -1 +1,3 @@
---
A ${'B'} C
---

View File

@ -1,3 +1,3 @@
<!--This comment should not be preserved-->
Hello World!
- Hello World!
<!--This comment should not be preserved-->

View File

@ -1,4 +1,6 @@
---
<compiler-options preserve-whitespace />
A
B
C
---

View File

@ -0,0 +1 @@
<div class="foo"></div>

View File

@ -0,0 +1 @@
<div class="foo"/>

View File

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

View File

@ -0,0 +1 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN">

View File

@ -0,0 +1 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN">

View File

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

View File

@ -1,4 +1,4 @@
<div data-attr="Hello \"John\" <foo>">
Hello &lt;John&gt;© <![CDATA[<hello>]]>
</div>
&copy;
- &copy;

View File

@ -1 +1 @@
<test-invalid-attr invalid="true">
<test-invalid-attr invalid="true"/>

View File

@ -1 +1 @@
<test-missing-required-attr>
<test-missing-required-attr/>

View File

@ -1 +1 @@
Global: ${out.global.foo}
- Global: ${out.global.foo}

View File

@ -1 +1 @@
Hello ${data.name}! Hello $!{data.name}! Hello $!{data.missing}!
- Hello ${data.name}! Hello $!{data.name}! Hello $!{data.missing}!

View File

@ -1 +1 @@
Hello John
- Hello John

View File

@ -0,0 +1 @@
A

View File

@ -0,0 +1,3 @@
<if(true)>
A
</if>

View File

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

View File

@ -1,3 +1,4 @@
-------
<layout-use("./layout-default.marko", {showHeader: false}) show-footer=false>
<layout-put into="body">BODY CONTENT</layout-put>
<layout-put into="footer">FOOTER CONTENT</layout-put>
@ -7,3 +8,4 @@
<layout-put into="body">BODY CONTENT</layout-put>
<layout-put into="footer">FOOTER CONTENT</layout-put>
</layout-use>
-------

View File

@ -1,3 +1,4 @@
---------
<layout-use("./layout-default.marko", {showHeader: false})>
<layout-put into="body">BODY CONTENT</layout-put>
<layout-put into="footer">FOOTER CONTENT</layout-put>
@ -13,3 +14,4 @@
<layout-put into="body">BODY CONTENT2</layout-put>
<layout-put into="footer">FOOTER CONTENT2</layout-put>
</layout-use>
---------

View File

@ -1 +1,3 @@
---
Hello ${({name: "World"}).name}!
---

View File

@ -1,6 +1,8 @@
---
<template-init>
var testHelpers = require('./test-helpers')
</template-init>
Hello ${testHelpers.upperCase("world")}!
Hello ${testHelpers.trim(" World ")}!
---

View File

@ -0,0 +1 @@
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>

View File

@ -0,0 +1,15 @@
<var showConditionalTab=data.showConditionalTab/>
<test-tabs-new>
<test-tab-new title="Tab 1">
Tab 1 content
</test-tab-new>
<test-tab-new title="Tab 2">
Tab 2 content
</test-tab-new>
<test-tab-new title="Tab 3" if(showConditionalTab)>
Tab 3 content
</test-tab-new>
</test-tabs-new>

View File

@ -0,0 +1 @@
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>

View File

@ -0,0 +1,15 @@
<var showConditionalTab=data.showConditionalTab/>
<test-tabs>
<test-tab title="Tab 1">
Tab 1 content
</test-tab>
<test-tab title="Tab 2">
Tab 2 content
</test-tab>
<test-tab title="Tab 3" if(showConditionalTab)>
Tab 3 content
</test-tab>
</test-tabs>

View File

@ -0,0 +1,3 @@
exports.templateData = {
"showConditionalTab": false
};

View File

@ -1 +1 @@
<test-tag-code-generator-return-self name="Frank" foo="bar"/><test-tag-code-generator-return-self name="John" foo="bar"/>
<test-tag-code-generator-return-self name="Frank" foo="bar"></test-tag-code-generator-return-self><test-tag-code-generator-return-self name="John" foo="bar"></test-tag-code-generator-return-self>

View File

@ -1,3 +1,5 @@
---
<var person=data.person/>
Hello ${person.name}. You are from ${person.address.city}, ${person.address.state} Zero: ${data.zero}
---

View File

@ -1,3 +1,4 @@
---
<unless(true)>
A
</unless>
@ -31,3 +32,4 @@
<div else>
C
</div>
---

View File

@ -3,4 +3,6 @@
${y}
${z}
</var>
---
${typeof x}
---

View File

@ -1 +1 @@
<p>A <i>B</i> C</p> --- <p>D <i>E</i> F</p> --- <p>G <i>H</i> I</p> --- <p>J <div>K</div> L <div>M</div> N</p> --- <p><div>O</div><div>P</div><span>Q</span> <span>R</span> </p>
<p>A <i>B</i> C</p> --- <p>D <i>E</i> F</p> --- <p>G <i>H</i> I</p> --- <p>J <div>K</div> L <div>M</div> N</p> --- <p><div>O</div><div>P</div><span>Q</span> <span>R</span></p>

View File

@ -1,3 +1,4 @@
------
<p>A <i>B</i> C</p>
---
<p>
@ -26,3 +27,4 @@
<span>Q</span> <span>R</span>
</p>
------

View File

@ -1,3 +1,4 @@
---
${"BEGIN this whitespace should be retained END"}
test <!-- text should be normalized to one space -->
@ -30,12 +31,4 @@ should <!-- This whitespace should be normalized --> retain spacing between l
begin <!-- this whitespace should not be normalized --> end
</div>
<if(true)>begin <!-- this whitespace should be preserved -->end</if>
<!--
- In not "xml:space" === "preserve":
- newline followed by whitespace should be removed
- More than one whitespace should be normalized into a single space (or new line character?)
- Certain elements should preserve whitespace (e.g. PRE elements)
- Allow for a xml:space="preserve" attribute
- http://xhtml.com/en/css/reference/white-space/
- When should whitespace removal happen? When generating code?
-->
---

View File

@ -1 +1,3 @@
---
scanned-b: Hello ${data.name}
---

View File

@ -8,4 +8,6 @@ TAG = {
}
}
-->
---
scanned-d: Hello ${data.NAME}
---

View File

@ -0,0 +1,6 @@
{
"import-var": {
"tabs": "__tabsHelper"
},
"@title": "string"
}

View File

@ -0,0 +1,4 @@
exports.render = function(input, out) {
var tabs = input.tabs;
tabs.addTab(input);
};

View File

@ -0,0 +1,6 @@
{
"import-var": {
"tabs": "tabs"
},
"@title": "string"
}

View File

@ -0,0 +1,4 @@
exports.render = function(input, out) {
var tabs = input.tabs;
tabs.addTab(input);
};

View File

@ -0,0 +1,3 @@
{
"body-function": "buildTabs(__tabsHelper)"
}

View File

@ -0,0 +1,33 @@
var template = require('./template.marko');
exports.render = function(input, out) {
var tabs = [],
activeFound = false;
if (input.buildTabs) {
input.buildTabs({
addTab: function(tab) {
if (tab.active) {
tab.activeFound = true;
}
tab.id = "tab" + tabs.length;
tabs.push(tab);
}
});
}
if (!activeFound && tabs.length) {
tabs[0].active = true;
}
tabs.forEach(function(tab) {
tab.liClass = tab.active ? "active" : "";
tab.divClass = tab.active ? "tab-pane active" : "tab-pane";
});
template.render({
tabs: tabs
}, out);
};

View File

@ -0,0 +1,16 @@
<var tabs=data.tabs/>
<div class="tabs">
<ul class="nav nav-tabs">
<li class="${tab.liClass}" for(tab in tabs)>
<a href="#${tab.id}" data-toggle="tab">
${tab.title}
</a>
</li>
</ul>
<div class="tab-content">
<div id="${tab.id}" class="${tab.divClass}" for(tab in tabs)>
<invoke tab.renderBody(out) if(tab.renderBody) />
</div>
</div>
</div>

View File

@ -0,0 +1,3 @@
{
"var": "tabs"
}

View File

@ -0,0 +1,31 @@
var template = require('./template.marko');
exports.render = function(input, out) {
var tabs = [],
activeFound = false;
input.renderBody(out, {
addTab: function(tab) {
if (tab.active) {
tab.activeFound = true;
}
tab.id = "tab" + tabs.length;
tabs.push(tab);
}
});
if (!activeFound && tabs.length) {
tabs[0].active = true;
}
tabs.forEach(function(tab) {
tab.liClass = tab.active ? "active" : "";
tab.divClass = tab.active ? "tab-pane active" : "tab-pane";
});
template.render({
tabs: tabs
}, out);
};

View File

@ -0,0 +1,16 @@
<var tabs=data.tabs/>
<div class="tabs">
<ul class="nav nav-tabs">
<li class="${tab.liClass}" for(tab in tabs)>
<a href="#${tab.id}" data-toggle="tab">
${tab.title}
</a>
</li>
</ul>
<div class="tab-content">
<div id=tab.id class=tab.divClass for(tab in tabs)>
<invoke tab.renderBody(out) />
</div>
</div>
</div>

View File

@ -1 +1,3 @@
---
Hello ${data.name}!
---

View File

@ -1 +1,3 @@
---
Hello ${data.name}!
---