mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
* fix(javascript): resolve Vite build-output preload dependencies With an absolute base, Vite writes __vite__mapDeps entries relative to the build output directory (for example assets/chunk.js), and its preload helper prefixes the configured base at runtime. REA emitted these entries as dynamic-import module specifiers, so a scheme-less path without a leading dot resolved as a bare package and every entry was reported as an unresolved "external" module, even when the build output directory was selected. The static analyzer now marks such entries with a vite-output: specifier, as Webpack chunk loads are marked with chunk:. Entries starting with ./, ../ or /, and scheme-qualified entries, keep their previous handling. The graph resolves a marked entry as a URL path under each ancestor directory of the importing file; exactly one ancestor holding the file establishes the build output directory. Otherwise the entry is not-found (or unavailable when several ancestors match) with a limitation that names the build output directory. On a synthetic 7-file Vite build, the 12 preload entries that were "external" now resolve when the build output directory is selected, and are not-found with that guidance when only assets/ is selected. * fix(javascript): keep unanalyzed Vite output candidates resolveViteOutputDependency considered only resolved ancestors. A file that exists under an ancestor but was not analyzed (for example dist/assets/lazy.css for the entry assets/lazy.css) resolves there as unavailable, and that result was replaced by the root's not-found with a limitation saying that no ancestor holds the file. Keep resolved and unavailable ancestor candidates together. One candidate is returned with its own path and limitations; several are reported as unavailable with every candidate path and limitation. Only when no ancestor can hold the file does the root's not-found result stand.
146 lines
4.9 KiB
TypeScript
146 lines
4.9 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.each([
|
|
["type DependencyHelper = typeof __vite__mapDeps;", "./original.js"],
|
|
["const labels = { __vite__mapDeps() {} };", "./original.js"],
|
|
["const labels = { [__vite__mapDeps]: true };", null],
|
|
["const dependencyTable = __vite__mapDeps.f;", null],
|
|
])(
|
|
"distinguishes an erased type query from a runtime helper escape: %s",
|
|
(read, expected) => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const __vite__mapDeps = (indexes, map = __vite__mapDeps, dependencies =
|
|
(map.f || (map.f = ["./original.js"]))) =>
|
|
indexes.map((index) => dependencies[index]);
|
|
${read}
|
|
__vite__mapDeps([0]);
|
|
`);
|
|
expect(
|
|
analysis.references
|
|
.filter(({ kind }) => kind === "dynamic-import")
|
|
.map(({ specifier }) => specifier),
|
|
).toEqual([expected]);
|
|
},
|
|
);
|
|
|
|
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]);
|
|
});
|
|
|
|
it("marks build-output paths apart from importer-relative module URLs", () => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const __vite__mapDeps = (indexes, map = __vite__mapDeps, dependencies =
|
|
(map.f || (map.f = ["assets/lazy.js", "./near.js", "https://cdn.example/x.js", "/abs.js"]))) =>
|
|
indexes.map((index) => dependencies[index]);
|
|
__vite__mapDeps([0, 1, 2, 3]);
|
|
`);
|
|
|
|
expect(
|
|
analysis.references
|
|
.filter(({ kind }) => kind === "dynamic-import")
|
|
.map(({ specifier }) => specifier)
|
|
.sort(),
|
|
).toEqual([
|
|
"./near.js",
|
|
"/abs.js",
|
|
"https://cdn.example/x.js",
|
|
"vite-output:assets/lazy.js",
|
|
]);
|
|
});
|
|
});
|