diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 007d52919f3c..23b0ea2f6cc8 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -281,7 +281,6 @@ export class ViteNodeRunner { const requestStubs = this.options.requestStubs || DEFAULT_REQUEST_STUBS if (id in requestStubs) return requestStubs[id] - let { code: transformed, externalize } = await this.options.fetchModule(id) if (externalize) { @@ -294,6 +293,8 @@ export class ViteNodeRunner { if (transformed == null) throw new Error(`[vite-node] Failed to load "${id}" imported from ${callstack[callstack.length - 2]}`) + const { Object, Reflect, Symbol } = this.getContextPrimitives() + const modulePath = cleanUrl(moduleId) // disambiguate the `:/` on windows: see nodejs/node#31710 const href = pathToFileURL(modulePath).href @@ -398,6 +399,10 @@ export class ViteNodeRunner { return exports } + protected getContextPrimitives() { + return { Object, Reflect, Symbol } + } + protected async runModule(context: Record, transformed: string) { // add 'use strict' since ESM enables it by default const codeDefinition = `'use strict';async (${Object.keys(context).join(',')})=>{{` diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index f32e6b40810d..870d93be63e4 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -131,6 +131,12 @@ export class VitestExecutor extends ViteNodeRunner { public mocker: VitestMocker public externalModules?: ExternalModulesExecutor + private primitives: { + Object: typeof Object + Reflect: typeof Reflect + Symbol: typeof Symbol + } + constructor(public options: ExecuteOptions) { super(options) @@ -147,6 +153,11 @@ export class VitestExecutor extends ViteNodeRunner { '/@vite/client': clientStub, '@vite/client': clientStub, } + this.primitives = { + Object, + Reflect, + Symbol, + } } else { this.externalModules = new ExternalModulesExecutor(options.context, options.findNearestPackageData || (() => Promise.resolve({}))) @@ -155,9 +166,14 @@ export class VitestExecutor extends ViteNodeRunner { '/@vite/client': clientStub, '@vite/client': clientStub, } + this.primitives = vm.runInContext('({ Object, Reflect, Symbol })', options.context) } } + protected getContextPrimitives() { + return this.primitives + } + get state() { return this.options.state } diff --git a/packages/vitest/src/runtime/external-executor.ts b/packages/vitest/src/runtime/external-executor.ts index af3bda492a0d..ed264e63a822 100644 --- a/packages/vitest/src/runtime/external-executor.ts +++ b/packages/vitest/src/runtime/external-executor.ts @@ -102,6 +102,9 @@ export class ExternalModulesExecutor { private esmLinkMap = new WeakMap>() private Module: typeof _Module + private primitives: { + Object: typeof Object + } constructor(private context: vm.Context, private findNearestPackageData: RuntimeRPC['findNearestPackageData']) { this.context = context @@ -110,7 +113,7 @@ export class ExternalModulesExecutor { const executor = this this.Module = class Module { - exports = {} + exports: any isPreloading = false require: NodeRequire id: string @@ -119,11 +122,10 @@ export class ExternalModulesExecutor { parent: null | Module | undefined children: Module[] = [] path: string - paths: string[] + paths: string[] = [] constructor(id: string, parent?: Module) { - this.exports = {} - this.isPreloading = false + this.exports = executor.primitives.Object.create(Object.prototype) this.require = Module.createRequire(id) // in our case the path should always be resolved already this.path = dirname(id) @@ -131,8 +133,6 @@ export class ExternalModulesExecutor { this.filename = id this.loaded = false this.parent = parent - this.children = [] - this.paths = [] } _compile(code: string, filename: string) { @@ -199,6 +199,8 @@ export class ExternalModulesExecutor { this.extensions['.js'] = this.requireJs this.extensions['.json'] = this.requireJson + + this.primitives = vm.runInContext('({ Object })', context) } private requireJs = (m: NodeModule, filename: string) => { @@ -226,6 +228,7 @@ export class ExternalModulesExecutor { } private async wrapSynteticModule(identifier: string, format: 'esm' | 'builtin' | 'cjs', exports: Record) { + // TODO: technically module should be parsed to find static exports, implement for #2854 const moduleKeys = Object.keys(exports) if (format !== 'esm' && !moduleKeys.includes('default')) moduleKeys.push('default')