@pnpm/exec.commands 1100.0.9 → 1100.1.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/lib/create.d.ts CHANGED
@@ -1,6 +1,7 @@
1
+ import type { CommandHandlerMap } from '@pnpm/cli.command';
1
2
  import * as dlx from './dlx.js';
2
3
  export declare const commandNames: string[];
3
- export declare function handler(_opts: dlx.DlxCommandOptions, params: string[]): Promise<{
4
+ export declare function handler(_opts: dlx.DlxCommandOptions, params: string[], commands?: CommandHandlerMap): Promise<{
4
5
  exitCode: number;
5
6
  } | string>;
6
7
  export declare function rcOptionsTypes(): Record<string, unknown>;
package/lib/create.js CHANGED
@@ -3,7 +3,7 @@ import { PnpmError } from '@pnpm/error';
3
3
  import { renderHelp } from 'render-help';
4
4
  import * as dlx from './dlx.js';
5
5
  export const commandNames = ['create'];
6
- export async function handler(_opts, params) {
6
+ export async function handler(_opts, params, commands) {
7
7
  // If the first argument is --help or -h, we show the help message.
8
8
  if (params[0] === '--help' || params[0] === '-h') {
9
9
  return help();
@@ -15,7 +15,7 @@ export async function handler(_opts, params) {
15
15
  'with <name> substituted for a package name.');
16
16
  }
17
17
  const createPackageName = convertToCreateName(packageName);
18
- return dlx.handler(_opts, [createPackageName, ...packageArgs]);
18
+ return dlx.handler(_opts, [createPackageName, ...packageArgs], commands);
19
19
  }
20
20
  export function rcOptionsTypes() {
21
21
  return {};
package/lib/dlx.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CommandHandlerMap } from '@pnpm/cli.command';
1
2
  import { type Config } from '@pnpm/config.reader';
2
3
  import { add } from '@pnpm/installing.commands';
3
4
  import type { PnpmSettings, SupportedArchitectures } from '@pnpm/types';
@@ -12,7 +13,7 @@ export type DlxCommandOptions = {
12
13
  shellMode?: boolean;
13
14
  allowBuild?: string[];
14
15
  } & Pick<Config, 'extraBinPaths' | 'minimumReleaseAgeExclude' | 'registries' | 'reporter' | 'userAgent' | 'cacheDir' | 'dlxCacheMaxAge' | 'symlink'> & Omit<add.AddCommandOptions, 'rootProjectManifestDir'> & PnpmSettings;
15
- export declare function handler(opts: DlxCommandOptions, [command, ...args]: string[]): Promise<{
16
+ export declare function handler(opts: DlxCommandOptions, [command, ...args]: string[], commands?: CommandHandlerMap): Promise<{
16
17
  exitCode: number;
17
18
  output?: string;
18
19
  }>;
package/lib/dlx.js CHANGED
@@ -2,6 +2,7 @@ import fs, {} from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import util from 'node:util';
4
4
  import { getBinsFromPackageManifest } from '@pnpm/bins.resolver';
5
+ import { getAutomaticallyIgnoredBuilds } from '@pnpm/building.commands';
5
6
  import { resolveFromCatalog, } from '@pnpm/catalogs.resolver';
6
7
  import { OUTPUT_OPTIONS } from '@pnpm/cli.common-cli-options-help';
7
8
  import { docsUrl, readProjectManifestOnly } from '@pnpm/cli.utils';
@@ -21,6 +22,13 @@ import { symlinkDir } from 'symlink-dir';
21
22
  import { makeEnv } from './makeEnv.js';
22
23
  export const skipPackageManagerCheck = true;
23
24
  export const commandNames = ['dlx'];
25
+ /**
26
+ * Test-only env var. When set, the dlx ignored-builds recovery path bypasses
27
+ * the TTY check and forwards `all: true` so `approve-builds` skips its
28
+ * multiselect and confirm prompts. Mirrors the same env var honored by
29
+ * `promptApproveGlobalBuilds` for global installs. Not for production use.
30
+ */
31
+ const AUTO_APPROVE_FOR_TESTS_ENV = 'PNPM_AUTO_APPROVE_BUILDS_FOR_TESTS';
24
32
  export const shorthands = {
25
33
  c: '--shell-mode',
26
34
  };
@@ -67,7 +75,7 @@ export function help() {
67
75
  usages: ['pnpm dlx <command> [args...]'],
68
76
  });
69
77
  }
70
- export async function handler(opts, [command, ...args]) {
78
+ export async function handler(opts, [command, ...args], commands) {
71
79
  if (!command && (!opts.package || opts.package.length === 0)) {
72
80
  return { exitCode: 1, output: help() };
73
81
  }
@@ -122,15 +130,22 @@ export async function handler(opts, [command, ...args]) {
122
130
  supportedArchitectures: opts.supportedArchitectures,
123
131
  });
124
132
  if (!cacheExists) {
133
+ const allowBuilds = Object.fromEntries([...resolvedPkgAliases, ...(opts.allowBuild ?? [])].map(pkg => [pkg, true]));
125
134
  try {
126
135
  fs.mkdirSync(cachedDir, { recursive: true });
127
136
  await add.handler({
128
137
  ...opts,
138
+ // Mirror the global install flow: dlx prompts via `approve-builds`
139
+ // when transitive deps have skipped build scripts, so it must not let
140
+ // strictDepBuilds (the v11 default) turn that into a hard error.
141
+ // Without this, `pnpm dlx <pkg>` cannot launch packages whose bin
142
+ // depends on a postinstall step (e.g. native modules).
143
+ strictDepBuilds: false,
129
144
  enableGlobalVirtualStore: opts.enableGlobalVirtualStore ?? true,
130
145
  bin: path.join(cachedDir, 'node_modules/.bin'),
131
146
  dir: cachedDir,
132
147
  lockfileDir: cachedDir,
133
- allowBuilds: Object.fromEntries([...resolvedPkgAliases, ...(opts.allowBuild ?? [])].map(pkg => [pkg, true])),
148
+ allowBuilds,
134
149
  rootProjectManifestDir: cachedDir,
135
150
  saveProd: true, // dlx will be looking for the package in the "dependencies" field!
136
151
  saveDev: false,
@@ -139,6 +154,7 @@ export async function handler(opts, [command, ...args]) {
139
154
  symlink: true,
140
155
  workspaceDir: undefined,
141
156
  }, resolvedPkgs);
157
+ await promptApproveDlxBuilds({ cachedDir, allowBuilds, inheritedOpts: opts }, commands);
142
158
  try {
143
159
  await symlinkDir(cachedDir, cacheLink, { overwrite: true });
144
160
  }
@@ -159,10 +175,15 @@ export async function handler(opts, [command, ...args]) {
159
175
  // directory swaps). If another process completed the cache in the meantime,
160
176
  // use that instead of failing.
161
177
  const completedDir = getValidCacheDir(cacheLink, opts.dlxCacheMaxAge);
162
- if (completedDir == null) {
178
+ if (completedDir != null) {
179
+ cachedDir = completedDir;
180
+ }
181
+ else {
182
+ // Drop the partially-populated cache so a subsequent dlx run starts
183
+ // clean instead of reusing a broken install.
184
+ await fs.promises.rm(cachedDir, { recursive: true, force: true });
163
185
  throw err;
164
186
  }
165
- cachedDir = completedDir;
166
187
  }
167
188
  }
168
189
  const binsDir = path.join(cachedDir, 'node_modules/.bin');
@@ -227,6 +248,47 @@ function scopeless(pkgName) {
227
248
  }
228
249
  return pkgName;
229
250
  }
251
+ /**
252
+ * After a dlx install with `strictDepBuilds: false`, check whether any
253
+ * transitive dependencies had their build scripts skipped and, if so, run
254
+ * the same `approve-builds` flow that `pnpm add -g` uses. Mirrors
255
+ * `promptApproveGlobalBuilds` in @pnpm/global.commands.
256
+ *
257
+ * In non-interactive mode (no TTY, no commands map) this is a no-op and
258
+ * the install is persisted to the dlx cache with builds skipped — same
259
+ * behavior as `pnpm add -g` in CI. Users who need the skipped scripts
260
+ * to run can re-invoke dlx with `--allow-build=<pkg>`, which produces a
261
+ * different cache key and forces a fresh install.
262
+ */
263
+ async function promptApproveDlxBuilds(opts, commands) {
264
+ if (!commands?.['approve-builds'])
265
+ return;
266
+ const autoApproveForTests = process.env[AUTO_APPROVE_FOR_TESTS_ENV] === '1';
267
+ if (!autoApproveForTests && !process.stdin.isTTY)
268
+ return;
269
+ const { automaticallyIgnoredBuilds } = await getAutomaticallyIgnoredBuilds({
270
+ dir: opts.cachedDir,
271
+ lockfileDir: opts.cachedDir,
272
+ });
273
+ if (!automaticallyIgnoredBuilds?.length)
274
+ return;
275
+ await commands['approve-builds']({
276
+ ...opts.inheritedOpts,
277
+ dir: opts.cachedDir,
278
+ lockfileDir: opts.cachedDir,
279
+ rootProjectManifestDir: opts.cachedDir,
280
+ modulesDir: undefined,
281
+ workspaceDir: undefined,
282
+ allProjects: undefined,
283
+ selectedProjectsGraph: undefined,
284
+ workspacePackagePatterns: undefined,
285
+ rootProjectManifest: undefined,
286
+ global: false,
287
+ pending: false,
288
+ allowBuilds: opts.allowBuilds,
289
+ all: autoApproveForTests ? true : undefined,
290
+ }, [], commands);
291
+ }
230
292
  function findCache(opts) {
231
293
  const dlxCommandCacheDir = createDlxCommandCacheDir(opts);
232
294
  const cacheLink = path.join(dlxCommandCacheDir, 'pkg');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/exec.commands",
3
- "version": "1100.0.9",
3
+ "version": "1100.1.0",
4
4
  "description": "Commands for running scripts",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -39,6 +39,7 @@
39
39
  "which": "npm:@pnpm/which@^3.0.1",
40
40
  "write-json-file": "^7.0.0",
41
41
  "@pnpm/bins.resolver": "1100.0.2",
42
+ "@pnpm/building.commands": "1100.0.10",
42
43
  "@pnpm/catalogs.resolver": "1100.0.0",
43
44
  "@pnpm/cli.command": "1100.0.1",
44
45
  "@pnpm/cli.common-cli-options-help": "1100.0.1",
@@ -48,16 +49,16 @@
48
49
  "@pnpm/core-loggers": "1100.0.1",
49
50
  "@pnpm/crypto.hash": "1100.0.1",
50
51
  "@pnpm/deps.status": "1100.0.8",
51
- "@pnpm/error": "1100.0.0",
52
52
  "@pnpm/engine.runtime.commands": "1100.0.9",
53
53
  "@pnpm/exec.lifecycle": "1100.0.5",
54
+ "@pnpm/error": "1100.0.0",
54
55
  "@pnpm/exec.pnpm-cli-runner": "1100.0.0",
55
56
  "@pnpm/installing.client": "1100.0.8",
56
- "@pnpm/installing.commands": "1100.1.7",
57
+ "@pnpm/installing.commands": "1100.1.8",
57
58
  "@pnpm/pkg-manifest.reader": "1100.0.2",
58
59
  "@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
59
- "@pnpm/shell.path": "1100.0.1",
60
60
  "@pnpm/store.path": "1100.0.1",
61
+ "@pnpm/shell.path": "1100.0.1",
61
62
  "@pnpm/types": "1101.0.0",
62
63
  "@pnpm/workspace.injected-deps-syncer": "1100.0.6",
63
64
  "@pnpm/workspace.project-manifest-reader": "1100.0.3",
@@ -74,13 +75,13 @@
74
75
  "@types/which": "^3.0.4",
75
76
  "is-windows": "^1.0.2",
76
77
  "write-yaml-file": "^6.0.0",
78
+ "@pnpm/exec.commands": "1100.1.0",
77
79
  "@pnpm/engine.runtime.system-node-version": "1100.0.2",
78
- "@pnpm/exec.commands": "1100.0.9",
79
- "@pnpm/logger": "1100.0.0",
80
80
  "@pnpm/prepare": "1100.0.4",
81
81
  "@pnpm/test-ipc-server": "1100.0.0",
82
82
  "@pnpm/testing.command-defaults": "1100.0.1",
83
- "@pnpm/workspace.projects-filter": "1100.0.7"
83
+ "@pnpm/workspace.projects-filter": "1100.0.7",
84
+ "@pnpm/logger": "1100.0.0"
84
85
  },
85
86
  "engines": {
86
87
  "node": ">=22.13"