UNPKG

@surface/custom-element

Version:

Provides support of directives and data binding on custom elements.

68 lines (67 loc) 3.25 kB
import { assert } from "@surface/core"; import Expression, { SyntaxError, TypeGuard } from "@surface/expression"; import InterpolatedExpression from "./interpolated-expression.js"; import { forExpression } from "./patterns.js"; const expressionCache = {}; const forLoopStatementCache = {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any function parseStatement(parser, statement, expression) { try { return parser(expression); } catch (error) { assert(error instanceof SyntaxError); throw getOffsetSyntaxError(statement, expression, error); } } export function getOffsetSyntaxError(statement, expression, error) { const offset = statement.indexOf(expression); const previous = statement.substring(0, offset); const previousLines = previous.match(/\n/g)?.length ?? 0; const previousColumn = expression.includes("\n") ? 0 : previous.substring(Math.max(previous.lastIndexOf("\n") + 1, 0), offset).length; throw new SyntaxError(error.message, error.lineNumber + previousLines, error.index + previous.length, error.column + previousColumn); } export function parseExpression(expression) { if (expression in expressionCache) { return expressionCache[expression]; } return expressionCache[expression] = Expression.parse(expression); } export function parseInterpolation(expression) { if (expression in expressionCache) { return expressionCache[expression]; } return expressionCache[expression] = InterpolatedExpression.parse(expression); } export function parseDestructuredPattern(expression) { const arrowExpression = `(${expression}) => 0`; if (arrowExpression in expressionCache) { return expressionCache[arrowExpression]; } try { return expressionCache[arrowExpression] = parseExpression(arrowExpression).parameters[0]; } catch (error) { assert(error instanceof SyntaxError); const message = error.message == "Duplicate parameter name not allowed in this context" ? "Cannot redeclare block-scoped variable" : error.message; throw new SyntaxError(message, error.lineNumber, error.index - 1, expression.includes("\n") ? error.column : error.column - 1); } } export function parseForLoopStatement(expression) { if (expression in forLoopStatementCache) { return forLoopStatementCache[expression]; } if (!forExpression.test(expression)) { throw new Error("Invalid for-loop statement"); } const [, rawLeft, operator, rawRigth] = Array.from(forExpression.exec(expression)); const destructured = rawLeft.startsWith("[") || rawLeft.startsWith("{"); const left = parseStatement(destructured ? parseDestructuredPattern : parseExpression, expression, rawLeft); const right = parseStatement(parseExpression, expression, rawRigth); if (!TypeGuard.isIdentifier(left) && !TypeGuard.isArrayPattern(left) && !TypeGuard.isObjectPattern(left)) { throw getOffsetSyntaxError(expression, rawLeft, new SyntaxError("Invalid left-hand side in for-loop", 1, 0, 1)); } return forLoopStatementCache[expression] = { left, operator, right }; }