mirror of
https://github.com/vitest-dev/vitest.git
synced 2025-12-08 18:26:03 +00:00
chore: remove vite-node from the monorepo (#8761)
This commit is contained in:
parent
9f0ecccb8b
commit
3dfe09526c
@ -44,7 +44,6 @@ Vitest is a next-generation testing framework powered by Vite. This is a monorep
|
||||
|
||||
### Core Packages (`packages/`)
|
||||
- `vitest` - Main testing framework
|
||||
- `vite-node` - Vite SSR runtime
|
||||
- `browser` - Browser testing support
|
||||
- `ui` - Web UI for test results
|
||||
- `runner` - Test runner core
|
||||
|
||||
@ -156,7 +156,7 @@ Color used by CLI and UI can be changed by providing an object with `color` prop
|
||||
|
||||
- **Type:** `{ sourcemap?, deps?, ... }`
|
||||
|
||||
Vite-Node server options.
|
||||
Moudle runner options.
|
||||
|
||||
#### server.sourcemap
|
||||
|
||||
@ -169,7 +169,7 @@ Inject inline source map to modules.
|
||||
|
||||
- **Type:** `{ dumpModules?, loadDumppedModules? }`
|
||||
|
||||
Vite-Node debugger options.
|
||||
Module runner debugger options.
|
||||
|
||||
#### server.debug.dumpModules
|
||||
|
||||
|
||||
@ -79,24 +79,6 @@ After the tests have run there should be a `main-profile/*.cpuprofile` file gene
|
||||
|
||||
## File transform
|
||||
|
||||
In cases where your test transform and collection time is high, you can use `DEBUG=vite-node:*` environment variable to see which files are being transformed and executed by `vite-node`.
|
||||
|
||||
```bash
|
||||
$ DEBUG=vite-node:* vitest --run
|
||||
|
||||
RUN v2.1.1 /x/vitest/examples/profiling
|
||||
|
||||
vite-node:server:request /x/vitest/examples/profiling/global-setup.ts +0ms
|
||||
vite-node:client:execute /x/vitest/examples/profiling/global-setup.ts +0ms
|
||||
vite-node:server:request /x/vitest/examples/profiling/test/prime-number.test.ts +45ms
|
||||
vite-node:client:execute /x/vitest/examples/profiling/test/prime-number.test.ts +26ms
|
||||
vite-node:server:request /src/prime-number.ts +9ms
|
||||
vite-node:client:execute /x/vitest/examples/profiling/src/prime-number.ts +9ms
|
||||
vite-node:server:request /src/unnecessary-file.ts +6ms
|
||||
vite-node:client:execute /x/vitest/examples/profiling/src/unnecessary-file.ts +4ms
|
||||
...
|
||||
```
|
||||
|
||||
This profiling strategy is a good way to identify unnecessary transforms caused by [barrel files](https://vitejs.dev/guide/performance.html#avoid-barrel-files).
|
||||
If these logs contain files that should not be loaded when your test is run, you might have barrel files that are importing files unnecessarily.
|
||||
|
||||
@ -131,17 +113,15 @@ test('formatter works', () => {
|
||||
|
||||
<img src="/module-graph-barrel-file.png" alt="Vitest UI demonstrating barrel file issues" />
|
||||
|
||||
To see how files are transformed, you can use `VITE_NODE_DEBUG_DUMP` environment variable to write transformed files in the file system:
|
||||
To see how files are transformed, you can use `VITEST_DEBUG_DUMP` environment variable to write transformed files in the file system:
|
||||
|
||||
```bash
|
||||
$ VITE_NODE_DEBUG_DUMP=true vitest --run
|
||||
|
||||
[vite-node] [debug] dump modules to /x/examples/profiling/.vite-node/dump
|
||||
$ VITEST_DEBUG_DUMP=true vitest --run
|
||||
|
||||
RUN v2.1.1 /x/vitest/examples/profiling
|
||||
...
|
||||
|
||||
$ ls .vite-node/dump/
|
||||
$ ls .vitest-dump/
|
||||
_x_examples_profiling_global-setup_ts-1292904907.js
|
||||
_x_examples_profiling_test_prime-number_test_ts-1413378098.js
|
||||
_src_prime-number_ts-525172412.js
|
||||
|
||||
@ -135,11 +135,4 @@ export default antfu(
|
||||
'unicorn/consistent-function-scoping': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [`packages/vite-node/${GLOB_SRC}`],
|
||||
rules: {
|
||||
// false positive on "exports" variable
|
||||
'antfu/no-cjs-exports': 'off',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@ -20,8 +20,6 @@ const stackIgnorePatterns: (string | RegExp)[] = [
|
||||
/\/@vitest\/\w+\/dist\//,
|
||||
'/vitest/dist/',
|
||||
'/vitest/src/',
|
||||
'/vite-node/dist/',
|
||||
'/vite-node/src/',
|
||||
'/node_modules/chai/',
|
||||
'/node_modules/tinyspy/',
|
||||
'/vite/dist/node/module-runner',
|
||||
|
||||
1
packages/vite-node/.gitignore
vendored
1
packages/vite-node/.gitignore
vendored
@ -1 +0,0 @@
|
||||
*.d.ts
|
||||
@ -1,193 +0,0 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/vitest-dev/vitest/blob/main/packages/vite-node/assets/vite-node.svg?raw=true" height="120">
|
||||
</p>
|
||||
|
||||
<h1 align="center">
|
||||
vite-node
|
||||
</h1>
|
||||
<p align="center">
|
||||
Vite as Node runtime.<br>The engine that powers <a href="https://github.com/vitest-dev/vitest">Vitest</a> and <a href="https://github.com/nuxt/nuxt">Nuxt 3 Dev SSR</a>.
|
||||
<p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/vitest"><img src="https://img.shields.io/npm/v/vite-node?color=FCC72B&label="></a>
|
||||
<p>
|
||||
|
||||
## Features
|
||||
|
||||
- On-demand evaluation
|
||||
- Vite's pipeline, plugins, resolve, aliasing
|
||||
- Out-of-box ESM & TypeScript support
|
||||
- Respect `vite.config.ts`
|
||||
- Hot module replacement (HMR)
|
||||
- Separate server/client architecture
|
||||
- Top-level `await`
|
||||
- Shims for `__dirname` and `__filename` in ESM
|
||||
- Access to native node modules like `fs`, `path`, etc.
|
||||
|
||||
## CLI Usage
|
||||
|
||||
Run JS/TS file on Node.js using Vite's resolvers and transformers.
|
||||
|
||||
```bash
|
||||
npx vite-node index.ts
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
```bash
|
||||
npx vite-node -h
|
||||
```
|
||||
|
||||
### Options via CLI
|
||||
|
||||
[All `ViteNodeServer` options](https://github.com/vitest-dev/vitest/blob/main/packages/vite-node/src/types.ts#L92-L111) are supported by the CLI. They may be defined through the dot syntax, as shown below:
|
||||
|
||||
```bash
|
||||
npx vite-node --options.deps.inline="module-name" --options.deps.external="/module-regexp/" index.ts
|
||||
```
|
||||
|
||||
Note that for options supporting RegExps, strings passed to the CLI must start _and_ end with a `/`;
|
||||
|
||||
### Hashbang
|
||||
|
||||
If you prefer to write scripts that don't need to be passed into Vite Node, you can declare it in the [hashbang](https://bash.cyberciti.biz/guide/Shebang).
|
||||
|
||||
Simply add `#!/usr/bin/env vite-node --script` at the top of your file:
|
||||
|
||||
_file.ts_
|
||||
|
||||
```ts
|
||||
#!/usr/bin/env vite-node --script
|
||||
|
||||
console.log('argv:', process.argv.slice(2))
|
||||
```
|
||||
|
||||
And make the file executable:
|
||||
|
||||
```sh
|
||||
chmod +x ./file.ts
|
||||
```
|
||||
|
||||
Now, you can run the file without passing it into Vite Node:
|
||||
|
||||
```sh
|
||||
$ ./file.ts hello
|
||||
argv: [ 'hello' ]
|
||||
```
|
||||
|
||||
Note that when using the `--script` option, Vite Node forwards every argument and option to the script to execute, even the one supported by Vite Node itself.
|
||||
|
||||
## Programmatic Usage
|
||||
|
||||
In Vite Node, the server and runner (client) are separated, so you can integrate them in different contexts (workers, cross-process, or remote) if needed. The demo below shows a simple example of having both (server and runner) running in the same context
|
||||
|
||||
```ts
|
||||
import { createServer, version as viteVersion } from 'vite'
|
||||
import { ViteNodeRunner } from 'vite-node/client'
|
||||
import { ViteNodeServer } from 'vite-node/server'
|
||||
import { installSourcemapsSupport } from 'vite-node/source-map'
|
||||
|
||||
// create vite server
|
||||
const server = await createServer({
|
||||
optimizeDeps: {
|
||||
// It's recommended to disable deps optimization
|
||||
noDiscovery: true,
|
||||
include: undefined,
|
||||
},
|
||||
})
|
||||
|
||||
// For old Vite, this is needed to initialize the plugins.
|
||||
if (Number(viteVersion.split('.')[0]) < 6) {
|
||||
await server.pluginContainer.buildStart({})
|
||||
}
|
||||
|
||||
// create vite-node server
|
||||
const node = new ViteNodeServer(server)
|
||||
|
||||
// fixes stacktraces in Errors
|
||||
installSourcemapsSupport({
|
||||
getSourceMap: source => node.getSourceMap(source),
|
||||
})
|
||||
|
||||
// create vite-node runner
|
||||
const runner = new ViteNodeRunner({
|
||||
root: server.config.root,
|
||||
base: server.config.base,
|
||||
// when having the server and runner in a different context,
|
||||
// you will need to handle the communication between them
|
||||
// and pass to this function
|
||||
fetchModule(id) {
|
||||
return node.fetchModule(id)
|
||||
},
|
||||
resolveId(id, importer) {
|
||||
return node.resolveId(id, importer)
|
||||
},
|
||||
})
|
||||
|
||||
// execute the file
|
||||
await runner.executeFile('./example.ts')
|
||||
|
||||
// close the vite server
|
||||
await server.close()
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Debug Transformation
|
||||
|
||||
Sometimes you might want to inspect the transformed code to investigate issues. You can set environment variable `VITE_NODE_DEBUG_DUMP=true` to let vite-node write the transformed result of each module under `.vite-node/dump`.
|
||||
|
||||
If you want to debug by modifying the dumped code, you can change the value of `VITE_NODE_DEBUG_DUMP` to `load` and search for the dumped files and use them for executing.
|
||||
|
||||
```bash
|
||||
VITE_NODE_DEBUG_DUMP=load vite-node example.ts
|
||||
```
|
||||
|
||||
Or programmatically:
|
||||
|
||||
```js
|
||||
import { ViteNodeServer } from 'vite-node/server'
|
||||
|
||||
const server = new ViteNodeServer(viteServer, {
|
||||
debug: {
|
||||
dumpModules: true,
|
||||
loadDumppedModules: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Debug Execution
|
||||
|
||||
If the process gets stuck, it might be because there are unresolvable circular dependencies. You can set `VITE_NODE_DEBUG_RUNNER=true` for vite-node to warn about this.
|
||||
|
||||
```bash
|
||||
VITE_NODE_DEBUG_RUNNER=true vite-node example.ts
|
||||
```
|
||||
|
||||
Or programmatically:
|
||||
|
||||
```js
|
||||
import { ViteNodeRunner } from 'vite-node/client'
|
||||
|
||||
const runner = new ViteNodeRunner({
|
||||
debug: true,
|
||||
})
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
Based on [@pi0](https://github.com/pi0)'s brilliant idea of having a Vite server as the on-demand transforming service for [Nuxt's Vite SSR](https://github.com/nuxt/vite/pull/201).
|
||||
|
||||
Thanks [@brillout](https://github.com/brillout) for kindly sharing this package name.
|
||||
|
||||
## Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
|
||||
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE) License © 2021 [Anthony Fu](https://github.com/antfu)
|
||||
@ -1,4 +0,0 @@
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M60 5.99962L106.765 32.9996V86.9996L60 114L13.2346 86.9996V32.9996L60 5.99962Z" stroke="#99D253" stroke-width="8" stroke-linejoin="round"/>
|
||||
<path d="M63.1707 49.8053L90.5738 47.6009L60.5096 114.684L56.9315 70.204L29.5284 72.4084L59.5926 5.32538L63.1707 49.8053Z" stroke="#FCC72B" stroke-width="7" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 439 B |
@ -1,96 +0,0 @@
|
||||
{
|
||||
"name": "vite-node",
|
||||
"type": "module",
|
||||
"version": "4.0.0-beta.19",
|
||||
"description": "Vite as Node.js runtime",
|
||||
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://github.com/vitest-dev/vitest/blob/main/packages/vite-node#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/vite-node"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./dist/client.d.ts",
|
||||
"import": "./dist/client.mjs",
|
||||
"require": "./dist/client.cjs"
|
||||
},
|
||||
"./server": {
|
||||
"types": "./dist/server.d.ts",
|
||||
"import": "./dist/server.mjs",
|
||||
"require": "./dist/server.cjs"
|
||||
},
|
||||
"./utils": {
|
||||
"types": "./dist/utils.d.ts",
|
||||
"import": "./dist/utils.mjs",
|
||||
"require": "./dist/utils.cjs"
|
||||
},
|
||||
"./hmr": {
|
||||
"types": "./dist/hmr.d.ts",
|
||||
"import": "./dist/hmr.mjs",
|
||||
"require": "./dist/hmr.cjs"
|
||||
},
|
||||
"./source-map": {
|
||||
"types": "./dist/source-map.d.ts",
|
||||
"import": "./dist/source-map.mjs",
|
||||
"require": "./dist/source-map.cjs"
|
||||
},
|
||||
"./constants": {
|
||||
"types": "./dist/constants.d.ts",
|
||||
"import": "./dist/constants.mjs",
|
||||
"require": "./dist/constants.cjs"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"./dist/*",
|
||||
"./dist/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"bin": {
|
||||
"vite-node": "./vite-node.mjs"
|
||||
},
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"*.mjs",
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "premove dist && rollup -c",
|
||||
"dev": "rollup -c --watch --watch.include 'src/**' -m inline",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"cac": "catalog:",
|
||||
"debug": "catalog:",
|
||||
"es-module-lexer": "^1.7.0",
|
||||
"pathe": "catalog:",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jridgewell/trace-mapping": "catalog:",
|
||||
"@types/debug": "catalog:",
|
||||
"tinyrainbow": "catalog:"
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
import { builtinModules, createRequire } from 'node:module'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import json from '@rollup/plugin-json'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import { defineConfig } from 'rollup'
|
||||
import oxc from 'unplugin-oxc/rollup'
|
||||
import { createDtsUtils } from '../../scripts/build-utils.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const pkg = require('./package.json')
|
||||
|
||||
const entries = {
|
||||
'index': 'src/index.ts',
|
||||
'server': 'src/server.ts',
|
||||
'types': 'src/types.ts',
|
||||
'client': 'src/client.ts',
|
||||
'utils': 'src/utils.ts',
|
||||
'cli': 'src/cli.ts',
|
||||
'constants': 'src/constants.ts',
|
||||
'hmr': 'src/hmr/index.ts',
|
||||
'source-map': 'src/source-map.ts',
|
||||
}
|
||||
|
||||
const external = [
|
||||
...builtinModules,
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
...Object.keys(pkg.peerDependencies || {}),
|
||||
'pathe',
|
||||
'birpc',
|
||||
'vite',
|
||||
'node:url',
|
||||
'node:events',
|
||||
'node:vm',
|
||||
]
|
||||
|
||||
const dtsUtils = createDtsUtils()
|
||||
|
||||
const plugins = [
|
||||
resolve({
|
||||
preferBuiltins: true,
|
||||
}),
|
||||
json(),
|
||||
commonjs(),
|
||||
oxc({
|
||||
transform: {
|
||||
target: 'node14',
|
||||
define: process.env.VITE_TEST_WATCHER_DEBUG === 'false'
|
||||
? { 'process.env.VITE_TEST_WATCHER_DEBUG': 'false' }
|
||||
: {},
|
||||
},
|
||||
sourcemap: true,
|
||||
}),
|
||||
]
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
input: entries,
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'esm',
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunk-[name].mjs',
|
||||
},
|
||||
external,
|
||||
plugins: [
|
||||
...dtsUtils.isolatedDecl(),
|
||||
...plugins,
|
||||
],
|
||||
onwarn,
|
||||
},
|
||||
{
|
||||
input: entries,
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'cjs',
|
||||
entryFileNames: '[name].cjs',
|
||||
chunkFileNames: 'chunk-[name].cjs',
|
||||
},
|
||||
external,
|
||||
plugins,
|
||||
onwarn,
|
||||
},
|
||||
{
|
||||
input: dtsUtils.dtsInput(entries, { ext: 'mts' }),
|
||||
output: {
|
||||
dir: 'dist',
|
||||
entryFileNames: '[name].d.ts',
|
||||
format: 'esm',
|
||||
},
|
||||
watch: false,
|
||||
external,
|
||||
plugins: dtsUtils.dts(),
|
||||
onwarn,
|
||||
},
|
||||
])
|
||||
|
||||
function onwarn(message) {
|
||||
if (['EMPTY_BUNDLE', 'CIRCULAR_DEPENDENCY'].includes(message.code)) {
|
||||
return
|
||||
}
|
||||
console.error(message)
|
||||
}
|
||||
@ -1,234 +0,0 @@
|
||||
import type { ViteNodeServerOptions } from './types'
|
||||
import { resolve } from 'node:path'
|
||||
import cac from 'cac'
|
||||
import c from 'tinyrainbow'
|
||||
import { createServer, loadEnv, version as viteVersion } from 'vite'
|
||||
import { version } from '../package.json'
|
||||
import { ViteNodeRunner } from './client'
|
||||
import { createHotContext, handleMessage, viteNodeHmrPlugin } from './hmr'
|
||||
import { ViteNodeServer } from './server'
|
||||
import { installSourcemapsSupport } from './source-map'
|
||||
import { toArray } from './utils'
|
||||
|
||||
const cli = cac('vite-node')
|
||||
|
||||
cli
|
||||
.option('-r, --root <path>', 'Use specified root directory')
|
||||
.option('-c, --config <path>', 'Use specified config file')
|
||||
.option('-m, --mode <mode>', 'Set env mode')
|
||||
.option('-w, --watch', 'Restart on file changes, similar to "nodemon"')
|
||||
.option('--script', 'Use vite-node as a script runner')
|
||||
.option('--options <options>', 'Use specified Vite server options')
|
||||
.option('-v, --version', 'Output the version number')
|
||||
.option('-h, --help', 'Display help for command')
|
||||
|
||||
cli.command('[...files]').allowUnknownOptions().action(run)
|
||||
|
||||
cli.parse(process.argv, { run: false })
|
||||
|
||||
if (cli.args.length === 0) {
|
||||
cli.runMatchedCommand()
|
||||
}
|
||||
else {
|
||||
const i = cli.rawArgs.indexOf(cli.args[0]) + 1
|
||||
const scriptArgs = cli.rawArgs.slice(i).filter(it => it !== '--')
|
||||
const executeArgs = [...cli.rawArgs.slice(0, i), '--', ...scriptArgs]
|
||||
cli.parse(executeArgs)
|
||||
}
|
||||
|
||||
export interface CliOptions {
|
||||
'root'?: string
|
||||
'script'?: boolean
|
||||
'config'?: string
|
||||
'mode'?: string
|
||||
'watch'?: boolean
|
||||
'options'?: ViteNodeServerOptionsCLI
|
||||
'version'?: boolean
|
||||
'help'?: boolean
|
||||
'--'?: string[]
|
||||
}
|
||||
|
||||
async function run(files: string[], options: CliOptions = {}) {
|
||||
if (options.script) {
|
||||
files = [files[0]]
|
||||
options = {}
|
||||
process.argv = [
|
||||
process.argv[0],
|
||||
resolve(files[0]),
|
||||
...process.argv
|
||||
.slice(2)
|
||||
.filter(arg => arg !== '--script' && arg !== files[0]),
|
||||
]
|
||||
}
|
||||
else {
|
||||
process.argv = [...process.argv.slice(0, 2), ...(options['--'] || [])]
|
||||
}
|
||||
|
||||
if (options.version) {
|
||||
cli.version(version)
|
||||
cli.outputVersion()
|
||||
process.exit(0)
|
||||
}
|
||||
if (options.help) {
|
||||
cli.version(version).outputHelp()
|
||||
process.exit(0)
|
||||
}
|
||||
if (!files.length) {
|
||||
console.error(c.red('No files specified.'))
|
||||
cli.version(version).outputHelp()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const serverOptions = options.options
|
||||
? parseServerOptions(options.options)
|
||||
: {}
|
||||
|
||||
const server = await createServer({
|
||||
logLevel: 'error',
|
||||
configFile: options.config,
|
||||
root: options.root,
|
||||
mode: options.mode,
|
||||
server: {
|
||||
hmr: !!options.watch,
|
||||
watch: options.watch ? undefined : null,
|
||||
},
|
||||
plugins: [options.watch && viteNodeHmrPlugin()],
|
||||
})
|
||||
if (Number(viteVersion.split('.')[0]) < 6) {
|
||||
await server.pluginContainer.buildStart({})
|
||||
}
|
||||
else {
|
||||
// directly access client plugin container until https://github.com/vitejs/vite/issues/19607
|
||||
await server.environments.client.pluginContainer.buildStart({})
|
||||
}
|
||||
|
||||
const env = loadEnv(server.config.mode, server.config.envDir, '')
|
||||
|
||||
for (const key in env) {
|
||||
process.env[key] ??= env[key]
|
||||
}
|
||||
|
||||
const node = new ViteNodeServer(server, serverOptions)
|
||||
|
||||
installSourcemapsSupport({
|
||||
getSourceMap: source => node.getSourceMap(source),
|
||||
})
|
||||
|
||||
const runner = new ViteNodeRunner({
|
||||
root: server.config.root,
|
||||
base: server.config.base,
|
||||
fetchModule(id) {
|
||||
return node.fetchModule(id)
|
||||
},
|
||||
resolveId(id, importer) {
|
||||
return node.resolveId(id, importer)
|
||||
},
|
||||
createHotContext(runner, url) {
|
||||
return createHotContext(runner, server.emitter, files, url)
|
||||
},
|
||||
})
|
||||
|
||||
// provide the vite define variable in this context
|
||||
await runner.executeId('/@vite/env')
|
||||
|
||||
for (const file of files) {
|
||||
await runner.executeFile(file)
|
||||
}
|
||||
|
||||
if (!options.watch) {
|
||||
await server.close()
|
||||
}
|
||||
|
||||
server.emitter?.on('message', (payload) => {
|
||||
handleMessage(runner, server.emitter, files, payload)
|
||||
})
|
||||
|
||||
if (options.watch) {
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error(c.red('[vite-node] Failed to execute file: \n'), err)
|
||||
})
|
||||
|
||||
if (process.env.VITE_TEST_WATCHER_DEBUG) {
|
||||
// manually check `watcher.getWatched()` to make sure entry files are ready
|
||||
// since watcher.on('ready', ...) event is not reliable since 5.1.
|
||||
// https://github.com/vitejs/vite/blob/63a39c244b08cf1f2299bc2c3cfddcb82070d05b/playground/hmr-ssr/__tests__/hmr.spec.ts#L1065
|
||||
|
||||
const nodePath = await import('node:path')
|
||||
|
||||
async function waitForWatched(files: string[]): Promise<void> {
|
||||
while (!files.every(file => isWatched(file))) {
|
||||
await new Promise(resolve => setTimeout(resolve, 20))
|
||||
}
|
||||
}
|
||||
|
||||
function isWatched(file: string): boolean {
|
||||
const watched = server.watcher.getWatched()
|
||||
const resolved = nodePath.resolve(file)
|
||||
const dir = nodePath.dirname(resolved)
|
||||
const base = nodePath.basename(resolved)
|
||||
return watched[dir]?.includes(base)
|
||||
}
|
||||
|
||||
await waitForWatched(files)
|
||||
|
||||
console.log('[debug] watcher is ready')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseServerOptions(
|
||||
serverOptions: ViteNodeServerOptionsCLI,
|
||||
): ViteNodeServerOptions {
|
||||
const inlineOptions
|
||||
= serverOptions.deps?.inline === true
|
||||
? true
|
||||
: toArray(serverOptions.deps?.inline)
|
||||
|
||||
return {
|
||||
...serverOptions,
|
||||
deps: {
|
||||
...serverOptions.deps,
|
||||
inlineFiles: toArray(serverOptions.deps?.inlineFiles),
|
||||
inline:
|
||||
inlineOptions !== true
|
||||
? inlineOptions.map((dep) => {
|
||||
return dep[0] === '/' && dep.endsWith('/')
|
||||
? new RegExp(dep)
|
||||
: dep
|
||||
})
|
||||
: true,
|
||||
external: toArray(serverOptions.deps?.external).map((dep) => {
|
||||
return dep[0] === '/' && dep.endsWith('/') ? new RegExp(dep) : dep
|
||||
}),
|
||||
moduleDirectories: serverOptions.deps?.moduleDirectories
|
||||
? toArray(serverOptions.deps?.moduleDirectories)
|
||||
: undefined,
|
||||
},
|
||||
|
||||
transformMode: {
|
||||
...serverOptions.transformMode,
|
||||
ssr: toArray(serverOptions.transformMode?.ssr).map(
|
||||
dep => new RegExp(dep),
|
||||
),
|
||||
web: toArray(serverOptions.transformMode?.web).map(
|
||||
dep => new RegExp(dep),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Optional<T> = T | undefined
|
||||
type ComputeViteNodeServerOptionsCLI<T extends Record<string, any>> = {
|
||||
[K in keyof T]: T[K] extends Optional<RegExp[]>
|
||||
? string | string[]
|
||||
: T[K] extends Optional<(string | RegExp)[]>
|
||||
? string | string[]
|
||||
: T[K] extends Optional<(string | RegExp)[] | true>
|
||||
? string | string[] | true
|
||||
: T[K] extends Optional<Record<string, any>>
|
||||
? ComputeViteNodeServerOptionsCLI<T[K]>
|
||||
: T[K];
|
||||
}
|
||||
|
||||
export type ViteNodeServerOptionsCLI
|
||||
= ComputeViteNodeServerOptionsCLI<ViteNodeServerOptions>
|
||||
@ -1,750 +0,0 @@
|
||||
import type { HotContext, ModuleCache, ViteNodeRunnerOptions } from './types'
|
||||
|
||||
import { createRequire } from 'node:module'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import vm from 'node:vm'
|
||||
import createDebug from 'debug'
|
||||
import { extractSourceMap } from './source-map'
|
||||
import {
|
||||
cleanUrl,
|
||||
createImportMetaEnvProxy,
|
||||
isBareImport,
|
||||
isInternalRequest,
|
||||
isNodeBuiltin,
|
||||
isPrimitive,
|
||||
normalizeModuleId,
|
||||
normalizeRequestId,
|
||||
slash,
|
||||
toFilePath,
|
||||
} from './utils'
|
||||
|
||||
const { setTimeout, clearTimeout } = globalThis
|
||||
|
||||
const debugExecute = createDebug('vite-node:client:execute')
|
||||
const debugNative = createDebug('vite-node:client:native')
|
||||
|
||||
const clientStub = {
|
||||
injectQuery: (id: string) => id,
|
||||
createHotContext: () => {
|
||||
return {
|
||||
accept: () => {},
|
||||
prune: () => {},
|
||||
dispose: () => {},
|
||||
decline: () => {},
|
||||
invalidate: () => {},
|
||||
on: () => {},
|
||||
send: () => {},
|
||||
}
|
||||
},
|
||||
updateStyle: () => {},
|
||||
removeStyle: () => {},
|
||||
}
|
||||
|
||||
const env = createImportMetaEnvProxy()
|
||||
|
||||
export const DEFAULT_REQUEST_STUBS: Record<string, Record<string, unknown>> = {
|
||||
'/@vite/client': clientStub,
|
||||
'@vite/client': clientStub,
|
||||
}
|
||||
|
||||
export class ModuleCacheMap extends Map<string, ModuleCache> {
|
||||
normalizePath(fsPath: string): string {
|
||||
return normalizeModuleId(fsPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign partial data to the map
|
||||
*/
|
||||
update(fsPath: string, mod: ModuleCache): this {
|
||||
fsPath = this.normalizePath(fsPath)
|
||||
if (!super.has(fsPath)) {
|
||||
this.setByModuleId(fsPath, mod)
|
||||
}
|
||||
else {
|
||||
Object.assign(super.get(fsPath) as ModuleCache, mod)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
setByModuleId(modulePath: string, mod: ModuleCache): this {
|
||||
return super.set(modulePath, mod)
|
||||
}
|
||||
|
||||
set(fsPath: string, mod: ModuleCache): this {
|
||||
return this.setByModuleId(this.normalizePath(fsPath), mod)
|
||||
}
|
||||
|
||||
getByModuleId(modulePath: string) {
|
||||
if (!super.has(modulePath)) {
|
||||
this.setByModuleId(modulePath, {})
|
||||
}
|
||||
|
||||
const mod = super.get(modulePath)!
|
||||
if (!mod.imports) {
|
||||
Object.assign(mod, {
|
||||
imports: new Set(),
|
||||
importers: new Set(),
|
||||
})
|
||||
}
|
||||
return mod as ModuleCache
|
||||
& Required<Pick<ModuleCache, 'imports' | 'importers'>>
|
||||
}
|
||||
|
||||
get(fsPath: string): ModuleCache & Required<Pick<ModuleCache, 'importers' | 'imports'>> {
|
||||
return this.getByModuleId(this.normalizePath(fsPath))
|
||||
}
|
||||
|
||||
deleteByModuleId(modulePath: string): boolean {
|
||||
return super.delete(modulePath)
|
||||
}
|
||||
|
||||
delete(fsPath: string): boolean {
|
||||
return this.deleteByModuleId(this.normalizePath(fsPath))
|
||||
}
|
||||
|
||||
invalidateModule(mod: ModuleCache) {
|
||||
delete mod.evaluated
|
||||
delete mod.resolving
|
||||
delete mod.promise
|
||||
delete mod.exports
|
||||
mod.importers?.clear()
|
||||
mod.imports?.clear()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate modules that dependent on the given modules, up to the main entry
|
||||
*/
|
||||
invalidateDepTree(
|
||||
ids: string[] | Set<string>,
|
||||
invalidated: Set<string> = new Set<string>(),
|
||||
): Set<string> {
|
||||
for (const _id of ids) {
|
||||
const id = this.normalizePath(_id)
|
||||
if (invalidated.has(id)) {
|
||||
continue
|
||||
}
|
||||
invalidated.add(id)
|
||||
const mod = super.get(id)
|
||||
if (mod?.importers) {
|
||||
this.invalidateDepTree(mod.importers, invalidated)
|
||||
}
|
||||
super.delete(id)
|
||||
}
|
||||
return invalidated
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate dependency modules of the given modules, down to the bottom-level dependencies
|
||||
*/
|
||||
invalidateSubDepTree(
|
||||
ids: string[] | Set<string>,
|
||||
invalidated: Set<string> = new Set<string>(),
|
||||
): Set<string> {
|
||||
for (const _id of ids) {
|
||||
const id = this.normalizePath(_id)
|
||||
if (invalidated.has(id)) {
|
||||
continue
|
||||
}
|
||||
invalidated.add(id)
|
||||
const subIds = Array.from(super.entries())
|
||||
.filter(([, mod]) => mod.importers?.has(id))
|
||||
.map(([key]) => key)
|
||||
if (subIds.length) {
|
||||
this.invalidateSubDepTree(subIds, invalidated)
|
||||
}
|
||||
super.delete(id)
|
||||
}
|
||||
return invalidated
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parsed source map based on inlined source map of the module
|
||||
*/
|
||||
getSourceMap(id: string): import('@jridgewell/trace-mapping').EncodedSourceMap | null {
|
||||
const cache = this.get(id)
|
||||
if (cache.map) {
|
||||
return cache.map
|
||||
}
|
||||
const map = cache.code && extractSourceMap(cache.code)
|
||||
if (map) {
|
||||
cache.map = map
|
||||
return map
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export type ModuleExecutionInfo = Map<string, ModuleExecutionInfoEntry>
|
||||
|
||||
export interface ModuleExecutionInfoEntry {
|
||||
startOffset: number
|
||||
|
||||
/** The duration that was spent executing the module. */
|
||||
duration: number
|
||||
|
||||
/** The time that was spent executing the module itself and externalized imports. */
|
||||
selfTime: number
|
||||
}
|
||||
|
||||
/** Stack to track nested module execution for self-time calculation. */
|
||||
type ExecutionStack = Array<{
|
||||
/** The file that is being executed. */
|
||||
filename: string
|
||||
|
||||
/** The start time of this module's execution. */
|
||||
startTime: number
|
||||
|
||||
/** Accumulated time spent importing all sub-imports. */
|
||||
subImportTime: number
|
||||
}>
|
||||
|
||||
export class ViteNodeRunner {
|
||||
root: string
|
||||
|
||||
debug: boolean
|
||||
|
||||
/**
|
||||
* Holds the cache of modules
|
||||
* Keys of the map are filepaths, or plain package names
|
||||
*/
|
||||
moduleCache: ModuleCacheMap
|
||||
|
||||
/**
|
||||
* Tracks the stack of modules being executed for the purpose of calculating import self-time.
|
||||
*
|
||||
* Note that while in most cases, imports are a linear stack of modules,
|
||||
* this is occasionally not the case, for example when you have parallel top-level dynamic imports like so:
|
||||
*
|
||||
* ```ts
|
||||
* await Promise.all([
|
||||
* import('./module1'),
|
||||
* import('./module2'),
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* In this case, the self time will be reported incorrectly for one of the modules (could go negative).
|
||||
* As top-level awaits with dynamic imports like this are uncommon, we don't handle this case specifically.
|
||||
*/
|
||||
private executionStack: ExecutionStack = []
|
||||
|
||||
// `performance` can be mocked, so make sure we're using the original function
|
||||
private performanceNow = performance.now.bind(performance)
|
||||
|
||||
constructor(public options: ViteNodeRunnerOptions) {
|
||||
this.root = options.root ?? process.cwd()
|
||||
this.moduleCache = options.moduleCache ?? new ModuleCacheMap()
|
||||
this.debug
|
||||
= options.debug
|
||||
?? (typeof process !== 'undefined'
|
||||
? !!process.env.VITE_NODE_DEBUG_RUNNER
|
||||
: false)
|
||||
}
|
||||
|
||||
async executeFile(file: string): Promise<any> {
|
||||
const url = `/@fs/${slash(resolve(file))}`
|
||||
return await this.cachedRequest(url, url, [])
|
||||
}
|
||||
|
||||
async executeId(rawId: string): Promise<any> {
|
||||
const [id, url] = await this.resolveUrl(rawId)
|
||||
return await this.cachedRequest(id, url, [])
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async cachedRequest(id: string, fsPath: string, callstack: string[]) {
|
||||
const importee = callstack.at(-1)
|
||||
|
||||
const mod = this.moduleCache.get(fsPath)
|
||||
const { imports, importers } = mod
|
||||
|
||||
if (importee) {
|
||||
importers.add(importee)
|
||||
}
|
||||
|
||||
const getStack = () =>
|
||||
`stack:\n${[...callstack, fsPath]
|
||||
.reverse()
|
||||
.map(p => ` - ${p}`)
|
||||
.join('\n')}`
|
||||
|
||||
// check circular dependency
|
||||
if (
|
||||
callstack.includes(fsPath)
|
||||
|| Array.from(imports.values()).some(i => importers.has(i))
|
||||
) {
|
||||
if (mod.exports) {
|
||||
return mod.exports
|
||||
}
|
||||
}
|
||||
|
||||
let debugTimer: any
|
||||
if (this.debug) {
|
||||
debugTimer = setTimeout(
|
||||
() =>
|
||||
console.warn(
|
||||
`[vite-node] module ${fsPath} takes over 2s to load.\n${getStack()}`,
|
||||
),
|
||||
2000,
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
// cached module
|
||||
if (mod.promise) {
|
||||
return await mod.promise
|
||||
}
|
||||
|
||||
const promise = this.directRequest(id, fsPath, callstack)
|
||||
Object.assign(mod, { promise, evaluated: false })
|
||||
return await promise
|
||||
}
|
||||
finally {
|
||||
mod.evaluated = true
|
||||
if (debugTimer) {
|
||||
clearTimeout(debugTimer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldResolveId(id: string, _importee?: string): boolean {
|
||||
return (
|
||||
!isInternalRequest(id) && !isNodeBuiltin(id) && !id.startsWith('data:')
|
||||
)
|
||||
}
|
||||
|
||||
private async _resolveUrl(
|
||||
id: string,
|
||||
importer?: string,
|
||||
): Promise<[url: string, fsPath: string]> {
|
||||
const dep = normalizeRequestId(id, this.options.base)
|
||||
if (!this.shouldResolveId(dep)) {
|
||||
return [dep, dep]
|
||||
}
|
||||
const { path, exists } = toFilePath(dep, this.root)
|
||||
if (!this.options.resolveId || exists) {
|
||||
return [dep, path]
|
||||
}
|
||||
const resolved = await this.options.resolveId(dep, importer)
|
||||
// supported since Vite 5-beta.19
|
||||
if (resolved?.meta?.['vite:alias']?.noResolved) {
|
||||
const error = new Error(
|
||||
`Cannot find module '${id}'${
|
||||
importer ? ` imported from '${importer}'` : ''
|
||||
}.`
|
||||
+ '\n\n- If you rely on tsconfig.json\'s "paths" to resolve modules, please install "vite-tsconfig-paths" plugin to handle module resolution.'
|
||||
+ '\n- Make sure you don\'t have relative aliases in your Vitest config. Use absolute paths instead. Read more: https://vitest.dev/guide/common-errors',
|
||||
)
|
||||
Object.defineProperty(error, 'code', {
|
||||
value: 'ERR_MODULE_NOT_FOUND',
|
||||
enumerable: true,
|
||||
})
|
||||
Object.defineProperty(error, Symbol.for('vitest.error.not_found.data'), {
|
||||
value: { id: dep, importer },
|
||||
enumerable: false,
|
||||
})
|
||||
throw error
|
||||
}
|
||||
const resolvedId = resolved
|
||||
? normalizeRequestId(resolved.id, this.options.base)
|
||||
: dep
|
||||
return [resolvedId, resolvedId]
|
||||
}
|
||||
|
||||
async resolveUrl(id: string, importee?: string): Promise<[url: string, fsPath: string]> {
|
||||
const resolveKey = `resolve:${id}`
|
||||
// put info about new import as soon as possible, so we can start tracking it
|
||||
this.moduleCache.setByModuleId(resolveKey, { resolving: true })
|
||||
try {
|
||||
return await this._resolveUrl(id, importee)
|
||||
}
|
||||
finally {
|
||||
this.moduleCache.deleteByModuleId(resolveKey)
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async dependencyRequest(id: string, fsPath: string, callstack: string[]) {
|
||||
return await this.cachedRequest(id, fsPath, callstack)
|
||||
}
|
||||
|
||||
private async _fetchModule(id: string, importer?: string) {
|
||||
try {
|
||||
return await this.options.fetchModule(id)
|
||||
}
|
||||
catch (cause: any) {
|
||||
// rethrow vite error if it cannot load the module because it's not resolved
|
||||
if (
|
||||
(typeof cause === 'object' && cause.code === 'ERR_LOAD_URL')
|
||||
|| (typeof cause?.message === 'string' && cause.message.includes('Failed to load url'))
|
||||
) {
|
||||
const error = new Error(
|
||||
`Cannot find ${isBareImport(id) ? 'package' : 'module'} '${id}'${importer ? ` imported from '${importer}'` : ''}`,
|
||||
{ cause },
|
||||
) as Error & { code: string }
|
||||
error.code = 'ERR_MODULE_NOT_FOUND'
|
||||
throw error
|
||||
}
|
||||
|
||||
throw cause
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async directRequest(id: string, fsPath: string, _callstack: string[]) {
|
||||
const moduleId = normalizeModuleId(fsPath)
|
||||
const callstack = [..._callstack, moduleId]
|
||||
|
||||
const mod = this.moduleCache.getByModuleId(moduleId)
|
||||
|
||||
const request = async (dep: string) => {
|
||||
const [id, depFsPath] = await this.resolveUrl(String(dep), fsPath)
|
||||
const depMod = this.moduleCache.getByModuleId(depFsPath)
|
||||
depMod.importers.add(moduleId)
|
||||
mod.imports.add(depFsPath)
|
||||
|
||||
return this.dependencyRequest(id, depFsPath, callstack)
|
||||
}
|
||||
|
||||
const requestStubs = this.options.requestStubs || DEFAULT_REQUEST_STUBS
|
||||
if (id in requestStubs) {
|
||||
return requestStubs[id]
|
||||
}
|
||||
let { code: transformed, externalize } = await this._fetchModule(
|
||||
id,
|
||||
callstack[callstack.length - 2],
|
||||
)
|
||||
|
||||
if (externalize) {
|
||||
debugNative(externalize)
|
||||
const exports = await this.interopedImport(externalize)
|
||||
mod.exports = exports
|
||||
return exports
|
||||
}
|
||||
|
||||
if (transformed == null) {
|
||||
throw new Error(
|
||||
`[vite-node] Failed to load "${id}" imported from ${
|
||||
callstack[callstack.length - 2]
|
||||
}`,
|
||||
)
|
||||
}
|
||||
|
||||
const { Object, Reflect, Symbol } = this.getContextPrimitives()
|
||||
|
||||
const modulePath = cleanUrl(moduleId)
|
||||
// disambiguate the `<UNIT>:/` on windows: see nodejs/node#31710
|
||||
const href = pathToFileURL(modulePath).href
|
||||
const __filename = fileURLToPath(href)
|
||||
const __dirname = dirname(__filename)
|
||||
const meta = {
|
||||
url: href,
|
||||
env,
|
||||
filename: __filename,
|
||||
dirname: __dirname,
|
||||
}
|
||||
const exports = Object.create(null)
|
||||
Object.defineProperty(exports, Symbol.toStringTag, {
|
||||
value: 'Module',
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
})
|
||||
const SYMBOL_NOT_DEFINED = Symbol('not defined')
|
||||
let moduleExports: unknown = SYMBOL_NOT_DEFINED
|
||||
// this proxy is triggered only on exports.{name} and module.exports access
|
||||
// inside the module itself. imported module is always "exports"
|
||||
const cjsExports = new Proxy(exports, {
|
||||
get: (target, p, receiver) => {
|
||||
if (Reflect.has(target, p)) {
|
||||
return Reflect.get(target, p, receiver)
|
||||
}
|
||||
return Reflect.get(Object.prototype, p, receiver)
|
||||
},
|
||||
getPrototypeOf: () => Object.prototype,
|
||||
set: (_, p, value) => {
|
||||
// treat "module.exports =" the same as "exports.default =" to not have nested "default.default",
|
||||
// so "exports.default" becomes the actual module
|
||||
if (
|
||||
p === 'default'
|
||||
&& this.shouldInterop(modulePath, { default: value })
|
||||
&& cjsExports !== value
|
||||
) {
|
||||
exportAll(cjsExports, value)
|
||||
exports.default = value
|
||||
return true
|
||||
}
|
||||
|
||||
if (!Reflect.has(exports, 'default')) {
|
||||
exports.default = {}
|
||||
}
|
||||
|
||||
// returns undefined, when accessing named exports, if default is not an object
|
||||
// but is still present inside hasOwnKeys, this is Node behaviour for CJS
|
||||
if (
|
||||
moduleExports !== SYMBOL_NOT_DEFINED
|
||||
&& isPrimitive(moduleExports)
|
||||
) {
|
||||
defineExport(exports, p, () => undefined)
|
||||
return true
|
||||
}
|
||||
|
||||
if (!isPrimitive(exports.default)) {
|
||||
exports.default[p] = value
|
||||
}
|
||||
|
||||
if (p !== 'default') {
|
||||
defineExport(exports, p, () => value)
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
})
|
||||
|
||||
Object.assign(mod, { code: transformed, exports })
|
||||
const moduleProxy = {
|
||||
set exports(value) {
|
||||
exportAll(cjsExports, value)
|
||||
exports.default = value
|
||||
moduleExports = value
|
||||
},
|
||||
get exports() {
|
||||
return cjsExports
|
||||
},
|
||||
}
|
||||
|
||||
// Vite hot context
|
||||
let hotContext: HotContext | undefined
|
||||
if (this.options.createHotContext) {
|
||||
Object.defineProperty(meta, 'hot', {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
hotContext ||= this.options.createHotContext?.(this, moduleId)
|
||||
return hotContext
|
||||
},
|
||||
set: (value) => {
|
||||
hotContext = value
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Be careful when changing this
|
||||
// changing context will change amount of code added on line :114 (vm.runInThisContext)
|
||||
// this messes up sourcemaps for coverage
|
||||
// adjust `WRAPPER_LENGTH` variable in packages/coverage-v8/src/provider.ts if you do change this
|
||||
const context = this.prepareContext({
|
||||
// esm transformed by Vite
|
||||
__vite_ssr_import__: request,
|
||||
__vite_ssr_dynamic_import__: request,
|
||||
__vite_ssr_exports__: exports,
|
||||
__vite_ssr_exportAll__: (obj: any) => exportAll(exports, obj),
|
||||
__vite_ssr_exportName__: (name: string, getter: () => unknown) => Object.defineProperty(exports, name, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: getter,
|
||||
}),
|
||||
__vite_ssr_import_meta__: meta,
|
||||
|
||||
// cjs compact
|
||||
require: createRequire(href),
|
||||
exports: cjsExports,
|
||||
module: moduleProxy,
|
||||
__filename,
|
||||
__dirname,
|
||||
})
|
||||
|
||||
debugExecute(__filename)
|
||||
|
||||
// remove shebang
|
||||
if (transformed[0] === '#') {
|
||||
transformed = transformed.replace(/^#!.*/, s => ' '.repeat(s.length))
|
||||
}
|
||||
|
||||
await this.runModule(context, transformed)
|
||||
|
||||
return exports
|
||||
}
|
||||
|
||||
protected getContextPrimitives(): {
|
||||
Object: ObjectConstructor
|
||||
Reflect: typeof Reflect
|
||||
Symbol: SymbolConstructor
|
||||
} {
|
||||
return { Object, Reflect, Symbol }
|
||||
}
|
||||
|
||||
protected async runModule(context: Record<string, any>, transformed: string): Promise<void> {
|
||||
// add 'use strict' since ESM enables it by default
|
||||
const codeDefinition = `'use strict';async (${Object.keys(context).join(
|
||||
',',
|
||||
)})=>{{`
|
||||
const code = `${codeDefinition}${transformed}\n}}`
|
||||
const options = {
|
||||
filename: context.__filename,
|
||||
lineOffset: 0,
|
||||
columnOffset: -codeDefinition.length,
|
||||
}
|
||||
|
||||
const finishModuleExecutionInfo = this.startCalculateModuleExecutionInfo(options.filename, codeDefinition.length)
|
||||
|
||||
try {
|
||||
const fn = vm.runInThisContext(code, options)
|
||||
await fn(...Object.values(context))
|
||||
}
|
||||
finally {
|
||||
this.options.moduleExecutionInfo?.set(options.filename, finishModuleExecutionInfo())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts calculating the module execution info such as the total duration and self time spent on executing the module.
|
||||
* Returns a function to call once the module has finished executing.
|
||||
*/
|
||||
protected startCalculateModuleExecutionInfo(filename: string, startOffset: number): () => ModuleExecutionInfoEntry {
|
||||
const startTime = this.performanceNow()
|
||||
|
||||
this.executionStack.push({
|
||||
filename,
|
||||
startTime,
|
||||
subImportTime: 0,
|
||||
})
|
||||
|
||||
return () => {
|
||||
const duration = this.performanceNow() - startTime
|
||||
|
||||
const currentExecution = this.executionStack.pop()
|
||||
|
||||
if (currentExecution == null) {
|
||||
throw new Error('Execution stack is empty, this should never happen')
|
||||
}
|
||||
|
||||
const selfTime = duration - currentExecution.subImportTime
|
||||
|
||||
if (this.executionStack.length > 0) {
|
||||
this.executionStack.at(-1)!.subImportTime += duration
|
||||
}
|
||||
|
||||
return {
|
||||
startOffset,
|
||||
duration,
|
||||
selfTime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepareContext(context: Record<string, any>): Record<string, any> {
|
||||
return context
|
||||
}
|
||||
|
||||
/**
|
||||
* Define if a module should be interop-ed
|
||||
* This function mostly for the ability to override by subclass
|
||||
*/
|
||||
shouldInterop(path: string, mod: any): boolean {
|
||||
if (this.options.interopDefault === false) {
|
||||
return false
|
||||
}
|
||||
// never interop ESM modules
|
||||
// TODO: should also skip for `.js` with `type="module"`
|
||||
return !path.endsWith('.mjs') && 'default' in mod
|
||||
}
|
||||
|
||||
protected importExternalModule(path: string): Promise<any> {
|
||||
return import(/* @vite-ignore */ path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a module and interop it
|
||||
*/
|
||||
async interopedImport(path: string): Promise<any> {
|
||||
const importedModule = await this.importExternalModule(path)
|
||||
|
||||
if (!this.shouldInterop(path, importedModule)) {
|
||||
return importedModule
|
||||
}
|
||||
|
||||
const { mod, defaultExport } = interopModule(importedModule)
|
||||
|
||||
return new Proxy(mod, {
|
||||
get(mod, prop) {
|
||||
if (prop === 'default') {
|
||||
return defaultExport
|
||||
}
|
||||
return mod[prop] ?? defaultExport?.[prop]
|
||||
},
|
||||
has(mod, prop) {
|
||||
if (prop === 'default') {
|
||||
return defaultExport !== undefined
|
||||
}
|
||||
return prop in mod || (defaultExport && prop in defaultExport)
|
||||
},
|
||||
getOwnPropertyDescriptor(mod, prop) {
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(mod, prop)
|
||||
if (descriptor) {
|
||||
return descriptor
|
||||
}
|
||||
if (prop === 'default' && defaultExport !== undefined) {
|
||||
return {
|
||||
value: defaultExport,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function interopModule(mod: any) {
|
||||
if (isPrimitive(mod)) {
|
||||
return {
|
||||
mod: { default: mod },
|
||||
defaultExport: mod,
|
||||
}
|
||||
}
|
||||
|
||||
let defaultExport = 'default' in mod ? mod.default : mod
|
||||
|
||||
if (!isPrimitive(defaultExport) && '__esModule' in defaultExport) {
|
||||
mod = defaultExport
|
||||
if ('default' in defaultExport) {
|
||||
defaultExport = defaultExport.default
|
||||
}
|
||||
}
|
||||
|
||||
return { mod, defaultExport }
|
||||
}
|
||||
|
||||
// keep consistency with Vite on how exports are defined
|
||||
function defineExport(exports: any, key: string | symbol, value: () => any) {
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: value,
|
||||
})
|
||||
}
|
||||
|
||||
function exportAll(exports: any, sourceModule: any) {
|
||||
// #1120 when a module exports itself it causes
|
||||
// call stack error
|
||||
if (exports === sourceModule) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
isPrimitive(sourceModule)
|
||||
|| Array.isArray(sourceModule)
|
||||
|| sourceModule instanceof Promise
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const key in sourceModule) {
|
||||
if (key !== 'default' && !(key in exports)) {
|
||||
try {
|
||||
defineExport(exports, key, () => sourceModule[key])
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
export const KNOWN_ASSET_TYPES: string[] = [
|
||||
// images
|
||||
'apng',
|
||||
'bmp',
|
||||
'png',
|
||||
'jpe?g',
|
||||
'jfif',
|
||||
'pjpeg',
|
||||
'pjp',
|
||||
'gif',
|
||||
'svg',
|
||||
'ico',
|
||||
'webp',
|
||||
'avif',
|
||||
|
||||
// media
|
||||
'mp4',
|
||||
'webm',
|
||||
'ogg',
|
||||
'mp3',
|
||||
'wav',
|
||||
'flac',
|
||||
'aac',
|
||||
|
||||
// fonts
|
||||
'woff2?',
|
||||
'eot',
|
||||
'ttf',
|
||||
'otf',
|
||||
|
||||
// other
|
||||
'webmanifest',
|
||||
'pdf',
|
||||
'txt',
|
||||
]
|
||||
|
||||
export const KNOWN_ASSET_RE: RegExp = new RegExp(
|
||||
`\\.(${KNOWN_ASSET_TYPES.join('|')})$`,
|
||||
)
|
||||
export const CSS_LANGS_RE: RegExp
|
||||
= /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/
|
||||
@ -1,110 +0,0 @@
|
||||
import type { TransformResult } from 'vite'
|
||||
import type { DebuggerOptions } from './types'
|
||||
/* eslint-disable no-console */
|
||||
import { existsSync, promises as fs } from 'node:fs'
|
||||
import { join, resolve } from 'pathe'
|
||||
import c from 'tinyrainbow'
|
||||
|
||||
function hashCode(s: string) {
|
||||
return s.split('').reduce((a, b) => {
|
||||
a = (a << 5) - a + b.charCodeAt(0)
|
||||
return a & a
|
||||
}, 0)
|
||||
}
|
||||
|
||||
export class Debugger {
|
||||
dumpDir: string | undefined
|
||||
initPromise: Promise<void> | undefined
|
||||
externalizeMap: Map<string, string> = new Map()
|
||||
|
||||
constructor(root: string, public options: DebuggerOptions) {
|
||||
if (options.dumpModules) {
|
||||
this.dumpDir = resolve(
|
||||
root,
|
||||
options.dumpModules === true ? '.vite-node/dump' : options.dumpModules,
|
||||
)
|
||||
}
|
||||
if (this.dumpDir) {
|
||||
if (options.loadDumppedModules) {
|
||||
console.info(
|
||||
c.gray(`[vite-node] [debug] load modules from ${this.dumpDir}`),
|
||||
)
|
||||
}
|
||||
else {
|
||||
console.info(
|
||||
c.gray(`[vite-node] [debug] dump modules to ${this.dumpDir}`),
|
||||
)
|
||||
}
|
||||
}
|
||||
this.initPromise = this.clearDump()
|
||||
}
|
||||
|
||||
async clearDump(): Promise<void> {
|
||||
if (!this.dumpDir) {
|
||||
return
|
||||
}
|
||||
if (!this.options.loadDumppedModules && existsSync(this.dumpDir)) {
|
||||
await fs.rm(this.dumpDir, { recursive: true, force: true })
|
||||
}
|
||||
await fs.mkdir(this.dumpDir, { recursive: true })
|
||||
}
|
||||
|
||||
encodeId(id: string) {
|
||||
return `${id.replace(/[^\w@\-]/g, '_').replace(/_+/g, '_')}-${hashCode(
|
||||
id,
|
||||
)}.js`
|
||||
}
|
||||
|
||||
async recordExternalize(id: string, path: string): Promise<void> {
|
||||
if (!this.dumpDir) {
|
||||
return
|
||||
}
|
||||
this.externalizeMap.set(id, path)
|
||||
await this.writeInfo()
|
||||
}
|
||||
|
||||
async dumpFile(id: string, result: TransformResult | null): Promise<void> {
|
||||
if (!result || !this.dumpDir) {
|
||||
return
|
||||
}
|
||||
await this.initPromise
|
||||
const name = this.encodeId(id)
|
||||
return await fs.writeFile(
|
||||
join(this.dumpDir, name),
|
||||
`// ${id.replace(/\0/g, '\\0')}\n${result.code}`,
|
||||
'utf-8',
|
||||
)
|
||||
}
|
||||
|
||||
async loadDump(id: string): Promise<TransformResult | null> {
|
||||
if (!this.dumpDir) {
|
||||
return null
|
||||
}
|
||||
await this.initPromise
|
||||
const name = this.encodeId(id)
|
||||
const path = join(this.dumpDir, name)
|
||||
if (!existsSync(path)) {
|
||||
return null
|
||||
}
|
||||
const code = await fs.readFile(path, 'utf-8')
|
||||
return {
|
||||
code: code.replace(/^\/\/.*\n/, ''),
|
||||
map: undefined!,
|
||||
}
|
||||
}
|
||||
|
||||
async writeInfo(): Promise<void> {
|
||||
if (!this.dumpDir) {
|
||||
return
|
||||
}
|
||||
const info = JSON.stringify(
|
||||
{
|
||||
time: new Date().toLocaleString(),
|
||||
externalize: Object.fromEntries(this.externalizeMap.entries()),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)
|
||||
return fs.writeFile(join(this.dumpDir, 'info.json'), info, 'utf-8')
|
||||
}
|
||||
}
|
||||
@ -1,191 +0,0 @@
|
||||
import type { DepsHandlingOptions } from './types'
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import * as esModuleLexer from 'es-module-lexer'
|
||||
import { dirname, extname, join } from 'pathe'
|
||||
import { KNOWN_ASSET_RE } from './constants'
|
||||
import { findNearestPackageData, isNodeBuiltin, slash } from './utils'
|
||||
|
||||
const BUILTIN_EXTENSIONS = new Set(['.mjs', '.cjs', '.node', '.wasm'])
|
||||
|
||||
const ESM_EXT_RE = /\.(es|esm|esm-browser|esm-bundler|es6|module)\.js$/
|
||||
const ESM_FOLDER_RE = /\/(es|esm)\/(.*\.js)$/
|
||||
|
||||
const defaultInline = [
|
||||
/virtual:/,
|
||||
/\.[mc]?ts$/,
|
||||
|
||||
// special Vite query strings
|
||||
/[?&](init|raw|url|inline)\b/,
|
||||
// Vite returns a string for assets imports, even if it's inside "node_modules"
|
||||
KNOWN_ASSET_RE,
|
||||
]
|
||||
|
||||
const depsExternal = [
|
||||
/\/node_modules\/.*\.cjs\.js$/,
|
||||
/\/node_modules\/.*\.mjs$/,
|
||||
]
|
||||
|
||||
export function guessCJSversion(id: string): string | undefined {
|
||||
if (id.match(ESM_EXT_RE)) {
|
||||
for (const i of [
|
||||
id.replace(ESM_EXT_RE, '.mjs'),
|
||||
id.replace(ESM_EXT_RE, '.umd.js'),
|
||||
id.replace(ESM_EXT_RE, '.cjs.js'),
|
||||
id.replace(ESM_EXT_RE, '.js'),
|
||||
]) {
|
||||
if (existsSync(i)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id.match(ESM_FOLDER_RE)) {
|
||||
for (const i of [
|
||||
id.replace(ESM_FOLDER_RE, '/umd/$1'),
|
||||
id.replace(ESM_FOLDER_RE, '/cjs/$1'),
|
||||
id.replace(ESM_FOLDER_RE, '/lib/$1'),
|
||||
id.replace(ESM_FOLDER_RE, '/$1'),
|
||||
]) {
|
||||
if (existsSync(i)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The code from https://github.com/unjs/mlly/blob/c5bcca0cda175921344fd6de1bc0c499e73e5dac/src/syntax.ts#L51-L98
|
||||
async function isValidNodeImport(id: string) {
|
||||
const extension = extname(id)
|
||||
|
||||
if (BUILTIN_EXTENSIONS.has(extension)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (extension !== '.js') {
|
||||
return false
|
||||
}
|
||||
|
||||
id = id.replace('file:///', '')
|
||||
|
||||
const package_ = await findNearestPackageData(dirname(id))
|
||||
|
||||
if (package_.type === 'module') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (/\.(?:\w+-)?esm?(?:-\w+)?\.js$|\/esm?\//.test(id)) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
await esModuleLexer.init
|
||||
const code = await fsp.readFile(id, 'utf8')
|
||||
const [, , , hasModuleSyntax] = esModuleLexer.parse(code)
|
||||
return !hasModuleSyntax
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const _defaultExternalizeCache = new Map<string, Promise<string | false>>()
|
||||
export async function shouldExternalize(
|
||||
id: string,
|
||||
options?: DepsHandlingOptions,
|
||||
cache: Map<string, Promise<string | false>> = _defaultExternalizeCache,
|
||||
): Promise<string | false> {
|
||||
if (!cache.has(id)) {
|
||||
cache.set(id, _shouldExternalize(id, options))
|
||||
}
|
||||
return cache.get(id)!
|
||||
}
|
||||
|
||||
async function _shouldExternalize(
|
||||
id: string,
|
||||
options?: DepsHandlingOptions,
|
||||
): Promise<string | false> {
|
||||
if (isNodeBuiltin(id)) {
|
||||
return id
|
||||
}
|
||||
|
||||
// data: should be processed by native import,
|
||||
// since it is a feature of ESM.
|
||||
// also externalize network imports since nodejs allows it when --experimental-network-imports
|
||||
if (id.startsWith('data:') || /^(?:https?:)?\/\//.test(id)) {
|
||||
return id
|
||||
}
|
||||
|
||||
id = patchWindowsImportPath(id)
|
||||
|
||||
const moduleDirectories = options?.moduleDirectories || ['/node_modules/']
|
||||
|
||||
if (matchExternalizePattern(id, moduleDirectories, options?.inline)) {
|
||||
return false
|
||||
}
|
||||
if (options?.inlineFiles && options?.inlineFiles.includes(id)) {
|
||||
return false
|
||||
}
|
||||
if (matchExternalizePattern(id, moduleDirectories, options?.external)) {
|
||||
return id
|
||||
}
|
||||
|
||||
// Unless the user explicitly opted to inline them, externalize Vite deps.
|
||||
// They are too big to inline by default.
|
||||
if (options?.cacheDir && id.includes(options.cacheDir)) {
|
||||
return id
|
||||
}
|
||||
|
||||
const isLibraryModule = moduleDirectories.some(dir => id.includes(dir))
|
||||
const guessCJS = isLibraryModule && options?.fallbackCJS
|
||||
id = guessCJS ? guessCJSversion(id) || id : id
|
||||
|
||||
if (matchExternalizePattern(id, moduleDirectories, defaultInline)) {
|
||||
return false
|
||||
}
|
||||
if (matchExternalizePattern(id, moduleDirectories, depsExternal)) {
|
||||
return id
|
||||
}
|
||||
|
||||
if (isLibraryModule && (await isValidNodeImport(id))) {
|
||||
return id
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function matchExternalizePattern(
|
||||
id: string,
|
||||
moduleDirectories: string[],
|
||||
patterns?: (string | RegExp)[] | true,
|
||||
) {
|
||||
if (patterns == null) {
|
||||
return false
|
||||
}
|
||||
if (patterns === true) {
|
||||
return true
|
||||
}
|
||||
for (const ex of patterns) {
|
||||
if (typeof ex === 'string') {
|
||||
if (moduleDirectories.some(dir => id.includes(join(dir, ex)))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ex.test(id)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function patchWindowsImportPath(path: string) {
|
||||
if (path.match(/^\w:\\/)) {
|
||||
return `file:///${slash(path)}`
|
||||
}
|
||||
else if (path.match(/^\w:\//)) {
|
||||
return `file:///${path}`
|
||||
}
|
||||
else {
|
||||
return path
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import type { HMRPayload, Plugin } from 'vite'
|
||||
import { EventEmitter } from 'node:events'
|
||||
|
||||
export type EventType = string | symbol
|
||||
export type Handler<T = unknown> = (event: T) => void
|
||||
export interface Emitter<Events extends Record<EventType, unknown>> {
|
||||
on: <Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler: Handler<Events[Key]>
|
||||
) => void
|
||||
off: <Key extends keyof Events>(
|
||||
type: Key,
|
||||
handler?: Handler<Events[Key]>
|
||||
) => void
|
||||
emit: (<Key extends keyof Events>(type: Key, event: Events[Key]) => void)
|
||||
& (<Key extends keyof Events>(
|
||||
type: undefined extends Events[Key] ? Key : never
|
||||
) => void)
|
||||
}
|
||||
|
||||
export type HMREmitter = Emitter<{
|
||||
message: HMRPayload
|
||||
}>
|
||||
& EventEmitter
|
||||
|
||||
declare module 'vite' {
|
||||
interface ViteDevServer {
|
||||
emitter: HMREmitter
|
||||
}
|
||||
}
|
||||
|
||||
export function createHmrEmitter(): HMREmitter {
|
||||
const emitter = new EventEmitter()
|
||||
return emitter as HMREmitter
|
||||
}
|
||||
|
||||
export function viteNodeHmrPlugin(): Plugin {
|
||||
const emitter = createHmrEmitter()
|
||||
return {
|
||||
name: 'vite-node:hmr',
|
||||
|
||||
config() {
|
||||
// chokidar fsevents is unstable on macos when emitting "ready" event
|
||||
if (
|
||||
process.platform === 'darwin'
|
||||
&& process.env.VITE_TEST_WATCHER_DEBUG
|
||||
) {
|
||||
return {
|
||||
server: {
|
||||
watch: {
|
||||
useFsEvents: false,
|
||||
usePolling: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
configureServer(server) {
|
||||
const _send = server.ws.send
|
||||
server.emitter = emitter
|
||||
server.ws.send = function (payload: any) {
|
||||
_send(payload)
|
||||
emitter.emit('message', payload)
|
||||
}
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-ignore Vite 6 compat
|
||||
const environments = server.environments
|
||||
if (environments) {
|
||||
environments.ssr.hot.send = function (payload: any) {
|
||||
_send(payload)
|
||||
emitter.emit('message', payload)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1,349 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import type { CustomEventMap } from 'vite/types/customEvent.js'
|
||||
import type { HMRPayload, Update } from 'vite/types/hmrPayload.js'
|
||||
import type { ViteNodeRunner } from '../client'
|
||||
import type { HotContext } from '../types'
|
||||
import type { HMREmitter } from './emitter'
|
||||
import createDebug from 'debug'
|
||||
import c from 'tinyrainbow'
|
||||
import { normalizeRequestId } from '../utils'
|
||||
|
||||
export type ModuleNamespace = Record<string, any> & {
|
||||
[Symbol.toStringTag]: 'Module'
|
||||
}
|
||||
|
||||
const debugHmr = createDebug('vite-node:hmr')
|
||||
|
||||
export type InferCustomEventPayload<T extends string>
|
||||
= T extends keyof CustomEventMap ? CustomEventMap[T] : any
|
||||
|
||||
export interface HotModule {
|
||||
id: string
|
||||
callbacks: HotCallback[]
|
||||
}
|
||||
|
||||
export interface HotCallback {
|
||||
// the dependencies must be fetchable paths
|
||||
deps: string[]
|
||||
fn: (modules: (ModuleNamespace | undefined)[]) => void
|
||||
}
|
||||
|
||||
interface CacheData {
|
||||
hotModulesMap: Map<string, HotModule>
|
||||
dataMap: Map<string, any>
|
||||
disposeMap: Map<string, (data: any) => void | Promise<void>>
|
||||
pruneMap: Map<string, (data: any) => void | Promise<void>>
|
||||
customListenersMap: Map<string, ((data: any) => void)[]>
|
||||
ctxToListenersMap: Map<string, Map<string, ((data: any) => void)[]>>
|
||||
messageBuffer: string[]
|
||||
isFirstUpdate: boolean
|
||||
pending: boolean
|
||||
queued: Promise<(() => void) | undefined>[]
|
||||
}
|
||||
|
||||
const cache: WeakMap<ViteNodeRunner, CacheData> = new WeakMap()
|
||||
|
||||
export function getCache(runner: ViteNodeRunner): CacheData {
|
||||
if (!cache.has(runner)) {
|
||||
cache.set(runner, {
|
||||
hotModulesMap: new Map(),
|
||||
dataMap: new Map(),
|
||||
disposeMap: new Map(),
|
||||
pruneMap: new Map(),
|
||||
customListenersMap: new Map(),
|
||||
ctxToListenersMap: new Map(),
|
||||
messageBuffer: [],
|
||||
isFirstUpdate: false,
|
||||
pending: false,
|
||||
queued: [],
|
||||
})
|
||||
}
|
||||
return cache.get(runner) as CacheData
|
||||
}
|
||||
|
||||
export function sendMessageBuffer(runner: ViteNodeRunner, emitter: HMREmitter): void {
|
||||
const maps = getCache(runner)
|
||||
maps.messageBuffer.forEach(msg => emitter.emit('custom', msg))
|
||||
maps.messageBuffer.length = 0
|
||||
}
|
||||
|
||||
export async function reload(runner: ViteNodeRunner, files: string[]): Promise<any[]> {
|
||||
// invalidate module cache but not node_modules
|
||||
Array.from(runner.moduleCache.keys()).forEach((fsPath) => {
|
||||
if (!fsPath.includes('node_modules')) {
|
||||
runner.moduleCache.delete(fsPath)
|
||||
}
|
||||
})
|
||||
|
||||
return Promise.all(files.map(file => runner.executeId(file)))
|
||||
}
|
||||
|
||||
async function notifyListeners<T extends string>(
|
||||
runner: ViteNodeRunner,
|
||||
event: T,
|
||||
data: InferCustomEventPayload<T>
|
||||
): Promise<void>
|
||||
async function notifyListeners(
|
||||
runner: ViteNodeRunner,
|
||||
event: string,
|
||||
data: any,
|
||||
): Promise<void> {
|
||||
const maps = getCache(runner)
|
||||
const cbs = maps.customListenersMap.get(event)
|
||||
if (cbs) {
|
||||
await Promise.all(cbs.map(cb => cb(data)))
|
||||
}
|
||||
}
|
||||
|
||||
async function queueUpdate(
|
||||
runner: ViteNodeRunner,
|
||||
p: Promise<(() => void) | undefined>,
|
||||
) {
|
||||
const maps = getCache(runner)
|
||||
maps.queued.push(p)
|
||||
if (!maps.pending) {
|
||||
maps.pending = true
|
||||
await Promise.resolve()
|
||||
maps.pending = false
|
||||
const loading = [...maps.queued]
|
||||
maps.queued = [];
|
||||
(await Promise.all(loading)).forEach(fn => fn && fn())
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUpdate(
|
||||
runner: ViteNodeRunner,
|
||||
{ path, acceptedPath }: Update,
|
||||
) {
|
||||
path = normalizeRequestId(path)
|
||||
acceptedPath = normalizeRequestId(acceptedPath)
|
||||
|
||||
const maps = getCache(runner)
|
||||
const mod = maps.hotModulesMap.get(path)
|
||||
|
||||
if (!mod) {
|
||||
// In a code-splitting project,
|
||||
// it is common that the hot-updating module is not loaded yet.
|
||||
// https://github.com/vitejs/vite/issues/721
|
||||
return
|
||||
}
|
||||
|
||||
const isSelfUpdate = path === acceptedPath
|
||||
let fetchedModule: ModuleNamespace | undefined
|
||||
|
||||
// determine the qualified callbacks before we re-import the modules
|
||||
const qualifiedCallbacks = mod.callbacks.filter(({ deps }) =>
|
||||
deps.includes(acceptedPath),
|
||||
)
|
||||
|
||||
if (isSelfUpdate || qualifiedCallbacks.length > 0) {
|
||||
const disposer = maps.disposeMap.get(acceptedPath)
|
||||
if (disposer) {
|
||||
await disposer(maps.dataMap.get(acceptedPath))
|
||||
}
|
||||
try {
|
||||
[fetchedModule] = await reload(runner, [acceptedPath])
|
||||
}
|
||||
catch (e: any) {
|
||||
warnFailedFetch(e, acceptedPath)
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
for (const { deps, fn } of qualifiedCallbacks) {
|
||||
fn(deps.map(dep => (dep === acceptedPath ? fetchedModule : undefined)))
|
||||
}
|
||||
|
||||
const loggedPath = isSelfUpdate ? path : `${acceptedPath} via ${path}`
|
||||
console.log(`${c.cyan('[vite-node]')} hot updated: ${loggedPath}`)
|
||||
}
|
||||
}
|
||||
|
||||
function warnFailedFetch(err: unknown, path: string | string[]) {
|
||||
if (!(err instanceof Error) || !err.message.match('fetch')) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
console.error(
|
||||
`[hmr] Failed to reload ${path}. `
|
||||
+ 'This could be due to syntax errors or importing non-existent '
|
||||
+ 'modules. (see errors above)',
|
||||
)
|
||||
}
|
||||
|
||||
export async function handleMessage(
|
||||
runner: ViteNodeRunner,
|
||||
emitter: HMREmitter,
|
||||
files: string[],
|
||||
payload: HMRPayload,
|
||||
): Promise<void> {
|
||||
const maps = getCache(runner)
|
||||
switch (payload.type) {
|
||||
case 'connected':
|
||||
sendMessageBuffer(runner, emitter)
|
||||
break
|
||||
case 'update':
|
||||
await notifyListeners(runner, 'vite:beforeUpdate', payload)
|
||||
await Promise.all(
|
||||
payload.updates.map((update) => {
|
||||
if (update.type === 'js-update') {
|
||||
return queueUpdate(runner, fetchUpdate(runner, update))
|
||||
}
|
||||
|
||||
// css-update
|
||||
console.error(`${c.cyan('[vite-node]')} no support css hmr.}`)
|
||||
return null
|
||||
}),
|
||||
)
|
||||
await notifyListeners(runner, 'vite:afterUpdate', payload)
|
||||
break
|
||||
case 'full-reload':
|
||||
await notifyListeners(runner, 'vite:beforeFullReload', payload)
|
||||
maps.customListenersMap.delete('vite:beforeFullReload')
|
||||
await reload(runner, files)
|
||||
break
|
||||
case 'custom':
|
||||
await notifyListeners(runner, payload.event, payload.data)
|
||||
break
|
||||
case 'prune':
|
||||
await notifyListeners(runner, 'vite:beforePrune', payload)
|
||||
payload.paths.forEach((path) => {
|
||||
const fn = maps.pruneMap.get(path)
|
||||
if (fn) {
|
||||
fn(maps.dataMap.get(path))
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'error': {
|
||||
await notifyListeners(runner, 'vite:error', payload)
|
||||
const err = payload.err
|
||||
console.error(
|
||||
`${c.cyan('[vite-node]')} Internal Server Error\n${err.message}\n${
|
||||
err.stack
|
||||
}`,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createHotContext(
|
||||
runner: ViteNodeRunner,
|
||||
emitter: HMREmitter,
|
||||
files: string[],
|
||||
ownerPath: string,
|
||||
): HotContext {
|
||||
debugHmr('createHotContext', ownerPath)
|
||||
const maps = getCache(runner)
|
||||
if (!maps.dataMap.has(ownerPath)) {
|
||||
maps.dataMap.set(ownerPath, {})
|
||||
}
|
||||
|
||||
// when a file is hot updated, a new context is created
|
||||
// clear its stale callbacks
|
||||
const mod = maps.hotModulesMap.get(ownerPath)
|
||||
if (mod) {
|
||||
mod.callbacks = []
|
||||
}
|
||||
|
||||
const newListeners = new Map()
|
||||
maps.ctxToListenersMap.set(ownerPath, newListeners)
|
||||
|
||||
function acceptDeps(deps: string[], callback: HotCallback['fn'] = () => {}) {
|
||||
const mod: HotModule = maps.hotModulesMap.get(ownerPath) || {
|
||||
id: ownerPath,
|
||||
callbacks: [],
|
||||
}
|
||||
mod.callbacks.push({
|
||||
deps,
|
||||
fn: callback,
|
||||
})
|
||||
maps.hotModulesMap.set(ownerPath, mod)
|
||||
}
|
||||
|
||||
const hot: HotContext = {
|
||||
get data() {
|
||||
return maps.dataMap.get(ownerPath)
|
||||
},
|
||||
|
||||
acceptExports(_, callback?: any) {
|
||||
acceptDeps([ownerPath], callback && (([mod]) => callback(mod)))
|
||||
},
|
||||
|
||||
accept(deps?: any, callback?: any) {
|
||||
if (typeof deps === 'function' || !deps) {
|
||||
// self-accept: hot.accept(() => {})
|
||||
acceptDeps([ownerPath], ([mod]) => deps && deps(mod))
|
||||
}
|
||||
else if (typeof deps === 'string') {
|
||||
// explicit deps
|
||||
acceptDeps([deps], ([mod]) => callback && callback(mod))
|
||||
}
|
||||
else if (Array.isArray(deps)) {
|
||||
acceptDeps(deps, callback)
|
||||
}
|
||||
else {
|
||||
throw new TypeError('invalid hot.accept() usage.')
|
||||
}
|
||||
},
|
||||
|
||||
dispose(cb) {
|
||||
maps.disposeMap.set(ownerPath, cb)
|
||||
},
|
||||
|
||||
prune(cb: (data: any) => void) {
|
||||
maps.pruneMap.set(ownerPath, cb)
|
||||
},
|
||||
|
||||
invalidate() {
|
||||
notifyListeners(runner, 'vite:invalidate', {
|
||||
path: ownerPath,
|
||||
message: undefined,
|
||||
firstInvalidatedBy: ownerPath,
|
||||
})
|
||||
return reload(runner, files)
|
||||
},
|
||||
|
||||
on<T extends string>(
|
||||
event: T,
|
||||
cb: (payload: InferCustomEventPayload<T>) => void,
|
||||
): void {
|
||||
const addToMap = (map: Map<string, any[]>) => {
|
||||
const existing = map.get(event) || []
|
||||
existing.push(cb)
|
||||
map.set(event, existing)
|
||||
}
|
||||
addToMap(maps.customListenersMap)
|
||||
addToMap(newListeners)
|
||||
},
|
||||
|
||||
off<T extends string>(
|
||||
event: T,
|
||||
cb: (payload: InferCustomEventPayload<T>) => void,
|
||||
) {
|
||||
const removeFromMap = (map: Map<string, any[]>) => {
|
||||
const existing = map.get(event)
|
||||
if (existing === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const pruned = existing.filter(l => l !== cb)
|
||||
if (pruned.length === 0) {
|
||||
map.delete(event)
|
||||
return
|
||||
}
|
||||
map.set(event, pruned)
|
||||
}
|
||||
removeFromMap(maps.customListenersMap)
|
||||
removeFromMap(newListeners)
|
||||
},
|
||||
|
||||
send<T extends string>(event: T, data?: InferCustomEventPayload<T>): void {
|
||||
maps.messageBuffer.push(JSON.stringify({ type: 'custom', event, data }))
|
||||
sendMessageBuffer(runner, emitter)
|
||||
},
|
||||
}
|
||||
|
||||
return hot
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
export * from './emitter'
|
||||
export * from './hmr'
|
||||
@ -1 +0,0 @@
|
||||
export * from './types'
|
||||
@ -1,441 +0,0 @@
|
||||
import type { TransformResult, ViteDevServer } from 'vite'
|
||||
import type {
|
||||
DebuggerOptions,
|
||||
EncodedSourceMap,
|
||||
FetchResult,
|
||||
ViteNodeResolveId,
|
||||
ViteNodeServerOptions,
|
||||
} from './types'
|
||||
import assert from 'node:assert'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import createDebug from 'debug'
|
||||
import { join, normalize, relative, resolve } from 'pathe'
|
||||
import { version as viteVersion } from 'vite'
|
||||
import { Debugger } from './debug'
|
||||
import { shouldExternalize } from './externalize'
|
||||
import { withInlineSourcemap } from './source-map'
|
||||
import {
|
||||
normalizeModuleId,
|
||||
toArray,
|
||||
toFilePath,
|
||||
withTrailingSlash,
|
||||
} from './utils'
|
||||
|
||||
export * from './externalize'
|
||||
|
||||
interface FetchCache {
|
||||
duration?: number
|
||||
timestamp: number
|
||||
result: FetchResult
|
||||
}
|
||||
|
||||
const debugRequest = createDebug('vite-node:server:request')
|
||||
|
||||
export class ViteNodeServer {
|
||||
private fetchPromiseMap = {
|
||||
ssr: new Map<string, Promise<FetchResult>>(),
|
||||
web: new Map<string, Promise<FetchResult>>(),
|
||||
}
|
||||
|
||||
private transformPromiseMap = {
|
||||
ssr: new Map<string, Promise<TransformResult | null | undefined>>(),
|
||||
web: new Map<string, Promise<TransformResult | null | undefined>>(),
|
||||
}
|
||||
|
||||
private durations = {
|
||||
ssr: new Map<string, number[]>(),
|
||||
web: new Map<string, number[]>(),
|
||||
}
|
||||
|
||||
private existingOptimizedDeps = new Set<string>()
|
||||
|
||||
fetchCaches: Record<'ssr' | 'web', Map<string, FetchCache>> = {
|
||||
ssr: new Map(),
|
||||
web: new Map(),
|
||||
}
|
||||
|
||||
fetchCache: Map<string, FetchCache> = new Map()
|
||||
|
||||
externalizeCache: Map<string, Promise<string | false>> = new Map()
|
||||
|
||||
debugger?: Debugger
|
||||
|
||||
constructor(
|
||||
public server: ViteDevServer,
|
||||
public options: ViteNodeServerOptions = {},
|
||||
) {
|
||||
const ssrOptions = server.config.ssr
|
||||
|
||||
options.deps ??= {}
|
||||
options.deps.cacheDir = relative(
|
||||
server.config.root,
|
||||
options.deps.cacheDir || server.config.cacheDir,
|
||||
)
|
||||
|
||||
if (ssrOptions) {
|
||||
// we don't externalize ssr, because it has different semantics in Vite
|
||||
// if (ssrOptions.external) {
|
||||
// options.deps.external ??= []
|
||||
// options.deps.external.push(...ssrOptions.external)
|
||||
// }
|
||||
|
||||
if (ssrOptions.noExternal === true) {
|
||||
options.deps.inline ??= true
|
||||
}
|
||||
else if (options.deps.inline !== true) {
|
||||
options.deps.inline ??= []
|
||||
const inline = options.deps.inline
|
||||
options.deps.inline.push(
|
||||
...toArray(ssrOptions.noExternal).filter(
|
||||
dep => !inline.includes(dep),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (process.env.VITE_NODE_DEBUG_DUMP) {
|
||||
options.debug = Object.assign(
|
||||
<DebuggerOptions>{
|
||||
dumpModules: !!process.env.VITE_NODE_DEBUG_DUMP,
|
||||
loadDumppedModules: process.env.VITE_NODE_DEBUG_DUMP === 'load',
|
||||
},
|
||||
options.debug ?? {},
|
||||
)
|
||||
}
|
||||
if (options.debug) {
|
||||
this.debugger = new Debugger(server.config.root, options.debug!)
|
||||
}
|
||||
|
||||
if (options.deps.inlineFiles) {
|
||||
options.deps.inlineFiles = options.deps.inlineFiles.flatMap((file) => {
|
||||
if (file.startsWith('file://')) {
|
||||
return file
|
||||
}
|
||||
const resolvedId = resolve(file)
|
||||
return [resolvedId, pathToFileURL(resolvedId).href]
|
||||
})
|
||||
}
|
||||
|
||||
options.deps.moduleDirectories ??= []
|
||||
|
||||
const envValue
|
||||
= process.env.VITE_NODE_DEPS_MODULE_DIRECTORIES
|
||||
|| process.env.npm_config_VITE_NODE_DEPS_MODULE_DIRECTORIES
|
||||
const customModuleDirectories = envValue?.split(',')
|
||||
if (customModuleDirectories) {
|
||||
options.deps.moduleDirectories.push(...customModuleDirectories)
|
||||
}
|
||||
|
||||
options.deps.moduleDirectories = options.deps.moduleDirectories.map(
|
||||
(dir) => {
|
||||
if (dir[0] !== '/') {
|
||||
dir = `/${dir}`
|
||||
}
|
||||
if (!dir.endsWith('/')) {
|
||||
dir += '/'
|
||||
}
|
||||
return normalize(dir)
|
||||
},
|
||||
)
|
||||
|
||||
// always add node_modules as a module directory
|
||||
if (!options.deps.moduleDirectories.includes('/node_modules/')) {
|
||||
options.deps.moduleDirectories.push('/node_modules/')
|
||||
}
|
||||
}
|
||||
|
||||
shouldExternalize(id: string): Promise<string | false> {
|
||||
return shouldExternalize(id, this.options.deps, this.externalizeCache)
|
||||
}
|
||||
|
||||
public getTotalDuration(): number {
|
||||
const ssrDurations = [...this.durations.ssr.values()].flat()
|
||||
const webDurations = [...this.durations.web.values()].flat()
|
||||
return [...ssrDurations, ...webDurations].reduce((a, b) => a + b, 0)
|
||||
}
|
||||
|
||||
private async ensureExists(id: string): Promise<boolean> {
|
||||
if (this.existingOptimizedDeps.has(id)) {
|
||||
return true
|
||||
}
|
||||
if (existsSync(id)) {
|
||||
this.existingOptimizedDeps.add(id)
|
||||
return true
|
||||
}
|
||||
return new Promise<boolean>((resolve) => {
|
||||
setTimeout(() => {
|
||||
this.ensureExists(id).then(() => {
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async resolveId(
|
||||
id: string,
|
||||
importer?: string,
|
||||
transformMode?: 'web' | 'ssr',
|
||||
): Promise<ViteNodeResolveId | null> {
|
||||
if (
|
||||
importer
|
||||
&& !importer.startsWith(withTrailingSlash(this.server.config.root))
|
||||
) {
|
||||
importer = resolve(this.server.config.root, importer)
|
||||
}
|
||||
const mode
|
||||
= transformMode ?? ((importer && this.getTransformMode(importer)) || 'ssr')
|
||||
return this.server.pluginContainer.resolveId(id, importer, {
|
||||
ssr: mode === 'ssr',
|
||||
})
|
||||
}
|
||||
|
||||
getSourceMap(source: string): EncodedSourceMap | null {
|
||||
source = normalizeModuleId(source)
|
||||
const fetchResult = this.fetchCache.get(source)?.result
|
||||
if (fetchResult?.map) {
|
||||
return fetchResult.map
|
||||
}
|
||||
const ssrTransformResult
|
||||
= this.server.moduleGraph.getModuleById(source)?.ssrTransformResult
|
||||
return (ssrTransformResult?.map
|
||||
|| null) as unknown as EncodedSourceMap | null
|
||||
}
|
||||
|
||||
private assertMode(mode: 'web' | 'ssr') {
|
||||
assert(
|
||||
mode === 'web' || mode === 'ssr',
|
||||
`"transformMode" can only be "web" or "ssr", received "${mode}".`,
|
||||
)
|
||||
}
|
||||
|
||||
async fetchModule(
|
||||
id: string,
|
||||
transformMode?: 'web' | 'ssr',
|
||||
): Promise<FetchResult> {
|
||||
const mode = transformMode || this.getTransformMode(id)
|
||||
return this.fetchResult(id, mode).then((r) => {
|
||||
return this.options.sourcemap !== true ? { ...r, map: undefined } : r
|
||||
})
|
||||
}
|
||||
|
||||
async fetchResult(id: string, mode: 'web' | 'ssr'): Promise<FetchResult> {
|
||||
const moduleId = normalizeModuleId(id)
|
||||
this.assertMode(mode)
|
||||
const promiseMap = this.fetchPromiseMap[mode]
|
||||
// reuse transform for concurrent requests
|
||||
if (!promiseMap.has(moduleId)) {
|
||||
promiseMap.set(
|
||||
moduleId,
|
||||
this._fetchModule(moduleId, mode).finally(() => {
|
||||
promiseMap.delete(moduleId)
|
||||
}),
|
||||
)
|
||||
}
|
||||
return promiseMap.get(moduleId)!
|
||||
}
|
||||
|
||||
async transformRequest(
|
||||
id: string,
|
||||
filepath: string = id,
|
||||
transformMode?: 'web' | 'ssr',
|
||||
): Promise<TransformResult | null | undefined> {
|
||||
const mode = transformMode || this.getTransformMode(id)
|
||||
this.assertMode(mode)
|
||||
const promiseMap = this.transformPromiseMap[mode]
|
||||
// reuse transform for concurrent requests
|
||||
if (!promiseMap.has(id)) {
|
||||
promiseMap.set(
|
||||
id,
|
||||
this._transformRequest(id, filepath, mode).finally(() => {
|
||||
promiseMap.delete(id)
|
||||
}),
|
||||
)
|
||||
}
|
||||
return promiseMap.get(id)!
|
||||
}
|
||||
|
||||
async transformModule(id: string, transformMode?: 'web' | 'ssr'): Promise<{
|
||||
code: string | undefined
|
||||
}> {
|
||||
if (transformMode !== 'web') {
|
||||
throw new Error(
|
||||
'`transformModule` only supports `transformMode: "web"`.',
|
||||
)
|
||||
}
|
||||
|
||||
const normalizedId = normalizeModuleId(id)
|
||||
const mod = this.server.moduleGraph.getModuleById(normalizedId)
|
||||
const result
|
||||
= mod?.transformResult
|
||||
|| (await this.server.transformRequest(normalizedId))
|
||||
|
||||
return {
|
||||
code: result?.code,
|
||||
}
|
||||
}
|
||||
|
||||
getTransformMode(id: string): 'ssr' | 'web' {
|
||||
const withoutQuery = id.split('?')[0]
|
||||
|
||||
if (this.options.transformMode?.web?.some(r => withoutQuery.match(r))) {
|
||||
return 'web'
|
||||
}
|
||||
if (this.options.transformMode?.ssr?.some(r => withoutQuery.match(r))) {
|
||||
return 'ssr'
|
||||
}
|
||||
|
||||
if (withoutQuery.match(/\.([cm]?[jt]sx?|json)$/)) {
|
||||
return 'ssr'
|
||||
}
|
||||
return 'web'
|
||||
}
|
||||
|
||||
private getChangedModule(id: string, file: string) {
|
||||
const module
|
||||
= this.server.moduleGraph.getModuleById(id)
|
||||
|| this.server.moduleGraph.getModuleById(file)
|
||||
if (module) {
|
||||
return module
|
||||
}
|
||||
const _modules = this.server.moduleGraph.getModulesByFile(file)
|
||||
if (!_modules || !_modules.size) {
|
||||
return null
|
||||
}
|
||||
// find the latest changed module
|
||||
const modules = [..._modules]
|
||||
let mod = modules[0]
|
||||
let latestMax = -1
|
||||
for (const m of _modules) {
|
||||
const timestamp = Math.max(
|
||||
m.lastHMRTimestamp,
|
||||
m.lastInvalidationTimestamp,
|
||||
)
|
||||
if (timestamp > latestMax) {
|
||||
latestMax = timestamp
|
||||
mod = m
|
||||
}
|
||||
}
|
||||
return mod
|
||||
}
|
||||
|
||||
private async _fetchModule(
|
||||
id: string,
|
||||
transformMode: 'web' | 'ssr',
|
||||
): Promise<FetchResult> {
|
||||
let result: FetchResult
|
||||
|
||||
const cacheDir = this.options.deps?.cacheDir
|
||||
|
||||
if (cacheDir && id.includes(cacheDir)) {
|
||||
if (!id.startsWith(withTrailingSlash(this.server.config.root))) {
|
||||
id = join(this.server.config.root, id)
|
||||
}
|
||||
const timeout = setTimeout(() => {
|
||||
throw new Error(
|
||||
`ViteNodeServer: ${id} not found. This is a bug, please report it.`,
|
||||
)
|
||||
}, 5000) // CI can be quite slow
|
||||
await this.ensureExists(id)
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
const { path: filePath } = toFilePath(id, this.server.config.root)
|
||||
|
||||
const moduleNode = this.getChangedModule(id, filePath)
|
||||
const cache = this.fetchCaches[transformMode].get(filePath)
|
||||
|
||||
// lastUpdateTimestamp is the timestamp that marks the last time the module was changed
|
||||
// if lastUpdateTimestamp is 0, then the module was not changed since the server started
|
||||
// we test "timestamp === 0" for expressiveness, but it's not necessary
|
||||
const timestamp = moduleNode
|
||||
? Math.max(
|
||||
moduleNode.lastHMRTimestamp,
|
||||
moduleNode.lastInvalidationTimestamp,
|
||||
)
|
||||
: 0
|
||||
if (cache && (timestamp === 0 || cache.timestamp >= timestamp)) {
|
||||
return cache.result
|
||||
}
|
||||
|
||||
const time = Date.now()
|
||||
const externalize = await this.shouldExternalize(filePath)
|
||||
let duration: number | undefined
|
||||
if (externalize) {
|
||||
result = { externalize }
|
||||
this.debugger?.recordExternalize(id, externalize)
|
||||
}
|
||||
else {
|
||||
const start = performance.now()
|
||||
const r = await this._transformRequest(id, filePath, transformMode)
|
||||
duration = performance.now() - start
|
||||
result = { code: r?.code, map: r?.map as any }
|
||||
}
|
||||
|
||||
const cacheEntry = {
|
||||
duration,
|
||||
timestamp: time,
|
||||
result,
|
||||
}
|
||||
|
||||
const durations = this.durations[transformMode].get(filePath) || []
|
||||
this.durations[transformMode].set(filePath, [...durations, duration ?? 0])
|
||||
|
||||
this.fetchCaches[transformMode].set(filePath, cacheEntry)
|
||||
this.fetchCache.set(filePath, cacheEntry)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
protected async processTransformResult(
|
||||
filepath: string,
|
||||
result: TransformResult,
|
||||
): Promise<TransformResult> {
|
||||
const mod = this.server.moduleGraph.getModuleById(filepath)
|
||||
return withInlineSourcemap(result, {
|
||||
filepath: mod?.file || filepath,
|
||||
root: this.server.config.root,
|
||||
noFirstLineMapping: Number(viteVersion.split('.')[0]) >= 6,
|
||||
})
|
||||
}
|
||||
|
||||
private async _transformRequest(
|
||||
id: string,
|
||||
filepath: string,
|
||||
transformMode: 'web' | 'ssr',
|
||||
) {
|
||||
debugRequest(id)
|
||||
|
||||
let result: TransformResult | null = null
|
||||
|
||||
if (this.options.debug?.loadDumppedModules) {
|
||||
result = (await this.debugger?.loadDump(id)) ?? null
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
if (transformMode === 'web') {
|
||||
// for components like Vue, we want to use the client side
|
||||
// plugins but then convert the code to be consumed by the server
|
||||
result = await this.server.transformRequest(id)
|
||||
if (result) {
|
||||
result = await this.server.ssrTransform(result.code, result.map, id)
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = await this.server.transformRequest(id, { ssr: true })
|
||||
}
|
||||
|
||||
const sourcemap = this.options.sourcemap ?? 'inline'
|
||||
if (sourcemap === 'inline' && result) {
|
||||
result = await this.processTransformResult(filepath, result)
|
||||
}
|
||||
|
||||
if (this.options.debug?.dumpModules) {
|
||||
await this.debugger?.dumpFile(id, result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -1,511 +0,0 @@
|
||||
// adapted from https://github.com/evanw/node-source-map-support/blob/master/source-map-support.js
|
||||
// we need this because "--enable-source-maps" flag doesn't support VM
|
||||
// we make a custom implementation because we don't need some features from source-map-support,
|
||||
// like polyfilled Buffer, browser support, etc.
|
||||
|
||||
import type {
|
||||
OriginalMapping,
|
||||
SourceMapInput,
|
||||
} from '@jridgewell/trace-mapping'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'
|
||||
|
||||
// Only install once if called multiple times
|
||||
let errorFormatterInstalled = false
|
||||
|
||||
// Maps a file path to a string containing the file contents
|
||||
const fileContentsCache: Record<string, string> = {}
|
||||
|
||||
// Maps a file path to a source map for that file
|
||||
const sourceMapCache: Record<
|
||||
string,
|
||||
{ url: string | null; map: TraceMap | null }
|
||||
> = {}
|
||||
|
||||
// Regex for detecting source maps
|
||||
const reSourceMap = /^data:application\/json[^,]+base64,/
|
||||
|
||||
type RetrieveFileHandler = (path: string) => string | null | undefined
|
||||
type RetrieveMapHandler = (
|
||||
source: string
|
||||
) => { url: string; map?: string | SourceMapInput | null } | null | undefined
|
||||
|
||||
// Priority list of retrieve handlers
|
||||
let retrieveFileHandlers: RetrieveFileHandler[] = []
|
||||
let retrieveMapHandlers: RetrieveMapHandler[] = []
|
||||
|
||||
function globalProcessVersion() {
|
||||
if (typeof process === 'object' && process !== null) {
|
||||
return process.version
|
||||
}
|
||||
else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function handlerExec<T>(list: ((arg: string) => T)[]) {
|
||||
return function (arg: string) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const ret = list[i](arg)
|
||||
if (ret) {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
let retrieveFile = handlerExec(retrieveFileHandlers)
|
||||
|
||||
retrieveFileHandlers.push((path) => {
|
||||
// Trim the path to make sure there is no extra whitespace.
|
||||
path = path.trim()
|
||||
if (path.startsWith('file:')) {
|
||||
// existsSync/readFileSync can't handle file protocol, but once stripped, it works
|
||||
path = path.replace(/file:\/\/\/(\w:)?/, (protocol, drive) => {
|
||||
return drive
|
||||
? '' // file:///C:/dir/file -> C:/dir/file
|
||||
: '/' // file:///root-dir/file -> /root-dir/file
|
||||
})
|
||||
}
|
||||
if (path in fileContentsCache) {
|
||||
return fileContentsCache[path]
|
||||
}
|
||||
|
||||
let contents = ''
|
||||
try {
|
||||
if (fs.existsSync(path)) {
|
||||
contents = fs.readFileSync(path, 'utf8')
|
||||
}
|
||||
}
|
||||
catch {
|
||||
/* ignore any errors */
|
||||
}
|
||||
|
||||
return (fileContentsCache[path] = contents)
|
||||
})
|
||||
|
||||
// Support URLs relative to a directory, but be careful about a protocol prefix
|
||||
function supportRelativeURL(file: string, url: string) {
|
||||
if (!file) {
|
||||
return url
|
||||
}
|
||||
const dir = path.dirname(file)
|
||||
const match = /^\w+:\/\/[^/]*/.exec(dir)
|
||||
let protocol = match ? match[0] : ''
|
||||
const startPath = dir.slice(protocol.length)
|
||||
if (protocol && /^\/\w:/.test(startPath)) {
|
||||
// handle file:///C:/ paths
|
||||
protocol += '/'
|
||||
return (
|
||||
protocol
|
||||
+ path.resolve(dir.slice(protocol.length), url).replace(/\\/g, '/')
|
||||
)
|
||||
}
|
||||
return protocol + path.resolve(dir.slice(protocol.length), url)
|
||||
}
|
||||
|
||||
function retrieveSourceMapURL(source: string) {
|
||||
// Get the URL of the source map
|
||||
const fileData = retrieveFile(source)
|
||||
if (!fileData) {
|
||||
return null
|
||||
}
|
||||
const re
|
||||
= /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm
|
||||
// Keep executing the search to find the *last* sourceMappingURL to avoid
|
||||
// picking up sourceMappingURLs from comments, strings, etc.
|
||||
let lastMatch, match
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((match = re.exec(fileData))) {
|
||||
lastMatch = match
|
||||
}
|
||||
if (!lastMatch) {
|
||||
return null
|
||||
}
|
||||
return lastMatch[1]
|
||||
}
|
||||
|
||||
// Can be overridden by the retrieveSourceMap option to install. Takes a
|
||||
// generated source filename; returns a {map, optional url} object, or null if
|
||||
// there is no source map. The map field may be either a string or the parsed
|
||||
// JSON object (ie, it must be a valid argument to the SourceMapConsumer
|
||||
// constructor).
|
||||
let retrieveSourceMap = handlerExec(retrieveMapHandlers)
|
||||
retrieveMapHandlers.push((source) => {
|
||||
let sourceMappingURL = retrieveSourceMapURL(source)
|
||||
if (!sourceMappingURL) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Read the contents of the source map
|
||||
let sourceMapData
|
||||
if (reSourceMap.test(sourceMappingURL)) {
|
||||
// Support source map URL as a data url
|
||||
const rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1)
|
||||
sourceMapData = Buffer.from(rawData, 'base64').toString()
|
||||
sourceMappingURL = source
|
||||
}
|
||||
else {
|
||||
// Support source map URLs relative to the source URL
|
||||
sourceMappingURL = supportRelativeURL(source, sourceMappingURL)
|
||||
sourceMapData = retrieveFile(sourceMappingURL)
|
||||
}
|
||||
|
||||
if (!sourceMapData) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
url: sourceMappingURL,
|
||||
map: sourceMapData,
|
||||
}
|
||||
})
|
||||
|
||||
// interface Position {
|
||||
// source: string
|
||||
// line: number
|
||||
// column: number
|
||||
// }
|
||||
|
||||
function mapSourcePosition(position: OriginalMapping) {
|
||||
if (!position.source) {
|
||||
return position
|
||||
}
|
||||
let sourceMap = sourceMapCache[position.source]
|
||||
if (!sourceMap) {
|
||||
// Call the (overridable) retrieveSourceMap function to get the source map.
|
||||
const urlAndMap = retrieveSourceMap(position.source)
|
||||
const map = urlAndMap && urlAndMap.map
|
||||
if (map && !(typeof map === 'object' && 'mappings' in map && map.mappings === '')) {
|
||||
sourceMap = sourceMapCache[position.source] = {
|
||||
url: urlAndMap.url,
|
||||
map: new TraceMap(map),
|
||||
}
|
||||
|
||||
// Load all sources stored inline with the source map into the file cache
|
||||
// to pretend like they are already loaded. They may not exist on disk.
|
||||
if (sourceMap.map?.sourcesContent) {
|
||||
sourceMap.map.sources.forEach((source, i) => {
|
||||
const contents = sourceMap.map?.sourcesContent?.[i]
|
||||
if (contents && source && sourceMap.url) {
|
||||
const url = supportRelativeURL(sourceMap.url, source)
|
||||
fileContentsCache[url] = contents
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
sourceMap = sourceMapCache[position.source] = {
|
||||
url: null,
|
||||
map: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the source URL relative to the URL of the source map
|
||||
if (sourceMap && sourceMap.map && sourceMap.url) {
|
||||
const originalPosition = originalPositionFor(sourceMap.map, position)
|
||||
|
||||
// Only return the original position if a matching line was found. If no
|
||||
// matching line is found then we return position instead, which will cause
|
||||
// the stack trace to print the path and line for the compiled file. It is
|
||||
// better to give a precise location in the compiled file than a vague
|
||||
// location in the original file.
|
||||
if (originalPosition.source !== null) {
|
||||
originalPosition.source = supportRelativeURL(
|
||||
sourceMap.url,
|
||||
originalPosition.source,
|
||||
)
|
||||
return originalPosition
|
||||
}
|
||||
}
|
||||
|
||||
return position
|
||||
}
|
||||
|
||||
// Parses code generated by FormatEvalOrigin(), a function inside V8:
|
||||
// https://code.google.com/p/v8/source/browse/trunk/src/messages.js
|
||||
function mapEvalOrigin(origin: string): string {
|
||||
// Most eval() calls are in this format
|
||||
let match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin)
|
||||
if (match) {
|
||||
const position = mapSourcePosition({
|
||||
name: null,
|
||||
source: match[2],
|
||||
line: +match[3],
|
||||
column: +match[4] - 1,
|
||||
})
|
||||
return `eval at ${match[1]} (${position.source}:${position.line}:${
|
||||
position.column + 1
|
||||
})`
|
||||
}
|
||||
|
||||
// Parse nested eval() calls using recursion
|
||||
match = /^eval at ([^(]+) \((.+)\)$/.exec(origin)
|
||||
if (match) {
|
||||
return `eval at ${match[1]} (${mapEvalOrigin(match[2])})`
|
||||
}
|
||||
|
||||
// Make sure we still return useful information if we didn't find anything
|
||||
return origin
|
||||
}
|
||||
|
||||
interface CallSite extends NodeJS.CallSite {
|
||||
getScriptNameOrSourceURL: () => string
|
||||
}
|
||||
|
||||
// This is copied almost verbatim from the V8 source code at
|
||||
// https://code.google.com/p/v8/source/browse/trunk/src/messages.js. The
|
||||
// implementation of wrapCallSite() used to just forward to the actual source
|
||||
// code of CallSite.prototype.toString but unfortunately a new release of V8
|
||||
// did something to the prototype chain and broke the shim. The only fix I
|
||||
// could find was copy/paste.
|
||||
function CallSiteToString(this: CallSite) {
|
||||
let fileName
|
||||
let fileLocation = ''
|
||||
if (this.isNative()) {
|
||||
fileLocation = 'native'
|
||||
}
|
||||
else {
|
||||
fileName = this.getScriptNameOrSourceURL()
|
||||
if (!fileName && this.isEval()) {
|
||||
fileLocation = this.getEvalOrigin() as string
|
||||
fileLocation += ', ' // Expecting source position to follow.
|
||||
}
|
||||
|
||||
if (fileName) {
|
||||
fileLocation += fileName
|
||||
}
|
||||
else {
|
||||
// Source code does not originate from a file and is not native, but we
|
||||
// can still get the source position inside the source string, e.g. in
|
||||
// an eval string.
|
||||
fileLocation += '<anonymous>'
|
||||
}
|
||||
const lineNumber = this.getLineNumber()
|
||||
if (lineNumber != null) {
|
||||
fileLocation += `:${lineNumber}`
|
||||
const columnNumber = this.getColumnNumber()
|
||||
if (columnNumber) {
|
||||
fileLocation += `:${columnNumber}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let line = ''
|
||||
const functionName = this.getFunctionName()
|
||||
let addSuffix = true
|
||||
const isConstructor = this.isConstructor()
|
||||
const isMethodCall = !(this.isToplevel() || isConstructor)
|
||||
if (isMethodCall) {
|
||||
let typeName = this.getTypeName()
|
||||
// Fixes shim to be backward compatible with Node v0 to v4
|
||||
if (typeName === '[object Object]') {
|
||||
typeName = 'null'
|
||||
}
|
||||
|
||||
const methodName = this.getMethodName()
|
||||
if (functionName) {
|
||||
if (typeName && functionName.indexOf(typeName) !== 0) {
|
||||
line += `${typeName}.`
|
||||
}
|
||||
|
||||
line += functionName
|
||||
if (
|
||||
methodName
|
||||
&& functionName.indexOf(`.${methodName}`)
|
||||
!== functionName.length - methodName.length - 1
|
||||
) {
|
||||
line += ` [as ${methodName}]`
|
||||
}
|
||||
}
|
||||
else {
|
||||
line += `${typeName}.${methodName || '<anonymous>'}`
|
||||
}
|
||||
}
|
||||
else if (isConstructor) {
|
||||
line += `new ${functionName || '<anonymous>'}`
|
||||
}
|
||||
else if (functionName) {
|
||||
line += functionName
|
||||
}
|
||||
else {
|
||||
line += fileLocation
|
||||
addSuffix = false
|
||||
}
|
||||
if (addSuffix) {
|
||||
line += ` (${fileLocation})`
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
function cloneCallSite(frame: CallSite) {
|
||||
const object = {} as CallSite
|
||||
Object.getOwnPropertyNames(Object.getPrototypeOf(frame)).forEach((name) => {
|
||||
const key = name as keyof CallSite
|
||||
// @ts-expect-error difficult to type
|
||||
object[key] = /^(?:is|get)/.test(name)
|
||||
? function () {
|
||||
// eslint-disable-next-line no-useless-call
|
||||
return frame[key].call(frame)
|
||||
}
|
||||
: frame[key]
|
||||
})
|
||||
object.toString = CallSiteToString
|
||||
return object
|
||||
}
|
||||
|
||||
interface State {
|
||||
nextPosition: null | OriginalMapping
|
||||
curPosition: null | OriginalMapping
|
||||
}
|
||||
|
||||
function wrapCallSite(frame: CallSite, state: State) {
|
||||
// provides interface backward compatibility
|
||||
if (state === undefined) {
|
||||
state = { nextPosition: null, curPosition: null }
|
||||
}
|
||||
|
||||
if (frame.isNative()) {
|
||||
state.curPosition = null
|
||||
return frame
|
||||
}
|
||||
|
||||
// Most call sites will return the source file from getFileName(), but code
|
||||
// passed to eval() ending in "//# sourceURL=..." will return the source file
|
||||
// from getScriptNameOrSourceURL() instead
|
||||
const source = frame.getFileName() || frame.getScriptNameOrSourceURL()
|
||||
if (source) {
|
||||
const line = frame.getLineNumber() as number
|
||||
let column = (frame.getColumnNumber() as number) - 1
|
||||
|
||||
// Fix position in Node where some (internal) code is prepended.
|
||||
// See https://github.com/evanw/node-source-map-support/issues/36
|
||||
// Header removed in node at ^10.16 || >=11.11.0
|
||||
// v11 is not an LTS candidate, we can just test the one version with it.
|
||||
// Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11
|
||||
const noHeader
|
||||
= /^v(?:10\.1[6-9]|10\.[2-9]\d|10\.\d{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/
|
||||
const headerLength = noHeader.test(globalProcessVersion()) ? 0 : 62
|
||||
if (line === 1 && column > headerLength && !frame.isEval()) {
|
||||
column -= headerLength
|
||||
}
|
||||
|
||||
const position = mapSourcePosition({
|
||||
name: null,
|
||||
source,
|
||||
line,
|
||||
column,
|
||||
})
|
||||
state.curPosition = position
|
||||
frame = cloneCallSite(frame)
|
||||
const originalFunctionName = frame.getFunctionName
|
||||
frame.getFunctionName = function () {
|
||||
if (state.nextPosition == null) {
|
||||
return originalFunctionName()
|
||||
}
|
||||
|
||||
return state.nextPosition.name || originalFunctionName()
|
||||
}
|
||||
frame.getFileName = function () {
|
||||
return position.source ?? null
|
||||
}
|
||||
frame.getLineNumber = function () {
|
||||
return position.line
|
||||
}
|
||||
frame.getColumnNumber = function () {
|
||||
return position.column + 1
|
||||
}
|
||||
frame.getScriptNameOrSourceURL = function () {
|
||||
return position.source as string
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
// Code called using eval() needs special handling
|
||||
let origin = frame.isEval() && frame.getEvalOrigin()
|
||||
if (origin) {
|
||||
origin = mapEvalOrigin(origin)
|
||||
frame = cloneCallSite(frame)
|
||||
frame.getEvalOrigin = function () {
|
||||
return origin || undefined
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
// If we get here then we were unable to change the source position
|
||||
return frame
|
||||
}
|
||||
|
||||
// This function is part of the V8 stack trace API, for more info see:
|
||||
// https://v8.dev/docs/stack-trace-api
|
||||
function prepareStackTrace(error: Error, stack: CallSite[]) {
|
||||
const name = error.name || 'Error'
|
||||
const message = error.message || ''
|
||||
const errorString = `${name}: ${message}`
|
||||
|
||||
const state = { nextPosition: null, curPosition: null }
|
||||
const processedStack = []
|
||||
for (let i = stack.length - 1; i >= 0; i--) {
|
||||
processedStack.push(`\n at ${wrapCallSite(stack[i], state)}`)
|
||||
state.nextPosition = state.curPosition
|
||||
}
|
||||
state.curPosition = state.nextPosition = null
|
||||
return errorString + processedStack.reverse().join('')
|
||||
}
|
||||
|
||||
const originalRetrieveFileHandlers = retrieveFileHandlers.slice(0)
|
||||
const originalRetrieveMapHandlers = retrieveMapHandlers.slice(0)
|
||||
|
||||
interface Options {
|
||||
hookRequire?: boolean
|
||||
overrideRetrieveFile?: boolean
|
||||
overrideRetrieveSourceMap?: boolean
|
||||
retrieveFile?: RetrieveFileHandler
|
||||
retrieveSourceMap?: RetrieveMapHandler
|
||||
}
|
||||
|
||||
export function install(options: Options): void {
|
||||
options = options || {}
|
||||
|
||||
// Allow sources to be found by methods other than reading the files
|
||||
// directly from disk.
|
||||
if (options.retrieveFile) {
|
||||
if (options.overrideRetrieveFile) {
|
||||
retrieveFileHandlers.length = 0
|
||||
}
|
||||
|
||||
retrieveFileHandlers.unshift(options.retrieveFile)
|
||||
}
|
||||
|
||||
// Allow source maps to be found by methods other than reading the files
|
||||
// directly from disk.
|
||||
if (options.retrieveSourceMap) {
|
||||
if (options.overrideRetrieveSourceMap) {
|
||||
retrieveMapHandlers.length = 0
|
||||
}
|
||||
|
||||
retrieveMapHandlers.unshift(options.retrieveSourceMap)
|
||||
}
|
||||
|
||||
// Install the error reformatter
|
||||
if (!errorFormatterInstalled) {
|
||||
errorFormatterInstalled = true
|
||||
Error.prepareStackTrace
|
||||
= prepareStackTrace as ErrorConstructor['prepareStackTrace']
|
||||
}
|
||||
}
|
||||
|
||||
export function resetRetrieveHandlers(): void {
|
||||
retrieveFileHandlers.length = 0
|
||||
retrieveMapHandlers.length = 0
|
||||
|
||||
retrieveFileHandlers = originalRetrieveFileHandlers.slice(0)
|
||||
retrieveMapHandlers = originalRetrieveMapHandlers.slice(0)
|
||||
|
||||
retrieveSourceMap = handlerExec(retrieveMapHandlers)
|
||||
retrieveFile = handlerExec(retrieveFileHandlers)
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
import type { TransformResult } from 'vite'
|
||||
import type { EncodedSourceMap } from './types'
|
||||
import { dirname, isAbsolute, relative, resolve } from 'pathe'
|
||||
import { install } from './source-map-handler'
|
||||
import { withTrailingSlash } from './utils'
|
||||
|
||||
interface InstallSourceMapSupportOptions {
|
||||
getSourceMap: (source: string) => EncodedSourceMap | null | undefined
|
||||
}
|
||||
|
||||
let SOURCEMAPPING_URL = 'sourceMa'
|
||||
SOURCEMAPPING_URL += 'ppingURL'
|
||||
|
||||
const VITE_NODE_SOURCEMAPPING_SOURCE = '//# sourceMappingSource=vite-node'
|
||||
const VITE_NODE_SOURCEMAPPING_URL = `${SOURCEMAPPING_URL}=data:application/json;charset=utf-8`
|
||||
|
||||
export function withInlineSourcemap(
|
||||
result: TransformResult,
|
||||
options: {
|
||||
root: string // project root path of this resource
|
||||
filepath: string
|
||||
noFirstLineMapping?: boolean
|
||||
},
|
||||
): TransformResult {
|
||||
const map = result.map
|
||||
let code = result.code
|
||||
|
||||
if (!map || code.includes(VITE_NODE_SOURCEMAPPING_SOURCE)) {
|
||||
return result
|
||||
}
|
||||
|
||||
if ('sources' in map) {
|
||||
map.sources = map.sources?.map((source) => {
|
||||
if (!source) {
|
||||
return source
|
||||
}
|
||||
// sometimes files here are absolute,
|
||||
// but they are considered absolute to the server url, not the file system
|
||||
// this is a bug in Vite
|
||||
// all files should be either absolute to the file system or relative to the source map file
|
||||
if (isAbsolute(source)) {
|
||||
const actualPath
|
||||
= !source.startsWith(withTrailingSlash(options.root))
|
||||
&& source[0] === '/'
|
||||
? resolve(options.root, source.slice(1))
|
||||
: source
|
||||
return relative(dirname(options.filepath), actualPath)
|
||||
}
|
||||
return source
|
||||
})
|
||||
}
|
||||
|
||||
// to reduce the payload size, we only inline vite node source map, because it's also the only one we use
|
||||
const OTHER_SOURCE_MAP_REGEXP = new RegExp(
|
||||
`//# ${SOURCEMAPPING_URL}=data:application/json[^,]+base64,([A-Za-z0-9+/=]+)$`,
|
||||
'gm',
|
||||
)
|
||||
while (OTHER_SOURCE_MAP_REGEXP.test(code)) {
|
||||
code = code.replace(OTHER_SOURCE_MAP_REGEXP, '')
|
||||
}
|
||||
|
||||
// If the first line is not present on source maps, add simple 1:1 mapping ([0,0,0,0], [1,0,0,0])
|
||||
// so that debuggers can be set to break on first line
|
||||
// Since Vite 6, import statements at the top of the file are preserved correctly,
|
||||
// so we don't need to add this mapping anymore.
|
||||
if (!options.noFirstLineMapping && map.mappings[0] === ';') {
|
||||
map.mappings = `AAAA,CAAA${map.mappings}`
|
||||
}
|
||||
|
||||
const sourceMap = Buffer.from(JSON.stringify(map), 'utf-8').toString(
|
||||
'base64',
|
||||
)
|
||||
result.code = `${code.trimEnd()}\n\n${VITE_NODE_SOURCEMAPPING_SOURCE}\n//# ${VITE_NODE_SOURCEMAPPING_URL};base64,${sourceMap}\n`
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function extractSourceMap(code: string): EncodedSourceMap | null {
|
||||
const regexp = new RegExp(
|
||||
`//# ${VITE_NODE_SOURCEMAPPING_URL};base64,(.+)`,
|
||||
'gm',
|
||||
)
|
||||
let lastMatch!: RegExpExecArray | null, match!: RegExpExecArray | null
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((match = regexp.exec(code))) {
|
||||
lastMatch = match
|
||||
}
|
||||
// pick only the last source map keeping user strings that look like maps
|
||||
if (lastMatch) {
|
||||
return JSON.parse(Buffer.from(lastMatch[1], 'base64').toString('utf-8'))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function installSourcemapsSupport(
|
||||
options: InstallSourceMapSupportOptions,
|
||||
): void {
|
||||
install({
|
||||
retrieveSourceMap(source) {
|
||||
const map = options.getSourceMap(source)
|
||||
if (map) {
|
||||
return {
|
||||
url: source,
|
||||
map,
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -1,144 +0,0 @@
|
||||
import type { EncodedSourceMap } from '@jridgewell/trace-mapping'
|
||||
import type { ViteHotContext } from 'vite/types/hot.js'
|
||||
import type { ModuleCacheMap, ModuleExecutionInfo, ViteNodeRunner } from './client'
|
||||
|
||||
export type Nullable<T> = T | null | undefined
|
||||
export type Arrayable<T> = T | Array<T>
|
||||
export type Awaitable<T> = T | PromiseLike<T>
|
||||
|
||||
export interface DepsHandlingOptions {
|
||||
external?: (string | RegExp)[]
|
||||
inline?: (string | RegExp)[] | true
|
||||
inlineFiles?: string[]
|
||||
/**
|
||||
* A list of directories that are considered to hold Node.js modules
|
||||
* Have to include "/" at the start and end of the path
|
||||
*
|
||||
* Vite-Node checks the whole absolute path of the import, so make sure you don't include
|
||||
* unwanted files accidentally
|
||||
* @default ['/node_modules/']
|
||||
*/
|
||||
moduleDirectories?: string[]
|
||||
cacheDir?: string
|
||||
/**
|
||||
* Try to guess the CJS version of a package when it's invalid ESM
|
||||
* @default false
|
||||
*/
|
||||
fallbackCJS?: boolean
|
||||
}
|
||||
|
||||
export interface StartOfSourceMap {
|
||||
file?: string
|
||||
sourceRoot?: string
|
||||
}
|
||||
|
||||
export type {
|
||||
DecodedSourceMap,
|
||||
EncodedSourceMap,
|
||||
SourceMapInput,
|
||||
} from '@jridgewell/trace-mapping'
|
||||
|
||||
export interface RawSourceMap extends StartOfSourceMap {
|
||||
version: number
|
||||
sources: string[]
|
||||
names: string[]
|
||||
sourcesContent?: (string | null)[]
|
||||
mappings: string
|
||||
}
|
||||
|
||||
export interface FetchResult {
|
||||
code?: string
|
||||
externalize?: string
|
||||
map?: EncodedSourceMap | null
|
||||
}
|
||||
|
||||
export type HotContext = Omit<ViteHotContext, 'acceptDeps' | 'decline'>
|
||||
|
||||
export type FetchFunction = (id: string) => Promise<FetchResult>
|
||||
|
||||
export type ResolveIdFunction = (
|
||||
id: string,
|
||||
importer?: string
|
||||
) => Awaitable<ViteNodeResolveId | null | undefined | void>
|
||||
|
||||
export type CreateHotContextFunction = (
|
||||
runner: ViteNodeRunner,
|
||||
url: string
|
||||
) => HotContext
|
||||
|
||||
export interface ModuleCache {
|
||||
promise?: Promise<any>
|
||||
exports?: any
|
||||
evaluated?: boolean
|
||||
resolving?: boolean
|
||||
code?: string
|
||||
map?: EncodedSourceMap
|
||||
/**
|
||||
* Module ids that imports this module
|
||||
*/
|
||||
importers?: Set<string>
|
||||
imports?: Set<string>
|
||||
}
|
||||
|
||||
export interface ViteNodeRunnerOptions {
|
||||
root: string
|
||||
fetchModule: FetchFunction
|
||||
resolveId?: ResolveIdFunction
|
||||
createHotContext?: CreateHotContextFunction
|
||||
base?: string
|
||||
moduleCache?: ModuleCacheMap
|
||||
moduleExecutionInfo?: ModuleExecutionInfo
|
||||
interopDefault?: boolean
|
||||
requestStubs?: Record<string, any>
|
||||
debug?: boolean
|
||||
}
|
||||
|
||||
export interface ViteNodeResolveId {
|
||||
external?: boolean | 'absolute' | 'relative'
|
||||
id: string
|
||||
meta?: Record<string, any> | null
|
||||
moduleSideEffects?: boolean | 'no-treeshake' | null
|
||||
syntheticNamedExports?: boolean | string | null
|
||||
}
|
||||
|
||||
export interface ViteNodeResolveModule {
|
||||
external: string | null
|
||||
id: string
|
||||
fsPath: string
|
||||
}
|
||||
|
||||
export interface ViteNodeServerOptions {
|
||||
/**
|
||||
* Inject inline sourcemap to modules
|
||||
* @default 'inline'
|
||||
*/
|
||||
sourcemap?: 'inline' | boolean
|
||||
/**
|
||||
* Deps handling
|
||||
*/
|
||||
deps?: DepsHandlingOptions
|
||||
/**
|
||||
* Transform method for modules
|
||||
*/
|
||||
transformMode?: {
|
||||
ssr?: RegExp[]
|
||||
web?: RegExp[]
|
||||
}
|
||||
|
||||
debug?: DebuggerOptions
|
||||
}
|
||||
|
||||
export interface DebuggerOptions {
|
||||
/**
|
||||
* Dump the transformed module to filesystem
|
||||
* Passing a string will dump to the specified path
|
||||
*/
|
||||
dumpModules?: boolean | string
|
||||
/**
|
||||
* Read dumpped module from filesystem whenever exists.
|
||||
* Useful for debugging by modifying the dump result from the filesystem.
|
||||
*/
|
||||
loadDumppedModules?: boolean
|
||||
}
|
||||
|
||||
export type { ModuleCacheMap, ModuleExecutionInfo }
|
||||
@ -1,317 +0,0 @@
|
||||
import type { Arrayable, Nullable } from './types'
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import nodeModule from 'node:module'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { dirname, join, resolve } from 'pathe'
|
||||
|
||||
export const isWindows: boolean = process.platform === 'win32'
|
||||
|
||||
const drive = isWindows ? process.cwd()[0] : null
|
||||
const driveOpposite = drive
|
||||
? drive === drive.toUpperCase()
|
||||
? drive.toLowerCase()
|
||||
: drive.toUpperCase()
|
||||
: null
|
||||
const driveRegexp = drive ? new RegExp(`(?:^|/@fs/)${drive}(\:[\\/])`) : null
|
||||
const driveOppositeRegext = driveOpposite
|
||||
? new RegExp(`(?:^|/@fs/)${driveOpposite}(\:[\\/])`)
|
||||
: null
|
||||
|
||||
export function slash(str: string): string {
|
||||
return str.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
const bareImportRE = /^(?![a-z]:)[\w@](?!.*:\/\/)/i
|
||||
|
||||
export function isBareImport(id: string): boolean {
|
||||
return bareImportRE.test(id)
|
||||
}
|
||||
|
||||
export const VALID_ID_PREFIX = '/@id/'
|
||||
|
||||
export function normalizeRequestId(id: string, base?: string): string {
|
||||
if (base && id.startsWith(withTrailingSlash(base))) {
|
||||
id = `/${id.slice(base.length)}`
|
||||
}
|
||||
|
||||
// keep drive the same as in process cwd. ideally, this should be resolved on Vite side
|
||||
// Vite always resolves drive letters to the upper case because of the use of `realpathSync`
|
||||
// https://github.com/vitejs/vite/blob/0ab20a3ee26eacf302415b3087732497d0a2f358/packages/vite/src/node/utils.ts#L635
|
||||
if (driveRegexp && !driveRegexp?.test(id) && driveOppositeRegext?.test(id)) {
|
||||
id = id.replace(driveOppositeRegext, `${drive}$1`)
|
||||
}
|
||||
|
||||
if (id.startsWith('file://')) {
|
||||
// preserve hash/query
|
||||
const { file, postfix } = splitFileAndPostfix(id)
|
||||
return fileURLToPath(file) + postfix
|
||||
}
|
||||
|
||||
return id
|
||||
.replace(/^\/@id\/__x00__/, '\0') // virtual modules start with `\0`
|
||||
.replace(/^\/@id\//, '')
|
||||
.replace(/^__vite-browser-external:/, '')
|
||||
.replace(/\?v=\w+/, '?') // remove ?v= query
|
||||
.replace(/&v=\w+/, '') // remove &v= query
|
||||
.replace(/\?t=\w+/, '?') // remove ?t= query
|
||||
.replace(/&t=\w+/, '') // remove &t= query
|
||||
.replace(/\?import/, '?') // remove ?import query
|
||||
.replace(/&import/, '') // remove &import query
|
||||
.replace(/\?&/, '?') // replace ?& with just ?
|
||||
.replace(/\?+$/, '') // remove end query mark
|
||||
}
|
||||
|
||||
const postfixRE = /[?#].*$/
|
||||
export function cleanUrl(url: string): string {
|
||||
return url.replace(postfixRE, '')
|
||||
}
|
||||
|
||||
function splitFileAndPostfix(path: string): {
|
||||
file: string
|
||||
postfix: string
|
||||
} {
|
||||
const file = cleanUrl(path)
|
||||
return { file, postfix: path.slice(file.length) }
|
||||
}
|
||||
|
||||
const internalRequests = ['@vite/client', '@vite/env']
|
||||
|
||||
const internalRequestRegexp = new RegExp(
|
||||
`^/?(?:${internalRequests.join('|')})$`,
|
||||
)
|
||||
|
||||
export function isInternalRequest(id: string): boolean {
|
||||
return internalRequestRegexp.test(id)
|
||||
}
|
||||
|
||||
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
||||
const prefixedBuiltins = new Set([
|
||||
'node:sea',
|
||||
'node:sqlite',
|
||||
'node:test',
|
||||
'node:test/reporters',
|
||||
])
|
||||
|
||||
const builtins = new Set([
|
||||
...nodeModule.builtinModules,
|
||||
'assert/strict',
|
||||
'diagnostics_channel',
|
||||
'dns/promises',
|
||||
'fs/promises',
|
||||
'path/posix',
|
||||
'path/win32',
|
||||
'readline/promises',
|
||||
'stream/consumers',
|
||||
'stream/promises',
|
||||
'stream/web',
|
||||
'timers/promises',
|
||||
'util/types',
|
||||
'wasi',
|
||||
])
|
||||
|
||||
export function normalizeModuleId(id: string): string {
|
||||
// unique id that is not available as "test"
|
||||
if (prefixedBuiltins.has(id)) {
|
||||
return id
|
||||
}
|
||||
if (id.startsWith('file://')) {
|
||||
return fileURLToPath(id)
|
||||
}
|
||||
return id
|
||||
.replace(/\\/g, '/')
|
||||
.replace(/^\/@fs\//, isWindows ? '' : '/')
|
||||
.replace(/^node:/, '')
|
||||
.replace(/^\/+/, '/')
|
||||
}
|
||||
|
||||
export function isPrimitive(v: any): boolean {
|
||||
return v !== Object(v)
|
||||
}
|
||||
|
||||
export function toFilePath(
|
||||
id: string,
|
||||
root: string,
|
||||
): { path: string; exists: boolean } {
|
||||
let { absolute, exists } = (() => {
|
||||
if (id.startsWith('/@fs/')) {
|
||||
return { absolute: id.slice(4), exists: true }
|
||||
}
|
||||
// check if /src/module.js -> <root>/src/module.js
|
||||
if (!id.startsWith(withTrailingSlash(root)) && id[0] === '/') {
|
||||
const resolved = resolve(root, id.slice(1))
|
||||
if (existsSync(cleanUrl(resolved))) {
|
||||
return { absolute: resolved, exists: true }
|
||||
}
|
||||
}
|
||||
else if (
|
||||
id.startsWith(withTrailingSlash(root))
|
||||
&& existsSync(cleanUrl(id))
|
||||
) {
|
||||
return { absolute: id, exists: true }
|
||||
}
|
||||
return { absolute: id, exists: false }
|
||||
})()
|
||||
|
||||
if (absolute.startsWith('//')) {
|
||||
absolute = absolute.slice(1)
|
||||
}
|
||||
|
||||
// disambiguate the `<UNIT>:/` on windows: see nodejs/node#31710
|
||||
return {
|
||||
path:
|
||||
isWindows && absolute[0] === '/'
|
||||
? slash(fileURLToPath(pathToFileURL(absolute.slice(1)).href))
|
||||
: absolute,
|
||||
exists,
|
||||
}
|
||||
}
|
||||
|
||||
const NODE_BUILTIN_NAMESPACE = 'node:'
|
||||
export function isNodeBuiltin(id: string): boolean {
|
||||
// Added in v18.6.0
|
||||
if (nodeModule.isBuiltin) {
|
||||
return nodeModule.isBuiltin(id)
|
||||
}
|
||||
if (prefixedBuiltins.has(id)) {
|
||||
return true
|
||||
}
|
||||
return builtins.has(
|
||||
id.startsWith(NODE_BUILTIN_NAMESPACE)
|
||||
? id.slice(NODE_BUILTIN_NAMESPACE.length)
|
||||
: id,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert `Arrayable<T>` to `Array<T>`
|
||||
*
|
||||
* @category Array
|
||||
*/
|
||||
export function toArray<T>(array?: Nullable<Arrayable<T>>): Array<T> {
|
||||
if (array === null || array === undefined) {
|
||||
array = []
|
||||
}
|
||||
|
||||
if (Array.isArray(array)) {
|
||||
return array
|
||||
}
|
||||
|
||||
return [array]
|
||||
}
|
||||
|
||||
export function getCachedData<T>(
|
||||
cache: Map<string, T>,
|
||||
basedir: string,
|
||||
originalBasedir: string,
|
||||
): NonNullable<T> | undefined {
|
||||
const pkgData = cache.get(getFnpdCacheKey(basedir))
|
||||
if (pkgData) {
|
||||
traverseBetweenDirs(originalBasedir, basedir, (dir) => {
|
||||
cache.set(getFnpdCacheKey(dir), pkgData)
|
||||
})
|
||||
return pkgData
|
||||
}
|
||||
}
|
||||
|
||||
export function setCacheData<T>(
|
||||
cache: Map<string, T>,
|
||||
data: T,
|
||||
basedir: string,
|
||||
originalBasedir: string,
|
||||
): void {
|
||||
cache.set(getFnpdCacheKey(basedir), data)
|
||||
traverseBetweenDirs(originalBasedir, basedir, (dir) => {
|
||||
cache.set(getFnpdCacheKey(dir), data)
|
||||
})
|
||||
}
|
||||
|
||||
function getFnpdCacheKey(basedir: string) {
|
||||
return `fnpd_${basedir}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse between `longerDir` (inclusive) and `shorterDir` (exclusive) and call `cb` for each dir.
|
||||
* @param longerDir Longer dir path, e.g. `/User/foo/bar/baz`
|
||||
* @param shorterDir Shorter dir path, e.g. `/User/foo`
|
||||
*/
|
||||
function traverseBetweenDirs(
|
||||
longerDir: string,
|
||||
shorterDir: string,
|
||||
cb: (dir: string) => void,
|
||||
) {
|
||||
while (longerDir !== shorterDir) {
|
||||
cb(longerDir)
|
||||
longerDir = dirname(longerDir)
|
||||
}
|
||||
}
|
||||
|
||||
export function withTrailingSlash(path: string): string {
|
||||
if (path.at(-1) !== '/') {
|
||||
return `${path}/`
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
export function createImportMetaEnvProxy(): NodeJS.ProcessEnv {
|
||||
// packages/vitest/src/node/plugins/index.ts:146
|
||||
const booleanKeys = ['DEV', 'PROD', 'SSR']
|
||||
return new Proxy(process.env, {
|
||||
get(_, key) {
|
||||
if (typeof key !== 'string') {
|
||||
return undefined
|
||||
}
|
||||
if (booleanKeys.includes(key)) {
|
||||
return !!process.env[key]
|
||||
}
|
||||
return process.env[key]
|
||||
},
|
||||
set(_, key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (booleanKeys.includes(key)) {
|
||||
process.env[key] = value ? '1' : ''
|
||||
}
|
||||
else {
|
||||
process.env[key] = value
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const packageCache = new Map<string, { type?: 'module' | 'commonjs' }>()
|
||||
|
||||
export async function findNearestPackageData(
|
||||
basedir: string,
|
||||
): Promise<{ type?: 'module' | 'commonjs' }> {
|
||||
const originalBasedir = basedir
|
||||
while (basedir) {
|
||||
const cached = getCachedData(packageCache, basedir, originalBasedir)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
const pkgPath = join(basedir, 'package.json')
|
||||
if ((await fsp.stat(pkgPath).catch(() => {}))?.isFile()) {
|
||||
const pkgData = JSON.parse(await fsp.readFile(pkgPath, 'utf8'))
|
||||
|
||||
if (packageCache) {
|
||||
setCacheData(packageCache, pkgData, basedir, originalBasedir)
|
||||
}
|
||||
|
||||
return pkgData
|
||||
}
|
||||
|
||||
const nextBasedir = dirname(basedir)
|
||||
if (nextBasedir === basedir) {
|
||||
break
|
||||
}
|
||||
basedir = nextBasedir
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"isolatedDeclarations": true
|
||||
},
|
||||
"include": ["./src/**/*.ts"],
|
||||
"exclude": ["./dist"]
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import('./dist/cli.mjs')
|
||||
@ -18,7 +18,6 @@ const external = [
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
...Object.keys(pkg.peerDependencies || {}),
|
||||
/^@?vitest(\/|$)/,
|
||||
'vite-node/utils',
|
||||
]
|
||||
|
||||
const dtsUtils = createDtsUtils()
|
||||
|
||||
300
pnpm-lock.yaml
generated
300
pnpm-lock.yaml
generated
@ -918,34 +918,6 @@ importers:
|
||||
specifier: ^0.3.2
|
||||
version: 0.3.2(picocolors@1.1.1)
|
||||
|
||||
packages/vite-node:
|
||||
dependencies:
|
||||
cac:
|
||||
specifier: 'catalog:'
|
||||
version: 6.7.14(patch_hash=a8f0f3517a47ce716ed90c0cfe6ae382ab763b021a664ada2a608477d0621588)
|
||||
debug:
|
||||
specifier: 'catalog:'
|
||||
version: 4.4.3
|
||||
es-module-lexer:
|
||||
specifier: ^1.7.0
|
||||
version: 1.7.0
|
||||
pathe:
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.3
|
||||
vite:
|
||||
specifier: ^7.1.5
|
||||
version: 7.1.5(@types/node@22.18.6)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.0)(sass@1.93.0)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)
|
||||
devDependencies:
|
||||
'@jridgewell/trace-mapping':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.31
|
||||
'@types/debug':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.12
|
||||
tinyrainbow:
|
||||
specifier: 'catalog:'
|
||||
version: 3.0.3
|
||||
|
||||
packages/vitest:
|
||||
dependencies:
|
||||
'@vitest/browser-playwright':
|
||||
@ -1338,9 +1310,6 @@ importers:
|
||||
url:
|
||||
specifier: ^0.11.4
|
||||
version: 0.11.4
|
||||
vite-node:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vite-node
|
||||
vitest:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vitest
|
||||
@ -1510,9 +1479,6 @@ importers:
|
||||
vite:
|
||||
specifier: ^7.1.5
|
||||
version: 7.1.5(@types/node@22.18.6)(jiti@2.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.0)(sass@1.93.0)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)
|
||||
vite-node:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vite-node
|
||||
vitest:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vitest
|
||||
@ -1542,21 +1508,6 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vitest
|
||||
|
||||
test/vite-node:
|
||||
devDependencies:
|
||||
'@types/inquirer':
|
||||
specifier: ^9.0.9
|
||||
version: 9.0.9
|
||||
inquirer:
|
||||
specifier: ^12.9.6
|
||||
version: 12.9.6(@types/node@22.18.6)
|
||||
vite-node:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vite-node
|
||||
vitest:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/vitest
|
||||
|
||||
test/watch:
|
||||
devDependencies:
|
||||
'@vitest/browser-playwright':
|
||||
@ -2947,15 +2898,6 @@ packages:
|
||||
resolution: {integrity: sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@inquirer/checkbox@4.2.4':
|
||||
resolution: {integrity: sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/confirm@5.1.18':
|
||||
resolution: {integrity: sha512-MilmWOzHa3Ks11tzvuAmFoAd/wRuaP3SwlT1IZhyMke31FKLxPiuDWcGXhU+PKveNOpAc4axzAgrgxuIJJRmLw==}
|
||||
engines: {node: '>=18'}
|
||||
@ -2974,100 +2916,10 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/editor@4.2.20':
|
||||
resolution: {integrity: sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/expand@4.0.20':
|
||||
resolution: {integrity: sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/external-editor@1.0.2':
|
||||
resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/figures@1.0.13':
|
||||
resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@inquirer/input@4.2.4':
|
||||
resolution: {integrity: sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/number@3.0.20':
|
||||
resolution: {integrity: sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/password@4.0.20':
|
||||
resolution: {integrity: sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/prompts@7.8.6':
|
||||
resolution: {integrity: sha512-68JhkiojicX9SBUD8FE/pSKbOKtwoyaVj1kwqLfvjlVXZvOy3iaSWX4dCLsZyYx/5Ur07Fq+yuDNOen+5ce6ig==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/rawlist@4.1.8':
|
||||
resolution: {integrity: sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/search@3.1.3':
|
||||
resolution: {integrity: sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/select@4.3.4':
|
||||
resolution: {integrity: sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/type@3.0.8':
|
||||
resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==}
|
||||
engines: {node: '>=18'}
|
||||
@ -4216,9 +4068,6 @@ packages:
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
|
||||
'@types/inquirer@9.0.9':
|
||||
resolution: {integrity: sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw==}
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6':
|
||||
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
|
||||
|
||||
@ -4294,9 +4143,6 @@ packages:
|
||||
'@types/tern@0.23.4':
|
||||
resolution: {integrity: sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==}
|
||||
|
||||
'@types/through@0.0.30':
|
||||
resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==}
|
||||
|
||||
'@types/tough-cookie@4.0.5':
|
||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||
|
||||
@ -5195,9 +5041,6 @@ packages:
|
||||
character-entities@2.0.2:
|
||||
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
|
||||
|
||||
chardet@2.1.0:
|
||||
resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==}
|
||||
|
||||
cheerio-select@2.1.0:
|
||||
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
|
||||
|
||||
@ -6540,10 +6383,6 @@ packages:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
iconv-lite@0.7.0:
|
||||
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
idb@7.1.1:
|
||||
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
|
||||
|
||||
@ -6590,15 +6429,6 @@ packages:
|
||||
ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
|
||||
inquirer@12.9.6:
|
||||
resolution: {integrity: sha512-603xXOgyfxhuis4nfnWaZrMaotNT0Km9XwwBNWUKbIDqeCY89jGr2F9YPEMiNhU6XjIP4VoWISMBFfcc5NgrTw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
internal-slot@1.0.7:
|
||||
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -8218,10 +8048,6 @@ packages:
|
||||
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
run-async@4.0.5:
|
||||
resolution: {integrity: sha512-oN9GTgxUNDBumHTTDmQ8dep6VIJbgj9S3dPP+9XylVLIK4xB9XTXtKWROd5pnhdXR9k0EgO1JRcNh0T+Ny2FsA==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
@ -11080,16 +10906,6 @@ snapshots:
|
||||
|
||||
'@inquirer/ansi@1.0.0': {}
|
||||
|
||||
'@inquirer/checkbox@4.2.4(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.0
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/confirm@5.1.18(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
@ -11110,95 +10926,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/editor@4.2.20(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/external-editor': 1.0.2(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/expand@4.0.20(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/external-editor@1.0.2(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
chardet: 2.1.0
|
||||
iconv-lite: 0.7.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/figures@1.0.13': {}
|
||||
|
||||
'@inquirer/input@4.2.4(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/number@3.0.20(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/password@4.0.20(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.0
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/prompts@7.8.6(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/checkbox': 4.2.4(@types/node@22.18.6)
|
||||
'@inquirer/confirm': 5.1.18(@types/node@22.18.6)
|
||||
'@inquirer/editor': 4.2.20(@types/node@22.18.6)
|
||||
'@inquirer/expand': 4.0.20(@types/node@22.18.6)
|
||||
'@inquirer/input': 4.2.4(@types/node@22.18.6)
|
||||
'@inquirer/number': 3.0.20(@types/node@22.18.6)
|
||||
'@inquirer/password': 4.0.20(@types/node@22.18.6)
|
||||
'@inquirer/rawlist': 4.1.8(@types/node@22.18.6)
|
||||
'@inquirer/search': 3.1.3(@types/node@22.18.6)
|
||||
'@inquirer/select': 4.3.4(@types/node@22.18.6)
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/rawlist@4.1.8(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/search@3.1.3(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/select@4.3.4(@types/node@22.18.6)':
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.0
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@inquirer/type@3.0.8(@types/node@22.18.6)':
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
@ -12153,11 +11882,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
|
||||
'@types/inquirer@9.0.9':
|
||||
dependencies:
|
||||
'@types/through': 0.0.30
|
||||
rxjs: 7.8.2
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||
|
||||
'@types/istanbul-lib-instrument@1.7.8':
|
||||
@ -12246,10 +11970,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
|
||||
'@types/through@0.0.30':
|
||||
dependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
'@types/tough-cookie@4.0.5': {}
|
||||
|
||||
'@types/trusted-types@2.0.7': {}
|
||||
@ -13356,8 +13076,6 @@ snapshots:
|
||||
|
||||
character-entities@2.0.2: {}
|
||||
|
||||
chardet@2.1.0: {}
|
||||
|
||||
cheerio-select@2.1.0:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
@ -14980,10 +14698,6 @@ snapshots:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
iconv-lite@0.7.0:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
idb@7.1.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
@ -15016,18 +14730,6 @@ snapshots:
|
||||
|
||||
ini@1.3.8: {}
|
||||
|
||||
inquirer@12.9.6(@types/node@22.18.6):
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.0
|
||||
'@inquirer/core': 10.2.2(@types/node@22.18.6)
|
||||
'@inquirer/prompts': 7.8.6(@types/node@22.18.6)
|
||||
'@inquirer/type': 3.0.8(@types/node@22.18.6)
|
||||
mute-stream: 2.0.0
|
||||
run-async: 4.0.5
|
||||
rxjs: 7.8.2
|
||||
optionalDependencies:
|
||||
'@types/node': 22.18.6
|
||||
|
||||
internal-slot@1.0.7:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@ -16936,8 +16638,6 @@ snapshots:
|
||||
|
||||
run-applescript@7.0.0: {}
|
||||
|
||||
run-async@4.0.5: {}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { expect, test } from 'vitest'
|
||||
import { isWindows } from '../../../packages/vite-node/src/utils'
|
||||
import { runVitest } from '../../test-utils'
|
||||
|
||||
test('with color', async () => {
|
||||
@ -31,7 +30,7 @@ test('without color', async () => {
|
||||
expect(stdout).not.toContain('\x1B[33mtrue\x1B[39m\n')
|
||||
})
|
||||
|
||||
test.skipIf(isWindows)('without color, forks pool in non-TTY parent', async () => {
|
||||
test.skipIf(process.platform === 'win32')('without color, forks pool in non-TTY parent', async () => {
|
||||
const { stdout } = await runVitest({
|
||||
root: 'fixtures/console-color',
|
||||
env: {
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
"tinyrainbow": "catalog:",
|
||||
"tinyspy": "^4.0.4",
|
||||
"url": "^0.11.4",
|
||||
"vite-node": "workspace:*",
|
||||
"vitest": "workspace:*",
|
||||
"vitest-environment-custom": "file:./vitest-environment-custom",
|
||||
"vitest-package-exports": "^0.1.1",
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { isWindows, slash, toFilePath } from '../../../packages/vite-node/src/utils'
|
||||
|
||||
vi.mock('fs', () => {
|
||||
return {
|
||||
@ -8,6 +6,8 @@ vi.mock('fs', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const isWindows = process.platform === 'win32'
|
||||
|
||||
describe('current url', () => {
|
||||
it('__filename is equal to import.meta.url', () => {
|
||||
expect(__filename).toEqual(import.meta.filename)
|
||||
@ -60,118 +60,3 @@ describe('current url', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('toFilePath', () => {
|
||||
// the following tests will work incorrectly on unix systems
|
||||
describe.runIf(isWindows)('windows', () => {
|
||||
it('windows', () => {
|
||||
const root = 'C:/path/to/project'
|
||||
const id = '/node_modules/pkg/file.js'
|
||||
const expected = 'C:/path/to/project/node_modules/pkg/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
|
||||
it('windows with /@fs/', () => {
|
||||
const root = 'C:/path/to/project'
|
||||
const id = '/@fs/C:/path/to/project/node_modules/pkg/file.js'
|
||||
const expected = 'C:/path/to/project/node_modules/pkg/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
// the following tests will work incorrectly on windows systems
|
||||
describe.runIf(!isWindows)('unix', () => {
|
||||
it('unix', () => {
|
||||
const root = '/path/to/project'
|
||||
const id = '/node_modules/pkg/file.js'
|
||||
const expected = '/path/to/project/node_modules/pkg/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const existsSpy = vi.mocked(existsSync).mockReturnValue(true)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
existsSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
|
||||
it('unix with /@fs/', () => {
|
||||
const root = '/path/to/project'
|
||||
const id = '/@fs//path/to/project/node_modules/pkg/file.js'
|
||||
const expected = '/path/to/project/node_modules/pkg/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const existsSpy = vi.mocked(existsSync).mockReturnValue(true)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
existsSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
|
||||
it('unix in first level catalog', () => {
|
||||
const root = '/root'
|
||||
const id = '/node_modules/pkg/file.js'
|
||||
const expected = '/root/node_modules/pkg/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const existsSpy = vi.mocked(existsSync).mockReturnValue(true)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
existsSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
|
||||
it('unix with /@fs/ in first level catalog', () => {
|
||||
const root = '/root'
|
||||
const id = '/@fs//root/node_modules/pkg/file.js'
|
||||
const expected = '/root/node_modules/pkg/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const existsSpy = vi.mocked(existsSync).mockReturnValue(true)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
existsSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
|
||||
it('unix with absolute path in first level catalog', () => {
|
||||
const root = '/root'
|
||||
const id = '/root/path/to/file.js'
|
||||
const expected = '/root/path/to/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const existsSpy = vi.mocked(existsSync).mockReturnValue(true)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
existsSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(expected)
|
||||
})
|
||||
|
||||
it('unix with sibling path', () => {
|
||||
const root = '/path/to/first/package'
|
||||
const id = '/path/to/second/package/file.js'
|
||||
|
||||
const processSpy = vi.spyOn(process, 'cwd').mockReturnValue(root)
|
||||
const existsSpy = vi.mocked(existsSync).mockReturnValue(false)
|
||||
const { path: filePath } = toFilePath(id, root)
|
||||
processSpy.mockRestore()
|
||||
existsSpy.mockRestore()
|
||||
|
||||
expect(slash(filePath)).toEqual(id)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -153,7 +153,7 @@ interface CliOptions extends Partial<Options> {
|
||||
preserveAnsi?: boolean
|
||||
}
|
||||
|
||||
async function runCli(command: 'vitest' | 'vite-node', _options?: CliOptions | string, ...args: string[]) {
|
||||
async function runCli(command: 'vitest', _options?: CliOptions | string, ...args: string[]) {
|
||||
let options = _options
|
||||
|
||||
if (typeof _options === 'string') {
|
||||
@ -228,13 +228,6 @@ export async function runVitestCli(_options?: CliOptions | string, ...args: stri
|
||||
return runCli('vitest', _options, ...args)
|
||||
}
|
||||
|
||||
export async function runViteNodeCli(_options?: CliOptions | string, ...args: string[]) {
|
||||
process.env.VITE_TEST_WATCHER_DEBUG = 'true'
|
||||
const { vitest, ...rest } = await runCli('vite-node', _options, ...args)
|
||||
|
||||
return { viteNode: vitest, ...rest }
|
||||
}
|
||||
|
||||
export function getInternalState(): WorkerGlobalState {
|
||||
// @ts-expect-error untyped global
|
||||
return globalThis.__vitest_worker__
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinyrainbow": "catalog:",
|
||||
"vite": "latest",
|
||||
"vite-node": "workspace:*",
|
||||
"vitest": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
MY_TEST_ENV=hello
|
||||
@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "@vitest/test-vite-node",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"dev": "vite-node --watch ./src/*.ts",
|
||||
"debug:dev": "DEBUG=vite-node:* node --inspect-brk ../../packages/vite-node/dist/cli.cjs --watch ./src/*",
|
||||
"debug": "node --inspect-brk ../../packages/vite-node/dist/cli.cjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/inquirer": "^9.0.9",
|
||||
"inquirer": "^12.9.6",
|
||||
"vite-node": "workspace:*",
|
||||
"vitest": "workspace:*"
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export default 'test'
|
||||
@ -1,21 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
const data: string[] = []
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
name: 'test-plugin',
|
||||
async buildStart() {
|
||||
data.push('buildStart:in')
|
||||
await new Promise(r => setTimeout(r, 100))
|
||||
data.push('buildStart:out')
|
||||
},
|
||||
transform(_code, id) {
|
||||
if (id.endsWith('/test.ts')) {
|
||||
console.log(JSON.stringify(data))
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -1,3 +0,0 @@
|
||||
export function a() {
|
||||
return 'A'
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
import { foo } from '.'
|
||||
|
||||
export function b() {
|
||||
return `B${foo()}`
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { a } from './a'
|
||||
import { b } from './b'
|
||||
|
||||
export const index = 'index'
|
||||
|
||||
export function foo() {
|
||||
return index
|
||||
}
|
||||
|
||||
export * from './a'
|
||||
export * from './b'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(a(), b(), index)
|
||||
@ -1,5 +0,0 @@
|
||||
import { c } from './reg'
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
|
||||
export const a = `a${c}`
|
||||
@ -1,6 +0,0 @@
|
||||
// eslint-disable-next-line unused-imports/no-unused-imports
|
||||
import { a } from './a'
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
|
||||
export const b = 'b'
|
||||
@ -1,3 +0,0 @@
|
||||
export const c = 'c'
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
@ -1,11 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import { a } from './a'
|
||||
import { b } from './b'
|
||||
|
||||
/**
|
||||
* index -> a -> b
|
||||
* ^ ^v
|
||||
* reg -> c
|
||||
*/
|
||||
|
||||
console.log(a, b)
|
||||
@ -1,6 +0,0 @@
|
||||
export { a } from './a'
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 20))
|
||||
|
||||
export { b } from './b'
|
||||
export { c } from './c'
|
||||
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(process.argv)
|
||||
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(JSON.stringify(process.env, null, 2))
|
||||
@ -1,3 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import './deps.css'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[deps.ts] imported')
|
||||
|
||||
export {}
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[deps.ts] hot reload!')
|
||||
})
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(new Error('[ok]').stack)
|
||||
@ -1,14 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
{
|
||||
name: 'repro',
|
||||
transform(code, id) {
|
||||
if (id.endsWith('/empty-mappings/main.ts')) {
|
||||
return { code, map: { mappings: '' } }
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -1,3 +0,0 @@
|
||||
export function add(a, b) {
|
||||
return a + b
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
console.error('Hello!')
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(() => {
|
||||
console.error('Accept')
|
||||
})
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(() => {})
|
||||
if (import.meta.hot.data.value == null) {
|
||||
import.meta.hot.data.value = 0
|
||||
}
|
||||
else {
|
||||
// eslint-disable-next-line no-throw-literal
|
||||
throw 'some error'
|
||||
}
|
||||
}
|
||||
console.error('ready')
|
||||
@ -1,12 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import { a } from './testMod'
|
||||
|
||||
console.log('[main.js] load!')
|
||||
console.log('[main.js] hello world')
|
||||
console.log('[main.js]', a)
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(() => {
|
||||
console.log('[main.ts] hot reload!')
|
||||
})
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
// const ansiEscapes = module.exports
|
||||
// module.exports.default = ansiEscapes
|
||||
// ansiEscapes.HelloWorld = 1
|
||||
// console.log(ansiEscapes.HelloWorld);
|
||||
|
||||
import inquirer from 'inquirer'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(inquirer.prompt)
|
||||
@ -1,4 +0,0 @@
|
||||
import ansiEscapes, { HelloWorld } from './self-export'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(ansiEscapes, HelloWorld)
|
||||
7
test/vite-node/src/self-export.d.ts
vendored
7
test/vite-node/src/self-export.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
declare const ansiEscapes: {
|
||||
HelloWorld: number
|
||||
}
|
||||
export default ansiEscapes
|
||||
|
||||
declare const HelloWorld: number
|
||||
export { HelloWorld }
|
||||
@ -1,3 +0,0 @@
|
||||
const ansiEscapes = module.exports
|
||||
module.exports.default = ansiEscapes
|
||||
ansiEscapes.HelloWorld = 1
|
||||
@ -1,6 +0,0 @@
|
||||
export const a = 'hello testModule'
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[testModule.js] load!')
|
||||
|
||||
import('./deps')
|
||||
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('test 1')
|
||||
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('test 1')
|
||||
@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('test 1')
|
||||
@ -1,16 +0,0 @@
|
||||
// 1
|
||||
// 1
|
||||
async function main() {
|
||||
try {
|
||||
// 2
|
||||
// 2
|
||||
throw new Error('boom')
|
||||
}
|
||||
catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
// 3
|
||||
// 3
|
||||
main()
|
||||
@ -1,15 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { expect, test } from 'vitest'
|
||||
import { runViteNodeCli } from '../../test-utils'
|
||||
|
||||
test('circular 1', async () => {
|
||||
const entryPath = resolve(import.meta.dirname, '../src/circular1/index.ts')
|
||||
const cli = await runViteNodeCli(entryPath)
|
||||
expect(cli.stdout).toContain('A Bindex index')
|
||||
}, 60_000)
|
||||
|
||||
test('circular 2', async () => {
|
||||
const entryPath = resolve(import.meta.dirname, '../src/circular2/index.ts')
|
||||
const cli = await runViteNodeCli(entryPath)
|
||||
expect(cli.stdout).toContain('ac b')
|
||||
}, 60_000)
|
||||
@ -1,82 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import pkg from 'vite-node/package.json'
|
||||
import { expect, it } from 'vitest'
|
||||
import { editFile, runViteNodeCli } from '../../test-utils'
|
||||
|
||||
const entryPath = resolve(import.meta.dirname, '../src/cli-parse-args.js')
|
||||
|
||||
const version = (pkg as any).version
|
||||
|
||||
const parseResult = (s: string) => JSON.parse(s.replaceAll('\'', '"'))
|
||||
|
||||
it('basic', async () => {
|
||||
const cli = await runViteNodeCli(entryPath)
|
||||
expect(cli.stdout).toContain('node')
|
||||
expect(parseResult(cli.stdout)).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('--help', async () => {
|
||||
const cli1 = await runViteNodeCli('--help', entryPath)
|
||||
expect(cli1.stdout).toContain('Usage:')
|
||||
const cli2 = await runViteNodeCli('-h', entryPath)
|
||||
expect(cli2.stdout).toContain('Usage:')
|
||||
})
|
||||
|
||||
it('--version', async () => {
|
||||
const cli1 = await runViteNodeCli('--version', entryPath)
|
||||
expect(cli1.stdout).toContain(`vite-node/${version}`)
|
||||
const cli2 = await runViteNodeCli('-v', entryPath)
|
||||
expect(cli2.stdout).toContain(`vite-node/${version}`)
|
||||
})
|
||||
|
||||
it('script args', async () => {
|
||||
const cli1 = await runViteNodeCli(entryPath, '--version', '--help')
|
||||
expect(parseResult(cli1.stdout)).include('--version').include('--help')
|
||||
})
|
||||
|
||||
it('script args in -- after', async () => {
|
||||
const cli1 = await runViteNodeCli(entryPath, '--', '--version', '--help')
|
||||
expect(parseResult(cli1.stdout)).include('--version').include('--help')
|
||||
})
|
||||
|
||||
it('exposes .env variables', async () => {
|
||||
const { stdout } = await runViteNodeCli(resolve(import.meta.dirname, '../src/cli-print-env.js'))
|
||||
const env = JSON.parse(stdout)
|
||||
expect(env.MY_TEST_ENV).toBe('hello')
|
||||
})
|
||||
|
||||
it.each(['index.js', 'index.cjs', 'index.mjs'])('correctly runs --watch %s', async (file) => {
|
||||
const entryPath = resolve(import.meta.dirname, '../src/watch', file)
|
||||
const { viteNode } = await runViteNodeCli('--watch', entryPath)
|
||||
await viteNode.waitForStdout('test 1')
|
||||
editFile(entryPath, c => c.replace('test 1', 'test 2'))
|
||||
await viteNode.waitForStdout('test 2')
|
||||
})
|
||||
|
||||
it('error stack', async () => {
|
||||
const entryPath = resolve(import.meta.dirname, '../src/watch/source-map.ts')
|
||||
const { viteNode } = await runViteNodeCli('--watch', entryPath)
|
||||
await viteNode.waitForStdout('source-map.ts:7:11')
|
||||
})
|
||||
|
||||
it('buildStart', async () => {
|
||||
const root = resolve(import.meta.dirname, '../src/buildStart')
|
||||
const result = await runViteNodeCli('--root', root, resolve(root, 'test.ts'))
|
||||
await result.viteNode.waitForStdout('["buildStart:in","buildStart:out"]')
|
||||
})
|
||||
|
||||
it('buildStart with all ssr', async () => {
|
||||
const root = resolve(import.meta.dirname, '../src/buildStart')
|
||||
const result = await runViteNodeCli(
|
||||
`--root=${root}`,
|
||||
'--options.transformMode.ssr=.*',
|
||||
resolve(root, 'test.ts'),
|
||||
)
|
||||
await result.viteNode.waitForStdout('["buildStart:in","buildStart:out"]')
|
||||
})
|
||||
|
||||
it('empty mappings', async () => {
|
||||
const root = resolve(import.meta.dirname, '../src/empty-mappings')
|
||||
const result = await runViteNodeCli('--root', root, resolve(root, 'main.ts'))
|
||||
await result.viteNode.waitForStdout('[ok]')
|
||||
})
|
||||
@ -1,35 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { test } from 'vitest'
|
||||
import { editFile, runViteNodeCli } from '../../test-utils'
|
||||
|
||||
test('hmr.accept works correctly', async () => {
|
||||
const scriptFile = resolve(import.meta.dirname, '../src/hmr-script.js')
|
||||
|
||||
const { viteNode } = await runViteNodeCli('--watch', scriptFile)
|
||||
|
||||
await viteNode.waitForStderr('Hello!')
|
||||
|
||||
editFile(scriptFile, content => content.replace('Hello!', 'Hello world!'))
|
||||
|
||||
await viteNode.waitForStderr('Hello world!')
|
||||
await viteNode.waitForStderr('Accept')
|
||||
await viteNode.waitForStdout(`[vite-node] hot updated: ${scriptFile}`)
|
||||
})
|
||||
|
||||
test('can handle top-level throw in self-accepting module', async () => {
|
||||
const scriptFile = resolve(import.meta.dirname, '../src/hmr-throw.js')
|
||||
|
||||
const { viteNode } = await runViteNodeCli('--watch', scriptFile)
|
||||
|
||||
await viteNode.waitForStderr('ready')
|
||||
|
||||
editFile(scriptFile, content => `${content}\nconsole.error("done")`)
|
||||
|
||||
await viteNode.waitForStderr('some error')
|
||||
await viteNode.waitForStderr(`[hmr] Failed to reload ${scriptFile}. This could be due to syntax errors or importing non-existent modules. (see errors above)`)
|
||||
})
|
||||
|
||||
test('basic', async () => {
|
||||
const { viteNode } = await runViteNodeCli('--watch', resolve(import.meta.dirname, '../src/testMod.ts'))
|
||||
await viteNode.waitForStdout('[deps.ts] imported')
|
||||
})
|
||||
@ -1,22 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { expect, it } from 'vitest'
|
||||
import { runViteNodeCli } from '../../test-utils'
|
||||
import ansiEscapes, { HelloWorld } from '../src/self-export'
|
||||
|
||||
it('should export self', () => {
|
||||
expect(ansiEscapes.HelloWorld).eq(HelloWorld)
|
||||
expect(Reflect.get(ansiEscapes, 'default').HelloWorld).eq(HelloWorld)
|
||||
expect(HelloWorld).eq(1)
|
||||
})
|
||||
|
||||
it('example 1', async () => {
|
||||
const entryPath = resolve(import.meta.dirname, '../src/self-export-example1.ts')
|
||||
const { viteNode } = await runViteNodeCli(entryPath)
|
||||
await viteNode.waitForStdout('Function')
|
||||
}, 60_000)
|
||||
|
||||
it('example 2', async () => {
|
||||
const entryPath = resolve(import.meta.dirname, '../src/self-export-example2.ts')
|
||||
const { viteNode } = await runViteNodeCli(entryPath)
|
||||
await viteNode.waitForStdout('HelloWorld: 1')
|
||||
}, 60_000)
|
||||
@ -1,284 +0,0 @@
|
||||
import type { Plugin, ViteDevServer } from 'vite'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { createServer } from 'vite'
|
||||
import { ViteNodeServer } from 'vite-node/server'
|
||||
import { describe, expect, test, vi } from 'vitest'
|
||||
import { extractSourceMap } from '../../../packages/vite-node/src/source-map'
|
||||
|
||||
describe('server works correctly', async () => {
|
||||
test('resolve id considers transform mode', async () => {
|
||||
const resolveId = vi.fn()
|
||||
|
||||
const vnServer = new ViteNodeServer({
|
||||
pluginContainer: { resolveId },
|
||||
config: {
|
||||
root: '/',
|
||||
},
|
||||
moduleGraph: {
|
||||
idToModuleMap: new Map(),
|
||||
},
|
||||
} as any, {
|
||||
transformMode: {
|
||||
web: [/web/],
|
||||
ssr: [/ssr/],
|
||||
},
|
||||
})
|
||||
|
||||
await vnServer.resolveId('/path', '/web path')
|
||||
expect(resolveId).toHaveBeenCalledWith('/path', '/web path', { ssr: false })
|
||||
|
||||
await vnServer.resolveId('/ssr', '/ssr path')
|
||||
expect(resolveId).toHaveBeenCalledWith('/ssr', '/ssr path', { ssr: true })
|
||||
})
|
||||
})
|
||||
|
||||
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
describe('server correctly caches data', () => {
|
||||
const it = test.extend<{
|
||||
root: string
|
||||
plugin: Plugin
|
||||
ssrFiles: string[]
|
||||
webFiles: string[]
|
||||
server: ViteDevServer
|
||||
viteNode: ViteNodeServer
|
||||
}>({
|
||||
ssrFiles: async ({}, use) => {
|
||||
await use([])
|
||||
},
|
||||
webFiles: async ({}, use) => {
|
||||
await use([])
|
||||
},
|
||||
root: resolve(import.meta.dirname, '../'),
|
||||
plugin: async ({ ssrFiles, webFiles }, use) => {
|
||||
const plugin: Plugin = {
|
||||
name: 'test',
|
||||
transform(code, id, options) {
|
||||
// this should be called only once if cached is configured correctly
|
||||
if (options?.ssr) {
|
||||
ssrFiles.push(id)
|
||||
}
|
||||
else {
|
||||
webFiles.push(id)
|
||||
}
|
||||
},
|
||||
}
|
||||
await use(plugin)
|
||||
},
|
||||
server: async ({ root, plugin }, use) => {
|
||||
const server = await createServer({
|
||||
configFile: false,
|
||||
root,
|
||||
server: {
|
||||
middlewareMode: true,
|
||||
watch: null,
|
||||
},
|
||||
optimizeDeps: {
|
||||
disabled: true,
|
||||
},
|
||||
plugins: [plugin],
|
||||
})
|
||||
await use(server)
|
||||
await server.close()
|
||||
},
|
||||
viteNode: async ({ server }, use) => {
|
||||
const vnServer = new ViteNodeServer(server)
|
||||
await use(vnServer)
|
||||
},
|
||||
})
|
||||
|
||||
it('fetchModule with id, and got sourcemap source in absolute path', async ({ viteNode }) => {
|
||||
const fetchResult = await viteNode.fetchModule('/src/foo.js')
|
||||
|
||||
const sourceMap = extractSourceMap(fetchResult.code!)
|
||||
|
||||
// expect got sourcemap source in a valid filesystem path
|
||||
expect(sourceMap?.sources[0]).toBe('foo.js')
|
||||
})
|
||||
|
||||
it('correctly returns cached and invalidated ssr modules', async ({ root, viteNode, ssrFiles, webFiles, server }) => {
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
|
||||
const fsPath = join(root, './src/foo.js')
|
||||
|
||||
expect(viteNode.fetchCaches.web.has(fsPath)).toBe(false)
|
||||
expect(viteNode.fetchCache.has(fsPath)).toBe(true)
|
||||
expect(viteNode.fetchCaches.ssr.has(fsPath)).toBe(true)
|
||||
|
||||
expect(webFiles).toHaveLength(0)
|
||||
expect(ssrFiles).toHaveLength(1)
|
||||
expect(ssrFiles).toContain(fsPath)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
|
||||
expect(ssrFiles).toHaveLength(1)
|
||||
|
||||
server.moduleGraph.invalidateModule(
|
||||
server.moduleGraph.getModuleById(fsPath)!,
|
||||
new Set(),
|
||||
Date.now(),
|
||||
false,
|
||||
)
|
||||
|
||||
// wait so TS are different
|
||||
await wait(100)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
|
||||
expect(ssrFiles).toHaveLength(2)
|
||||
|
||||
// another fetch after invalidation returns cached result
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
|
||||
expect(ssrFiles).toHaveLength(2)
|
||||
|
||||
server.moduleGraph.invalidateModule(
|
||||
server.moduleGraph.getModuleById(fsPath)!,
|
||||
new Set(),
|
||||
Date.now(),
|
||||
true,
|
||||
)
|
||||
|
||||
// wait so TS are different
|
||||
await wait(100)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
|
||||
await expect.poll(() => ssrFiles).toHaveLength(3)
|
||||
|
||||
// another fetch after invalidation returns cached result
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
|
||||
await expect.poll(() => ssrFiles).toHaveLength(3)
|
||||
expect(webFiles).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('correctly returns cached and invalidated web modules', async ({ root, viteNode, webFiles, ssrFiles, server }) => {
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
const fsPath = join(root, './src/foo.js')
|
||||
|
||||
expect(viteNode.fetchCaches.ssr.has(fsPath)).toBe(false)
|
||||
expect(viteNode.fetchCache.has(fsPath)).toBe(true)
|
||||
expect(viteNode.fetchCaches.web.has(fsPath)).toBe(true)
|
||||
|
||||
expect(ssrFiles).toHaveLength(0)
|
||||
expect(webFiles).toHaveLength(1)
|
||||
expect(webFiles).toContain(fsPath)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
expect(webFiles).toHaveLength(1)
|
||||
|
||||
server.moduleGraph.invalidateModule(
|
||||
server.moduleGraph.getModuleById(fsPath)!,
|
||||
new Set(),
|
||||
Date.now(),
|
||||
false,
|
||||
)
|
||||
|
||||
// wait so TS are different
|
||||
await wait(100)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
expect(webFiles).toHaveLength(2)
|
||||
|
||||
// another fetch after invalidation returns cached result
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
expect(webFiles).toHaveLength(2)
|
||||
|
||||
server.moduleGraph.invalidateModule(
|
||||
server.moduleGraph.getModuleById(fsPath)!,
|
||||
new Set(),
|
||||
Date.now(),
|
||||
true,
|
||||
)
|
||||
|
||||
// wait so TS are different
|
||||
await wait(100)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
await expect.poll(() => webFiles).toHaveLength(3)
|
||||
|
||||
// another fetch after invalidation returns cached result
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
await expect.poll(() => webFiles).toHaveLength(3)
|
||||
await expect.poll(() => ssrFiles).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('correctly processes the same file with both transform modes', async ({ viteNode, ssrFiles, webFiles, root }) => {
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
const fsPath = join(root, './src/foo.js')
|
||||
|
||||
expect(viteNode.fetchCaches.ssr.has(fsPath)).toBe(true)
|
||||
expect(viteNode.fetchCache.has(fsPath)).toBe(true)
|
||||
expect(viteNode.fetchCaches.web.has(fsPath)).toBe(true)
|
||||
|
||||
expect(ssrFiles).toHaveLength(1)
|
||||
expect(webFiles).toHaveLength(1)
|
||||
|
||||
await viteNode.fetchModule('/src/foo.js', 'ssr')
|
||||
await viteNode.fetchModule('/src/foo.js', 'web')
|
||||
|
||||
expect(ssrFiles).toHaveLength(1)
|
||||
expect(webFiles).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('externalize', () => {
|
||||
describe('by default', () => {
|
||||
test('should externalize vite\'s cached dependencies', async () => {
|
||||
const vnServer = new ViteNodeServer({
|
||||
config: {
|
||||
root: '/',
|
||||
cacheDir: '/node_modules/.vite',
|
||||
},
|
||||
} as any, {})
|
||||
|
||||
const externalize = await vnServer.shouldExternalize('/node_modules/.vite/cached.js')
|
||||
expect(externalize).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with server.deps.inline: true', () => {
|
||||
test('should not externalize vite\'s cached dependencies', async () => {
|
||||
const vnServer = new ViteNodeServer({
|
||||
config: {
|
||||
root: '/',
|
||||
cacheDir: '/node_modules/.vite',
|
||||
},
|
||||
} as any, {
|
||||
deps: {
|
||||
inline: true,
|
||||
},
|
||||
})
|
||||
|
||||
const externalize = await vnServer.shouldExternalize('/node_modules/.vite/cached.js')
|
||||
expect(externalize).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with server.deps.inline including the cache dir', () => {
|
||||
test('should not externalize vite\'s cached dependencies', async () => {
|
||||
const vnServer = new ViteNodeServer({
|
||||
config: {
|
||||
root: '/',
|
||||
cacheDir: '/node_modules/.vite',
|
||||
},
|
||||
} as any, {
|
||||
deps: {
|
||||
inline: [/node_modules\/\.vite/],
|
||||
},
|
||||
})
|
||||
|
||||
const externalize = await vnServer.shouldExternalize('/node_modules/.vite/cached.js')
|
||||
expect(externalize).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,91 +0,0 @@
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
import type { TransformResult } from 'vite'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { withInlineSourcemap } from '../../../packages/vite-node/src/source-map'
|
||||
|
||||
it('regex match', () => {
|
||||
const regex = /\/\/# sourceMappingURL=data:application\/json;charset=utf-8;base64,[A-Za-z0-9+/=]+$/gm
|
||||
expect('function foo(src) {\n return `//# sourceMappingURL=data:application/json;base64,${src}`;\n}\nObject.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});\n'.match(regex)).toBeNull()
|
||||
expect(`function foo(src) {
|
||||
return \`//# sourceMappingURL=data:application/json;base64,\${src}\`;
|
||||
}
|
||||
Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});
|
||||
|
||||
//# sourceMappingSource=vite-node
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udHMifQ==`.match(regex)).deep.eq(['//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udHMifQ=='])
|
||||
expect(`function foo(src) {
|
||||
return \`//# sourceMappingURL=data:application/json;base64,\${src}\`;
|
||||
}
|
||||
Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});
|
||||
|
||||
//# sourceMappingSource=vite-node
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udHMifQ==`.replace(regex, '')).not.include('//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udHMifQ==')
|
||||
})
|
||||
|
||||
describe('withInlineSourcemap', () => {
|
||||
const input: TransformResult = {
|
||||
code: 'function foo(src) {\n return `//# sourceMappingURL=data:application/json;base64,${src}`;\n}\nObject.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});\n',
|
||||
map: {
|
||||
version: 3,
|
||||
mappings: 'AAAO,SAAS,IAAI,KAAqB;AACvC,SAAO,qDAAqD;AAC9D;iHAAA',
|
||||
names: [],
|
||||
sources: [
|
||||
'/foo.ts',
|
||||
],
|
||||
sourcesContent: [
|
||||
'export function foo(src: string): string {\n return `//# sourceMappingURL=data:application/json;base64,${src}`\n}\n',
|
||||
],
|
||||
file: '/src/foo.ts',
|
||||
toUrl: () => '',
|
||||
},
|
||||
deps: [
|
||||
],
|
||||
dynamicDeps: [
|
||||
],
|
||||
}
|
||||
const options = {
|
||||
root: '/',
|
||||
filepath: '/foo.ts',
|
||||
}
|
||||
|
||||
it('Check that the original sourcemap in the string is not removed', () => {
|
||||
expect(withInlineSourcemap(input, options).code).contain('return `//# sourceMappingURL=data:application/json;base64,${src}`')
|
||||
})
|
||||
it('Check that the appended sourcemap is removed', () => {
|
||||
input.code = `function foo(src) {
|
||||
return \`//# sourceMappingURL=data:application/json;base64,\${src}\`;
|
||||
}
|
||||
Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});
|
||||
|
||||
//# sourceMappingSource=other
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udtest==`
|
||||
|
||||
expect(withInlineSourcemap(input, options).code).not.toContain('//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udtest==')
|
||||
})
|
||||
it('Check that the vite-node sourcemap isnot removed', () => {
|
||||
input.code = `function foo(src) {
|
||||
return \`//# sourceMappingURL=data:application/json;base64,\${src}\`;
|
||||
}
|
||||
Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});
|
||||
|
||||
//# sourceMappingSource=vite-node
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udHMifQ==`
|
||||
|
||||
expect(withInlineSourcemap(input, options).code).include('//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQU8sU0FBUyxJQUFJLEtBQXFCO0FBQ3ZDLFNBQU8scURBQXFEO0FBQzlEO2lIQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL2Zvby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gZm9vKHNyYzogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAvLyMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCR7c3JjfWBcbn1cbiJdLCJmaWxlIjoiL3NyYy9mb28udHMifQ==')
|
||||
})
|
||||
it('Check that the vite-node in real code', () => {
|
||||
input.code = `
|
||||
import { expect, it } from 'vitest'
|
||||
|
||||
it('should have sourcemaps', () => {
|
||||
expect('\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9==').toBeTruthy()
|
||||
expect("\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9==").toBeTruthy()
|
||||
expect(\`\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9==\`).toBeTruthy()
|
||||
})
|
||||
`
|
||||
expect(withInlineSourcemap(input, options).code)
|
||||
.include('\'\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9==\'')
|
||||
.include('"\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9=="')
|
||||
.include('`\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9==`')
|
||||
})
|
||||
})
|
||||
@ -1,13 +0,0 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
clearMocks: true,
|
||||
testTimeout: process.env.CI ? 120_000 : 5_000,
|
||||
onConsoleLog(log) {
|
||||
if (log.includes('Port is already')) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -28,9 +28,7 @@
|
||||
"vitest/internal/module-runner": ["./packages/vitest/src/public/module-runner.ts"],
|
||||
"vitest/globals": ["./packages/vitest/globals.d.ts"],
|
||||
"vitest/browser": ["./packages/vitest/browser/context.d.ts"],
|
||||
"vitest/*": ["./packages/vitest/src/public/*"],
|
||||
"vite-node": ["./packages/vite-node/src/index.ts"],
|
||||
"vite-node/*": ["./packages/vite-node/src/*"]
|
||||
"vitest/*": ["./packages/vitest/src/public/*"]
|
||||
},
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user