fix(ssr): preserve fetchModule error details (#18626)

This commit is contained in:
Hiroshi Ogawa 2024-11-11 21:28:52 +09:00 committed by GitHub
parent 9eab231c7b
commit 866a433a34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 103 additions and 2 deletions

View File

@ -1,5 +1,6 @@
packages/*/CHANGELOG.md packages/*/CHANGELOG.md
packages/vite/src/node/ssr/runtime/__tests__/fixtures packages/vite/src/node/ssr/runtime/__tests__/fixtures
packages/vite/src/node/ssr/__tests__/fixtures/errors
playground-temp/ playground-temp/
dist/ dist/
temp/ temp/

View File

@ -213,6 +213,7 @@ export const normalizeHotChannel = (
name: error.name, name: error.name,
message: error.message, message: error.message,
stack: error.stack, stack: error.stack,
...error, // preserve enumerable properties such as RollupError.loc, frame, plugin
}, },
} }
} }

View File

@ -0,0 +1,57 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`parse error 1`] = `
{
"frame": "
Expected ";" but found "code"
1 | invalid code
| ^
2 |
",
"id": "<root>/fixtures/errors/syntax-error.ts",
"loc": {
"column": 8,
"file": "<root>/fixtures/errors/syntax-error.ts",
"line": 1,
},
"message": "Transform failed with 1 error:
<root>/fixtures/errors/syntax-error.ts:1:8: ERROR: Expected ";" but found "code"",
}
`;
exports[`parse error 2`] = `
{
"frame": "",
"id": "",
"loc": undefined,
"message": "Expected ';', '}' or <eof>",
}
`;
exports[`parse error 3`] = `
{
"frame": "
Expected ";" but found "code"
1 | invalid code
| ^
2 |
",
"id": "<root>/fixtures/errors/syntax-error.ts",
"loc": {
"column": 8,
"file": "<root>/fixtures/errors/syntax-error.ts",
"line": 1,
},
"message": "Transform failed with 1 error:
<root>/fixtures/errors/syntax-error.ts:1:8: ERROR: Expected ";" but found "code"",
}
`;
exports[`parse error 4`] = `
{
"frame": "",
"id": "",
"loc": undefined,
"message": "Expected ';', '}' or <eof>",
}
`;

View File

@ -0,0 +1 @@
import './syntax-error.js'

View File

@ -0,0 +1 @@
import './syntax-error.ts'

View File

@ -0,0 +1 @@
invalid code

View File

@ -0,0 +1 @@
invalid code

View File

@ -1,5 +1,6 @@
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import path from 'node:path' import path from 'node:path'
import { stripVTControlCharacters } from 'node:util'
import { expect, test } from 'vitest' import { expect, test } from 'vitest'
import { createServer } from '../../server' import { createServer } from '../../server'
import { normalizePath } from '../../utils' import { normalizePath } from '../../utils'
@ -178,3 +179,36 @@ test('can access nodejs global', async () => {
const mod = await server.ssrLoadModule('/fixtures/global/test.js') const mod = await server.ssrLoadModule('/fixtures/global/test.js')
expect(mod.default).toBe(globalThis) expect(mod.default).toBe(globalThis)
}) })
test('parse error', async () => {
const server = await createDevServer()
function stripRoot(s?: string) {
return (s || '').replace(server.config.root, '<root>')
}
for (const file of [
'/fixtures/errors/syntax-error.ts',
'/fixtures/errors/syntax-error.js',
'/fixtures/errors/syntax-error-dep.ts',
'/fixtures/errors/syntax-error-dep.js',
]) {
try {
await server.ssrLoadModule(file)
} catch (e) {
expect(e).toBeInstanceOf(Error)
expect({
message: stripRoot(e.message),
frame: stripVTControlCharacters(e.frame || ''),
id: stripRoot(e.id),
loc: e.loc && {
file: stripRoot(e.loc.file),
column: e.loc.column,
line: e.loc.line,
},
}).toMatchSnapshot()
continue
}
expect.unreachable()
}
})

View File

@ -32,6 +32,10 @@ type InvokeableModuleRunnerTransport = Omit<ModuleRunnerTransport, 'invoke'> & {
): Promise<ReturnType<Awaited<InvokeMethods[T]>>> ): Promise<ReturnType<Awaited<InvokeMethods[T]>>>
} }
function reviveInvokeError(e: any) {
return Object.assign(new Error(e.message || 'Unknown invoke error'), e)
}
const createInvokeableTransport = ( const createInvokeableTransport = (
transport: ModuleRunnerTransport, transport: ModuleRunnerTransport,
): InvokeableModuleRunnerTransport => { ): InvokeableModuleRunnerTransport => {
@ -49,7 +53,7 @@ const createInvokeableTransport = (
} satisfies InvokeSendData, } satisfies InvokeSendData,
} satisfies CustomPayload) } satisfies CustomPayload)
if ('e' in result) { if ('e' in result) {
throw result.e throw reviveInvokeError(result.e)
} }
return result.r return result.r
}, },
@ -90,7 +94,7 @@ const createInvokeableTransport = (
const { e, r } = data.data const { e, r } = data.data
if (e) { if (e) {
promise.reject(e) promise.reject(reviveInvokeError(e))
} else { } else {
promise.resolve(r) promise.resolve(r)
} }