fix(javascript): preserve unknown Next package ownership

This commit is contained in:
morluto committed 2026-10-27 07:40:45 +08:00
1 parent 6a9911ea77
commit c74059d89f
4 files changed
+221 -17

No files matched your search

@@ -305,9 +305,10 @@ export const reconstructJavaScriptArtifact = async (
.map(({ logical_path }) => logical_path),
);
const nextAppContext = createJavaScriptNextAppContext(
files.files,
files,
analysis.jsonDocuments,
directoryPaths,
snapshot.coverage,
);
const completed = {
route_inventory: await completeJavaScriptAnalysisSteps(
@@ -4,6 +4,18 @@ import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptA
import { JavaScriptJsonDocuments } from "./JavaScriptJsonDocuments.js";
import { createJavaScriptNextAppContext } from "./JavaScriptNextAppContext.js";
const contextFor = (
files: readonly JavaScriptArtifactFile[],
documents: JavaScriptJsonDocuments,
directories: ReadonlySet<string>,
) =>
createJavaScriptNextAppContext(
{ files, unavailable_relevant_files: [] },
documents,
directories,
{ scope: "container-entries", status: "complete" },
);
const file = (path: string, text = ""): JavaScriptArtifactFile => ({
path,
container_sha256: "a".repeat(64),
@@ -23,7 +35,7 @@ it("keeps package applicability and its root cache scoped to the source containe
};
const page = file("app/page.js");
const foreignPage = { ...page, container_sha256: foreign.container_sha256 };
const context = createJavaScriptNextAppContext(
const context = contextFor(
[manifest, foreign, page, foreignPage],
new JavaScriptJsonDocuments(),
new Set(["app"]),
@@ -49,7 +61,7 @@ it("uses the nearest declared Next package and root directory precedence", () =>
file("node_modules/ordinary/package.json", '{"name":"ordinary"}'),
file("node_modules/ordinary/app/page.js"),
];
const context = createJavaScriptNextAppContext(
const context = contextFor(
files,
new JavaScriptJsonDocuments(),
new Set([
@@ -76,7 +88,7 @@ it("uses the nearest declared Next package and root directory precedence", () =>
it("keeps default compiled output under its declared package and unrelated chunk-like paths unknown", () => {
const manifest = file("package.json", '{"dependencies":{"next":"16.4.2"}}');
const chunk = file(".next/static/chunks/app/build/page-abc.js");
const context = createJavaScriptNextAppContext(
const context = contextFor(
[manifest, chunk],
new JavaScriptJsonDocuments(),
new Set(),
@@ -86,11 +98,7 @@ it("keeps default compiled output under its declared package and unrelated chunk
directory: ".next/static/chunks/app",
});
expect(
createJavaScriptNextAppContext(
[chunk],
new JavaScriptJsonDocuments(),
new Set(),
)(chunk),
contextFor([chunk], new JavaScriptJsonDocuments(), new Set())(chunk),
).toMatchObject({ kind: "unknown" });
});
@@ -101,17 +109,97 @@ it("supports src/app only when app is not inventoried and retains missing framew
);
const page = file("src/app/control/page.js");
expect(
createJavaScriptNextAppContext(
contextFor(
[manifest, page],
new JavaScriptJsonDocuments(),
new Set(["src/app"]),
)(page),
).toMatchObject({ kind: "app-directory", directory: "src/app" });
expect(
createJavaScriptNextAppContext(
contextFor(
[page],
new JavaScriptJsonDocuments(),
new Set(["src/app"]),
)(page),
).toMatchObject({ kind: "unknown", package: null });
});
it("does not infer absent manifests or higher-priority directories from incomplete traversal", () => {
const manifest = file("package.json", '{"dependencies":{"next":"16.4.2"}}');
const page = file("src/app/control/page.js");
const files = { files: [manifest, page], unavailable_relevant_files: [] };
const context = createJavaScriptNextAppContext(
files,
new JavaScriptJsonDocuments(),
new Set(["src/app"]),
{ scope: "container-entries", status: "unknown" },
);
expect(context(page)).toMatchObject({
kind: "unknown",
package: null,
reason: expect.stringContaining("src/app/control/package.json"),
});
const rootPage = file("page.js");
expect(
createJavaScriptNextAppContext(
files,
new JavaScriptJsonDocuments(),
new Set(["src/app"]),
{ scope: "container-entries", status: "unknown" },
)(rootPage),
).toMatchObject({
kind: "unknown",
package: { path: "package.json" },
reason: expect.stringContaining("higher-priority app directory"),
});
});
it("retains the nearest unavailable manifest's evidence instead of borrowing an ancestor's Next declaration", () => {
const parent = file("package.json", '{"dependencies":{"next":"16.4.2"}}');
const page = file("app/widget/app/page.js");
const unavailable = {
path: "app/widget/package.json",
container_sha256: parent.container_sha256,
sha256: null,
bytes: 42,
inventory_artifact_id: null,
inventory_occurrence_id: "unreadable-nested-package",
kind: "package-json" as const,
limitations: ["The unpacked package companion is missing."],
};
const context = createJavaScriptNextAppContext(
{ files: [parent, page], unavailable_relevant_files: [unavailable] },
new JavaScriptJsonDocuments(),
new Set(["app", "app/widget/app"]),
{ scope: "container-entries", status: "complete" },
);
expect(context(page)).toMatchObject({
kind: "unknown",
package: null,
reason: expect.stringContaining(unavailable.path),
});
const result = context(page);
if (result.kind !== "unknown")
throw new Error("Expected unknown package ownership");
expect(result.reason).toContain(unavailable.inventory_occurrence_id);
expect(result.reason).toContain(unavailable.limitations[0]);
expect(context(file("app/page.js"))).toMatchObject({
kind: "app-directory",
package: { path: parent.path },
});
const foreign = createJavaScriptNextAppContext(
{
files: [parent, page],
unavailable_relevant_files: [
{ ...unavailable, container_sha256: "c".repeat(64) },
],
},
new JavaScriptJsonDocuments(),
new Set(["app", "app/widget/app"]),
{ scope: "container-entries", status: "complete" },
);
expect(foreign(page)).toMatchObject({
kind: "app-directory",
package: { path: parent.path },
});
});
@@ -1,20 +1,36 @@
import { posix } from "node:path";
import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js";
import type { ArtifactInventoryCoverage } from "../../domain/artifactGraph.js";
import type {
JavaScriptArtifactFileSet,
JavaScriptArtifactFile,
} from "../../domain/javascript/javascriptArtifactFiles.js";
import type { NextAppSourceContext } from "../../domain/javascript/javascriptRoutePaths.js";
import { compositeKey } from "../../domain/unicodeCodePointOrder.js";
import type { JavaScriptJsonDocuments } from "./JavaScriptJsonDocuments.js";
/** Package declarations and inventoried directories own source-router applicability. */
export const createJavaScriptNextAppContext = (
files: readonly JavaScriptArtifactFile[],
fileSet: Pick<
JavaScriptArtifactFileSet,
"files" | "unavailable_relevant_files"
>,
documents: JavaScriptJsonDocuments,
directories: ReadonlySet<string>,
coverage: ArtifactInventoryCoverage,
): ((
file: Pick<JavaScriptArtifactFile, "path" | "container_sha256">,
) => NextAppSourceContext) => {
const packages = new Map(
files
fileSet.files
.filter(({ kind }) => kind === "package-json")
.map((file) => [
compositeKey([file.container_sha256, posix.dirname(file.path)]),
file,
]),
);
const unavailablePackages = new Map(
fileSet.unavailable_relevant_files
.filter(({ kind }) => kind === "package-json")
.map((file) => [
compositeKey([file.container_sha256, posix.dirname(file.path)]),
@@ -28,9 +44,15 @@ export const createJavaScriptNextAppContext = (
;
directory = posix.dirname(directory)
) {
const manifest = packages.get(
compositeKey([file.container_sha256, directory]),
);
const key = compositeKey([file.container_sha256, directory]);
const unavailable = unavailablePackages.get(key);
if (unavailable !== undefined)
return {
kind: "unknown",
package: null,
reason: `The nearest owning package manifest ${unavailable.path}${unavailable.inventory_occurrence_id === undefined ? "" : ` (inventory occurrence ${unavailable.inventory_occurrence_id})`} is unavailable: ${unavailable.limitations?.join("; ") || "Inventoried package bytes are unavailable."}`,
};
const manifest = packages.get(key);
if (manifest !== undefined) {
const identity = {
path: manifest.path,
@@ -53,6 +75,14 @@ export const createJavaScriptNextAppContext = (
directory: compiled,
package: identity,
};
// Selecting src/app requires proving that the higher-priority app is absent.
if (coverage.status !== "complete" && !directories.has(`${prefix}app`))
return {
kind: "unknown",
package: identity,
reason:
"Incomplete artifact traversal does not establish whether the owning package has a higher-priority app directory.",
};
let root = roots.get(manifest);
if (root === undefined) {
root =
@@ -65,6 +95,12 @@ export const createJavaScriptNextAppContext = (
? { kind: "app-directory", directory: root, package: identity }
: { kind: "outside-app-directory", package: identity };
}
if (coverage.status !== "complete")
return {
kind: "unknown",
package: null,
reason: `Incomplete artifact traversal does not establish whether ${directory === "." ? "" : `${directory}/`}package.json owns this source.`,
};
if (directory === "." || directory === "/") break;
}
return {