From a10e7410656d3614cbfd07ba772776ff334a8d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 21 Nov 2024 16:35:32 +0900 Subject: [PATCH] fix: catch error in full reload handler (#18713) --- packages/vite/src/module-runner/hmrHandler.ts | 11 ++++++++++- packages/vite/src/node/constants.ts | 1 - packages/vite/src/node/plugins/optimizedDeps.ts | 2 +- packages/vite/src/node/server/environment.ts | 2 +- packages/vite/src/node/server/index.ts | 7 ++----- .../src/node/server/middlewares/transform.ts | 6 ++++-- packages/vite/src/shared/constants.ts | 2 ++ .../vite/src/shared/moduleRunnerTransport.ts | 16 +++++++++++++--- .../__tests__/environment-react-ssr.spec.ts | 8 ++++---- playground/environment-react-ssr/vite.config.ts | 16 +++++++++++++++- 10 files changed, 52 insertions(+), 19 deletions(-) diff --git a/packages/vite/src/module-runner/hmrHandler.ts b/packages/vite/src/module-runner/hmrHandler.ts index bedfc7198..5844c0de8 100644 --- a/packages/vite/src/module-runner/hmrHandler.ts +++ b/packages/vite/src/module-runner/hmrHandler.ts @@ -1,5 +1,6 @@ import type { HotPayload } from 'types/hmrPayload' import { slash, unwrapId } from '../shared/utils' +import { ERR_OUTDATED_OPTIMIZED_DEP } from '../shared/constants' import type { ModuleRunner } from './runner' // updates to HMR should go one after another. It is possible to trigger another update during the invalidation for example. @@ -56,7 +57,15 @@ export async function handleHotPayload( runner.evaluatedModules.clear() for (const url of clearEntrypointUrls) { - await runner.import(url) + try { + await runner.import(url) + } catch (err) { + if (err.code !== ERR_OUTDATED_OPTIMIZED_DEP) { + hmrClient.logger.error( + `An error happened during full reload\n${err.message}\n${err.stack}`, + ) + } + } } break } diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 453a1bb5b..0f865742c 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -187,6 +187,5 @@ export const METADATA_FILENAME = '_metadata.json' export const ERR_OPTIMIZE_DEPS_PROCESSING_ERROR = 'ERR_OPTIMIZE_DEPS_PROCESSING_ERROR' -export const ERR_OUTDATED_OPTIMIZED_DEP = 'ERR_OUTDATED_OPTIMIZED_DEP' export const ERR_FILE_NOT_FOUND_IN_OPTIMIZED_DEP_DIR = 'ERR_FILE_NOT_FOUND_IN_OPTIMIZED_DEP_DIR' diff --git a/packages/vite/src/node/plugins/optimizedDeps.ts b/packages/vite/src/node/plugins/optimizedDeps.ts index 9b67dfdbe..544dcc329 100644 --- a/packages/vite/src/node/plugins/optimizedDeps.ts +++ b/packages/vite/src/node/plugins/optimizedDeps.ts @@ -6,11 +6,11 @@ import { DEP_VERSION_RE, ERR_FILE_NOT_FOUND_IN_OPTIMIZED_DEP_DIR, ERR_OPTIMIZE_DEPS_PROCESSING_ERROR, - ERR_OUTDATED_OPTIMIZED_DEP, } from '../constants' import { createDebugger } from '../utils' import { optimizedDepInfoFromFile } from '../optimizer' import { cleanUrl } from '../../shared/utils' +import { ERR_OUTDATED_OPTIMIZED_DEP } from '../../shared/constants' const debug = createDebugger('vite:optimize-deps') diff --git a/packages/vite/src/node/server/environment.ts b/packages/vite/src/node/server/environment.ts index f42d305af..39e363931 100644 --- a/packages/vite/src/node/server/environment.ts +++ b/packages/vite/src/node/server/environment.ts @@ -19,7 +19,7 @@ import { createExplicitDepsOptimizer, } from '../optimizer/optimizer' import { resolveEnvironmentPlugins } from '../plugin' -import { ERR_OUTDATED_OPTIMIZED_DEP } from '../constants' +import { ERR_OUTDATED_OPTIMIZED_DEP } from '../../shared/constants' import { promiseWithResolvers } from '../../shared/utils' import type { ViteDevServer } from '../server' import { EnvironmentModuleGraph } from './moduleGraph' diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 607ede599..c7b4df492 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -43,11 +43,8 @@ import { ssrTransform } from '../ssr/ssrTransform' import { reloadOnTsconfigChange } from '../plugins/esbuild' import { bindCLIShortcuts } from '../shortcuts' import type { BindCLIShortcutsOptions } from '../shortcuts' -import { - CLIENT_DIR, - DEFAULT_DEV_PORT, - ERR_OUTDATED_OPTIMIZED_DEP, -} from '../constants' +import { ERR_OUTDATED_OPTIMIZED_DEP } from '../../shared/constants' +import { CLIENT_DIR, DEFAULT_DEV_PORT } from '../constants' import type { Logger } from '../logger' import { printServerUrls } from '../logger' import { warnFutureDeprecation } from '../deprecations' diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index 402ef4f4c..abd790b1a 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -25,7 +25,6 @@ import { DEP_VERSION_RE, ERR_FILE_NOT_FOUND_IN_OPTIMIZED_DEP_DIR, ERR_OPTIMIZE_DEPS_PROCESSING_ERROR, - ERR_OUTDATED_OPTIMIZED_DEP, FS_PREFIX, } from '../../constants' import { @@ -35,7 +34,10 @@ import { } from '../../plugins/css' import { ERR_CLOSED_SERVER } from '../pluginContainer' import { cleanUrl, unwrapId, withTrailingSlash } from '../../../shared/utils' -import { NULL_BYTE_PLACEHOLDER } from '../../../shared/constants' +import { + ERR_OUTDATED_OPTIMIZED_DEP, + NULL_BYTE_PLACEHOLDER, +} from '../../../shared/constants' import { ensureServingAccess } from './static' const debugCache = createDebugger('vite:cache') diff --git a/packages/vite/src/shared/constants.ts b/packages/vite/src/shared/constants.ts index a12c674cc..1b79d14e7 100644 --- a/packages/vite/src/shared/constants.ts +++ b/packages/vite/src/shared/constants.ts @@ -21,3 +21,5 @@ SOURCEMAPPING_URL += 'ppingURL' export const MODULE_RUNNER_SOURCEMAPPING_SOURCE = '//# sourceMappingSource=vite-generated' + +export const ERR_OUTDATED_OPTIMIZED_DEP = 'ERR_OUTDATED_OPTIMIZED_DEP' diff --git a/packages/vite/src/shared/moduleRunnerTransport.ts b/packages/vite/src/shared/moduleRunnerTransport.ts index 6d5bd9a68..f10ef69ba 100644 --- a/packages/vite/src/shared/moduleRunnerTransport.ts +++ b/packages/vite/src/shared/moduleRunnerTransport.ts @@ -33,7 +33,13 @@ type InvokeableModuleRunnerTransport = Omit & { } function reviveInvokeError(e: any) { - return Object.assign(new Error(e.message || 'Unknown invoke error'), e) + const error = new Error(e.message || 'Unknown invoke error') + Object.assign(error, e, { + // pass the whole error instead of just the stacktrace + // so that it gets formatted nicely with console.log + runnerError: new Error('RunnerError'), + }) + return error } const createInvokeableTransport = ( @@ -94,7 +100,7 @@ const createInvokeableTransport = ( const { e, r } = data.data if (e) { - promise.reject(reviveInvokeError(e)) + promise.reject(e) } else { promise.resolve(r) } @@ -161,7 +167,11 @@ const createInvokeableTransport = ( }) } - return await promise + try { + return await promise + } catch (err) { + throw reviveInvokeError(err) + } }, } } diff --git a/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts b/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts index bc9c716ff..3bf14f6e4 100644 --- a/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts +++ b/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts @@ -1,5 +1,6 @@ import fs from 'node:fs' import path from 'node:path' +import { stripVTControlCharacters } from 'node:util' import { describe, expect, onTestFinished, test } from 'vitest' import type { DepOptimizationMetadata } from 'vite' import { @@ -74,10 +75,9 @@ describe.runIf(!isBuild)('pre-bundling', () => { serverLogs .map( (log) => - log - // eslint-disable-next-line no-control-regex - .replace(/\x1B\[\d+m/g, '') - .match(/new dependencies optimized: (react-fake-.*)/)?.[1], + stripVTControlCharacters(log).match( + /new dependencies optimized: (react-fake-.*)/, + )?.[1], ) .filter(Boolean) .join(', '), diff --git a/playground/environment-react-ssr/vite.config.ts b/playground/environment-react-ssr/vite.config.ts index f3a226131..7518f47bf 100644 --- a/playground/environment-react-ssr/vite.config.ts +++ b/playground/environment-react-ssr/vite.config.ts @@ -75,9 +75,23 @@ export function vitePluginSsrMiddleware({ const runner = createServerModuleRunner(server.environments.ssr, { hmr: { logger: false }, }) + const importWithRetry = async () => { + try { + return await runner.import(entry) + } catch (e) { + if ( + e instanceof Error && + (e as any).code === 'ERR_OUTDATED_OPTIMIZED_DEP' + ) { + runner.clearCache() + return await importWithRetry() + } + throw e + } + } const handler: Connect.NextHandleFunction = async (req, res, next) => { try { - const mod = await runner.import(entry) + const mod = await importWithRetry() await mod['default'](req, res, next) } catch (e) { next(e)