Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions packages/package-loader-node/src/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,14 @@ export function declaredExternalPackageRoot(root: string, from: string | URL, na
while (true) {
const manifest = join(cursor, "package.json");
if (existsSync(manifest)) {
const value = JSON.parse(readFileSync(manifest, "utf8")) as { dependencies?: Record<string, string> };
const version = value.dependencies?.[name];
const value = JSON.parse(readFileSync(manifest, "utf8")) as {
dependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
};
// An upstream asset a package ships as optional is still selected by one exact version here.
// `hypit packages install` places it under the machine npm root named by this selection, so
// reading only the required map makes the documented repair unusable for every optional asset.
const version = value.dependencies?.[name] ?? value.optionalDependencies?.[name];
return version === undefined ? undefined : externalPackageInstallRoot(root, name, version);
}
const parent = dirname(cursor);
Expand Down
32 changes: 32 additions & 0 deletions packages/package-loader-node/test/location.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,35 @@ test("package Source resolution reads one public export without activating packa
await rm(project, { recursive: true, force: true });
}
});

test("a machine npm fallback resolves an upstream asset the importer declares optionally", async () => {
const distribution = await mkdtemp(join(tmpdir(), "hypit-optional-distribution-"));
const machine = await mkdtemp(join(tmpdir(), "hypit-optional-machine-"));
try {
const asset = await machinePackage(
externalPackageInstallRoot(machine, "optional-only", "3.2.1"), "optional-only",
{ version: "3.2.1" }, { "files/asset.css": "/* asset */\n" });
const importer = join(distribution, "packages", "fonts", "surface.mjs");
await mkdir(join(importer, ".."), { recursive: true });
// `hypit packages install <name>@<version>` tells the author to install exactly this asset, so
// the version selection must read the same declaration the install was addressed to.
await writeFile(join(importer, "..", "package.json"), JSON.stringify({
optionalDependencies: { "optional-only": "3.2.1" },
}));
const options = {
from: importer,
distributionRoots: [distribution],
externalRoots: [machine],
} as const;

const canonical = await realpath(asset);
assert.equal(locateNodePackage("optional-only", options).root, canonical);
assert.equal(
resolveNodePackageResource("optional-only", "files/asset.css", options),
join(canonical, "files", "asset.css"),
);
} finally {
await rm(distribution, { recursive: true, force: true });
await rm(machine, { recursive: true, force: true });
}
});
Loading