fix(ui): detect composed accessibility copy

This commit is contained in:
Vincent Koc
2026-07-28 08:12:41 +02:00
parent 8f40fac955
commit 7e0f43a5b3
2 changed files with 35 additions and 10 deletions
+32 -9
View File
@@ -78,6 +78,28 @@ function pushRawCopySegments(
}
}
function collectStaticStringSegments(node: ts.Expression): string[] {
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
return [node.text];
}
if (ts.isTemplateExpression(node)) {
return [node.head.text, ...node.templateSpans.map((span) => span.literal.text)];
}
if (ts.isParenthesizedExpression(node)) {
return collectStaticStringSegments(node.expression);
}
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
return [...collectStaticStringSegments(node.left), ...collectStaticStringSegments(node.right)];
}
if (ts.isConditionalExpression(node)) {
return [
...collectStaticStringSegments(node.whenTrue),
...collectStaticStringSegments(node.whenFalse),
];
}
return [];
}
async function walkSourceFiles(dir: string): Promise<string[]> {
const entries = await readdir(dir, { withFileTypes: true });
const files: string[] = [];
@@ -154,16 +176,17 @@ export function collectControlUiRawCopyFromSource(params: {
nameArg &&
valueArg &&
(ts.isStringLiteral(nameArg) || ts.isNoSubstitutionTemplateLiteral(nameArg)) &&
RAW_COPY_ATTRIBUTE_NAMES.has(nameArg.text) &&
(ts.isStringLiteral(valueArg) || ts.isNoSubstitutionTemplateLiteral(valueArg))
RAW_COPY_ATTRIBUTE_NAMES.has(nameArg.text)
) {
pushRawCopyFinding(findings, {
kind: "html-attribute",
line: toLine(valueArg.getStart(sourceFile)),
name: nameArg.text,
path: repoPath,
text: valueArg.text,
});
for (const text of collectStaticStringSegments(valueArg)) {
pushRawCopyFinding(findings, {
kind: "html-attribute",
line: toLine(valueArg.getStart(sourceFile)),
name: nameArg.text,
path: repoPath,
text,
});
}
}
}
if (ts.isTaggedTemplateExpression(node) && node.tag.getText(sourceFile) === "html") {
+3 -1
View File
@@ -214,7 +214,7 @@ describe("control-ui-i18n process runner", () => {
it("finds raw text and attributes split by template interpolation", () => {
const source =
'const jsx = <button aria-label="Archive" />; const view = html`<button title="Delete ${name}">Delete ${name}</button>`; const image = html`<img alt="Preview" />`; menu.setAttribute("aria-label", "Selection actions");';
'const jsx = <button aria-label="Archive" />; const view = html`<button title="Delete ${name}">Delete ${name}</button>`; const image = html`<img alt="Preview" />`; menu.setAttribute("aria-label", "Selection actions"); reply.setAttribute("aria-label", `Reply to ${name}`); file.setAttribute("title", "Open " + fileName);';
const sourceFile = ts.createSourceFile(
"ui/src/pages/example.ts",
source,
@@ -235,6 +235,8 @@ describe("control-ui-i18n process runner", () => {
{ kind: "html-attribute", text: "Delete" },
{ kind: "html-text", text: "Delete" },
{ kind: "html-attribute", text: "Selection actions" },
{ kind: "html-attribute", text: "Reply to" },
{ kind: "html-attribute", text: "Open" },
]);
});