isolate-package 1.33.0 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/isolate-bin.mjs +5 -6
- package/dist/isolate-bin.mjs.map +1 -1
- package/dist/{isolate-DyRD5Zd_.mjs → isolate-ts-Igq7C.mjs} +888 -271
- package/dist/isolate-ts-Igq7C.mjs.map +1 -0
- package/package.json +23 -19
- package/src/get-internal-package-names.test.ts +1 -1
- package/src/get-internal-package-names.ts +2 -2
- package/src/isolate-bin.ts +5 -5
- package/src/isolate.ts +22 -17
- package/src/lib/config.test.ts +1 -1
- package/src/lib/config.ts +3 -3
- package/src/lib/lockfile/helpers/bun-lockfile.ts +153 -0
- package/src/lib/lockfile/helpers/generate-bun-lockfile.test.ts +3 -3
- package/src/lib/lockfile/helpers/generate-bun-lockfile.ts +14 -146
- package/src/lib/lockfile/helpers/generate-npm-lockfile.integration.test.ts +1 -5
- package/src/lib/lockfile/helpers/generate-npm-lockfile.test.ts +311 -16
- package/src/lib/lockfile/helpers/generate-npm-lockfile.ts +193 -22
- package/src/lib/lockfile/helpers/generate-pnpm-lockfile.test.ts +83 -2
- package/src/lib/lockfile/helpers/generate-pnpm-lockfile.ts +33 -6
- package/src/lib/lockfile/helpers/generate-yarn-lockfile.ts +5 -5
- package/src/lib/lockfile/process-lockfile.test.ts +2 -2
- package/src/lib/manifest/adapt-target-package-manifest.ts +22 -13
- package/src/lib/manifest/helpers/adapt-internal-package-manifests.test.ts +72 -3
- package/src/lib/manifest/helpers/adapt-internal-package-manifests.ts +22 -12
- package/src/lib/manifest/helpers/adapt-manifest-internal-deps.ts +1 -1
- package/src/lib/manifest/helpers/adopt-pnpm-fields-from-root.test.ts +4 -4
- package/src/lib/manifest/helpers/adopt-pnpm-fields-from-root.ts +7 -7
- package/src/lib/manifest/helpers/resolve-catalog-dependencies.test.ts +410 -0
- package/src/lib/manifest/helpers/resolve-catalog-dependencies.ts +115 -27
- package/src/lib/manifest/io.ts +6 -2
- package/src/lib/manifest/validate-manifest.ts +2 -2
- package/src/lib/output/get-build-output-dir.ts +1 -1
- package/src/lib/output/pack-dependencies.ts +1 -1
- package/src/lib/output/process-build-output-files.ts +6 -17
- package/src/lib/package-manager/helpers/infer-from-files.ts +5 -5
- package/src/lib/package-manager/helpers/infer-from-manifest.ts +7 -8
- package/src/lib/package-manager/index.ts +1 -1
- package/src/lib/package-manager/names.ts +8 -10
- package/src/lib/patches/collect-installed-names-bun.test.ts +154 -0
- package/src/lib/patches/collect-installed-names-bun.ts +87 -0
- package/src/lib/patches/collect-installed-names-pnpm.test.ts +316 -0
- package/src/lib/patches/collect-installed-names-pnpm.ts +365 -0
- package/src/lib/patches/copy-patches.test.ts +130 -13
- package/src/lib/patches/copy-patches.ts +47 -10
- package/src/lib/patches/write-isolate-pnpm-workspace.test.ts +83 -3
- package/src/lib/patches/write-isolate-pnpm-workspace.ts +4 -4
- package/src/lib/registry/collect-reachable-package-names.test.ts +1 -1
- package/src/lib/registry/create-packages-registry.ts +34 -31
- package/src/lib/registry/helpers/find-packages-globs.ts +23 -19
- package/src/lib/registry/list-internal-packages.test.ts +2 -2
- package/src/lib/types.ts +2 -2
- package/src/lib/utils/filter-patched-dependencies.test.ts +1 -1
- package/src/lib/utils/filter-patched-dependencies.ts +2 -2
- package/src/lib/utils/get-dirname.ts +1 -1
- package/src/lib/utils/index.ts +1 -1
- package/src/lib/utils/json.ts +12 -14
- package/src/lib/utils/pack.ts +32 -22
- package/src/lib/utils/reset-isolate-dir.test.ts +165 -0
- package/src/lib/utils/reset-isolate-dir.ts +147 -0
- package/src/lib/utils/unpack.test.ts +76 -0
- package/src/lib/utils/unpack.ts +16 -10
- package/src/lib/utils/wait-for-complete-file.test.ts +105 -0
- package/src/lib/utils/wait-for-complete-file.ts +44 -0
- package/src/lib/utils/yaml.ts +8 -9
- package/src/testing/setup.ts +1 -1
- package/dist/isolate-DyRD5Zd_.mjs.map +0 -1
|
@@ -23,9 +23,9 @@ export async function getInternalPackageNames(
|
|
|
23
23
|
|
|
24
24
|
detectPackageManager(workspaceRootDir);
|
|
25
25
|
|
|
26
|
-
const targetPackageManifest = await readTypedJson
|
|
26
|
+
const targetPackageManifest = (await readTypedJson(
|
|
27
27
|
path.join(targetPackageDir, "package.json"),
|
|
28
|
-
);
|
|
28
|
+
)) as PackageManifest;
|
|
29
29
|
|
|
30
30
|
const packagesRegistry = await createPackagesRegistry(
|
|
31
31
|
workspaceRootDir,
|
package/src/isolate-bin.ts
CHANGED
|
@@ -97,11 +97,11 @@ async function run() {
|
|
|
97
97
|
await isolate(mergedConfig);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
run().catch((
|
|
101
|
-
if (
|
|
102
|
-
console.error(
|
|
103
|
-
process.exit(1);
|
|
100
|
+
run().catch((error) => {
|
|
101
|
+
if (error instanceof Error) {
|
|
102
|
+
console.error(error.stack);
|
|
104
103
|
} else {
|
|
105
|
-
console.error(
|
|
104
|
+
console.error(error);
|
|
106
105
|
}
|
|
106
|
+
process.exit(1);
|
|
107
107
|
});
|
package/src/isolate.ts
CHANGED
|
@@ -31,29 +31,30 @@ import {
|
|
|
31
31
|
getRootRelativeLogPath,
|
|
32
32
|
isRushWorkspace,
|
|
33
33
|
readTypedJson,
|
|
34
|
+
resetIsolateDir,
|
|
34
35
|
writeTypedYamlSync,
|
|
35
36
|
} from "./lib/utils";
|
|
36
37
|
|
|
37
38
|
const __dirname = getDirname(import.meta.url);
|
|
38
39
|
|
|
39
|
-
export function createIsolator(
|
|
40
|
-
const resolvedConfig = resolveConfig(
|
|
40
|
+
export function createIsolator(initialConfig?: IsolateConfig) {
|
|
41
|
+
const resolvedConfig = resolveConfig(initialConfig);
|
|
41
42
|
|
|
42
|
-
return async function
|
|
43
|
+
return async function runIsolate(): Promise<string> {
|
|
43
44
|
const config = resolvedConfig;
|
|
44
45
|
setLogLevel(config.logLevel);
|
|
45
46
|
const log = useLogger();
|
|
46
47
|
|
|
47
|
-
const { version: libraryVersion } = await readTypedJson
|
|
48
|
+
const { version: libraryVersion } = (await readTypedJson(
|
|
48
49
|
path.join(path.join(__dirname, "..", "package.json")),
|
|
49
|
-
);
|
|
50
|
+
)) as PackageManifest;
|
|
50
51
|
|
|
51
52
|
log.debug("Using isolate-package version", libraryVersion);
|
|
52
53
|
|
|
53
54
|
const { targetPackageDir, workspaceRootDir } =
|
|
54
55
|
resolveWorkspacePaths(config);
|
|
55
56
|
|
|
56
|
-
const buildOutputDir =
|
|
57
|
+
const buildOutputDir = getBuildOutputDir({
|
|
57
58
|
targetPackageDir,
|
|
58
59
|
buildDirName: config.buildDirName,
|
|
59
60
|
tsconfigPath: config.tsconfigPath,
|
|
@@ -77,19 +78,23 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
77
78
|
getRootRelativeLogPath(isolateDir, workspaceRootDir),
|
|
78
79
|
);
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Place the trash sibling outside `targetPackageDir`, since that directory
|
|
83
|
+
* is later packed by `processBuildOutputFiles`. Keeping the trash next to
|
|
84
|
+
* the target package (still on the same filesystem) preserves the atomic
|
|
85
|
+
* rename without risking that the trash gets picked up by `npm pack` or
|
|
86
|
+
* races with that step.
|
|
87
|
+
*/
|
|
88
|
+
await resetIsolateDir(isolateDir, {
|
|
89
|
+
trashParentDir: path.dirname(targetPackageDir),
|
|
90
|
+
});
|
|
86
91
|
|
|
87
92
|
const tmpDir = path.join(isolateDir, "__tmp");
|
|
88
93
|
await fs.ensureDir(tmpDir);
|
|
89
94
|
|
|
90
|
-
const targetPackageManifest = await readTypedJson
|
|
95
|
+
const targetPackageManifest = (await readTypedJson(
|
|
91
96
|
path.join(targetPackageDir, "package.json"),
|
|
92
|
-
);
|
|
97
|
+
)) as PackageManifest;
|
|
93
98
|
|
|
94
99
|
/** Validate mandatory fields for the target package */
|
|
95
100
|
validateManifestMandatoryFields(
|
|
@@ -233,8 +238,10 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
233
238
|
const copiedPatches = shouldCopyPatches
|
|
234
239
|
? await copyPatches({
|
|
235
240
|
workspaceRootDir,
|
|
241
|
+
targetPackageDir,
|
|
236
242
|
targetPackageManifest: outputManifest,
|
|
237
243
|
packagesRegistry,
|
|
244
|
+
internalDepPackageNames: internalPackageNames,
|
|
238
245
|
isolateDir,
|
|
239
246
|
includeDevDependencies: config.includeDevDependencies,
|
|
240
247
|
})
|
|
@@ -276,9 +283,7 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
276
283
|
if (packageManager.name === "bun") {
|
|
277
284
|
manifest.patchedDependencies = patchEntries;
|
|
278
285
|
} else {
|
|
279
|
-
|
|
280
|
-
manifest.pnpm = {};
|
|
281
|
-
}
|
|
286
|
+
manifest.pnpm ??= {};
|
|
282
287
|
manifest.pnpm.patchedDependencies = patchEntries;
|
|
283
288
|
}
|
|
284
289
|
|
package/src/lib/config.test.ts
CHANGED
package/src/lib/config.ts
CHANGED
|
@@ -93,7 +93,7 @@ function loadModuleConfig(filePath: string): IsolateConfig {
|
|
|
93
93
|
throw new Error("Failed to extract config JSON from subprocess output");
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
const parsed = JSON.parse(jsonMatch);
|
|
96
|
+
const parsed = JSON.parse(jsonMatch) as unknown;
|
|
97
97
|
|
|
98
98
|
if (
|
|
99
99
|
typeof parsed !== "object" ||
|
|
@@ -105,7 +105,7 @@ function loadModuleConfig(filePath: string): IsolateConfig {
|
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
return parsed;
|
|
108
|
+
return parsed as IsolateConfig;
|
|
109
109
|
} catch (error) {
|
|
110
110
|
const stderr =
|
|
111
111
|
error instanceof Error && "stderr" in error
|
|
@@ -151,7 +151,7 @@ export function loadConfigFromFile(): IsolateConfig {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
if (jsonExists) {
|
|
154
|
-
return readTypedJsonSync
|
|
154
|
+
return readTypedJsonSync(jsonConfigPath) as IsolateConfig;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
return {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and walker logic for the Bun workspace lockfile (`bun.lock`).
|
|
3
|
+
*
|
|
4
|
+
* Used both when generating the isolated lockfile and when computing the set
|
|
5
|
+
* of package names that will end up installed in the isolate (so that patches
|
|
6
|
+
* for deep transitive deps are preserved).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type BunWorkspaceEntry = {
|
|
10
|
+
name?: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
dependencies?: Record<string, string>;
|
|
13
|
+
devDependencies?: Record<string, string>;
|
|
14
|
+
optionalDependencies?: Record<string, string>;
|
|
15
|
+
peerDependencies?: Record<string, string>;
|
|
16
|
+
optionalPeers?: string[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type BunLockfile = {
|
|
20
|
+
lockfileVersion: number;
|
|
21
|
+
workspaces: Record<string, BunWorkspaceEntry>;
|
|
22
|
+
packages: Record<string, unknown[]>;
|
|
23
|
+
trustedDependencies?: string[];
|
|
24
|
+
patchedDependencies?: Record<string, string>;
|
|
25
|
+
overrides?: Record<string, string>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Extract dependency names from a workspace entry. */
|
|
29
|
+
export function collectDependencyNames(
|
|
30
|
+
entry: BunWorkspaceEntry,
|
|
31
|
+
includeDevDependencies: boolean,
|
|
32
|
+
): string[] {
|
|
33
|
+
const names = new Set<string>();
|
|
34
|
+
|
|
35
|
+
for (const name of Object.keys(entry.dependencies ?? {})) {
|
|
36
|
+
names.add(name);
|
|
37
|
+
}
|
|
38
|
+
for (const name of Object.keys(entry.optionalDependencies ?? {})) {
|
|
39
|
+
names.add(name);
|
|
40
|
+
}
|
|
41
|
+
for (const name of Object.keys(entry.peerDependencies ?? {})) {
|
|
42
|
+
names.add(name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (includeDevDependencies) {
|
|
46
|
+
for (const name of Object.keys(entry.devDependencies ?? {})) {
|
|
47
|
+
names.add(name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return [...names];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check whether a package entry represents a workspace package by examining
|
|
56
|
+
* its identifier string (first element in the entry array).
|
|
57
|
+
*/
|
|
58
|
+
export function isWorkspacePackageEntry(entry: unknown[]): boolean {
|
|
59
|
+
const ident = entry[0];
|
|
60
|
+
return typeof ident === "string" && ident.includes("@workspace:");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extract the info object from a packages entry. The position varies by type:
|
|
65
|
+
* - npm packages: [ident, registry, info, checksum] -> index 2
|
|
66
|
+
* - workspace packages: [ident, info] -> index 1
|
|
67
|
+
* - git/github packages: [ident, info, checksum] -> index 1
|
|
68
|
+
*
|
|
69
|
+
* Detection: if the second element is a string (registry URL or checksum),
|
|
70
|
+
* the info object is deeper. Workspace entries have only 2 elements.
|
|
71
|
+
*/
|
|
72
|
+
export function getPackageInfoObject(
|
|
73
|
+
entry: unknown[],
|
|
74
|
+
): Record<string, unknown> | undefined {
|
|
75
|
+
if (entry.length <= 1) return undefined;
|
|
76
|
+
|
|
77
|
+
/** Workspace entries: [ident, info] */
|
|
78
|
+
if (isWorkspacePackageEntry(entry)) {
|
|
79
|
+
return typeof entry[1] === "object"
|
|
80
|
+
? (entry[1] as Record<string, unknown>)
|
|
81
|
+
: undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* npm entries with registry URL: [ident, registryUrl, info, checksum]. The
|
|
86
|
+
* second element is a string (the registry URL).
|
|
87
|
+
*/
|
|
88
|
+
if (typeof entry[1] === "string") {
|
|
89
|
+
return typeof entry[2] === "object"
|
|
90
|
+
? (entry[2] as Record<string, unknown>)
|
|
91
|
+
: undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** git/tarball entries: [ident, info, checksum] */
|
|
95
|
+
return typeof entry[1] === "object"
|
|
96
|
+
? (entry[1] as Record<string, unknown>)
|
|
97
|
+
: undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Recursively collect all package keys that are required, starting from a set
|
|
102
|
+
* of direct dependency names and walking through their transitive
|
|
103
|
+
* dependencies in the packages section.
|
|
104
|
+
*/
|
|
105
|
+
export function collectRequiredPackages(
|
|
106
|
+
directDependencyNames: Set<string>,
|
|
107
|
+
packages: Record<string, unknown[]>,
|
|
108
|
+
): Set<string> {
|
|
109
|
+
const required = new Set<string>();
|
|
110
|
+
const queue = [...directDependencyNames];
|
|
111
|
+
|
|
112
|
+
let name: string | undefined;
|
|
113
|
+
while ((name = queue.pop()) !== undefined) {
|
|
114
|
+
if (required.has(name)) continue;
|
|
115
|
+
|
|
116
|
+
const entry = packages[name];
|
|
117
|
+
if (!entry) continue;
|
|
118
|
+
|
|
119
|
+
required.add(name);
|
|
120
|
+
|
|
121
|
+
const info = getPackageInfoObject(entry);
|
|
122
|
+
if (!info) continue;
|
|
123
|
+
|
|
124
|
+
/** Walk transitive dependencies from the info object */
|
|
125
|
+
for (const depField of [
|
|
126
|
+
"dependencies",
|
|
127
|
+
"optionalDependencies",
|
|
128
|
+
"peerDependencies",
|
|
129
|
+
]) {
|
|
130
|
+
enqueueDeps(info[depField], required, queue);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return required;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Push any names from a dependency map onto the BFS queue, skipping anything
|
|
139
|
+
* already marked required so we don't revisit it. `deps` is typed as `unknown`
|
|
140
|
+
* because it comes from a freshly-parsed lockfile entry with no schema.
|
|
141
|
+
*/
|
|
142
|
+
function enqueueDeps(
|
|
143
|
+
deps: unknown,
|
|
144
|
+
required: Set<string>,
|
|
145
|
+
queue: string[],
|
|
146
|
+
): void {
|
|
147
|
+
if (!deps || typeof deps !== "object") return;
|
|
148
|
+
for (const depName of Object.keys(deps as Record<string, unknown>)) {
|
|
149
|
+
if (!required.has(depName)) {
|
|
150
|
+
queue.push(depName);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -13,8 +13,8 @@ vi.mock("fs-extra", () => ({
|
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
15
|
/** Mock the utils */
|
|
16
|
-
vi.mock("
|
|
17
|
-
const actual = await importOriginal<typeof import("
|
|
16
|
+
vi.mock("#/lib/utils", async (importOriginal) => {
|
|
17
|
+
const actual = await importOriginal<typeof import("#/lib/utils")>();
|
|
18
18
|
return {
|
|
19
19
|
getErrorMessage: vi.fn((err: Error) => err.message),
|
|
20
20
|
getPackageName: actual.getPackageName,
|
|
@@ -23,7 +23,7 @@ vi.mock("~/lib/utils", async (importOriginal) => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
const fs = vi.mocked((await import("fs-extra")).default);
|
|
26
|
-
const { readTypedJsonSync } = vi.mocked(await import("
|
|
26
|
+
const { readTypedJsonSync } = vi.mocked(await import("#/lib/utils"));
|
|
27
27
|
|
|
28
28
|
/** Reusable packages registry fixture */
|
|
29
29
|
function createPackagesRegistry() {
|
|
@@ -1,32 +1,20 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import { got } from "get-or-throw";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { useLogger } from "
|
|
5
|
-
import type { PackagesRegistry } from "
|
|
4
|
+
import { useLogger } from "#/lib/logger";
|
|
5
|
+
import type { PackagesRegistry } from "#/lib/types";
|
|
6
6
|
import {
|
|
7
7
|
getErrorMessage,
|
|
8
8
|
getPackageName,
|
|
9
9
|
readTypedJsonSync,
|
|
10
|
-
} from "
|
|
11
|
-
|
|
12
|
-
type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
peerDependencies?: Record<string, string>;
|
|
19
|
-
optionalPeers?: string[];
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type BunLockfile = {
|
|
23
|
-
lockfileVersion: number;
|
|
24
|
-
workspaces: Record<string, BunWorkspaceEntry>;
|
|
25
|
-
packages: Record<string, unknown[]>;
|
|
26
|
-
trustedDependencies?: string[];
|
|
27
|
-
patchedDependencies?: Record<string, string>;
|
|
28
|
-
overrides?: Record<string, string>;
|
|
29
|
-
};
|
|
10
|
+
} from "#/lib/utils";
|
|
11
|
+
import {
|
|
12
|
+
type BunLockfile,
|
|
13
|
+
type BunWorkspaceEntry,
|
|
14
|
+
collectDependencyNames,
|
|
15
|
+
collectRequiredPackages,
|
|
16
|
+
isWorkspacePackageEntry,
|
|
17
|
+
} from "./bun-lockfile";
|
|
30
18
|
|
|
31
19
|
/**
|
|
32
20
|
* Serialize a value to JSON with trailing commas after every array element and
|
|
@@ -54,126 +42,6 @@ export function serializeWithTrailingCommas(
|
|
|
54
42
|
return result;
|
|
55
43
|
}
|
|
56
44
|
|
|
57
|
-
/**
|
|
58
|
-
* Extract dependency names from a workspace entry, optionally including
|
|
59
|
-
* devDependencies.
|
|
60
|
-
*/
|
|
61
|
-
function collectDependencyNames(
|
|
62
|
-
entry: BunWorkspaceEntry,
|
|
63
|
-
includeDevDependencies: boolean,
|
|
64
|
-
): string[] {
|
|
65
|
-
const names = new Set<string>();
|
|
66
|
-
|
|
67
|
-
for (const name of Object.keys(entry.dependencies ?? {})) {
|
|
68
|
-
names.add(name);
|
|
69
|
-
}
|
|
70
|
-
for (const name of Object.keys(entry.optionalDependencies ?? {})) {
|
|
71
|
-
names.add(name);
|
|
72
|
-
}
|
|
73
|
-
for (const name of Object.keys(entry.peerDependencies ?? {})) {
|
|
74
|
-
names.add(name);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (includeDevDependencies) {
|
|
78
|
-
for (const name of Object.keys(entry.devDependencies ?? {})) {
|
|
79
|
-
names.add(name);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return [...names];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Check whether a package entry represents a workspace package by examining its
|
|
88
|
-
* identifier string (first element in the entry array).
|
|
89
|
-
*/
|
|
90
|
-
function isWorkspacePackageEntry(entry: unknown[]): boolean {
|
|
91
|
-
const ident = entry[0];
|
|
92
|
-
return typeof ident === "string" && ident.includes("@workspace:");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Extract the info object from a packages entry. The position varies by type:
|
|
97
|
-
* - npm packages: [ident, registry, info, checksum] -> index 2
|
|
98
|
-
* - workspace packages: [ident, info] -> index 1
|
|
99
|
-
* - git/github packages: [ident, info, checksum] -> index 1
|
|
100
|
-
*
|
|
101
|
-
* Detection: if the second element is a string (registry URL or checksum), the
|
|
102
|
-
* info object is deeper. Workspace entries have only 2 elements.
|
|
103
|
-
*/
|
|
104
|
-
function getPackageInfoObject(
|
|
105
|
-
entry: unknown[],
|
|
106
|
-
): Record<string, unknown> | undefined {
|
|
107
|
-
if (entry.length <= 1) return undefined;
|
|
108
|
-
|
|
109
|
-
/** Workspace entries: [ident, info] */
|
|
110
|
-
if (isWorkspacePackageEntry(entry)) {
|
|
111
|
-
return typeof entry[1] === "object"
|
|
112
|
-
? (entry[1] as Record<string, unknown>)
|
|
113
|
-
: undefined;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* npm entries with registry URL: [ident, registryUrl, info, checksum].
|
|
118
|
-
* The second element is a string (the registry URL).
|
|
119
|
-
*/
|
|
120
|
-
if (typeof entry[1] === "string") {
|
|
121
|
-
return typeof entry[2] === "object"
|
|
122
|
-
? (entry[2] as Record<string, unknown>)
|
|
123
|
-
: undefined;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** git/tarball entries: [ident, info, checksum] */
|
|
127
|
-
return typeof entry[1] === "object"
|
|
128
|
-
? (entry[1] as Record<string, unknown>)
|
|
129
|
-
: undefined;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Recursively collect all package keys that are required, starting from a set
|
|
134
|
-
* of direct dependency names and walking through their transitive dependencies
|
|
135
|
-
* in the packages section.
|
|
136
|
-
*/
|
|
137
|
-
function collectRequiredPackages(
|
|
138
|
-
directDependencyNames: Set<string>,
|
|
139
|
-
packages: Record<string, unknown[]>,
|
|
140
|
-
): Set<string> {
|
|
141
|
-
const required = new Set<string>();
|
|
142
|
-
const queue = [...directDependencyNames];
|
|
143
|
-
|
|
144
|
-
while (queue.length > 0) {
|
|
145
|
-
const name = queue.pop()!;
|
|
146
|
-
|
|
147
|
-
if (required.has(name)) continue;
|
|
148
|
-
|
|
149
|
-
const entry = packages[name];
|
|
150
|
-
if (!entry) continue;
|
|
151
|
-
|
|
152
|
-
required.add(name);
|
|
153
|
-
|
|
154
|
-
const info = getPackageInfoObject(entry);
|
|
155
|
-
if (!info) continue;
|
|
156
|
-
|
|
157
|
-
/** Walk transitive dependencies from the info object */
|
|
158
|
-
for (const depField of [
|
|
159
|
-
"dependencies",
|
|
160
|
-
"optionalDependencies",
|
|
161
|
-
"peerDependencies",
|
|
162
|
-
]) {
|
|
163
|
-
const deps = info[depField];
|
|
164
|
-
if (deps && typeof deps === "object") {
|
|
165
|
-
for (const depName of Object.keys(deps as Record<string, unknown>)) {
|
|
166
|
-
if (!required.has(depName)) {
|
|
167
|
-
queue.push(depName);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return required;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
45
|
export async function generateBunLockfile({
|
|
178
46
|
workspaceRootDir,
|
|
179
47
|
targetPackageDir,
|
|
@@ -200,7 +68,7 @@ export async function generateBunLockfile({
|
|
|
200
68
|
throw new Error(`Failed to find bun.lock at ${lockfilePath}`);
|
|
201
69
|
}
|
|
202
70
|
|
|
203
|
-
const lockfile = readTypedJsonSync
|
|
71
|
+
const lockfile = readTypedJsonSync(lockfilePath) as BunLockfile;
|
|
204
72
|
|
|
205
73
|
/** Compute workspace keys for the target and internal deps */
|
|
206
74
|
const targetWorkspaceKey = path
|
|
@@ -347,8 +215,8 @@ export async function generateBunLockfile({
|
|
|
347
215
|
);
|
|
348
216
|
|
|
349
217
|
log.debug("Created lockfile at", outputPath);
|
|
350
|
-
} catch (
|
|
351
|
-
log.error(`Failed to generate lockfile: ${getErrorMessage(
|
|
352
|
-
throw
|
|
218
|
+
} catch (error) {
|
|
219
|
+
log.error(`Failed to generate lockfile: ${getErrorMessage(error)}`);
|
|
220
|
+
throw error;
|
|
353
221
|
}
|
|
354
222
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
5
|
import { generateNpmLockfile } from "./generate-npm-lockfile";
|
|
7
6
|
|
|
8
|
-
const
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
const FIXTURES_DIR = path.join(__dirname, "__fixtures__");
|
|
7
|
+
const FIXTURES_DIR = path.join(import.meta.dirname, "__fixtures__");
|
|
12
8
|
|
|
13
9
|
/**
|
|
14
10
|
* Copy a fixture's `workspace/` tree into a fresh tmp directory so that the
|