diff --git a/packages/vite/src/module-runner/hmrHandler.ts b/packages/vite/src/module-runner/hmrHandler.ts index bedfc71980943f..5844c0de844ef2 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 35b23efef139b4..c135f797146cff 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -184,6 +184,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 9b67dfdbe444ec..544dcc329291d1 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 353fe1f310cf11..6971a43411cf16 100644 --- a/packages/vite/src/node/server/environment.ts +++ b/packages/vite/src/node/server/environment.ts @@ -23,7 +23,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 9797a4374fb6f7..2527cbcfb54cab 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 70a62abbdd7974..df0178b7af69bf 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 a12c674cc98ed6..1b79d14e704f18 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 6d5bd9a689108a..2d3331bb5db8d6 100644 --- a/packages/vite/src/shared/moduleRunnerTransport.ts +++ b/packages/vite/src/shared/moduleRunnerTransport.ts @@ -33,7 +33,15 @@ type InvokeableModuleRunnerTransport = Omit & { } function reviveInvokeError(e: any) { - return Object.assign(new Error(e.message || 'Unknown invoke error'), e) + const innerError = new Error('received error stacktrace') + innerError.stack = e.stack + + // set properties to wrapped error, but use the current stacktrace + const wrappedError = new Error(e.message || 'Unknown invoke error', { + cause: innerError, + }) + Object.assign(wrappedError, { ...e, stack: wrappedError.stack }) + return wrappedError } const createInvokeableTransport = ( @@ -94,7 +102,7 @@ const createInvokeableTransport = ( const { e, r } = data.data if (e) { - promise.reject(reviveInvokeError(e)) + promise.reject(e) } else { promise.resolve(r) } @@ -161,7 +169,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 bc9c716ffb097b..3bf14f6e4f5eca 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 f3a2261319bf50..7518f47bf06dc7 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)