UNPKG

derby

Version:

MVC framework making it easy to write realtime, collaborative applications that run in both Node.js and browsers.

264 lines (263 loc) 9.81 kB
Object.defineProperty(exports, "__esModule", { value: true }); exports.createPathExpression = void 0; var esprima = require("esprima-derby"); var templates_1 = require("../templates"); var util_1 = require("../templates/util"); var Syntax = esprima.Syntax; function createPathExpression(source) { // @ts-expect-error `parse` not declared in @types/esprima var parsed = esprima.parse(source); var node = parsed.expression; return reduce(node); } exports.createPathExpression = createPathExpression; function reduce(node) { var type = node.type; if (type === Syntax.MemberExpression) { return reduceMemberExpression(node); } else if (type === Syntax.Identifier) { return reduceIdentifier(node); } else if (type === Syntax.ThisExpression) { return reduceThis(node); } else if (type === Syntax.CallExpression) { return reduceCallExpression(node); } else if (type === Syntax.Literal) { return reduceLiteral(node); } else if (type === Syntax.UnaryExpression) { return reduceUnaryExpression(node); } else if (type === Syntax.BinaryExpression || type === Syntax.LogicalExpression) { return reduceBinaryExpression(node); } else if (type === Syntax.ConditionalExpression) { return reduceConditionalExpression(node); } else if (type === Syntax.ArrayExpression) { return reduceArrayExpression(node); } else if (type === Syntax.ObjectExpression) { return reduceObjectExpression(node); } else if (type === Syntax.SequenceExpression) { return reduceSequenceExpression(node); } else if (type === Syntax.NewExpression) { return reduceNewExpression(node); } unexpected(node); } function reduceMemberExpression(node, afterSegments) { if (node.computed) { // Square brackets if (node.property.type === Syntax.Literal) { return reducePath(node, node.property.value, afterSegments); } var before_1 = reduce(node.object); var inside = reduce(node.property); return new templates_1.expressions.BracketsExpression(before_1, inside, afterSegments); } // Dot notation if (node.property.type === Syntax.Identifier) { return reducePath(node, node.property.name); } unexpected(node); } function reducePath(node, segment, afterSegments) { var segments = [segment]; if (afterSegments) segments = segments.concat(afterSegments); var relative = false; while ((node = node.object)) { if (node.type === Syntax.MemberExpression) { if (node.computed) { return reduceMemberExpression(node, segments); } else if (node.property.type === Syntax.Identifier) { segments.unshift(node.property.name); } else { unexpected(node); } } else if (node.type === Syntax.Identifier) { segments.unshift(node.name); } else if (node.type === Syntax.ThisExpression) { relative = true; } else if (node.type === Syntax.CallExpression) { return reduceCallExpression(node, segments); } else if (node.type === Syntax.SequenceExpression) { return reduceSequenceExpression(node, segments); } else if (node.type === Syntax.NewExpression) { return reduceNewExpression(node, segments); } else { unexpected(node); } } return (relative) ? new templates_1.expressions.RelativePathExpression(segments) : createSegmentsExpression(segments); } function reduceIdentifier(node) { var segments = [node.name]; return createSegmentsExpression(segments); } function reduceThis(_node) { var segments = []; return new templates_1.expressions.RelativePathExpression(segments); } function createSegmentsExpression(segments) { var firstSegment = segments[0]; var firstChar = firstSegment.charAt && firstSegment.charAt(0); if (firstChar === '#') { var alias = firstSegment; segments.shift(); return new templates_1.expressions.AliasPathExpression(alias, segments); } else if (firstChar === '@') { var attribute = firstSegment.slice(1); segments.shift(); return new templates_1.expressions.AttributePathExpression(attribute, segments); } else { return new templates_1.expressions.PathExpression(segments); } } function reduceCallExpression(node, afterSegments) { return reduceFnExpression(node, afterSegments, templates_1.expressions.FnExpression); } function reduceNewExpression(node, afterSegments) { return reduceFnExpression(node, afterSegments, templates_1.expressions.NewExpression); } function reduceFnExpression(node, afterSegments, Constructor) { var args = node.arguments.map(reduce); var callee = node.callee; if (callee.type === Syntax.Identifier) { if (callee.name === '$at') { return new templates_1.expressions.ScopedModelExpression(args[0]); } return new Constructor([callee.name], args, afterSegments); } else if (callee.type === Syntax.MemberExpression) { var segments = reduceMemberExpression(callee).segments; return new Constructor(segments, args, afterSegments); } else { unexpected(node); } } function reduceLiteral(node) { return new templates_1.expressions.LiteralExpression(node.value); } function reduceUnaryExpression(node) { // `-` and `+` can be either unary or binary, so all unary operators are // postfixed with `U` to differentiate var operator = node.operator + 'U'; var expression = reduce(node.argument); if (expression instanceof templates_1.expressions.LiteralExpression) { var fn = templates_1.operatorFns.get[operator]; expression.value = fn(expression.value); return expression; } return new templates_1.expressions.OperatorExpression(operator, [expression]); } function reduceBinaryExpression(node) { var operator = node.operator; var left = reduce(node.left); var right = reduce(node.right); if (left instanceof templates_1.expressions.LiteralExpression && right instanceof templates_1.expressions.LiteralExpression) { var fn = templates_1.operatorFns.get[operator]; var value = fn(left.value, right.value); return new templates_1.expressions.LiteralExpression(value); } return new templates_1.expressions.OperatorExpression(operator, [left, right]); } function reduceConditionalExpression(node) { var test = reduce(node.test); var consequent = reduce(node.consequent); var alternate = reduce(node.alternate); if (test instanceof templates_1.expressions.LiteralExpression && consequent instanceof templates_1.expressions.LiteralExpression && alternate instanceof templates_1.expressions.LiteralExpression) { var value = (test.value) ? consequent.value : alternate.value; return new templates_1.expressions.LiteralExpression(value); } return new templates_1.expressions.OperatorExpression('?', [test, consequent, alternate]); } function reduceArrayExpression(node) { var literal = []; var items = []; var isLiteral = true; for (var i = 0; i < node.elements.length; i++) { var expression = reduce(node.elements[i]); items.push(expression); if (isLiteral && expression instanceof templates_1.expressions.LiteralExpression) { literal.push(expression.value); } else { isLiteral = false; } } return (isLiteral) ? new templates_1.expressions.LiteralExpression(literal) : new templates_1.expressions.ArrayExpression(items); } function isProperty(property) { return property.type === Syntax.Property; } function reduceObjectExpression(node) { var literal = {}; var properties = {}; var isLiteral = true; for (var i = 0; i < node.properties.length; i++) { var property = node.properties[i]; if (isProperty(property)) { var key = getKeyName(property.key); var expression = reduce(property.value); (0, util_1.checkKeyIsSafe)(key); properties[key] = expression; if (isLiteral && expression instanceof templates_1.expressions.LiteralExpression) { literal[key] = expression.value; } else { isLiteral = false; } } else { // actually a estree.SpreadElement and not supported unexpected(node); } } return (isLiteral) ? new templates_1.expressions.LiteralExpression(literal) : new templates_1.expressions.ObjectExpression(properties); } function getKeyName(node) { return (node.type === Syntax.Identifier) ? node.name : (node.type === Syntax.Literal) ? node.value : unexpected(node); } function reduceSequenceExpression(node, afterSegments) { // Note that sequence expressions are not reduced to a literal if they only // contain literals. There isn't any utility to such an expression, so it // isn't worth optimizing. // // The fact that expressions separated by commas always parse into a sequence // is relied upon in parsing template tags that have comma-separated // arguments following a keyword var args = node.expressions.map(reduce); return new templates_1.expressions.SequenceExpression(args, afterSegments); } function unexpected(node) { throw new Error('Unexpected Esprima node: ' + JSON.stringify(node, null, 2)); }