Skip to content
Open
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
2 changes: 1 addition & 1 deletion scripts/build-backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const { abi: NODE_ABI } = readNodeJsMobileVersions();
// 2. Enumerate every concrete (name, version) pair of native modules
// in the dep tree. `npm ci` ran via the `prebackend:build` npm
// hook, so node_modules is current.
const nativePairs = collectNativePairs(BACKEND_SRC_DIR, NATIVE_MODULES);
const nativePairs = await collectNativePairs(BACKEND_SRC_DIR, NATIVE_MODULES);

// 3. Rollup bundles backend/index.js into both per-platform output
// dirs in one pass. The `OUTPUT_DIR_*` env vars point rollup at
Expand Down
86 changes: 27 additions & 59 deletions scripts/lib/native-modules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { $ } from "execa";

export type NativeModule = { name: string; usesNapi: boolean };

Expand Down Expand Up @@ -33,65 +34,32 @@ export type NativePair = {
usesNapi: boolean;
};

/**
* Walk `<backendDir>/node_modules` for every installed `(name,
* version)` of `name` (top-level + every nested copy). Multi-version
* dep trees ship one artifact per `(name, version)` pair, not one per
* name (which would silently shadow the lower version with the higher
* one).
*/
function findNativeModuleVersions(
export async function collectNativePairs(
backendDir: string,
name: string,
): { version: string }[] {
const topLevelNodeModules = join(backendDir, "node_modules");
const versions: { version: string }[] = [];
const seenDirs = new Set<string>();
const stack: string[] = [topLevelNodeModules];
while (stack.length > 0) {
const dir = stack.pop()!;
const candidate = join(dir, name, "package.json");
if (existsSync(candidate)) {
const packageDir = dirname(candidate);
if (!seenDirs.has(packageDir)) {
seenDirs.add(packageDir);
const { version } = JSON.parse(readFileSync(candidate, "utf-8"));
versions.push({ version });
}
}
const entries = (() => {
try {
return readdirSync(dir, { withFileTypes: true });
} catch {
return [];
}
})();
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const nested = join(dir, entry.name, "node_modules");
if (existsSync(nested)) stack.push(nested);
}
modules: readonly NativeModule[],
): Promise<NativePair[]> {
const pairs: NativePair[] = [];
const deps = new Set<string>();

const npmListResult = await $({
cwd: backendDir,
lines: true,
})`npm list --all --parseable --long --production`;

for (const line of npmListResult.stdout) {
const moduleInfo = line.split(":").at(-1);
if (!moduleInfo) continue;
deps.add(moduleInfo);
}
return versions;
}

/**
* Resolve every `(name, version)` pair that needs a prebuild +
* platform packaging. Multiple disk locations can share the same
* version (e.g. four nested `sodium-native@5.1.0` copies); the
* addon-loader rewrite loads each one by its versioned key, so build-
* side dedup avoids racing parallel fetches into the same temp dir.
*/
export function collectNativePairs(
backendDir: string,
modules: readonly NativeModule[],
): NativePair[] {
const seen = new Map<string, NativePair>();
for (const { name, usesNapi } of modules) {
for (const { version } of findNativeModuleVersions(backendDir, name)) {
const key = `${name}__${version}`;
if (!seen.has(key)) seen.set(key, { name, version, usesNapi });
}
for (const moduleInfo of deps.values()) {
const version = moduleInfo.split("@").at(-1);
if (!version) continue;
const name = moduleInfo.slice(0, -version.length - 1);
const nativeModule = modules.find((m) => m.name === name);
if (!nativeModule) continue;
const { usesNapi } = nativeModule;
pairs.push({ name, version, usesNapi });
}
return [...seen.values()];
return pairs;
}
Loading