From 786aa3e8281335fdaac7b8e20f95f19c16514de2 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 1 Jun 2023 16:46:51 +0200 Subject: [PATCH 01/12] feat!: remove transformMode option --- docs/config/index.md | 36 ----------- examples/solid/vite.config.mjs | 3 - examples/vue-jsx/vite.config.ts | 3 - .../src/integrations/env/edge-runtime.ts | 1 + .../vitest/src/integrations/env/happy-dom.ts | 1 + packages/vitest/src/integrations/env/index.ts | 22 ++++++- packages/vitest/src/integrations/env/jsdom.ts | 1 + packages/vitest/src/integrations/env/node.ts | 1 + packages/vitest/src/node/pools/rpc.ts | 7 +-- packages/vitest/src/runtime/child.ts | 8 +-- packages/vitest/src/runtime/entry.ts | 8 +-- packages/vitest/src/runtime/execute.ts | 17 +++-- packages/vitest/src/runtime/setup.node.ts | 27 ++------ packages/vitest/src/runtime/worker.ts | 6 +- packages/vitest/src/types/config.ts | 21 ------- packages/vitest/src/types/general.ts | 1 + packages/vitest/src/types/rpc.ts | 14 ++++- packages/vitest/src/utils/base.ts | 8 +-- pnpm-lock.yaml | 14 +++++ .../{define.test.ts => define-ssr.test.ts} | 0 test/core/test/define-web.test.ts | 63 +++++++++++++++++++ test/resolve/package.json | 1 + test/resolve/vitest.config.ts | 8 +-- 23 files changed, 151 insertions(+), 120 deletions(-) rename test/core/test/{define.test.ts => define-ssr.test.ts} (100%) create mode 100644 test/core/test/define-web.test.ts diff --git a/docs/config/index.md b/docs/config/index.md index b5b32314a3cf..6e70eb845bcd 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -1120,42 +1120,6 @@ Will call [`vi.unstubAllEnvs`](/api/vi#vi-unstuballenvs) before each test. Will call [`vi.unstubAllGlobals`](/api/vi#vi-unstuballglobals) before each test. -### transformMode - -- **Type:** `{ web?, ssr? }` - -Determine the transform method of modules - -#### transformMode.ssr - -- **Type:** `RegExp[]` -- **Default:** `[/\.([cm]?[jt]sx?|json)$/]` - -Use SSR transform pipeline for the specified files.
-Vite plugins will receive `ssr: true` flag when processing those files. - -#### transformMode.web - -- **Type:** `RegExp[]` -- **Default:** *modules other than those specified in `transformMode.ssr`* - -First do a normal transform pipeline (targeting browser), then do a SSR rewrite to run the code in Node.
-Vite plugins will receive `ssr: false` flag when processing those files. - -When you use JSX as component models other than React (e.g. Vue JSX or SolidJS), you might want to config as following to make `.tsx` / `.jsx` transformed as client-side components: - -```ts -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - transformMode: { - web: [/\.[jt]sx$/], - }, - }, -}) -``` - ### snapshotFormat - **Type:** `PrettyFormatOptions` diff --git a/examples/solid/vite.config.mjs b/examples/solid/vite.config.mjs index 9e837c7e0dd6..6e5124f131b3 100644 --- a/examples/solid/vite.config.mjs +++ b/examples/solid/vite.config.mjs @@ -7,9 +7,6 @@ import solid from 'vite-plugin-solid' export default defineConfig({ test: { environment: 'jsdom', - transformMode: { - web: [/.[jt]sx?/], - }, threads: false, isolate: false, }, diff --git a/examples/vue-jsx/vite.config.ts b/examples/vue-jsx/vite.config.ts index c71fb1b68c63..e957af21263b 100644 --- a/examples/vue-jsx/vite.config.ts +++ b/examples/vue-jsx/vite.config.ts @@ -7,8 +7,5 @@ export default defineConfig({ test: { globals: true, environment: 'jsdom', - transformMode: { - web: [/.[tj]sx$/], - }, }, }) diff --git a/packages/vitest/src/integrations/env/edge-runtime.ts b/packages/vitest/src/integrations/env/edge-runtime.ts index e65db7fbf3ca..9d072eb0aaec 100644 --- a/packages/vitest/src/integrations/env/edge-runtime.ts +++ b/packages/vitest/src/integrations/env/edge-runtime.ts @@ -4,6 +4,7 @@ import { populateGlobal } from './utils' export default ({ name: 'edge-runtime', + transformMode: 'ssr', async setup(global) { const { EdgeVM } = await importModule('@edge-runtime/vm') as typeof import('@edge-runtime/vm') const vm = new EdgeVM({ diff --git a/packages/vitest/src/integrations/env/happy-dom.ts b/packages/vitest/src/integrations/env/happy-dom.ts index 328139fc7ae2..0a0cb1cc89b0 100644 --- a/packages/vitest/src/integrations/env/happy-dom.ts +++ b/packages/vitest/src/integrations/env/happy-dom.ts @@ -4,6 +4,7 @@ import { populateGlobal } from './utils' export default ({ name: 'happy-dom', + transformMode: 'web', async setup(global) { // happy-dom v3 introduced a breaking change to Window, but // provides GlobalWindow as a way to use previous behaviour diff --git a/packages/vitest/src/integrations/env/index.ts b/packages/vitest/src/integrations/env/index.ts index 4084cb71c6ec..875d45bbf8f9 100644 --- a/packages/vitest/src/integrations/env/index.ts +++ b/packages/vitest/src/integrations/env/index.ts @@ -1,4 +1,6 @@ -import type { VitestEnvironment } from '../../types/config' +import type { BuiltinEnvironment, VitestEnvironment } from '../../types/config' +import type { VitestExecutor } from '../../node' +import type { Environment } from '../../types' import node from './node' import jsdom from './jsdom' import happy from './happy-dom' @@ -19,6 +21,10 @@ export const envPackageNames: Record, 'edge-runtime': '@edge-runtime/vm', } +function isBuiltinEnvironment(env: VitestEnvironment): env is BuiltinEnvironment { + return env in environments +} + export function getEnvPackageName(env: VitestEnvironment) { if (env === 'node') return null @@ -26,3 +32,17 @@ export function getEnvPackageName(env: VitestEnvironment) { return (envPackageNames as any)[env] return `vitest-environment-${env}` } + +export async function loadEnvironment(name: VitestEnvironment, executor: VitestExecutor): Promise { + if (isBuiltinEnvironment(name)) + return environments[name] + const packageId = (name[0] === '.' || name[0] === '/') ? name : `vitest-environment-${name}` + const pkg = await executor.executeId(packageId) + if (!pkg || !pkg.default || typeof pkg.default !== 'object' || typeof pkg.default.setup !== 'function') { + throw new Error( + `Environment "${name}" is not a valid environment. ` + + `Package "vitest-environment-${name}" should have default export with "setup" method.`, + ) + } + return pkg.default +} diff --git a/packages/vitest/src/integrations/env/jsdom.ts b/packages/vitest/src/integrations/env/jsdom.ts index a9c9ed784330..f197e9c3444e 100644 --- a/packages/vitest/src/integrations/env/jsdom.ts +++ b/packages/vitest/src/integrations/env/jsdom.ts @@ -28,6 +28,7 @@ function catchWindowErrors(window: Window) { export default ({ name: 'jsdom', + transformMode: 'web', async setup(global, { jsdom = {} }) { const { CookieJar, diff --git a/packages/vitest/src/integrations/env/node.ts b/packages/vitest/src/integrations/env/node.ts index e984c343dc23..ef1a01cb1523 100644 --- a/packages/vitest/src/integrations/env/node.ts +++ b/packages/vitest/src/integrations/env/node.ts @@ -3,6 +3,7 @@ import type { Environment } from '../../types' export default ({ name: 'node', + transformMode: 'ssr', async setup(global) { global.console.Console = Console return { diff --git a/packages/vitest/src/node/pools/rpc.ts b/packages/vitest/src/node/pools/rpc.ts index 0b67d50e4b8a..ad05fbcad48d 100644 --- a/packages/vitest/src/node/pools/rpc.ts +++ b/packages/vitest/src/node/pools/rpc.ts @@ -1,6 +1,5 @@ import type { RawSourceMap } from 'vite-node' import type { RuntimeRPC } from '../../types' -import { getEnvironmentTransformMode } from '../../utils/base' import type { WorkspaceProject } from '../workspace' export function createMethodsRPC(project: WorkspaceProject): RuntimeRPC { @@ -25,12 +24,10 @@ export function createMethodsRPC(project: WorkspaceProject): RuntimeRPC { const r = await project.vitenode.transformRequest(id) return r?.map as RawSourceMap | undefined }, - fetch(id, environment) { - const transformMode = getEnvironmentTransformMode(project.config, environment) + fetch(id, transformMode) { return project.vitenode.fetchModule(id, transformMode) }, - resolveId(id, importer, environment) { - const transformMode = getEnvironmentTransformMode(project.config, environment) + resolveId(id, importer, transformMode) { return project.vitenode.resolveId(id, importer, transformMode) }, onPathsCollected(paths) { diff --git a/packages/vitest/src/runtime/child.ts b/packages/vitest/src/runtime/child.ts index 6d3705b56cbc..3dcd5874acfe 100644 --- a/packages/vitest/src/runtime/child.ts +++ b/packages/vitest/src/runtime/child.ts @@ -11,7 +11,7 @@ import { rpcDone } from './rpc' import { setupInspect } from './inspector' function init(ctx: ChildContext) { - const { config } = ctx + const { config, environment } = ctx process.env.VITEST_WORKER_ID = '1' process.env.VITEST_POOL_ID = '1' @@ -22,7 +22,7 @@ function init(ctx: ChildContext) { }) // @ts-expect-error untyped global - globalThis.__vitest_environment__ = config.environment + globalThis.__vitest_environment__ = environment.name // @ts-expect-error I know what I am doing :P globalThis.__vitest_worker__ = { ctx, @@ -77,8 +77,8 @@ export async function run(ctx: ChildContext) { try { init(ctx) - const { run, executor } = await startViteNode(ctx) - await run(ctx.files, ctx.config, ctx.environment, executor) + const { run, executor, environment } = await startViteNode(ctx) + await run(ctx.files, ctx.config, { ...ctx.environment, environment }, executor) await rpcDone() } finally { diff --git a/packages/vitest/src/runtime/entry.ts b/packages/vitest/src/runtime/entry.ts index 45586375c4b1..a2c9edaeaa76 100644 --- a/packages/vitest/src/runtime/entry.ts +++ b/packages/vitest/src/runtime/entry.ts @@ -2,7 +2,7 @@ import { performance } from 'node:perf_hooks' import type { VitestRunner, VitestRunnerConstructor } from '@vitest/runner' import { startTests } from '@vitest/runner' import { resolve } from 'pathe' -import type { ContextTestEnvironment, ResolvedConfig } from '../types' +import type { ResolvedConfig, ResolvedTestEnvironment } from '../types' import { getWorkerState, resetModules } from '../utils' import { vi } from '../integrations/vi' import { distDir } from '../paths' @@ -89,7 +89,7 @@ async function getTestRunner(config: ResolvedConfig, executor: VitestExecutor): } // browser shouldn't call this! -export async function run(files: string[], config: ResolvedConfig, environment: ContextTestEnvironment, executor: VitestExecutor): Promise { +export async function run(files: string[], config: ResolvedConfig, environment: ResolvedTestEnvironment, executor: VitestExecutor): Promise { const workerState = getWorkerState() await setupGlobalEnv(config) @@ -104,11 +104,11 @@ export async function run(files: string[], config: ResolvedConfig, environment: workerState.durations.prepare = performance.now() - workerState.durations.prepare // @ts-expect-error untyped global - globalThis.__vitest_environment__ = environment + globalThis.__vitest_environment__ = environment.name workerState.durations.environment = performance.now() - await withEnv(environment.name, environment.options || config.environmentOptions || {}, executor, async () => { + await withEnv(environment, environment.options || config.environmentOptions || {}, async () => { workerState.durations.environment = performance.now() - workerState.durations.environment for (const file of files) { diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index de7cf1550c07..e6e377f92c14 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -6,8 +6,9 @@ import { normalize, relative, resolve } from 'pathe' import { processError } from '@vitest/utils/error' import type { MockMap } from '../types/mocker' import { getCurrentEnvironment, getWorkerState } from '../utils/global' -import type { ContextRPC, ContextTestEnvironment, ResolvedConfig } from '../types' +import type { ContextRPC, Environment, ResolvedConfig, ResolvedTestEnvironment } from '../types' import { distDir } from '../paths' +import { loadEnvironment } from '../integrations/env' import { VitestMocker } from './mocker' import { rpc } from './rpc' @@ -25,8 +26,9 @@ export async function createVitestExecutor(options: ExecuteOptions) { } let _viteNode: { - run: (files: string[], config: ResolvedConfig, environment: ContextTestEnvironment, executor: VitestExecutor) => Promise + run: (files: string[], config: ResolvedConfig, environment: ResolvedTestEnvironment, executor: VitestExecutor) => Promise executor: VitestExecutor + environment: Environment } export const moduleCache = new ModuleCacheMap() @@ -61,12 +63,14 @@ export async function startViteNode(ctx: ContextRPC) { process.on('uncaughtException', e => catchError(e, 'Uncaught Exception')) process.on('unhandledRejection', e => catchError(e, 'Unhandled Rejection')) + let transformMode: 'ssr' | 'web' = 'ssr' + const executor = await createVitestExecutor({ fetchModule(id) { - return rpc().fetch(id, ctx.environment.name) + return rpc().fetch(id, transformMode) }, resolveId(id, importer) { - return rpc().resolveId(id, importer, ctx.environment.name) + return rpc().resolveId(id, importer, transformMode) }, moduleCache, mockMap, @@ -76,9 +80,12 @@ export async function startViteNode(ctx: ContextRPC) { base: config.base, }) + const environment = await loadEnvironment(ctx.environment.name, executor) + transformMode = environment.transformMode ?? 'ssr' + const { run } = await import(pathToFileURL(resolve(distDir, 'entry.js')).href) - _viteNode = { run, executor } + _viteNode = { run, executor, environment } return _viteNode } diff --git a/packages/vitest/src/runtime/setup.node.ts b/packages/vitest/src/runtime/setup.node.ts index 789a6e69beb7..15625fcad1f7 100644 --- a/packages/vitest/src/runtime/setup.node.ts +++ b/packages/vitest/src/runtime/setup.node.ts @@ -2,8 +2,7 @@ import { createRequire } from 'node:module' import { isatty } from 'node:tty' import { installSourcemapsSupport } from 'vite-node/source-map' import { createColors, setupColors } from '@vitest/utils' -import { environments } from '../integrations/env' -import type { Environment, ResolvedConfig } from '../types' +import type { EnvironmentOptions, ResolvedConfig, ResolvedTestEnvironment } from '../types' import { VitestSnapshotEnvironment } from '../integrations/snapshot/environments/node' import { getSafeTimers, getWorkerState } from '../utils' import * as VitestIndex from '../index' @@ -11,7 +10,6 @@ import { RealDate } from '../integrations/mock/date' import { expect } from '../integrations/chai' import { rpc } from './rpc' import { setupCommonEnv } from './setup.common' -import type { VitestExecutor } from './execute' // this should only be used in Node let globalSetup = false @@ -157,30 +155,17 @@ export async function setupConsoleLogSpy() { }) } -async function loadEnvironment(name: string, executor: VitestExecutor) { - const pkg = await executor.executeId(`vitest-environment-${name}`) - if (!pkg || !pkg.default || typeof pkg.default !== 'object' || typeof pkg.default.setup !== 'function') { - throw new Error( - `Environment "${name}" is not a valid environment. ` - + `Package "vitest-environment-${name}" should have default export with "setup" method.`, - ) - } - return pkg.default -} - export async function withEnv( - name: ResolvedConfig['environment'], - options: ResolvedConfig['environmentOptions'], - executor: VitestExecutor, + { environment, name }: ResolvedTestEnvironment, + options: EnvironmentOptions, fn: () => Promise, ) { - const config: Environment = (environments as any)[name] || await loadEnvironment(name, executor) // @ts-expect-error untyped global - globalThis.__vitest_environment__ = config.name || name + globalThis.__vitest_environment__ = name expect.setState({ - environment: config.name || name || 'node', + environment: name, }) - const env = await config.setup(globalThis, options) + const env = await environment.setup(globalThis, options) try { await fn() } diff --git a/packages/vitest/src/runtime/worker.ts b/packages/vitest/src/runtime/worker.ts index 0db3280c364a..ce2fa1a8b5f4 100644 --- a/packages/vitest/src/runtime/worker.ts +++ b/packages/vitest/src/runtime/worker.ts @@ -24,7 +24,7 @@ function init(ctx: WorkerContext) { }) // @ts-expect-error untyped global - globalThis.__vitest_environment__ = config.environment + globalThis.__vitest_environment__ = config.environment.name // @ts-expect-error I know what I am doing :P globalThis.__vitest_worker__ = { ctx, @@ -62,8 +62,8 @@ export async function run(ctx: WorkerContext) { try { init(ctx) - const { run, executor } = await startViteNode(ctx) - await run(ctx.files, ctx.config, ctx.environment, executor) + const { run, executor, environment } = await startViteNode(ctx) + await run(ctx.files, ctx.config, { ...ctx.environment, environment }, executor) await rpcDone() } finally { diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 6c2a79706b69..76a0c58b66dd 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -448,27 +448,6 @@ export interface InlineConfig { */ uiBase?: string - /** - * Determine the transform method of modules - */ - transformMode?: { - /** - * Use SSR transform pipeline for the specified files. - * Vite plugins will receive `ssr: true` flag when processing those files. - * - * @default [/\.([cm]?[jt]sx?|json)$/] - */ - ssr?: RegExp[] - /** - * First do a normal transform pipeline (targeting browser), - * then then do a SSR rewrite to run the code in Node. - * Vite plugins will receive `ssr: false` flag when processing those files. - * - * @default other than `ssr` - */ - web?: RegExp[] - } - /** * Format options for snapshot testing. */ diff --git a/packages/vitest/src/types/general.ts b/packages/vitest/src/types/general.ts index 1af33776a435..7e8da33bdbcd 100644 --- a/packages/vitest/src/types/general.ts +++ b/packages/vitest/src/types/general.ts @@ -23,6 +23,7 @@ export interface EnvironmentReturn { export interface Environment { name: string + transformMode?: 'web' | 'ssr' setup(global: any, options: Record): Awaitable } diff --git a/packages/vitest/src/types/rpc.ts b/packages/vitest/src/types/rpc.ts index 1e8bbe93d2ac..536eb3fc4274 100644 --- a/packages/vitest/src/types/rpc.ts +++ b/packages/vitest/src/types/rpc.ts @@ -1,14 +1,16 @@ import type { FetchResult, RawSourceMap, ViteNodeResolveId } from 'vite-node' import type { CancelReason } from '@vitest/runner' import type { EnvironmentOptions, ResolvedConfig, VitestEnvironment } from './config' -import type { UserConsoleLog } from './general' +import type { Environment, UserConsoleLog } from './general' import type { SnapshotResult } from './snapshot' import type { File, TaskResultPack } from './tasks' import type { AfterSuiteRunMeta } from './worker' +type TransformMode = 'web' | 'ssr' + export interface RuntimeRPC { - fetch: (id: string, environment: VitestEnvironment) => Promise - resolveId: (id: string, importer: string | undefined, environment: VitestEnvironment) => Promise + fetch: (id: string, environment: TransformMode) => Promise + resolveId: (id: string, importer: string | undefined, environment: TransformMode) => Promise getSourceMap: (id: string, force?: boolean) => Promise onFinished: (files: File[], errors?: unknown[]) => void @@ -35,6 +37,12 @@ export interface ContextTestEnvironment { options: EnvironmentOptions | null } +export interface ResolvedTestEnvironment extends ContextTestEnvironment { + name: VitestEnvironment + environment: Environment + options: EnvironmentOptions | null +} + export interface ContextRPC { config: ResolvedConfig files: string[] diff --git a/packages/vitest/src/utils/base.ts b/packages/vitest/src/utils/base.ts index aa111023d85c..010a1b0e3107 100644 --- a/packages/vitest/src/utils/base.ts +++ b/packages/vitest/src/utils/base.ts @@ -1,4 +1,4 @@ -import type { Arrayable, Nullable, ResolvedConfig, VitestEnvironment } from '../types' +import type { Arrayable, Nullable } from '../types' export { notNullish, getCallLastIndex } from '@vitest/utils' @@ -128,12 +128,6 @@ export function stdout(): NodeJS.WriteStream { return console._stdout || process.stdout } -export function getEnvironmentTransformMode(config: ResolvedConfig, environment: VitestEnvironment) { - if (!config.deps?.experimentalOptimizer?.ssr?.enabled && !config.deps?.experimentalOptimizer?.web?.enabled) - return undefined - return (environment === 'happy-dom' || environment === 'jsdom') ? 'web' : 'ssr' -} - // AggregateError is supported in Node.js 15.0.0+ class AggregateErrorPonyfill extends Error { errors: unknown[] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84a3982440cf..9de2b5d95906 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1820,6 +1820,9 @@ importers: test/resolve: devDependencies: + happy-dom: + specifier: ^9.20.3 + version: 9.20.3 vitest: specifier: workspace:* version: link:../../packages/vitest @@ -16056,6 +16059,17 @@ packages: whatwg-mimetype: 3.0.0 dev: true + /happy-dom@9.20.3: + resolution: {integrity: sha512-eBsgauT435fXFvQDNcmm5QbGtYzxEzOaX35Ia+h6yP/wwa4xSWZh1CfP+mGby8Hk6Xu59mTkpyf72rUXHNxY7A==} + dependencies: + css.escape: 1.5.1 + entities: 4.5.0 + iconv-lite: 0.6.3 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + dev: true + /has-ansi@2.0.0: resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} engines: {node: '>=0.10.0'} diff --git a/test/core/test/define.test.ts b/test/core/test/define-ssr.test.ts similarity index 100% rename from test/core/test/define.test.ts rename to test/core/test/define-ssr.test.ts diff --git a/test/core/test/define-web.test.ts b/test/core/test/define-web.test.ts new file mode 100644 index 000000000000..3af47b384850 --- /dev/null +++ b/test/core/test/define-web.test.ts @@ -0,0 +1,63 @@ +// @vitest-environment jsdom + +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 + } +} + +// functions to test that they are not statically replaced +function get__DEFINE__() { + return __DEFINE__ +} +function get__JSON__() { + return __JSON__ +} +function get__MODE__() { + return __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 complicated __MODE__', () => { + const env = process.env.MODE + expect(get__MODE__()).toBe(env) + process.env.MODE = 'development' + expect(get__MODE__()).not.toBe('development') +}) + +test('dotted defines can be reassigned', () => { + expect(SOME.VARIABLE).toBe('variable') + expect(SOME.SOME.VARIABLE).toBe('nested variable') + SOME.VARIABLE = 'new variable' + expect(SOME.VARIABLE).toBe('new variable') +}) diff --git a/test/resolve/package.json b/test/resolve/package.json index 9aae7475f41c..3d0613a16e6f 100644 --- a/test/resolve/package.json +++ b/test/resolve/package.json @@ -6,6 +6,7 @@ "coverage": "vitest run --coverage" }, "devDependencies": { + "happy-dom": "^9.20.3", "vitest": "workspace:*" } } diff --git a/test/resolve/vitest.config.ts b/test/resolve/vitest.config.ts index 38ae8d564abf..f18228bd9474 100644 --- a/test/resolve/vitest.config.ts +++ b/test/resolve/vitest.config.ts @@ -2,10 +2,10 @@ import { defineConfig } from 'vite' export default defineConfig({ test: { - transformMode: { - web: [/web\.test\.ts/], - ssr: [/ssr\.test\.ts/], - }, + environmentMatchGlobs: [ + ['**/web.test.ts', 'happy-dom'], + ['**/ssr.test.ts', 'node'], + ], deps: { external: [/pkg-/], }, From 56b597e55cf1793941d89fcd3a51567a4095d639 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 2 Jun 2023 18:10:06 +0200 Subject: [PATCH 02/12] chore: inject react-refresh --- packages/vitest/package.json | 1 + packages/vitest/setup/react-refresh.js | 8 +++++ .../src/node/plugins/commonPluginsHandler.ts | 29 +++++++++++++++++++ packages/vitest/src/node/plugins/index.ts | 2 ++ packages/vitest/src/node/plugins/workspace.ts | 2 ++ packages/vitest/src/runtime/execute.ts | 1 + packages/vitest/src/runtime/loader.ts | 5 ++-- packages/vitest/src/types/rpc.ts | 1 + packages/vitest/src/utils/config-helpers.ts | 22 ++++++++++++++ packages/web-worker/src/utils.ts | 6 ++-- 10 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 packages/vitest/setup/react-refresh.js create mode 100644 packages/vitest/src/node/plugins/commonPluginsHandler.ts diff --git a/packages/vitest/package.json b/packages/vitest/package.json index b5acbf00a7f6..77f41c8e0db6 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -83,6 +83,7 @@ }, "files": [ "dist", + "setup", "bin", "*.d.ts", "*.d.cts", diff --git a/packages/vitest/setup/react-refresh.js b/packages/vitest/setup/react-refresh.js new file mode 100644 index 000000000000..d1095c2f3475 --- /dev/null +++ b/packages/vitest/setup/react-refresh.js @@ -0,0 +1,8 @@ +if (typeof window !== 'undefined') { + const { default: RefreshRuntime } = await import('/@react-refresh') + + RefreshRuntime.injectIntoGlobalHook(window) + window.$RefreshReg$ = () => {} + window.$RefreshSig$ = () => type => type + window.__vite_plugin_react_preamble_installed__ = true +} diff --git a/packages/vitest/src/node/plugins/commonPluginsHandler.ts b/packages/vitest/src/node/plugins/commonPluginsHandler.ts new file mode 100644 index 000000000000..abfdec984328 --- /dev/null +++ b/packages/vitest/src/node/plugins/commonPluginsHandler.ts @@ -0,0 +1,29 @@ +import type { Plugin } from 'vite' +import { resolve } from 'pathe' +import { hasVitePlugin } from '../../utils/config-helpers' +import { distDir } from '../../paths' + +export function CommonPluginsHandler(): Plugin { + return { + name: 'vitest:common-plugins-enforcer', + enforce: 'post', + config: { + order: 'post', + async handler(config) { + const plugins = (config.plugins || []) + const hasReact = await hasVitePlugin(plugins, 'vite:react-babel') + if (hasReact) { + return { + test: { + deps: { + inline: [/vitest\/setup\/react-refresh.js/], + }, + // Since this is an official plugin, it should be OK to inline reac-refresh HRM logic + setupFiles: [resolve(distDir, '../setup/react-refresh.js')], + }, + } + } + }, + }, + } +} diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index f5d17675a420..fcb6c8c9ac9c 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -14,6 +14,7 @@ import { CoverageTransform } from './coverageTransform' import { MocksPlugin } from './mocks' import { deleteDefineConfig, resolveOptimizerConfig } from './utils' import { VitestResolver } from './vitestResolver' +import { CommonPluginsHandler } from './commonPluginsHandler' export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('test')): Promise { const userConfig = deepMerge({}, options) as UserConfig @@ -178,6 +179,7 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t : null, MocksPlugin(), VitestResolver(ctx), + CommonPluginsHandler(), ] .filter(notNullish) } diff --git a/packages/vitest/src/node/plugins/workspace.ts b/packages/vitest/src/node/plugins/workspace.ts index 69340e681afe..9fecece9f46e 100644 --- a/packages/vitest/src/node/plugins/workspace.ts +++ b/packages/vitest/src/node/plugins/workspace.ts @@ -12,6 +12,7 @@ import { GlobalSetupPlugin } from './globalSetup' import { MocksPlugin } from './mocks' import { deleteDefineConfig, resolveOptimizerConfig } from './utils' import { VitestResolver } from './vitestResolver' +import { CommonPluginsHandler } from './commonPluginsHandler' interface WorkspaceOptions extends UserWorkspaceConfig { root?: string @@ -123,5 +124,6 @@ export function WorkspaceVitestPlugin(project: WorkspaceProject, options: Worksp GlobalSetupPlugin(project, project.ctx.logger), MocksPlugin(), VitestResolver(project.ctx), + CommonPluginsHandler(), ] } diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index e6e377f92c14..954afe7c4abd 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -81,6 +81,7 @@ export async function startViteNode(ctx: ContextRPC) { }) const environment = await loadEnvironment(ctx.environment.name, executor) + ctx.environment.environment = environment transformMode = environment.transformMode ?? 'ssr' const { run } = await import(pathToFileURL(resolve(distDir, 'entry.js')).href) diff --git a/packages/vitest/src/runtime/loader.ts b/packages/vitest/src/runtime/loader.ts index f226ff761bbb..91bcd2d739ce 100644 --- a/packages/vitest/src/runtime/loader.ts +++ b/packages/vitest/src/runtime/loader.ts @@ -44,13 +44,14 @@ export const resolve: Resolver = async (url, context, next) => { const { parentURL } = context const state = getWorkerState() const resolver = state?.rpc.resolveId + const environment = state?.ctx.environment.environment - if (!parentURL || isNodeBuiltin(url) || !resolver) + if (!parentURL || isNodeBuiltin(url) || !resolver || !environment) return next(url, context, next) const id = normalizeModuleId(url) const importer = normalizeModuleId(parentURL) - const resolved = await resolver(id, importer, state.ctx.environment.name) + const resolved = await resolver(id, importer, environment.transformMode ?? 'ssr') let result: ResolveResult let filepath: string diff --git a/packages/vitest/src/types/rpc.ts b/packages/vitest/src/types/rpc.ts index 536eb3fc4274..9a14b253ab66 100644 --- a/packages/vitest/src/types/rpc.ts +++ b/packages/vitest/src/types/rpc.ts @@ -34,6 +34,7 @@ export interface RunnerRPC { export interface ContextTestEnvironment { name: VitestEnvironment + environment?: Environment options: EnvironmentOptions | null } diff --git a/packages/vitest/src/utils/config-helpers.ts b/packages/vitest/src/utils/config-helpers.ts index bd5f0b50fc97..a5d496a7ab7b 100644 --- a/packages/vitest/src/utils/config-helpers.ts +++ b/packages/vitest/src/utils/config-helpers.ts @@ -1,3 +1,4 @@ +import type { PluginOption } from 'vite' import type { BenchmarkBuiltinReporters, BuiltinReporters } from '../node/reporters' interface PotentialConfig { @@ -13,3 +14,24 @@ export function getOutputFile(config: PotentialConfig | undefined, reporter: Bui return config.outputFile[reporter] } + +async function isVitePlugin(plugin: PluginOption, name: string): Promise { + if (!plugin) + return false + if (Array.isArray(plugin)) + return hasVitePlugin(plugin, name) + if (plugin instanceof Promise) + return isVitePlugin(await plugin, name) + if (typeof plugin === 'object') + return plugin.name === name + return false +} + +export async function hasVitePlugin(plugins: PluginOption[], name: string): Promise { + for (const plugin of plugins) { + if (await isVitePlugin(plugin, name)) + return true + } + + return false +} diff --git a/packages/web-worker/src/utils.ts b/packages/web-worker/src/utils.ts index 9a59d4101e5c..758b357a46b4 100644 --- a/packages/web-worker/src/utils.ts +++ b/packages/web-worker/src/utils.ts @@ -61,14 +61,14 @@ export function createMessageEvent(data: any, transferOrOptions: StructuredSeria } export function getRunnerOptions(): any { - const { config, ctx, rpc, mockMap, moduleCache } = getWorkerState() + const { config, rpc, mockMap, moduleCache } = getWorkerState() return { fetchModule(id: string) { - return rpc.fetch(id, ctx.environment.name) + return rpc.fetch(id, 'web') }, resolveId(id: string, importer?: string) { - return rpc.resolveId(id, importer, ctx.environment.name) + return rpc.resolveId(id, importer, 'web') }, moduleCache, mockMap, From c7d92779a6c7c6e44fcbc99403c0b980a4e2718c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 6 Jun 2023 13:17:02 +0200 Subject: [PATCH 03/12] chore: implement testTransformMode --- docs/config/index.md | 23 +++++++++++++++ packages/vitest/setup/react-refresh.js | 8 ----- packages/vitest/src/node/config.ts | 2 ++ .../src/node/plugins/commonPluginsHandler.ts | 29 ------------------- packages/vitest/src/node/plugins/index.ts | 2 -- packages/vitest/src/node/plugins/workspace.ts | 2 -- packages/vitest/src/node/pools/child.ts | 2 +- packages/vitest/src/runtime/execute.ts | 4 +-- packages/vitest/src/types/config.ts | 23 +++++++++++++++ packages/vitest/src/types/rpc.ts | 1 + packages/vitest/src/utils/config-helpers.ts | 22 -------------- packages/vitest/src/utils/test-helpers.ts | 13 ++++++++- 12 files changed, 64 insertions(+), 67 deletions(-) delete mode 100644 packages/vitest/setup/react-refresh.js delete mode 100644 packages/vitest/src/node/plugins/commonPluginsHandler.ts diff --git a/docs/config/index.md b/docs/config/index.md index 6e70eb845bcd..26c997aab0aa 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -1120,6 +1120,29 @@ Will call [`vi.unstubAllEnvs`](/api/vi#vi-unstuballenvs) before each test. Will call [`vi.unstubAllGlobals`](/api/vi#vi-unstuballglobals) before each test. +### testTransformMode + + - **Type:** `{ web?, ssr? }` + - **Version:** Since Vitest 0.32.0 + + Determine the transform method for all modules inported inside a test that matches the glob pattern. By default, relies on the environment. For example, tests with JSDOM environment will process all files with `ssr: false` flag and tests with Node environment process all modules with `ssr: true`. + + #### testTransformMode.ssr + + - **Type:** `string[]` + - **Default:** `[]` + + Use SSR transform pipeline for all modules inside specified tests.
+ Vite plugins will receive `ssr: true` flag when processing those files. + + #### testTransformMode.web + + - **Type:** `string[]` + - **Default:** `[]` + + First do a normal transform pipeline (targeting browser), then do a SSR rewrite to run the code in Node.
+ Vite plugins will receive `ssr: false` flag when processing those files. + ### snapshotFormat - **Type:** `PrettyFormatOptions` diff --git a/packages/vitest/setup/react-refresh.js b/packages/vitest/setup/react-refresh.js deleted file mode 100644 index d1095c2f3475..000000000000 --- a/packages/vitest/setup/react-refresh.js +++ /dev/null @@ -1,8 +0,0 @@ -if (typeof window !== 'undefined') { - const { default: RefreshRuntime } = await import('/@react-refresh') - - RefreshRuntime.injectIntoGlobalHook(window) - window.$RefreshReg$ = () => {} - window.$RefreshSig$ = () => type => type - window.__vite_plugin_react_preamble_installed__ = true -} diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 9493232739cb..b1cb96922097 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -289,6 +289,8 @@ export function resolveConfig( port: defaultBrowserPort, } + resolved.testTransformMode ??= {} + return resolved } diff --git a/packages/vitest/src/node/plugins/commonPluginsHandler.ts b/packages/vitest/src/node/plugins/commonPluginsHandler.ts deleted file mode 100644 index abfdec984328..000000000000 --- a/packages/vitest/src/node/plugins/commonPluginsHandler.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { Plugin } from 'vite' -import { resolve } from 'pathe' -import { hasVitePlugin } from '../../utils/config-helpers' -import { distDir } from '../../paths' - -export function CommonPluginsHandler(): Plugin { - return { - name: 'vitest:common-plugins-enforcer', - enforce: 'post', - config: { - order: 'post', - async handler(config) { - const plugins = (config.plugins || []) - const hasReact = await hasVitePlugin(plugins, 'vite:react-babel') - if (hasReact) { - return { - test: { - deps: { - inline: [/vitest\/setup\/react-refresh.js/], - }, - // Since this is an official plugin, it should be OK to inline reac-refresh HRM logic - setupFiles: [resolve(distDir, '../setup/react-refresh.js')], - }, - } - } - }, - }, - } -} diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index fcb6c8c9ac9c..f5d17675a420 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -14,7 +14,6 @@ import { CoverageTransform } from './coverageTransform' import { MocksPlugin } from './mocks' import { deleteDefineConfig, resolveOptimizerConfig } from './utils' import { VitestResolver } from './vitestResolver' -import { CommonPluginsHandler } from './commonPluginsHandler' export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('test')): Promise { const userConfig = deepMerge({}, options) as UserConfig @@ -179,7 +178,6 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t : null, MocksPlugin(), VitestResolver(ctx), - CommonPluginsHandler(), ] .filter(notNullish) } diff --git a/packages/vitest/src/node/plugins/workspace.ts b/packages/vitest/src/node/plugins/workspace.ts index 9fecece9f46e..69340e681afe 100644 --- a/packages/vitest/src/node/plugins/workspace.ts +++ b/packages/vitest/src/node/plugins/workspace.ts @@ -12,7 +12,6 @@ import { GlobalSetupPlugin } from './globalSetup' import { MocksPlugin } from './mocks' import { deleteDefineConfig, resolveOptimizerConfig } from './utils' import { VitestResolver } from './vitestResolver' -import { CommonPluginsHandler } from './commonPluginsHandler' interface WorkspaceOptions extends UserWorkspaceConfig { root?: string @@ -124,6 +123,5 @@ export function WorkspaceVitestPlugin(project: WorkspaceProject, options: Worksp GlobalSetupPlugin(project, project.ctx.logger), MocksPlugin(), VitestResolver(project.ctx), - CommonPluginsHandler(), ] } diff --git a/packages/vitest/src/node/pools/child.ts b/packages/vitest/src/node/pools/child.ts index 20ba9e7b6824..8ba9fe620c67 100644 --- a/packages/vitest/src/node/pools/child.ts +++ b/packages/vitest/src/node/pools/child.ts @@ -112,7 +112,7 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env }: PoolProce if (!files?.length) continue - const filesByOptions = groupBy(files, ({ project, environment }) => project.getName() + JSON.stringify(environment.options)) + const filesByOptions = groupBy(files, ({ project, environment }) => project.getName() + JSON.stringify(environment.options) + environment.transformMode) for (const option in filesByOptions) { const files = filesByOptions[option] diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index 954afe7c4abd..b571d7b44336 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -63,7 +63,7 @@ export async function startViteNode(ctx: ContextRPC) { process.on('uncaughtException', e => catchError(e, 'Uncaught Exception')) process.on('unhandledRejection', e => catchError(e, 'Unhandled Rejection')) - let transformMode: 'ssr' | 'web' = 'ssr' + let transformMode: 'ssr' | 'web' = ctx.environment.transformMode ?? 'ssr' const executor = await createVitestExecutor({ fetchModule(id) { @@ -82,7 +82,7 @@ export async function startViteNode(ctx: ContextRPC) { const environment = await loadEnvironment(ctx.environment.name, executor) ctx.environment.environment = environment - transformMode = environment.transformMode ?? 'ssr' + transformMode = ctx.environment.transformMode ?? environment.transformMode ?? 'ssr' const { run } = await import(pathToFileURL(resolve(distDir, 'entry.js')).href) diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 76a0c58b66dd..c6cc51bd0f34 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -79,6 +79,24 @@ export type DepsOptimizationOptions = Omit { - if (!plugin) - return false - if (Array.isArray(plugin)) - return hasVitePlugin(plugin, name) - if (plugin instanceof Promise) - return isVitePlugin(await plugin, name) - if (typeof plugin === 'object') - return plugin.name === name - return false -} - -export async function hasVitePlugin(plugins: PluginOption[], name: string): Promise { - for (const plugin of plugins) { - if (await isVitePlugin(plugin, name)) - return true - } - - return false -} diff --git a/packages/vitest/src/utils/test-helpers.ts b/packages/vitest/src/utils/test-helpers.ts index 3b7d0f0651b5..1f079cc0e3a9 100644 --- a/packages/vitest/src/utils/test-helpers.ts +++ b/packages/vitest/src/utils/test-helpers.ts @@ -1,6 +1,6 @@ import { promises as fs } from 'node:fs' import mm from 'micromatch' -import type { EnvironmentOptions, VitestEnvironment } from '../types' +import type { EnvironmentOptions, TransformModePatterns, VitestEnvironment } from '../types' import type { WorkspaceProject } from '../node/workspace' import { groupBy } from './base' @@ -17,6 +17,14 @@ export interface FileByEnv { envOptions: EnvironmentOptions | null } +function getTransformMode(patterns: TransformModePatterns, filename: string): 'web' | 'ssr' | undefined { + if (patterns.web && mm.isMatch(filename, patterns.web)) + return 'web' + if (patterns.ssr && mm.isMatch(filename, patterns.ssr)) + return 'ssr' + return undefined +} + export async function groupFilesByEnv(files: (readonly [WorkspaceProject, string])[]) { const filesWithEnv = await Promise.all(files.map(async ([project, file]) => { const code = await fs.readFile(file, 'utf-8') @@ -35,12 +43,15 @@ export async function groupFilesByEnv(files: (readonly [WorkspaceProject, string // 3. Fallback to global env env ||= project.config.environment || 'node' + const transformMode = getTransformMode(project.config.testTransformMode, file) + const envOptions = JSON.parse(code.match(/@(?:vitest|jest)-environment-options\s+?(.+)/)?.[1] || 'null') return { file, project, environment: { name: env as VitestEnvironment, + transformMode, options: envOptions ? { [env]: envOptions } as EnvironmentOptions : null, }, } From 289d6d435f8a35fa555b5b14a68ac54b2d11491a Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 6 Jun 2023 13:20:38 +0200 Subject: [PATCH 04/12] chore: cleanup --- packages/vitest/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 77f41c8e0db6..b5acbf00a7f6 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -83,7 +83,6 @@ }, "files": [ "dist", - "setup", "bin", "*.d.ts", "*.d.cts", From cd75762cf38f14846303c4a2289320d9186ec772 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 6 Jun 2023 13:26:30 +0200 Subject: [PATCH 05/12] chore: lockfile --- pnpm-lock.yaml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9de2b5d95906..505e60ea42ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8673,7 +8673,7 @@ packages: resolution: {integrity: sha512-xryQlOEIe1TduDWAOphR0ihfebKFSWOXpIsk+70JskCfRfW+xALdnJ0r1ZOTo85F9Qsjk6vtlU7edTYHbls9tA==} dependencies: '@types/cheerio': 0.22.31 - '@types/react': 18.2.14 + '@types/react': 18.2.8 dev: true /@types/eslint-scope@3.7.4: @@ -8921,19 +8921,19 @@ packages: /@types/react-dom@18.0.6: resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} dependencies: - '@types/react': 18.2.14 + '@types/react': 18.2.8 dev: true /@types/react-dom@18.0.8: resolution: {integrity: sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==} dependencies: - '@types/react': 18.2.14 + '@types/react': 18.2.8 dev: true /@types/react-is@17.0.3: resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} dependencies: - '@types/react': 18.2.14 + '@types/react': 18.2.8 dev: false /@types/react-test-renderer@17.0.2: @@ -8945,7 +8945,7 @@ packages: /@types/react-transition-group@4.4.5: resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} dependencies: - '@types/react': 18.2.14 + '@types/react': 18.2.8 dev: false /@types/react@17.0.49: @@ -8970,6 +8970,14 @@ packages: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 csstype: 3.1.2 + dev: true + + /@types/react@18.2.8: + resolution: {integrity: sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.2 + csstype: 3.1.2 /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -16048,11 +16056,11 @@ packages: uglify-js: 3.17.0 dev: true - /happy-dom@9.20.3: - resolution: {integrity: sha512-eBsgauT435fXFvQDNcmm5QbGtYzxEzOaX35Ia+h6yP/wwa4xSWZh1CfP+mGby8Hk6Xu59mTkpyf72rUXHNxY7A==} + /happy-dom@9.10.7: + resolution: {integrity: sha512-AF9qCTEDmwY+4Rc+YQ9mHSFuw7dxwhLeSnNMJWGKUsOz3VWsC7O9kniuil5yVgnpx9UIzAjtK9UdbuRKMSGfSQ==} dependencies: css.escape: 1.5.1 - entities: 4.5.0 + he: 1.2.0 iconv-lite: 0.6.3 webidl-conversions: 7.0.0 whatwg-encoding: 2.0.0 From 9dd88ad3f8405f79c47a05e07c1df83f6a967a1a Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 6 Jun 2023 13:28:53 +0200 Subject: [PATCH 06/12] chore: loader fix --- packages/vitest/src/runtime/loader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vitest/src/runtime/loader.ts b/packages/vitest/src/runtime/loader.ts index 91bcd2d739ce..645a338c69b1 100644 --- a/packages/vitest/src/runtime/loader.ts +++ b/packages/vitest/src/runtime/loader.ts @@ -44,14 +44,14 @@ export const resolve: Resolver = async (url, context, next) => { const { parentURL } = context const state = getWorkerState() const resolver = state?.rpc.resolveId - const environment = state?.ctx.environment.environment + const environment = state?.ctx.environment if (!parentURL || isNodeBuiltin(url) || !resolver || !environment) return next(url, context, next) const id = normalizeModuleId(url) const importer = normalizeModuleId(parentURL) - const resolved = await resolver(id, importer, environment.transformMode ?? 'ssr') + const resolved = await resolver(id, importer, environment.transformMode ?? environment.environment?.transformMode ?? 'ssr') let result: ResolveResult let filepath: string From 73fd01824e5ac1626d19dfbf639a8df886d8bbed Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 28 Jun 2023 16:10:49 +0200 Subject: [PATCH 07/12] chore: update lockfile --- pnpm-lock.yaml | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 505e60ea42ce..744d72ad49b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8673,7 +8673,7 @@ packages: resolution: {integrity: sha512-xryQlOEIe1TduDWAOphR0ihfebKFSWOXpIsk+70JskCfRfW+xALdnJ0r1ZOTo85F9Qsjk6vtlU7edTYHbls9tA==} dependencies: '@types/cheerio': 0.22.31 - '@types/react': 18.2.8 + '@types/react': 18.2.14 dev: true /@types/eslint-scope@3.7.4: @@ -8921,19 +8921,19 @@ packages: /@types/react-dom@18.0.6: resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} dependencies: - '@types/react': 18.2.8 + '@types/react': 18.2.14 dev: true /@types/react-dom@18.0.8: resolution: {integrity: sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==} dependencies: - '@types/react': 18.2.8 + '@types/react': 18.2.14 dev: true /@types/react-is@17.0.3: resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} dependencies: - '@types/react': 18.2.8 + '@types/react': 18.2.14 dev: false /@types/react-test-renderer@17.0.2: @@ -8945,7 +8945,7 @@ packages: /@types/react-transition-group@4.4.5: resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} dependencies: - '@types/react': 18.2.8 + '@types/react': 18.2.14 dev: false /@types/react@17.0.49: @@ -8970,14 +8970,6 @@ packages: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 csstype: 3.1.2 - dev: true - - /@types/react@18.2.8: - resolution: {integrity: sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.2 - csstype: 3.1.2 /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -16056,17 +16048,6 @@ packages: uglify-js: 3.17.0 dev: true - /happy-dom@9.10.7: - resolution: {integrity: sha512-AF9qCTEDmwY+4Rc+YQ9mHSFuw7dxwhLeSnNMJWGKUsOz3VWsC7O9kniuil5yVgnpx9UIzAjtK9UdbuRKMSGfSQ==} - dependencies: - css.escape: 1.5.1 - he: 1.2.0 - iconv-lite: 0.6.3 - webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 - whatwg-mimetype: 3.0.0 - dev: true - /happy-dom@9.20.3: resolution: {integrity: sha512-eBsgauT435fXFvQDNcmm5QbGtYzxEzOaX35Ia+h6yP/wwa4xSWZh1CfP+mGby8Hk6Xu59mTkpyf72rUXHNxY7A==} dependencies: From 3139293e731fca2de2aa59754c060436f4f3353a Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 28 Jun 2023 16:44:20 +0200 Subject: [PATCH 08/12] chore: update plugin-react --- examples/react-storybook/package.json | 2 +- examples/react-testing-lib-msw/package.json | 2 +- pnpm-lock.yaml | 46 ++++++--------------- 3 files changed, 15 insertions(+), 35 deletions(-) diff --git a/examples/react-storybook/package.json b/examples/react-storybook/package.json index 6fe2707944d3..e14106bac721 100644 --- a/examples/react-storybook/package.json +++ b/examples/react-storybook/package.json @@ -28,7 +28,7 @@ "@testing-library/react": "^12.1.5", "@types/react": "^17.0.45", "@types/react-dom": "^17.0.17", - "@vitejs/plugin-react": "^1.3.2", + "@vitejs/plugin-react": "^4.0.1", "@vitest/ui": "latest", "babel-loader": "^8.2.5", "jsdom": "latest", diff --git a/examples/react-testing-lib-msw/package.json b/examples/react-testing-lib-msw/package.json index f6b85b9f8a11..af8d6a2eab41 100644 --- a/examples/react-testing-lib-msw/package.json +++ b/examples/react-testing-lib-msw/package.json @@ -19,7 +19,7 @@ "@testing-library/user-event": "^13.5.0", "@types/react": "^17.0.45", "@types/react-dom": "^17.0.17", - "@vitejs/plugin-react": "^1.3.2", + "@vitejs/plugin-react": "^4.0.1", "@vitest/ui": "latest", "cross-fetch": "^3.1.5", "jsdom": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 744d72ad49b1..f7d406ac6fd2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -565,8 +565,8 @@ importers: specifier: ^17.0.17 version: 17.0.17 '@vitejs/plugin-react': - specifier: ^1.3.2 - version: 1.3.2 + specifier: ^4.0.1 + version: 4.0.1(vite@4.3.9) '@vitest/ui': specifier: latest version: link:../../packages/ui @@ -663,8 +663,8 @@ importers: specifier: ^17.0.17 version: 17.0.17 '@vitejs/plugin-react': - specifier: ^1.3.2 - version: 1.3.2 + specifier: ^4.0.1 + version: 4.0.1(vite@4.3.9) '@vitest/ui': specifier: latest version: link:../../packages/ui @@ -4289,16 +4289,6 @@ packages: '@babel/plugin-transform-react-jsx': 7.19.0(@babel/core@7.22.5) dev: true - /@babel/plugin-transform-react-jsx-self@7.18.6(@babel/core@7.18.13): - resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.18.13 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.22.5): resolution: {integrity: sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==} engines: {node: '>=6.9.0'} @@ -4309,16 +4299,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx-source@7.18.6(@babel/core@7.18.13): - resolution: {integrity: sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.18.13 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.22.5): resolution: {integrity: sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==} engines: {node: '>=6.9.0'} @@ -4329,17 +4309,17 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-transform-react-jsx@7.18.10(@babel/core@7.18.13): + /@babel/plugin-transform-react-jsx@7.18.10(@babel/core@7.22.5): resolution: {integrity: sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.18.13 + '@babel/core': 7.22.5 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-module-imports': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.18.13) + '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.22.5) '@babel/types': 7.22.5 dev: true @@ -9528,14 +9508,14 @@ packages: resolution: {integrity: sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==} engines: {node: '>=12.0.0'} dependencies: - '@babel/core': 7.18.13 - '@babel/plugin-transform-react-jsx': 7.18.10(@babel/core@7.18.13) - '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.18.13) - '@babel/plugin-transform-react-jsx-self': 7.18.6(@babel/core@7.18.13) - '@babel/plugin-transform-react-jsx-source': 7.18.6(@babel/core@7.18.13) + '@babel/core': 7.22.5 + '@babel/plugin-transform-react-jsx': 7.18.10(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-self': 7.22.5(@babel/core@7.22.5) + '@babel/plugin-transform-react-jsx-source': 7.22.5(@babel/core@7.22.5) '@rollup/pluginutils': 4.2.1 react-refresh: 0.13.0 - resolve: 1.22.1 + resolve: 1.22.2 transitivePeerDependencies: - supports-color dev: true From e1e2560a4118d22f72ec7e8c777dfc49e3b22d89 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 28 Jun 2023 17:15:35 +0200 Subject: [PATCH 09/12] fix: always replace import.meta.url with __vite_ssr_import_meta__ --- packages/vitest/src/node/plugins/index.ts | 7 +++++-- .../plugins/{envReplacer.ts => ssrReplacer.ts} | 18 +++++++++++++++--- packages/vitest/src/node/plugins/workspace.ts | 4 ++-- 3 files changed, 22 insertions(+), 7 deletions(-) rename packages/vitest/src/node/plugins/{envReplacer.ts => ssrReplacer.ts} (63%) diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index f5d17675a420..6366e83e4084 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -7,7 +7,7 @@ import { ensurePackageInstalled } from '../pkg' import { resolveApiServerConfig } from '../config' import { Vitest } from '../core' import { generateScopedClassName } from '../../integrations/css/css-modules' -import { EnvReplacerPlugin } from './envReplacer' +import { SsrReplacerPlugin } from './ssrReplacer' import { GlobalSetupPlugin } from './globalSetup' import { CSSEnablerPlugin } from './cssEnabler' import { CoverageTransform } from './coverageTransform' @@ -63,6 +63,9 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t const config: ViteConfig = { root: viteConfig.test?.root || options.root, + define: { + 'import.meta.url': '__vite_ssr_import_meta__.url', + }, esbuild: { sourcemap: 'external', @@ -169,7 +172,7 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t await server.watcher.close() }, }, - EnvReplacerPlugin(), + SsrReplacerPlugin(), GlobalSetupPlugin(ctx, ctx.logger), ...CSSEnablerPlugin(ctx), CoverageTransform(ctx), diff --git a/packages/vitest/src/node/plugins/envReplacer.ts b/packages/vitest/src/node/plugins/ssrReplacer.ts similarity index 63% rename from packages/vitest/src/node/plugins/envReplacer.ts rename to packages/vitest/src/node/plugins/ssrReplacer.ts index 11268649d8f8..15fd60e42dc9 100644 --- a/packages/vitest/src/node/plugins/envReplacer.ts +++ b/packages/vitest/src/node/plugins/ssrReplacer.ts @@ -5,16 +5,17 @@ import { cleanUrl } from 'vite-node/utils' // so people can reassign envs at runtime // import.meta.env.VITE_NAME = 'app' -> process.env.VITE_NAME = 'app' -export function EnvReplacerPlugin(): Plugin { +export function SsrReplacerPlugin(): Plugin { return { name: 'vitest:env-replacer', enforce: 'pre', transform(code, id) { - if (!/\bimport\.meta\.env\b/g.test(code)) + if (!/\bimport\.meta\.env\b/.test(code) && !/\bimport\.meta\.url\b/.test(code)) return null let s: MagicString | null = null - const envs = stripLiteral(code).matchAll(/\bimport\.meta\.env\b/g) + const cleanCode = stripLiteral(code) + const envs = cleanCode.matchAll(/\bimport\.meta\.env\b/g) for (const env of envs) { s ||= new MagicString(code) @@ -25,6 +26,17 @@ export function EnvReplacerPlugin(): Plugin { s.overwrite(startIndex, endIndex, 'process.env') } + const urls = cleanCode.matchAll(/\bimport\.meta\.url\b/g) + + for (const env of urls) { + s ||= new MagicString(code) + + const startIndex = env.index! + const endIndex = startIndex + env[0].length + + s.overwrite(startIndex, endIndex, '__vite_ssr_import_meta__.url') + } + if (s) { return { code: s.toString(), diff --git a/packages/vitest/src/node/plugins/workspace.ts b/packages/vitest/src/node/plugins/workspace.ts index 69340e681afe..004bdb76b0a4 100644 --- a/packages/vitest/src/node/plugins/workspace.ts +++ b/packages/vitest/src/node/plugins/workspace.ts @@ -7,7 +7,7 @@ import type { WorkspaceProject } from '../workspace' import type { UserWorkspaceConfig } from '../../types' import { CoverageTransform } from './coverageTransform' import { CSSEnablerPlugin } from './cssEnabler' -import { EnvReplacerPlugin } from './envReplacer' +import { SsrReplacerPlugin } from './ssrReplacer' import { GlobalSetupPlugin } from './globalSetup' import { MocksPlugin } from './mocks' import { deleteDefineConfig, resolveOptimizerConfig } from './utils' @@ -117,7 +117,7 @@ export function WorkspaceVitestPlugin(project: WorkspaceProject, options: Worksp await server.watcher.close() }, }, - EnvReplacerPlugin(), + SsrReplacerPlugin(), ...CSSEnablerPlugin(project), CoverageTransform(project.ctx), GlobalSetupPlugin(project, project.ctx.logger), From 73bc02489db15f56898e0501f720daeed16625c9 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 28 Jun 2023 17:18:28 +0200 Subject: [PATCH 10/12] chore: cleanup error message --- packages/vitest/src/integrations/env/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vitest/src/integrations/env/index.ts b/packages/vitest/src/integrations/env/index.ts index 875d45bbf8f9..0cbd7261df8d 100644 --- a/packages/vitest/src/integrations/env/index.ts +++ b/packages/vitest/src/integrations/env/index.ts @@ -41,7 +41,7 @@ export async function loadEnvironment(name: VitestEnvironment, executor: VitestE if (!pkg || !pkg.default || typeof pkg.default !== 'object' || typeof pkg.default.setup !== 'function') { throw new Error( `Environment "${name}" is not a valid environment. ` - + `Package "vitest-environment-${name}" should have default export with "setup" method.`, + + `Path "${packageId}" should export default object with a "setup" method.`, ) } return pkg.default From fce77dff1f5e5ba57729c5aa744e4116ea17f1a5 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 28 Jun 2023 17:22:37 +0200 Subject: [PATCH 11/12] chore: cleanup --- packages/vitest/src/node/plugins/index.ts | 3 --- packages/vitest/src/runtime/execute.ts | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index 6366e83e4084..a71864ccfc04 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -63,9 +63,6 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t const config: ViteConfig = { root: viteConfig.test?.root || options.root, - define: { - 'import.meta.url': '__vite_ssr_import_meta__.url', - }, esbuild: { sourcemap: 'external', diff --git a/packages/vitest/src/runtime/execute.ts b/packages/vitest/src/runtime/execute.ts index b571d7b44336..cbce90c327e6 100644 --- a/packages/vitest/src/runtime/execute.ts +++ b/packages/vitest/src/runtime/execute.ts @@ -12,6 +12,8 @@ import { loadEnvironment } from '../integrations/env' import { VitestMocker } from './mocker' import { rpc } from './rpc' +const entryUrl = pathToFileURL(resolve(distDir, 'entry.js')).href + export interface ExecuteOptions extends ViteNodeRunnerOptions { mockMap: MockMap moduleDirectories?: string[] @@ -84,7 +86,7 @@ export async function startViteNode(ctx: ContextRPC) { ctx.environment.environment = environment transformMode = ctx.environment.transformMode ?? environment.transformMode ?? 'ssr' - const { run } = await import(pathToFileURL(resolve(distDir, 'entry.js')).href) + const { run } = await import(entryUrl) _viteNode = { run, executor, environment } From 8651d95df8a7fff7291340ee2d93d6e9e010cbd5 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 11 Jul 2023 10:23:58 +0200 Subject: [PATCH 12/12] chore: lockfile --- pnpm-lock.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7d406ac6fd2..ef0b016e5ee1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -566,7 +566,7 @@ importers: version: 17.0.17 '@vitejs/plugin-react': specifier: ^4.0.1 - version: 4.0.1(vite@4.3.9) + version: 4.0.3(vite@4.3.9) '@vitest/ui': specifier: latest version: link:../../packages/ui @@ -664,7 +664,7 @@ importers: version: 17.0.17 '@vitejs/plugin-react': specifier: ^4.0.1 - version: 4.0.1(vite@4.3.9) + version: 4.0.3(vite@4.3.9) '@vitest/ui': specifier: latest version: link:../../packages/ui @@ -21765,15 +21765,6 @@ packages: engines: {node: '>=10'} dev: true - /resolve@1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true - dependencies: - is-core-module: 2.12.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - /resolve@1.22.2: resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} hasBin: true