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
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import yaml from "yaml";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
const mockLogger = {
|
|
8
|
+
debug: vi.fn(),
|
|
9
|
+
warn: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
vi.mock("#/lib/logger", () => ({
|
|
13
|
+
useLogger: vi.fn(() => mockLogger),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const { resolveCatalogDependencies } =
|
|
17
|
+
await import("./resolve-catalog-dependencies");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a temporary directory with optional pnpm-workspace.yaml and
|
|
21
|
+
* package.json for testing catalog resolution.
|
|
22
|
+
*/
|
|
23
|
+
async function createTempWorkspace({
|
|
24
|
+
workspaceYaml,
|
|
25
|
+
workspaceYamlRaw,
|
|
26
|
+
packageJson,
|
|
27
|
+
}: {
|
|
28
|
+
workspaceYaml?: object;
|
|
29
|
+
workspaceYamlRaw?: string;
|
|
30
|
+
packageJson?: object;
|
|
31
|
+
}): Promise<string> {
|
|
32
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "isolate-test-"));
|
|
33
|
+
|
|
34
|
+
if (workspaceYaml !== undefined) {
|
|
35
|
+
await fs.writeFile(
|
|
36
|
+
path.join(dir, "pnpm-workspace.yaml"),
|
|
37
|
+
yaml.stringify(workspaceYaml),
|
|
38
|
+
"utf-8",
|
|
39
|
+
);
|
|
40
|
+
} else if (workspaceYamlRaw !== undefined) {
|
|
41
|
+
await fs.writeFile(
|
|
42
|
+
path.join(dir, "pnpm-workspace.yaml"),
|
|
43
|
+
workspaceYamlRaw,
|
|
44
|
+
"utf-8",
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Always write a minimal package.json so the fallback doesn't throw. */
|
|
49
|
+
const manifest = packageJson ?? { name: "root", version: "0.0.0" };
|
|
50
|
+
await fs.writeJson(path.join(dir, "package.json"), manifest);
|
|
51
|
+
|
|
52
|
+
return dir;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe("resolveCatalogDependencies", () => {
|
|
56
|
+
let tmpDir: string;
|
|
57
|
+
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(async () => {
|
|
63
|
+
if (tmpDir) {
|
|
64
|
+
await fs.remove(tmpDir);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("with no dependencies", () => {
|
|
69
|
+
it("returns undefined when dependencies is undefined", async () => {
|
|
70
|
+
tmpDir = await createTempWorkspace({});
|
|
71
|
+
const result = await resolveCatalogDependencies(undefined, tmpDir);
|
|
72
|
+
expect(result).toBeUndefined();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("pnpm-workspace.yaml: default catalog (catalog:)", () => {
|
|
77
|
+
it("resolves catalog: specifiers from pnpm-workspace.yaml", async () => {
|
|
78
|
+
tmpDir = await createTempWorkspace({
|
|
79
|
+
workspaceYaml: {
|
|
80
|
+
packages: ["packages/*"],
|
|
81
|
+
catalog: {
|
|
82
|
+
react: "^18.3.1",
|
|
83
|
+
typescript: "^5.0.0",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const result = await resolveCatalogDependencies(
|
|
89
|
+
{ react: "catalog:", typescript: "catalog:", lodash: "^4.0.0" },
|
|
90
|
+
tmpDir,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(result).toEqual({
|
|
94
|
+
react: "^18.3.1",
|
|
95
|
+
typescript: "^5.0.0",
|
|
96
|
+
lodash: "^4.0.0",
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("also handles catalog:default as the default catalog name", async () => {
|
|
101
|
+
tmpDir = await createTempWorkspace({
|
|
102
|
+
workspaceYaml: {
|
|
103
|
+
packages: ["packages/*"],
|
|
104
|
+
catalogs: {
|
|
105
|
+
default: {
|
|
106
|
+
react: "^18.3.1",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const result = await resolveCatalogDependencies(
|
|
113
|
+
{ react: "catalog:default" },
|
|
114
|
+
tmpDir,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(result).toEqual({ react: "^18.3.1" });
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("resolves catalog:default from the top-level catalog field", async () => {
|
|
121
|
+
tmpDir = await createTempWorkspace({
|
|
122
|
+
workspaceYaml: {
|
|
123
|
+
packages: ["packages/*"],
|
|
124
|
+
catalog: {
|
|
125
|
+
react: "^18.3.1",
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const result = await resolveCatalogDependencies(
|
|
131
|
+
{ react: "catalog:default" },
|
|
132
|
+
tmpDir,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(result).toEqual({ react: "^18.3.1" });
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("pnpm-workspace.yaml: named catalogs (catalog:<name>)", () => {
|
|
140
|
+
it("resolves named catalog specifiers from pnpm-workspace.yaml", async () => {
|
|
141
|
+
tmpDir = await createTempWorkspace({
|
|
142
|
+
workspaceYaml: {
|
|
143
|
+
packages: ["packages/*"],
|
|
144
|
+
catalogs: {
|
|
145
|
+
react18: {
|
|
146
|
+
react: "^18.3.1",
|
|
147
|
+
"react-dom": "^18.3.1",
|
|
148
|
+
},
|
|
149
|
+
react19: {
|
|
150
|
+
react: "^19.0.0",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const result = await resolveCatalogDependencies(
|
|
157
|
+
{
|
|
158
|
+
react: "catalog:react18",
|
|
159
|
+
"react-dom": "catalog:react18",
|
|
160
|
+
},
|
|
161
|
+
tmpDir,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(result).toEqual({
|
|
165
|
+
react: "^18.3.1",
|
|
166
|
+
"react-dom": "^18.3.1",
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("pnpm-workspace.yaml: missing package in catalog", () => {
|
|
172
|
+
it("keeps original specifier and warns when package not found in catalog", async () => {
|
|
173
|
+
tmpDir = await createTempWorkspace({
|
|
174
|
+
workspaceYaml: {
|
|
175
|
+
packages: ["packages/*"],
|
|
176
|
+
catalog: { react: "^18.0.0" },
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const result = await resolveCatalogDependencies(
|
|
181
|
+
{ react: "catalog:", "missing-pkg": "catalog:" },
|
|
182
|
+
tmpDir,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
expect(result).toEqual({
|
|
186
|
+
react: "^18.0.0",
|
|
187
|
+
"missing-pkg": "catalog:", // kept as-is with a warning
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("package.json fallback (Bun format)", () => {
|
|
193
|
+
it("resolves catalog: from root-level catalog field in package.json", async () => {
|
|
194
|
+
tmpDir = await createTempWorkspace({
|
|
195
|
+
packageJson: {
|
|
196
|
+
name: "root",
|
|
197
|
+
version: "0.0.0",
|
|
198
|
+
catalog: {
|
|
199
|
+
react: "^18.3.1",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const result = await resolveCatalogDependencies(
|
|
205
|
+
{ react: "catalog:" },
|
|
206
|
+
tmpDir,
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
expect(result).toEqual({ react: "^18.3.1" });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("resolves catalog: from workspaces.catalog in package.json", async () => {
|
|
213
|
+
tmpDir = await createTempWorkspace({
|
|
214
|
+
packageJson: {
|
|
215
|
+
name: "root",
|
|
216
|
+
version: "0.0.0",
|
|
217
|
+
workspaces: {
|
|
218
|
+
packages: ["packages/*"],
|
|
219
|
+
catalog: {
|
|
220
|
+
typescript: "^5.4.0",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const result = await resolveCatalogDependencies(
|
|
227
|
+
{ typescript: "catalog:" },
|
|
228
|
+
tmpDir,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
expect(result).toEqual({ typescript: "^5.4.0" });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("resolves named catalog: from root-level catalogs field in package.json", async () => {
|
|
235
|
+
tmpDir = await createTempWorkspace({
|
|
236
|
+
packageJson: {
|
|
237
|
+
name: "root",
|
|
238
|
+
version: "0.0.0",
|
|
239
|
+
catalogs: {
|
|
240
|
+
react18: {
|
|
241
|
+
react: "^18.3.1",
|
|
242
|
+
"react-dom": "^18.3.1",
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const result = await resolveCatalogDependencies(
|
|
249
|
+
{
|
|
250
|
+
react: "catalog:react18",
|
|
251
|
+
"react-dom": "catalog:react18",
|
|
252
|
+
},
|
|
253
|
+
tmpDir,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
expect(result).toEqual({
|
|
257
|
+
react: "^18.3.1",
|
|
258
|
+
"react-dom": "^18.3.1",
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("resolves named catalog: from workspaces.catalogs in package.json", async () => {
|
|
263
|
+
tmpDir = await createTempWorkspace({
|
|
264
|
+
packageJson: {
|
|
265
|
+
name: "root",
|
|
266
|
+
version: "0.0.0",
|
|
267
|
+
workspaces: {
|
|
268
|
+
packages: ["packages/*"],
|
|
269
|
+
catalogs: {
|
|
270
|
+
tools: {
|
|
271
|
+
typescript: "^5.4.0",
|
|
272
|
+
eslint: "^8.0.0",
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const result = await resolveCatalogDependencies(
|
|
280
|
+
{ typescript: "catalog:tools", eslint: "catalog:tools" },
|
|
281
|
+
tmpDir,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
expect(result).toEqual({
|
|
285
|
+
typescript: "^5.4.0",
|
|
286
|
+
eslint: "^8.0.0",
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("precedence", () => {
|
|
292
|
+
it("prefers pnpm-workspace.yaml over package.json when both define the same catalog dependency", async () => {
|
|
293
|
+
tmpDir = await createTempWorkspace({
|
|
294
|
+
workspaceYaml: {
|
|
295
|
+
packages: ["packages/*"],
|
|
296
|
+
catalog: {
|
|
297
|
+
react: "^18.3.1",
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
packageJson: {
|
|
301
|
+
name: "root",
|
|
302
|
+
version: "0.0.0",
|
|
303
|
+
catalog: {
|
|
304
|
+
react: "^18.2.0",
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const result = await resolveCatalogDependencies(
|
|
310
|
+
{ react: "catalog:" },
|
|
311
|
+
tmpDir,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
expect(result).toEqual({ react: "^18.3.1" });
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe("pnpm-workspace.yaml with invalid YAML", () => {
|
|
319
|
+
it("warns about invalid YAML and falls back to package.json", async () => {
|
|
320
|
+
tmpDir = await createTempWorkspace({
|
|
321
|
+
workspaceYamlRaw: "catalog:\n react: ^18.3.1\n - invalid format",
|
|
322
|
+
packageJson: {
|
|
323
|
+
name: "root",
|
|
324
|
+
version: "0.0.0",
|
|
325
|
+
catalog: { react: "^18.0.0" },
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const result = await resolveCatalogDependencies(
|
|
330
|
+
{ react: "catalog:" },
|
|
331
|
+
tmpDir,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
/** Verify it fell back to package.json correctly. */
|
|
335
|
+
expect(result).toEqual({ react: "^18.0.0" });
|
|
336
|
+
|
|
337
|
+
/** Verify a warning was logged. */
|
|
338
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
339
|
+
expect.stringContaining(
|
|
340
|
+
`Failed to parse ${path.join(tmpDir, "pnpm-workspace.yaml")}:`,
|
|
341
|
+
),
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe("pnpm-workspace.yaml without catalog fields", () => {
|
|
347
|
+
it("falls back to package.json when pnpm-workspace.yaml has no catalog", async () => {
|
|
348
|
+
tmpDir = await createTempWorkspace({
|
|
349
|
+
workspaceYaml: {
|
|
350
|
+
/** No catalog or catalogs field - just packages. */
|
|
351
|
+
packages: ["packages/*"],
|
|
352
|
+
},
|
|
353
|
+
packageJson: {
|
|
354
|
+
name: "root",
|
|
355
|
+
version: "0.0.0",
|
|
356
|
+
catalog: { react: "^18.0.0" },
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const result = await resolveCatalogDependencies(
|
|
361
|
+
{ react: "catalog:" },
|
|
362
|
+
tmpDir,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
expect(result).toEqual({ react: "^18.0.0" });
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe("no catalog defined anywhere", () => {
|
|
370
|
+
it("returns dependencies as-is when no catalog is found", async () => {
|
|
371
|
+
tmpDir = await createTempWorkspace({});
|
|
372
|
+
|
|
373
|
+
const result = await resolveCatalogDependencies(
|
|
374
|
+
{ react: "^18.0.0", typescript: "^5.0.0" },
|
|
375
|
+
tmpDir,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
expect(result).toEqual({
|
|
379
|
+
react: "^18.0.0",
|
|
380
|
+
typescript: "^5.0.0",
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe("non-catalog specifiers", () => {
|
|
386
|
+
it("leaves non-catalog specifiers unchanged", async () => {
|
|
387
|
+
tmpDir = await createTempWorkspace({
|
|
388
|
+
workspaceYaml: {
|
|
389
|
+
packages: ["packages/*"],
|
|
390
|
+
catalog: { react: "^18.0.0" },
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const result = await resolveCatalogDependencies(
|
|
395
|
+
{
|
|
396
|
+
react: "catalog:",
|
|
397
|
+
lodash: "^4.0.0",
|
|
398
|
+
typescript: "workspace:*",
|
|
399
|
+
},
|
|
400
|
+
tmpDir,
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
expect(result).toEqual({
|
|
404
|
+
react: "^18.0.0",
|
|
405
|
+
lodash: "^4.0.0",
|
|
406
|
+
typescript: "workspace:*",
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
});
|
|
@@ -1,16 +1,112 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
1
2
|
import path from "node:path";
|
|
2
|
-
import { useLogger } from "
|
|
3
|
-
import type { PackageManifest } from "
|
|
4
|
-
import { readTypedJson } from "
|
|
3
|
+
import { useLogger } from "#/lib/logger";
|
|
4
|
+
import type { PackageManifest } from "#/lib/types";
|
|
5
|
+
import { readTypedJson } from "#/lib/utils";
|
|
6
|
+
import yaml from "yaml";
|
|
7
|
+
|
|
8
|
+
type CatalogMap = Record<string, string>;
|
|
9
|
+
type CatalogsMap = Record<string, CatalogMap>;
|
|
10
|
+
|
|
11
|
+
type CatalogSource = {
|
|
12
|
+
catalog?: CatalogMap;
|
|
13
|
+
catalogs?: CatalogsMap;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const catalogSourceCache = new Map<string, Promise<CatalogSource>>();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Loads catalog definitions by checking pnpm-workspace.yaml first (pnpm
|
|
20
|
+
* format), then falling back to the root package.json (Bun format).
|
|
21
|
+
*
|
|
22
|
+
* Pnpm defines catalogs in pnpm-workspace.yaml:
|
|
23
|
+
*
|
|
24
|
+
* ```yaml
|
|
25
|
+
* catalog:
|
|
26
|
+
* react: ^18.3.1
|
|
27
|
+
* catalogs:
|
|
28
|
+
* react18:
|
|
29
|
+
* react: ^18.3.1
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Bun defines catalogs in package.json (at root level or under workspaces).
|
|
33
|
+
*/
|
|
34
|
+
async function loadCatalogSource(
|
|
35
|
+
workspaceRootDir: string,
|
|
36
|
+
): Promise<CatalogSource> {
|
|
37
|
+
const cached = catalogSourceCache.get(workspaceRootDir);
|
|
38
|
+
if (cached) {
|
|
39
|
+
return cached;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const loadPromise = (async () => {
|
|
43
|
+
const log = useLogger();
|
|
44
|
+
|
|
45
|
+
/** Try pnpm-workspace.yaml first. */
|
|
46
|
+
const workspaceYamlPath = path.join(
|
|
47
|
+
workspaceRootDir,
|
|
48
|
+
"pnpm-workspace.yaml",
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (await fs.pathExists(workspaceYamlPath)) {
|
|
52
|
+
try {
|
|
53
|
+
const rawContent = await fs.readFile(workspaceYamlPath, "utf-8");
|
|
54
|
+
const yamlConfig = yaml.parse(rawContent) as CatalogSource | null;
|
|
55
|
+
|
|
56
|
+
if (yamlConfig?.catalog || yamlConfig?.catalogs) {
|
|
57
|
+
return {
|
|
58
|
+
catalog: yamlConfig.catalog,
|
|
59
|
+
catalogs: yamlConfig.catalogs,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
log.warn(
|
|
64
|
+
`Failed to parse ${workspaceYamlPath}: ${error instanceof Error ? error.message : String(error)}. Falling back to package.json for catalog definitions.`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Fall back to package.json (Bun format). */
|
|
70
|
+
const rootManifestPath = path.join(workspaceRootDir, "package.json");
|
|
71
|
+
const rootManifest = (await readTypedJson(
|
|
72
|
+
rootManifestPath,
|
|
73
|
+
)) as PackageManifest & {
|
|
74
|
+
catalog?: CatalogMap;
|
|
75
|
+
catalogs?: CatalogsMap;
|
|
76
|
+
workspaces?: {
|
|
77
|
+
catalog?: CatalogMap;
|
|
78
|
+
catalogs?: CatalogsMap;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
catalog: rootManifest.catalog ?? rootManifest.workspaces?.catalog,
|
|
84
|
+
catalogs: rootManifest.catalogs ?? rootManifest.workspaces?.catalogs,
|
|
85
|
+
};
|
|
86
|
+
})();
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Drop the cache entry if loading fails so a subsequent call can retry
|
|
90
|
+
* instead of immediately rethrowing the same rejection.
|
|
91
|
+
*/
|
|
92
|
+
const cachedLoadPromise = loadPromise.catch((error) => {
|
|
93
|
+
catalogSourceCache.delete(workspaceRootDir);
|
|
94
|
+
throw error;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
catalogSourceCache.set(workspaceRootDir, cachedLoadPromise);
|
|
98
|
+
return cachedLoadPromise;
|
|
99
|
+
}
|
|
5
100
|
|
|
6
101
|
/**
|
|
7
102
|
* Resolves catalog dependencies by replacing "catalog:" specifiers with their
|
|
8
|
-
* actual versions
|
|
103
|
+
* actual versions.
|
|
9
104
|
*
|
|
10
105
|
* Supports both pnpm and Bun catalog formats:
|
|
11
106
|
*
|
|
12
|
-
* - Pnpm: catalog
|
|
13
|
-
* - Bun: catalog or catalogs at root level, or workspaces.catalog
|
|
107
|
+
* - Pnpm: catalog/catalogs defined in pnpm-workspace.yaml
|
|
108
|
+
* - Bun: catalog or catalogs at root level, or workspaces.catalog in
|
|
109
|
+
* package.json
|
|
14
110
|
*/
|
|
15
111
|
export async function resolveCatalogDependencies(
|
|
16
112
|
dependencies: Record<string, string> | undefined,
|
|
@@ -21,22 +117,8 @@ export async function resolveCatalogDependencies(
|
|
|
21
117
|
}
|
|
22
118
|
|
|
23
119
|
const log = useLogger();
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
PackageManifest & {
|
|
27
|
-
catalog?: Record<string, string>;
|
|
28
|
-
catalogs?: Record<string, Record<string, string>>;
|
|
29
|
-
workspaces?: {
|
|
30
|
-
catalog?: Record<string, string>;
|
|
31
|
-
catalogs?: Record<string, Record<string, string>>;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
>(rootManifestPath);
|
|
35
|
-
|
|
36
|
-
// Try to find catalog in various locations (pnpm and Bun formats)
|
|
37
|
-
const flatCatalog = rootManifest.catalog || rootManifest.workspaces?.catalog;
|
|
38
|
-
const nestedCatalogs =
|
|
39
|
-
rootManifest.catalogs || rootManifest.workspaces?.catalogs;
|
|
120
|
+
const { catalog: flatCatalog, catalogs: nestedCatalogs } =
|
|
121
|
+
await loadCatalogSource(workspaceRootDir);
|
|
40
122
|
|
|
41
123
|
if (!flatCatalog && !nestedCatalogs) {
|
|
42
124
|
// No catalog found, return dependencies as-is
|
|
@@ -50,12 +132,18 @@ export async function resolveCatalogDependencies(
|
|
|
50
132
|
if (specifier === "catalog:" || specifier.startsWith("catalog:")) {
|
|
51
133
|
let catalogVersion: string | undefined;
|
|
52
134
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
135
|
+
const groupName =
|
|
136
|
+
specifier === "catalog:" ? "default" : specifier.slice(8);
|
|
137
|
+
|
|
138
|
+
if (groupName === "default") {
|
|
139
|
+
/**
|
|
140
|
+
* Per pnpm semantics, `catalog:` and `catalog:default` are
|
|
141
|
+
* equivalent: the default catalog can live under the top-level
|
|
142
|
+
* `catalog` field or under `catalogs.default`, so check both.
|
|
143
|
+
*/
|
|
144
|
+
catalogVersion =
|
|
145
|
+
flatCatalog?.[packageName] ?? nestedCatalogs?.default?.[packageName];
|
|
56
146
|
} else {
|
|
57
|
-
// Catalog group reference (e.g., "catalog:group1")
|
|
58
|
-
const groupName = specifier.slice(8);
|
|
59
147
|
catalogVersion = nestedCatalogs?.[groupName]?.[packageName];
|
|
60
148
|
}
|
|
61
149
|
|
package/src/lib/manifest/io.ts
CHANGED
|
@@ -3,8 +3,12 @@ import path from "node:path";
|
|
|
3
3
|
import type { PackageManifest } from "../types";
|
|
4
4
|
import { readTypedJson } from "../utils";
|
|
5
5
|
|
|
6
|
-
export async function readManifest(
|
|
7
|
-
|
|
6
|
+
export async function readManifest(
|
|
7
|
+
packageDir: string,
|
|
8
|
+
): Promise<PackageManifest> {
|
|
9
|
+
return (await readTypedJson(
|
|
10
|
+
path.join(packageDir, "package.json"),
|
|
11
|
+
)) as PackageManifest;
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
export async function writeManifest(
|
|
@@ -45,8 +45,8 @@ export function validateManifestMandatoryFields(
|
|
|
45
45
|
missingFields.push("files");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const [field] = missingFields;
|
|
49
|
+
if (field) {
|
|
50
50
|
const errorMessage =
|
|
51
51
|
missingFields.length === 1
|
|
52
52
|
? `Package at ${packagePath} is missing the "${field}" field in its package.json. See ${fieldDocUrls[field] ?? "https://isolate-package.codecompose.dev/getting-started#prerequisites"}`
|
|
@@ -2,7 +2,7 @@ import { got } from "get-or-throw";
|
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import { useLogger } from "../logger";
|
|
4
4
|
import type { PackagesRegistry } from "../types";
|
|
5
|
-
import { pack } from "../utils";
|
|
5
|
+
import { pack } from "../utils/pack";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Pack dependencies so that we extract only the files that are supposed to be
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import { pack
|
|
5
|
-
|
|
6
|
-
const TIMEOUT_MS = 5000;
|
|
3
|
+
import { unpack } from "../utils";
|
|
4
|
+
import { pack } from "../utils/pack";
|
|
7
5
|
|
|
8
6
|
export async function processBuildOutputFiles({
|
|
9
7
|
targetPackageDir,
|
|
@@ -14,22 +12,13 @@ export async function processBuildOutputFiles({
|
|
|
14
12
|
tmpDir: string;
|
|
15
13
|
isolateDir: string;
|
|
16
14
|
}) {
|
|
17
|
-
const log = useLogger();
|
|
18
|
-
|
|
19
15
|
const packedFilePath = await pack(targetPackageDir, tmpDir);
|
|
20
16
|
const unpackDir = path.join(tmpDir, "target");
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!isWaitingYet) {
|
|
27
|
-
log.debug(`Waiting for ${packedFilePath} to become available...`);
|
|
28
|
-
}
|
|
29
|
-
isWaitingYet = true;
|
|
30
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
31
|
-
}
|
|
32
|
-
|
|
18
|
+
/**
|
|
19
|
+
* `pack` already waits for the tarball to be fully written before returning,
|
|
20
|
+
* so it is safe to unpack immediately.
|
|
21
|
+
*/
|
|
33
22
|
await unpack(packedFilePath, unpackDir);
|
|
34
23
|
await fs.copy(path.join(unpackDir, "package"), isolateDir);
|
|
35
24
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { getErrorMessage } from "
|
|
5
|
-
import { getMajorVersion } from "
|
|
4
|
+
import { getErrorMessage } from "#/lib/utils";
|
|
5
|
+
import { getMajorVersion } from "#/lib/utils/get-major-version";
|
|
6
6
|
import type { PackageManager, PackageManagerName } from "../names";
|
|
7
7
|
import { getLockfileFileName, supportedPackageManagerNames } from "../names";
|
|
8
8
|
|
|
@@ -15,10 +15,10 @@ export function inferFromFiles(workspaceRoot: string): PackageManager {
|
|
|
15
15
|
const version = getVersion(name);
|
|
16
16
|
|
|
17
17
|
return { name, version, majorVersion: getMajorVersion(version) };
|
|
18
|
-
} catch (
|
|
18
|
+
} catch (error) {
|
|
19
19
|
throw new Error(
|
|
20
|
-
`Failed to find package manager version for ${name}: ${getErrorMessage(
|
|
21
|
-
{ cause:
|
|
20
|
+
`Failed to find package manager version for ${name}: ${getErrorMessage(error)}`,
|
|
21
|
+
{ cause: error },
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
24
|
}
|