isolate-package 1.29.0-1 → 1.29.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/{isolate-CqPWp-1k.mjs → isolate-3GcdAuUK.mjs} +31 -217
- package/dist/isolate-3GcdAuUK.mjs.map +1 -0
- package/dist/isolate-bin.mjs +1 -1
- package/package.json +1 -1
- package/src/get-internal-package-names.test.ts +10 -0
- package/src/isolate.ts +3 -25
- package/src/lib/lockfile/helpers/index.ts +0 -1
- package/src/lib/lockfile/process-lockfile.ts +6 -6
- package/src/lib/manifest/adapt-target-package-manifest.ts +4 -5
- package/src/lib/manifest/helpers/adapt-internal-package-manifests.test.ts +132 -0
- package/src/lib/manifest/helpers/adapt-internal-package-manifests.ts +5 -6
- package/src/lib/manifest/validate-manifest.test.ts +19 -10
- package/src/lib/manifest/validate-manifest.ts +13 -1
- package/src/lib/patches/copy-patches.test.ts +301 -311
- package/src/lib/patches/copy-patches.ts +28 -19
- package/src/lib/types.ts +2 -3
- package/src/lib/utils/filter-patched-dependencies.test.ts +11 -1
- package/dist/isolate-CqPWp-1k.mjs.map +0 -1
- package/src/lib/lockfile/helpers/generate-bun-lockfile.test.ts +0 -619
- package/src/lib/lockfile/helpers/generate-bun-lockfile.ts +0 -340
- package/src/lib/lockfile/helpers/generate-pnpm-lockfile.test.ts +0 -387
- package/src/lib/lockfile/helpers/pnpm-map-importer.test.ts +0 -183
- package/src/lib/lockfile/process-lockfile.test.ts +0 -238
- package/src/testing/setup.ts +0 -13
|
@@ -4,6 +4,16 @@ 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
|
+
|
|
7
17
|
const packageManagerResult = {
|
|
8
18
|
name: "pnpm",
|
|
9
19
|
version: "9.0.0",
|
package/src/isolate.ts
CHANGED
|
@@ -193,12 +193,11 @@ 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
|
|
197
|
-
*
|
|
196
|
+
* correct paths. Only copy patches when output uses pnpm, since patched
|
|
197
|
+
* dependencies are a pnpm-specific feature.
|
|
198
198
|
*/
|
|
199
199
|
const shouldCopyPatches =
|
|
200
|
-
|
|
201
|
-
!config.forceNpm;
|
|
200
|
+
packageManager.name === "pnpm" && !config.forceNpm;
|
|
202
201
|
|
|
203
202
|
const copiedPatches = shouldCopyPatches
|
|
204
203
|
? await copyPatches({
|
|
@@ -292,18 +291,6 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
292
291
|
}
|
|
293
292
|
}
|
|
294
293
|
|
|
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
|
-
|
|
307
294
|
/**
|
|
308
295
|
* If there is an .npmrc file in the workspace root, copy it to the isolate
|
|
309
296
|
* because the settings there could affect how the lockfile is resolved.
|
|
@@ -318,15 +305,6 @@ export function createIsolator(config?: IsolateConfig) {
|
|
|
318
305
|
log.debug("Copied .npmrc file to the isolate output");
|
|
319
306
|
}
|
|
320
307
|
|
|
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
|
-
|
|
330
308
|
/**
|
|
331
309
|
* Clean up. Only do this when things succeed, so we can look at the temp
|
|
332
310
|
* folder in case something goes wrong.
|
|
@@ -3,7 +3,6 @@ import { useLogger } from "../logger";
|
|
|
3
3
|
import { usePackageManager } from "../package-manager";
|
|
4
4
|
import type { PackageManifest, PackagesRegistry, PatchFile } from "../types";
|
|
5
5
|
import {
|
|
6
|
-
generateBunLockfile,
|
|
7
6
|
generateNpmLockfile,
|
|
8
7
|
generatePnpmLockfile,
|
|
9
8
|
generateYarnLockfile,
|
|
@@ -98,14 +97,15 @@ export async function processLockfile({
|
|
|
98
97
|
break;
|
|
99
98
|
}
|
|
100
99
|
case "bun": {
|
|
101
|
-
|
|
100
|
+
log.warn(
|
|
101
|
+
`Ouput lockfiles for Bun are not yet supported. Using NPM for output`,
|
|
102
|
+
);
|
|
103
|
+
await generateNpmLockfile({
|
|
102
104
|
workspaceRootDir,
|
|
103
|
-
targetPackageDir,
|
|
104
105
|
isolateDir,
|
|
105
|
-
internalDepPackageNames,
|
|
106
|
-
packagesRegistry,
|
|
107
|
-
includeDevDependencies: config.includeDevDependencies,
|
|
108
106
|
});
|
|
107
|
+
|
|
108
|
+
usedFallbackToNpm = true;
|
|
109
109
|
break;
|
|
110
110
|
}
|
|
111
111
|
default:
|
|
@@ -51,12 +51,11 @@ export async function adaptTargetPackageManifest({
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const adaptedManifest =
|
|
54
|
-
|
|
55
|
-
!forceNpm
|
|
54
|
+
packageManager.name === "pnpm" && !forceNpm
|
|
56
55
|
? /**
|
|
57
|
-
* For PNPM
|
|
58
|
-
*
|
|
59
|
-
*
|
|
56
|
+
* For PNPM the output itself is a workspace so we can preserve the specifiers
|
|
57
|
+
* with "workspace:*" in the output manifest, but we do want to adopt the
|
|
58
|
+
* pnpm.overrides field from the root package.json.
|
|
60
59
|
*/
|
|
61
60
|
await adoptPnpmFieldsFromRoot(
|
|
62
61
|
manifestWithResolvedCatalogs,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import type { PackageManifest, PackagesRegistry } from "~/lib/types";
|
|
3
|
+
|
|
4
|
+
/** Mock dependencies */
|
|
5
|
+
vi.mock("~/lib/package-manager", () => ({
|
|
6
|
+
usePackageManager: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("../io", () => ({
|
|
10
|
+
writeManifest: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock("./adapt-manifest-internal-deps", () => ({
|
|
14
|
+
adaptManifestInternalDeps: vi.fn(({ manifest }) => manifest),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("./resolve-catalog-dependencies", () => ({
|
|
18
|
+
resolveCatalogDependencies: vi.fn((deps) => Promise.resolve(deps)),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const { usePackageManager } = vi.mocked(await import("~/lib/package-manager"));
|
|
22
|
+
|
|
23
|
+
const { writeManifest } = vi.mocked(await import("../io"));
|
|
24
|
+
|
|
25
|
+
const { adaptInternalPackageManifests } =
|
|
26
|
+
await import("./adapt-internal-package-manifests");
|
|
27
|
+
|
|
28
|
+
describe("adaptInternalPackageManifests", () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
usePackageManager.mockReturnValue({
|
|
32
|
+
name: "pnpm",
|
|
33
|
+
version: "9.0.0",
|
|
34
|
+
majorVersion: 9,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function createRegistry(
|
|
39
|
+
entries: Record<
|
|
40
|
+
string,
|
|
41
|
+
{ rootRelativeDir: string; manifest: PackageManifest }
|
|
42
|
+
>,
|
|
43
|
+
): PackagesRegistry {
|
|
44
|
+
const registry: PackagesRegistry = {};
|
|
45
|
+
for (const [name, { rootRelativeDir, manifest }] of Object.entries(
|
|
46
|
+
entries,
|
|
47
|
+
)) {
|
|
48
|
+
registry[name] = {
|
|
49
|
+
absoluteDir: `/workspace/${rootRelativeDir}`,
|
|
50
|
+
rootRelativeDir,
|
|
51
|
+
manifest,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return registry;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
it("should preserve scripts in internal dependency manifests", async () => {
|
|
58
|
+
const manifest: PackageManifest = {
|
|
59
|
+
name: "@repo/database",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
scripts: {
|
|
62
|
+
postinstall: "prisma generate",
|
|
63
|
+
build: "tsc",
|
|
64
|
+
},
|
|
65
|
+
dependencies: {
|
|
66
|
+
prisma: "^5.0.0",
|
|
67
|
+
},
|
|
68
|
+
devDependencies: {
|
|
69
|
+
typescript: "^5.0.0",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const packagesRegistry = createRegistry({
|
|
74
|
+
"@repo/database": {
|
|
75
|
+
rootRelativeDir: "packages/database",
|
|
76
|
+
manifest,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await adaptInternalPackageManifests({
|
|
81
|
+
internalPackageNames: ["@repo/database"],
|
|
82
|
+
packagesRegistry,
|
|
83
|
+
isolateDir: "/output",
|
|
84
|
+
forceNpm: false,
|
|
85
|
+
workspaceRootDir: "/workspace",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(writeManifest).toHaveBeenCalledOnce();
|
|
89
|
+
|
|
90
|
+
const writtenManifest = writeManifest.mock.calls[0]![1];
|
|
91
|
+
|
|
92
|
+
expect(writtenManifest.scripts).toEqual({
|
|
93
|
+
postinstall: "prisma generate",
|
|
94
|
+
build: "tsc",
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should strip devDependencies from internal dependency manifests", async () => {
|
|
99
|
+
const manifest: PackageManifest = {
|
|
100
|
+
name: "@repo/shared",
|
|
101
|
+
version: "1.0.0",
|
|
102
|
+
dependencies: {
|
|
103
|
+
lodash: "^4.0.0",
|
|
104
|
+
},
|
|
105
|
+
devDependencies: {
|
|
106
|
+
vitest: "^1.0.0",
|
|
107
|
+
typescript: "^5.0.0",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const packagesRegistry = createRegistry({
|
|
112
|
+
"@repo/shared": {
|
|
113
|
+
rootRelativeDir: "packages/shared",
|
|
114
|
+
manifest,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await adaptInternalPackageManifests({
|
|
119
|
+
internalPackageNames: ["@repo/shared"],
|
|
120
|
+
packagesRegistry,
|
|
121
|
+
isolateDir: "/output",
|
|
122
|
+
forceNpm: false,
|
|
123
|
+
workspaceRootDir: "/workspace",
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(writeManifest).toHaveBeenCalledOnce();
|
|
127
|
+
|
|
128
|
+
const writtenManifest = writeManifest.mock.calls[0]![1];
|
|
129
|
+
|
|
130
|
+
expect(writtenManifest.devDependencies).toBeUndefined();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -31,8 +31,8 @@ export async function adaptInternalPackageManifests({
|
|
|
31
31
|
internalPackageNames.map(async (packageName) => {
|
|
32
32
|
const { manifest, rootRelativeDir } = got(packagesRegistry, packageName);
|
|
33
33
|
|
|
34
|
-
/** Dev dependencies
|
|
35
|
-
const strippedManifest = omit(manifest, ["
|
|
34
|
+
/** Dev dependencies are never included for internal deps */
|
|
35
|
+
const strippedManifest = omit(manifest, ["devDependencies"]);
|
|
36
36
|
|
|
37
37
|
/** Resolve catalog dependencies before adapting internal deps */
|
|
38
38
|
const manifestWithResolvedCatalogs = {
|
|
@@ -44,11 +44,10 @@ export async function adaptInternalPackageManifests({
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
const outputManifest =
|
|
47
|
-
|
|
48
|
-
!forceNpm
|
|
47
|
+
packageManager.name === "pnpm" && !forceNpm
|
|
49
48
|
? /**
|
|
50
|
-
* For PNPM
|
|
51
|
-
*
|
|
49
|
+
* For PNPM the output itself is a workspace so we can preserve the specifiers
|
|
50
|
+
* with "workspace:*" in the output manifest.
|
|
52
51
|
*/
|
|
53
52
|
manifestWithResolvedCatalogs
|
|
54
53
|
: /** For other package managers we replace the links to internal dependencies */
|
|
@@ -43,7 +43,7 @@ describe("validateManifestMandatoryFields", () => {
|
|
|
43
43
|
|
|
44
44
|
expect(() =>
|
|
45
45
|
validateManifestMandatoryFields(invalidDevManifest, packagePath, false),
|
|
46
|
-
).toThrow(/missing
|
|
46
|
+
).toThrow(/missing the "version" field/);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it("should throw error when version field is missing", () => {
|
|
@@ -54,7 +54,7 @@ describe("validateManifestMandatoryFields", () => {
|
|
|
54
54
|
|
|
55
55
|
expect(() =>
|
|
56
56
|
validateManifestMandatoryFields(invalidManifest, packagePath),
|
|
57
|
-
).toThrow(/missing
|
|
57
|
+
).toThrow(/missing the "version" field/);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
it("should throw error when files field is missing", () => {
|
|
@@ -65,7 +65,7 @@ describe("validateManifestMandatoryFields", () => {
|
|
|
65
65
|
|
|
66
66
|
expect(() =>
|
|
67
67
|
validateManifestMandatoryFields(invalidManifest, packagePath),
|
|
68
|
-
).toThrow(/missing
|
|
68
|
+
).toThrow(/missing the "files" field/);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
it("should throw error when files field is empty array", () => {
|
|
@@ -77,7 +77,7 @@ describe("validateManifestMandatoryFields", () => {
|
|
|
77
77
|
|
|
78
78
|
expect(() =>
|
|
79
79
|
validateManifestMandatoryFields(invalidManifest, packagePath),
|
|
80
|
-
).toThrow(/missing
|
|
80
|
+
).toThrow(/missing the "files" field/);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
it("should throw error when files field is not an array", () => {
|
|
@@ -89,7 +89,7 @@ describe("validateManifestMandatoryFields", () => {
|
|
|
89
89
|
|
|
90
90
|
expect(() =>
|
|
91
91
|
validateManifestMandatoryFields(invalidManifest, packagePath),
|
|
92
|
-
).toThrow(/missing
|
|
92
|
+
).toThrow(/missing the "files" field/);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
it("should throw error when both fields are missing", () => {
|
|
@@ -99,20 +99,29 @@ describe("validateManifestMandatoryFields", () => {
|
|
|
99
99
|
|
|
100
100
|
expect(() =>
|
|
101
101
|
validateManifestMandatoryFields(invalidManifest, packagePath),
|
|
102
|
-
).toThrow(/missing mandatory fields: version, files/);
|
|
102
|
+
).toThrow(/missing mandatory fields in its package\.json: version, files/);
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
it("should include
|
|
105
|
+
it("should include documentation URL in error message", () => {
|
|
106
106
|
const invalidManifest = {
|
|
107
107
|
name: "test-package",
|
|
108
108
|
} as PackageManifest;
|
|
109
109
|
|
|
110
110
|
expect(() =>
|
|
111
111
|
validateManifestMandatoryFields(invalidManifest, packagePath),
|
|
112
|
-
).toThrow(
|
|
112
|
+
).toThrow(
|
|
113
|
+
/See https:\/\/isolate-package\.codecompose\.dev\/getting-started#prerequisites/,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const singleFieldManifest = {
|
|
117
|
+
name: "test-package",
|
|
118
|
+
version: "1.0.0",
|
|
119
|
+
} as PackageManifest;
|
|
113
120
|
|
|
114
121
|
expect(() =>
|
|
115
|
-
validateManifestMandatoryFields(
|
|
116
|
-
).toThrow(
|
|
122
|
+
validateManifestMandatoryFields(singleFieldManifest, packagePath),
|
|
123
|
+
).toThrow(
|
|
124
|
+
/See https:\/\/isolate-package\.codecompose\.dev\/getting-started#define-files-field-in-each-package-manifest/,
|
|
125
|
+
);
|
|
117
126
|
});
|
|
118
127
|
});
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { useLogger } from "../logger";
|
|
2
2
|
import type { PackageManifest } from "../types";
|
|
3
3
|
|
|
4
|
+
/** Maps field names to their documentation URLs */
|
|
5
|
+
const fieldDocUrls: Record<string, string> = {
|
|
6
|
+
version:
|
|
7
|
+
"https://isolate-package.codecompose.dev/getting-started#define-version-field-in-each-package-manifest",
|
|
8
|
+
files:
|
|
9
|
+
"https://isolate-package.codecompose.dev/getting-started#define-files-field-in-each-package-manifest",
|
|
10
|
+
};
|
|
11
|
+
|
|
4
12
|
/**
|
|
5
13
|
* Validate that mandatory fields are present in the package manifest. These
|
|
6
14
|
* fields are required for the isolate process to work properly.
|
|
@@ -38,7 +46,11 @@ export function validateManifestMandatoryFields(
|
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
if (missingFields.length > 0) {
|
|
41
|
-
const
|
|
49
|
+
const field = missingFields[0]!;
|
|
50
|
+
const errorMessage =
|
|
51
|
+
missingFields.length === 1
|
|
52
|
+
? `Package at ${packagePath} is missing the "${field}" field in its package.json. See ${fieldDocUrls[field] ?? "https://isolate-package.codecompose.dev/getting-started#prerequisites"}`
|
|
53
|
+
: `Package at ${packagePath} is missing mandatory fields in its package.json: ${missingFields.join(", ")}. See https://isolate-package.codecompose.dev/getting-started#prerequisites`;
|
|
42
54
|
|
|
43
55
|
log.error(errorMessage);
|
|
44
56
|
throw new Error(errorMessage);
|