Skip to content

Commit f693d33

Browse files
committed
feat(dev): support opening a custom path or url
1 parent b6ee3b9 commit f693d33

3 files changed

Lines changed: 37 additions & 2 deletions

File tree

packages/nuxt-cli/src/commands/dev.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ const command = defineCommand({
6565
alias: ['o'],
6666
default: false,
6767
},
68+
'open.url': {
69+
type: 'string',
70+
description: 'Path or URL to open instead of the dev server root',
71+
},
6872
'clipboard': {
6973
type: 'boolean',
7074
description: 'Copy the URL to the clipboard',
@@ -298,7 +302,8 @@ function resolveListenOverrides(args: ParsedArgs<ArgsT>): DevListenOverrides {
298302
|| undefined,
299303
strictPort: args.strictPort,
300304
hostname: host === true || host === '' ? '' : host || undefined,
301-
open: args.open,
305+
open: args.open || !!args['open.url'],
306+
openURL: args['open.url'] || undefined,
302307
clipboard: args.clipboard,
303308
qr: args.qr,
304309
tunnel: args.tunnel,

packages/nuxt-cli/src/dev/listen.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export interface ListenOptions {
2525
baseURL?: string
2626
showURL?: boolean
2727
open?: boolean
28+
/** Path (resolved against the dev server URL) or absolute URL to open. */
29+
openURL?: string
2830
clipboard?: boolean
2931
qr?: boolean
3032
tunnel?: boolean
@@ -202,7 +204,7 @@ export async function listen(handler: RequestListener, options: ListenOptions =
202204
}
203205

204206
if (options.open) {
205-
openBrowser(url)
207+
openBrowser(options.openURL ? resolveOpenURL(options.openURL, url) : url)
206208
}
207209

208210
return {
@@ -253,6 +255,16 @@ function centerBlock(block: string): string {
253255
return lines.map(line => indent + line).join('\n')
254256
}
255257

258+
function resolveOpenURL(target: string, baseURL: string): string {
259+
try {
260+
return new URL(target, baseURL).href
261+
}
262+
catch {
263+
logger.warn(`Ignoring invalid \`--open.url\` value: ${target}`)
264+
return baseURL
265+
}
266+
}
267+
256268
/**
257269
* Resolve the command that opens `url`, honouring the de facto `BROWSER` and
258270
* `BROWSER_ARGS` environment variables (`BROWSER=none` disables opening).

packages/nuxt-cli/test/unit/listen.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
66

77
import { getNetworkAddresses, listen, resolveOpenCommand } from '../../src/dev/listen'
88

9+
const spawn = vi.hoisted(() => vi.fn((_command: string, _args: string[]) => ({ on: () => ({ unref: () => {} }) })))
10+
11+
vi.mock('node:child_process', () => ({ spawn }))
12+
913
vi.mock('node:os', () => ({
1014
networkInterfaces: vi.fn(() => ({})),
1115
release: () => 'test',
@@ -91,6 +95,20 @@ describe('listen', () => {
9195
await expect(start({ port: first.address.port, strictPort: true })).rejects.toThrow(/already in use/)
9296
})
9397

98+
it('should open the dev server URL', async () => {
99+
const listener = await start({ port: 0, open: true })
100+
101+
expect(spawn.mock.lastCall?.[1]).toContain(listener.url)
102+
})
103+
104+
it('should open a path or URL relative to the dev server', async () => {
105+
const listener = await start({ port: 0, open: true, openURL: '/dashboard' })
106+
expect(spawn.mock.lastCall?.[1]).toContain(`${listener.url}dashboard`)
107+
108+
await start({ port: 0, open: true, openURL: 'https://example.com/foo' })
109+
expect(spawn.mock.lastCall?.[1]).toContain('https://example.com/foo')
110+
})
111+
94112
it('should use the requested port with `strictPort`', async () => {
95113
const { address } = await start({ port: 0 })
96114
const port = address.port

0 commit comments

Comments
 (0)