@pnpm/exec.commands 1001.0.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/lib/exec.js ADDED
@@ -0,0 +1,328 @@
1
+ import path from 'node:path';
2
+ import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help';
3
+ import { docsUrl, readProjectManifestOnly, throwOnCommandFail } from '@pnpm/cli.utils';
4
+ import { getWorkspaceConcurrency, types } from '@pnpm/config.reader';
5
+ import { lifecycleLogger } from '@pnpm/core-loggers';
6
+ import { PnpmError } from '@pnpm/error';
7
+ import { makeNodeRequireOption } from '@pnpm/exec.lifecycle';
8
+ import { logger } from '@pnpm/logger';
9
+ import { prependDirsToPath } from '@pnpm/shell.path';
10
+ import { tryReadProjectManifest } from '@pnpm/workspace.project-manifest-reader';
11
+ import { sortProjects } from '@pnpm/workspace.projects-sorter';
12
+ import { safeExeca as execa } from 'execa';
13
+ import pLimit from 'p-limit';
14
+ import { pick } from 'ramda';
15
+ import { renderHelp } from 'render-help';
16
+ import which from 'which';
17
+ import { writeJsonFile } from 'write-json-file';
18
+ import { getNearestProgram, getNearestScript } from './buildCommandNotFoundHint.js';
19
+ import { existsInDir } from './existsInDir.js';
20
+ import { makeEnv } from './makeEnv.js';
21
+ import { PARALLEL_OPTION_HELP, REPORT_SUMMARY_OPTION_HELP, RESUME_FROM_OPTION_HELP, shorthands as runShorthands, } from './run.js';
22
+ import { runDepsStatusCheck } from './runDepsStatusCheck.js';
23
+ export const shorthands = {
24
+ parallel: runShorthands.parallel,
25
+ c: '--shell-mode',
26
+ };
27
+ export const commandNames = ['exec'];
28
+ export function rcOptionsTypes() {
29
+ return {
30
+ ...pick([
31
+ 'bail',
32
+ 'sort',
33
+ 'unsafe-perm',
34
+ 'workspace-concurrency',
35
+ 'reporter-hide-prefix',
36
+ ], types),
37
+ 'shell-mode': Boolean,
38
+ 'resume-from': String,
39
+ 'report-summary': Boolean,
40
+ };
41
+ }
42
+ export const cliOptionsTypes = () => ({
43
+ ...rcOptionsTypes(),
44
+ recursive: Boolean,
45
+ reverse: Boolean,
46
+ });
47
+ export function help() {
48
+ return renderHelp({
49
+ description: 'Run a shell command in the context of a project.',
50
+ descriptionLists: [
51
+ {
52
+ title: 'Options',
53
+ list: [
54
+ {
55
+ description: 'Do not hide project name prefix from output of recursively running command.',
56
+ name: '--no-reporter-hide-prefix',
57
+ },
58
+ PARALLEL_OPTION_HELP,
59
+ {
60
+ description: 'Run the shell command in every package found in subdirectories \
61
+ or every workspace package, when executed inside a workspace. \
62
+ For options that may be used with `-r`, see "pnpm help recursive"',
63
+ name: '--recursive',
64
+ shortAlias: '-r',
65
+ },
66
+ {
67
+ description: 'If exist, runs file inside of a shell. \
68
+ Uses /bin/sh on UNIX and \\cmd.exe on Windows. \
69
+ The shell should understand the -c switch on UNIX or /d /s /c on Windows.',
70
+ name: '--shell-mode',
71
+ shortAlias: '-c',
72
+ },
73
+ RESUME_FROM_OPTION_HELP,
74
+ REPORT_SUMMARY_OPTION_HELP,
75
+ ...UNIVERSAL_OPTIONS,
76
+ ],
77
+ },
78
+ FILTERING,
79
+ ],
80
+ url: docsUrl('exec'),
81
+ usages: ['pnpm [-r] [-c] exec <command> [args...]'],
82
+ });
83
+ }
84
+ export function getResumedPackageChunks({ resumeFrom, chunks, selectedProjectsGraph, }) {
85
+ const resumeFromPackagePrefix = Object.keys(selectedProjectsGraph)
86
+ .find((prefix) => selectedProjectsGraph[prefix]?.package.manifest.name === resumeFrom);
87
+ if (!resumeFromPackagePrefix) {
88
+ throw new PnpmError('RESUME_FROM_NOT_FOUND', `Cannot find package ${resumeFrom}. Could not determine where to resume from.`);
89
+ }
90
+ const chunkPosition = chunks.findIndex(chunk => chunk.includes(resumeFromPackagePrefix));
91
+ return chunks.slice(chunkPosition);
92
+ }
93
+ export async function writeRecursiveSummary(opts) {
94
+ await writeJsonFile(path.join(opts.dir, 'pnpm-exec-summary.json'), {
95
+ executionStatus: opts.summary,
96
+ });
97
+ }
98
+ export function createEmptyRecursiveSummary(chunks) {
99
+ const acc = {};
100
+ for (const prefix of chunks.flat()) {
101
+ acc[prefix] = { status: 'queued' };
102
+ }
103
+ return acc;
104
+ }
105
+ export function getExecutionDuration(start) {
106
+ const end = process.hrtime(start);
107
+ return (end[0] * 1e9 + end[1]) / 1e6;
108
+ }
109
+ export async function handler(opts, params) {
110
+ // For backward compatibility
111
+ if (params[0] === '--') {
112
+ params.shift();
113
+ }
114
+ if (!params[0]) {
115
+ throw new PnpmError('EXEC_MISSING_COMMAND', '\'pnpm exec\' requires a command to run');
116
+ }
117
+ const limitRun = pLimit(getWorkspaceConcurrency(opts.workspaceConcurrency));
118
+ if (opts.verifyDepsBeforeRun) {
119
+ await runDepsStatusCheck(opts);
120
+ }
121
+ let chunks;
122
+ if (opts.recursive) {
123
+ chunks = opts.sort
124
+ ? sortProjects(opts.selectedProjectsGraph)
125
+ : [Object.keys(opts.selectedProjectsGraph).sort()];
126
+ if (opts.reverse) {
127
+ chunks = chunks.reverse();
128
+ }
129
+ }
130
+ else {
131
+ chunks = [[(opts.cliOptions.dir ?? process.cwd())]];
132
+ const project = await tryReadProjectManifest(opts.dir);
133
+ if (project.manifest != null) {
134
+ opts.selectedProjectsGraph = {
135
+ [opts.dir]: {
136
+ dependencies: [],
137
+ package: {
138
+ ...project,
139
+ rootDir: opts.dir,
140
+ rootDirRealPath: opts.dir,
141
+ },
142
+ },
143
+ };
144
+ }
145
+ }
146
+ if (!opts.selectedProjectsGraph) {
147
+ throw new PnpmError('RECURSIVE_EXEC_NO_PACKAGE', 'No package found in this workspace');
148
+ }
149
+ if (opts.resumeFrom) {
150
+ chunks = getResumedPackageChunks({
151
+ resumeFrom: opts.resumeFrom,
152
+ chunks,
153
+ selectedProjectsGraph: opts.selectedProjectsGraph,
154
+ });
155
+ }
156
+ const result = createEmptyRecursiveSummary(chunks);
157
+ const existsPnp = existsInDir.bind(null, '.pnp.cjs');
158
+ const workspacePnpPath = opts.workspaceDir && existsPnp(opts.workspaceDir);
159
+ let exitCode = 0;
160
+ const prependPaths = [
161
+ './node_modules/.bin',
162
+ ...(opts.extraBinPaths ?? []),
163
+ ];
164
+ const reporterShowPrefix = opts.recursive && opts.reporterHidePrefix === false;
165
+ for (const chunk of chunks) {
166
+ // eslint-disable-next-line no-await-in-loop
167
+ await Promise.all(chunk.map(async (prefix) => limitRun(async () => {
168
+ result[prefix].status = 'running';
169
+ const startTime = process.hrtime();
170
+ try {
171
+ const pnpPath = workspacePnpPath ?? existsPnp(prefix);
172
+ const extraEnv = {
173
+ ...opts.extraEnv,
174
+ ...(pnpPath ? makeNodeRequireOption(pnpPath) : {}),
175
+ };
176
+ const env = makeEnv({
177
+ extraEnv: {
178
+ ...extraEnv,
179
+ PNPM_PACKAGE_NAME: opts.selectedProjectsGraph[prefix]?.package.manifest.name,
180
+ ...(opts.nodeOptions ? { NODE_OPTIONS: opts.nodeOptions } : {}),
181
+ },
182
+ prependPaths,
183
+ userAgent: opts.userAgent,
184
+ });
185
+ const [cmd, ...args] = params;
186
+ if (reporterShowPrefix) {
187
+ const manifest = await readProjectManifestOnly(prefix);
188
+ const child = execa(cmd, args, {
189
+ cwd: prefix,
190
+ env,
191
+ stdio: 'pipe',
192
+ shell: opts.shellMode ?? false,
193
+ });
194
+ const lifecycleOpts = {
195
+ wd: prefix,
196
+ depPath: manifest.name ?? path.relative(opts.dir, prefix),
197
+ stage: '(exec)',
198
+ };
199
+ const logFn = (stdio) => (data) => {
200
+ for (const line of String(data).split('\n')) {
201
+ lifecycleLogger.debug({
202
+ ...lifecycleOpts,
203
+ stdio,
204
+ line,
205
+ });
206
+ }
207
+ };
208
+ child.stdout.on('data', logFn('stdout'));
209
+ child.stderr.on('data', logFn('stderr'));
210
+ await new Promise((resolve) => {
211
+ void child.once('close', exitCode => {
212
+ lifecycleLogger.debug({
213
+ ...lifecycleOpts,
214
+ exitCode: exitCode ?? 1,
215
+ optional: false,
216
+ });
217
+ resolve();
218
+ });
219
+ });
220
+ await child;
221
+ }
222
+ else {
223
+ await execa(cmd, args, {
224
+ cwd: prefix,
225
+ env,
226
+ stdio: 'inherit',
227
+ shell: opts.shellMode ?? false,
228
+ });
229
+ }
230
+ result[prefix].status = 'passed';
231
+ result[prefix].duration = getExecutionDuration(startTime);
232
+ }
233
+ catch (err) { // eslint-disable-line
234
+ if (isErrorCommandNotFound(params[0], err, prependPaths)) {
235
+ err.message = `Command "${params[0]}" not found`;
236
+ err.hint = await createExecCommandNotFoundHint(params[0], {
237
+ implicitlyFellbackFromRun: opts.implicitlyFellbackFromRun ?? false,
238
+ dir: opts.dir,
239
+ workspaceDir: opts.workspaceDir,
240
+ modulesDir: opts.modulesDir ?? 'node_modules',
241
+ });
242
+ }
243
+ else if (!opts.recursive && typeof err.exitCode === 'number') {
244
+ exitCode = err.exitCode;
245
+ return;
246
+ }
247
+ logger.info(err);
248
+ result[prefix] = {
249
+ status: 'failure',
250
+ duration: getExecutionDuration(startTime),
251
+ error: err,
252
+ message: err.message,
253
+ prefix,
254
+ };
255
+ if (!opts.bail) {
256
+ return;
257
+ }
258
+ if (!err['code']?.startsWith('ERR_PNPM_')) {
259
+ err['code'] = 'ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL';
260
+ }
261
+ err['prefix'] = prefix;
262
+ if (opts.reportSummary) {
263
+ await writeRecursiveSummary({
264
+ dir: opts.lockfileDir ?? opts.dir,
265
+ summary: result,
266
+ });
267
+ }
268
+ throw err;
269
+ }
270
+ })));
271
+ }
272
+ if (opts.reportSummary) {
273
+ await writeRecursiveSummary({
274
+ dir: opts.lockfileDir ?? opts.dir,
275
+ summary: result,
276
+ });
277
+ }
278
+ throwOnCommandFail('pnpm recursive exec', result);
279
+ return { exitCode };
280
+ }
281
+ async function createExecCommandNotFoundHint(programName, opts) {
282
+ if (opts.implicitlyFellbackFromRun) {
283
+ let nearestScript;
284
+ try {
285
+ nearestScript = getNearestScript(programName, (await readProjectManifestOnly(opts.dir)).scripts);
286
+ }
287
+ catch { }
288
+ if (nearestScript) {
289
+ return `Did you mean "pnpm ${nearestScript}"?`;
290
+ }
291
+ const nearestProgram = getNearestProgram({
292
+ programName,
293
+ dir: opts.dir,
294
+ workspaceDir: opts.workspaceDir,
295
+ modulesDir: opts.modulesDir,
296
+ });
297
+ if (nearestProgram) {
298
+ return `Did you mean "pnpm ${nearestProgram}"?`;
299
+ }
300
+ return undefined;
301
+ }
302
+ const nearestProgram = getNearestProgram({
303
+ programName,
304
+ dir: opts.dir,
305
+ workspaceDir: opts.workspaceDir,
306
+ modulesDir: opts.modulesDir,
307
+ });
308
+ if (nearestProgram) {
309
+ return `Did you mean "pnpm exec ${nearestProgram}"?`;
310
+ }
311
+ return undefined;
312
+ }
313
+ function isErrorCommandNotFound(command, error, prependPaths) {
314
+ // Mac/Linux
315
+ if (process.platform === 'linux' || process.platform === 'darwin') {
316
+ return error.originalMessage === `spawn ${command} ENOENT`;
317
+ }
318
+ // Windows
319
+ if (process.platform === 'win32') {
320
+ const { value: path } = prependDirsToPath(prependPaths);
321
+ return !which.sync(command, {
322
+ nothrow: true,
323
+ path,
324
+ });
325
+ }
326
+ return false;
327
+ }
328
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ export declare function existsInDir(entityName: string, dir: string): string | undefined;
@@ -0,0 +1,9 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export function existsInDir(entityName, dir) {
4
+ const entityPath = path.join(dir, entityName);
5
+ if (fs.existsSync(entityPath))
6
+ return entityPath;
7
+ return undefined;
8
+ }
9
+ //# sourceMappingURL=existsInDir.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import * as create from './create.js';
2
+ import * as dlx from './dlx.js';
3
+ import * as exec from './exec.js';
4
+ import * as restart from './restart.js';
5
+ import * as run from './run.js';
6
+ export { create, dlx, exec, restart, run };
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import * as create from './create.js';
2
+ import * as dlx from './dlx.js';
3
+ import * as exec from './exec.js';
4
+ import * as restart from './restart.js';
5
+ import * as run from './run.js';
6
+ export { create, dlx, exec, restart, run };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ export interface Env extends NodeJS.ProcessEnv {
2
+ npm_config_user_agent: string;
3
+ PATH?: string;
4
+ Path?: string;
5
+ }
6
+ export declare function makeEnv(opts: {
7
+ extraEnv?: NodeJS.ProcessEnv;
8
+ userAgent?: string;
9
+ prependPaths: string[];
10
+ }): Env;
package/lib/makeEnv.js ADDED
@@ -0,0 +1,20 @@
1
+ import path from 'node:path';
2
+ import { PnpmError } from '@pnpm/error';
3
+ import { prependDirsToPath } from '@pnpm/shell.path';
4
+ export function makeEnv(opts) {
5
+ for (const prependPath of opts.prependPaths) {
6
+ if (prependPath.includes(path.delimiter)) {
7
+ // Unfortunately, there is no way to escape the PATH delimiter,
8
+ // so directories added to the PATH should not contain it.
9
+ throw new PnpmError('BAD_PATH_DIR', `Cannot add ${prependPath} to PATH because it contains the path delimiter character (${path.delimiter})`);
10
+ }
11
+ }
12
+ const pathEnv = prependDirsToPath(opts.prependPaths);
13
+ return {
14
+ ...process.env,
15
+ ...opts.extraEnv,
16
+ npm_config_user_agent: opts.userAgent ?? 'pnpm',
17
+ [pathEnv.name]: pathEnv.value,
18
+ };
19
+ }
20
+ //# sourceMappingURL=makeEnv.js.map
@@ -0,0 +1 @@
1
+ export declare function tryBuildRegExpFromCommand(command: string): RegExp | null;
@@ -0,0 +1,21 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ export function tryBuildRegExpFromCommand(command) {
3
+ // https://github.com/stdlib-js/regexp-regexp/blob/6428051ac9ef7c9d03468b19bdbb1dc6fc2a5509/lib/regexp.js
4
+ const regExpDetectRegExpScriptCommand = /^\/((?:\\\/|[^/])+)\/([dgimuvys]*)$/;
5
+ const match = command.match(regExpDetectRegExpScriptCommand);
6
+ // if the passed script selector is not in the format of RegExp literal like /build:.*/, return null and handle it as a string script command
7
+ if (!match) {
8
+ return null;
9
+ }
10
+ // if the passed RegExp script selector includes flag, report the error because RegExp flag is not useful for script selector and pnpm does not support this.
11
+ if (match[2]) {
12
+ throw new PnpmError('UNSUPPORTED_SCRIPT_COMMAND_FORMAT', 'RegExp flags are not supported in script command selector');
13
+ }
14
+ try {
15
+ return new RegExp(match[1]);
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ //# sourceMappingURL=regexpCommand.js.map
@@ -0,0 +1,6 @@
1
+ import { type RunOpts } from './run.js';
2
+ export declare function rcOptionsTypes(): Record<string, unknown>;
3
+ export declare function cliOptionsTypes(): Record<string, unknown>;
4
+ export declare const commandNames: string[];
5
+ export declare function help(): string;
6
+ export declare function handler(opts: RunOpts, params: string[]): Promise<void>;
package/lib/restart.js ADDED
@@ -0,0 +1,35 @@
1
+ import { types as allTypes } from '@pnpm/config.reader';
2
+ import { pick } from 'ramda';
3
+ import { renderHelp } from 'render-help';
4
+ import { handler as run, IF_PRESENT_OPTION, IF_PRESENT_OPTION_HELP, } from './run.js';
5
+ export function rcOptionsTypes() {
6
+ return {
7
+ ...pick([
8
+ 'npm-path',
9
+ ], allTypes),
10
+ };
11
+ }
12
+ export function cliOptionsTypes() {
13
+ return IF_PRESENT_OPTION;
14
+ }
15
+ export const commandNames = ['restart'];
16
+ export function help() {
17
+ return renderHelp({
18
+ description: 'Restarts a package. Runs a package\'s "stop", "restart", and "start" scripts, and associated pre- and post- scripts.',
19
+ descriptionLists: [
20
+ {
21
+ title: 'Options',
22
+ list: [
23
+ IF_PRESENT_OPTION_HELP,
24
+ ],
25
+ },
26
+ ],
27
+ usages: ['pnpm restart [-- <args>...]'],
28
+ });
29
+ }
30
+ export async function handler(opts, params) {
31
+ await run(opts, ['stop', ...params]);
32
+ await run(opts, ['restart', ...params]);
33
+ await run(opts, ['start', ...params]);
34
+ }
35
+ //# sourceMappingURL=restart.js.map
package/lib/run.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { CompletionFunc } from '@pnpm/cli.command';
2
+ import { type Config } from '@pnpm/config.reader';
3
+ import type { CheckDepsStatusOptions } from '@pnpm/deps.status';
4
+ import { type RunLifecycleHookOptions } from '@pnpm/exec.lifecycle';
5
+ import type { ProjectManifest } from '@pnpm/types';
6
+ import { type RecursiveRunOpts } from './runRecursive.js';
7
+ export declare const IF_PRESENT_OPTION: Record<string, unknown>;
8
+ export interface DescriptionItem {
9
+ shortAlias?: string;
10
+ name: string;
11
+ description?: string;
12
+ }
13
+ export declare const IF_PRESENT_OPTION_HELP: DescriptionItem;
14
+ export declare const PARALLEL_OPTION_HELP: DescriptionItem;
15
+ export declare const RESUME_FROM_OPTION_HELP: DescriptionItem;
16
+ export declare const SEQUENTIAL_OPTION_HELP: DescriptionItem;
17
+ export declare const REPORT_SUMMARY_OPTION_HELP: DescriptionItem;
18
+ export declare const REPORTER_HIDE_PREFIX_HELP: DescriptionItem;
19
+ export declare const shorthands: Record<string, string[]>;
20
+ export declare function rcOptionsTypes(): Record<string, unknown>;
21
+ export declare function cliOptionsTypes(): Record<string, unknown>;
22
+ export declare const completion: CompletionFunc;
23
+ export declare const commandNames: string[];
24
+ export declare function help(): string;
25
+ export type RunOpts = Omit<RecursiveRunOpts, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'> & {
26
+ recursive?: boolean;
27
+ } & Pick<Config, 'bin' | 'cliOptions' | 'verifyDepsBeforeRun' | 'dir' | 'enablePrePostScripts' | 'engineStrict' | 'extraBinPaths' | 'extraEnv' | 'nodeOptions' | 'pnpmHomeDir' | 'reporter' | 'scriptShell' | 'scriptsPrependNodePath' | 'shellEmulator' | 'syncInjectedDepsAfterScripts' | 'userAgent'> & (({
28
+ recursive?: false;
29
+ } & Partial<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>) | ({
30
+ recursive: true;
31
+ } & Required<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>)) & {
32
+ argv?: {
33
+ original: string[];
34
+ };
35
+ fallbackCommandUsed?: boolean;
36
+ } & CheckDepsStatusOptions;
37
+ export declare function handler(opts: RunOpts, params: string[]): Promise<string | {
38
+ exitCode: number;
39
+ } | undefined>;
40
+ export interface RunScriptOptions {
41
+ enablePrePostScripts: boolean;
42
+ syncInjectedDepsAfterScripts: string[] | undefined;
43
+ workspaceDir: string | undefined;
44
+ }
45
+ export declare function runScript(opts: {
46
+ manifest: ProjectManifest;
47
+ lifecycleOpts: RunLifecycleHookOptions;
48
+ runScriptOptions: RunScriptOptions;
49
+ passedThruArgs: string[];
50
+ }, scriptName: string): Promise<void>;