From 1663f5ca007159c319ab25739e12d8453d9c822f Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 12 Jan 2024 14:06:10 +0100 Subject: [PATCH] fix(vitest): add inlined deps to ssr.noExternal so they are added to the module graph (#4945) --- packages/vite-node/src/server.ts | 3 +- packages/vitest/src/config.ts | 1 + packages/vitest/src/constants.ts | 9 ++ packages/vitest/src/node/config.ts | 11 +- packages/vitest/src/node/plugins/index.ts | 21 +++- test/config/test/resolution.test.ts | 123 +++++++++++++++++++++- 6 files changed, 153 insertions(+), 15 deletions(-) diff --git a/packages/vite-node/src/server.ts b/packages/vite-node/src/server.ts index fdfb4915a56f..de68171d5539 100644 --- a/packages/vite-node/src/server.ts +++ b/packages/vite-node/src/server.ts @@ -65,7 +65,8 @@ export class ViteNodeServer { } else if (options.deps.inline !== true) { options.deps.inline ??= [] - options.deps.inline.push(...toArray(ssrOptions.noExternal)) + const inline = options.deps.inline + options.deps.inline.push(...toArray(ssrOptions.noExternal).filter(dep => !inline.includes(dep))) } } if (process.env.VITE_NODE_DEBUG_DUMP) { diff --git a/packages/vitest/src/config.ts b/packages/vitest/src/config.ts index b3e71144ff7a..e207476f31c2 100644 --- a/packages/vitest/src/config.ts +++ b/packages/vitest/src/config.ts @@ -8,6 +8,7 @@ export interface UserWorkspaceConfig extends ViteUserConfig { // will import vitest declare test in module 'vite' export { configDefaults, defaultInclude, defaultExclude, coverageConfigDefaults } from './defaults' export { mergeConfig } from 'vite' +export { extraInlineDeps } from './constants' export type { ConfigEnv, ViteUserConfig as UserConfig } export type UserConfigFnObject = (env: ConfigEnv) => ViteUserConfig diff --git a/packages/vitest/src/constants.ts b/packages/vitest/src/constants.ts index 45be55753db8..87164b9a2b70 100644 --- a/packages/vitest/src/constants.ts +++ b/packages/vitest/src/constants.ts @@ -6,6 +6,15 @@ export const EXIT_CODE_RESTART = 43 export const API_PATH = '/__vitest_api__' +export const extraInlineDeps = [ + /^(?!.*(?:node_modules)).*\.mjs$/, + /^(?!.*(?:node_modules)).*\.cjs\.js$/, + // Vite client + /vite\w*\/dist\/client\/env.mjs/, + // Nuxt + '@nuxt/test-utils', +] + export const CONFIG_NAMES = [ 'vitest.config', 'vite.config', diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index d319d61fb2d5..69714f4f6dbb 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -3,7 +3,7 @@ import { normalize, relative, resolve } from 'pathe' import c from 'picocolors' import type { ResolvedConfig as ResolvedViteConfig } from 'vite' import type { ApiConfig, ResolvedConfig, UserConfig, VitestRunMode } from '../types' -import { defaultBrowserPort, defaultPort } from '../constants' +import { defaultBrowserPort, defaultPort, extraInlineDeps } from '../constants' import { benchmarkConfigDefaults, configDefaults } from '../defaults' import { isCI, stdProvider, toArray } from '../utils' import type { BuiltinPool } from '../types/pool-options' @@ -13,15 +13,6 @@ import { RandomSequencer } from './sequencers/RandomSequencer' import type { BenchmarkBuiltinReporters } from './reporters' import { builtinPools } from './pool' -const extraInlineDeps = [ - /^(?!.*(?:node_modules)).*\.mjs$/, - /^(?!.*(?:node_modules)).*\.cjs\.js$/, - // Vite client - /vite\w*\/dist\/client\/env.mjs/, - // Nuxt - '@nuxt/test-utils', -] - function resolvePath(path: string, root: string) { return normalize( resolveModule(path, { paths: [root] }) diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index df0d95783ced..bccc28de7ec6 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -2,7 +2,7 @@ import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite' import { relative } from 'pathe' import { configDefaults } from '../../defaults' import type { ResolvedConfig, UserConfig } from '../../types' -import { deepMerge, notNullish, removeUndefinedValues } from '../../utils' +import { deepMerge, notNullish, removeUndefinedValues, toArray } from '../../utils' import { ensurePackageInstalled } from '../pkg' import { resolveApiServerConfig } from '../config' import { Vitest } from '../core' @@ -103,6 +103,25 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t }, } + // we want inline dependencies to be resolved by analyser plugin so module graph is populated correctly + if (viteConfig.ssr?.noExternal !== true) { + const inline = testConfig.server?.deps?.inline + if (inline === true) { + config.ssr = { noExternal: true } + } + else { + const noExternal = viteConfig.ssr?.noExternal + const noExternalArray = typeof noExternal !== 'undefined' ? toArray(noExternal) : undefined + // filter the same packages + const uniqueInline = inline && noExternalArray + ? inline.filter(dep => !noExternalArray.includes(dep)) + : inline + config.ssr = { + noExternal: uniqueInline, + } + } + } + // chokidar fsevents is unstable on macos when emitting "ready" event if (process.platform === 'darwin' && process.env.VITE_TEST_WATCHER_DEBUG) { config.server!.watch!.useFsEvents = false diff --git a/test/config/test/resolution.test.ts b/test/config/test/resolution.test.ts index a222e7812692..bf7f4e9007f3 100644 --- a/test/config/test/resolution.test.ts +++ b/test/config/test/resolution.test.ts @@ -1,10 +1,17 @@ import type { UserConfig } from 'vitest' +import type { UserConfig as ViteUserConfig } from 'vite' import { describe, expect, it } from 'vitest' import { createVitest } from 'vitest/node' +import { extraInlineDeps } from 'vitest/config' -async function config(cliOptions: UserConfig, configValue: UserConfig = {}) { - const vitest = await createVitest('test', { ...cliOptions, watch: false }, { test: configValue }) - return vitest.config +async function vitest(cliOptions: UserConfig, configValue: UserConfig = {}, viteConfig: ViteUserConfig = {}) { + const vitest = await createVitest('test', { ...cliOptions, watch: false }, { ...viteConfig, test: configValue as any }) + return vitest +} + +async function config(cliOptions: UserConfig, configValue: UserConfig = {}, viteConfig: ViteUserConfig = {}) { + const v = await vitest(cliOptions, configValue, viteConfig) + return v.config } describe('correctly defines isolated flags', async () => { @@ -102,3 +109,113 @@ describe('correctly defines isolated flags', async () => { expect(c.isolate).toBe(true) }) }) + +describe('correctly defines inline and noExternal flags', async () => { + it('both are true if inline is true', async () => { + const v = await vitest({}, { + server: { + deps: { + inline: true, + }, + }, + }) + expect(v.vitenode.options.deps?.inline).toBe(true) + expect(v.vitenode.server.config.ssr.noExternal).toBe(true) + }) + + it('both are true if noExternal is true', async () => { + const v = await vitest({}, {}, { + ssr: { + noExternal: true, + }, + }) + expect(v.vitenode.options.deps?.inline).toBe(true) + expect(v.vitenode.server.config.ssr.noExternal).toBe(true) + }) + + it('inline are added to noExternal', async () => { + const regexp1 = /dep1/ + const regexp2 = /dep2/ + + const v = await vitest({}, { + server: { + deps: { + inline: ['dep1', 'dep2', regexp1, regexp2], + }, + }, + }) + + expect(v.vitenode.options.deps?.inline).toEqual([ + 'dep1', + 'dep2', + regexp1, + regexp2, + ...extraInlineDeps, + ]) + expect(v.server.config.ssr.noExternal).toEqual([ + 'dep1', + 'dep2', + regexp1, + regexp2, + ...extraInlineDeps, + ]) + }) + + it('noExternal are added to inline', async () => { + const regexp1 = /dep1/ + const regexp2 = /dep2/ + + const v = await vitest({}, {}, { + ssr: { + noExternal: ['dep1', 'dep2', regexp1, regexp2], + }, + }) + + expect(v.vitenode.options.deps?.inline).toEqual([ + ...extraInlineDeps, + 'dep1', + 'dep2', + regexp1, + regexp2, + ]) + expect(v.server.config.ssr.noExternal).toEqual([ + 'dep1', + 'dep2', + regexp1, + regexp2, + ]) + }) + + it('noExternal and inline don\'t have duplicates', async () => { + const regexp1 = /dep1/ + const regexp2 = /dep2/ + + const v = await vitest({}, { + server: { + deps: { + inline: ['dep2', regexp1, 'dep3'], + }, + }, + }, { + ssr: { + noExternal: ['dep1', 'dep2', regexp1, regexp2], + }, + }) + + expect(v.vitenode.options.deps?.inline).toEqual([ + 'dep2', + regexp1, + 'dep3', + ...extraInlineDeps, + 'dep1', + regexp2, + ]) + expect(v.server.config.ssr.noExternal).toEqual([ + 'dep1', + 'dep2', + regexp1, + regexp2, + 'dep3', + ]) + }) +})