Skip to content

Commit

Permalink
implement stable tree
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Feb 4, 2023
1 parent 508807c commit 1da3ef2
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 57 deletions.
118 changes: 68 additions & 50 deletions src/rollup/plugins/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { PackageJson } from "pkg-types";
import { nodeFileTrace, NodeFileTraceOptions } from "@vercel/nft";
import type { Plugin } from "rollup";
import { resolvePath, isValidNodeImport, normalizeid } from "mlly";
// import semver from "semver";
import semver from "semver";
import { isDirectory } from "../../utils";

export interface NodeExternalsOptions {
Expand Down Expand Up @@ -324,7 +324,10 @@ export function externals(opts: NodeExternalsOptions): Plugin {
const linkPackage = async (from: string, to: string) => {
const src = join(opts.outDir, "node_modules", from);
const dst = join(opts.outDir, "node_modules", to);
if (existsSync(dst)) {
const dstStat = await fsp.lstat(dst).catch(() => null);
const exists = dstStat && dstStat.isSymbolicLink();
// console.log("Linking", from, "to", to, exists ? "!!!!" : "");
if (exists) {
return;
}
await fsp.mkdir(dirname(dst), { recursive: true });
Expand All @@ -335,7 +338,7 @@ export function externals(opts: NodeExternalsOptions): Plugin {
isWindows ? "junction" : "dir"
)
.catch((err) => {
console.error("Cannot link", src, "to", dst, ":", err.message);
console.error("Cannot link", from, "to", to, err);
});
};

Expand Down Expand Up @@ -363,52 +366,68 @@ export function externals(opts: NodeExternalsOptions): Plugin {
return parentPkgs;
};

const writeTracedPackage = async (tracedPackage: TracedPackage) => {
// Analyze dependency tree
const multiVersionPkgs: Record<string, { [version: string]: string[] }> =
{};
const singleVersionPackages: string[] = [];
for (const tracedPackage of Object.values(tracedPackages)) {
const versions = Object.keys(tracedPackage.versions);
if (versions.length === 1) {
// Write the only version into node_modules/{name}
await writePackage(tracedPackage.name, versions[0]);
return;
singleVersionPackages.push(tracedPackage.name);
continue;
}
multiVersionPkgs[tracedPackage.name] = {};
for (const version of versions) {
const parentPkgs = findPackageParents(tracedPackage, version);
if (parentPkgs.length === 0) {
// No parent packages, assume as the hoisted version
await writePackage(tracedPackage.name, version);
continue;
}
// Write alternative version into node_modules/{name}@{version}
await writePackage(
tracedPackage.name,
version,
`.nitro/${tracedPackage.name}@${version}`
multiVersionPkgs[tracedPackage.name][version] = findPackageParents(
tracedPackage,
version
);
}
}

// Directly write single version packages
await Promise.all(
singleVersionPackages.map((pkgName) => {
const pkg = tracedPackages[pkgName];
const version = Object.keys(pkg.versions)[0];
return writePackage(pkgName, version);
})
);

// Write packages with multiple versions
for (const [pkgName, pkgVersions] of Object.entries(multiVersionPkgs)) {
const versionEntires = Object.entries(pkgVersions).sort(
([v1, p1], [v2, p2]) => {
// 1. Packege with no parent packages to be hoisted
if (p1.length === 0) {
return -1;
}
if (p2.length === 0) {
return 1;
}
// 2. Newest version to be hoisted
return compareVersions(v1, v2);
}
);
for (const [version, parentPkgs] of versionEntires) {
// Write each version into node_modules/.nitro/{name}@{version}
await writePackage(pkgName, version, `.nitro/${pkgName}@${version}`);
// Link one version to the top level (for indirect bundle deps)
await linkPackage(
`.nitro/${tracedPackage.name}@${version}`,
`${tracedPackage.name}`
);
// For each parent, link into node_modules/{parent}/node_modules/{name}
for (const parentPath of parentPkgs) {
await linkPackage(
`.nitro/${tracedPackage.name}@${version}`,
`.nitro/${parentPath}/node_modules/${tracedPackage.name}`
);
await linkPackage(
`.nitro/${tracedPackage.name}@${version}`,
`${parentPath.split("@")[0]}/node_modules/${tracedPackage.name}`
);
await linkPackage(`.nitro/${pkgName}@${version}`, `${pkgName}`);
// Link to parent packages
for (const parentPkg of parentPkgs) {
const parentPkgName = parentPkg.replace(/@[^@]+$/, "");
await (multiVersionPkgs[parentPkgName]
? linkPackage(
`.nitro/${pkgName}@${version}`,
`.nitro/${parentPkg}/node_modules/${pkgName}`
)
: linkPackage(
`.nitro/${pkgName}@${version}`,
`${parentPkgName}/node_modules/${pkgName}`
));
}
}
};
// Write traced packages
// await Promise.all(
// Object.values(tracedPackages).map(async (tracedPackage) => {
// await writeTracedPackage(tracedPackage);
// })
// );
for (const tracedPackage of Object.values(tracedPackages)) {
await writeTracedPackage(tracedPackage);
}

// Write an informative package.json
Expand All @@ -418,6 +437,7 @@ export function externals(opts: NodeExternalsOptions): Plugin {
Object.keys(pkg.versions).join(" | "),
])
);

await fsp.writeFile(
resolve(opts.outDir, "package.json"),
JSON.stringify(
Expand All @@ -436,15 +456,13 @@ export function externals(opts: NodeExternalsOptions): Plugin {
};
}

// function sortVersions(versions: string[]) {
// return versions.sort((v1 = "0.0.0", v2 = "0.0.0") => {
// try {
// return semver.lt(v1, v2, { loose: true }) ? 1 : -1;
// } catch {
// return v1.localeCompare(v2);
// }
// });
// }
function compareVersions(v1 = "0.0.0", v2 = "0.0.0") {
try {
return semver.lt(v1, v2, { loose: true }) ? 1 : -1;
} catch {
return v1.localeCompare(v2);
}
}

function parseNodeModulePath(path: string) {
if (!path) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/fixture/_/node_modules/nitro-dep-b/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/fixture/_/node_modules/nitro-lib/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 16 additions & 4 deletions test/fixture/routes/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,23 @@ import depLib from "nitro-lib";
// @ts-ignore
import subpathLib from "nitro-lib/subpath";

/*
Structure in fixture/_/node_modules:
| nitrodep-a (1.0.0)
| nitro-lib (1.0.0)
| nested-lib (1.0.0)
| nitrodep-b (2.0.1)
| nitro-lib (2.0.1)
| nested-lib (2.0.1)
| nitro-lib (2.0.0)
| nested-lib (2.0.0)
*/

export default defineEventHandler(() => {
return {
depA,
depB,
depLib,
subpathLib,
depA, // expected to all be 1.0.0
depB, // expected to all be 2.0.1
depLib, // expected to all be 2.0.0
subpathLib, // expected to 2.0.0
};
});

0 comments on commit 1da3ef2

Please sign in to comment.