Skip to content

Commit ece0be0

Browse files
authored
fix: only report unknown flags that look like a typo (#1485)
1 parent 14c268e commit ece0be0

2 files changed

Lines changed: 48 additions & 9 deletions

File tree

packages/nuxt-cli/src/main.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,13 @@ const _main = defineCommand({
9595
})
9696

9797
/**
98-
* Report long flags the resolved command does not declare. Unknown flags are
99-
* otherwise parsed and silently ignored, so a misspelling looks like the flag
100-
* simply had no effect.
98+
* Report long flags the resolved command does not declare *and* that look like
99+
* a misspelling of one it does, so a typo does not look like a flag that simply
100+
* had no effect.
101+
*
102+
* A flag with no close match is passed through in silence: projects read their
103+
* own flags out of `process.argv` in `nuxt.config`, and there is no way to tell
104+
* one of those from a typo nothing resembles.
101105
*/
102106
async function warnUnknownFlags(command: string, rawArgs: string[]): Promise<void> {
103107
let def: CommandDef<any>
@@ -120,22 +124,28 @@ async function warnUnknownFlags(command: string, rawArgs: string[]): Promise<voi
120124
return
121125
}
122126

123-
const suggestions = await suggestFlags(unknown)
127+
const suggestions = (await suggestFlags(unknown)).filter((entry): entry is { flag: string, suggestion: string } => {
128+
if (entry.suggestion) {
129+
return true
130+
}
131+
debug(`Passing through unknown option ${entry.flag}.`)
132+
return false
133+
})
134+
if (suggestions.length === 0) {
135+
return
136+
}
137+
124138
const { isInteractive } = await import('./utils/stdout')
125139
if (!isInteractive()) {
126140
for (const { flag, suggestion } of suggestions) {
127-
logger.warn(`Unknown option ${styleText('cyan', flag)}.${suggestion ? ` Did you mean ${styleText('cyan', suggestion)}?` : ''}`)
141+
logger.warn(`Unknown option ${styleText('cyan', flag)}. Did you mean ${styleText('cyan', suggestion)}?`)
128142
}
129143
return
130144
}
131145

132146
const { cancel, confirm, isCancel } = await import('@clack/prompts')
133147
const { restoreRawMode, withDirectStdout } = await import('./utils/console')
134148
for (const { flag, suggestion } of suggestions) {
135-
if (!suggestion) {
136-
logger.warn(`Unknown option ${styleText('cyan', flag)}.`)
137-
continue
138-
}
139149
// A negated unknown flag is matched against its bare name, so the offered
140150
// replacement has to restore the negation the user asked for.
141151
const replacement = flag.startsWith('--no-') && !suggestion.startsWith('--no-')
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import process from 'node:process'
2+
import { fileURLToPath } from 'node:url'
3+
import { x } from 'tinyexec'
4+
import { describe, expect, it } from 'vitest'
5+
6+
const fixtureDir = fileURLToPath(new URL('../fixtures/dev', import.meta.url))
7+
const nuxi = fileURLToPath(new URL('../../bin/nuxi.mjs', import.meta.url))
8+
9+
function run(args: string[]) {
10+
return x('node', [nuxi, ...args], {
11+
nodeOptions: { cwd: fixtureDir, env: { ...process.env, NO_COLOR: '1' } },
12+
})
13+
}
14+
15+
describe('unknown flags', () => {
16+
it('should suggest the declared flag a misspelling is closest to', { timeout: 60_000 }, async () => {
17+
const res = await run(['prepare', '--loglevel=info'])
18+
const output = res.stdout + res.stderr
19+
20+
expect(output).toContain('Unknown option --loglevel. Did you mean --logLevel?')
21+
})
22+
23+
it('should pass through a flag nothing is close to', { timeout: 60_000 }, async () => {
24+
const res = await run(['prepare', '--ui-only'])
25+
const output = res.stdout + res.stderr
26+
27+
expect(output).not.toContain('Unknown option')
28+
})
29+
})

0 commit comments

Comments
 (0)