diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml
index c2c50da948b0be..30951771278a60 100644
--- a/.github/workflows/ecosystem-ci-trigger.yml
+++ b/.github/workflows/ecosystem-ci-trigger.yml
@@ -9,7 +9,9 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'vitejs/vite' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
steps:
- - uses: actions/github-script@v7
+ - name: Check User Permissions
+ uses: actions/github-script@v7
+ id: check-permissions
with:
script: |
const user = context.payload.sender.login
@@ -28,7 +30,7 @@ jobs:
}
if (hasTriagePermission) {
- console.log('Allowed')
+ console.log('User is allowed. Adding +1 reaction.')
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
@@ -36,16 +38,18 @@ jobs:
content: '+1',
})
} else {
- console.log('Not allowed')
+ console.log('User is not allowed. Adding -1 reaction.')
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: '-1',
})
- throw new Error('not allowed')
+ throw new Error('User does not have the necessary permissions.')
}
- - uses: actions/github-script@v7
+
+ - name: Get PR Data
+ uses: actions/github-script@v7
id: get-pr-data
with:
script: |
@@ -55,25 +59,158 @@ jobs:
repo: context.repo.repo,
pull_number: context.issue.number
})
+ core.setOutput('head_sha', pr.head.sha)
return {
num: context.issue.number,
branchName: pr.head.ref,
- repo: pr.head.repo.full_name,
- commit: pr.head.sha
+ commit: pr.head.sha,
+ repo: pr.head.repo.full_name
}
- - id: generate-token
+
+ - name: Check Package Existence
+ uses: actions/github-script@v7
+ id: check-package
+ with:
+ script: |
+ const prData = ${{ steps.get-pr-data.outputs.result }}
+ const url = `https://pkg.pr.new/vite@${prData.commit}`
+ const response = await fetch(url)
+ console.log(`Package check URL: ${url}, Status: ${response.status}`)
+
+ // Add 'rocket' reaction to the issue comment
+ if (response.status === 404) {
+ const { data: reaction } = await github.rest.reactions.createForIssueComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: context.payload.comment.id,
+ content: 'rocket',
+ })
+ return { exists: false, reaction: reaction.id }
+ }
+
+ return { exists: true, reaction: null }
+
+ - name: Generate Token
+ id: generate-token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }}
installation_retrieval_payload: "${{ github.repository_owner }}/vite-ecosystem-ci"
private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
- - uses: actions/github-script@v7
+
+ - name: Trigger Preview Release (if Package Not Found)
+ if: fromJSON(steps.check-package.outputs.result).exists == false
+ uses: actions/github-script@v7
+ id: trigger-preview-release
+ with:
+ github-token: ${{ steps.generate-token.outputs.token }}
+ script: |
+ const prData = ${{ steps.get-pr-data.outputs.result }}
+ console.log('Package not found, triggering preview release...')
+
+ // Add label "trigger: preview" to the PR
+ await github.rest.issues.addLabels({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prData.num,
+ labels: ['trigger: preview']
+ })
+ console.log('Added "trigger: preview" label.')
+
+ - name: Wait for Preview Release Completion (if Package Not Found)
+ if: fromJSON(steps.check-package.outputs.result).exists == false
+ uses: actions/github-script@v7
+ id: wait-preview-release
+ with:
+ script: |
+ const prData = ${{ steps.get-pr-data.outputs.result }}
+ const reaction = ${{ fromJSON(steps.check-package.outputs.result).reaction }}
+ const workflowFileName = 'preview-release.yml'
+ const workflow = await github.rest.actions.getWorkflow({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ workflow_id: workflowFileName,
+ })
+ const workflowId = workflow.data.id
+ console.log(`Waiting for workflow ID ${workflowId} to complete...`)
+
+ const maxRetries = 60 // Wait up to 10 minutes
+ const delay = 10000 // 10 seconds
+ let completed = false
+
+ for (let i = 0; i < maxRetries; i++) {
+ const runsData = await github.rest.actions.listWorkflowRuns({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ workflow_id: workflowId,
+ head_sha: prData.commit,
+ per_page: 100,
+ page: 1,
+ })
+
+ const runs = runsData.data.workflow_runs
+
+ if (runs.length > 0) {
+ const latestRun = runs[0]
+ console.log(`Latest run status: ${latestRun.status}, conclusion: ${latestRun.conclusion}`)
+ if (latestRun.status === 'completed') {
+ if (latestRun.conclusion === 'success') {
+ console.log('Preview release workflow completed successfully.')
+ completed = true
+ break
+ } else if (latestRun.conclusion === 'skipped') {
+ // noop
+ } else {
+ throw new Error('Preview Release workflow failed.')
+ }
+ }
+ }
+
+ console.log(`Retrying... (${i + 1}/${maxRetries})`)
+ await new Promise(resolve => setTimeout(resolve, delay))
+ }
+
+ if (!completed) {
+ throw new Error('Preview Release workflow did not complete in time.')
+ }
+
+ // Remove the 'rocket' reaction
+ if (reaction) {
+ await github.rest.reactions.deleteForIssueComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: context.payload.comment.id,
+ reaction_id: reaction,
+ })
+ console.log('Removed "rocket" reaction.')
+ }
+
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: refs/pull/${{ fromJSON(steps.get-pr-data.outputs.result).num }}/head
+ fetch-depth: 0
+
+ # This step can be removed on May 26 2025
+ - name: Check Commit Hash Ambiguity
+ id: check_ambiguity
+ run: |
+ HEAD_SHA=${{ steps.get-pr-data.outputs.head_sha }}
+ COMMIT_SHORT=${HEAD_SHA:0:7}
+
+ if git show "$COMMIT_SHORT"; then
+ echo "COLLISION=false" >> $GITHUB_ENV
+ else
+ echo "COLLISION=true" >> $GITHUB_ENV
+ fi
+
+ - name: Trigger Downstream Workflow
+ uses: actions/github-script@v7
id: trigger
env:
COMMENT: ${{ github.event.comment.body }}
with:
github-token: ${{ steps.generate-token.outputs.token }}
- result-encoding: string
script: |
const comment = process.env.COMMENT.trim()
const prData = ${{ steps.get-pr-data.outputs.result }}
@@ -89,7 +226,7 @@ jobs:
prNumber: '' + prData.num,
branchName: prData.branchName,
repo: prData.repo,
- commit: prData.commit,
+ commit: process.env.COLLISION === 'false' ? prData.commit : '',
suite: suite === '' ? '-' : suite
}
})
diff --git a/docs/.vitepress/theme/components/landing/1. hero-section/HeroDiagram.vue b/docs/.vitepress/theme/components/landing/1. hero-section/HeroDiagram.vue
index 10bd32fa4d0fb9..f76e7092220094 100644
--- a/docs/.vitepress/theme/components/landing/1. hero-section/HeroDiagram.vue
+++ b/docs/.vitepress/theme/components/landing/1. hero-section/HeroDiagram.vue
@@ -437,6 +437,12 @@ const isChromiumBrowser = ref(false)
onMounted(() => {
isChromiumBrowser.value = 'chrome' in window
})
+
+// Check for uwu query
+const isUwu = ref(false)
+onMounted(() => {
+ isUwu.value = location.search.includes('?uwu')
+})
@@ -463,7 +469,12 @@ onMounted(() => {
>
-
+
@@ -638,6 +649,10 @@ onMounted(() => {
z-index: 3;
}
+ .uwu.vite-chip__logo {
+ width: 134px;
+ }
+
&.active {
box-shadow: 0 30px 35px -10px rgba(0, 0, 0, 0.6);
transform: translate3d(0, 0, 0) scale(1);
diff --git a/docs/config/index.md b/docs/config/index.md
index b380b4b5988230..0583f69bd52d13 100644
--- a/docs/config/index.md
+++ b/docs/config/index.md
@@ -8,8 +8,7 @@ When running `vite` from the command line, Vite will automatically try to resolv
The most basic config file looks like this:
-```js
-// vite.config.js
+```js [vite.config.js]
export default {
// config options
}
diff --git a/docs/guide/build.md b/docs/guide/build.md
index d0c1da79bdab28..e08ad32b247f72 100644
--- a/docs/guide/build.md
+++ b/docs/guide/build.md
@@ -71,7 +71,7 @@ window.addEventListener('vite:preloadError', (event) => {
When a new deployment occurs, the hosting service may delete the assets from previous deployments. As a result, a user who visited your site before the new deployment might encounter an import error. This error happens because the assets running on that user's device are outdated and it tries to import the corresponding old chunk, which is deleted. This event is useful for addressing this situation.
-## Rebuild on files changes
+## Rebuild on Files Changes
You can enable rollup watcher with `vite build --watch`. Or, you can directly adjust the underlying [`WatcherOptions`](https://rollupjs.org/configuration-options/#watch) via `build.watch`:
diff --git a/docs/guide/philosophy.md b/docs/guide/philosophy.md
index 9e4012825435bc..ccceeb7016241d 100644
--- a/docs/guide/philosophy.md
+++ b/docs/guide/philosophy.md
@@ -18,7 +18,7 @@ When adding new features, these patterns are followed to create a future-proof A
Vite has been focused on performance since its [origins](./why.md). Its dev server architecture allows HMR that stays fast as projects scale. Vite uses native tools like [esbuild](https://esbuild.github.io/) and [SWC](https://github.com/vitejs/vite-plugin-react-swc) to implement intensive tasks but keeps the rest of the code in JS to balance speed with flexibility. When needed, framework plugins will tap into [Babel](https://babeljs.io/) to compile user code. And during build time Vite currently uses [Rollup](https://rollupjs.org/) where bundling size and having access to a wide ecosystem of plugins are more important than raw speed. Vite will continue to evolve internally, using new libraries as they appear to improve DX while keeping its API stable.
-## Building Frameworks on top of Vite
+## Building Frameworks on Top of Vite
Although Vite can be used by users directly, it shines as a tool to create frameworks. Vite core is framework agnostic, but there are polished plugins for each UI framework. Its [JS API](./api-javascript.md) allows App Framework authors to use Vite features to create tailored experiences for their users. Vite includes support for [SSR primitives](./ssr.md), usually present in higher-level tools but fundamental to building modern web frameworks. And Vite plugins complete the picture by offering a way to share between frameworks. Vite is also a great fit when paired with [Backend frameworks](./backend-integration.md) like [Ruby](https://vite-ruby.netlify.app/) and [Laravel](https://laravel.com/docs/10.x/vite).
diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md
index 1804b589ceb3ed..051ca18a7cd894 100644
--- a/docs/guide/troubleshooting.md
+++ b/docs/guide/troubleshooting.md
@@ -172,7 +172,7 @@ You will need to access the file with `http` protocol. The easiest way to achiev
The hash key used to invalidate optimized dependencies depends on the package lock contents, the patches applied to dependencies, and the options in the Vite config file that affects the bundling of node modules. This means that Vite will detect when a dependency is overridden using a feature as [npm overrides](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides), and re-bundle your dependencies on the next server start. Vite won't invalidate the dependencies when you use a feature like [npm link](https://docs.npmjs.com/cli/v9/commands/npm-link). In case you link or unlink a dependency, you'll need to force re-optimization on the next server start by using `vite --force`. We recommend using overrides instead, which are supported now by every package manager (see also [pnpm overrides](https://pnpm.io/package_json#pnpmoverrides) and [yarn resolutions](https://yarnpkg.com/configuration/manifest/#resolutions)).
-## Performance bottlenecks
+## Performance Bottlenecks
If you suffer any application performance bottlenecks resulting in slow load times, you can start the built-in Node.js inspector with your Vite dev server or when building your application to create the CPU profile:
diff --git a/packages/vite/rollup.dts.config.ts b/packages/vite/rollup.dts.config.ts
index be280e237c7186..39fe554311c81f 100644
--- a/packages/vite/rollup.dts.config.ts
+++ b/packages/vite/rollup.dts.config.ts
@@ -113,13 +113,15 @@ function patchTypes(): Plugin {
* Runner chunk should only import local dependencies to stay lightweight
*/
function validateRunnerChunk(this: PluginContext, chunk: RenderedChunk) {
- for (const id of chunk.imports) {
+ for (const [id, bindings] of Object.entries(chunk.importedBindings)) {
if (
!id.startsWith('./') &&
!id.startsWith('../') &&
!id.startsWith('types.d')
) {
- this.warn(`${chunk.fileName} imports "${id}" which is not allowed`)
+ this.warn(
+ `${chunk.fileName} imports "${bindings.join(', ')}" from "${id}" which is not allowed`,
+ )
process.exitCode = 1
}
}
@@ -130,7 +132,7 @@ function validateRunnerChunk(this: PluginContext, chunk: RenderedChunk) {
*/
function validateChunkImports(this: PluginContext, chunk: RenderedChunk) {
const deps = Object.keys(pkg.dependencies)
- for (const id of chunk.imports) {
+ for (const [id, bindings] of Object.entries(chunk.importedBindings)) {
if (
!id.startsWith('./') &&
!id.startsWith('../') &&
@@ -142,7 +144,9 @@ function validateChunkImports(this: PluginContext, chunk: RenderedChunk) {
) {
// If validation failed, only warn and set exit code 1 so that files
// are written to disk for inspection, but the build will fail
- this.warn(`${chunk.fileName} imports "${id}" which is not allowed`)
+ this.warn(
+ `${chunk.fileName} imports "${bindings.join(', ')}" from "${id}" which is not allowed`,
+ )
process.exitCode = 1
}
}
diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts
index 69f47f3164c148..ae52db468474f1 100644
--- a/packages/vite/src/node/index.ts
+++ b/packages/vite/src/node/index.ts
@@ -145,7 +145,7 @@ export type {
WebSocketClient,
WebSocketCustomListener,
} from './server/ws'
-export type { PluginContainer } from './server/pluginContainer'
+export type { SkipInformation, PluginContainer } from './server/pluginContainer'
export type {
EnvironmentModuleGraph,
EnvironmentModuleNode,
diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts
index 36503d4ee37f5e..1e528190f85b7d 100644
--- a/packages/vite/src/node/optimizer/index.ts
+++ b/packages/vite/src/node/optimizer/index.ts
@@ -775,9 +775,11 @@ async function prepareEsbuildOptimizerRun(
if (optimizerContext.cancelled) return { context: undefined, idToExports }
const define = {
- 'process.env.NODE_ENV': JSON.stringify(
- process.env.NODE_ENV || environment.config.mode,
- ),
+ 'process.env.NODE_ENV': environment.config.keepProcessEnv
+ ? // define process.env.NODE_ENV even for keepProcessEnv === true
+ // as esbuild will replace it automatically when `platform` is `'browser'`
+ 'process.env.NODE_ENV'
+ : JSON.stringify(process.env.NODE_ENV || environment.config.mode),
}
const platform =
@@ -1210,7 +1212,9 @@ function getConfigHash(environment: Environment): string {
const { optimizeDeps } = config
const content = JSON.stringify(
{
- mode: process.env.NODE_ENV || config.mode,
+ define: !config.keepProcessEnv
+ ? process.env.NODE_ENV || config.mode
+ : null,
root: config.root,
resolve: config.resolve,
assetsInclude: config.assetsInclude,
diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts
index 04c0f87982fa2e..3453cadf1d2332 100644
--- a/packages/vite/src/node/plugins/css.ts
+++ b/packages/vite/src/node/plugins/css.ts
@@ -82,7 +82,7 @@ import {
urlRE,
} from '../utils'
import type { Logger } from '../logger'
-import { cleanUrl, slash } from '../../shared/utils'
+import { cleanUrl, isWindows, slash } from '../../shared/utils'
import { createBackCompatIdResolver } from '../idResolver'
import type { ResolveIdFn } from '../idResolver'
import { PartialEnvironment } from '../baseEnvironment'
@@ -1162,8 +1162,14 @@ function createCSSResolvers(config: ResolvedConfig): CSSAtImportResolvers {
preferRelative: true,
})
sassResolve = async (...args) => {
+ // the modern API calls `canonicalize` with resolved file URLs
+ // for relative URLs before raw specifiers
if (args[1].startsWith('file://')) {
- args[1] = fileURLToPath(args[1])
+ args[1] = fileURLToPath(args[1], {
+ windows:
+ // file:///foo cannot be converted to path with windows mode
+ isWindows && args[1].startsWith('file:///') ? false : undefined,
+ })
}
return resolver(...args)
}
diff --git a/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts b/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts
index e78cf7a11b981b..37890f95ebef62 100644
--- a/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts
+++ b/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts
@@ -323,6 +323,53 @@ describe('plugin container', () => {
`)
})
})
+
+ describe('resolveId', () => {
+ describe('skipSelf', () => {
+ it('should skip the plugin itself when skipSelf is true', async () => {
+ let calledCount = 0
+ const plugin: Plugin = {
+ name: 'p1',
+ async resolveId(id, importer) {
+ calledCount++
+ if (calledCount <= 1) {
+ return await this.resolve(id, importer, { skipSelf: true })
+ }
+ return id
+ },
+ }
+
+ const environment = await getDevEnvironment({ plugins: [plugin] })
+ await environment.pluginContainer.resolveId('/x.js')
+ expect(calledCount).toBe(1)
+ })
+
+ it('should skip the plugin only when id and importer is same', async () => {
+ const p1: Plugin = {
+ name: 'p1',
+ async resolveId(id, importer) {
+ if (id === 'foo/modified') {
+ return 'success'
+ }
+ return await this.resolve(id, importer, { skipSelf: true })
+ },
+ }
+ const p2: Plugin = {
+ name: 'p2',
+ async resolveId(id, importer) {
+ const resolved = await this.resolve(id + '/modified', importer, {
+ skipSelf: true,
+ })
+ return resolved ?? 'failed'
+ },
+ }
+
+ const environment = await getDevEnvironment({ plugins: [p1, p2] })
+ const result = await environment.pluginContainer.resolveId('foo')
+ expect(result).toStrictEqual({ id: 'success' })
+ })
+ })
+ })
})
async function getDevEnvironment(
diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts
index b4993bd31c6b7e..8683f9a7b37c24 100644
--- a/packages/vite/src/node/server/hmr.ts
+++ b/packages/vite/src/node/server/hmr.ts
@@ -193,7 +193,7 @@ export const normalizeHotChannel = (
) => {
if (!invokeHandlers) {
return {
- e: {
+ error: {
name: 'TransportError',
message: 'invokeHandlers is not set',
stack: new Error().stack,
@@ -207,10 +207,10 @@ export const normalizeHotChannel = (
const invokeHandler = invokeHandlers[name]
// @ts-expect-error `invokeHandler` is `InvokeMethods[T]`, so passing the args is fine
const result = await invokeHandler(...args)
- return { r: result }
+ return { result }
} catch (error) {
return {
- e: {
+ error: {
name: error.name,
message: error.message,
stack: error.stack,
@@ -301,13 +301,7 @@ export const normalizeHotChannel = (
}
channel.on?.('vite:invoke', listenerForInvokeHandler)
},
- handleInvoke: async (payload) => {
- const data = await handleInvoke(payload)
- if (data.e) {
- return { error: data.e }
- }
- return { result: data.r }
- },
+ handleInvoke,
send: (...args: any[]) => {
let payload: HotPayload
if (typeof args[0] === 'string') {
diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts
index 5828a90be3043d..32a3371cef527b 100644
--- a/packages/vite/src/node/server/index.ts
+++ b/packages/vite/src/node/server/index.ts
@@ -690,6 +690,7 @@ export async function _createServer(
server._ssrCompatModuleRunner?.close(),
])
server.resolvedUrls = null
+ server._ssrCompatModuleRunner = undefined
},
printUrls() {
if (server.resolvedUrls) {
diff --git a/packages/vite/src/node/server/mixedModuleGraph.ts b/packages/vite/src/node/server/mixedModuleGraph.ts
index 51fdc3ad1d3809..b86c6eb752e783 100644
--- a/packages/vite/src/node/server/mixedModuleGraph.ts
+++ b/packages/vite/src/node/server/mixedModuleGraph.ts
@@ -13,6 +13,9 @@ import type {
* We are going to deprecate these types and we can try to use them back in the future.
*/
+// same default value of "moduleInfo.meta" as in Rollup
+const EMPTY_OBJECT = Object.freeze({})
+
export class ModuleNode {
_moduleGraph: ModuleGraph
_clientModule: EnvironmentModuleNode | undefined
@@ -76,6 +79,48 @@ export class ModuleNode {
}
return importedModules
}
+ _getModuleInfoUnion(prop: 'info'): ModuleInfo | undefined {
+ const _clientValue = this._clientModule?.[prop]
+ const _ssrValue = this._ssrModule?.[prop]
+
+ if (_clientValue == null && _ssrValue == null) return undefined
+
+ return new Proxy({} as any, {
+ get: (_, key: string) => {
+ // `meta` refers to `ModuleInfo.meta` so we refer to `this.meta` to
+ // handle the object union between client and ssr
+ if (key === 'meta') {
+ return this.meta || EMPTY_OBJECT
+ }
+ if (_clientValue) {
+ if (key in _clientValue) {
+ return _clientValue[key as keyof ModuleInfo]
+ }
+ }
+ if (_ssrValue) {
+ if (key in _ssrValue) {
+ return _ssrValue[key as keyof ModuleInfo]
+ }
+ }
+ },
+ })
+ }
+ _getModuleObjectUnion(prop: 'meta'): RecordCSS
@import "file:///xxx/absolute-path.scss" should be orange
@import "./dir" should be orange
++ @import "/nested/root-relative.scss" should be orange +
Less: This should be blue
diff --git a/playground/css/nested/_index.scss b/playground/css/nested/_index.scss index 72e6b14268334e..ff81e7d82351b9 100644 --- a/playground/css/nested/_index.scss +++ b/playground/css/nested/_index.scss @@ -1,4 +1,5 @@ @use 'sass:string'; +@use '/nested/root-relative'; // root relative path @import './css-in-scss.css'; diff --git a/playground/css/nested/root-relative.scss b/playground/css/nested/root-relative.scss new file mode 100644 index 00000000000000..775dca855743b3 --- /dev/null +++ b/playground/css/nested/root-relative.scss @@ -0,0 +1,3 @@ +.sass-root-relative { + color: orange; +} diff --git a/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts b/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts index 2f4da3b570395d..247f980cab9944 100644 --- a/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts +++ b/playground/environment-react-ssr/__tests__/environment-react-ssr.spec.ts @@ -38,6 +38,19 @@ describe.runIf(!isBuild)('pre-bundling', () => { expect(metaJson.optimized['react/jsx-dev-runtime']).toBeTruthy() expect(metaJson.optimized['react-dom/client']).toBeFalsy() + + // process.env.NODE_ENV should be kept as keepProcessEnv is true + const depsFiles = fs + .readdirSync(path.resolve(testDir, 'node_modules/.vite/deps_ssr'), { + withFileTypes: true, + }) + .filter((file) => file.isFile() && file.name.endsWith('.js')) + .map((file) => path.join(file.parentPath, file.name)) + const depsFilesWithProcessEnvNodeEnv = depsFiles.filter((file) => + fs.readFileSync(file, 'utf-8').includes('process.env.NODE_ENV'), + ) + + expect(depsFilesWithProcessEnvNodeEnv.length).toBeGreaterThan(0) }) test('deps reload', async () => {