diff --git a/.prettierignore b/.prettierignore index 95541be45..d5283c0ed 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,6 @@ packages/*/CHANGELOG.md packages/vite/src/node/ssr/runtime/__tests__/fixtures +packages/vite/src/node/ssr/__tests__/fixtures/errors playground-temp/ dist/ temp/ diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index 18c9537d0..60db76c93 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -213,6 +213,7 @@ export const normalizeHotChannel = ( name: error.name, message: error.message, stack: error.stack, + ...error, // preserve enumerable properties such as RollupError.loc, frame, plugin }, } } diff --git a/packages/vite/src/node/ssr/__tests__/__snapshots__/ssrLoadModule.spec.ts.snap b/packages/vite/src/node/ssr/__tests__/__snapshots__/ssrLoadModule.spec.ts.snap new file mode 100644 index 000000000..7b9a13e9f --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/__snapshots__/ssrLoadModule.spec.ts.snap @@ -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": "/fixtures/errors/syntax-error.ts", + "loc": { + "column": 8, + "file": "/fixtures/errors/syntax-error.ts", + "line": 1, + }, + "message": "Transform failed with 1 error: +/fixtures/errors/syntax-error.ts:1:8: ERROR: Expected ";" but found "code"", +} +`; + +exports[`parse error 2`] = ` +{ + "frame": "", + "id": "", + "loc": undefined, + "message": "Expected ';', '}' or ", +} +`; + +exports[`parse error 3`] = ` +{ + "frame": " +Expected ";" but found "code" +1 | invalid code + | ^ +2 | +", + "id": "/fixtures/errors/syntax-error.ts", + "loc": { + "column": 8, + "file": "/fixtures/errors/syntax-error.ts", + "line": 1, + }, + "message": "Transform failed with 1 error: +/fixtures/errors/syntax-error.ts:1:8: ERROR: Expected ";" but found "code"", +} +`; + +exports[`parse error 4`] = ` +{ + "frame": "", + "id": "", + "loc": undefined, + "message": "Expected ';', '}' or ", +} +`; diff --git a/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error-dep.js b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error-dep.js new file mode 100644 index 000000000..defb897f2 --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error-dep.js @@ -0,0 +1 @@ +import './syntax-error.js' diff --git a/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error-dep.ts b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error-dep.ts new file mode 100644 index 000000000..af356b5c5 --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error-dep.ts @@ -0,0 +1 @@ +import './syntax-error.ts' diff --git a/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error.js b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error.js new file mode 100644 index 000000000..618a16e1c --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error.js @@ -0,0 +1 @@ +invalid code diff --git a/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error.ts b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error.ts new file mode 100644 index 000000000..618a16e1c --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/fixtures/errors/syntax-error.ts @@ -0,0 +1 @@ +invalid code diff --git a/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts index b5bae3ae1..4479d20be 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts @@ -1,5 +1,6 @@ import { fileURLToPath } from 'node:url' import path from 'node:path' +import { stripVTControlCharacters } from 'node:util' import { expect, test } from 'vitest' import { createServer } from '../../server' import { normalizePath } from '../../utils' @@ -178,3 +179,36 @@ test('can access nodejs global', async () => { const mod = await server.ssrLoadModule('/fixtures/global/test.js') expect(mod.default).toBe(globalThis) }) + +test('parse error', async () => { + const server = await createDevServer() + + function stripRoot(s?: string) { + return (s || '').replace(server.config.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() + } +}) diff --git a/packages/vite/src/shared/moduleRunnerTransport.ts b/packages/vite/src/shared/moduleRunnerTransport.ts index f643e3d6a..6d5bd9a68 100644 --- a/packages/vite/src/shared/moduleRunnerTransport.ts +++ b/packages/vite/src/shared/moduleRunnerTransport.ts @@ -32,6 +32,10 @@ type InvokeableModuleRunnerTransport = Omit & { ): Promise>> } +function reviveInvokeError(e: any) { + return Object.assign(new Error(e.message || 'Unknown invoke error'), e) +} + const createInvokeableTransport = ( transport: ModuleRunnerTransport, ): InvokeableModuleRunnerTransport => { @@ -49,7 +53,7 @@ const createInvokeableTransport = ( } satisfies InvokeSendData, } satisfies CustomPayload) if ('e' in result) { - throw result.e + throw reviveInvokeError(result.e) } return result.r }, @@ -90,7 +94,7 @@ const createInvokeableTransport = ( const { e, r } = data.data if (e) { - promise.reject(e) + promise.reject(reviveInvokeError(e)) } else { promise.resolve(r) }