diff --git a/.changeset/twelve-buses-smile.md b/.changeset/twelve-buses-smile.md new file mode 100644 index 000000000000..ac8878662a91 --- /dev/null +++ b/.changeset/twelve-buses-smile.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': patch +--- + +fix: log the actual adapter-node listening address diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index a7a5c96b5184..f5040cb14685 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -3,6 +3,7 @@ import process from 'node:process'; import { handler } from 'HANDLER'; import { env, timeout_env } from 'ENV'; import polka from 'polka'; +import { format_listening_address } from '../utils.js'; export const path = env('SOCKET_PATH', false); export const host = env('HOST', '0.0.0.0'); @@ -57,7 +58,7 @@ if (socket_activation) { }); } else { server.listen({ path, host, port }, () => { - console.log(`Listening on ${path || `http://${host}:${port}`}`); + console.log(`Listening on ${format_listening_address(path, host, port, httpServer.address())}`); }); } diff --git a/packages/adapter-node/tests/utils.spec.ts b/packages/adapter-node/tests/utils.spec.ts index e2dd7af99dda..bab652944ce7 100644 --- a/packages/adapter-node/tests/utils.spec.ts +++ b/packages/adapter-node/tests/utils.spec.ts @@ -1,5 +1,5 @@ import { expect, test, describe } from 'vitest'; -import { parse_as_bytes, parse_origin } from '../utils.js'; +import { format_listening_address, parse_as_bytes, parse_origin } from '../utils.js'; describe('parse_as_bytes', () => { test.each([ @@ -47,3 +47,37 @@ describe('parse_origin', () => { } ); }); + +describe('format_listening_address', () => { + test('uses the actual listening port assigned by the server', () => { + expect( + format_listening_address(false, '0.0.0.0', '0', { + address: '0.0.0.0', + family: 'IPv4', + port: 43521 + }) + ).toBe('http://0.0.0.0:43521'); + }); + + test('formats IPv6 addresses as valid URLs', () => { + expect( + format_listening_address(false, '::1', '3000', { + address: '::1', + family: 'IPv6', + port: 3000 + }) + ).toBe('http://[::1]:3000'); + }); + + test('falls back to configured host and port when the address is unavailable', () => { + expect(format_listening_address(false, 'localhost', '3000', null)).toBe( + 'http://localhost:3000' + ); + }); + + test('returns the socket path unchanged', () => { + expect(format_listening_address('/tmp/sveltekit.sock', '0.0.0.0', '3000', null)).toBe( + '/tmp/sveltekit.sock' + ); + }); +}); diff --git a/packages/adapter-node/utils.js b/packages/adapter-node/utils.js index b5bb51c35f75..38c543ef39c8 100644 --- a/packages/adapter-node/utils.js +++ b/packages/adapter-node/utils.js @@ -1,3 +1,5 @@ +import { format } from 'node:url'; + /** * Parses the given value into number of bytes. * @@ -50,3 +52,32 @@ export function parse_origin(value) { return url.origin; } + +/** + * Formats the address the server is listening on. + * + * @param {string | false} path + * @param {string} host + * @param {string | false} port + * @param {import('node:net').AddressInfo | string | null} address + * @returns {string} + */ +export function format_listening_address(path, host, port, address) { + if (path) { + return path; + } + + if (address && typeof address === 'object') { + return format({ + protocol: 'http:', + hostname: address.address, + port: address.port + }); + } + + return format({ + protocol: 'http:', + hostname: host, + port: String(port) + }); +}