From 646319cc845f24a12ac5f8f6d176597a5bf66fd3 Mon Sep 17 00:00:00 2001 From: Caven Date: Wed, 3 Apr 2024 20:38:40 +0800 Subject: [PATCH] fix: do not access document in `/@vite/client` when not defined (#16318) Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com> --- packages/vite/src/client/client.ts | 77 +++++++++++-------- packages/vite/src/client/overlay.ts | 59 +++++++------- .../worker/__tests__/es/worker-es.spec.ts | 2 +- .../worker-relative-base.spec.ts | 2 +- .../emit-chunk-dynamic-import-worker.js | 6 +- 5 files changed, 82 insertions(+), 64 deletions(-) diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 38b632cb945b57..8fcb145fd0242c 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -102,9 +102,11 @@ function setupWebSocket( notifyListeners('vite:ws:disconnect', { webSocket: socket }) - console.log(`[vite] server connection lost. polling for restart...`) - await waitForSuccessfulPing(protocol, hostAndPath) - location.reload() + if (hasDocument) { + console.log(`[vite] server connection lost. polling for restart...`) + await waitForSuccessfulPing(protocol, hostAndPath) + location.reload() + } }) return socket @@ -182,16 +184,20 @@ async function handleMessage(payload: HMRPayload) { break case 'update': notifyListeners('vite:beforeUpdate', payload) - // if this is the first update and there's already an error overlay, it - // means the page opened with existing server compile error and the whole - // module script failed to load (since one of the nested imports is 500). - // in this case a normal update won't work and a full reload is needed. - if (isFirstUpdate && hasErrorOverlay()) { - window.location.reload() - return - } else { - clearErrorOverlay() - isFirstUpdate = false + if (hasDocument) { + // if this is the first update and there's already an error overlay, it + // means the page opened with existing server compile error and the whole + // module script failed to load (since one of the nested imports is 500). + // in this case a normal update won't work and a full reload is needed. + if (isFirstUpdate && hasErrorOverlay()) { + window.location.reload() + return + } else { + if (enableOverlay) { + clearErrorOverlay() + } + isFirstUpdate = false + } } await Promise.all( payload.updates.map(async (update): Promise => { @@ -249,21 +255,23 @@ async function handleMessage(payload: HMRPayload) { } case 'full-reload': notifyListeners('vite:beforeFullReload', payload) - if (payload.path && payload.path.endsWith('.html')) { - // if html file is edited, only reload the page if the browser is - // currently on that page. - const pagePath = decodeURI(location.pathname) - const payloadPath = base + payload.path.slice(1) - if ( - pagePath === payloadPath || - payload.path === '/index.html' || - (pagePath.endsWith('/') && pagePath + 'index.html' === payloadPath) - ) { + if (hasDocument) { + if (payload.path && payload.path.endsWith('.html')) { + // if html file is edited, only reload the page if the browser is + // currently on that page. + const pagePath = decodeURI(location.pathname) + const payloadPath = base + payload.path.slice(1) + if ( + pagePath === payloadPath || + payload.path === '/index.html' || + (pagePath.endsWith('/') && pagePath + 'index.html' === payloadPath) + ) { + pageReload() + } + return + } else { pageReload() } - return - } else { - pageReload() } break case 'prune': @@ -272,13 +280,15 @@ async function handleMessage(payload: HMRPayload) { break case 'error': { notifyListeners('vite:error', payload) - const err = payload.err - if (enableOverlay) { - createErrorOverlay(err) - } else { - console.error( - `[vite] Internal Server Error\n${err.message}\n${err.stack}`, - ) + if (hasDocument) { + const err = payload.err + if (enableOverlay) { + createErrorOverlay(err) + } else { + console.error( + `[vite] Internal Server Error\n${err.message}\n${err.stack}`, + ) + } } break } @@ -298,6 +308,7 @@ function notifyListeners(event: string, data: any): void { } const enableOverlay = __HMR_ENABLE_OVERLAY__ +const hasDocument = 'document' in globalThis function createErrorOverlay(err: ErrorPayload['err']) { clearErrorOverlay() diff --git a/packages/vite/src/client/overlay.ts b/packages/vite/src/client/overlay.ts index dffdafd9197ddb..f0e34cac19e68e 100644 --- a/packages/vite/src/client/overlay.ts +++ b/packages/vite/src/client/overlay.ts @@ -165,39 +165,41 @@ kbd { ` // Error Template -const template = h( - 'div', - { class: 'backdrop', part: 'backdrop' }, +let template: HTMLElement +const createTemplate = () => h( 'div', - { class: 'window', part: 'window' }, - h( - 'pre', - { class: 'message', part: 'message' }, - h('span', { class: 'plugin', part: 'plugin' }), - h('span', { class: 'message-body', part: 'message-body' }), - ), - h('pre', { class: 'file', part: 'file' }), - h('pre', { class: 'frame', part: 'frame' }), - h('pre', { class: 'stack', part: 'stack' }), + { class: 'backdrop', part: 'backdrop' }, h( 'div', - { class: 'tip', part: 'tip' }, - 'Click outside, press ', - h('kbd', {}, 'Esc'), - ' key, or fix the code to dismiss.', - h('br'), - 'You can also disable this overlay by setting ', - h('code', { part: 'config-option-name' }, 'server.hmr.overlay'), - ' to ', - h('code', { part: 'config-option-value' }, 'false'), - ' in ', - h('code', { part: 'config-file-name' }, hmrConfigName), - '.', + { class: 'window', part: 'window' }, + h( + 'pre', + { class: 'message', part: 'message' }, + h('span', { class: 'plugin', part: 'plugin' }), + h('span', { class: 'message-body', part: 'message-body' }), + ), + h('pre', { class: 'file', part: 'file' }), + h('pre', { class: 'frame', part: 'frame' }), + h('pre', { class: 'stack', part: 'stack' }), + h( + 'div', + { class: 'tip', part: 'tip' }, + 'Click outside, press ', + h('kbd', {}, 'Esc'), + ' key, or fix the code to dismiss.', + h('br'), + 'You can also disable this overlay by setting ', + h('code', { part: 'config-option-name' }, 'server.hmr.overlay'), + ' to ', + h('code', { part: 'config-option-value' }, 'false'), + ' in ', + h('code', { part: 'config-file-name' }, hmrConfigName), + '.', + ), ), - ), - h('style', {}, templateStyle), -) + h('style', {}, templateStyle), + ) const fileRE = /(?:[a-zA-Z]:\\|\/).*?:\d+:\d+/g const codeframeRE = /^(?:>?\s*\d+\s+\|.*|\s+\|\s*\^.*)\r?\n/gm @@ -213,6 +215,7 @@ export class ErrorOverlay extends HTMLElement { super() this.root = this.attachShadow({ mode: 'open' }) + template ??= createTemplate() this.root.appendChild(template) codeframeRE.lastIndex = 0 diff --git a/playground/worker/__tests__/es/worker-es.spec.ts b/playground/worker/__tests__/es/worker-es.spec.ts index 748cd2b0592bb2..3656b07c18d576 100644 --- a/playground/worker/__tests__/es/worker-es.spec.ts +++ b/playground/worker/__tests__/es/worker-es.spec.ts @@ -200,7 +200,7 @@ test('emit chunk', async () => { ) await untilUpdated( () => page.textContent('.emit-chunk-dynamic-import-worker'), - '"A string/es/"', + '"A stringmodule1/es/"', true, ) }) diff --git a/playground/worker/__tests__/relative-base/worker-relative-base.spec.ts b/playground/worker/__tests__/relative-base/worker-relative-base.spec.ts index ae791ff7f36fe4..6c3180c7e2aa31 100644 --- a/playground/worker/__tests__/relative-base/worker-relative-base.spec.ts +++ b/playground/worker/__tests__/relative-base/worker-relative-base.spec.ts @@ -141,7 +141,7 @@ test.runIf(isBuild)('emit chunk', async () => { ) await untilUpdated( () => page.textContent('.emit-chunk-dynamic-import-worker'), - '"A string./"', + '"A stringmodule1./"', true, ) }) diff --git a/playground/worker/emit-chunk-dynamic-import-worker.js b/playground/worker/emit-chunk-dynamic-import-worker.js index 9c3ede1faa2ed9..3df9fff1e7191b 100644 --- a/playground/worker/emit-chunk-dynamic-import-worker.js +++ b/playground/worker/emit-chunk-dynamic-import-worker.js @@ -1,5 +1,9 @@ +import module1Url from './modules/module1.js?url' + import('./modules/module0').then((module) => { - self.postMessage(module.default + import.meta.env.BASE_URL) + import(/* @vite-ignore */ module1Url).then((module1) => { + self.postMessage(module.default + module1.msg1 + import.meta.env.BASE_URL) + }) }) // for sourcemap