51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import { Parser } from "acorn";
|
|
import jsx from "acorn-jsx";
|
|
|
|
import type { AstNode } from "./interpreter-foundation";
|
|
import { asNode, asNodeArray, SafeJsxError } from "./interpreter-foundation";
|
|
import { CUSTOM_JSX_LIMITS } from "./policy";
|
|
|
|
const JsxParser = Parser.extend(jsx());
|
|
|
|
export function parseCustomJsxTemplate(template: string): AstNode {
|
|
if (template.length > CUSTOM_JSX_LIMITS.templateLength) {
|
|
throw new SafeJsxError(`Template exceeds the ${CUSTOM_JSX_LIMITS.templateLength} character limit`);
|
|
}
|
|
let program: AstNode;
|
|
try {
|
|
program = JsxParser.parse(`<>${template}</>`, {
|
|
ecmaVersion: "latest",
|
|
sourceType: "module",
|
|
allowAwaitOutsideFunction: false,
|
|
allowReturnOutsideFunction: false,
|
|
}) as unknown as AstNode;
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : "Unable to parse JSX template";
|
|
throw new SafeJsxError(
|
|
/Argument name clash/iu.test(message)
|
|
? "DUPLICATE_LOCAL_BINDING: Callback parameter names must be unique"
|
|
: message,
|
|
);
|
|
}
|
|
const body = asNodeArray(program.body, "program body");
|
|
if (body.length !== 1 || body[0]?.type !== "ExpressionStatement") {
|
|
throw new SafeJsxError("Template must contain JSX only");
|
|
}
|
|
return asNode(body[0].expression, "template expression");
|
|
}
|
|
|
|
export function normalizeCustomJsxText(value: string): string {
|
|
const lines = value.replace(/\r/g, "").split("\n");
|
|
let result = "";
|
|
for (let index = 0; index < lines.length; index += 1) {
|
|
let line = lines[index] ?? "";
|
|
line = line.replace(/\t/g, " ");
|
|
if (index !== 0) line = line.replace(/^\s+/, "");
|
|
if (index !== lines.length - 1) line = line.replace(/\s+$/, "");
|
|
if (!line) continue;
|
|
if (result) result += " ";
|
|
result += line;
|
|
}
|
|
return result;
|
|
}
|