diff --git a/packages/next/cli/next-dev.ts b/packages/next/cli/next-dev.ts index 9048fd028aed..ac1135b1b81b 100755 --- a/packages/next/cli/next-dev.ts +++ b/packages/next/cli/next-dev.ts @@ -75,9 +75,20 @@ const nextDev: cliCommand = (argv) => { ) } } - const allowRetry = !args['--port'] - let port: number = - args['--port'] || (process.env.PORT && parseInt(process.env.PORT)) || 3000 + + const hasPortFlag = Object.hasOwnProperty.call(args, '--port') + const allowRetry = !hasPortFlag + let port: number + if (hasPortFlag) { + port = args['--port'] + } else { + const parsed = process.env.PORT && parseInt(process.env.PORT) + if (typeof parsed === 'number' && !Number.isNaN(parsed)) { + port = parsed + } else { + port = 3000 + } + } // we allow the server to use a random port while testing // instead of attempting to find a random port and then hope diff --git a/packages/next/cli/next-start.ts b/packages/next/cli/next-start.ts index fc7cfe39369b..6893ccacfd9d 100755 --- a/packages/next/cli/next-start.ts +++ b/packages/next/cli/next-start.ts @@ -52,8 +52,17 @@ const nextStart: cliCommand = (argv) => { } const dir = getProjectDir(args._[0]) - let port: number = - args['--port'] || (process.env.PORT && parseInt(process.env.PORT)) || 3000 + let port: number + if (Object.hasOwnProperty.call(args, '--port')) { + port = args['--port'] + } else { + const parsed = process.env.PORT && parseInt(process.env.PORT) + if (typeof parsed === 'number' && !Number.isNaN(parsed)) { + port = parsed + } else { + port = 3000 + } + } const host = args['--hostname'] || '0.0.0.0' if (process.env.__NEXT_FORCED_PORT) { diff --git a/test/integration/cli/test/index.test.js b/test/integration/cli/test/index.test.js index 6e5aeb0451fd..4d228a31f569 100644 --- a/test/integration/cli/test/index.test.js +++ b/test/integration/cli/test/index.test.js @@ -178,6 +178,34 @@ describe('CLI Usage', () => { expect(output).toMatch(new RegExp(`http://localhost:${port}`)) }) + test('--port 0', async () => { + const output = await runNextCommandDev([dir, '--port', '0'], true) + const matches = /on 0.0.0.0:(\d+)/.exec(output) + expect(matches).not.toBe(null) + + const port = parseInt(matches[1]) + // Regression test: port 0 was interpreted as if no port had been + // provided, falling back to 3000. + expect(port).not.toBe(3000) + + expect(output).toMatch(new RegExp(`http://localhost:${port}`)) + }) + + test('PORT=0', async () => { + const output = await runNextCommandDev([dir], true, { + env: { PORT: 0 }, + }) + const matches = /on 0.0.0.0:(\d+)/.exec(output) + expect(matches).not.toBe(null) + + const port = parseInt(matches[1]) + // Regression test: port 0 was interpreted as if no port had been + // provided, falling back to 3000. + expect(port).not.toBe(3000) + + expect(output).toMatch(new RegExp(`http://localhost:${port}`)) + }) + test("NODE_OPTIONS='--inspect'", async () => { // this test checks that --inspect works by launching a single debugger for the main Next.js process, // not for its subprocesses