From 156eeb0b988d659c7583fe377c7dd727d4b0ceed Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Sun, 14 Oct 2018 22:52:41 +1100 Subject: [PATCH] Implement classes with syntax like "div.my-class.my-class2() {}", improve handling of reserved words in tag or attributes, allowing quotes if still not handled --- page.jst | 2 +- src/expression.js | 5 +- src/lval.js | 10 +- template.js | 337 +++++++++++++++++++++++++++------------------- 4 files changed, 212 insertions(+), 142 deletions(-) diff --git a/page.jst b/page.jst index c031dcd..722c626 100644 --- a/page.jst +++ b/page.jst @@ -1,4 +1,4 @@ -async _env => html(lang=_env.lang val=_env.val quoted-val="\"val\"") { +async _env => html.'cls-1'.cls-2(lang=_env.lang true=_env.val something-else="\"val\"") { head {} body { `hello diff --git a/src/expression.js b/src/expression.js index 60d9168..5a35146 100644 --- a/src/expression.js +++ b/src/expression.js @@ -264,7 +264,8 @@ pp.parseSubscripts = function(base, startPos, startLoc, noCalls) { if ((computed = this.eat(tt.bracketL)) || this.eat(tt.dot)) { let node = this.startNodeAt(startPos, startLoc) node.object = base - node.property = computed ? this.parseExpression() : this.parseIdent(true) + //node.property = computed ? this.parseExpression() : this.parseIdent(true) + node.property = computed ? this.parseExpression() : this.type === tt.string ? this.parseLiteral(this.value) : this.parseIdent(true) // Nick node.computed = !!computed if (computed) this.expect(tt.bracketR) base = this.finishNode(node, "MemberExpression") @@ -400,7 +401,7 @@ pp.parseExprAtom = function(refDestructuringErrors) { return this.parseTemplate() default: - this.unexpected() + return this.parseIdent(true) // Nick this.unexpected() } } diff --git a/src/lval.js b/src/lval.js index 847ba36..e5ba610 100644 --- a/src/lval.js +++ b/src/lval.js @@ -80,14 +80,15 @@ pp.toAssignable = function(node, isBinding, refDestructuringErrors) { this.raise(node.start, "Assigning to rvalue") case "BinaryExpression": // Nick + case "Literal": // Nick let temp = node while ( temp.type === "BinaryExpression" && temp.operator === "-" && - temp.right.type === "Identifier" + (temp.right.type === "Identifier" || temp.right.type === "Literal") ) temp = temp.left - if (temp.type !== "Identifier") + if (temp.type !== "Identifier" && temp.type !== "Literal") this.raise(node.start, "Assigning to rvalue") break } @@ -247,14 +248,15 @@ pp.checkLVal = function(expr, bindingType = BIND_NONE, checkClashes) { this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue") case "BinaryExpression": // Nick + case "Literal": // Nick let temp = expr while ( temp.type === "BinaryExpression" && temp.operator === "-" && - temp.right.type === "Identifier" + (temp.right.type === "Identifier" || temp.right.type === "Literal") ) temp = temp.left - if (temp.type !== "Identifier") + if (temp.type !== "Identifier" && temp.type !== "Literal") this.raise(expr.start, (bindingType ? "Binding" : "Assigning to") + " rvalue") break } diff --git a/template.js b/template.js index 816101b..0d273c5 100644 --- a/template.js +++ b/template.js @@ -2,49 +2,90 @@ let assert = require('assert') let transform = require('./transform') let html_escape = require('html-escape') -let html_body = (node, st, c) => { - let tag, arguments - if (node.tag.type === 'CallExpression') { - tag = node.tag.callee - arguments = node.tag.arguments +let expr_to_tag = (node, context, html_allowed, call_allowed) => { + if (node.type === 'Identifier') + context.name.push(node.name) + else if (node.type === 'Literal') + context.name.push(node.value.toString()) + else if (node.type === 'BinaryExpression' && node.operator === '-') { + if (!expr_to_tag(node.left, context, false, false)) + return false; + if (!expr_to_tag(node.right, context, html_allowed, call_allowed)) + return false; } - else { - tag = node.tag - arguments = [] + else if (node.type === 'MemberExpression') { + if (!expr_to_tag(node.object, context, false, false)) + return false; + context.tag[context.tag_type].push(context.name.join('-')) + context.name = [] + context.tag_type = 1 + if (!expr_to_tag(node.property, context, html_allowed, call_allowed)) + return false; } - assert(tag.type === 'Identifier') + else if (html_allowed && node.type === 'HTMLExpression') { + if (!expr_to_tag(node.tag, context, false, true)) + return false; + context.tag[context.tag_type].push(context.name.join('-')) + context.body = node.body + } + else if (call_allowed && node.type === 'CallExpression') { + if (!expr_to_tag(node.callee, context, false, false)) + return false; + context.arguments = node.arguments + } + else + return false; + return true; +} + +let expr_to_name = (node, name) => { + if (node.type === 'Identifier') + name.push(node.name) + else if (node.type === 'Literal') + name.push(node.value.toString()) + else if (node.type === 'BinaryExpression' && node.operator === '-') { + expr_to_name(node.left, name) + expr_to_name(node.right, name) + } + else + assert(false) +} + +let html_body = (context, st, c) => { + assert(context.tag[0].length == 1) + let tag = context.tag[0][0] + let prefix = '<' + tag + + if (context.tag[1].length) + prefix += ' class="' + context.tag[1].join(' ') + '"' + + let expr = undefined + for (var i = 0; i < context.arguments.length; ++i) { + let argument = context.arguments[i] - let prefix = '<' + tag.name, expr = undefined, expr1; - for (var i = 0; i < arguments.length; ++i) { let name_expr, value_expr - if (arguments[i].type === 'AssignmentExpression') { - name_expr = arguments[i].left - value_expr = arguments[i].right + if ( + argument.type === 'AssignmentExpression' && + argument.operator === '=' + ) { + name_expr = argument.left + value_expr = c(argument.right, st, 'Expression') } else { - name_expr = arguments[i] + name_expr = argument value_expr = undefined } - let name = '' - while ( - name_expr.type === 'BinaryExpression' && - name_expr.operator === '-' && - name_expr.right.type === 'Identifier' - ) { - name = '-' + name_expr.right.name + name - name_expr = name_expr.left - } - assert(name_expr.type === 'Identifier') - name = name_expr.name + name - prefix += ' ' + name - + name = [] + expr_to_name(name_expr, name) + prefix += ' ' + name.join('-') + if (value_expr !== undefined) { prefix += '="' if (value_expr.type === 'Literal') prefix += html_escape(value_expr.value) else { - expr1 = { + let expr1 = { type: 'Literal', value: prefix } @@ -54,9 +95,7 @@ let html_body = (node, st, c) => { type: 'BinaryExpression', left: expr, operator: '+', - right: expr1, - start: node.start, - end: node.end + right: expr1 }, operator: '+', right: { @@ -88,7 +127,8 @@ let html_body = (node, st, c) => { prefix += '"' } } - expr1 = { + + let expr1 = { type: 'Literal', value: prefix + '>', } @@ -98,6 +138,7 @@ let html_body = (node, st, c) => { operator: '+', right: expr1, } + let result = [ { type: 'ExpressionStatement', @@ -120,13 +161,13 @@ let html_body = (node, st, c) => { ] } } - ].concat(c(node.body, st, 'Statement').body) + ].concat(c(context.body, st, 'Statement').body) if ( - tag.name !== 'br' && - tag.name !== 'img' && - tag.name !== 'input' && - tag.name !== 'link' && - tag.name !== 'meta' + tag !== 'br' && + tag !== 'img' && + tag !== 'input' && + tag !== 'link' && + tag !== 'meta' ) result.push( { @@ -148,7 +189,7 @@ let html_body = (node, st, c) => { arguments: [ { type: 'Literal', - value: '' + value: '' } ] } @@ -159,111 +200,137 @@ let html_body = (node, st, c) => { let visitors = Object.assign({}, transform.visitors) let visitors_ExpressionStatement = visitors.ExpressionStatement -visitors.ExpressionStatement = (node, st, c) => - node.expression.type === 'Literal' || - node.expression.type === 'TemplateLiteral' || - node.expression.type === 'TaggedTemplateLiteral' ? - { - type: 'ExpressionStatement', - expression: { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { - type: 'Identifier', - name: '_out' - }, - property: { - type: 'Identifier', - name: 'push' - }, - computed: false - }, - arguments: [ - { - type: 'CallExpression', - callee: { +visitors.ExpressionStatement = (node, st, c) => { + if ( + node.expression.type === 'Literal' || + node.expression.type === 'TemplateLiteral' || + node.expression.type === 'TaggedTemplateLiteral' + ) + return { + type: 'ExpressionStatement', + expression: { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { type: 'Identifier', - name: '_esc' + name: '_out' }, - arguments: [ - c(node.expression, st, 'Expression') - ] - } - ] - } - } : - node.expression.type === 'HTMLExpression' ? - { - type: 'BlockStatement', - body: html_body(node.expression, st, c) - } : - visitors_ExpressionStatement(node, st, c) -visitors.HTMLExpression = (node, st, c) => { - return { - type: 'CallExpression', - callee: { - type: 'ArrowFunctionExpression', - id: null, - expression: false, - generator: false, - async: false, - params: [], - body: { - type: 'BlockStatement', - body: [ + property: { + type: 'Identifier', + name: 'push' + }, + computed: false + }, + arguments: [ { - type: 'VariableDeclaration', - declarations: [ - { - type: 'VariableDeclarator', - id: { - type: 'Identifier', - name: '_out' - }, - init: { - type: 'ArrayExpression', - elements: [] - } - } - ], - kind: 'let' + type: 'CallExpression', + callee: { + type: 'Identifier', + name: '_esc' + }, + arguments: [ + c(node.expression, st, 'Expression') + ] } ] - .concat(html_body(node, st, c)) - .concat( - [ - { - type: 'ReturnStatement', - argument: { - type: 'CallExpression', - callee: { - type: 'MemberExpression', - object: { - type: 'Identifier', - name: '_out' - }, - property: { - type: 'Identifier', - name: 'join' - }, - computed: false - }, - arguments: [ + } + } + if ( + node.expression.type === 'BinaryExpression' || + node.expression.type === 'MemberExpression' || + node.expression.type === 'HTMLExpression' + ) { + context = {name: [], tag: [[], []], tag_type: 0, arguments: []} + if ( + expr_to_tag(node.expression, context, true, false) && + context.body !== undefined + ) + return { + type: 'BlockStatement', + body: html_body(context, st, c) + } + } + return visitors_ExpressionStatement(node, st, c) +} +let visitors_Expression = visitors.Expression +visitors.Expression = (node, st, c) => { + if ( + node.type === 'BinaryExpression' || + node.type === 'MemberExpression' || + node.type === 'HTMLExpression' + ) { + context = {name: [], tag: [[], []], tag_type: 0, arguments: []} + if ( + expr_to_tag(node, context, true, false) && + context.body !== undefined + ) + return { + type: 'CallExpression', + callee: { + type: 'ArrowFunctionExpression', + id: null, + expression: false, + generator: false, + async: false, + params: [], + body: { + type: 'BlockStatement', + body: [ + { + type: 'VariableDeclaration', + declarations: [ { - type: 'Literal', - value: '', - raw: '\'\'' + type: 'VariableDeclarator', + id: { + type: 'Identifier', + name: '_out' + }, + init: { + type: 'ArrayExpression', + elements: [] + } } - ] + ], + kind: 'let' } - } - ] - ) + ] + .concat(html_body(context, st, c)) + .concat( + [ + { + type: 'ReturnStatement', + argument: { + type: 'CallExpression', + callee: { + type: 'MemberExpression', + object: { + type: 'Identifier', + name: '_out' + }, + property: { + type: 'Identifier', + name: 'join' + }, + computed: false + }, + arguments: [ + { + type: 'Literal', + value: '', + raw: '\'\'' + } + ] + } + } + ] + ) + } + }, + arguments: [] } - }, - arguments: [] } + return visitors_Expression(node, st, c) } module.exports = visitors -- 2.34.1