fix: catch error in full reload handler (#18713)
Some checks are pending
CI / Get changed files (push) Waiting to run
CI / Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }} (18, ubuntu-latest) (push) Blocked by required conditions
CI / Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }} (20, ubuntu-latest) (push) Blocked by required conditions
CI / Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }} (22, macos-latest) (push) Blocked by required conditions
CI / Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }} (22, ubuntu-latest) (push) Blocked by required conditions
CI / Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }} (22, windows-latest) (push) Blocked by required conditions
CI / Build & Test Passed or Skipped (push) Blocked by required conditions
CI / Build & Test Failed (push) Blocked by required conditions
CI / Lint: node-20, ubuntu-latest (push) Waiting to run
Preview release / preview (push) Waiting to run

This commit is contained in:
翠 / green 2024-11-21 16:35:32 +09:00 committed by GitHub
parent d5e7db08a4
commit a10e741065
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 52 additions and 19 deletions

View File

@ -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) {
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
}

View File

@ -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'

View File

@ -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')

View File

@ -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'

View File

@ -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'

View File

@ -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')

View File

@ -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'

View File

@ -33,7 +33,13 @@ type InvokeableModuleRunnerTransport = Omit<ModuleRunnerTransport, 'invoke'> & {
}
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 = (
})
}
try {
return await promise
} catch (err) {
throw reviveInvokeError(err)
}
},
}
}

View File

@ -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(', '),

View File

@ -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)