fix(javascript): keep recovered invalid templates dynamic in semantic effects (#3815)
dataEffectLiteralString asked the literal owner for an exact value, and when a template recovered with an invalid escape had no cooked value it fell back to the raw spelling. Child-process commands, request endpoints and event names then became resolved values that are not JavaScript strings. Use only the owner's cooked value; a template without one stays dynamic. Fixes #3361 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0781716788
commit
4135c911d3
2 files changed
+45
-4
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
|
||||
|
||||
const effects = (call: (value: string) => string, value: string) => {
|
||||
const ir = analyzeJavaScriptSemantics(`
|
||||
const { spawn } = require("child_process");
|
||||
const { EventEmitter } = require("events");
|
||||
${call(value)}
|
||||
`);
|
||||
return {
|
||||
commands: ir.childProcessSpawns.map(({ command }) => command),
|
||||
endpoints: ir.requestOperations.map(({ endpoint }) => endpoint),
|
||||
events: ir.eventOperations.map(({ eventName }) => eventName),
|
||||
};
|
||||
};
|
||||
|
||||
const calls = (value: string) =>
|
||||
[
|
||||
`spawn(${value});`,
|
||||
`fetch(${value});`,
|
||||
`new EventEmitter().on(${value}, () => 1);`,
|
||||
].join("\n");
|
||||
|
||||
it("keeps recovered templates without a cooked value dynamic", () => {
|
||||
// `\\unicode` is an invalid escape: Babel recovers it with `cooked: null`.
|
||||
expect(effects(calls, "`\\unicode`")).toEqual({
|
||||
commands: [null],
|
||||
endpoints: [null],
|
||||
events: [null],
|
||||
});
|
||||
// Valid templates keep their cooked value, including escapes.
|
||||
expect(effects(calls, "`ready\\n`")).toEqual({
|
||||
commands: ["ready\n"],
|
||||
endpoints: ["ready\n"],
|
||||
events: ["ready\n"],
|
||||
});
|
||||
});
|
||||
@@ -924,7 +924,11 @@ export const dataEffectPrimitive = (
|
||||
return null;
|
||||
};
|
||||
|
||||
/** Read one static string or expression-free template. */
|
||||
/**
|
||||
* Read one static string or expression-free template. A template recovered
|
||||
* with an invalid escape has no cooked value; its raw spelling is not a
|
||||
* JavaScript string, so it stays dynamic.
|
||||
*/
|
||||
export const dataEffectLiteralString = (
|
||||
node:
|
||||
| t.Expression
|
||||
@@ -935,9 +939,8 @@ export const dataEffectLiteralString = (
|
||||
): string | null => {
|
||||
if (node === undefined) return null;
|
||||
const literal = readExactJavaScriptLiteral(node);
|
||||
if (literal.found && typeof literal.value === "string") return literal.value;
|
||||
return t.isTemplateLiteral(node) && node.expressions.length === 0
|
||||
? (node.quasis[0]?.value.raw ?? null)
|
||||
return literal.found && typeof literal.value === "string"
|
||||
? literal.value
|
||||
: null;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user