isolate-package 1.28.2 → 1.29.0-1
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/{isolate-B0zQOSqE.mjs → isolate-CqPWp-1k.mjs} +211 -19
- package/dist/isolate-CqPWp-1k.mjs.map +1 -0
- package/dist/isolate-bin.mjs +1 -1
- package/package.json +1 -1
- package/src/get-internal-package-names.test.ts +0 -10
- package/src/isolate.ts +25 -3
- package/src/lib/lockfile/helpers/generate-bun-lockfile.test.ts +619 -0
- package/src/lib/lockfile/helpers/generate-bun-lockfile.ts +340 -0
- package/src/lib/lockfile/helpers/generate-pnpm-lockfile.test.ts +387 -0
- package/src/lib/lockfile/helpers/index.ts +1 -0
- package/src/lib/lockfile/helpers/pnpm-map-importer.test.ts +183 -0
- package/src/lib/lockfile/process-lockfile.test.ts +238 -0
- package/src/lib/lockfile/process-lockfile.ts +6 -6
- package/src/lib/manifest/adapt-target-package-manifest.ts +5 -4
- package/src/lib/manifest/helpers/adapt-internal-package-manifests.ts +4 -3
- package/src/lib/manifest/validate-manifest.test.ts +10 -19
- package/src/lib/manifest/validate-manifest.ts +1 -13
- package/src/lib/patches/copy-patches.test.ts +46 -11
- package/src/lib/patches/copy-patches.ts +13 -4
- package/src/lib/types.ts +3 -0
- package/src/lib/utils/filter-patched-dependencies.test.ts +1 -11
- package/src/testing/setup.ts +13 -0
- package/dist/isolate-B0zQOSqE.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -4,16 +4,6 @@ import path from "node:path";
|
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
5
|
import { getInternalPackageNames } from "./get-internal-package-names";
|
|
6
6
|
|
|
7
|
-
vi.mock("~/lib/logger", () => ({
|
|
8
|
-
useLogger: () => ({
|
|
9
|
-
debug: vi.fn(),
|
|
10
|
-
info: vi.fn(),
|
|
11
|
-
warn: vi.fn(),
|
|
12
|
-
error: vi.fn(),
|
|
13
|
-
}),
|
|
14
|
-
setLogLevel: vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
7
|
const packageManagerResult = {
|
|
18
8
|
name: "pnpm",
|
|
19
9
|
version: "9.0.0",
|
package/src/isolate.ts
CHANGED
|
@@ -193,11 +193,12 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
195
|
* Copy patch files before generating lockfile so the lockfile contains the
|
|
196
|
-
* correct paths. Only copy patches when output uses pnpm, since
|
|
197
|
-
* dependencies are
|
|
196
|
+
* correct paths. Only copy patches when output uses pnpm or bun, since
|
|
197
|
+
* patched dependencies are stored in their lockfiles.
|
|
198
198
|
*/
|
|
199
199
|
const shouldCopyPatches =
|
|
200
|
-
packageManager.name === "pnpm"
|
|
200
|
+
(packageManager.name === "pnpm" || packageManager.name === "bun") &&
|
|
201
|
+
!config.forceNpm;
|
|
201
202
|
|
|
202
203
|
const copiedPatches = shouldCopyPatches
|
|
203
204
|
? await copyPatches({
|
|
@@ -291,6 +292,18 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
291
292
|
}
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
if (packageManager.name === "bun" && !config.forceNpm) {
|
|
296
|
+
/** Add workspaces field to the manifest so Bun treats the isolate as a workspace */
|
|
297
|
+
const manifest = await readManifest(isolateDir);
|
|
298
|
+
const workspaceGlobs = unique(
|
|
299
|
+
internalPackageNames.map(
|
|
300
|
+
(name) => path.parse(got(packagesRegistry, name).rootRelativeDir).dir,
|
|
301
|
+
),
|
|
302
|
+
).map((x) => path.join(x, "/*"));
|
|
303
|
+
manifest.workspaces = workspaceGlobs;
|
|
304
|
+
await writeManifest(isolateDir, manifest);
|
|
305
|
+
}
|
|
306
|
+
|
|
294
307
|
/**
|
|
295
308
|
* If there is an .npmrc file in the workspace root, copy it to the isolate
|
|
296
309
|
* because the settings there could affect how the lockfile is resolved.
|
|
@@ -305,6 +318,15 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
305
318
|
log.debug("Copied .npmrc file to the isolate output");
|
|
306
319
|
}
|
|
307
320
|
|
|
321
|
+
if (packageManager.name === "bun" && !config.forceNpm) {
|
|
322
|
+
const bunfigPath = path.join(workspaceRootDir, "bunfig.toml");
|
|
323
|
+
|
|
324
|
+
if (fs.existsSync(bunfigPath)) {
|
|
325
|
+
fs.copyFileSync(bunfigPath, path.join(isolateDir, "bunfig.toml"));
|
|
326
|
+
log.debug("Copied bunfig.toml file to the isolate output");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
308
330
|
/**
|
|
309
331
|
* Clean up. Only do this when things succeed, so we can look at the temp
|
|
310
332
|
* folder in case something goes wrong.
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
generateBunLockfile,
|
|
4
|
+
serializeWithTrailingCommas,
|
|
5
|
+
} from "./generate-bun-lockfile";
|
|
6
|
+
|
|
7
|
+
/** Mock fs-extra */
|
|
8
|
+
vi.mock("fs-extra", () => ({
|
|
9
|
+
default: {
|
|
10
|
+
existsSync: vi.fn(),
|
|
11
|
+
writeFile: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
/** Mock the utils */
|
|
16
|
+
vi.mock("~/lib/utils", async (importOriginal) => {
|
|
17
|
+
const actual = await importOriginal<typeof import("~/lib/utils")>();
|
|
18
|
+
return {
|
|
19
|
+
getErrorMessage: vi.fn((err: Error) => err.message),
|
|
20
|
+
getPackageName: actual.getPackageName,
|
|
21
|
+
readTypedJsonSync: vi.fn(),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const fs = vi.mocked((await import("fs-extra")).default);
|
|
26
|
+
const { readTypedJsonSync } = vi.mocked(await import("~/lib/utils"));
|
|
27
|
+
|
|
28
|
+
/** Reusable packages registry fixture */
|
|
29
|
+
function createPackagesRegistry() {
|
|
30
|
+
return {
|
|
31
|
+
shared: {
|
|
32
|
+
absoluteDir: "/workspace/packages/shared",
|
|
33
|
+
rootRelativeDir: "packages/shared",
|
|
34
|
+
manifest: {
|
|
35
|
+
name: "shared",
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
utils: {
|
|
40
|
+
absoluteDir: "/workspace/packages/utils",
|
|
41
|
+
rootRelativeDir: "packages/utils",
|
|
42
|
+
manifest: {
|
|
43
|
+
name: "utils",
|
|
44
|
+
version: "1.0.0",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type BunLockfileFixture = {
|
|
51
|
+
lockfileVersion: number;
|
|
52
|
+
workspaces: Record<string, Record<string, unknown>>;
|
|
53
|
+
packages: Record<string, unknown[]>;
|
|
54
|
+
overrides?: Record<string, string>;
|
|
55
|
+
trustedDependencies?: string[];
|
|
56
|
+
patchedDependencies?: Record<string, string>;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Reusable bun lockfile fixture */
|
|
60
|
+
function createBunLockfile(): BunLockfileFixture {
|
|
61
|
+
return {
|
|
62
|
+
lockfileVersion: 0,
|
|
63
|
+
workspaces: {
|
|
64
|
+
"": {
|
|
65
|
+
name: "root",
|
|
66
|
+
dependencies: { lodash: "^4.17.21" },
|
|
67
|
+
},
|
|
68
|
+
"apps/my-app": {
|
|
69
|
+
name: "my-app",
|
|
70
|
+
dependencies: {
|
|
71
|
+
shared: "workspace:*",
|
|
72
|
+
express: "^4.18.0",
|
|
73
|
+
},
|
|
74
|
+
devDependencies: {
|
|
75
|
+
vitest: "^1.0.0",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
"packages/shared": {
|
|
79
|
+
name: "shared",
|
|
80
|
+
version: "1.0.0",
|
|
81
|
+
dependencies: {
|
|
82
|
+
utils: "workspace:*",
|
|
83
|
+
lodash: "^4.17.21",
|
|
84
|
+
},
|
|
85
|
+
devDependencies: {
|
|
86
|
+
typescript: "^5.0.0",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
"packages/utils": {
|
|
90
|
+
name: "utils",
|
|
91
|
+
version: "1.0.0",
|
|
92
|
+
dependencies: {},
|
|
93
|
+
},
|
|
94
|
+
"packages/other": {
|
|
95
|
+
name: "other",
|
|
96
|
+
version: "1.0.0",
|
|
97
|
+
dependencies: {
|
|
98
|
+
axios: "^1.0.0",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
packages: {
|
|
103
|
+
express: [
|
|
104
|
+
"express@4.18.2",
|
|
105
|
+
"https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
|
106
|
+
{ dependencies: { "body-parser": "1.20.1" } },
|
|
107
|
+
"sha512-abc",
|
|
108
|
+
],
|
|
109
|
+
"body-parser": [
|
|
110
|
+
"body-parser@1.20.1",
|
|
111
|
+
"https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
|
112
|
+
{},
|
|
113
|
+
"sha512-def",
|
|
114
|
+
],
|
|
115
|
+
lodash: [
|
|
116
|
+
"lodash@4.17.21",
|
|
117
|
+
"https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
|
118
|
+
{},
|
|
119
|
+
"sha512-ghi",
|
|
120
|
+
],
|
|
121
|
+
vitest: [
|
|
122
|
+
"vitest@1.0.0",
|
|
123
|
+
"https://registry.npmjs.org/vitest/-/vitest-1.0.0.tgz",
|
|
124
|
+
{},
|
|
125
|
+
"sha512-jkl",
|
|
126
|
+
],
|
|
127
|
+
typescript: [
|
|
128
|
+
"typescript@5.3.0",
|
|
129
|
+
"https://registry.npmjs.org/typescript/-/typescript-5.3.0.tgz",
|
|
130
|
+
{},
|
|
131
|
+
"sha512-mno",
|
|
132
|
+
],
|
|
133
|
+
axios: [
|
|
134
|
+
"axios@1.6.0",
|
|
135
|
+
"https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
|
136
|
+
{},
|
|
137
|
+
"sha512-pqr",
|
|
138
|
+
],
|
|
139
|
+
shared: ["shared@workspace:packages/shared", {}],
|
|
140
|
+
utils: ["utils@workspace:packages/utils", {}],
|
|
141
|
+
other: ["other@workspace:packages/other", {}],
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
describe("serializeWithTrailingCommas", () => {
|
|
147
|
+
it("should add trailing commas to JSON output", () => {
|
|
148
|
+
const input = { a: 1, b: [2, 3] };
|
|
149
|
+
const result = serializeWithTrailingCommas(input);
|
|
150
|
+
|
|
151
|
+
expect(result).toContain('"a": 1,');
|
|
152
|
+
expect(result).toContain("3,\n");
|
|
153
|
+
expect(result).toMatch(/\}$/);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle nested objects", () => {
|
|
157
|
+
const input = { a: { b: "c" } };
|
|
158
|
+
const result = serializeWithTrailingCommas(input);
|
|
159
|
+
const parsed = JSON.parse(result.replace(/,(\s*[}\]])/g, "$1"));
|
|
160
|
+
expect(parsed).toEqual(input);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("should handle boolean and null values", () => {
|
|
164
|
+
const input = {
|
|
165
|
+
enabled: true,
|
|
166
|
+
disabled: false,
|
|
167
|
+
empty: null,
|
|
168
|
+
list: [true, null, 42],
|
|
169
|
+
};
|
|
170
|
+
const result = serializeWithTrailingCommas(input);
|
|
171
|
+
/** Trailing commas after each value type */
|
|
172
|
+
expect(result).toContain("true,");
|
|
173
|
+
expect(result).toContain("false,");
|
|
174
|
+
expect(result).toContain("null,");
|
|
175
|
+
expect(result).toContain("42,");
|
|
176
|
+
/** Round-trips correctly */
|
|
177
|
+
const parsed = JSON.parse(result.replace(/,(\s*[}\]])/g, "$1"));
|
|
178
|
+
expect(parsed).toEqual(input);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should produce valid content that round-trips through strip-json-comments", () => {
|
|
182
|
+
const input = { key: "value", arr: [1, 2] };
|
|
183
|
+
const result = serializeWithTrailingCommas(input);
|
|
184
|
+
/** The output has trailing commas, which strip-json-comments can handle */
|
|
185
|
+
expect(result).toMatch(/,\n\s*\]/);
|
|
186
|
+
expect(result).toMatch(/,\n\s*\}/);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("generateBunLockfile", () => {
|
|
191
|
+
beforeEach(() => {
|
|
192
|
+
vi.clearAllMocks();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
afterEach(() => {
|
|
196
|
+
vi.restoreAllMocks();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("should throw when bun.lock is missing", async () => {
|
|
200
|
+
fs.existsSync.mockReturnValue(false);
|
|
201
|
+
|
|
202
|
+
await expect(
|
|
203
|
+
generateBunLockfile({
|
|
204
|
+
workspaceRootDir: "/workspace",
|
|
205
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
206
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
207
|
+
internalDepPackageNames: ["shared"],
|
|
208
|
+
packagesRegistry: createPackagesRegistry(),
|
|
209
|
+
includeDevDependencies: false,
|
|
210
|
+
}),
|
|
211
|
+
).rejects.toThrow("Failed to find bun.lock");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("should filter workspaces to only target and internal deps", async () => {
|
|
215
|
+
fs.existsSync.mockReturnValue(true);
|
|
216
|
+
fs.writeFile.mockResolvedValue();
|
|
217
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
218
|
+
|
|
219
|
+
await generateBunLockfile({
|
|
220
|
+
workspaceRootDir: "/workspace",
|
|
221
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
222
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
223
|
+
internalDepPackageNames: ["shared", "utils"],
|
|
224
|
+
packagesRegistry: createPackagesRegistry(),
|
|
225
|
+
|
|
226
|
+
includeDevDependencies: false,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
230
|
+
const written = JSON.parse(
|
|
231
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
/** Target is remapped to "" */
|
|
235
|
+
expect(written.workspaces[""]).toBeDefined();
|
|
236
|
+
expect(written.workspaces[""].name).toBe("my-app");
|
|
237
|
+
|
|
238
|
+
/** Internal deps are present */
|
|
239
|
+
expect(written.workspaces["packages/shared"]).toBeDefined();
|
|
240
|
+
expect(written.workspaces["packages/utils"]).toBeDefined();
|
|
241
|
+
|
|
242
|
+
/** Root workspace and non-internal packages are excluded */
|
|
243
|
+
expect(written.workspaces["packages/other"]).toBeUndefined();
|
|
244
|
+
expect(written.workspaces["apps/my-app"]).toBeUndefined();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("should remap target workspace to root key", async () => {
|
|
248
|
+
fs.existsSync.mockReturnValue(true);
|
|
249
|
+
fs.writeFile.mockResolvedValue();
|
|
250
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
251
|
+
|
|
252
|
+
await generateBunLockfile({
|
|
253
|
+
workspaceRootDir: "/workspace",
|
|
254
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
255
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
256
|
+
internalDepPackageNames: ["shared"],
|
|
257
|
+
packagesRegistry: createPackagesRegistry(),
|
|
258
|
+
|
|
259
|
+
includeDevDependencies: false,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
263
|
+
const written = JSON.parse(
|
|
264
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
expect(written.workspaces[""]).toBeDefined();
|
|
268
|
+
expect(written.workspaces["apps/my-app"]).toBeUndefined();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("should prune unreferenced packages", async () => {
|
|
272
|
+
fs.existsSync.mockReturnValue(true);
|
|
273
|
+
fs.writeFile.mockResolvedValue();
|
|
274
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
275
|
+
|
|
276
|
+
await generateBunLockfile({
|
|
277
|
+
workspaceRootDir: "/workspace",
|
|
278
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
279
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
280
|
+
internalDepPackageNames: ["shared", "utils"],
|
|
281
|
+
packagesRegistry: createPackagesRegistry(),
|
|
282
|
+
|
|
283
|
+
includeDevDependencies: false,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
287
|
+
const written = JSON.parse(
|
|
288
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
/** express and its transitive dep body-parser should be kept */
|
|
292
|
+
expect(written.packages["express"]).toBeDefined();
|
|
293
|
+
expect(written.packages["body-parser"]).toBeDefined();
|
|
294
|
+
|
|
295
|
+
/** lodash is used by shared, should be kept */
|
|
296
|
+
expect(written.packages["lodash"]).toBeDefined();
|
|
297
|
+
|
|
298
|
+
/** Workspace entries for kept internal deps should be kept */
|
|
299
|
+
expect(written.packages["shared"]).toBeDefined();
|
|
300
|
+
expect(written.packages["utils"]).toBeDefined();
|
|
301
|
+
|
|
302
|
+
/** axios is only used by "other" which is not in the isolate */
|
|
303
|
+
expect(written.packages["axios"]).toBeUndefined();
|
|
304
|
+
|
|
305
|
+
/** Workspace entry for "other" should be removed */
|
|
306
|
+
expect(written.packages["other"]).toBeUndefined();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("should remove workspace package entries for non-isolated packages", async () => {
|
|
310
|
+
fs.existsSync.mockReturnValue(true);
|
|
311
|
+
fs.writeFile.mockResolvedValue();
|
|
312
|
+
|
|
313
|
+
const lockfile = createBunLockfile();
|
|
314
|
+
/** Add a reference to "other" from the target so it's in requiredPackages */
|
|
315
|
+
const appWorkspace = lockfile.workspaces["apps/my-app"]!;
|
|
316
|
+
(appWorkspace.dependencies as Record<string, string>)["other"] =
|
|
317
|
+
"workspace:*";
|
|
318
|
+
readTypedJsonSync.mockReturnValue(lockfile);
|
|
319
|
+
|
|
320
|
+
await generateBunLockfile({
|
|
321
|
+
workspaceRootDir: "/workspace",
|
|
322
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
323
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
324
|
+
internalDepPackageNames: ["shared", "utils"],
|
|
325
|
+
packagesRegistry: createPackagesRegistry(),
|
|
326
|
+
|
|
327
|
+
includeDevDependencies: false,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
331
|
+
const written = JSON.parse(
|
|
332
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* "other" is referenced but not in internalDepPackageNames, so its
|
|
337
|
+
* workspace entry should be removed from packages
|
|
338
|
+
*/
|
|
339
|
+
expect(written.packages["other"]).toBeUndefined();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("should exclude devDependencies from target when includeDevDependencies is false", async () => {
|
|
343
|
+
fs.existsSync.mockReturnValue(true);
|
|
344
|
+
fs.writeFile.mockResolvedValue();
|
|
345
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
346
|
+
|
|
347
|
+
await generateBunLockfile({
|
|
348
|
+
workspaceRootDir: "/workspace",
|
|
349
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
350
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
351
|
+
internalDepPackageNames: ["shared"],
|
|
352
|
+
packagesRegistry: createPackagesRegistry(),
|
|
353
|
+
|
|
354
|
+
includeDevDependencies: false,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
358
|
+
const written = JSON.parse(
|
|
359
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
/** Target workspace should not have devDependencies */
|
|
363
|
+
expect(written.workspaces[""].devDependencies).toBeUndefined();
|
|
364
|
+
|
|
365
|
+
/** vitest (a devDep) should not be in packages */
|
|
366
|
+
expect(written.packages["vitest"]).toBeUndefined();
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("should include devDependencies from target when includeDevDependencies is true", async () => {
|
|
370
|
+
fs.existsSync.mockReturnValue(true);
|
|
371
|
+
fs.writeFile.mockResolvedValue();
|
|
372
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
373
|
+
|
|
374
|
+
await generateBunLockfile({
|
|
375
|
+
workspaceRootDir: "/workspace",
|
|
376
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
377
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
378
|
+
internalDepPackageNames: ["shared"],
|
|
379
|
+
packagesRegistry: createPackagesRegistry(),
|
|
380
|
+
|
|
381
|
+
includeDevDependencies: true,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
385
|
+
const written = JSON.parse(
|
|
386
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
/** Target workspace should have devDependencies */
|
|
390
|
+
expect(written.workspaces[""].devDependencies).toEqual({
|
|
391
|
+
vitest: "^1.0.0",
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
/** vitest should be in packages */
|
|
395
|
+
expect(written.packages["vitest"]).toBeDefined();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("should always strip devDependencies from internal deps", async () => {
|
|
399
|
+
fs.existsSync.mockReturnValue(true);
|
|
400
|
+
fs.writeFile.mockResolvedValue();
|
|
401
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
402
|
+
|
|
403
|
+
await generateBunLockfile({
|
|
404
|
+
workspaceRootDir: "/workspace",
|
|
405
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
406
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
407
|
+
internalDepPackageNames: ["shared", "utils"],
|
|
408
|
+
packagesRegistry: createPackagesRegistry(),
|
|
409
|
+
|
|
410
|
+
includeDevDependencies: true,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
414
|
+
const written = JSON.parse(
|
|
415
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
/** Internal dep "shared" should not have devDependencies */
|
|
419
|
+
expect(
|
|
420
|
+
written.workspaces["packages/shared"].devDependencies,
|
|
421
|
+
).toBeUndefined();
|
|
422
|
+
|
|
423
|
+
/** typescript (shared's devDep) should not be in packages */
|
|
424
|
+
expect(written.packages["typescript"]).toBeUndefined();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should preserve overrides", async () => {
|
|
428
|
+
fs.existsSync.mockReturnValue(true);
|
|
429
|
+
fs.writeFile.mockResolvedValue();
|
|
430
|
+
|
|
431
|
+
const lockfile = createBunLockfile();
|
|
432
|
+
lockfile.overrides = { lodash: "4.17.21" };
|
|
433
|
+
readTypedJsonSync.mockReturnValue(lockfile);
|
|
434
|
+
|
|
435
|
+
await generateBunLockfile({
|
|
436
|
+
workspaceRootDir: "/workspace",
|
|
437
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
438
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
439
|
+
internalDepPackageNames: ["shared"],
|
|
440
|
+
packagesRegistry: createPackagesRegistry(),
|
|
441
|
+
|
|
442
|
+
includeDevDependencies: false,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
446
|
+
const written = JSON.parse(
|
|
447
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
expect(written.overrides).toEqual({ lodash: "4.17.21" });
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("should filter trustedDependencies to only included packages", async () => {
|
|
454
|
+
fs.existsSync.mockReturnValue(true);
|
|
455
|
+
fs.writeFile.mockResolvedValue();
|
|
456
|
+
|
|
457
|
+
const lockfile = createBunLockfile();
|
|
458
|
+
lockfile.trustedDependencies = ["express", "axios", "lodash"];
|
|
459
|
+
readTypedJsonSync.mockReturnValue(lockfile);
|
|
460
|
+
|
|
461
|
+
await generateBunLockfile({
|
|
462
|
+
workspaceRootDir: "/workspace",
|
|
463
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
464
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
465
|
+
internalDepPackageNames: ["shared"],
|
|
466
|
+
packagesRegistry: createPackagesRegistry(),
|
|
467
|
+
|
|
468
|
+
includeDevDependencies: false,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
472
|
+
const written = JSON.parse(
|
|
473
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
/** axios is not in the output, so it should be filtered out */
|
|
477
|
+
expect(written.trustedDependencies).toEqual(
|
|
478
|
+
expect.arrayContaining(["express", "lodash"]),
|
|
479
|
+
);
|
|
480
|
+
expect(written.trustedDependencies).not.toContain("axios");
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("should filter patchedDependencies to only included packages", async () => {
|
|
484
|
+
fs.existsSync.mockReturnValue(true);
|
|
485
|
+
fs.writeFile.mockResolvedValue();
|
|
486
|
+
|
|
487
|
+
const lockfile = createBunLockfile();
|
|
488
|
+
lockfile.patchedDependencies = {
|
|
489
|
+
"express@4.18.2": "patches/express.patch",
|
|
490
|
+
"axios@1.6.0": "patches/axios.patch",
|
|
491
|
+
};
|
|
492
|
+
readTypedJsonSync.mockReturnValue(lockfile);
|
|
493
|
+
|
|
494
|
+
await generateBunLockfile({
|
|
495
|
+
workspaceRootDir: "/workspace",
|
|
496
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
497
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
498
|
+
internalDepPackageNames: ["shared"],
|
|
499
|
+
packagesRegistry: createPackagesRegistry(),
|
|
500
|
+
|
|
501
|
+
includeDevDependencies: false,
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
505
|
+
const written = JSON.parse(
|
|
506
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
expect(written.patchedDependencies).toEqual({
|
|
510
|
+
"express@4.18.2": "patches/express.patch",
|
|
511
|
+
});
|
|
512
|
+
/** axios is not in the output */
|
|
513
|
+
expect(written.patchedDependencies["axios@1.6.0"]).toBeUndefined();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it("should write output with trailing commas", async () => {
|
|
517
|
+
fs.existsSync.mockReturnValue(true);
|
|
518
|
+
fs.writeFile.mockResolvedValue();
|
|
519
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
520
|
+
|
|
521
|
+
await generateBunLockfile({
|
|
522
|
+
workspaceRootDir: "/workspace",
|
|
523
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
524
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
525
|
+
internalDepPackageNames: ["shared"],
|
|
526
|
+
packagesRegistry: createPackagesRegistry(),
|
|
527
|
+
|
|
528
|
+
includeDevDependencies: false,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
532
|
+
const content = writeCall[1] as string;
|
|
533
|
+
|
|
534
|
+
/** Should contain trailing commas before closing braces/brackets */
|
|
535
|
+
expect(content).toMatch(/,\n\s*\}/);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("should write to the correct output path", async () => {
|
|
539
|
+
fs.existsSync.mockReturnValue(true);
|
|
540
|
+
fs.writeFile.mockResolvedValue();
|
|
541
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
542
|
+
|
|
543
|
+
await generateBunLockfile({
|
|
544
|
+
workspaceRootDir: "/workspace",
|
|
545
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
546
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
547
|
+
internalDepPackageNames: ["shared"],
|
|
548
|
+
packagesRegistry: createPackagesRegistry(),
|
|
549
|
+
|
|
550
|
+
includeDevDependencies: false,
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
554
|
+
"/workspace/apps/my-app/isolate/bun.lock",
|
|
555
|
+
expect.any(String),
|
|
556
|
+
);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it("should preserve lockfileVersion", async () => {
|
|
560
|
+
fs.existsSync.mockReturnValue(true);
|
|
561
|
+
fs.writeFile.mockResolvedValue();
|
|
562
|
+
readTypedJsonSync.mockReturnValue(createBunLockfile());
|
|
563
|
+
|
|
564
|
+
await generateBunLockfile({
|
|
565
|
+
workspaceRootDir: "/workspace",
|
|
566
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
567
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
568
|
+
internalDepPackageNames: ["shared"],
|
|
569
|
+
packagesRegistry: createPackagesRegistry(),
|
|
570
|
+
|
|
571
|
+
includeDevDependencies: false,
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
575
|
+
const written = JSON.parse(
|
|
576
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
expect(written.lockfileVersion).toBe(0);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it("should resolve transitive dependencies", async () => {
|
|
583
|
+
fs.existsSync.mockReturnValue(true);
|
|
584
|
+
fs.writeFile.mockResolvedValue();
|
|
585
|
+
|
|
586
|
+
const lockfile = createBunLockfile();
|
|
587
|
+
/** Add a chain: express -> body-parser -> raw-body */
|
|
588
|
+
lockfile.packages["body-parser"]![2] = {
|
|
589
|
+
dependencies: { "raw-body": "2.5.1" },
|
|
590
|
+
};
|
|
591
|
+
lockfile.packages["raw-body"] = [
|
|
592
|
+
"raw-body@2.5.1",
|
|
593
|
+
"https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
|
594
|
+
{},
|
|
595
|
+
"sha512-stu",
|
|
596
|
+
];
|
|
597
|
+
readTypedJsonSync.mockReturnValue(lockfile);
|
|
598
|
+
|
|
599
|
+
await generateBunLockfile({
|
|
600
|
+
workspaceRootDir: "/workspace",
|
|
601
|
+
targetPackageDir: "/workspace/apps/my-app",
|
|
602
|
+
isolateDir: "/workspace/apps/my-app/isolate",
|
|
603
|
+
internalDepPackageNames: ["shared"],
|
|
604
|
+
packagesRegistry: createPackagesRegistry(),
|
|
605
|
+
|
|
606
|
+
includeDevDependencies: false,
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
const writeCall = fs.writeFile.mock.calls[0]!;
|
|
610
|
+
const written = JSON.parse(
|
|
611
|
+
(writeCall[1] as string).replace(/,(\s*[}\]])/g, "$1"),
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
/** The full transitive chain should be present */
|
|
615
|
+
expect(written.packages["express"]).toBeDefined();
|
|
616
|
+
expect(written.packages["body-parser"]).toBeDefined();
|
|
617
|
+
expect(written.packages["raw-body"]).toBeDefined();
|
|
618
|
+
});
|
|
619
|
+
});
|