diff --git a/src/commands/root.ts b/src/commands/root.ts index 8bbfb19..e6df521 100644 --- a/src/commands/root.ts +++ b/src/commands/root.ts @@ -641,6 +641,7 @@ export async function root(argv: string[]) { if (flags.help || _.length === 0) return console.log(rootHelp) const [src, selector] = _ + const isUrl = /^https?:\/\//.test(src!) const opts = { limit: num(flags.limit, 50, { flag: '--limit', kind: 'positive integer', fail }), all: flags.all === true, @@ -722,7 +723,6 @@ export async function root(argv: string[]) { } const emitStructured = (value: unknown[]) => jsonEnvelope ? emitJsonEnvelope(value, opts) : emitJson(value, opts) - const isUrl = /^https?:\/\//.test(src!) const envelopeConflict = flags.md === true ? '--md' @@ -758,6 +758,14 @@ export async function root(argv: string[]) { 'use it with --row, --table, --locate, or a selector; fetch mode already returns JSON' ) } + const fetchOnlyOutputMode = + typeof flags.output === 'string' ? '--output' : flags.body === true ? '--body' : null + if (fetchOnlyOutputMode && (!isUrl || selector !== undefined)) { + fail( + `${fetchOnlyOutputMode} requires fetch mode without a selector`, + 'use a URL without a selector, or choose a parse output mode' + ) + } const headers = isUrl ? requestHeaders(flags) : {} const parseFlags = selector !== undefined || diff --git a/test/cli.test.ts b/test/cli.test.ts index 8c435ae..f905d97 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -590,6 +590,24 @@ test('--where fails outside --row or --table before source I/O', () => { expect(r.err).not.toContain('ENOENT') }) +test('fetch-only output modes fail in parse mode before source I/O', () => { + const cases: [string[], string][] = [ + [['missing.html', '.x', '--body'], '--body'], + [['http://127.0.0.1:1', '.x', '--body'], '--body'], + [['missing.html', '--body'], '--body'], + [['missing.html', '.x', '--output', 'out.bin'], '--output'], + [['missing.html', '.x', '-o', 'out.bin'], '--output'], + ] + + for (const [args, outputMode] of cases) { + const r = ax(args) + expect(r.code).toBe(1) + expect(r.out).toBe('') + expect(r.err).toContain(`ax: error: ${outputMode} requires fetch mode without a selector`) + expect(r.err).not.toContain('ENOENT') + } +}) + test('cap: default limit with stderr note', () => { const r = ax(['many.html', '.x']) expect(r.out.split('\n')).toHaveLength(50)