From 8f6225b8828b0f0441c2990b2ea76ee4a2a8c3d2 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 28 Dec 2023 14:57:27 +0100 Subject: [PATCH] fix: don't fail if `inline: true` is set (#4815) --- packages/vite-node/src/client.ts | 9 +++++++-- packages/vitest/src/runtime/execute.ts | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index c790e1a2f706..11bb58a5e765 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -303,6 +303,8 @@ export class ViteNodeRunner { enumerable: false, configurable: false, }) + const SYMBOL_NOT_DEFINED = Symbol('not defined') + let moduleExports: unknown = SYMBOL_NOT_DEFINED // this proxy is triggered only on exports.{name} and module.exports access // inside the module itself. imported module is always "exports" const cjsExports = new Proxy(exports, { @@ -326,12 +328,14 @@ export class ViteNodeRunner { // returns undefined, when accessing named exports, if default is not an object // but is still present inside hasOwnKeys, this is Node behaviour for CJS - if (isPrimitive(exports.default)) { + if (moduleExports !== SYMBOL_NOT_DEFINED && isPrimitive(moduleExports)) { defineExport(exports, p, () => undefined) return true } - exports.default[p] = value + if (!isPrimitive(exports.default)) + exports.default[p] = value + if (p !== 'default') defineExport(exports, p, () => value) @@ -345,6 +349,7 @@ export class ViteNodeRunner { set exports(value) { exportAll(cjsExports, value) exports.default = value + moduleExports = value }, get exports() { return cjsExports diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index fbbbf6771649..cafc20eae700 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -1,7 +1,7 @@ import { pathToFileURL } from 'node:url' import vm from 'node:vm' import { DEFAULT_REQUEST_STUBS, ModuleCacheMap, ViteNodeRunner } from 'vite-node/client' -import { isInternalRequest, isNodeBuiltin, isPrimitive } from 'vite-node/utils' +import { isInternalRequest, isNodeBuiltin, isPrimitive, toFilePath } from 'vite-node/utils' import type { ViteNodeRunnerOptions } from 'vite-node' import { normalize, relative, resolve } from 'pathe' import { processError } from '@vitest/utils/error' @@ -41,6 +41,7 @@ export const packageCache = new Map() export const moduleCache = new ModuleCacheMap() export const mockMap: MockMap = new Map() export const fileMap = new FileMap() +const externalizeMap = new Map() export async function startViteNode(options: ContextExecutorOptions) { if (_viteNode) @@ -64,7 +65,7 @@ export interface ContextExecutorOptions { export async function startVitestExecutor(options: ContextExecutorOptions) { // @ts-expect-error injected untyped global - const state = () => globalThis.__vitest_worker__ || options.state + const state = (): WorkerGlobalState => globalThis.__vitest_worker__ || options.state const rpc = () => state().rpc const processExit = process.exit @@ -97,7 +98,18 @@ export async function startVitestExecutor(options: ContextExecutorOptions) { } return await createVitestExecutor({ - fetchModule(id) { + async fetchModule(id) { + if (externalizeMap.has(id)) + return { externalize: externalizeMap.get(id)! } + // always externalize Vitest because we import from there before running tests + // so we already have it cached by Node.js + if (id.includes(distDir)) { + const { path } = toFilePath(id, state().config.root) + const externalize = pathToFileURL(path).toString() + externalizeMap.set(id, externalize) + return { externalize } + } + return rpc().fetch(id, getTransformMode()) }, resolveId(id, importer) {