[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix: allow inlining fully dynamic import (#9137)

authored by

Hiroshi Ogawa and committed by
GitHub
(Dec 9, 2025, 5:25 PM +0100) 568513ca fec55796

+70 -11
+8
pnpm-lock.yaml
··· 1229 1229 '@test/test-dep-config': 1230 1230 specifier: link:./deps/test-dep-config 1231 1231 version: link:deps/test-dep-config 1232 + '@vitejs/test-dep-virtual': 1233 + specifier: file:./deps/test-dep-virtual 1234 + version: file:test/config/deps/test-dep-virtual 1232 1235 '@vitest/browser-playwright': 1233 1236 specifier: workspace:* 1234 1237 version: link:../../packages/browser-playwright ··· 4555 4558 peerDependencies: 4556 4559 vite: ^7.1.5 4557 4560 vue: ^3.2.25 4561 + 4562 + '@vitejs/test-dep-virtual@file:test/config/deps/test-dep-virtual': 4563 + resolution: {directory: test/config/deps/test-dep-virtual, type: directory} 4558 4564 4559 4565 '@vitest/cjs-lib@file:test/browser/cjs-lib': 4560 4566 resolution: {directory: test/browser/cjs-lib, type: directory} ··· 12675 12681 '@rolldown/pluginutils': 1.0.0-beta.50 12676 12682 vite: 7.1.5(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.3)(sass@1.93.3)(terser@5.44.0)(tsx@4.21.0)(yaml@2.8.1) 12677 12683 vue: 3.5.25(typescript@5.9.3) 12684 + 12685 + '@vitejs/test-dep-virtual@file:test/config/deps/test-dep-virtual': {} 12678 12686 12679 12687 '@vitest/cjs-lib@file:test/browser/cjs-lib': {} 12680 12688
+1
test/config/package.json
··· 7 7 }, 8 8 "devDependencies": { 9 9 "@test/test-dep-config": "link:./deps/test-dep-config", 10 + "@vitejs/test-dep-virtual": "file:./deps/test-dep-virtual", 10 11 "@vitest/browser-playwright": "workspace:*", 11 12 "@vitest/browser-preview": "workspace:*", 12 13 "@vitest/browser-webdriverio": "workspace:*",
+15
test/config/test/external.test.ts
··· 1 + import { expect, it } from 'vitest' 2 + import { runVitest } from '../../test-utils' 3 + 4 + it('can inline fully dynamic import', async () => { 5 + const { errorTree } = await runVitest({ 6 + root: 'fixtures/external/dynamic', 7 + }) 8 + expect(errorTree()).toMatchInlineSnapshot(` 9 + { 10 + "basic.test.ts": { 11 + "basic": "passed", 12 + }, 13 + } 14 + `) 15 + })
+1
test/config/deps/test-dep-virtual/index.js
··· 1 + export * from 'virtual:test'
+6
test/config/deps/test-dep-virtual/package.json
··· 1 + { 2 + "name": "@vitejs/test-dep-virtual", 3 + "type": "module", 4 + "private": true, 5 + "exports": "./index.js" 6 + }
+9
packages/vitest/src/node/environments/fetchModule.ts
··· 12 12 import { join } from 'pathe' 13 13 import { fetchModule } from 'vite' 14 14 import { hash } from '../hash' 15 + import { normalizeResolvedIdToUrl } from './normalizeUrl' 15 16 16 17 const saveCachePromises = new Map<string, Promise<FetchResult>>() 17 18 const readFilePromises = new Map<string, Promise<string | null>>() ··· 52 53 if (isExternalUrl(url) && !isFileUrl) { 53 54 trace.setAttribute('vitest.module.external', url) 54 55 return { externalize: url, type: 'network' } 56 + } 57 + 58 + // handle unresolved id of dynamic import skipped by Vite import analysis 59 + if (url[0] !== '/') { 60 + const resolved = await environment.pluginContainer.resolveId(url, importer) 61 + if (resolved) { 62 + url = normalizeResolvedIdToUrl(environment, resolved.id) 63 + } 55 64 } 56 65 57 66 const moduleGraphModule = await environment.moduleGraph.ensureEntryFromUrl(unwrapId(url))
+1 -11
packages/vitest/src/runtime/moduleRunner/moduleEvaluator.ts
··· 322 322 ? vm.runInContext(wrappedCode, this.vm.context, options) 323 323 : vm.runInThisContext(wrappedCode, options) 324 324 325 - const dynamicRequest = async (dep: string, options: ImportCallOptions) => { 326 - dep = String(dep) 327 - // TODO: support more edge cases? 328 - // vite doesn't support dynamic modules by design, but we have to 329 - if (dep[0] === '#') { 330 - return context[ssrDynamicImportKey](wrapId(dep), options) 331 - } 332 - return context[ssrDynamicImportKey](dep, options) 333 - } 334 - 335 325 await initModule( 336 326 context[ssrModuleExportsKey], 337 327 context[ssrImportMetaKey], 338 328 context[ssrImportKey], 339 - dynamicRequest, 329 + context[ssrDynamicImportKey], 340 330 context[ssrExportAllKey], 341 331 // vite 7 support, remove when vite 7+ is supported 342 332 (context as any).__vite_ssr_exportName__
+7
test/config/fixtures/external/dynamic/basic.test.ts
··· 1 + import { expect, it } from "vitest"; 2 + 3 + it("basic", async () => { 4 + const getId = () => "@vitejs/test-dep-virtual"; 5 + const mod = await import(getId()); 6 + expect(mod.test).toBe("ok"); 7 + });
+22
test/config/fixtures/external/dynamic/vitest.config.ts
··· 1 + import { defineConfig } from "vitest/config"; 2 + 3 + export default defineConfig({ 4 + ssr: { 5 + noExternal: ["@vitejs/test-dep-virtual"], 6 + }, 7 + plugins: [ 8 + { 9 + name: 'test-virtual', 10 + resolveId(source) { 11 + if (source === 'virtual:test') { 12 + return '\0' + source; 13 + } 14 + }, 15 + load(id) { 16 + if (id === '\0virtual:test') { 17 + return `export const test = "ok";`; 18 + } 19 + } 20 + } 21 + ], 22 + });