From d36a3725309dd43b921bc1a0fb715b8b2bbdd0a0 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 16 Feb 2022 20:34:09 +0300 Subject: [PATCH 1/4] feat: allow reassigning `define` globals --- .../vitest/src/node/plugins/envReplacer.ts | 30 ------------- packages/vitest/src/node/plugins/index.ts | 44 ++++++++++++++----- 2 files changed, 33 insertions(+), 41 deletions(-) delete mode 100644 packages/vitest/src/node/plugins/envReplacer.ts diff --git a/packages/vitest/src/node/plugins/envReplacer.ts b/packages/vitest/src/node/plugins/envReplacer.ts deleted file mode 100644 index 28c7027e182d..000000000000 --- a/packages/vitest/src/node/plugins/envReplacer.ts +++ /dev/null @@ -1,30 +0,0 @@ -import MagicString from 'magic-string' -import type { Plugin } from 'vite' - -export const EnvReplacerPlugin = (): Plugin => { - return { - name: 'vitest:env-replacer', - enforce: 'pre', - transform(code) { - let s: MagicString | null = null - - const envs = code.matchAll(/\bimport\.meta\.env\b/g) - - for (const env of envs) { - s ||= new MagicString(code) - - const startIndex = env.index! - const endIndex = startIndex + env[0].length - - s.overwrite(startIndex, endIndex, 'process.env') - } - - if (s) { - return { - code: s.toString(), - map: s.generateMap({ hires: true }), - } - } - }, - } -} diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index 7f5e055b445c..826d10f9a73d 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -6,7 +6,6 @@ import { resolveApiConfig } from '../config' import { Vitest } from '../core' import { GlobalSetupPlugin } from './globalSetup' import { MocksPlugin } from './mock' -import { EnvReplacerPlugin } from './envReplacer' export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()): Promise { let haveStarted = false @@ -27,6 +26,30 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) const preOptions = deepMerge({}, options, viteConfig.test ?? {}) preOptions.api = resolveApiConfig(preOptions) + // make user defines globals, if possible + // so people can reassign them + for (const key in viteConfig.define) { + const val = viteConfig.define[key] + let replacement: any + try { + replacement = typeof val === 'string' ? JSON.parse(val) : val + } + catch { + // probably means it contains reference to some variable, + // like this: "__VAR__": "process.env.VAR" + continue + } + if (key.startsWith('import.meta.env.')) { + const envKey = key.slice('import.meta.env.'.length) + process.env[envKey] = replacement + delete viteConfig.define[key] + } + else if (!key.includes('.')) { + (globalThis as any)[key] = replacement + delete viteConfig.define[key] + } + } + return { // we are setting NODE_ENV when running CLI to 'test', // but it can be overridden @@ -37,6 +60,14 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) // setting this option can bypass that and fallback to cjs version mainFields: [], }, + define: { + 'process.env.NODE_ENV': 'process.env.NODE_ENV', + 'global.process.env.NODE_ENV': 'global.process.env.NODE_ENV', + 'globalThis.process.env.NODE_ENV': 'globalThis.process.env.NODE_ENV', + // so people can reassign envs at runtime + // import.meta.env.VITE_NAME = 'app' -> process.env.VITE_NAME = 'app' + 'import.meta.env': 'process.env', + }, server: { ...preOptions.api, open: preOptions.ui && preOptions.open @@ -69,15 +100,6 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) process.env.PROD ??= viteConfig.env.PROD ? '1' : '' process.env.DEV ??= viteConfig.env.DEV ? '1' : '' process.env.SSR ??= '1' - - // account for user env defines - for (const key in viteConfig.define) { - if (key.startsWith('import.meta.env.')) { - const val = viteConfig.define[key] - const envKey = key.slice('import.meta.env.'.length) - process.env[envKey] = typeof val === 'string' ? JSON.parse(val) : val - } - } }, async configureServer(server) { if (haveStarted) @@ -92,7 +114,7 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) await server.watcher.close() }, }, - EnvReplacerPlugin(), + // EnvReplacerPlugin(), MocksPlugin(), GlobalSetupPlugin(ctx), options.ui From 663aa38a64ce632809e815b4c43429bb391c15f8 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 16 Feb 2022 20:34:57 +0300 Subject: [PATCH 2/4] chore: remove comment --- packages/vitest/src/node/plugins/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index 826d10f9a73d..f70ed2a8747c 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -114,7 +114,6 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) await server.watcher.close() }, }, - // EnvReplacerPlugin(), MocksPlugin(), GlobalSetupPlugin(ctx), options.ui From c892ea617607d3d31b690caba244144527017608 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 17 Feb 2022 12:33:48 +0300 Subject: [PATCH 3/4] fix: move assigning defines to worker context --- packages/vitest/src/node/plugins/index.ts | 17 +++++-- packages/vitest/src/runtime/setup.ts | 9 ++++ packages/vitest/src/types/config.ts | 2 + test/core/test/define.test.ts | 57 +++++++++++++++++++++++ test/core/vitest.config.ts | 10 ++++ 5 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 test/core/test/define.test.ts diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index a9b5afc06cce..f9be67b3ca70 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -1,6 +1,6 @@ import type { Plugin as VitePlugin } from 'vite' import { configDefaults } from '../../defaults' -import type { UserConfig } from '../../types' +import type { ResolvedConfig, UserConfig } from '../../types' import { deepMerge, ensurePackageInstalled, notNullish } from '../../utils' import { resolveApiConfig } from '../config' import { Vitest } from '../core' @@ -26,8 +26,10 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) const preOptions = deepMerge({}, options, viteConfig.test ?? {}) preOptions.api = resolveApiConfig(preOptions) - // make user defines globals, if possible - // so people can reassign them + // store defines for globalThis to make them + // reassignable when running in worker in src/runtime/setup.ts + const defines: Record = {} + for (const key in viteConfig.define) { const val = viteConfig.define[key] let replacement: any @@ -44,12 +46,19 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) process.env[envKey] = replacement delete viteConfig.define[key] } + else if (key.startsWith('process.env.')) { + const envKey = key.slice('process.env.'.length) + process.env[envKey] = replacement + delete viteConfig.define[key] + } else if (!key.includes('.')) { - (globalThis as any)[key] = replacement + defines[key] = replacement delete viteConfig.define[key] } } + (options as ResolvedConfig).defines = defines + return { // we are setting NODE_ENV when running CLI to 'test', // but it can be overridden diff --git a/packages/vitest/src/runtime/setup.ts b/packages/vitest/src/runtime/setup.ts index 7b7f9cefe065..70f2f73d4d9e 100644 --- a/packages/vitest/src/runtime/setup.ts +++ b/packages/vitest/src/runtime/setup.ts @@ -8,6 +8,10 @@ import { rpc } from './rpc' let globalSetup = false export async function setupGlobalEnv(config: ResolvedConfig) { + // should be redeclared for each test + // if run with "threads: false" + setupDefines(config.defines) + if (globalSetup) return @@ -20,6 +24,11 @@ export async function setupGlobalEnv(config: ResolvedConfig) { (await import('../integrations/globals')).registerApiGlobally() } +function setupDefines(defines: Record) { + for (const key in defines) + (globalThis as any)[key] = defines[key] +} + export function setupConsoleLogSpy() { const stdout = new Writable({ write(data, encoding, callback) { diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index d245d7d69d45..61e0dcee63c2 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -324,5 +324,7 @@ export interface ResolvedConfig extends Omit, 'config' | 'f reporters: (Reporter | BuiltinReporters)[] + defines: Record + api?: ApiConfig } diff --git a/test/core/test/define.test.ts b/test/core/test/define.test.ts new file mode 100644 index 000000000000..6b371c8b543b --- /dev/null +++ b/test/core/test/define.test.ts @@ -0,0 +1,57 @@ +import { afterAll, expect, test } from 'vitest' + +declare let __DEFINE__: string +declare let __JSON__: any +declare let __MODE__: string +declare let SOME: { + VARIABLE: string + SOME: { + VARIABLE: string + } +} + +export {} + +// functions to test that they are not statically replaced +const get__DEFINE__ = () => __DEFINE__ +const get__JSON__ = () => __JSON__ +const get__MODE__ = () => __MODE__ + +const MODE = process.env.MODE + +afterAll(() => { + process.env.MODE = MODE +}) + +test('process.env.HELLO_PROCESS is defined on "defined" but exists on process.env', () => { + expect('HELLO_PROCESS' in process.env).toBe(true) + expect(process.env.HELLO_PROCESS).toBe('hello process') +}) + +test('can redeclare standard define', () => { + expect(get__DEFINE__()).toBe('defined') + __DEFINE__ = 'new defined' + expect(get__DEFINE__()).toBe('new defined') +}) + +test('can redeclare json object', () => { + expect(get__JSON__()).toEqual({ hello: 'world' }) + __JSON__ = { hello: 'test' } + const name = '__JSON__' + expect(get__JSON__()).toEqual({ hello: 'test' }) + expect((globalThis as any)[name]).toEqual({ hello: 'test' }) +}) + +test('reassigning __MODE__', () => { + const env = process.env.MODE + expect(get__MODE__()).toBe(env) + process.env.MODE = 'development' + expect(get__MODE__()).toBe('development') +}) + +test('dotted defines are processed by Vite, but cannot be reassigned', () => { + expect(SOME.VARIABLE).toBe('variable') + expect(SOME.SOME.VARIABLE).toBe('nested variable') + SOME.VARIABLE = 'new variable' + expect(SOME.VARIABLE).not.toBe('new variable') +}) diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index 3a2f0b1749d7..61dd6529fa35 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -19,6 +19,16 @@ export default defineConfig({ ], define: { 'import.meta.env.TEST_NAME': '"hello world"', + 'process.env.HELLO_PROCESS': '"hello process"', + // can reassign + '__DEFINE__': '"defined"', + '__JSON__': JSON.stringify({ hello: 'world' }), + // edge cases + // should not be available for reassigning as __MODE__ = 'test2' + // but can reassign with process.env.MODE = 'test2' + '__MODE__': 'process.env.MODE', + 'SOME.VARIABLE': '"variable"', + 'SOME.SOME.VARIABLE': '"nested variable"', }, test: { testTimeout: 2000, From f44af432a58cb99dde9427d622e15f8398633c3e Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 17 Feb 2022 12:39:45 +0300 Subject: [PATCH 4/4] chore: remove unnecessary export in test --- test/core/test/define.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/core/test/define.test.ts b/test/core/test/define.test.ts index 6b371c8b543b..15f87ba52e77 100644 --- a/test/core/test/define.test.ts +++ b/test/core/test/define.test.ts @@ -10,8 +10,6 @@ declare let SOME: { } } -export {} - // functions to test that they are not statically replaced const get__DEFINE__ = () => __DEFINE__ const get__JSON__ = () => __JSON__