mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-31 19:18:19 -05:00
6
.changeset/metal-forks-grow.md
Normal file
6
.changeset/metal-forks-grow.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
`pnpm run` is passed through to `pnpm exec` when it detects a command that is not in the scripts.
|
||||
5
.changeset/ninety-dolphins-begin.md
Normal file
5
.changeset/ninety-dolphins-begin.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/parse-cli-args": minor
|
||||
---
|
||||
|
||||
A new property is returned in the result: fallbackCommandUsed. It is true when an unknown command was used, so the fallback command had to be used instead.
|
||||
@@ -16,6 +16,7 @@ export interface ParsedCliArgs {
|
||||
options: Record<string, any>
|
||||
cmd: string | null
|
||||
unknownOptions: Map<string, string[]>
|
||||
fallbackCommandUsed: boolean
|
||||
workspaceDir?: string
|
||||
}
|
||||
|
||||
@@ -54,16 +55,18 @@ export default async function parseCliArgs (
|
||||
options: {},
|
||||
params: noptExploratoryResults.argv.remain,
|
||||
unknownOptions: new Map(),
|
||||
fallbackCommandUsed: false,
|
||||
}
|
||||
}
|
||||
|
||||
const recursiveCommandUsed = RECURSIVE_CMDS.has(noptExploratoryResults.argv.remain[0])
|
||||
let commandName = getCommandName(noptExploratoryResults.argv.remain)
|
||||
let cmd = commandName ? opts.getCommandLongName(commandName) : null
|
||||
if (commandName && !cmd && opts.fallbackCommand) {
|
||||
cmd = opts.fallbackCommand
|
||||
commandName = opts.fallbackCommand
|
||||
inputArgv.unshift(opts.fallbackCommand)
|
||||
const fallbackCommandUsed = Boolean(commandName && !cmd && opts.fallbackCommand)
|
||||
if (fallbackCommandUsed) {
|
||||
cmd = opts.fallbackCommand!
|
||||
commandName = opts.fallbackCommand!
|
||||
inputArgv.unshift(opts.fallbackCommand!)
|
||||
}
|
||||
const types = {
|
||||
...opts.universalOptionsTypes,
|
||||
@@ -141,6 +144,7 @@ export default async function parseCliArgs (
|
||||
cmd,
|
||||
params,
|
||||
workspaceDir,
|
||||
fallbackCommandUsed,
|
||||
...normalizeOptions(options, knownOptions),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ test('allow any option that starts with "config."', async () => {
|
||||
})
|
||||
|
||||
test('do not incorrectly change "install" command to "add"', async () => {
|
||||
const { cmd } = await parseCliArgs({
|
||||
const { cmd, fallbackCommandUsed } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
getTypesByCommandName: (commandName: string) => {
|
||||
switch (commandName) {
|
||||
@@ -148,6 +148,7 @@ test('do not incorrectly change "install" command to "add"', async () => {
|
||||
},
|
||||
}, ['install', '-C', os.homedir(), '--network-concurrency', '1'])
|
||||
expect(cmd).toBe('install')
|
||||
expect(fallbackCommandUsed).toBeFalsy()
|
||||
})
|
||||
|
||||
test('if a help option is used, set cmd to "help"', async () => {
|
||||
@@ -183,7 +184,7 @@ test('use command-specific shorthands', async () => {
|
||||
})
|
||||
|
||||
test('any unknown command is treated as a script', async () => {
|
||||
const { options, cmd, params } = await parseCliArgs({
|
||||
const { options, cmd, params, fallbackCommandUsed } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
fallbackCommand: 'run',
|
||||
getCommandLongName: () => null,
|
||||
@@ -192,6 +193,7 @@ test('any unknown command is treated as a script', async () => {
|
||||
expect(cmd).toBe('run')
|
||||
expect(params).toStrictEqual(['foo'])
|
||||
expect(options).toHaveProperty(['recursive'])
|
||||
expect(fallbackCommandUsed).toBeTruthy()
|
||||
})
|
||||
|
||||
test("don't use the fallback command if no command is present", async () => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import realpathMissing from 'realpath-missing'
|
||||
import renderHelp from 'render-help'
|
||||
import runRecursive, { RecursiveRunOpts } from './runRecursive'
|
||||
import existsInDir from './existsInDir'
|
||||
import { handler as exec } from './exec'
|
||||
|
||||
export const IF_PRESENT_OPTION = {
|
||||
'if-present': Boolean,
|
||||
@@ -111,13 +112,19 @@ For options that may be used with `-r`, see "pnpm help recursive"',
|
||||
export type RunOpts =
|
||||
& Omit<RecursiveRunOpts, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>
|
||||
& { recursive?: boolean }
|
||||
& Pick<Config, 'dir' | 'engineStrict' | 'reporter' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts'>
|
||||
& Pick<Config, 'dir' | 'engineStrict' | 'extraBinPaths' | 'reporter' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts'>
|
||||
& (
|
||||
& { recursive?: false }
|
||||
& Partial<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
|
||||
| { recursive: true }
|
||||
& Required<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
|
||||
)
|
||||
& {
|
||||
argv?: {
|
||||
original: string[]
|
||||
}
|
||||
fallbackCommandUsed?: boolean
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
opts: RunOpts,
|
||||
@@ -143,6 +150,14 @@ export async function handler (
|
||||
}
|
||||
if (scriptName !== 'start' && !manifest.scripts?.[scriptName]) {
|
||||
if (opts.ifPresent) return
|
||||
if (opts.fallbackCommandUsed) {
|
||||
if (opts.argv == null) throw new Error('Could not fallback because opts.argv.original was not passed to the script runner')
|
||||
await exec({
|
||||
selectedProjectsGraph: {},
|
||||
...opts,
|
||||
}, opts.argv.original.slice(1))
|
||||
return
|
||||
}
|
||||
if (opts.workspaceDir) {
|
||||
const { manifest: rootManifest } = await tryReadProjectManifest(opts.workspaceDir, opts)
|
||||
if (rootManifest?.scripts?.[scriptName]) {
|
||||
|
||||
@@ -54,6 +54,7 @@ export default async function run (inputArgv: string[]) {
|
||||
params: cliParams,
|
||||
options: cliOptions,
|
||||
cmd,
|
||||
fallbackCommandUsed,
|
||||
unknownOptions,
|
||||
workspaceDir,
|
||||
} = parsedCliArgs
|
||||
@@ -62,7 +63,7 @@ export default async function run (inputArgv: string[]) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (unknownOptions.size > 0) {
|
||||
if (unknownOptions.size > 0 && !fallbackCommandUsed) {
|
||||
const unknownOptionsArray = Array.from(unknownOptions.keys())
|
||||
if (unknownOptionsArray.every((option) => DEPRECATED_OPTIONS.has(option))) {
|
||||
let deprecationMsg = `${chalk.bgYellow.black('\u2009WARN\u2009')}`
|
||||
@@ -82,6 +83,7 @@ export default async function run (inputArgv: string[]) {
|
||||
let config: Config & {
|
||||
forceSharedLockfile: boolean
|
||||
argv: { remain: string[], cooked: string[], original: string[] }
|
||||
fallbackCommandUsed: boolean
|
||||
}
|
||||
try {
|
||||
// When we just want to print the location of the global bin directory,
|
||||
@@ -96,6 +98,7 @@ export default async function run (inputArgv: string[]) {
|
||||
}) as typeof config
|
||||
config.forceSharedLockfile = typeof config.workspaceDir === 'string' && config.sharedWorkspaceLockfile === true
|
||||
config.argv = argv
|
||||
config.fallbackCommandUsed = fallbackCommandUsed
|
||||
} catch (err) {
|
||||
// Reporting is not initialized at this point, so just printing the error
|
||||
const hint = err['hint'] ? err['hint'] : `For help, run: pnpm help${cmd ? ` ${cmd}` : ''}`
|
||||
|
||||
@@ -67,10 +67,9 @@ test('pass through to npm with all the args', async () => {
|
||||
test('pnpm fails when an unsupported command is used', async () => {
|
||||
prepare()
|
||||
|
||||
const { status, stdout } = execPnpmSync(['unsupported-command'])
|
||||
const { status } = execPnpmSync(['unsupported-command'])
|
||||
|
||||
expect(status).toBe(1)
|
||||
expect(stdout.toString()).toMatch(/Missing script: unsupported-command/)
|
||||
})
|
||||
|
||||
test('pnpm fails when no command is specified', async () => {
|
||||
@@ -181,3 +180,9 @@ test('use the specified Node.js version for running scripts', async () => {
|
||||
await execPnpm(['run', 'test'])
|
||||
expect(await fs.readFile('version', 'utf8')).toBe('v14.0.0')
|
||||
})
|
||||
|
||||
test('if an unknown command is executed, run it', async () => {
|
||||
prepare({})
|
||||
await execPnpm(['node', '-e', "require('fs').writeFileSync('foo','','utf8')"])
|
||||
expect(await fs.readFile('foo', 'utf8')).toBe('')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user