isolate-package 1.33.0-0 → 1.34.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.
Files changed (67) hide show
  1. package/dist/index.mjs +1 -1
  2. package/dist/index.mjs.map +1 -1
  3. package/dist/{isolate-DTwgcMAN.mjs → isolate-DI3eUTci.mjs} +576 -242
  4. package/dist/isolate-DI3eUTci.mjs.map +1 -0
  5. package/dist/isolate-bin.mjs +5 -6
  6. package/dist/isolate-bin.mjs.map +1 -1
  7. package/package.json +23 -19
  8. package/src/get-internal-package-names.test.ts +1 -1
  9. package/src/get-internal-package-names.ts +2 -2
  10. package/src/isolate-bin.ts +5 -5
  11. package/src/isolate.ts +20 -17
  12. package/src/lib/config.test.ts +1 -1
  13. package/src/lib/config.ts +3 -3
  14. package/src/lib/lockfile/helpers/bun-lockfile.ts +21 -11
  15. package/src/lib/lockfile/helpers/generate-bun-lockfile.test.ts +3 -3
  16. package/src/lib/lockfile/helpers/generate-bun-lockfile.ts +7 -7
  17. package/src/lib/lockfile/helpers/generate-npm-lockfile.integration.test.ts +1 -5
  18. package/src/lib/lockfile/helpers/generate-npm-lockfile.test.ts +311 -16
  19. package/src/lib/lockfile/helpers/generate-npm-lockfile.ts +193 -22
  20. package/src/lib/lockfile/helpers/generate-pnpm-lockfile.test.ts +2 -2
  21. package/src/lib/lockfile/helpers/generate-pnpm-lockfile.ts +6 -6
  22. package/src/lib/lockfile/helpers/generate-yarn-lockfile.ts +5 -5
  23. package/src/lib/lockfile/process-lockfile.test.ts +2 -2
  24. package/src/lib/manifest/helpers/adapt-internal-package-manifests.test.ts +3 -3
  25. package/src/lib/manifest/helpers/adapt-internal-package-manifests.ts +2 -2
  26. package/src/lib/manifest/helpers/adapt-manifest-internal-deps.ts +1 -1
  27. package/src/lib/manifest/helpers/adopt-pnpm-fields-from-root.test.ts +4 -4
  28. package/src/lib/manifest/helpers/adopt-pnpm-fields-from-root.ts +7 -7
  29. package/src/lib/manifest/helpers/resolve-catalog-dependencies.test.ts +410 -0
  30. package/src/lib/manifest/helpers/resolve-catalog-dependencies.ts +115 -27
  31. package/src/lib/manifest/io.ts +6 -2
  32. package/src/lib/manifest/validate-manifest.ts +2 -2
  33. package/src/lib/output/get-build-output-dir.ts +1 -1
  34. package/src/lib/output/pack-dependencies.ts +1 -1
  35. package/src/lib/output/process-build-output-files.ts +6 -17
  36. package/src/lib/package-manager/helpers/infer-from-files.ts +5 -5
  37. package/src/lib/package-manager/helpers/infer-from-manifest.ts +7 -8
  38. package/src/lib/package-manager/index.ts +1 -1
  39. package/src/lib/package-manager/names.ts +8 -10
  40. package/src/lib/patches/collect-installed-names-bun.test.ts +2 -2
  41. package/src/lib/patches/collect-installed-names-bun.ts +8 -8
  42. package/src/lib/patches/collect-installed-names-pnpm.test.ts +1 -1
  43. package/src/lib/patches/collect-installed-names-pnpm.ts +13 -12
  44. package/src/lib/patches/copy-patches.test.ts +5 -13
  45. package/src/lib/patches/copy-patches.ts +9 -9
  46. package/src/lib/patches/write-isolate-pnpm-workspace.test.ts +83 -3
  47. package/src/lib/patches/write-isolate-pnpm-workspace.ts +4 -4
  48. package/src/lib/registry/collect-reachable-package-names.test.ts +1 -1
  49. package/src/lib/registry/create-packages-registry.ts +34 -31
  50. package/src/lib/registry/helpers/find-packages-globs.ts +23 -19
  51. package/src/lib/registry/list-internal-packages.test.ts +2 -2
  52. package/src/lib/types.ts +2 -2
  53. package/src/lib/utils/filter-patched-dependencies.test.ts +1 -1
  54. package/src/lib/utils/filter-patched-dependencies.ts +2 -2
  55. package/src/lib/utils/get-dirname.ts +1 -1
  56. package/src/lib/utils/index.ts +1 -1
  57. package/src/lib/utils/json.ts +12 -14
  58. package/src/lib/utils/pack.ts +32 -22
  59. package/src/lib/utils/reset-isolate-dir.test.ts +165 -0
  60. package/src/lib/utils/reset-isolate-dir.ts +147 -0
  61. package/src/lib/utils/unpack.test.ts +76 -0
  62. package/src/lib/utils/unpack.ts +16 -10
  63. package/src/lib/utils/wait-for-complete-file.test.ts +105 -0
  64. package/src/lib/utils/wait-for-complete-file.ts +44 -0
  65. package/src/lib/utils/yaml.ts +8 -9
  66. package/src/testing/setup.ts +1 -1
  67. package/dist/isolate-DTwgcMAN.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isolate-package",
3
- "version": "1.33.0-0",
3
+ "version": "1.34.0",
4
4
  "description": "Isolate monorepo packages to form a self-contained deployable unit",
5
5
  "keywords": [
6
6
  "ci",
@@ -23,7 +23,8 @@
23
23
  "url": "git+https://github.com/0x80/isolate-package.git"
24
24
  },
25
25
  "bin": {
26
- "isolate": "dist/isolate-bin.mjs"
26
+ "isolate": "./dist/isolate-bin.mjs",
27
+ "isolate-package": "./dist/isolate-bin.mjs"
27
28
  },
28
29
  "files": [
29
30
  "src",
@@ -31,6 +32,9 @@
31
32
  ],
32
33
  "type": "module",
33
34
  "types": "./dist/index.d.mts",
35
+ "imports": {
36
+ "#/*": "./src/*"
37
+ },
34
38
  "exports": {
35
39
  ".": "./dist/index.mjs",
36
40
  "./isolate-bin": "./dist/isolate-bin.mjs",
@@ -41,7 +45,7 @@
41
45
  "dev": "tsdown --watch",
42
46
  "test": "vitest run",
43
47
  "format": "oxfmt",
44
- "lint": "oxlint -c .oxlintrc.json --type-aware",
48
+ "lint": "oxlint -c .oxlintrc.json --import-plugin --type-aware",
45
49
  "check-format": "oxfmt --check",
46
50
  "check-types": "tsc --noEmit",
47
51
  "prepare": "(husky || true) && pnpm check-types && pnpm build",
@@ -52,7 +56,7 @@
52
56
  "dependencies": {
53
57
  "@npmcli/arborist": "^9.4.2",
54
58
  "@npmcli/config": "^10.8.1",
55
- "@pnpm/logger": "^1001.0.1",
59
+ "@pnpm/logger": "^1100.0.0",
56
60
  "@pnpm/types": "^1001.3.0",
57
61
  "consola": "^3.4.2",
58
62
  "detect-monorepo": "^1.0.0",
@@ -63,41 +67,41 @@
63
67
  "meow": "^14.1.0",
64
68
  "outdent": "^0.8.0",
65
69
  "pnpm_lockfile_file_v8": "npm:@pnpm/lockfile-file@8",
66
- "pnpm_lockfile_file_v9": "npm:@pnpm/lockfile-file@9",
70
+ "pnpm_lockfile_file_v9": "npm:@pnpm/lockfile-file@9.0.0",
67
71
  "pnpm_prune_lockfile_v8": "npm:@pnpm/prune-lockfile@5",
68
- "pnpm_prune_lockfile_v9": "npm:@pnpm/prune-lockfile@6",
72
+ "pnpm_prune_lockfile_v9": "npm:@pnpm/prune-lockfile@6.0.0",
69
73
  "remeda": "^2.33.7",
70
74
  "source-map-support": "^0.5.21",
71
75
  "strip-json-comments": "^5.0.3",
72
76
  "tar-fs": "^3.1.2",
73
- "yaml": "^2.8.3"
77
+ "yaml": "^2.9.0"
74
78
  },
75
79
  "devDependencies": {
76
80
  "@codecompose/typescript-config": "^3.2.0",
77
81
  "@types/fs-extra": "^11.0.4",
78
- "@types/node": "^25.5.2",
82
+ "@types/node": "^25.7.0",
79
83
  "@types/npmcli__config": "^6.0.3",
80
84
  "@types/source-map-support": "^0.5.10",
81
85
  "@types/tar-fs": "^2.0.4",
82
86
  "husky": "^9.1.7",
83
- "lint-staged": "^16.4.0",
84
- "oxfmt": "^0.44.0",
85
- "oxlint": "^1.59.0",
86
- "oxlint-tsgolint": "^0.20.0",
87
- "tsdown": "0.21.7",
88
- "typescript": "6.0.2",
89
- "vite": "^7.0.0",
87
+ "lint-staged": "^17.0.4",
88
+ "oxfmt": "^0.49.0",
89
+ "oxlint": "^1.64.0",
90
+ "oxlint-tsgolint": "^0.22.1",
91
+ "tsdown": "0.22.0",
92
+ "typescript": "6.0.3",
93
+ "vite": "^8.0.12",
90
94
  "vitepress": "^1.6.3",
91
- "vitest": "^4.1.3"
95
+ "vitest": "^4.1.6"
92
96
  },
93
97
  "lint-staged": {
94
98
  "*.{ts,tsx,js,mjs,cjs}": [
95
99
  "oxfmt",
96
- "oxlint -c .oxlintrc.json --type-aware"
100
+ "oxlint -c .oxlintrc.json --import-plugin --type-aware"
97
101
  ]
98
102
  },
99
103
  "engines": {
100
- "node": ">=22.12.0"
104
+ "node": ">=22.22.1"
101
105
  },
102
- "packageManager": "pnpm@9.0.0+sha256.bdfc9a7b372b5c462176993e586492603e20da5864d2f8881edc2462482c76fa"
106
+ "packageManager": "pnpm@11.1.3+sha512.c85357fe17ca12dd23dd7071822666dfd7e3cb76fe214e3370b5ea2fb34f2a231185509b63e717f3cd0acb38dd3f8d82bcd5e8172400ae678b70ea4fbed0896d"
103
107
  }
@@ -14,7 +14,7 @@ const mockDetectPackageManager = vi.fn(
14
14
  (_workspaceRootDir: string) => packageManagerResult,
15
15
  );
16
16
 
17
- vi.mock("~/lib/package-manager", () => ({
17
+ vi.mock("#/lib/package-manager", () => ({
18
18
  usePackageManager: () => packageManagerResult,
19
19
  detectPackageManager: (workspaceRootDir: string) =>
20
20
  mockDetectPackageManager(workspaceRootDir),
@@ -23,9 +23,9 @@ export async function getInternalPackageNames(
23
23
 
24
24
  detectPackageManager(workspaceRootDir);
25
25
 
26
- const targetPackageManifest = await readTypedJson<PackageManifest>(
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,
@@ -97,11 +97,11 @@ async function run() {
97
97
  await isolate(mergedConfig);
98
98
  }
99
99
 
100
- run().catch((err) => {
101
- if (err instanceof Error) {
102
- console.error(err.stack);
103
- process.exit(1);
100
+ run().catch((error) => {
101
+ if (error instanceof Error) {
102
+ console.error(error.stack);
104
103
  } else {
105
- console.error(err);
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(config?: IsolateConfig) {
40
- const resolvedConfig = resolveConfig(config);
40
+ export function createIsolator(initialConfig?: IsolateConfig) {
41
+ const resolvedConfig = resolveConfig(initialConfig);
41
42
 
42
- return async function isolate(): Promise<string> {
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<PackageManifest>(
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 = await getBuildOutputDir({
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
- if (fs.existsSync(isolateDir)) {
81
- await fs.remove(isolateDir);
82
- log.debug("Cleaned the existing isolate output directory");
83
- }
84
-
85
- await fs.ensureDir(isolateDir);
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<PackageManifest>(
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(
@@ -278,9 +283,7 @@ export function createIsolator(config?: IsolateConfig) {
278
283
  if (packageManager.name === "bun") {
279
284
  manifest.patchedDependencies = patchEntries;
280
285
  } else {
281
- if (!manifest.pnpm) {
282
- manifest.pnpm = {};
283
- }
286
+ manifest.pnpm ??= {};
284
287
  manifest.pnpm.patchedDependencies = patchEntries;
285
288
  }
286
289
 
@@ -12,7 +12,7 @@ const mockLogger = {
12
12
  error: vi.fn(),
13
13
  };
14
14
 
15
- vi.mock("~/lib/logger", () => ({
15
+ vi.mock("#/lib/logger", () => ({
16
16
  useLogger: () => mockLogger,
17
17
  }));
18
18
 
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<IsolateConfig>(jsonConfigPath);
154
+ return readTypedJsonSync(jsonConfigPath) as IsolateConfig;
155
155
  }
156
156
 
157
157
  return {};
@@ -109,9 +109,8 @@ export function collectRequiredPackages(
109
109
  const required = new Set<string>();
110
110
  const queue = [...directDependencyNames];
111
111
 
112
- while (queue.length > 0) {
113
- const name = queue.pop()!;
114
-
112
+ let name: string | undefined;
113
+ while ((name = queue.pop()) !== undefined) {
115
114
  if (required.has(name)) continue;
116
115
 
117
116
  const entry = packages[name];
@@ -128,16 +127,27 @@ export function collectRequiredPackages(
128
127
  "optionalDependencies",
129
128
  "peerDependencies",
130
129
  ]) {
131
- const deps = info[depField];
132
- if (deps && typeof deps === "object") {
133
- for (const depName of Object.keys(deps as Record<string, unknown>)) {
134
- if (!required.has(depName)) {
135
- queue.push(depName);
136
- }
137
- }
138
- }
130
+ enqueueDeps(info[depField], required, queue);
139
131
  }
140
132
  }
141
133
 
142
134
  return required;
143
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("~/lib/utils", async (importOriginal) => {
17
- const actual = await importOriginal<typeof import("~/lib/utils")>();
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("~/lib/utils"));
26
+ const { readTypedJsonSync } = vi.mocked(await import("#/lib/utils"));
27
27
 
28
28
  /** Reusable packages registry fixture */
29
29
  function createPackagesRegistry() {
@@ -1,13 +1,13 @@
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 "~/lib/logger";
5
- import type { PackagesRegistry } from "~/lib/types";
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 "~/lib/utils";
10
+ } from "#/lib/utils";
11
11
  import {
12
12
  type BunLockfile,
13
13
  type BunWorkspaceEntry,
@@ -68,7 +68,7 @@ export async function generateBunLockfile({
68
68
  throw new Error(`Failed to find bun.lock at ${lockfilePath}`);
69
69
  }
70
70
 
71
- const lockfile = readTypedJsonSync<BunLockfile>(lockfilePath);
71
+ const lockfile = readTypedJsonSync(lockfilePath) as BunLockfile;
72
72
 
73
73
  /** Compute workspace keys for the target and internal deps */
74
74
  const targetWorkspaceKey = path
@@ -215,8 +215,8 @@ export async function generateBunLockfile({
215
215
  );
216
216
 
217
217
  log.debug("Created lockfile at", outputPath);
218
- } catch (err) {
219
- log.error(`Failed to generate lockfile: ${getErrorMessage(err)}`);
220
- throw err;
218
+ } catch (error) {
219
+ log.error(`Failed to generate lockfile: ${getErrorMessage(error)}`);
220
+ throw error;
221
221
  }
222
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 __filename = fileURLToPath(import.meta.url);
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