Skip to content

Commit c74962b

Browse files
authored
fix(dev): leave build asset upgrades to the bundler (#1595)
1 parent 7ec8cd2 commit c74962b

2 files changed

Lines changed: 48 additions & 31 deletions

File tree

‎packages/nuxt-cli/src/dev/utils.ts‎

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,18 +1099,17 @@ export class NuxtDevServer extends EventEmitter<DevServerEventMap> {
10991099
})
11001100

11011101
let viteHmrPinned = false
1102-
let viteWsPath: string | undefined
1103-
let viteHmrUrl: string | undefined
1102+
let viteHmrAttached = false
11041103
if (!process.env.NUXI_DISABLE_VITE_HMR) {
11051104
this.#currentNuxt.hooks.hook('vite:extend', ({ config }) => {
11061105
if (config.server) {
1107-
viteWsPath = attachViteHmrServer(config.server, this.listener.server)
1106+
attachViteHmrServer(config.server, this.listener.server)
11081107
viteHmrPinned = true
11091108
}
11101109
})
1111-
this.#currentNuxt.hooks.hook('vite:serverCreated', (server, { isClient }) => {
1110+
this.#currentNuxt.hooks.hook('vite:serverCreated', (_server, { isClient }) => {
11121111
if (isClient && viteHmrPinned) {
1113-
viteHmrUrl = viteWsPath ? join(server.config.base, viteWsPath) : server.config.base
1112+
viteHmrAttached = true
11141113
}
11151114
})
11161115
}
@@ -1151,32 +1150,19 @@ export class NuxtDevServer extends EventEmitter<DevServerEventMap> {
11511150

11521151
const nuxt = this.#currentNuxt
11531152
const baseURL = nuxt.options.app.baseURL.startsWith('./') ? nuxt.options.app.baseURL.slice(1) : nuxt.options.app.baseURL
1154-
const viteHmrPath = `${baseURL.replace(/\/$/, '')}/${nuxt.options.app.buildAssetsDir.replace(/^\//, '')}`
1153+
const buildAssetsPath = `${baseURL.replace(/\/$/, '')}/${nuxt.options.app.buildAssetsDir.replace(/^\//, '')}`
11551154
const expectsViteHmr = !process.env.NUXI_DISABLE_VITE_HMR && (!nuxt.options.builder || String(nuxt.options.builder).includes('vite'))
11561155
this.listener.server.on('upgrade', (req, socket, head) => {
11571156
this.#websocketConnections.add(socket)
11581157
socket.on('close', () => {
11591158
this.#websocketConnections.delete(socket)
11601159
})
1161-
const protocol = req.headers['sec-websocket-protocol']
1162-
if (expectsViteHmr && (protocol === 'vite-hmr' || protocol === 'vite-ping')) {
1163-
if (viteHmrUrl === undefined && req.url?.startsWith(viteHmrPath)) {
1160+
if (req.url?.startsWith(buildAssetsPath)) {
1161+
const protocol = req.headers['sec-websocket-protocol']
1162+
if (expectsViteHmr && !viteHmrAttached && (protocol === 'vite-hmr' || protocol === 'vite-ping')) {
11641163
socket.destroy()
1165-
return
1166-
}
1167-
if (viteHmrUrl !== undefined) {
1168-
let pathname: string
1169-
try {
1170-
pathname = new URL(`http://localhost${req.url}`).pathname
1171-
}
1172-
catch {
1173-
socket.destroy()
1174-
return
1175-
}
1176-
if (pathname === viteHmrUrl) {
1177-
return
1178-
}
11791164
}
1165+
return
11801166
}
11811167
if (nuxt.server && 'upgrade' in nuxt.server) {
11821168
nuxt.server.upgrade(req, socket as any, head)

‎packages/nuxt-cli/test/unit/dev/lifecycle.spec.ts‎

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ describe('dev server handover', () => {
666666
})
667667

668668
describe('dev server websocket upgrades', () => {
669-
function upgrade(server: InstanceType<typeof NuxtDevServer>, path: string, protocol = 'vite-ping') {
669+
function upgrade(server: InstanceType<typeof NuxtDevServer>, path: string, protocol: string | null = 'vite-ping') {
670670
const { port } = server.listener.address as AddressInfo
671671
const client = connect(port, '127.0.0.1')
672672
client.on('error', () => {})
@@ -675,7 +675,7 @@ describe('dev server websocket upgrades', () => {
675675
response += chunk
676676
})
677677
const closed = new Promise<void>(resolve => client.once('close', () => resolve()))
678-
client.write(`GET ${path} HTTP/1.1\r\nHost: 127.0.0.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Protocol: ${protocol}\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n`)
678+
client.write(`GET ${path} HTTP/1.1\r\nHost: 127.0.0.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n${protocol ? `Sec-WebSocket-Protocol: ${protocol}\r\n` : ''}Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n`)
679679
return { client, closed, response: () => response }
680680
}
681681

@@ -789,16 +789,26 @@ describe('dev server websocket upgrades', () => {
789789
it.each([
790790
['another asset path', '/_nuxt/other', 'vite-ping'],
791791
['another protocol', '/_nuxt/', 'graphql-ws'],
792-
])('should route upgrades Vite does not accept to the Nuxt server (%s)', async (_label, path, protocol) => {
792+
])('should leave asset upgrades Vite does not accept to other listeners (%s)', async (_label, path, protocol) => {
793793
const nuxt = createNuxt()
794794
const nitroUpgrade = vi.fn((_req: unknown, socket: Socket) => socket.destroy())
795795
Object.assign(nuxt.server, { upgrade: nitroUpgrade })
796+
nuxt.hook('listen', (server: import('node:http').Server) => {
797+
server.on('upgrade', (req: import('node:http').IncomingMessage, socket: Socket) => {
798+
if (req.url === path && req.headers['sec-websocket-protocol'] === protocol) {
799+
accept(req, socket)
800+
}
801+
})
802+
})
796803
const server = await startServer(nuxt)
797804
await attachFakeVite(nuxt)
798805

799-
const { closed } = upgrade(server, path, protocol)
800-
await expectClosedPromptly(closed)
801-
expect(nitroUpgrade).toHaveBeenCalledTimes(1)
806+
const { client, response } = upgrade(server, path, protocol)
807+
await vi.waitFor(() => expect(response()).toContain('101 Switching Protocols'))
808+
await new Promise(resolve => setTimeout(resolve, 50))
809+
expect(client.destroyed).toBe(false)
810+
expect(nitroUpgrade).not.toHaveBeenCalled()
811+
client.destroy()
802812
})
803813

804814
it.each(['//a:b', 'http://a:b/'])('should not throw on an upgrade to %s', async (path) => {
@@ -845,13 +855,34 @@ describe('dev server websocket upgrades', () => {
845855
client.destroy()
846856
})
847857

848-
it.each(['@nuxt/webpack-builder', '@nuxt/rspack-builder'])('should route asset upgrades to the Nuxt server with %s', async (builder) => {
858+
it.each([undefined, 'vite', 'webpack', 'rspack', '@nuxt/webpack-builder', '@nuxt/rspack-builder'])('should leave asset upgrades to the bundler with %s', async (builder) => {
849859
const nuxt = createNuxt({ builder })
850860
const nitroUpgrade = vi.fn((_req: unknown, socket: Socket) => socket.destroy())
851861
Object.assign(nuxt.server, { upgrade: nitroUpgrade })
862+
nuxt.hook('listen', (server: import('node:http').Server) => {
863+
server.on('upgrade', (req: import('node:http').IncomingMessage, socket: Socket) => {
864+
if (new URL(`http://example.com${req.url}`).pathname === '/_nuxt/rsbuild-hmr') {
865+
accept(req, socket)
866+
}
867+
})
868+
})
852869
const server = await startServer(nuxt)
853870

854-
const { closed } = upgrade(server, '/_nuxt/')
871+
const { client, response } = upgrade(server, '/_nuxt/rsbuild-hmr?token=abc', null)
872+
await vi.waitFor(() => expect(response()).toContain('101 Switching Protocols'))
873+
await new Promise(resolve => setTimeout(resolve, 50))
874+
expect(client.destroyed).toBe(false)
875+
expect(nitroUpgrade).not.toHaveBeenCalled()
876+
client.destroy()
877+
})
878+
879+
it.each(['webpack', 'rspack'])('should route other upgrades to the Nuxt server with %s', async (builder) => {
880+
const nuxt = createNuxt({ builder })
881+
const nitroUpgrade = vi.fn((_req: unknown, socket: Socket) => socket.destroy())
882+
Object.assign(nuxt.server, { upgrade: nitroUpgrade })
883+
const server = await startServer(nuxt)
884+
885+
const { closed } = upgrade(server, '/_ws', null)
855886
await expectClosedPromptly(closed)
856887
expect(nitroUpgrade).toHaveBeenCalledTimes(1)
857888
})

0 commit comments

Comments
 (0)