@surface/custom-element
Version:
Provides support of directives and data binding on custom elements.
68 lines (67 loc) • 3.25 kB
JavaScript
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 };
}