Skip to content

Commit

Permalink
fix: resolve assets imported with require (#6159)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va authored Jul 18, 2024
1 parent 0881023 commit 807a2cb
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 12 deletions.
6 changes: 6 additions & 0 deletions docs/guide/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ By default, you can use these environments:
- `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
- `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

::: info
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`.

Since Vitest 2.0.4 the `require` of CSS and assets inside the external dependencies are resolved automatically.
:::

## Environments for Specific Files

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:
Expand Down
21 changes: 17 additions & 4 deletions packages/vitest/src/runtime/runVmTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import timers from 'node:timers'
import { performance } from 'node:perf_hooks'
import { collectTests, startTests } from '@vitest/runner'
import { installSourcemapsSupport } from 'vite-node/source-map'
import { KNOWN_ASSET_TYPES } from 'vite-node/constants'
import { setupChaiConfig } from '../integrations/chai/config'
import {
startCoverageInsideWorker,
Expand Down Expand Up @@ -36,10 +37,14 @@ export async function run(
if (workerState.environment.transformMode === 'web') {
const _require = createRequire(import.meta.url)
// always mock "required" `css` files, because we cannot process them
_require.extensions['.css'] = () => ({})
_require.extensions['.scss'] = () => ({})
_require.extensions['.sass'] = () => ({})
_require.extensions['.less'] = () => ({})
_require.extensions['.css'] = resolveCss
_require.extensions['.scss'] = resolveCss
_require.extensions['.sass'] = resolveCss
_require.extensions['.less'] = resolveCss
// since we are using Vite, we can assume how these will be resolved
KNOWN_ASSET_TYPES.forEach((type) => {
_require.extensions[`.${type}`] = resolveAsset
})
}

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

await stopCoverageInsideWorker(config.coverage, executor)
}

function resolveCss(mod: NodeJS.Module) {
mod.exports = ''
}

function resolveAsset(mod: NodeJS.Module, url: string) {
mod.exports = url
}
21 changes: 17 additions & 4 deletions packages/vitest/src/runtime/setup-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createRequire } from 'node:module'
import util from 'node:util'
import timers from 'node:timers'
import { installSourcemapsSupport } from 'vite-node/source-map'
import { KNOWN_ASSET_TYPES } from 'vite-node/constants'
import type {
EnvironmentOptions,
ResolvedConfig,
Expand Down Expand Up @@ -44,10 +45,14 @@ export async function setupGlobalEnv(
if (environment.transformMode === 'web') {
const _require = createRequire(import.meta.url)
// always mock "required" `css` files, because we cannot process them
_require.extensions['.css'] = () => ({})
_require.extensions['.scss'] = () => ({})
_require.extensions['.sass'] = () => ({})
_require.extensions['.less'] = () => ({})
_require.extensions['.css'] = resolveCss
_require.extensions['.scss'] = resolveCss
_require.extensions['.sass'] = resolveCss
_require.extensions['.less'] = resolveCss
// since we are using Vite, we can assume how these will be resolved
KNOWN_ASSET_TYPES.forEach((type) => {
_require.extensions[`.${type}`] = resolveAsset
})
process.env.SSR = ''
}
else {
Expand All @@ -69,6 +74,14 @@ export async function setupGlobalEnv(
}
}

function resolveCss(mod: NodeJS.Module) {
mod.exports = ''
}

function resolveAsset(mod: NodeJS.Module, url: string) {
mod.exports = url
}

export async function setupConsoleLogSpy() {
const { createCustomConsole } = await import('./console')

Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"tinyrainbow": "^1.2.0",
"tinyspy": "^1.0.2",
"url": "^0.11.0",
"vite-node": "workspace:*",
"vitest": "workspace:*",
"vitest-environment-custom": "file:./vitest-environment-custom",
"vue": "^3.4.26",
Expand Down
1 change: 1 addition & 0 deletions test/core/src/file-txt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
text
14 changes: 10 additions & 4 deletions test/core/test/require.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @vitest-environment jsdom

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

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

expect(css).toEqual({})
expect(sass).toEqual({})
expect(scss).toEqual({})
expect(less).toEqual({})
expect(css).toEqual('')
expect(sass).toEqual('')
expect(scss).toEqual('')
expect(less).toEqual('')
})

it('importing assets works', () => {
const path = _require.resolve('./../src/file-txt.txt')
expect(_require('./../src/file-txt.txt')).toBe(path)
})
})

0 comments on commit 807a2cb

Please sign in to comment.