diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 1786957b97c3..0b32672bc6a2 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -310,8 +310,15 @@ export class ViteNodeRunner { enumerable: false, configurable: false, }) - // this prosxy is triggered only on exports.{name} and module.exports access + // 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, { + get: (target, p, receiver) => { + if (Reflect.has(target, p)) + return Reflect.get(target, p, receiver) + return Reflect.get(Object.prototype, p, receiver) + }, + getPrototypeOf: () => Object.prototype, set: (_, p, value) => { // treat "module.exports =" the same as "exports.default =" to not have nested "default.default", // so "exports.default" becomes the actual module diff --git a/test/cjs/src/prototype.cjs b/test/cjs/src/prototype.cjs new file mode 100644 index 000000000000..d52c76dfdcae --- /dev/null +++ b/test/cjs/src/prototype.cjs @@ -0,0 +1,7 @@ +exports.test = () => { + // eslint-disable-next-line no-prototype-builtins + return exports.hasOwnProperty('test') +} +exports.getPrototype = () => { + return Object.getPrototypeOf(exports) +} diff --git a/test/cjs/src/prototype.d.cts b/test/cjs/src/prototype.d.cts new file mode 100644 index 000000000000..4b913956249d --- /dev/null +++ b/test/cjs/src/prototype.d.cts @@ -0,0 +1,2 @@ +export const test: () => boolean +export const getPrototype: () => any diff --git a/test/cjs/test/prototype.test.ts b/test/cjs/test/prototype.test.ts new file mode 100644 index 000000000000..7cab942b87e9 --- /dev/null +++ b/test/cjs/test/prototype.test.ts @@ -0,0 +1,8 @@ +import { expect, it } from 'vitest' +import * as cjsExports from '../src/prototype.cjs' + +it('has object prototype', () => { + expect(cjsExports.getPrototype()).toBe(Object.prototype) + expect(() => cjsExports.test()).not.toThrow() + expect(cjsExports.test()).toBe(true) +}) diff --git a/test/esm/src/prototype.d.mts b/test/esm/src/prototype.d.mts new file mode 100644 index 000000000000..d424a6707c48 --- /dev/null +++ b/test/esm/src/prototype.d.mts @@ -0,0 +1 @@ +declare export const test: number diff --git a/test/esm/src/prototype.mjs b/test/esm/src/prototype.mjs new file mode 100644 index 000000000000..ae67ddada465 --- /dev/null +++ b/test/esm/src/prototype.mjs @@ -0,0 +1 @@ +export const test = 1 diff --git a/test/esm/test/prototype.spec.ts b/test/esm/test/prototype.spec.ts new file mode 100644 index 000000000000..aeda07fe9bb2 --- /dev/null +++ b/test/esm/test/prototype.spec.ts @@ -0,0 +1,8 @@ +import { expect, it } from 'vitest' +import * as exports from '../src/prototype.mjs' + +it('prototype is null', () => { + expect(Object.getPrototypeOf(exports)).toBe(null) + expect({}.hasOwnProperty).toBeTypeOf('function') + expect(exports.hasOwnProperty).toBeTypeOf('undefined') +}) diff --git a/test/esm/vite.config.ts b/test/esm/vite.config.ts index 286c3487b689..506d02b84dfd 100644 --- a/test/esm/vite.config.ts +++ b/test/esm/vite.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { deps: { - external: [/tslib/, /css-what/], + external: [/tslib/, /css-what/, /prototype\.mjs/], registerNodeLoader: true, }, },