Skip to content

Commit 807a2cb

Browse files
authored
fix: resolve assets imported with require (#6159)
1 parent 0881023 commit 807a2cb

File tree

7 files changed

+55
-12
lines changed

7 files changed

+55
-12
lines changed

docs/guide/environment.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ By default, you can use these environments:
1313
- `happy-dom` emulates browser environment by providing Browser API, and considered to be faster than jsdom, but lacks some API, uses [`happy-dom`](https://github.com/capricorn86/happy-dom) package
1414
- `edge-runtime` emulates Vercel's [edge-runtime](https://edge-runtime.vercel.app/), uses [`@edge-runtime/vm`](https://www.npmjs.com/package/@edge-runtime/vm) package
1515

16+
::: info
17+
When using `jsdom` or `happy-dom` environments, Vitest follows the same rules that Vite does when importing [CSS](https://vitejs.dev/guide/features.html#css) and [assets](https://vitejs.dev/guide/features.html#static-assets). If importing external dependency fails with `unknown extension .css` error, you need to inline the whole import chain manually by adding all packages to [`server.deps.external`](/config/#server-deps-external). For example, if the error happens in `package-3` in this import chain: `source code -> package-1 -> package-2 -> package-3`, you need to add all three packages to `server.deps.external`.
18+
19+
Since Vitest 2.0.4 the `require` of CSS and assets inside the external dependencies are resolved automatically.
20+
:::
21+
1622
## Environments for Specific Files
1723

1824
When setting `environment` option in your config, it will apply to all the test files in your project. To have more fine-grained control, you can use control comments to specify environment for specific files. Control comments are comments that start with `@vitest-environment` and are followed by the environment name:

packages/vitest/src/runtime/runVmTests.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import timers from 'node:timers'
44
import { performance } from 'node:perf_hooks'
55
import { collectTests, startTests } from '@vitest/runner'
66
import { installSourcemapsSupport } from 'vite-node/source-map'
7+
import { KNOWN_ASSET_TYPES } from 'vite-node/constants'
78
import { setupChaiConfig } from '../integrations/chai/config'
89
import {
910
startCoverageInsideWorker,
@@ -36,10 +37,14 @@ export async function run(
3637
if (workerState.environment.transformMode === 'web') {
3738
const _require = createRequire(import.meta.url)
3839
// always mock "required" `css` files, because we cannot process them
39-
_require.extensions['.css'] = () => ({})
40-
_require.extensions['.scss'] = () => ({})
41-
_require.extensions['.sass'] = () => ({})
42-
_require.extensions['.less'] = () => ({})
40+
_require.extensions['.css'] = resolveCss
41+
_require.extensions['.scss'] = resolveCss
42+
_require.extensions['.sass'] = resolveCss
43+
_require.extensions['.less'] = resolveCss
44+
// since we are using Vite, we can assume how these will be resolved
45+
KNOWN_ASSET_TYPES.forEach((type) => {
46+
_require.extensions[`.${type}`] = resolveAsset
47+
})
4348
}
4449

4550
// @ts-expect-error not typed global for patched timers
@@ -93,3 +98,11 @@ export async function run(
9398

9499
await stopCoverageInsideWorker(config.coverage, executor)
95100
}
101+
102+
function resolveCss(mod: NodeJS.Module) {
103+
mod.exports = ''
104+
}
105+
106+
function resolveAsset(mod: NodeJS.Module, url: string) {
107+
mod.exports = url
108+
}

packages/vitest/src/runtime/setup-node.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createRequire } from 'node:module'
22
import util from 'node:util'
33
import timers from 'node:timers'
44
import { installSourcemapsSupport } from 'vite-node/source-map'
5+
import { KNOWN_ASSET_TYPES } from 'vite-node/constants'
56
import type {
67
EnvironmentOptions,
78
ResolvedConfig,
@@ -44,10 +45,14 @@ export async function setupGlobalEnv(
4445
if (environment.transformMode === 'web') {
4546
const _require = createRequire(import.meta.url)
4647
// always mock "required" `css` files, because we cannot process them
47-
_require.extensions['.css'] = () => ({})
48-
_require.extensions['.scss'] = () => ({})
49-
_require.extensions['.sass'] = () => ({})
50-
_require.extensions['.less'] = () => ({})
48+
_require.extensions['.css'] = resolveCss
49+
_require.extensions['.scss'] = resolveCss
50+
_require.extensions['.sass'] = resolveCss
51+
_require.extensions['.less'] = resolveCss
52+
// since we are using Vite, we can assume how these will be resolved
53+
KNOWN_ASSET_TYPES.forEach((type) => {
54+
_require.extensions[`.${type}`] = resolveAsset
55+
})
5156
process.env.SSR = ''
5257
}
5358
else {
@@ -69,6 +74,14 @@ export async function setupGlobalEnv(
6974
}
7075
}
7176

77+
function resolveCss(mod: NodeJS.Module) {
78+
mod.exports = ''
79+
}
80+
81+
function resolveAsset(mod: NodeJS.Module, url: string) {
82+
mod.exports = url
83+
}
84+
7285
export async function setupConsoleLogSpy() {
7386
const { createCustomConsole } = await import('./console')
7487

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"tinyrainbow": "^1.2.0",
2929
"tinyspy": "^1.0.2",
3030
"url": "^0.11.0",
31+
"vite-node": "workspace:*",
3132
"vitest": "workspace:*",
3233
"vitest-environment-custom": "file:./vitest-environment-custom",
3334
"vue": "^3.4.26",

test/core/src/file-txt.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
text

test/core/test/require.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @vitest-environment jsdom
22

3+
// import { KNOWN_ASSET_RE } from 'vite-node/constants'
34
import { describe, expect, it } from 'vitest'
45

56
const _require = require
@@ -11,9 +12,14 @@ describe('using "require" to import a module', () => {
1112
const scss = _require('./../src/file-scss.scss')
1213
const less = _require('./../src/file-less.less')
1314

14-
expect(css).toEqual({})
15-
expect(sass).toEqual({})
16-
expect(scss).toEqual({})
17-
expect(less).toEqual({})
15+
expect(css).toEqual('')
16+
expect(sass).toEqual('')
17+
expect(scss).toEqual('')
18+
expect(less).toEqual('')
19+
})
20+
21+
it('importing assets works', () => {
22+
const path = _require.resolve('./../src/file-txt.txt')
23+
expect(_require('./../src/file-txt.txt')).toBe(path)
1824
})
1925
})

0 commit comments

Comments
 (0)