Files
rea/src/domain/javascript/javascriptVitePreloadDependencies.test.ts
T
Wade d8199ab6af fix(javascript): keep Vite dependency slots after a spread unknown (#4023)
The __vite__mapDeps table indexer mapped a spread element to one unknown slot, so every later entry kept a definite index even though the spread can add any number of elements. A call such as __vite__mapDeps([0, 2]) over ["./first.js", ...["./middle-a.js", "./middle-b.js"], "./last.js"] then resolved index 2 to last.js instead of middle-b.js. Keep only the entries before the first spread; indexes from the spread onward stay unknown.
2026-10-22 03:50:42 +08:00

102 lines
3.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
describe("Vite preload dependency recovery", () => {
it("decodes helper table values and preserves sparse and dynamic slots", () => {
const source = String.raw`
const __vite__mapDeps = (indexes, map = __vite__mapDeps, dependencies =
(map.f || (map.f = ["./caf\u00e9.js", , getDependency(), "./last.js"]))) =>
indexes.map((index) => dependencies[index]);
const dynamicIndex = chooseIndex();
__vite__mapDeps([0, , 3, dynamicIndex]);
`;
const analysis = analyzeJavaScriptStaticSource(source);
const references = analysis.references.filter(
({ kind }) => kind === "dynamic-import",
);
expect(references.map(({ specifier }) => specifier).sort()).toEqual([
"./café.js",
"./last.js",
null,
]);
expect(references.every(({ location }) => location.start.line === 6)).toBe(
true,
);
expect(references.filter(({ specifier }) => specifier === null)).toEqual([
expect.objectContaining({
expression: expect.stringContaining(
"__vite__mapDeps([0, , 3, dynamicIndex])",
),
}),
]);
});
it("does not take dependency strings from comments or unrelated helpers", () => {
const analysis = analyzeJavaScriptStaticSource(`
// cache.f = ["./comment.js"]
const unrelated = (cache) => cache.f || (cache.f = ["./unrelated.js"]);
__vite__mapDeps([0]);
`);
expect(
analysis.references.filter(({ kind }) => kind === "dynamic-import"),
).toEqual([]);
});
it("keeps cache escapes and explicit cache overrides unknown", () => {
const helper = `
const __vite__mapDeps = (indexes, map = __vite__mapDeps, dependencies =
(map.f || (map.f = ["./original.js"]))) =>
indexes.map((index) => dependencies[index]);
`;
const escaped = analyzeJavaScriptStaticSource(`
${helper}
const dependencies = __vite__mapDeps.f;
dependencies[0] = "./changed.js";
__vite__mapDeps([0]);
`);
expect(
escaped.references
.filter(({ kind }) => kind === "dynamic-import")
.map(({ specifier }) => specifier),
).toEqual([null]);
const overridden = analyzeJavaScriptStaticSource(`
${helper}
__vite__mapDeps([0], customMap);
`);
expect(
overridden.references
.filter(({ kind }) => kind === "dynamic-import")
.map(({ specifier }) => specifier),
).toEqual([null]);
const defaulted = analyzeJavaScriptStaticSource(`
${helper}
__vite__mapDeps([0], undefined, void 0);
`);
expect(
defaulted.references
.filter(({ kind }) => kind === "dynamic-import")
.map(({ specifier }) => specifier),
).toEqual(["./original.js"]);
});
it("keeps table slots from a spread onward unknown", () => {
const analysis = analyzeJavaScriptStaticSource(`
const __vite__mapDeps = (indexes, map = __vite__mapDeps, dependencies =
(map.f || (map.f = ["./first.js", ...["./middle-a.js", "./middle-b.js"], "./last.js"]))) =>
indexes.map((index) => dependencies[index]);
__vite__mapDeps([0, 2]);
`);
expect(
analysis.references
.filter(({ kind }) => kind === "dynamic-import")
.map(({ specifier }) => specifier),
).toEqual(["./first.js", null]);
});
});