diff --git a/.jshintrc b/.jshintrc index 09913a4a5..fcba8f725 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,4 +1,7 @@ { + "predef": [ + "document" + ], "node" : true, "esnext": true, "boss" : false, diff --git a/compiler/CompileContext.js b/compiler/CompileContext.js index 737f5f599..7a1ad5fbf 100644 --- a/compiler/CompileContext.js +++ b/compiler/CompileContext.js @@ -55,6 +55,7 @@ const helpers = { 'attrs': 'as', 'classAttr': 'ca', 'classList': 'cl', + 'const': 'const', 'createElement': 'e', 'escapeXml': 'x', 'escapeXmlAttr': 'xa', @@ -516,7 +517,7 @@ class CompileContext extends EventEmitter { get helpersIdentifier() { if (!this._helpersIdentifier) { if (this.inline) { - this._helpersIdentifier = this.importModule('__markoHelpers', 'marko/runtime/helpers'); + this._helpersIdentifier = this.importModule('__markoHelpers', 'marko/runtime/html/helpers'); } else { // The helpers variable is a parameter of the outer create function this._helpersIdentifier = this.builder.identifier('__markoHelpers'); diff --git a/compiler/HtmlJsParser.js b/compiler/HtmlJsParser.js index 406d1c08e..777eb1b9a 100644 --- a/compiler/HtmlJsParser.js +++ b/compiler/HtmlJsParser.js @@ -9,7 +9,7 @@ class HtmlJsParser { parse(src, handlers) { var listeners = { onText(event) { - handlers.handleCharacters(event.value); + handlers.handleCharacters(event.value, event.parseMode); }, onPlaceholder(event) { @@ -32,7 +32,7 @@ class HtmlJsParser { }, onCDATA(event) { - handlers.handleCharacters(event.value); + handlers.handleCharacters(event.value, 'static-text'); }, onOpenTag(event, parser) { diff --git a/compiler/Parser.js b/compiler/Parser.js index ba8b93fff..2b4458042 100644 --- a/compiler/Parser.js +++ b/compiler/Parser.js @@ -110,13 +110,17 @@ class Parser { return rootNode; } - handleCharacters(text) { + handleCharacters(text, parseMode) { var builder = this.context.builder; - if (this.prevTextNode && this.prevTextNode.isLiteral()) { + var escape = parseMode !== 'html'; + // NOTE: If parseMode is 'static-text' or 'parsed-text' then that means that special + // HTML characters may not have been escaped on the way in so we need to escape + // them on the way out + + if (this.prevTextNode && this.prevTextNode.isLiteral() && this.prevTextNode.escape === escape) { this.prevTextNode.argument.value += text; } else { - var escape = false; this.prevTextNode = builder.text(builder.literal(text), escape); this.parentNode.appendChild(this.prevTextNode); } diff --git a/compiler/ast/HtmlAttribute/vdom/generateCode.js b/compiler/ast/HtmlAttribute/vdom/generateCode.js index d56d79aa2..bd8e17c6d 100644 --- a/compiler/ast/HtmlAttribute/vdom/generateCode.js +++ b/compiler/ast/HtmlAttribute/vdom/generateCode.js @@ -1,6 +1,22 @@ +'use strict'; + module.exports = function generateCode(node, codegen, vdomUtil) { - node.name = codegen.generateCode(node.name); + var context = codegen.context; + var builder = codegen.builder; + + // node.name = codegen.generateCode(node.name); node.value = codegen.generateCode(node.value); node.isStatic = vdomUtil.isStaticValue(node.value); + + var name = node.name; + + if (node.value && node.value.type !== 'Literal') { + if (name === 'class') { + node.value = builder.functionCall(context.helper('classAttr'), [node.value]); + } else if (name === 'style') { + node.value = builder.functionCall(context.helper('styleAttr'), [node.value]); + } + } + return node; }; \ No newline at end of file diff --git a/compiler/ast/HtmlComment.js b/compiler/ast/HtmlComment.js index 1c4a8295b..50084c511 100644 --- a/compiler/ast/HtmlComment.js +++ b/compiler/ast/HtmlComment.js @@ -19,6 +19,19 @@ class HtmlComment extends Node { ]; } + generateVDOMCode(codegen) { + var comment = this.comment; + var builder = codegen.builder; + + return builder.functionCall( + builder.memberExpression( + builder.identifierOut(), + builder.identifier('comment')), + [ + comment + ]); + } + walk(walker) { this.comment = walker.walk(this.comment); } diff --git a/compiler/ast/HtmlElement/html/generateCode.js b/compiler/ast/HtmlElement/html/generateCode.js index a7e41e55d..5938f1f36 100644 --- a/compiler/ast/HtmlElement/html/generateCode.js +++ b/compiler/ast/HtmlElement/html/generateCode.js @@ -13,15 +13,6 @@ module.exports = function generateCode(node, codegen) { tagName = node.tagNameExpression; } - var context = codegen.context; - - if (context.isMacro(node.tagName)) { - // At code generation time, if node tag corresponds to a registered macro - // then invoke the macro based on node HTML element instead of generating - // the code to render an HTML element. - return codegen.builder.invokeMacroFromEl(node); - } - var attributes = node._attributes && node._attributes.all; var body = node.body; var argument = node.argument; diff --git a/compiler/ast/HtmlElement/index.js b/compiler/ast/HtmlElement/index.js index e2c4059fb..450b5bcd4 100644 --- a/compiler/ast/HtmlElement/index.js +++ b/compiler/ast/HtmlElement/index.js @@ -43,10 +43,24 @@ class HtmlElement extends Node { } generateHTMLCode(codegen) { + if (codegen.context.isMacro(this.tagName)) { + // At code generation time, if node tag corresponds to a registered macro + // then invoke the macro based on node HTML element instead of generating + // the code to render an HTML element. + return codegen.builder.invokeMacroFromEl(this); + } + return generateHTMLCode(this, codegen); } generateVDOMCode(codegen) { + if (codegen.context.isMacro(this.tagName)) { + // At code generation time, if node tag corresponds to a registered macro + // then invoke the macro based on node HTML element instead of generating + // the code to render an HTML element. + return codegen.builder.invokeMacroFromEl(this); + } + return generateVDOMCode(this, codegen, vdomUtil); } diff --git a/compiler/ast/HtmlElement/vdom/HtmlElementVDOM.js b/compiler/ast/HtmlElement/vdom/HtmlElementVDOM.js index a818ca1cc..032c4940c 100644 --- a/compiler/ast/HtmlElement/vdom/HtmlElementVDOM.js +++ b/compiler/ast/HtmlElement/vdom/HtmlElementVDOM.js @@ -73,6 +73,10 @@ class HtmlElementVDOM extends Node { attributes.forEach((attr) => { let value = attr.value; + if (value == null) { + value = builder.literal(true); + } + if (!attr.name) { return; } @@ -154,7 +158,6 @@ class HtmlElementVDOM extends Node { } else if (this.isHtmlOnly) { writer.write('out.'); funcCall = builder.functionCall( - builder.identifier('e'), createArgs); } else { diff --git a/compiler/ast/HtmlElement/vdom/generateCode.js b/compiler/ast/HtmlElement/vdom/generateCode.js index 3e3631426..34b0ac8b6 100644 --- a/compiler/ast/HtmlElement/vdom/generateCode.js +++ b/compiler/ast/HtmlElement/vdom/generateCode.js @@ -21,6 +21,7 @@ module.exports = function(node, codegen, vdomUtil) { var tagName = codegen.generateCode(node.tagNameExpression); var attributes = codegen.generateCode(node.getAttributes()); var dynamicAttributes = codegen.generateCode(node.dynamicAttributes); + var builder = codegen.builder; var isAttrsStatic = checkAttributesStatic(attributes); var isStatic = isAttrsStatic && node.isLiteralTagName(); @@ -30,6 +31,9 @@ module.exports = function(node, codegen, vdomUtil) { for (var i=0; i body to avoid the `' - * }; - * - * - * - * Without escaping the ending '' sequence the opening ' + * }; + * + * + * + * Without escaping the ending '' sequence the opening ' } }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/html-comment-tag/expected.html b/test/autotests/render/html-comment-tag/expected.html index 6bd67f32a..d716f7e5f 100644 --- a/test/autotests/render/html-comment-tag/expected.html +++ b/test/autotests/render/html-comment-tag/expected.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/test/autotests/render/html-comment-tag/template.marko b/test/autotests/render/html-comment-tag/template.marko index ab8080b53..91fc52e71 100644 --- a/test/autotests/render/html-comment-tag/template.marko +++ b/test/autotests/render/html-comment-tag/template.marko @@ -1 +1 @@ -
\ No newline at end of file +This is a comment \ No newline at end of file diff --git a/test/autotests/render/html-comment-tag/test.js b/test/autotests/render/html-comment-tag/test.js index 27e834445..1908aabb0 100644 --- a/test/autotests/render/html-comment-tag/test.js +++ b/test/autotests/render/html-comment-tag/test.js @@ -1,3 +1,5 @@ exports.templateData = { "name": "World" }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/inline-script/test.js b/test/autotests/render/inline-script/test.js index 27e834445..1908aabb0 100644 --- a/test/autotests/render/inline-script/test.js +++ b/test/autotests/render/inline-script/test.js @@ -1,3 +1,5 @@ exports.templateData = { "name": "World" }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/marko-body-attr-parsed-text/expected.html b/test/autotests/render/marko-body-attr-parsed-text/expected.html index 05de41a0c..802cc5009 100644 --- a/test/autotests/render/marko-body-attr-parsed-text/expected.html +++ b/test/autotests/render/marko-body-attr-parsed-text/expected.html @@ -1 +1 @@ -
Hello Frank!
\ No newline at end of file +
<span if(foo)> Hello Frank! </span>
\ No newline at end of file diff --git a/test/autotests/render/marko-body-attr-static-text/expected.html b/test/autotests/render/marko-body-attr-static-text/expected.html index d722681df..7e3205c16 100644 --- a/test/autotests/render/marko-body-attr-static-text/expected.html +++ b/test/autotests/render/marko-body-attr-static-text/expected.html @@ -1 +1 @@ -
Hello ${THIS IS NOT VALID}!
\ No newline at end of file +
<span if(foo)> Hello ${THIS IS NOT VALID}! </span>
\ No newline at end of file diff --git a/test/autotests/render/marko-compiler-options-preserve-whitespace-trim/test.js b/test/autotests/render/marko-compiler-options-preserve-whitespace-trim/test.js index c4013b344..651f5cf87 100644 --- a/test/autotests/render/marko-compiler-options-preserve-whitespace-trim/test.js +++ b/test/autotests/render/marko-compiler-options-preserve-whitespace-trim/test.js @@ -1 +1,2 @@ exports.templateData = {}; +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/nested-tags-repeated-parent/tags/test-nested-tags-overlay/template.marko b/test/autotests/render/nested-tags-repeated-parent/tags/test-nested-tags-overlay/template.marko index c4a7bf39e..2bfa2303f 100644 --- a/test/autotests/render/nested-tags-repeated-parent/tags/test-nested-tags-overlay/template.marko +++ b/test/autotests/render/nested-tags-repeated-parent/tags/test-nested-tags-overlay/template.marko @@ -1,5 +1,5 @@
-
+
diff --git a/test/autotests/render/nested-tags/expected.html b/test/autotests/render/nested-tags/expected.html index d7e2d326b..63eaf646c 100644 --- a/test/autotests/render/nested-tags/expected.html +++ b/test/autotests/render/nested-tags/expected.html @@ -1 +1 @@ -
Header content!
Body content
\ No newline at end of file +
Header content!
Body content
\ No newline at end of file diff --git a/test/autotests/render/nested-tags/tags/test-nested-tags-overlay/template.marko b/test/autotests/render/nested-tags/tags/test-nested-tags-overlay/template.marko index c4a7bf39e..7e8bc7795 100644 --- a/test/autotests/render/nested-tags/tags/test-nested-tags-overlay/template.marko +++ b/test/autotests/render/nested-tags/tags/test-nested-tags-overlay/template.marko @@ -1,5 +1,5 @@
-
+
diff --git a/test/autotests/render/preserveWhitespace-global/test.js b/test/autotests/render/preserveWhitespace-global/test.js index 68b5bd880..8b450ded0 100644 --- a/test/autotests/render/preserveWhitespace-global/test.js +++ b/test/autotests/render/preserveWhitespace-global/test.js @@ -1,2 +1,3 @@ exports.templateData = {}; -exports.preserveWhitespaceGlobal = true; \ No newline at end of file +exports.preserveWhitespaceGlobal = true; +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/preserveWhitespace-load-option/test.js b/test/autotests/render/preserveWhitespace-load-option/test.js index 44b8f285a..82d7b4da2 100644 --- a/test/autotests/render/preserveWhitespace-load-option/test.js +++ b/test/autotests/render/preserveWhitespace-load-option/test.js @@ -2,3 +2,4 @@ exports.templateData = {}; exports.loadOptions = { preserveWhitespace: true }; +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/script-tag-entities/test.js b/test/autotests/render/script-tag-entities/test.js index 4221021f4..b25447bea 100644 --- a/test/autotests/render/script-tag-entities/test.js +++ b/test/autotests/render/script-tag-entities/test.js @@ -1,3 +1,5 @@ exports.templateData = { "name": "" }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/syntax-concise/test.js b/test/autotests/render/syntax-concise/test.js index c4013b344..40282cbb0 100644 --- a/test/autotests/render/syntax-concise/test.js +++ b/test/autotests/render/syntax-concise/test.js @@ -1 +1,3 @@ exports.templateData = {}; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/syntax-mixed/test.js b/test/autotests/render/syntax-mixed/test.js index c4013b344..40282cbb0 100644 --- a/test/autotests/render/syntax-mixed/test.js +++ b/test/autotests/render/syntax-mixed/test.js @@ -1 +1,3 @@ exports.templateData = {}; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/syntax-simple-concise/test.js b/test/autotests/render/syntax-simple-concise/test.js index 641b5f35e..bb66f142f 100644 --- a/test/autotests/render/syntax-simple-concise/test.js +++ b/test/autotests/render/syntax-simple-concise/test.js @@ -1,3 +1,5 @@ exports.templateData = { colors: ['red', 'green', 'blue'] }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/syntax-simple-mixed/test.js b/test/autotests/render/syntax-simple-mixed/test.js index 641b5f35e..bb66f142f 100644 --- a/test/autotests/render/syntax-simple-mixed/test.js +++ b/test/autotests/render/syntax-simple-mixed/test.js @@ -1,3 +1,5 @@ exports.templateData = { colors: ['red', 'green', 'blue'] }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/syntax-simple-verbose/test.js b/test/autotests/render/syntax-simple-verbose/test.js index 641b5f35e..bb66f142f 100644 --- a/test/autotests/render/syntax-simple-verbose/test.js +++ b/test/autotests/render/syntax-simple-verbose/test.js @@ -1,3 +1,5 @@ exports.templateData = { colors: ['red', 'green', 'blue'] }; + +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/syntax-verbose/test.js b/test/autotests/render/syntax-verbose/test.js index c4013b344..651f5cf87 100644 --- a/test/autotests/render/syntax-verbose/test.js +++ b/test/autotests/render/syntax-verbose/test.js @@ -1 +1,2 @@ exports.templateData = {}; +exports.vdomSkip = true; \ No newline at end of file diff --git a/test/autotests/render/whitespace-inline-elements/expected.html b/test/autotests/render/whitespace-inline-elements/expected.html index a106fdd77..0338a66c7 100644 --- a/test/autotests/render/whitespace-inline-elements/expected.html +++ b/test/autotests/render/whitespace-inline-elements/expected.html @@ -1 +1 @@ -

A B C

---

D E F

---

G H I

---

J

K
L
M
N

---

O
P
Q R

\ No newline at end of file +

A B C

---

D E F

---

G H I

---

J K L M N

---

OPQ R

\ No newline at end of file diff --git a/test/autotests/render/whitespace-inline-elements/template.marko b/test/autotests/render/whitespace-inline-elements/template.marko index 52c75e9f2..9d9ffd141 100644 --- a/test/autotests/render/whitespace-inline-elements/template.marko +++ b/test/autotests/render/whitespace-inline-elements/template.marko @@ -14,16 +14,16 @@ ---

J -

K
+ K L -
M
+ M N

---

-

O
+ O -
P
+ P Q R

diff --git a/test/autotests/vdom-compiler/attr-class-expression/expected.js b/test/autotests/vdom-compiler/attr-class-expression/expected.js new file mode 100644 index 000000000..7db822316 --- /dev/null +++ b/test/autotests/vdom-compiler/attr-class-expression/expected.js @@ -0,0 +1,19 @@ +function create(__markoHelpers) { + var marko_classList = __markoHelpers.cl, + marko_str = __markoHelpers.s, + marko_classAttr = __markoHelpers.ca; + + return function render(data, out) { + out.e("div", { + "class": marko_classAttr(marko_classList("foo", { + bar: true, + baz: false + })) + }, 1) + .t("Hello " + + marko_str(name) + + "!"); + }; +} + +(module.exports = require("marko/vdom").c(__filename)).c(create); diff --git a/test/autotests/vdom-compiler/attr-class-expression/template.marko b/test/autotests/vdom-compiler/attr-class-expression/template.marko new file mode 100644 index 000000000..2cade4e4a --- /dev/null +++ b/test/autotests/vdom-compiler/attr-class-expression/template.marko @@ -0,0 +1,3 @@ + + Hello ${name}! +
\ No newline at end of file diff --git a/test/autotests/vdom-compiler/attrs-dynamic-object-literal/expected.js b/test/autotests/vdom-compiler/attrs-dynamic-object-literal/expected.js index 4cbd3a5fa..e9f3eac6e 100644 --- a/test/autotests/vdom-compiler/attrs-dynamic-object-literal/expected.js +++ b/test/autotests/vdom-compiler/attrs-dynamic-object-literal/expected.js @@ -1,10 +1,14 @@ function create(__markoHelpers) { + var marko_str = __markoHelpers.s; + return function render(data, out) { out.e("div", { foo: "bar", hello: "world" }, 1) - .t(("Hello " + name) + "!"); + .t("Hello " + + marko_str(name) + + "!"); }; } diff --git a/test/autotests/vdom-compiler/attrs-dynamic/expected.js b/test/autotests/vdom-compiler/attrs-dynamic/expected.js index 328483100..3bc8d03e0 100644 --- a/test/autotests/vdom-compiler/attrs-dynamic/expected.js +++ b/test/autotests/vdom-compiler/attrs-dynamic/expected.js @@ -1,4 +1,6 @@ function create(__markoHelpers) { + var marko_str = __markoHelpers.s; + return function render(data, out) { var attrs = { foo: "bar", @@ -6,7 +8,9 @@ function create(__markoHelpers) { }; out.e("div", attrs, 1) - .t(("Hello " + name) + "!"); + .t("Hello " + + marko_str(name) + + "!"); }; } diff --git a/test/autotests/vdom-compiler/dynamic-body-text/expected.js b/test/autotests/vdom-compiler/dynamic-body-text/expected.js index 5e61967fd..54e41223a 100644 --- a/test/autotests/vdom-compiler/dynamic-body-text/expected.js +++ b/test/autotests/vdom-compiler/dynamic-body-text/expected.js @@ -1,11 +1,14 @@ function create(__markoHelpers) { - var marko_attrs0 = { + var marko_str = __markoHelpers.s, + marko_attrs0 = { "class": "foo" }; return function render(data, out) { out.e("div", marko_attrs0, 1) - .t(("Hello " + name) + "!"); + .t("Hello " + + marko_str(name) + + "!"); }; } diff --git a/test/autotests/vdom-compiler/no-escape/expected.js b/test/autotests/vdom-compiler/no-escape/expected.js new file mode 100644 index 000000000..360cfa202 --- /dev/null +++ b/test/autotests/vdom-compiler/no-escape/expected.js @@ -0,0 +1,13 @@ +function create(__markoHelpers) { + var marko_str = __markoHelpers.s; + + return function render(data, out) { + out.t("Hello " + + marko_str(name) + + "! "); + + out.h(marko_str(message)); + }; +} + +(module.exports = require("marko/vdom").c(__filename)).c(create); diff --git a/test/autotests/vdom-compiler/no-escape/template.marko b/test/autotests/vdom-compiler/no-escape/template.marko new file mode 100644 index 000000000..96d5a0117 --- /dev/null +++ b/test/autotests/vdom-compiler/no-escape/template.marko @@ -0,0 +1 @@ +- Hello ${name}! $!{message} \ No newline at end of file diff --git a/test/autotests/vdom-compiler/simple/expected.js b/test/autotests/vdom-compiler/simple/expected.js index c8ec91d42..57244e8a9 100644 --- a/test/autotests/vdom-compiler/simple/expected.js +++ b/test/autotests/vdom-compiler/simple/expected.js @@ -1,21 +1,24 @@ function create(__markoHelpers) { - var marko_forEach = __markoHelpers.f, - marko_createElement = require("marko/vdom/createElement"), - marko_const = require("marko/runtime/vdom/const"), + var marko_str = __markoHelpers.s, + marko_forEach = __markoHelpers.f, + marko_createElement = __markoHelpers.e, + marko_const = __markoHelpers.const, marko_const_nextId = marko_const("733fee"), marko_node0 = marko_createElement("div", null, 1, marko_const_nextId()) .t("No colors!"); return function render(data, out) { out.e("h1", null, 1) - .t(("Hello " + data.name) + "!"); + .t("Hello " + + marko_str(data.name) + + "!"); if (data.colors.length) { out.be("ul"); marko_forEach(data.colors, function(color) { out.e("li", null, 1) - .t(color); + .t(marko_str(color)); }); out.ee(); diff --git a/test/autotests/vdom-compiler/static-element-nested/expected.js b/test/autotests/vdom-compiler/static-element-nested/expected.js index b2f2459b4..06d69b47d 100644 --- a/test/autotests/vdom-compiler/static-element-nested/expected.js +++ b/test/autotests/vdom-compiler/static-element-nested/expected.js @@ -1,6 +1,7 @@ function create(__markoHelpers) { - var marko_createElement = require("marko/vdom/createElement"), - marko_const = require("marko/runtime/vdom/const"), + var marko_str = __markoHelpers.s, + marko_createElement = __markoHelpers.e, + marko_const = __markoHelpers.const, marko_const_nextId = marko_const("69a896"), marko_node0 = marko_createElement("div", { "class": "hello", @@ -11,7 +12,9 @@ function create(__markoHelpers) { return function render(data, out) { out.e("span", null, 2) .e("h1", null, 1) - .t(("Hello " + data.name) + "!") + .t("Hello " + + marko_str(data.name) + + "!") .n(marko_node0); }; } diff --git a/test/autotests/vdom-compiler/static-element-root/expected.js b/test/autotests/vdom-compiler/static-element-root/expected.js index 1ba759f0f..4b2540bb6 100644 --- a/test/autotests/vdom-compiler/static-element-root/expected.js +++ b/test/autotests/vdom-compiler/static-element-root/expected.js @@ -1,6 +1,6 @@ function create(__markoHelpers) { - var marko_createElement = require("marko/vdom/createElement"), - marko_const = require("marko/runtime/vdom/const"), + var marko_createElement = __markoHelpers.e, + marko_const = __markoHelpers.const, marko_const_nextId = marko_const("0524f9"), marko_node0 = marko_createElement("div", { "class": "hello", diff --git a/test/autotests/vdom-compiler/tag-body/expected.js b/test/autotests/vdom-compiler/tag-body/expected.js new file mode 100644 index 000000000..7fdd83e80 --- /dev/null +++ b/test/autotests/vdom-compiler/tag-body/expected.js @@ -0,0 +1,15 @@ +function create(__markoHelpers) { + var marko_loadTag = __markoHelpers.t, + test_hello = marko_loadTag(require("./tags/test-hello/renderer")); + + return function render(data, out) { + test_hello({ + name: "World", + renderBody: function renderBody(out) { + out.t("Body content"); + } + }, out); + }; +} + +(module.exports = require("marko/vdom").c(__filename)).c(create); diff --git a/test/autotests/vdom-compiler/tag-body/marko.json b/test/autotests/vdom-compiler/tag-body/marko.json new file mode 100644 index 000000000..e85a78200 --- /dev/null +++ b/test/autotests/vdom-compiler/tag-body/marko.json @@ -0,0 +1,3 @@ +{ + "tags-dir": "./tags" +} \ No newline at end of file diff --git a/test/autotests/vdom-compiler/tag-body/tags/test-hello/marko-tag.json b/test/autotests/vdom-compiler/tag-body/tags/test-hello/marko-tag.json new file mode 100644 index 000000000..e83d0748c --- /dev/null +++ b/test/autotests/vdom-compiler/tag-body/tags/test-hello/marko-tag.json @@ -0,0 +1,5 @@ +{ + "renderer": "./renderer.js", + "@name": "string", + "@adult": "boolean" +} \ No newline at end of file diff --git a/test/autotests/vdom-compiler/tag-body/tags/test-hello/renderer.js b/test/autotests/vdom-compiler/tag-body/tags/test-hello/renderer.js new file mode 100644 index 000000000..80ac9cc8d --- /dev/null +++ b/test/autotests/vdom-compiler/tag-body/tags/test-hello/renderer.js @@ -0,0 +1,2 @@ +exports.render = function(input, out) { +}; \ No newline at end of file diff --git a/test/autotests/vdom-compiler/tag-body/template.marko b/test/autotests/vdom-compiler/tag-body/template.marko new file mode 100644 index 000000000..7fcc28cdf --- /dev/null +++ b/test/autotests/vdom-compiler/tag-body/template.marko @@ -0,0 +1,3 @@ + + Body content + \ No newline at end of file diff --git a/test/patch-module.js b/test/patch-module.js index fe2d5a294..23ceed184 100644 --- a/test/patch-module.js +++ b/test/patch-module.js @@ -4,10 +4,17 @@ var Module = require('module').Module; var oldResolveFilename = Module._resolveFilename; var rootDir = nodePath.join(__dirname, '../'); + Module._resolveFilename = function(request, parent, isMain) { - if (request.startsWith('marko')) { - request = request.substring('marko'.length); - request = rootDir + request; - } + if (request.charAt(0) !== '.') { + var firstSlash = request.indexOf('/'); + var targetPackageName = firstSlash === -1 ? request : request.substring(0, firstSlash); + + if (targetPackageName === 'marko') { + request = request.substring('marko'.length); + request = rootDir + request; + } + } + return oldResolveFilename.call(this, request, parent, isMain); }; \ No newline at end of file diff --git a/test/util/domToHTML.js b/test/util/domToHTML.js new file mode 100644 index 000000000..1e4b082a3 --- /dev/null +++ b/test/util/domToHTML.js @@ -0,0 +1,117 @@ +function ltrim(s) { + return s ? s.replace(/^\s\s*/,'') : ''; +} + +function vdomToHTML(node, options) { + + // NOTE: We don't use XMLSerializer because we need to sort the attributes to correctly compare output HTML strings + // BAD: return (new XMLSerializer()).serializeToString(node); + var html = ''; + function serializeHelper(node, indent) { + if (node.nodeType === 1) { + serializeElHelper(node, indent); + } else if (node.nodeType === 3) { + serializeTextHelper(node, indent); + } else if (node.nodeType === 8) { + serializeCommentHelper(node, indent); + } else { + console.log('Invalid node:', node); + html += indent + `INVALID NODE TYPE ${node.nodeType}\n`; + // throw new Error('Unexpected node type'); + } + } + + function serializeElHelper(el, indent) { + var tagName = el.nodeName; + + if (el.namespaceURI === 'http://www.w3.org/2000/svg') { + tagName = 'svg:' + tagName; + } else if (el.namespaceURI === 'http://www.w3.org/1998/Math/MathML') { + tagName = 'math:' + tagName; + } + + html += indent + '<' + tagName; + + var attributes = el.attributes; + var attributesArray = []; + var attrName; + + if (typeof attributes.length === 'number') { + for (var i=0; i\n'; + } + + if (node.nodeType === 11 /* DocumentFragment */ || (options && options.childrenOnly)) { + var curChild = node.firstChild; + while(curChild) { + serializeHelper(curChild, ''); + curChild = curChild.nextSibling; + } + } else { + serializeHelper(node, ''); + } + + return html; +} + +module.exports = vdomToHTML; \ No newline at end of file diff --git a/test/vdom-render-test.js b/test/vdom-render-test.js new file mode 100644 index 000000000..c90ae8c49 --- /dev/null +++ b/test/vdom-render-test.js @@ -0,0 +1,139 @@ +'use strict'; +require('./patch-module'); + +var chai = require('chai'); +chai.config.includeStack = true; +var path = require('path'); +var marko = require('../'); +var markoVDOM = require('../vdom'); +var autotest = require('./autotest'); +var fs = require('fs'); +var fsExtra = require('fs-extra'); +var domToHTML = require('./util/domToHTML'); +var jsdom = require("jsdom").jsdom; + +require('../node-require').install(); + +var defaultDocument = jsdom(''); +markoVDOM.setDocument(defaultDocument); // We need this to parse HTML fragments on the server + +describe('render-vdom', function() { + var autoTestDir = path.join(__dirname, 'autotests/render'); + + autotest.scanDir( + autoTestDir, + function run(dir, helpers, done) { + require('../compiler').configure({ output: 'html' }); + + var vdomDir = path.join(dir, '../' + path.basename(dir) + '_vdom.skip'); + + fsExtra.removeSync(vdomDir); + + fsExtra.copySync(dir, vdomDir, { + filter: function(file) { + if (file.endsWith('.marko.js') || file.indexOf('.generated.') !== -1) { + return false; + } + return true; + } + }); + + var htmlTemplatePath = path.join(dir, 'template.marko'); + var vdomMainPath = path.join(vdomDir, 'test.js'); + var htmlMainPath = path.join(dir, 'test.js'); + var htmlMain = fs.existsSync(htmlMainPath) ? require(htmlMainPath) : {}; + var htmlTemplate = htmlMain.checkError ? null : marko.load(htmlTemplatePath); + + + require('../compiler').configure({ output: 'vdom' }); + + var oldDone = done; + done = function(err) { + require('../compiler').configure({ output: 'html' }); + oldDone(err); + }; + + var vdomMain = fs.existsSync(vdomMainPath) ? require(vdomMainPath) : {}; + + if (vdomMain && vdomMain.vdomSkip) { + return done(); + } + + var loadOptions = vdomMain.loadOptions; + if (loadOptions) { + loadOptions = Object.assign({}, loadOptions); + } else { + loadOptions = {}; + } + + loadOptions.output = 'vdom'; + // loadOptions.writeToDisk = false; + + if (vdomMain.writeToDisk === false) { + require('marko/compiler').defaultOptions.writeToDisk = false; + } + + if (vdomMain.preserveWhitespaceGlobal === true) { + require('marko/compiler').defaultOptions.preserveWhitespace = true; + } + + var templateSrc = fs.readFileSync(htmlTemplatePath, { encoding: 'utf8' }); + var vdomTemplatePath = path.join(vdomDir, 'template.marko'); + + try { + if (vdomMain.checkError) { + var e; + + try { + marko.load(vdomTemplatePath, templateSrc, loadOptions); + } catch(_e) { + e = _e; + var errorFile = path.join(dir, 'error.txt'); + fs.writeFileSync(errorFile, e.stack.toString(), { encoding: 'utf8' }); + } + + if (!e) { + throw new Error('Error expected'); + } + + vdomMain.checkError(e); + require('../compiler').configure({ output: 'html' }); + return done(); + } else { + var vdomTemplate = marko.load(vdomTemplatePath, loadOptions); + + var templateData = vdomMain.templateData || {}; + + var vdomTree = vdomTemplate.renderSync(templateData); + + + var expectedHtml; + + try { + expectedHtml = fs.readFileSync(path.join(dir, 'vdom-expected.html'), { encoding: 'utf8'}); + } catch(e) {} + + if (!expectedHtml) { + var html = htmlTemplate.renderSync(htmlMain.templateData || {}); + var document = jsdom('' + html + ''); + expectedHtml = domToHTML(document.body, { childrenOnly: true }); + } + + fs.writeFileSync(path.join(dir, 'vdom-expected.generated.html'), expectedHtml, { encoding: 'utf8' }); + var vdomHtml = domToHTML(vdomTree.actualize(defaultDocument)); + helpers.compare(vdomHtml, 'vdom-', '.generated.html'); + require('../compiler').configure({ output: 'html' }); + return done(); + } + } finally { + if (vdomMain.writeToDisk === false) { + delete require('marko/compiler').defaultOptions.writeToDisk; + } + + if (vdomMain.preserveWhitespaceGlobal === true) { + delete require('marko/compiler').defaultOptions.preserveWhitespace; + } + } + }); + +}); \ No newline at end of file