From 4f87f02bb19bf77349a683a5bcb2da2d1da427d0 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Fri, 16 Oct 2026 04:41:26 +0800 Subject: [PATCH] fix(wasm): retain cooked directive literals in glue references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collect leading directive literals using Babel cooked values, preserving source locations and selected candidate identities. Exercise escaped directives through the existing provider workflow. Refs: #4001, #4149 Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com> --- src/wasm/WabtArtifactProvider.test.ts | 40 +++++++++++++++++------- src/wasm/WabtOutput.test.ts | 45 +++++++++++++++++++++++++++ src/wasm/WasmGlueAdapter.ts | 13 ++++++-- 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/src/wasm/WabtArtifactProvider.test.ts b/src/wasm/WabtArtifactProvider.test.ts index e7dc2a515..206e9ac73 100644 --- a/src/wasm/WabtArtifactProvider.test.ts +++ b/src/wasm/WabtArtifactProvider.test.ts @@ -275,12 +275,17 @@ it.each([false, true])( ); const assertGlueObservations = ( value: unknown, - inputs: readonly { readonly path: string; readonly source: string }[], + inputs: readonly { + readonly path: string; + readonly source: string; + readonly line: number; + readonly column: number; + }[], selectedPath: string, ): void => { const glue = wasmArtifactSchema.parse(value).glue; expect(glue).toHaveLength(inputs.length); - for (const [index, { path, source }] of inputs.entries()) { + for (const [index, { path, source, line, column }] of inputs.entries()) { expect(glue[index]).toMatchObject({ artifact: { path, @@ -291,8 +296,8 @@ const assertGlueObservations = ( references: [ { value: "./selected.wasm", - line: source.includes("\n") ? 2 : 1, - column: source.startsWith("\uFEFFfetch") ? 7 : 6, + line, + column, candidate_paths: [selectedPath], association: "local-path-candidate", evidence_kind: "static-literal", @@ -403,19 +408,30 @@ it.each([ }, ); const literal = 'fetch("./selected.wasm");'; - const glueInputs = ( + const glueSources = mode === "success" ? [ - literal, - `\uFEFF${literal}`, - `\uFEFF\n${literal}`, - `#!/usr/bin/env node\n${literal}`, - `\uFEFF#!/usr/bin/env node\n${literal}`, + { source: literal, line: 1, column: 6 }, + { source: `\uFEFF${literal}`, line: 1, column: 7 }, + { source: `\uFEFF\n${literal}`, line: 2, column: 6 }, + { source: `#!/usr/bin/env node\n${literal}`, line: 2, column: 6 }, + { + source: `\uFEFF#!/usr/bin/env node\n${literal}`, + line: 2, + column: 6, + }, + { + source: String.raw`"./selected\u002ewasm";`, + line: 1, + column: 0, + }, ] - : [] - ).map((source, index) => ({ + : []; + const glueInputs = glueSources.map(({ source, line, column }, index) => ({ path: join(selectedRoot, `glue-${index}.js`), source, + line, + column, })); await Promise.all( glueInputs.map(({ path, source }) => writeFile(path, source)), diff --git a/src/wasm/WabtOutput.test.ts b/src/wasm/WabtOutput.test.ts index 536769bb2..d69cafa73 100644 --- a/src/wasm/WabtOutput.test.ts +++ b/src/wasm/WabtOutput.test.ts @@ -284,6 +284,51 @@ it("keeps exact local, ambiguous URL, computed and unparseable glue distinct", ( }); }); +it("retains directive literals and matches their cooked escaped path", () => { + const candidates = ["/app/module.wasm"]; + const directive = associateWasmGlue( + '"module.wasm";\n', + "/app/glue.js", + candidates, + ); + expect(directive.references).toMatchObject([ + { + value: "module.wasm", + line: 1, + column: 0, + candidate_paths: candidates, + association: "local-path-candidate", + evidence_kind: "static-literal", + }, + ]); + + const escaped = String.raw`"mod\u0075le\x2ewasm";`; + expect( + associateWasmGlue(escaped, "/app/glue.js", candidates).references, + ).toMatchObject([ + { + value: "module.wasm", + line: 1, + column: 0, + candidate_paths: candidates, + association: "local-path-candidate", + evidence_kind: "static-literal", + }, + ]); + expect( + associateWasmGlue(`;${escaped}`, "/app/glue.js", candidates).references, + ).toMatchObject([ + { + value: "module.wasm", + line: 1, + column: 1, + candidate_paths: candidates, + association: "local-path-candidate", + evidence_kind: "static-literal", + }, + ]); +}); + it("matches a CLI-selected ./ module exactly against its glue reference", () => { // The CLI makes `./module.wasm` absolute from /app with absoluteCallerPath. const selected = absoluteCallerPath("./module.wasm", { diff --git a/src/wasm/WasmGlueAdapter.ts b/src/wasm/WasmGlueAdapter.ts index aac966495..f249c232c 100644 --- a/src/wasm/WasmGlueAdapter.ts +++ b/src/wasm/WasmGlueAdapter.ts @@ -25,11 +25,18 @@ export const associateWasmGlue = ( if (file === null) return { parse_status: "failed" as const, references }; traverseJavaScriptAst(file, { enter(node) { + // Babel retains the source spelling in DirectiveLiteral.value, while + // extra.expressionValue is the same cooked value used by StringLiteral. + const directiveValue = t.isDirectiveLiteral(node) + ? node.extra?.expressionValue + : undefined; const value = t.isStringLiteral(node) ? node.value - : t.isTemplateLiteral(node) && node.expressions.length === 0 - ? node.quasis[0]?.value.cooked - : undefined; + : typeof directiveValue === "string" + ? directiveValue + : t.isTemplateLiteral(node) && node.expressions.length === 0 + ? node.quasis[0]?.value.cooked + : undefined; if ( value === undefined || value === null ||