use TemplateLiteral node

This commit is contained in:
Michael Rawlings 2018-12-03 10:53:41 -08:00
parent eb51003030
commit 1eb2842d23
20 changed files with 160 additions and 87 deletions

6
package-lock.json generated
View File

@ -2957,9 +2957,9 @@
}
},
"htmljs-parser": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/htmljs-parser/-/htmljs-parser-2.3.2.tgz",
"integrity": "sha1-HMW/mCSgkcKIILM+r3gIOo6qhWw=",
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/htmljs-parser/-/htmljs-parser-2.5.0.tgz",
"integrity": "sha512-1P/WMHVYA+hZdVPmkwlvzBR/rp/VDX4X41Jyd94NpSz9l4QH5V8sOIXdtpLbUdvgI2IucpITIflW7iLgL5oKkA==",
"requires": {
"char-props": "^0.1.5",
"complain": "^1.0.0"

View File

@ -40,7 +40,7 @@
"events": "^1.0.2",
"events-light": "^1.0.0",
"he": "^1.1.0",
"htmljs-parser": "^2.3.2",
"htmljs-parser": "^2.5.0",
"lasso-caching-fs": "^1.0.1",
"lasso-modules-client": "^2.0.4",
"lasso-package-root": "^1.0.1",

View File

@ -40,6 +40,7 @@ var ArrayExpression = require("./ast/ArrayExpression");
var Property = require("./ast/Property");
var VariableDeclarator = require("./ast/VariableDeclarator");
var ThisExpression = require("./ast/ThisExpression");
var TemplateLiteral = require("./ast/TemplateLiteral");
var Expression = require("./ast/Expression");
var Scriptlet = require("./ast/Scriptlet");
var ContainerNode = require("./ast/ContainerNode");
@ -53,7 +54,6 @@ var parseExpression = require("./util/parseExpression");
var parseStatement = require("./util/parseStatement");
var parseJavaScriptArgs = require("./util/parseJavaScriptArgs");
var parseJavaScriptParams = require("./util/parseJavaScriptParams");
var replacePlaceholderEscapeFuncs = require("./util/replacePlaceholderEscapeFuncs");
var isValidJavaScriptIdentifier = require("./util/isValidJavaScriptIdentifier");
var DEFAULT_BUILDER;
@ -487,8 +487,8 @@ class Builder {
return parsed;
}
replacePlaceholderEscapeFuncs(node, context) {
return replacePlaceholderEscapeFuncs(node, context);
replacePlaceholderEscapeFuncs(node) {
return node;
}
program(body) {
@ -563,6 +563,10 @@ class Builder {
return new BinaryExpression({ left, right, operator });
}
templateLiteral(quasis, expressions) {
return new TemplateLiteral({ quasis, expressions });
}
templateRoot(body) {
return new TemplateRoot({ body });
}

View File

@ -21,17 +21,21 @@ class HtmlJsParser {
event.escape
);
}
} else if (event.withinOpenTag || event.withinTagName) {
// Don't escape placeholder for dynamic attributes. For example: <div ${data.myAttrs}></div>
} else {
// placeholder within attribute
if (event.escape) {
event.value = "$escapeXml(" + event.value + ")";
} else {
event.value = "$noEscapeXml(" + event.value + ")";
}
}
// placeholder within content
},
onString(event) {
if (!event.isStringLiteral) {
let value = "";
event.stringParts.forEach(part => {
if (part.type === "placeholder") {
value += "${" + part.value + "}";
} else {
value += part.replace(/`/g, "\\`");
}
});
event.value = "$nonstandard`" + value + "`";
}
},
onCDATA(event) {

View File

@ -2,7 +2,6 @@
var ok = require("assert").ok;
var extend = require("raptor-util/extend");
var Normalizer = require("./Normalizer");
var replacePlaceholderEscapeFuncs = require("./util/replacePlaceholderEscapeFuncs");
var COMPILER_ATTRIBUTE_HANDLERS = {
"preserve-whitespace": function(attr, context) {
@ -269,10 +268,7 @@ class Parser {
}
if (valid) {
attrValue = replacePlaceholderEscapeFuncs(
parsedExpression,
context
);
attrValue = parsedExpression;
} else {
attrValue = null;
}

View File

@ -47,7 +47,8 @@ class Html extends Node {
}
}
generateHTMLCode() {
generateHTMLCode(codegen) {
this.argument = codegen.generateCode(this.argument);
return this;
}

View File

@ -148,6 +148,9 @@ module.exports = function generateCode(node, codegen) {
return builder.htmlLiteral(attr(name, literalValue));
} else if (value != null) {
if (value.type === "TemplateLiteral") {
value.nonstandard = false;
}
return generateCodeForExpressionAttr(name, value, escape, codegen);
} else if (argument) {
return [

View File

@ -4,6 +4,10 @@ module.exports = function generateCode(node, codegen, vdomUtil) {
var context = codegen.context;
var builder = codegen.builder;
if (node.value && node.value.type === "TemplateLiteral") {
node.value.nonstandard = false;
}
// node.name = codegen.generateCode(node.name);
node.value = codegen.generateCode(node.value);
node.isStatic = vdomUtil.isStaticValue(node.value);

View File

@ -0,0 +1,72 @@
"use strict";
var Node = require("./Node");
class TemplateLiteral extends Node {
constructor(def) {
super("TemplateLiteral");
this.quasis = def.quasis;
this.expressions = def.expressions;
this.nonstandard = false;
}
generateCode(codegen) {
const context = codegen.context;
const builder = context.builder;
const parts = [];
for (let i = 0; i <= this.quasis.length; i++) {
const quasi = this.quasis[i];
const expr = this.expressions[i];
if (quasi || (i === 0 && !this.nonstandard)) {
parts.push(builder.literal(quasi));
}
if (expr) {
parts.push(codegen.generateCode(expr));
}
}
if (parts.length === 1) return parts[0];
let expression = builder.binaryExpression(parts[0], "+", parts[1]);
for (let i = 2; i < parts.length; i++) {
expression = builder.binaryExpression(expression, "+", parts[i]);
}
return expression;
}
writeCode(writer) {
for (let i = 0; i <= this.quasis.length; i++) {
const quasi = this.quasis[i];
const expr = this.expressions[i];
if (quasi || i === 0) {
if (i > 0) writer.write("+");
writer.write(JSON.stringify(quasi));
}
if (expr) {
writer.write("+");
writer.write("(");
writer.write(expr);
writer.write(")");
}
}
writer.write("\n");
}
toString() {
let code = "";
let quote = this.nonstandard ? '"' : "`";
let escape = new RegExp(quote, "g");
for (let i = 0; i <= this.quasis.length; i++) {
const quasi = this.quasis[i];
const expr = this.expressions[i];
if (quasi) code += quasi.replace(escape, `\\${quote}`);
if (expr) code += "${" + expr.toString() + "}";
}
return quote + code + quote;
}
}
module.exports = TemplateLiteral;

View File

@ -144,6 +144,24 @@ function parseExpression(src, builder, isExpression) {
return builder.literal(literalValue);
}
case "TaggedTemplateExpression": {
if (node.tag.name === "$nonstandard") {
const quasi = convert(node.quasi);
if (quasi) {
quasi.nonstandard = true;
return quasi;
}
}
return null;
}
case "TemplateLiteral": {
const quasis = node.quasis.map(q => q.value.cooked);
const expressions = convert(node.expressions);
if (expressions) {
return builder.templateLiteral(quasis, expressions);
}
return null;
}
case "LogicalExpression": {
let left = convert(node.left);
if (!left) {

View File

@ -1,26 +0,0 @@
var AttributePlaceholder = require("../ast/AttributePlaceholder");
module.exports = function replacePlaceholderEscapeFuncs(node, context) {
var walker = context.createWalker({
exit: function(node) {
if (
node.type === "FunctionCall" &&
node.callee.type === "Identifier"
) {
if (node.callee.name === "$noEscapeXml") {
return new AttributePlaceholder({
escape: false,
value: node.args[0]
});
} else if (node.callee.name === "$escapeXml") {
return new AttributePlaceholder({
escape: true,
value: node.args[0]
});
}
}
}
});
return walker.walk(node);
};

View File

@ -118,10 +118,7 @@ module.exports = function handleComponentEvents() {
return;
}
targetMethod = builder.replacePlaceholderEscapeFuncs(
parsedArgs[0],
context
);
targetMethod = parsedArgs[0];
if (parsedArgs.length > 1) {
extraArgs = parsedArgs.slice(1);

View File

@ -1,5 +1,3 @@
const replacePlaceholderEscapeFuncs = require("../../compiler/util/replacePlaceholderEscapeFuncs");
module.exports = function codeGenerator(elNode, context) {
const attributes = elNode.attributes;
const builder = context.builder;
@ -17,18 +15,15 @@ module.exports = function codeGenerator(elNode, context) {
elNode.replaceWith(
builder.scriptlet({
value: replacePlaceholderEscapeFuncs(
builder.parseExpression(
elNode.attributes
.map(
attr =>
attr.value == null
? attr.name
: `${attr.name} = ${attr.rawValue}`
)
.join(", ")
),
context
value: builder.parseExpression(
elNode.attributes
.map(
attr =>
attr.value == null
? attr.name
: `${attr.name} = ${attr.rawValue}`
)
.join(", ")
)
})
);

View File

@ -1,5 +1,4 @@
const isValidJavaScriptVarName = require("../../compiler/util/isValidJavaScriptVarName");
const replacePlaceholderEscapeFuncs = require("../../compiler/util/replacePlaceholderEscapeFuncs");
module.exports = function nodeFactory(elNode, context) {
const attributes = elNode.attributes;
@ -50,10 +49,7 @@ module.exports = function nodeFactory(elNode, context) {
let parsedExpression = val;
if (val != null) {
parsedExpression = replacePlaceholderEscapeFuncs(
builder.parseExpression(val),
context
);
parsedExpression = builder.parseExpression(val);
}
return builder.variableDeclarator(name, parsedExpression);

View File

@ -7,7 +7,7 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
marko_defineComponent = components_helpers.c,
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_classAttr = marko_helpers.ca,
marko_str = marko_helpers.s,
marko_attr = marko_helpers.a,
marko_escapeXmlAttr = marko_helpers.xa;
function render(input, out, __component, component, state) {
@ -15,17 +15,14 @@ function render(input, out, __component, component, state) {
out.w("<div" +
marko_classAttr(input.className) +
" class2=\"" +
marko_str(input.className) +
"\" foo=\"a" +
marko_attr("class2", "" + input.className) +
" foo=\"a" +
marko_escapeXmlAttr(input.foo) +
"b\" bar=\"a " +
marko_escapeXmlAttr(input.foo) +
" b\" baz=\"a " +
marko_str(input.foo) +
" b\" nested=\"a " +
marko_str(input.foo + ("nested " + input.bar)) +
" b\"></div>");
"b\"" +
marko_attr("bar", ("a " + input.foo) + " b") +
marko_attr("baz", ("a " + input.foo) + " b") +
marko_attr("nested", ("a " + (input.foo + ("nested " + input.bar))) + " b") +
"></div>");
}
marko_template._ = marko_renderer(render, {

View File

@ -6,14 +6,14 @@ var marko_template = module.exports = require("marko/src/html").t(__filename),
marko_renderer = components_helpers.r,
marko_defineComponent = components_helpers.c,
marko_helpers = require("marko/src/runtime/html/helpers"),
marko_escapeXmlAttr = marko_helpers.xa;
marko_attr = marko_helpers.a;
function render(input, out, __component, component, state) {
var data = input;
out.w("<div foo=\"Hello " +
marko_escapeXmlAttr(input.name) +
"\"></div>");
out.w("<div" +
marko_attr("foo", "Hello " + input.name) +
"></div>");
var foo = "Hello " + input.name;
}

View File

@ -0,0 +1 @@
<div data-foo=input.foo/>

View File

@ -0,0 +1 @@
<div data-foo='{"name":"Frank"}'></div><div data-foo='{"name":"Frank"}'></div>

View File

@ -0,0 +1,9 @@
$ var foo = {
name: 'Frank',
toString: function() {
return this.name;
}
};
<tag foo="${foo}"/>
<tag foo=foo/>

View File

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