fix: parse JSX in custom widget architecture guard

This commit is contained in:
ajnart committed 2026-09-12 19:48:37 +02:00
1 parent a29ec26c27
commit cf60c9e4cf
1 file changed
+22 -6
@@ -1,5 +1,6 @@
import { readdir, readFile } from "node:fs/promises";
import { extname, join, relative, resolve } from "node:path";
import ts from "typescript-compiler-api";
const packageRoot = resolve(import.meta.dirname, "..");
const repositoryRoot = resolve(packageRoot, "../..");
@@ -33,13 +34,28 @@ for (const file of productionFiles) {
failures.push(`${name} imports a forbidden adapter package`);
}
if (extname(file) === ".tsx" && /(?:runtime|workbench|custom-widgets|custom-api)/u.test(name)) {
for (const line of source.split("\n")) {
const match = line.match(/>\s*([A-Za-z][A-Za-z0-9 .,!?'-]*)\s*</u);
if (match) failures.push(`${name} contains untranslated JSX text: ${JSON.stringify(match[1]?.trim())}`);
}
if (/\b(?:aria-label|description|label|placeholder|title)=["'][A-Za-z]/u.test(source)) {
failures.push(`${name} contains an untranslated user-facing attribute`);
const parsed = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
function checkJsx(node) {
if (ts.isJsxText(node)) {
// Preserve the inline-literal rule without interpreting TypeScript arrows or generics as JSX.
const literal = source.slice(node.pos - 1, node.end + 1);
for (const line of literal.split("\n")) {
const match = line.match(/>\s*([A-Za-z][A-Za-z0-9 .,!?'-]*)\s*</u);
if (match) failures.push(`${name} contains untranslated JSX text: ${JSON.stringify(match[1]?.trim())}`);
}
}
if (
ts.isJsxAttribute(node) &&
/^(?:aria-label|description|label|placeholder|title)$/u.test(node.name.getText(parsed)) &&
node.initializer &&
ts.isStringLiteral(node.initializer) &&
/^[A-Za-z]/u.test(node.initializer.text)
) {
failures.push(`${name} contains an untranslated user-facing attribute`);
}
ts.forEachChild(node, checkJsx);
}
checkJsx(parsed);
}
const dependencies = [];
for (const match of source.matchAll(/from\s+["'](\.[^"']+)["']/gu)) {