@pnpm/exec.lifecycle 1100.1.14 → 1100.1.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @pnpm/lifecycle
2
2
 
3
+ ## 1100.1.15
4
+
5
+ ### Patch Changes
6
+
7
+ - Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group.
8
+
9
+ - Fixed recursive `run` cleanup on Windows when a lifecycle script fails while another script's process tree is still running.
10
+
11
+ - Updated dependencies:
12
+ - @pnpm/bins.linker@1100.0.29
13
+ - @pnpm/core-loggers@1100.3.4
14
+ - @pnpm/fetching.directory-fetcher@1100.0.31
15
+ - @pnpm/pkg-manifest.reader@1100.0.18
16
+ - @pnpm/store.cafs-types@1100.1.0
17
+ - @pnpm/store.controller-types@1101.2.0
18
+ - @pnpm/types@1102.1.0
19
+
3
20
  ## 1100.1.14
4
21
 
5
22
  ### Patch Changes
@@ -7,6 +7,7 @@ import { lifecycle } from '@pnpm/npm-lifecycle';
7
7
  import chalk from 'chalk';
8
8
  import isWindows from 'is-windows';
9
9
  import { join as shellQuote } from 'shlex';
10
+ import { trackChildProcess } from './trackChildProcess.js';
10
11
  function noop() { } // eslint-disable-line:no-empty
11
12
  export async function runLifecycleHook(stage, manifest, opts) {
12
13
  const optional = opts.optional === true;
@@ -108,6 +109,7 @@ Please unset the scriptShell option, or configure it to a .exe instead.
108
109
  globalWarn(msg.join(' '));
109
110
  },
110
111
  },
112
+ onSpawn: trackChildProcess,
111
113
  runConcurrently: true,
112
114
  scriptsPrependNodePath: opts.scriptsPrependNodePath,
113
115
  scriptShell: opts.scriptShell,
@@ -15,4 +15,10 @@ export interface Importer {
15
15
  stages?: string[];
16
16
  targetDirs?: string[];
17
17
  }
18
- export declare function runLifecycleHooksConcurrently(stages: string[], importers: Importer[], childConcurrency: number, opts: RunLifecycleHooksConcurrentlyOptions): Promise<void>;
18
+ export declare function runLifecycleHooksConcurrently(params: {
19
+ childConcurrency: number;
20
+ importers: Importer[];
21
+ opts: RunLifecycleHooksConcurrentlyOptions;
22
+ projectDependencies?: Map<ProjectRootDir, ProjectRootDir[]>;
23
+ stages: string[];
24
+ }): Promise<void>;
@@ -2,66 +2,93 @@ import path from 'node:path';
2
2
  import { linkBins } from '@pnpm/bins.linker';
3
3
  import { fetchFromDir } from '@pnpm/fetching.directory-fetcher';
4
4
  import { logger } from '@pnpm/logger';
5
- import { runGroups } from 'run-groups';
5
+ import { scheduleGraph } from '@pnpm/workspace.task-scheduler';
6
6
  import { runLifecycleHook } from './runLifecycleHook.js';
7
- export async function runLifecycleHooksConcurrently(stages, importers, childConcurrency, opts) {
8
- const importersByBuildIndex = new Map();
9
- for (const importer of importers) {
10
- if (!importersByBuildIndex.has(importer.buildIndex)) {
11
- importersByBuildIndex.set(importer.buildIndex, [importer]);
12
- }
13
- else {
14
- importersByBuildIndex.get(importer.buildIndex).push(importer);
15
- }
16
- }
17
- const sortedBuildIndexes = Array.from(importersByBuildIndex.keys()).sort((a, b) => a - b);
18
- const groups = sortedBuildIndexes.map((buildIndex) => {
19
- const importers = importersByBuildIndex.get(buildIndex);
20
- return importers.map(({ manifest, modulesDir, rootDir, stages: importerStages, targetDirs }) => async () => {
21
- // We are linking the bin files, in case they were created by lifecycle scripts of other workspace packages.
22
- await linkBins(modulesDir, path.join(modulesDir, '.bin'), {
23
- extraNodePaths: opts.extraNodePaths,
24
- allowExoticManifests: true,
25
- preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
26
- projectManifest: manifest,
27
- warn: (message) => {
28
- logger.warn({ message, prefix: rootDir });
29
- },
30
- });
31
- const runLifecycleHookOpts = {
32
- ...opts,
33
- depPath: rootDir,
34
- pkgRoot: rootDir,
35
- rootModulesDir: modulesDir,
36
- };
37
- let isBuilt = false;
38
- for (const stage of (importerStages ?? stages)) {
39
- if (await runLifecycleHook(stage, manifest, runLifecycleHookOpts)) { // eslint-disable-line no-await-in-loop
40
- isBuilt = true;
7
+ export async function runLifecycleHooksConcurrently(params) {
8
+ const { childConcurrency, importers, opts, projectDependencies, stages } = params;
9
+ const importersByRootDir = new Map(importers.map((importer) => [importer.rootDir, importer]));
10
+ const dependencies = projectDependencies == null
11
+ ? dependenciesFromBuildIndexes(importers)
12
+ : new Map(importers.map(({ rootDir }) => [
13
+ rootDir,
14
+ (projectDependencies.get(rootDir) ?? []).filter((dependency) => importersByRootDir.has(dependency)),
15
+ ]));
16
+ let firstError;
17
+ await scheduleGraph(dependencies, {
18
+ bail: true,
19
+ concurrency: childConcurrency,
20
+ runNode: async (rootDir) => {
21
+ const { manifest, modulesDir, stages: importerStages, targetDirs } = importersByRootDir.get(rootDir);
22
+ try {
23
+ // We are linking the bin files, in case they were created by lifecycle scripts of other workspace packages.
24
+ await linkBins(modulesDir, path.join(modulesDir, '.bin'), {
25
+ extraNodePaths: opts.extraNodePaths,
26
+ allowExoticManifests: true,
27
+ preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
28
+ projectManifest: manifest,
29
+ warn: (message) => {
30
+ logger.warn({ message, prefix: rootDir });
31
+ },
32
+ });
33
+ const runLifecycleHookOpts = {
34
+ ...opts,
35
+ depPath: rootDir,
36
+ pkgRoot: rootDir,
37
+ rootModulesDir: modulesDir,
38
+ };
39
+ let isBuilt = false;
40
+ for (const stage of (importerStages ?? stages)) {
41
+ if (await runLifecycleHook(stage, manifest, runLifecycleHookOpts)) { // eslint-disable-line no-await-in-loop
42
+ isBuilt = true;
43
+ }
41
44
  }
45
+ if (targetDirs == null || targetDirs.length === 0 || !isBuilt)
46
+ return 'passed';
47
+ // Re-import only the freshly-built source — fetchFromDir already
48
+ // excludes the source's node_modules/. `keepModulesDir: true` makes
49
+ // importIndexedDir skip the destructive makeEmptyDir fast path
50
+ // (#11088) and preserve the target's existing node_modules (bin
51
+ // symlinks + transitive deps from the initial install) via its
52
+ // staging/move path. Replaces the old scanDir-into-filesMap
53
+ // workaround (#4299) that the fast path then wiped, causing ENOENT
54
+ // on .bin/<tool>. Stays on storeController.importPackage so source
55
+ // files keep their hardlinks (no copy-loop).
56
+ const filesResponse = await fetchFromDir(rootDir, { resolveSymlinks: opts.resolveSymlinksInInjectedDirs });
57
+ await Promise.all(targetDirs.map(async (targetDir) => opts.storeController.importPackage(targetDir, {
58
+ filesResponse: {
59
+ resolvedFrom: 'local-dir',
60
+ ...filesResponse,
61
+ },
62
+ force: false,
63
+ keepModulesDir: true,
64
+ })));
65
+ return 'passed';
66
+ }
67
+ catch (error) {
68
+ firstError ??= error;
69
+ return 'aborted';
42
70
  }
43
- if (targetDirs == null || targetDirs.length === 0 || !isBuilt)
44
- return;
45
- // Re-import only the freshly-built source — fetchFromDir already
46
- // excludes the source's node_modules/. `keepModulesDir: true` makes
47
- // importIndexedDir skip the destructive makeEmptyDir fast path
48
- // (#11088) and preserve the target's existing node_modules (bin
49
- // symlinks + transitive deps from the initial install) via its
50
- // staging/move path. Replaces the old scanDir-into-filesMap
51
- // workaround (#4299) that the fast path then wiped, causing ENOENT
52
- // on .bin/<tool>. Stays on storeController.importPackage so source
53
- // files keep their hardlinks (no copy-loop).
54
- const filesResponse = await fetchFromDir(rootDir, { resolveSymlinks: opts.resolveSymlinksInInjectedDirs });
55
- await Promise.all(targetDirs.map(async (targetDir) => opts.storeController.importPackage(targetDir, {
56
- filesResponse: {
57
- resolvedFrom: 'local-dir',
58
- ...filesResponse,
59
- },
60
- force: false,
61
- keepModulesDir: true,
62
- })));
63
- });
71
+ },
72
+ onNodeSkipped: () => { },
64
73
  });
65
- await runGroups(childConcurrency, groups);
74
+ if (firstError != null)
75
+ throw firstError;
76
+ }
77
+ function dependenciesFromBuildIndexes(importers) {
78
+ const groups = new Map();
79
+ for (const { buildIndex, rootDir } of importers) {
80
+ const group = groups.get(buildIndex) ?? [];
81
+ group.push(rootDir);
82
+ groups.set(buildIndex, group);
83
+ }
84
+ const dependencies = new Map();
85
+ let previous = [];
86
+ for (const buildIndex of [...groups.keys()].sort((left, right) => left - right)) {
87
+ const group = groups.get(buildIndex);
88
+ for (const rootDir of group)
89
+ dependencies.set(rootDir, previous);
90
+ previous = group;
91
+ }
92
+ return dependencies;
66
93
  }
67
94
  //# sourceMappingURL=runLifecycleHooksConcurrently.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/exec.lifecycle",
3
- "version": "1100.1.14",
3
+ "version": "1100.1.15",
4
4
  "description": "Package lifecycle hook runner",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -29,19 +29,19 @@
29
29
  "!*.map"
30
30
  ],
31
31
  "dependencies": {
32
- "@pnpm/bins.linker": "1100.0.28",
33
- "@pnpm/core-loggers": "1100.3.3",
32
+ "@pnpm/bins.linker": "1100.0.29",
33
+ "@pnpm/core-loggers": "1100.3.4",
34
34
  "@pnpm/error": "1100.1.3",
35
- "@pnpm/fetching.directory-fetcher": "1100.0.30",
36
- "@pnpm/npm-lifecycle": "^1100.0.0",
37
- "@pnpm/pkg-manifest.reader": "1100.0.17",
38
- "@pnpm/store.cafs-types": "1100.0.2",
39
- "@pnpm/store.controller-types": "1101.1.2",
40
- "@pnpm/types": "1102.0.0",
35
+ "@pnpm/fetching.directory-fetcher": "1100.0.31",
36
+ "@pnpm/npm-lifecycle": "^1100.1.0",
37
+ "@pnpm/pkg-manifest.reader": "1100.0.18",
38
+ "@pnpm/store.cafs-types": "1100.1.0",
39
+ "@pnpm/store.controller-types": "1101.2.0",
40
+ "@pnpm/types": "1102.1.0",
41
+ "@pnpm/workspace.task-scheduler": "1100.0.0",
41
42
  "chalk": "^6.0.0",
42
43
  "is-windows": "^1.0.2",
43
44
  "path-exists": "^5.0.0",
44
- "run-groups": "^5.0.0",
45
45
  "shlex": "^3.0.0"
46
46
  },
47
47
  "peerDependencies": {
@@ -49,9 +49,9 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@jest/globals": "30.4.1",
52
- "@pnpm/exec.lifecycle": "1100.1.14",
52
+ "@pnpm/exec.lifecycle": "1100.1.15",
53
53
  "@pnpm/logger": "1100.0.0",
54
- "@pnpm/prepare": "1100.0.27",
54
+ "@pnpm/prepare": "1100.0.28",
55
55
  "@pnpm/test-fixtures": "1100.0.1",
56
56
  "@pnpm/test-ipc-server": "1100.0.0",
57
57
  "@types/is-windows": "^1.0.2",