Good')
- })
- const content = await page.waitForSelector('text=Good Html')
- expect(content).toBeTruthy()
- })
- })
-}
diff --git a/packages/playground/html/index.html b/packages/playground/html/index.html
deleted file mode 100644
index 7320ff2b097db0..00000000000000
--- a/packages/playground/html/index.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/packages/playground/html/invalid.html b/packages/playground/html/invalid.html
deleted file mode 100644
index 5b5cf429687466..00000000000000
--- a/packages/playground/html/invalid.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/packages/playground/html/nested/index.html b/packages/playground/html/nested/index.html
deleted file mode 100644
index 4fb855b783c890..00000000000000
--- a/packages/playground/html/nested/index.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
Nested
-
diff --git a/packages/playground/html/package.json b/packages/playground/html/package.json
deleted file mode 100644
index a101033f8d3470..00000000000000
--- a/packages/playground/html/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "test-html",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- }
-}
diff --git "a/packages/playground/html/unicode-path/\344\270\255\346\226\207-\343\201\253\343\201\273\343\202\223\343\201\224-\355\225\234\352\270\200-\360\237\214\225\360\237\214\226\360\237\214\227/index.html" "b/packages/playground/html/unicode-path/\344\270\255\346\226\207-\343\201\253\343\201\273\343\202\223\343\201\224-\355\225\234\352\270\200-\360\237\214\225\360\237\214\226\360\237\214\227/index.html"
deleted file mode 100644
index f3c55befe1f315..00000000000000
--- "a/packages/playground/html/unicode-path/\344\270\255\346\226\207-\343\201\253\343\201\273\343\202\223\343\201\224-\355\225\234\352\270\200-\360\237\214\225\360\237\214\226\360\237\214\227/index.html"
+++ /dev/null
@@ -1 +0,0 @@
-
unicode-path
diff --git a/packages/playground/html/vite.config.js b/packages/playground/html/vite.config.js
deleted file mode 100644
index 1703e02cc05366..00000000000000
--- a/packages/playground/html/vite.config.js
+++ /dev/null
@@ -1,162 +0,0 @@
-const { resolve } = require('path')
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- build: {
- rollupOptions: {
- input: {
- main: resolve(__dirname, 'index.html'),
- nested: resolve(__dirname, 'nested/index.html'),
- scriptAsync: resolve(__dirname, 'scriptAsync.html'),
- scriptMixed: resolve(__dirname, 'scriptMixed.html'),
- emptyAttr: resolve(__dirname, 'emptyAttr.html'),
- link: resolve(__dirname, 'link.html'),
- 'link/target': resolve(__dirname, 'index.html'),
- zeroJS: resolve(__dirname, 'zeroJS.html'),
- noHead: resolve(__dirname, 'noHead.html'),
- noBody: resolve(__dirname, 'noBody.html'),
- inline1: resolve(__dirname, 'inline/shared-1.html'),
- inline2: resolve(__dirname, 'inline/shared-2.html'),
- inline3: resolve(__dirname, 'inline/unique.html'),
- unicodePath: resolve(
- __dirname,
- 'unicode-path/中文-にほんご-한글-🌕🌖🌗/index.html'
- )
- }
- }
- },
-
- plugins: [
- {
- name: 'pre-transform',
- transformIndexHtml: {
- enforce: 'pre',
- transform(html, { filename }) {
- if (html.includes('/@vite/client')) {
- throw new Error('pre transform applied at wrong time!')
- }
- const head = `
-
-
-
-
{{ title }}
- `
- return `
-${filename.includes('noHead') ? '' : head}
-${
- filename.includes('noBody')
- ? html
- : `
- ${html}
-`
-}
-
- `
- }
- }
- },
- {
- name: 'string-transform',
- transformIndexHtml(html) {
- return html.replace('Hello', 'Transformed')
- }
- },
- {
- name: 'tags-transform',
- transformIndexHtml() {
- return [
- {
- tag: 'meta',
- attrs: { name: 'description', content: 'a vite app' }
- // default injection is head-prepend
- },
- {
- tag: 'meta',
- attrs: { name: 'keywords', content: 'es modules' },
- injectTo: 'head'
- }
- ]
- }
- },
- {
- name: 'combined-transform',
- transformIndexHtml(html) {
- return {
- html: html.replace('{{ title }}', 'Test HTML transforms'),
- tags: [
- {
- tag: 'p',
- attrs: { class: 'inject' },
- children: 'This is injected',
- injectTo: 'body'
- }
- ]
- }
- }
- },
- {
- name: 'serve-only-transform',
- transformIndexHtml(_, ctx) {
- if (ctx.server) {
- return [
- {
- tag: 'p',
- attrs: { class: 'server' },
- children: 'This is injected only during dev',
- injectTo: 'body'
- }
- ]
- }
- }
- },
- {
- name: 'build-only-transform',
- transformIndexHtml(_, ctx) {
- if (ctx.bundle) {
- return [
- {
- tag: 'p',
- attrs: { class: 'build' },
- children: 'This is injected only during build',
- injectTo: 'body'
- }
- ]
- }
- }
- },
- {
- name: 'path-conditional-transform',
- transformIndexHtml(_, ctx) {
- if (ctx.path.includes('nested')) {
- return [
- {
- tag: 'p',
- attrs: { class: 'conditional' },
- children: 'This is injected only for /nested/index.html',
- injectTo: 'body'
- }
- ]
- }
- }
- },
- {
- name: 'body-prepend-transform',
- transformIndexHtml() {
- return [
- {
- tag: 'noscript',
- children: '',
- injectTo: 'body'
- },
- {
- tag: 'noscript',
- children: '',
- injectTo: 'body-prepend'
- }
- ]
- }
- }
- ]
-}
diff --git a/packages/playground/json/__tests__/json.spec.ts b/packages/playground/json/__tests__/json.spec.ts
deleted file mode 100644
index 2897ee22332e44..00000000000000
--- a/packages/playground/json/__tests__/json.spec.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { isBuild } from '../../testUtils'
-
-const json = require('../test.json')
-const deepJson = require('vue/package.json')
-const stringified = JSON.stringify(json)
-const deepStringified = JSON.stringify(deepJson)
-
-test('default import', async () => {
- expect(await page.textContent('.full')).toBe(stringified)
-})
-
-test('named import', async () => {
- expect(await page.textContent('.named')).toBe(json.hello)
-})
-
-test('deep import', async () => {
- expect(await page.textContent('.deep-full')).toBe(deepStringified)
-})
-
-test('named deep import', async () => {
- expect(await page.textContent('.deep-named')).toBe(deepJson.name)
-})
-
-test('dynamic import', async () => {
- expect(await page.textContent('.dynamic')).toBe(stringified)
-})
-
-test('dynamic import, named', async () => {
- expect(await page.textContent('.dynamic-named')).toBe(json.hello)
-})
-
-test('fetch', async () => {
- expect(await page.textContent('.fetch')).toBe(stringified)
-})
-
-test('?url', async () => {
- expect(await page.textContent('.url')).toMatch(
- isBuild ? 'data:application/json' : '/test.json'
- )
-})
-
-test('?raw', async () => {
- expect(await page.textContent('.raw')).toBe(
- require('fs').readFileSync(require.resolve('../test.json'), 'utf-8')
- )
-})
diff --git a/packages/playground/json/index.html b/packages/playground/json/index.html
deleted file mode 100644
index cf16636f91cb68..00000000000000
--- a/packages/playground/json/index.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
Normal Import
-
-
-
-
Deep Import
-
-
-
-
Dynamic Import
-
-
-
-
fetch
-
-
-
Importing as URL
-
-
-
Raw Import
-
-
-
JSON Module
-
-
-
diff --git a/packages/playground/json/json-module/package.json b/packages/playground/json/json-module/package.json
deleted file mode 100644
index 17db87793a74e3..00000000000000
--- a/packages/playground/json/json-module/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "json-module",
- "version": "0.0.0"
-}
diff --git a/packages/playground/json/package.json b/packages/playground/json/package.json
deleted file mode 100644
index 203846bab73b48..00000000000000
--- a/packages/playground/json/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "test-json",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "devDependencies": {
- "vue": "^3.2.25",
- "json-module": "file:./json-module"
- }
-}
diff --git a/packages/playground/legacy/__tests__/legacy.spec.ts b/packages/playground/legacy/__tests__/legacy.spec.ts
deleted file mode 100644
index b8025694437502..00000000000000
--- a/packages/playground/legacy/__tests__/legacy.spec.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import {
- listAssets,
- findAssetFile,
- isBuild,
- readManifest,
- untilUpdated,
- getColor
-} from '../../testUtils'
-
-test('should work', async () => {
- expect(await page.textContent('#app')).toMatch('Hello')
-})
-
-test('import.meta.env.LEGACY', async () => {
- expect(await page.textContent('#env')).toMatch(isBuild ? 'true' : 'false')
-})
-
-// https://github.com/vitejs/vite/issues/3400
-test('transpiles down iterators correctly', async () => {
- expect(await page.textContent('#iterators')).toMatch('hello')
-})
-
-test('wraps with iife', async () => {
- expect(await page.textContent('#babel-helpers')).toMatch(
- 'exposed babel helpers: false'
- )
-})
-
-test('generates assets', async () => {
- await untilUpdated(
- () => page.textContent('#assets'),
- isBuild
- ? [
- 'index: 404',
- 'index-legacy: 404',
- 'chunk-async: 404',
- 'chunk-async-legacy: 404',
- 'immutable-chunk: 200',
- 'immutable-chunk-legacy: 200',
- 'polyfills-legacy: 404'
- ].join('\n')
- : [
- 'index: 404',
- 'index-legacy: 404',
- 'chunk-async: 404',
- 'chunk-async-legacy: 404',
- 'immutable-chunk: 404',
- 'immutable-chunk-legacy: 404',
- 'polyfills-legacy: 404'
- ].join('\n'),
- true
- )
-})
-
-test('correctly emits styles', async () => {
- expect(await getColor('#app')).toBe('red')
-})
-
-if (isBuild) {
- test('should generate correct manifest', async () => {
- const manifest = readManifest()
- expect(manifest['../../../vite/legacy-polyfills']).toBeDefined()
- expect(manifest['../../../vite/legacy-polyfills'].src).toBe(
- '../../../vite/legacy-polyfills'
- )
- })
-
- test('should minify legacy chunks with terser', async () => {
- // This is a ghetto heuristic, but terser output seems to reliably start
- // with one of the following, and non-terser output (including unminified or
- // ebuild-minified) does not!
- const terserPatt = /^(?:!function|System.register)/
-
- expect(findAssetFile(/chunk-async-legacy/)).toMatch(terserPatt)
- expect(findAssetFile(/chunk-async\./)).not.toMatch(terserPatt)
- expect(findAssetFile(/immutable-chunk-legacy/)).toMatch(terserPatt)
- expect(findAssetFile(/immutable-chunk\./)).not.toMatch(terserPatt)
- expect(findAssetFile(/index-legacy/)).toMatch(terserPatt)
- expect(findAssetFile(/index\./)).not.toMatch(terserPatt)
- expect(findAssetFile(/polyfills-legacy/)).toMatch(terserPatt)
- })
-
- test('should emit css file', async () => {
- expect(listAssets().some((filename) => filename.endsWith('.css')))
- })
-}
diff --git a/packages/playground/legacy/__tests__/ssr/legacy-ssr.spec.ts b/packages/playground/legacy/__tests__/ssr/legacy-ssr.spec.ts
deleted file mode 100644
index dad9b94d83509e..00000000000000
--- a/packages/playground/legacy/__tests__/ssr/legacy-ssr.spec.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { isBuild } from '../../../testUtils'
-import { port } from './serve'
-
-const url = `http://localhost:${port}`
-
-if (isBuild) {
- test('should work', async () => {
- await page.goto(url)
- expect(await page.textContent('#app')).toMatch('Hello')
- })
-
- test('import.meta.env.LEGACY', async () => {
- // SSR build is always modern
- expect(await page.textContent('#env')).toMatch('false')
- })
-} else {
- // this test doesn't support serve mode
- // must contain at least one test
- test('should work', () => void 0)
-}
diff --git a/packages/playground/legacy/__tests__/ssr/serve.js b/packages/playground/legacy/__tests__/ssr/serve.js
deleted file mode 100644
index df43f180afb188..00000000000000
--- a/packages/playground/legacy/__tests__/ssr/serve.js
+++ /dev/null
@@ -1,52 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-const path = require('path')
-
-const port = (exports.port = 9527)
-
-/**
- * @param {string} root
- * @param {boolean} _isProd
- */
-exports.serve = async function serve(root, _isProd) {
- const { build } = require('vite')
- await build({
- root,
- logLevel: 'silent',
- build: {
- target: 'esnext',
- ssr: 'entry-server.js',
- outDir: 'dist/server'
- }
- })
-
- const express = require('express')
- const app = express()
-
- app.use('/', async (_req, res) => {
- const { render } = require(path.resolve(
- root,
- './dist/server/entry-server.js'
- ))
- const html = await render()
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- })
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/legacy/immutable-chunk.js b/packages/playground/legacy/immutable-chunk.js
deleted file mode 100644
index 4227b718b98309..00000000000000
--- a/packages/playground/legacy/immutable-chunk.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const chunks = [
- 'index',
- 'index-legacy',
- 'chunk-async',
- 'chunk-async-legacy',
- 'immutable-chunk',
- 'immutable-chunk-legacy',
- 'polyfills-legacy'
-]
-
-export function fn() {
- return Promise.all(
- chunks.map(async (name) => {
- const response = await fetch(`/assets/${name}.js`)
- return `${name}: ${response.status}`
- })
- )
-}
diff --git a/packages/playground/legacy/index.html b/packages/playground/legacy/index.html
deleted file mode 100644
index bdc2feac6b4fbe..00000000000000
--- a/packages/playground/legacy/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/packages/playground/legacy/main.js b/packages/playground/legacy/main.js
deleted file mode 100644
index b05acf439bdff8..00000000000000
--- a/packages/playground/legacy/main.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import './style.css'
-
-async function run() {
- const { fn } = await import('./async.js')
- fn()
-}
-
-run()
-
-let isLegacy
-
-// make sure that branching works despite esbuild's constant folding (#1999)
-if (import.meta.env.LEGACY) {
- if (import.meta.env.LEGACY === true) isLegacy = true
-} else {
- if (import.meta.env.LEGACY === false) isLegacy = false
-}
-
-text('#env', `is legacy: ${isLegacy}`)
-
-// Iterators
-text('#iterators', [...new Set(['hello'])].join(''))
-
-// babel-helpers
-// Using `String.raw` to inject `@babel/plugin-transform-template-literals`
-// helpers.
-text(
- '#babel-helpers',
- String.raw`exposed babel helpers: ${window._templateObject != null}`
-)
-
-// dynamic chunk names
-import('./immutable-chunk.js')
- .then(({ fn }) => fn())
- .then((assets) => {
- text('#assets', assets.join('\n'))
- })
-
-function text(el, text) {
- document.querySelector(el).textContent = text
-}
diff --git a/packages/playground/legacy/package.json b/packages/playground/legacy/package.json
deleted file mode 100644
index 3a3315c42aa832..00000000000000
--- a/packages/playground/legacy/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "test-legacy",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build --debug legacy",
- "build:custom-filename": "vite --config ./vite.config-custom-filename.js build --debug legacy",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "devDependencies": {
- "@vitejs/plugin-legacy": "workspace:*",
- "express": "^4.17.1"
- }
-}
diff --git a/packages/playground/legacy/vite.config-custom-filename.js b/packages/playground/legacy/vite.config-custom-filename.js
deleted file mode 100644
index 9a96133b015588..00000000000000
--- a/packages/playground/legacy/vite.config-custom-filename.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const legacy = require('@vitejs/plugin-legacy').default
-
-module.exports = {
- plugins: [legacy()],
- build: {
- manifest: true,
- minify: false,
- rollupOptions: {
- output: {
- entryFileNames: `assets/[name].js`,
- chunkFileNames: `assets/[name].js`
- }
- }
- }
-}
diff --git a/packages/playground/legacy/vite.config.js b/packages/playground/legacy/vite.config.js
deleted file mode 100644
index 90d3be7f7c56a0..00000000000000
--- a/packages/playground/legacy/vite.config.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-const legacy = require('@vitejs/plugin-legacy').default
-
-module.exports = {
- plugins: [
- legacy({
- targets: 'IE 11'
- })
- ],
-
- build: {
- cssCodeSplit: false,
- manifest: true,
- rollupOptions: {
- output: {
- chunkFileNames(chunkInfo) {
- if (chunkInfo.name === 'immutable-chunk') {
- return `assets/${chunkInfo.name}.js`
- }
-
- return `assets/chunk-[name].[hash].js`
- }
- }
- }
- },
-
- // special test only hook
- // for tests, remove `
-
-
-
-
-
-
-
-
diff --git a/packages/playground/lib/package.json b/packages/playground/lib/package.json
deleted file mode 100644
index 2c3ae4be3d4bcb..00000000000000
--- a/packages/playground/lib/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "@example/my-lib",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- }
-}
diff --git a/packages/playground/lib/src/main.js b/packages/playground/lib/src/main.js
deleted file mode 100644
index 2422edf5829a0e..00000000000000
--- a/packages/playground/lib/src/main.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function myLib(sel) {
- document.querySelector(sel).textContent = 'It works'
-}
diff --git a/packages/playground/lib/src/main2.js b/packages/playground/lib/src/main2.js
deleted file mode 100644
index 0c729fad8d165c..00000000000000
--- a/packages/playground/lib/src/main2.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export default async function message(sel) {
- const message = await import('./message.js')
- document.querySelector(sel).textContent = message.default
-}
diff --git a/packages/playground/lib/vite.config.js b/packages/playground/lib/vite.config.js
deleted file mode 100644
index 50cd188b1a40cc..00000000000000
--- a/packages/playground/lib/vite.config.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- build: {
- lib: {
- entry: path.resolve(__dirname, 'src/main.js'),
- name: 'MyLib',
- formats: ['es', 'umd', 'iife'],
- fileName: (format) => `my-lib-custom-filename.${format}.js`
- }
- },
- plugins: [
- {
- name: 'emit-index',
- generateBundle() {
- this.emitFile({
- type: 'asset',
- fileName: 'index.html',
- source: fs.readFileSync(
- path.resolve(__dirname, 'index.dist.html'),
- 'utf-8'
- )
- })
- }
- }
- ]
-}
diff --git a/packages/playground/lib/vite.dyimport.config.js b/packages/playground/lib/vite.dyimport.config.js
deleted file mode 100644
index 76311f4b8ba138..00000000000000
--- a/packages/playground/lib/vite.dyimport.config.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- build: {
- minify: false,
- lib: {
- entry: path.resolve(__dirname, 'src/main2.js'),
- formats: ['es'],
- name: 'message',
- fileName: () => 'dynamic-import-message.js'
- },
- outDir: 'dist/lib'
- }
-}
diff --git a/packages/playground/multiple-entrypoints/__tests__/multiple-entrypoints.spec.ts b/packages/playground/multiple-entrypoints/__tests__/multiple-entrypoints.spec.ts
deleted file mode 100644
index 56c0b46c8a3e6f..00000000000000
--- a/packages/playground/multiple-entrypoints/__tests__/multiple-entrypoints.spec.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { getColor, untilUpdated } from '../../testUtils'
-
-test('should have css applied on second dynamic import', async () => {
- await untilUpdated(() => page.textContent('.content'), 'Initial', true)
- await page.click('.b')
-
- await untilUpdated(() => page.textContent('.content'), 'Reference', true)
- expect(await getColor('.content')).toBe('red')
-})
diff --git a/packages/playground/multiple-entrypoints/package.json b/packages/playground/multiple-entrypoints/package.json
deleted file mode 100644
index 6c338a64518ddb..00000000000000
--- a/packages/playground/multiple-entrypoints/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "multiple-entrypoints",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "devDependencies": {
- "fast-glob": "^3.2.11",
- "sass": "^1.43.4"
- }
-}
diff --git a/packages/playground/multiple-entrypoints/vite.config.js b/packages/playground/multiple-entrypoints/vite.config.js
deleted file mode 100644
index c2a44858a3ce6b..00000000000000
--- a/packages/playground/multiple-entrypoints/vite.config.js
+++ /dev/null
@@ -1,40 +0,0 @@
-const { resolve } = require('path')
-const fs = require('fs')
-
-module.exports = {
- build: {
- outDir: './dist',
- emptyOutDir: true,
- rollupOptions: {
- preserveEntrySignatures: 'strict',
- input: {
- a0: resolve(__dirname, 'entrypoints/a0.js'),
- a1: resolve(__dirname, 'entrypoints/a1.js'),
- a2: resolve(__dirname, 'entrypoints/a2.js'),
- a3: resolve(__dirname, 'entrypoints/a3.js'),
- a4: resolve(__dirname, 'entrypoints/a4.js'),
- a5: resolve(__dirname, 'entrypoints/a5.js'),
- a6: resolve(__dirname, 'entrypoints/a6.js'),
- a7: resolve(__dirname, 'entrypoints/a7.js'),
- a8: resolve(__dirname, 'entrypoints/a8.js'),
- a9: resolve(__dirname, 'entrypoints/a9.js'),
- a10: resolve(__dirname, 'entrypoints/a10.js'),
- a11: resolve(__dirname, 'entrypoints/a11.js'),
- a12: resolve(__dirname, 'entrypoints/a12.js'),
- a13: resolve(__dirname, 'entrypoints/a13.js'),
- a14: resolve(__dirname, 'entrypoints/a14.js'),
- a15: resolve(__dirname, 'entrypoints/a15.js'),
- a16: resolve(__dirname, 'entrypoints/a16.js'),
- a17: resolve(__dirname, 'entrypoints/a17.js'),
- a18: resolve(__dirname, 'entrypoints/a18.js'),
- a19: resolve(__dirname, 'entrypoints/a19.js'),
- a20: resolve(__dirname, 'entrypoints/a20.js'),
- a21: resolve(__dirname, 'entrypoints/a21.js'),
- a22: resolve(__dirname, 'entrypoints/a22.js'),
- a23: resolve(__dirname, 'entrypoints/a23.js'),
- a24: resolve(__dirname, 'entrypoints/a24.js'),
- index: resolve(__dirname, './index.html')
- }
- }
- }
-}
diff --git a/packages/playground/nested-deps/__tests__/nested-deps.spec.ts b/packages/playground/nested-deps/__tests__/nested-deps.spec.ts
deleted file mode 100644
index 2ef0e191da7b50..00000000000000
--- a/packages/playground/nested-deps/__tests__/nested-deps.spec.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-test('handle nested package', async () => {
- expect(await page.textContent('.a')).toBe('A@2.0.0')
- expect(await page.textContent('.b')).toBe('B@1.0.0')
- expect(await page.textContent('.nested-a')).toBe('A@1.0.0')
- const c = await page.textContent('.c')
- expect(c).toBe('es-C@1.0.0')
- expect(await page.textContent('.side-c')).toBe(c)
- expect(await page.textContent('.d')).toBe('D@1.0.0')
- expect(await page.textContent('.nested-d')).toBe('D-nested@1.0.0')
- expect(await page.textContent('.nested-e')).toBe('1')
-})
diff --git a/packages/playground/nested-deps/index.html b/packages/playground/nested-deps/index.html
deleted file mode 100644
index 3243c1689bf0cd..00000000000000
--- a/packages/playground/nested-deps/index.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
direct dependency A
-
-
-
direct dependency B
-
-
-
nested dependency A
-
-
-
direct dependency C
-
-
-
side dependency C
-
-
-
direct dependency D
-
-
-
nested dependency nested-D (dep of D)
-
-
-
exclude dependency of pre-bundled dependency
-
nested module instance count:
-
-
diff --git a/packages/playground/nested-deps/package.json b/packages/playground/nested-deps/package.json
deleted file mode 100644
index d7450d0545fcb4..00000000000000
--- a/packages/playground/nested-deps/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "@test/nested-deps",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "test-package-a": "link:./test-package-a",
- "test-package-b": "link:./test-package-b",
- "test-package-c": "link:./test-package-c",
- "test-package-d": "link:./test-package-d",
- "test-package-e": "link:./test-package-e"
- }
-}
diff --git a/packages/playground/nested-deps/test-package-a/package.json b/packages/playground/nested-deps/test-package-a/package.json
deleted file mode 100644
index 688fab78bab766..00000000000000
--- a/packages/playground/nested-deps/test-package-a/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "test-package-a",
- "private": true,
- "version": "2.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/nested-deps/test-package-b/package.json b/packages/playground/nested-deps/test-package-b/package.json
deleted file mode 100644
index 6e32e8eba153a1..00000000000000
--- a/packages/playground/nested-deps/test-package-b/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "test-package-b",
- "private": true,
- "version": "1.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/nested-deps/test-package-c/package.json b/packages/playground/nested-deps/test-package-c/package.json
deleted file mode 100644
index 47672d07b3881f..00000000000000
--- a/packages/playground/nested-deps/test-package-c/package.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "test-package-c",
- "private": true,
- "version": "1.0.0",
- "main": "index.js",
- "module": "index-es.js"
-}
diff --git a/packages/playground/nested-deps/test-package-c/side.js b/packages/playground/nested-deps/test-package-c/side.js
deleted file mode 100644
index 4d46da7c26e02e..00000000000000
--- a/packages/playground/nested-deps/test-package-c/side.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as C } from 'test-package-c'
diff --git a/packages/playground/nested-deps/test-package-d/index.js b/packages/playground/nested-deps/test-package-d/index.js
deleted file mode 100644
index f5b35d06fd5001..00000000000000
--- a/packages/playground/nested-deps/test-package-d/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export { default as nestedD } from 'test-package-d-nested'
-
-export default 'D@1.0.0'
diff --git a/packages/playground/nested-deps/test-package-d/package.json b/packages/playground/nested-deps/test-package-d/package.json
deleted file mode 100644
index e9f522c2c5ddf6..00000000000000
--- a/packages/playground/nested-deps/test-package-d/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "test-package-d",
- "private": true,
- "version": "1.0.0",
- "main": "index.js",
- "dependencies": {
- "test-package-d-nested": "link:./test-package-d-nested"
- }
-}
diff --git a/packages/playground/nested-deps/test-package-d/test-package-d-nested/package.json b/packages/playground/nested-deps/test-package-d/test-package-d-nested/package.json
deleted file mode 100644
index 50f1123b6f7ff7..00000000000000
--- a/packages/playground/nested-deps/test-package-d/test-package-d-nested/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "test-package-d-nested",
- "private": true,
- "version": "1.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/nested-deps/test-package-e/index.js b/packages/playground/nested-deps/test-package-e/index.js
deleted file mode 100644
index 3d9c38b0ab3886..00000000000000
--- a/packages/playground/nested-deps/test-package-e/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export { testIncluded } from 'test-package-e-included'
-export { testExcluded } from 'test-package-e-excluded'
diff --git a/packages/playground/nested-deps/test-package-e/package.json b/packages/playground/nested-deps/test-package-e/package.json
deleted file mode 100644
index 45779d1a7676ad..00000000000000
--- a/packages/playground/nested-deps/test-package-e/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "test-package-e",
- "private": true,
- "version": "0.1.0",
- "main": "index.js",
- "dependencies": {
- "test-package-e-excluded": "link:./test-package-e-excluded",
- "test-package-e-included": "link:./test-package-e-included"
- }
-}
diff --git a/packages/playground/nested-deps/test-package-e/test-package-e-excluded/package.json b/packages/playground/nested-deps/test-package-e/test-package-e-excluded/package.json
deleted file mode 100644
index 8722324da53499..00000000000000
--- a/packages/playground/nested-deps/test-package-e/test-package-e-excluded/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "test-package-e-excluded",
- "private": true,
- "version": "0.1.0",
- "main": "index.js"
-}
diff --git a/packages/playground/nested-deps/test-package-e/test-package-e-included/index.js b/packages/playground/nested-deps/test-package-e/test-package-e-included/index.js
deleted file mode 100644
index 23239f157bfa38..00000000000000
--- a/packages/playground/nested-deps/test-package-e/test-package-e-included/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { testExcluded } from 'test-package-e-excluded'
-
-export function testIncluded() {
- return testExcluded()
-}
diff --git a/packages/playground/nested-deps/test-package-e/test-package-e-included/package.json b/packages/playground/nested-deps/test-package-e/test-package-e-included/package.json
deleted file mode 100644
index 37198ee7d6a7c7..00000000000000
--- a/packages/playground/nested-deps/test-package-e/test-package-e-included/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "test-package-e-included",
- "private": true,
- "version": "0.1.0",
- "main": "index.js",
- "dependencies": {
- "test-package-e-excluded": "link:../test-package-e-excluded"
- }
-}
diff --git a/packages/playground/nested-deps/vite.config.js b/packages/playground/nested-deps/vite.config.js
deleted file mode 100644
index 015598af64b016..00000000000000
--- a/packages/playground/nested-deps/vite.config.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- optimizeDeps: {
- include: [
- 'test-package-a',
- 'test-package-b',
- 'test-package-c',
- 'test-package-c/side',
- 'test-package-d > test-package-d-nested',
- 'test-package-e-included'
- ],
- exclude: ['test-package-d', 'test-package-e-excluded']
- }
-}
diff --git a/packages/playground/optimize-deps/__tests__/optimize-deps.spec.ts b/packages/playground/optimize-deps/__tests__/optimize-deps.spec.ts
deleted file mode 100644
index d95a6d984cd9aa..00000000000000
--- a/packages/playground/optimize-deps/__tests__/optimize-deps.spec.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { getColor, isBuild } from '../../testUtils'
-
-test('default + named imports from cjs dep (react)', async () => {
- expect(await page.textContent('.cjs button')).toBe('count is 0')
- await page.click('.cjs button')
- expect(await page.textContent('.cjs button')).toBe('count is 1')
-})
-
-test('named imports from webpacked cjs (phoenix)', async () => {
- expect(await page.textContent('.cjs-phoenix')).toBe('ok')
-})
-
-test('default import from webpacked cjs (clipboard)', async () => {
- expect(await page.textContent('.cjs-clipboard')).toBe('ok')
-})
-
-test('dynamic imports from cjs dep (react)', async () => {
- expect(await page.textContent('.cjs-dynamic button')).toBe('count is 0')
- await page.click('.cjs-dynamic button')
- expect(await page.textContent('.cjs-dynamic button')).toBe('count is 1')
-})
-
-test('dynamic named imports from webpacked cjs (phoenix)', async () => {
- expect(await page.textContent('.cjs-dynamic-phoenix')).toBe('ok')
-})
-
-test('dynamic default import from webpacked cjs (clipboard)', async () => {
- expect(await page.textContent('.cjs-dynamic-clipboard')).toBe('ok')
-})
-
-test('dynamic default import from cjs (cjs-dynamic-dep-cjs-compiled-from-esm)', async () => {
- expect(await page.textContent('.cjs-dynamic-dep-cjs-compiled-from-esm')).toBe(
- 'ok'
- )
-})
-
-test('dynamic default import from cjs (cjs-dynamic-dep-cjs-compiled-from-cjs)', async () => {
- expect(await page.textContent('.cjs-dynamic-dep-cjs-compiled-from-cjs')).toBe(
- 'ok'
- )
-})
-
-test('dedupe', async () => {
- expect(await page.textContent('.dedupe button')).toBe('count is 0')
- await page.click('.dedupe button')
- expect(await page.textContent('.dedupe button')).toBe('count is 1')
-})
-
-test('cjs browser field (axios)', async () => {
- expect(await page.textContent('.cjs-browser-field')).toBe('pong')
-})
-
-test('dep from linked dep (lodash-es)', async () => {
- expect(await page.textContent('.deps-linked')).toBe('fooBarBaz')
-})
-
-test('forced include', async () => {
- expect(await page.textContent('.force-include')).toMatch(`[success]`)
-})
-
-test('import * from optimized dep', async () => {
- expect(await page.textContent('.import-star')).toMatch(`[success]`)
-})
-
-test('import from dep with .notjs files', async () => {
- expect(await page.textContent('.not-js')).toMatch(`[success]`)
-})
-
-test('dep with dynamic import', async () => {
- expect(await page.textContent('.dep-with-dynamic-import')).toMatch(
- `[success]`
- )
-})
-
-test('dep with css import', async () => {
- expect(await getColor('h1')).toBe('red')
-})
-
-test('dep w/ non-js files handled via plugin', async () => {
- expect(await page.textContent('.plugin')).toMatch(`[success]`)
-})
-
-test('vue + vuex', async () => {
- expect(await page.textContent('.vue')).toMatch(`[success]`)
-})
-
-test('esbuild-plugin', async () => {
- expect(await page.textContent('.esbuild-plugin')).toMatch(
- isBuild ? `Hello from a package` : `Hello from an esbuild plugin`
- )
-})
-
-test('import from hidden dir', async () => {
- expect(await page.textContent('.hidden-dir')).toBe('hello!')
-})
-
-test('import optimize-excluded package that imports optimized-included package', async () => {
- expect(await page.textContent('.nested-include')).toBe('nested-include')
-})
-
-test('import aliased package with colon', async () => {
- expect(await page.textContent('.url')).toBe('vitejs.dev')
-})
-
-test('variable names are reused in different scripts', async () => {
- expect(await page.textContent('.reused-variable-names')).toBe('reused')
-})
diff --git a/packages/playground/optimize-deps/cjs-dynamic.js b/packages/playground/optimize-deps/cjs-dynamic.js
deleted file mode 100644
index 91dc5a964d5481..00000000000000
--- a/packages/playground/optimize-deps/cjs-dynamic.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// test dynamic import to cjs deps
-// mostly ensuring consistency between dev server behavior and build behavior
-// of @rollup/plugin-commonjs
-;(async () => {
- const { useState } = await import('react')
- const React = (await import('react')).default
- const ReactDOM = await import('react-dom')
-
- const clip = await import('clipboard')
- if (typeof clip.default === 'function') {
- text('.cjs-dynamic-clipboard', 'ok')
- }
-
- const { Socket } = await import('phoenix')
- if (typeof Socket === 'function') {
- text('.cjs-dynamic-phoenix', 'ok')
- }
-
- const cjsFromESM = await import('dep-cjs-compiled-from-esm')
- console.log('cjsFromESM', cjsFromESM)
- if (typeof cjsFromESM.default === 'function') {
- text('.cjs-dynamic-dep-cjs-compiled-from-esm', 'ok')
- }
-
- const cjsFromCJS = await import('dep-cjs-compiled-from-cjs')
- console.log('cjsFromCJS', cjsFromCJS)
- if (typeof cjsFromCJS.default === 'function') {
- text('.cjs-dynamic-dep-cjs-compiled-from-cjs', 'ok')
- }
-
- function App() {
- const [count, setCount] = useState(0)
-
- return React.createElement(
- 'button',
- {
- onClick() {
- setCount(count + 1)
- }
- },
- `count is ${count}`
- )
- }
-
- ReactDOM.render(
- React.createElement(App),
- document.querySelector('.cjs-dynamic')
- )
-
- function text(el, text) {
- document.querySelector(el).textContent = text
- }
-})()
diff --git a/packages/playground/optimize-deps/cjs.js b/packages/playground/optimize-deps/cjs.js
deleted file mode 100644
index 9dc613c4b3ba71..00000000000000
--- a/packages/playground/optimize-deps/cjs.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// test importing both default and named exports from a CommonJS module
-// React is the ultimate test of this because its dynamic exports assignments
-// are not statically detectable by @rollup/plugin-commonjs.
-import React, { useState } from 'react'
-import ReactDOM from 'react-dom'
-import { Socket } from 'phoenix'
-import clip from 'clipboard'
-
-if (typeof clip === 'function') {
- text('.cjs-clipboard', 'ok')
-}
-
-if (typeof Socket === 'function') {
- text('.cjs-phoenix', 'ok')
-}
-
-function App() {
- const [count, setCount] = useState(0)
-
- return React.createElement(
- 'button',
- {
- onClick() {
- setCount(count + 1)
- }
- },
- `count is ${count}`
- )
-}
-
-ReactDOM.render(React.createElement(App), document.querySelector('.cjs'))
-
-function text(el, text) {
- document.querySelector(el).textContent = text
-}
diff --git a/packages/playground/optimize-deps/dedupe.js b/packages/playground/optimize-deps/dedupe.js
deleted file mode 100644
index d04726330a6138..00000000000000
--- a/packages/playground/optimize-deps/dedupe.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom'
-
-// #1302: The linked package has a different version of React in its deps
-// and is itself optimized. Without `dedupe`, the linked package is optimized
-// with a separate copy of React included, and results in runtime errors.
-import { useCount } from 'dep-linked-include/index.mjs'
-
-function App() {
- const [count, setCount] = useCount()
-
- return React.createElement(
- 'button',
- {
- onClick() {
- setCount(count + 1)
- }
- },
- `count is ${count}`
- )
-}
-
-ReactDOM.render(React.createElement(App), document.querySelector('.dedupe'))
diff --git a/packages/playground/optimize-deps/dep-cjs-compiled-from-cjs/index.js b/packages/playground/optimize-deps/dep-cjs-compiled-from-cjs/index.js
deleted file mode 100644
index 38d4c85c7c33ce..00000000000000
--- a/packages/playground/optimize-deps/dep-cjs-compiled-from-cjs/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-'use strict'
-function foo() {
- return 'foo'
-}
-module.exports = foo
diff --git a/packages/playground/optimize-deps/dep-cjs-compiled-from-cjs/package.json b/packages/playground/optimize-deps/dep-cjs-compiled-from-cjs/package.json
deleted file mode 100644
index 8fbd661730eafd..00000000000000
--- a/packages/playground/optimize-deps/dep-cjs-compiled-from-cjs/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "dep-cjs-compiled-from-cjs",
- "private": true,
- "version": "0.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/optimize-deps/dep-cjs-compiled-from-esm/package.json b/packages/playground/optimize-deps/dep-cjs-compiled-from-esm/package.json
deleted file mode 100644
index 27ba12a1ec6f7b..00000000000000
--- a/packages/playground/optimize-deps/dep-cjs-compiled-from-esm/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "dep-cjs-compiled-from-esm",
- "private": true,
- "version": "0.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/optimize-deps/dep-esbuild-plugin-transform/package.json b/packages/playground/optimize-deps/dep-esbuild-plugin-transform/package.json
deleted file mode 100644
index 4adb0e7032ae20..00000000000000
--- a/packages/playground/optimize-deps/dep-esbuild-plugin-transform/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "dep-esbuild-plugin-transform",
- "private": true,
- "version": "0.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/optimize-deps/dep-linked-include/index.mjs b/packages/playground/optimize-deps/dep-linked-include/index.mjs
deleted file mode 100644
index 81c43abc0387ac..00000000000000
--- a/packages/playground/optimize-deps/dep-linked-include/index.mjs
+++ /dev/null
@@ -1,21 +0,0 @@
-export { msg } from './foo.js'
-
-import { useState } from 'react'
-
-export function useCount() {
- return useState(0)
-}
-
-// test dep with css/asset imports
-import './test.css'
-
-// test importing node built-ins
-import fs from 'fs'
-
-if (false) {
- fs.readFileSync()
-} else {
- console.log('ok')
-}
-
-export { default as VueSFC } from './Test.vue'
diff --git a/packages/playground/optimize-deps/dep-linked-include/package.json b/packages/playground/optimize-deps/dep-linked-include/package.json
deleted file mode 100644
index 0cfabdb9a2f2b9..00000000000000
--- a/packages/playground/optimize-deps/dep-linked-include/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "dep-linked-include",
- "private": true,
- "version": "0.0.0",
- "main": "index.mjs",
- "dependencies": {
- "react": "17.0.2"
- }
-}
diff --git a/packages/playground/optimize-deps/dep-linked-include/test.css b/packages/playground/optimize-deps/dep-linked-include/test.css
deleted file mode 100644
index 60f1eab97137f7..00000000000000
--- a/packages/playground/optimize-deps/dep-linked-include/test.css
+++ /dev/null
@@ -1,3 +0,0 @@
-body {
- color: red;
-}
diff --git a/packages/playground/optimize-deps/dep-linked/package.json b/packages/playground/optimize-deps/dep-linked/package.json
deleted file mode 100644
index 915340f10ae4a0..00000000000000
--- a/packages/playground/optimize-deps/dep-linked/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "dep-linked",
- "private": true,
- "version": "0.0.0",
- "main": "index.js",
- "dependencies": {
- "lodash-es": "^4.17.21"
- }
-}
diff --git a/packages/playground/optimize-deps/dep-not-js/package.json b/packages/playground/optimize-deps/dep-not-js/package.json
deleted file mode 100644
index 39ebafb6217b6e..00000000000000
--- a/packages/playground/optimize-deps/dep-not-js/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "dep-not-js",
- "private": true,
- "version": "1.0.0",
- "main": "index.notjs"
-}
diff --git a/packages/playground/optimize-deps/dep-with-dynamic-import/package.json b/packages/playground/optimize-deps/dep-with-dynamic-import/package.json
deleted file mode 100644
index 81c5d2dda6a62a..00000000000000
--- a/packages/playground/optimize-deps/dep-with-dynamic-import/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "dep-with-dynamic-import",
- "private": true,
- "version": "0.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/optimize-deps/glob/foo.js b/packages/playground/optimize-deps/glob/foo.js
deleted file mode 100644
index 7f2da6bfb1c276..00000000000000
--- a/packages/playground/optimize-deps/glob/foo.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import axios from 'axios'
-
-axios.get('/ping').then((res) => {
- document.querySelector('.cjs-browser-field').textContent = res.data
-})
diff --git a/packages/playground/optimize-deps/index.html b/packages/playground/optimize-deps/index.html
deleted file mode 100644
index 2be896d00acba9..00000000000000
--- a/packages/playground/optimize-deps/index.html
+++ /dev/null
@@ -1,124 +0,0 @@
-
Optimize Deps
-
-
CommonJS w/ named imports (react)
-
-
CommonJS w/ named imports (phoenix)
-
fail
-
CommonJS w/ default export (clipboard)
-
fail
-
-
-
-
CommonJS dynamic import default + named (react)
-
-
CommonJS dynamic import named (phoenix)
-
-
CommonJS dynamic import default (clipboard)
-
-
CommonJS dynamic import default (dep-cjs-compiled-from-esm)
-
-
CommonJS dynamic import default (dep-cjs-compiled-from-cjs)
-
-
-
-
-
Dedupe (dep in linked & optimized package)
-
-
-
-
CommonJS w/ browser field mapping (axios)
-
This should show pong:
-
-
Detecting linked src package and optimizing its deps (lodash-es)
-
This should show fooBarBaz:
-
-
Optimizing force included dep even when it's linked
-
-
-
import * as ...
-
-
-
Import from dependency with .notjs files
-
-
-
Import from dependency with dynamic import
-
-
-
Dep w/ special file format supported via plugins
-
-
-
Vue & Vuex
-
-
-
Dep with changes from esbuild plugin
-
This should show a greeting:
-
-
Dep from hidden dir
-
This should show hello!:
-
-
Nested include
-
Module path:
-
-
Alias with colon
-
URL:
-
-
Reused variable names
-
This should show reused:
-
-
-
-
-
-
-
diff --git a/packages/playground/optimize-deps/nested-exclude/index.js b/packages/playground/optimize-deps/nested-exclude/index.js
deleted file mode 100644
index 3910458ef4d26d..00000000000000
--- a/packages/playground/optimize-deps/nested-exclude/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export { default as nestedInclude } from 'nested-include'
-
-export default 'nested-exclude'
diff --git a/packages/playground/optimize-deps/nested-exclude/nested-include/package.json b/packages/playground/optimize-deps/nested-exclude/nested-include/package.json
deleted file mode 100644
index 581ef4dada69ce..00000000000000
--- a/packages/playground/optimize-deps/nested-exclude/nested-include/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "nested-include",
- "private": true,
- "version": "1.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/optimize-deps/nested-exclude/package.json b/packages/playground/optimize-deps/nested-exclude/package.json
deleted file mode 100644
index 57dfc20ea1f801..00000000000000
--- a/packages/playground/optimize-deps/nested-exclude/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "nested-exclude",
- "private": true,
- "version": "1.0.0",
- "main": "index.js",
- "dependencies": {
- "nested-include": "link:./nested-include"
- }
-}
diff --git a/packages/playground/optimize-deps/package.json b/packages/playground/optimize-deps/package.json
deleted file mode 100644
index 2752e691da6fb2..00000000000000
--- a/packages/playground/optimize-deps/package.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "name": "test-optimize-deps",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview",
- "postinstall": "ts-node ../../../scripts/patchFileDeps.ts"
- },
- "dependencies": {
- "axios": "^0.24.0",
- "clipboard": "^2.0.8",
- "dep-cjs-compiled-from-cjs": "file:./dep-cjs-compiled-from-cjs",
- "dep-cjs-compiled-from-esm": "file:./dep-cjs-compiled-from-esm",
- "dep-esbuild-plugin-transform": "file:./dep-esbuild-plugin-transform",
- "dep-linked": "link:./dep-linked",
- "dep-linked-include": "link:./dep-linked-include",
- "dep-not-js": "file:./dep-not-js",
- "dep-with-dynamic-import": "file:./dep-with-dynamic-import",
- "lodash-es": "^4.17.21",
- "nested-exclude": "file:./nested-exclude",
- "phoenix": "^1.6.2",
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
- "resolve-linked": "workspace:0.0.0",
- "url": "^0.11.0",
- "vue": "^3.2.25",
- "vuex": "^4.0.0"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*"
- }
-}
diff --git a/packages/playground/optimize-deps/vite.config.js b/packages/playground/optimize-deps/vite.config.js
deleted file mode 100644
index a989cf1961de11..00000000000000
--- a/packages/playground/optimize-deps/vite.config.js
+++ /dev/null
@@ -1,91 +0,0 @@
-const fs = require('fs')
-const vue = require('@vitejs/plugin-vue')
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- resolve: {
- dedupe: ['react'],
- alias: {
- 'node:url': 'url'
- }
- },
-
- optimizeDeps: {
- include: ['dep-linked-include', 'nested-exclude > nested-include'],
- exclude: ['nested-exclude'],
- esbuildOptions: {
- plugins: [
- {
- name: 'replace-a-file',
- setup(build) {
- build.onLoad(
- { filter: /dep-esbuild-plugin-transform(\\|\/)index\.js$/ },
- () => ({
- contents: `export const hello = () => 'Hello from an esbuild plugin'`,
- loader: 'js'
- })
- )
- }
- }
- ]
- }
- },
-
- build: {
- // to make tests faster
- minify: false
- },
-
- plugins: [
- vue(),
- notjs(),
- // for axios request test
- {
- name: 'mock',
- configureServer({ middlewares }) {
- middlewares.use('/ping', (_, res) => {
- res.statusCode = 200
- res.end('pong')
- })
- }
- }
- ]
-}
-
-// Handles .notjs file, basically remove wrapping
and tags
-function notjs() {
- return {
- name: 'notjs',
- config() {
- return {
- optimizeDeps: {
- extensions: ['.notjs'],
- esbuildOptions: {
- plugins: [
- {
- name: 'esbuild-notjs',
- setup(build) {
- build.onLoad({ filter: /\.notjs$/ }, ({ path }) => {
- let contents = fs.readFileSync(path, 'utf-8')
- contents = contents
- .replace('
', '')
- .replace(' ', '')
- return { contents, loader: 'js' }
- })
- }
- }
- ]
- }
- }
- }
- },
- transform(code, id) {
- if (id.endsWith('.notjs')) {
- code = code.replace('
', '').replace(' ', '')
- return { code }
- }
- }
- }
-}
diff --git a/packages/playground/optimize-missing-deps/__test__/optimize-missing-deps.spec.ts b/packages/playground/optimize-missing-deps/__test__/optimize-missing-deps.spec.ts
deleted file mode 100644
index dd776daeceadbf..00000000000000
--- a/packages/playground/optimize-missing-deps/__test__/optimize-missing-deps.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { port } from './serve'
-import fetch from 'node-fetch'
-import { untilUpdated } from '../../testUtils'
-
-const url = `http://localhost:${port}`
-
-test('*', async () => {
- await page.goto(url)
- // reload page to get optimized missing deps
- await page.reload()
- await untilUpdated(() => page.textContent('div'), 'Client')
-
- // raw http request
- const aboutHtml = await (await fetch(url)).text()
- expect(aboutHtml).toMatch('Server')
-})
diff --git a/packages/playground/optimize-missing-deps/__test__/serve.js b/packages/playground/optimize-missing-deps/__test__/serve.js
deleted file mode 100644
index 9f293024f83913..00000000000000
--- a/packages/playground/optimize-missing-deps/__test__/serve.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9529)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- const { createServer } = require(path.resolve(root, 'server.js'))
- const { app, vite } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- if (vite) {
- await vite.close()
- }
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/optimize-missing-deps/index.html b/packages/playground/optimize-missing-deps/index.html
deleted file mode 100644
index 13e9831870aa18..00000000000000
--- a/packages/playground/optimize-missing-deps/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
Vite App
-
-
-
-
-
diff --git a/packages/playground/optimize-missing-deps/main.js b/packages/playground/optimize-missing-deps/main.js
deleted file mode 100644
index 93f3e1221298bf..00000000000000
--- a/packages/playground/optimize-missing-deps/main.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { sayName } from 'missing-dep'
-
-export const name = sayName()
diff --git a/packages/playground/optimize-missing-deps/missing-dep/index.js b/packages/playground/optimize-missing-deps/missing-dep/index.js
deleted file mode 100644
index f5d61c545d080a..00000000000000
--- a/packages/playground/optimize-missing-deps/missing-dep/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { name } from 'multi-entry-dep'
-
-export function sayName() {
- return name
-}
diff --git a/packages/playground/optimize-missing-deps/missing-dep/package.json b/packages/playground/optimize-missing-deps/missing-dep/package.json
deleted file mode 100644
index bbfc8ba2c87a57..00000000000000
--- a/packages/playground/optimize-missing-deps/missing-dep/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "missing-dep",
- "private": true,
- "version": "0.0.0",
- "main": "index.js",
- "dependencies": {
- "multi-entry-dep": "file:../multi-entry-dep"
- }
-}
diff --git a/packages/playground/optimize-missing-deps/multi-entry-dep/index.js b/packages/playground/optimize-missing-deps/multi-entry-dep/index.js
deleted file mode 100644
index 0717b87c27c2d8..00000000000000
--- a/packages/playground/optimize-missing-deps/multi-entry-dep/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const path = require('path')
-
-exports.name = path.normalize('./Server')
diff --git a/packages/playground/optimize-missing-deps/multi-entry-dep/package.json b/packages/playground/optimize-missing-deps/multi-entry-dep/package.json
deleted file mode 100644
index ac4f3e542d152b..00000000000000
--- a/packages/playground/optimize-missing-deps/multi-entry-dep/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "multi-entry-dep",
- "private": true,
- "version": "0.0.0",
- "main": "index.js",
- "browser": {
- "./index.js": "./index.browser.js"
- }
-}
diff --git a/packages/playground/optimize-missing-deps/package.json b/packages/playground/optimize-missing-deps/package.json
deleted file mode 100644
index 431cf3b33c3847..00000000000000
--- a/packages/playground/optimize-missing-deps/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "optimize-missing-deps",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "node server",
- "postinstall": "ts-node ../../../scripts/patchFileDeps.ts"
- },
- "dependencies": {
- "missing-dep": "file:./missing-dep",
- "multi-entry-dep": "file:./multi-entry-dep"
- },
- "devDependencies": {
- "express": "^4.17.1"
- }
-}
diff --git a/packages/playground/optimize-missing-deps/server.js b/packages/playground/optimize-missing-deps/server.js
deleted file mode 100644
index b9422feb622584..00000000000000
--- a/packages/playground/optimize-missing-deps/server.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// @ts-check
-const fs = require('fs')
-const path = require('path')
-const express = require('express')
-
-const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
-
-async function createServer(root = process.cwd()) {
- const resolve = (p) => path.resolve(__dirname, p)
-
- const app = express()
-
- /**
- * @type {import('vite').ViteDevServer}
- */
- const vite = await require('vite').createServer({
- root,
- logLevel: isTest ? 'error' : 'info',
- server: { middlewareMode: 'ssr' }
- })
- app.use(vite.middlewares)
-
- app.use('*', async (req, res) => {
- try {
- let template = fs.readFileSync(resolve('index.html'), 'utf-8')
- template = await vite.transformIndexHtml(req.originalUrl, template)
-
- // this will import missing deps nest built-in deps that should not be optimized
- const { name } = await vite.ssrLoadModule('./main.js')
-
- // this will import missing deps that should be optimized correctly
- const appHtml = `
${name}
-`
-
- const html = template.replace(``, appHtml)
-
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- } catch (e) {
- vite.ssrFixStacktrace(e)
- console.log(e.stack)
- res.status(500).end(e.stack)
- }
- })
-
- return { app, vite }
-}
-
-if (!isTest) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/package.json b/packages/playground/package.json
deleted file mode 100644
index 58ef368099e82f..00000000000000
--- a/packages/playground/package.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "name": "vite-playground",
- "private": true,
- "version": "1.0.0",
- "devDependencies": {
- "css-color-names": "^1.0.1"
- }
-}
diff --git a/packages/playground/preload/__tests__/preload.spec.ts b/packages/playground/preload/__tests__/preload.spec.ts
deleted file mode 100644
index 27a64930487797..00000000000000
--- a/packages/playground/preload/__tests__/preload.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { isBuild } from '../../testUtils'
-
-test('should have no 404s', () => {
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('404')
- })
-})
-
-if (isBuild) {
- test('dynamic import', async () => {
- const appHtml = await page.content()
- expect(appHtml).toMatch('This is
home page.')
- })
-
- test('dynamic import with comments', async () => {
- await page.goto(viteTestUrl + '/#/hello')
- const html = await page.content()
- expect(html).toMatch(
- /link rel="modulepreload".*?href="\/assets\/Hello\.\w{8}\.js"/
- )
- expect(html).toMatch(
- /link rel="stylesheet".*?href="\/assets\/Hello\.\w{8}\.css"/
- )
- })
-}
diff --git a/packages/playground/preload/index.html b/packages/playground/preload/index.html
deleted file mode 100644
index affed21f9791cf..00000000000000
--- a/packages/playground/preload/index.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/packages/playground/preload/package.json b/packages/playground/preload/package.json
deleted file mode 100644
index 5e65dafc8099c4..00000000000000
--- a/packages/playground/preload/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "test-preload",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "vue": "^3.2.25",
- "vue-router": "^4.0.0"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*"
- }
-}
diff --git a/packages/playground/preload/router.js b/packages/playground/preload/router.js
deleted file mode 100644
index d3d5afdc99f6a3..00000000000000
--- a/packages/playground/preload/router.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { createRouter, createWebHashHistory } from 'vue-router'
-import Home from './src/components/Home.vue'
-
-const routes = [
- { path: '/', name: 'Home', component: Home },
- {
- path: '/hello',
- name: 'Hello',
- component: () => import(/* a comment */ './src/components/Hello.vue')
- },
- {
- path: '/about',
- name: 'About',
- component: () => import('./src/components/About.vue')
- } // Lazy load route component
-]
-
-export default createRouter({
- routes,
- history: createWebHashHistory()
-})
diff --git a/packages/playground/preload/src/App.vue b/packages/playground/preload/src/App.vue
deleted file mode 100644
index 3582faf75e1216..00000000000000
--- a/packages/playground/preload/src/App.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/packages/playground/preload/src/components/About.vue b/packages/playground/preload/src/components/About.vue
deleted file mode 100644
index 2e73e80099446b..00000000000000
--- a/packages/playground/preload/src/components/About.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- This is about page.
-
- Go to Home page
-
-
-
-
diff --git a/packages/playground/preload/src/components/Hello.vue b/packages/playground/preload/src/components/Hello.vue
deleted file mode 100644
index 33b44d278d305d..00000000000000
--- a/packages/playground/preload/src/components/Hello.vue
+++ /dev/null
@@ -1,18 +0,0 @@
-
- {{ msg }}
-
- This is hello page.
-
- Go to Home page
-
-
-
-
-
-
diff --git a/packages/playground/preload/src/components/Home.vue b/packages/playground/preload/src/components/Home.vue
deleted file mode 100644
index 20f6b4948ac30a..00000000000000
--- a/packages/playground/preload/src/components/Home.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- This is home page.
-
- Go to About page
-
- Go to Hello page
-
-
-
-
diff --git a/packages/playground/preload/vite.config.js b/packages/playground/preload/vite.config.js
deleted file mode 100644
index 96fb82f51ed349..00000000000000
--- a/packages/playground/preload/vite.config.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const vuePlugin = require('@vitejs/plugin-vue')
-
-module.exports = {
- plugins: [vuePlugin()],
- build: {
- terserOptions: {
- format: {
- beautify: true
- },
- compress: {
- passes: 3
- }
- }
- }
-}
diff --git a/packages/playground/preserve-symlinks/__tests__/preserve-symlinks.spec.ts b/packages/playground/preserve-symlinks/__tests__/preserve-symlinks.spec.ts
deleted file mode 100644
index 7e0b546d7dbdbb..00000000000000
--- a/packages/playground/preserve-symlinks/__tests__/preserve-symlinks.spec.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-test('should have no 404s', () => {
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('404')
- })
-})
-
-test('not-preserve-symlinks', async () => {
- expect(await page.textContent('#root')).toBe('hello vite')
-})
diff --git a/packages/playground/preserve-symlinks/index.html b/packages/playground/preserve-symlinks/index.html
deleted file mode 100644
index 07f82cb05c3eec..00000000000000
--- a/packages/playground/preserve-symlinks/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
Vite App
-
-
-
-
-
-
diff --git a/packages/playground/preserve-symlinks/moduleA/package.json b/packages/playground/preserve-symlinks/moduleA/package.json
deleted file mode 100644
index 3df68a0a78a164..00000000000000
--- a/packages/playground/preserve-symlinks/moduleA/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "@symlinks/moduleA",
- "private": true,
- "version": "0.0.0",
- "main": "linked.js"
-}
diff --git a/packages/playground/preserve-symlinks/moduleA/src/data.js b/packages/playground/preserve-symlinks/moduleA/src/data.js
deleted file mode 100644
index e1bc98ec67da12..00000000000000
--- a/packages/playground/preserve-symlinks/moduleA/src/data.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const data = {
- msg: 'hello vite'
-}
diff --git a/packages/playground/preserve-symlinks/package.json b/packages/playground/preserve-symlinks/package.json
deleted file mode 100644
index 00a8ef23a3b05e..00000000000000
--- a/packages/playground/preserve-symlinks/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "preserve-symlinks",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite --force",
- "build": "vite build",
- "preview": "vite preview"
- },
- "dependencies": {
- "@symlinks/moduleA": "link:./moduleA"
- }
-}
diff --git a/packages/playground/preserve-symlinks/src/main.js b/packages/playground/preserve-symlinks/src/main.js
deleted file mode 100644
index 7257c44f1ba83f..00000000000000
--- a/packages/playground/preserve-symlinks/src/main.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { sayHi } from '@symlinks/moduleA'
-
-document.getElementById('root').innerText = sayHi().msg
diff --git a/packages/playground/react-emotion/App.jsx b/packages/playground/react-emotion/App.jsx
deleted file mode 100644
index b3715369614530..00000000000000
--- a/packages/playground/react-emotion/App.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { useState } from 'react'
-import { css } from '@emotion/react'
-
-import _Switch from 'react-switch'
-const Switch = _Switch.default || _Switch
-
-export function Counter() {
- const [count, setCount] = useState(0)
-
- return (
-
setCount((count) => count + 1)}
- >
- count is: {count}
-
- )
-}
-
-function FragmentTest() {
- const [checked, setChecked] = useState(false)
- return (
- <>
-
-
-
-
- >
- )
-}
-
-function App() {
- return (
-
- )
-}
-
-export default App
diff --git a/packages/playground/react-emotion/__tests__/react.spec.ts b/packages/playground/react-emotion/__tests__/react.spec.ts
deleted file mode 100644
index 49a66b9e103374..00000000000000
--- a/packages/playground/react-emotion/__tests__/react.spec.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { editFile, untilUpdated } from '../../testUtils'
-
-test('should render', async () => {
- expect(await page.textContent('h1')).toMatch(
- 'Hello Vite + React + @emotion/react'
- )
-})
-
-test('should update', async () => {
- expect(await page.textContent('button')).toMatch('count is: 0')
- await page.click('button')
- expect(await page.textContent('button')).toMatch('count is: 1')
-})
-
-test('should hmr', async () => {
- editFile('App.jsx', (code) =>
- code.replace('Vite + React + @emotion/react', 'Updated')
- )
- await untilUpdated(() => page.textContent('h1'), 'Hello Updated')
- // preserve state
- expect(await page.textContent('button')).toMatch('count is: 1')
-})
-
-test('should update button style', async () => {
- function getButtonBorderStyle() {
- return page.evaluate(() => {
- return window.getComputedStyle(document.querySelector('button')).border
- })
- }
-
- const styles = await page.evaluate(() => {
- return document.querySelector('button').style
- })
-
- expect(await getButtonBorderStyle()).toMatch('2px solid rgb(0, 0, 0)')
-
- editFile('App.jsx', (code) =>
- code.replace('border: 2px solid #000', 'border: 4px solid red')
- )
-
- await untilUpdated(getButtonBorderStyle, '4px solid rgb(255, 0, 0)')
-
- // preserve state
- expect(await page.textContent('button')).toMatch('count is: 1')
-})
diff --git a/packages/playground/react-emotion/index.html b/packages/playground/react-emotion/index.html
deleted file mode 100644
index ce1b9213c82763..00000000000000
--- a/packages/playground/react-emotion/index.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/packages/playground/react-emotion/package.json b/packages/playground/react-emotion/package.json
deleted file mode 100644
index fc78ac30b34a8d..00000000000000
--- a/packages/playground/react-emotion/package.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "test-react-emotion",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "@emotion/react": "^11.5.0",
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
- "react-switch": "^6.0.0"
- },
- "devDependencies": {
- "@babel/plugin-proposal-pipeline-operator": "^7.16.0",
- "@emotion/babel-plugin": "^11.3.0",
- "@vitejs/plugin-react": "workspace:*"
- },
- "babel": {
- "presets": [
- "@babel/preset-env"
- ]
- }
-}
diff --git a/packages/playground/react-emotion/vite.config.ts b/packages/playground/react-emotion/vite.config.ts
deleted file mode 100644
index 9364c8f616c2f5..00000000000000
--- a/packages/playground/react-emotion/vite.config.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import react from '@vitejs/plugin-react'
-import type { UserConfig } from 'vite'
-
-const config: UserConfig = {
- plugins: [
- react({
- jsxImportSource: '@emotion/react',
- babel: {
- plugins: ['@emotion/babel-plugin']
- }
- })
- ],
- clearScreen: false,
- build: {
- // to make tests faster
- minify: false
- }
-}
-
-export default config
diff --git a/packages/playground/react/App.jsx b/packages/playground/react/App.jsx
deleted file mode 100644
index 70d1f961b8cce9..00000000000000
--- a/packages/playground/react/App.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { useState } from 'react'
-import Dummy from './components/Dummy?qs-should-not-break-plugin-react'
-
-function App() {
- const [count, setCount] = useState(0)
- return (
-
-
- Hello Vite + React
-
- setCount((count) => count + 1)}>
- count is: {count}
-
-
-
- Edit App.jsx and save to test HMR updates.
-
-
- Learn React
-
-
-
-
-
- )
-}
-
-export default App
diff --git a/packages/playground/react/__tests__/react.spec.ts b/packages/playground/react/__tests__/react.spec.ts
deleted file mode 100644
index 46eb752924f801..00000000000000
--- a/packages/playground/react/__tests__/react.spec.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { editFile, untilUpdated, isBuild } from '../../testUtils'
-
-test('should render', async () => {
- expect(await page.textContent('h1')).toMatch('Hello Vite + React')
-})
-
-test('should update', async () => {
- expect(await page.textContent('button')).toMatch('count is: 0')
- await page.click('button')
- expect(await page.textContent('button')).toMatch('count is: 1')
-})
-
-test('should hmr', async () => {
- editFile('App.jsx', (code) => code.replace('Vite + React', 'Updated'))
- await untilUpdated(() => page.textContent('h1'), 'Hello Updated')
- // preserve state
- expect(await page.textContent('button')).toMatch('count is: 1')
-})
-
-test('should have annotated jsx with file location metadata', async () => {
- // we're not annotating in prod,
- // so we skip this test when isBuild is true
- if (isBuild) return
-
- const meta = await page.evaluate(() => {
- const button = document.querySelector('button')
- const key = Object.keys(button).find(
- (key) => key.indexOf('__reactFiber') === 0
- )
- return button[key]._debugSource
- })
- // If the evaluate call doesn't crash, and the returned metadata has
- // the expected fields, we're good.
- expect(Object.keys(meta).sort()).toEqual([
- 'columnNumber',
- 'fileName',
- 'lineNumber'
- ])
-})
diff --git a/packages/playground/react/components/Dummy.jsx b/packages/playground/react/components/Dummy.jsx
deleted file mode 100644
index 27ec3c21de30fd..00000000000000
--- a/packages/playground/react/components/Dummy.jsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function Dummy() {
- return <>>
-}
diff --git a/packages/playground/react/index.html b/packages/playground/react/index.html
deleted file mode 100644
index ce1b9213c82763..00000000000000
--- a/packages/playground/react/index.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/packages/playground/react/package.json b/packages/playground/react/package.json
deleted file mode 100644
index fc9b8e69d3999e..00000000000000
--- a/packages/playground/react/package.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "name": "test-react",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "react": "^17.0.2",
- "react-dom": "^17.0.2"
- },
- "devDependencies": {
- "@vitejs/plugin-react": "workspace:*"
- },
- "babel": {
- "presets": [
- "@babel/preset-env"
- ]
- }
-}
diff --git a/packages/playground/react/vite.config.ts b/packages/playground/react/vite.config.ts
deleted file mode 100644
index c6955a131d375f..00000000000000
--- a/packages/playground/react/vite.config.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import react from '@vitejs/plugin-react'
-import type { UserConfig } from 'vite'
-
-const config: UserConfig = {
- plugins: [react()],
- build: {
- // to make tests faster
- minify: false
- }
-}
-
-export default config
diff --git a/packages/playground/resolve-config/__tests__/resolve-config.spec.ts b/packages/playground/resolve-config/__tests__/resolve-config.spec.ts
deleted file mode 100644
index 13ea5ea6f59a4f..00000000000000
--- a/packages/playground/resolve-config/__tests__/resolve-config.spec.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import fs from 'fs'
-import path from 'path'
-import { commandSync } from 'execa'
-import { isBuild, testDir, workspaceRoot } from '../../testUtils'
-
-const viteBin = path.join(workspaceRoot, 'packages', 'vite', 'bin', 'vite.js')
-
-const fromTestDir = (...p: string[]) => path.resolve(testDir, ...p)
-
-const build = (configName: string) => {
- commandSync(`${viteBin} build`, { cwd: fromTestDir(configName) })
-}
-const getDistFile = (configName: string) => {
- return fs.readFileSync(fromTestDir(`${configName}/dist/index.es.js`), 'utf8')
-}
-
-if (isBuild) {
- it('loads vite.config.js', () => {
- build('js')
- expect(getDistFile('js')).toContain('console.log(true)')
- })
- it('loads vite.config.js with package#type module', () => {
- build('js-module')
- expect(getDistFile('js-module')).toContain('console.log(true)')
- })
- it('loads vite.config.cjs', () => {
- build('cjs')
- expect(getDistFile('cjs')).toContain('console.log(true)')
- })
- it('loads vite.config.cjs with package#type module', () => {
- build('cjs-module')
- expect(getDistFile('cjs-module')).toContain('console.log(true)')
- })
- it('loads vite.config.mjs', () => {
- build('mjs')
- expect(getDistFile('mjs')).toContain('console.log(true)')
- })
- it('loads vite.config.mjs with package#type module', () => {
- build('mjs-module')
- expect(getDistFile('mjs-module')).toContain('console.log(true)')
- })
- it('loads vite.config.ts', () => {
- build('ts')
- expect(getDistFile('ts')).toContain('console.log(true)')
- })
- it('loads vite.config.ts with package#type module', () => {
- build('ts-module')
- expect(getDistFile('ts-module')).toContain('console.log(true)')
- })
-} else {
- // this test doesn't support serve mode
- // must contain at least one test
- test('should work', () => void 0)
-}
diff --git a/packages/playground/resolve-config/__tests__/serve.js b/packages/playground/resolve-config/__tests__/serve.js
deleted file mode 100644
index bd451d4cf6f6bc..00000000000000
--- a/packages/playground/resolve-config/__tests__/serve.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-const fs = require('fs-extra')
-const { testDir } = require('../../testUtils')
-
-const fromTestDir = (/** @type{string[]} */ ...p) => path.resolve(testDir, ...p)
-
-const configNames = ['js', 'cjs', 'mjs', 'ts']
-
-/** @param {string} root @param {boolean} isProd */
-exports.serve = async function serve(root, isProd) {
- if (!isProd) return
-
- // create separate directories for all config types:
- // ./{js,cjs,mjs,ts} and ./{js,cjs,mjs,ts}-module (with package#type)
- for (const configName of configNames) {
- const pathToConf = fromTestDir(configName, `vite.config.${configName}`)
-
- await fs.copy(fromTestDir('root'), fromTestDir(configName))
- await fs.rename(fromTestDir(configName, 'vite.config.js'), pathToConf)
-
- if (configName === 'cjs') {
- const conf = await fs.readFile(pathToConf, 'utf8')
- await fs.writeFile(
- pathToConf,
- conf.replace('export default', 'module.exports = ')
- )
- }
-
- // copy directory and add package.json with "type": "module"
- await fs.copy(fromTestDir(configName), fromTestDir(`${configName}-module`))
- await fs.writeJSON(fromTestDir(`${configName}-module`, 'package.json'), {
- type: 'module'
- })
- }
-}
diff --git a/packages/playground/resolve-config/package.json b/packages/playground/resolve-config/package.json
deleted file mode 100644
index 5459dad99003cd..00000000000000
--- a/packages/playground/resolve-config/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "resolve-config",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite --force",
- "build": "vite build",
- "preview": "vite preview"
- }
-}
diff --git a/packages/playground/resolve-config/root/index.js b/packages/playground/resolve-config/root/index.js
deleted file mode 100644
index a3f8f13f20f96e..00000000000000
--- a/packages/playground/resolve-config/root/index.js
+++ /dev/null
@@ -1 +0,0 @@
-console.log(__CONFIG_LOADED__)
diff --git a/packages/playground/resolve-config/root/vite.config.js b/packages/playground/resolve-config/root/vite.config.js
deleted file mode 100644
index ed72046f940d59..00000000000000
--- a/packages/playground/resolve-config/root/vite.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- define: { __CONFIG_LOADED__: true },
- logLevel: 'silent',
- build: {
- minify: false,
- sourcemap: false,
- lib: { entry: 'index.js', fileName: 'index', formats: ['es'] }
- }
-}
diff --git a/packages/playground/resolve-linked/package.json b/packages/playground/resolve-linked/package.json
deleted file mode 100644
index 204cfd931c63ab..00000000000000
--- a/packages/playground/resolve-linked/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "resolve-linked",
- "private": true,
- "version": "0.0.0",
- "main": "src/index.js"
-}
diff --git a/packages/playground/resolve/__tests__/resolve.spec.ts b/packages/playground/resolve/__tests__/resolve.spec.ts
deleted file mode 100644
index b64da138033fc0..00000000000000
--- a/packages/playground/resolve/__tests__/resolve.spec.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { isBuild } from '../../testUtils'
-
-test('bom import', async () => {
- expect(await page.textContent('.utf8-bom')).toMatch('[success]')
-})
-
-test('deep import', async () => {
- expect(await page.textContent('.deep-import')).toMatch('[2,4]')
-})
-
-test('entry with exports field', async () => {
- expect(await page.textContent('.exports-entry')).toMatch('[success]')
-})
-
-test('deep import with exports field', async () => {
- expect(await page.textContent('.exports-deep')).toMatch('[success]')
-})
-
-test('deep import with query with exports field', async () => {
- expect(await page.textContent('.exports-deep-query')).not.toMatch('fail')
-})
-
-test('deep import with exports field + exposed dir', async () => {
- expect(await page.textContent('.exports-deep-exposed-dir')).toMatch(
- '[success]'
- )
-})
-
-test('deep import with exports field + mapped dir', async () => {
- expect(await page.textContent('.exports-deep-mapped-dir')).toMatch(
- '[success]'
- )
-})
-
-test('Respect exports field env key priority', async () => {
- expect(await page.textContent('.exports-env')).toMatch('[success]')
-})
-
-test('Respect production/development conditionals', async () => {
- expect(await page.textContent('.exports-env')).toMatch(
- isBuild ? `browser.prod.mjs` : `browser.mjs`
- )
-})
-
-test('implicit dir/index.js', async () => {
- expect(await page.textContent('.index')).toMatch('[success]')
-})
-
-test('implicit dir/index.js vs explicit file', async () => {
- expect(await page.textContent('.dir-vs-file')).toMatch('[success]')
-})
-
-test('exact extension vs. duplicated (.js.js)', async () => {
- expect(await page.textContent('.exact-extension')).toMatch('[success]')
-})
-
-test('dont add extension to directory name (./dir-with-ext.js/index.js)', async () => {
- expect(await page.textContent('.dir-with-ext')).toMatch('[success]')
-})
-
-test('a ts module can import another ts module using its corresponding js file name', async () => {
- expect(await page.textContent('.ts-extension')).toMatch('[success]')
-})
-
-test('filename with dot', async () => {
- expect(await page.textContent('.dot')).toMatch('[success]')
-})
-
-test('browser field', async () => {
- expect(await page.textContent('.browser')).toMatch('[success]')
-})
-
-test('css entry', async () => {
- expect(await page.textContent('.css')).toMatch('[success]')
-})
-
-test('monorepo linked dep', async () => {
- expect(await page.textContent('.monorepo')).toMatch('[success]')
-})
-
-test('plugin resolved virtual file', async () => {
- expect(await page.textContent('.virtual')).toMatch('[success]')
-})
-
-test('plugin resolved custom virtual file', async () => {
- expect(await page.textContent('.custom-virtual')).toMatch('[success]')
-})
-
-test('resolve inline package', async () => {
- expect(await page.textContent('.inline-pkg')).toMatch('[success]')
-})
-
-test('resolve.extensions', async () => {
- expect(await page.textContent('.custom-ext')).toMatch('[success]')
-})
-
-test('resolve.mainFields', async () => {
- expect(await page.textContent('.custom-main-fields')).toMatch('[success]')
-})
-
-test('resolve.conditions', async () => {
- expect(await page.textContent('.custom-condition')).toMatch('[success]')
-})
-
-test('resolve package that contains # in path', async () => {
- expect(await page.textContent('.path-contains-sharp-symbol')).toMatch(
- '[success]'
- )
-})
diff --git a/packages/playground/resolve/browser-field/multiple.dot.path.js b/packages/playground/resolve/browser-field/multiple.dot.path.js
deleted file mode 100644
index b4022f73daaf6e..00000000000000
--- a/packages/playground/resolve/browser-field/multiple.dot.path.js
+++ /dev/null
@@ -1,2 +0,0 @@
-const fs = require('fs')
-console.log('this should not run in the browser')
diff --git a/packages/playground/resolve/browser-field/no-ext-index/index.js b/packages/playground/resolve/browser-field/no-ext-index/index.js
deleted file mode 100644
index d3f4967324ffb7..00000000000000
--- a/packages/playground/resolve/browser-field/no-ext-index/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import jsdom from 'jsdom' // should be redireted to empty module
-export default ''
diff --git a/packages/playground/resolve/browser-field/no-ext.js b/packages/playground/resolve/browser-field/no-ext.js
deleted file mode 100644
index d3f4967324ffb7..00000000000000
--- a/packages/playground/resolve/browser-field/no-ext.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import jsdom from 'jsdom' // should be redireted to empty module
-export default ''
diff --git a/packages/playground/resolve/browser-field/not-browser.js b/packages/playground/resolve/browser-field/not-browser.js
deleted file mode 100644
index b4022f73daaf6e..00000000000000
--- a/packages/playground/resolve/browser-field/not-browser.js
+++ /dev/null
@@ -1,2 +0,0 @@
-const fs = require('fs')
-console.log('this should not run in the browser')
diff --git a/packages/playground/resolve/browser-field/out/esm.browser.js b/packages/playground/resolve/browser-field/out/esm.browser.js
deleted file mode 100644
index bddf03df0b0c22..00000000000000
--- a/packages/playground/resolve/browser-field/out/esm.browser.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import jsdom from 'jsdom' // should be redireted to empty module
-export default '[success] resolve browser field'
diff --git a/packages/playground/resolve/browser-field/package.json b/packages/playground/resolve/browser-field/package.json
deleted file mode 100644
index 006f9b4b5f4fc6..00000000000000
--- a/packages/playground/resolve/browser-field/package.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "name": "resolve-browser-field",
- "private": true,
- "version": "1.0.0",
- "//": "real world example: https://github.com/axios/axios/blob/3f2ef030e001547eb06060499f8a2e3f002b5a14/package.json#L71-L73",
- "main": "out/cjs.node.js",
- "browser": {
- "./out/cjs.node.js": "./out/esm.browser.js",
- "./no-ext": "./out/esm.browser.js",
- "./ext.js": "./out/esm.browser.js",
- "./ext-index/index.js": "./out/esm.browser.js",
- "./no-ext-index": "./out/esm.browser.js",
- "./not-browser.js": false,
- "./multiple.dot.path.js": false,
- "jsdom": false
- }
-}
diff --git a/packages/playground/resolve/browser-field/relative.js b/packages/playground/resolve/browser-field/relative.js
deleted file mode 100644
index bbf6a2c74a10b5..00000000000000
--- a/packages/playground/resolve/browser-field/relative.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import ra from './no-ext'
-import rb from './no-ext.js' // no substitution
-import rc from './ext'
-import rd from './ext.js'
-import re from './ext-index/index.js'
-import rf from './ext-index'
-import rg from './no-ext-index/index.js' // no substitution
-
-export { ra, rb, rc, rd, re, rf, rg }
diff --git a/packages/playground/resolve/config-dep.js b/packages/playground/resolve/config-dep.js
deleted file mode 100644
index 8bc3563c743bcd..00000000000000
--- a/packages/playground/resolve/config-dep.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- a: 1
-}
diff --git a/packages/playground/resolve/custom-condition/package.json b/packages/playground/resolve/custom-condition/package.json
deleted file mode 100644
index 490a420fe2dbfc..00000000000000
--- a/packages/playground/resolve/custom-condition/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "resolve-custom-condition",
- "private": true,
- "version": "1.0.0",
- "main": "index.js",
- "exports": {
- ".": {
- "custom": "./index.custom.js",
- "import": "./index.js",
- "require": "./index.js"
- }
- }
-}
diff --git a/packages/playground/resolve/custom-main-field/package.json b/packages/playground/resolve/custom-main-field/package.json
deleted file mode 100644
index bb948c3261eb1c..00000000000000
--- a/packages/playground/resolve/custom-main-field/package.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "resolve-custom-main-field",
- "private": true,
- "version": "1.0.0",
- "main": "index.js",
- "custom": "index.custom.js"
-}
diff --git a/packages/playground/resolve/exports-env/package.json b/packages/playground/resolve/exports-env/package.json
deleted file mode 100644
index f9e635b5a19c24..00000000000000
--- a/packages/playground/resolve/exports-env/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "resolve-exports-env",
- "private": true,
- "version": "1.0.0",
- "exports": {
- "import": {
- "browser": {
- "production": "./browser.prod.mjs",
- "development": "./browser.mjs"
- }
- },
- "browser": "./browser.js",
- "default": "./fallback.umd.js"
- }
-}
diff --git a/packages/playground/resolve/exports-path/package.json b/packages/playground/resolve/exports-path/package.json
deleted file mode 100644
index 7355da2f63f616..00000000000000
--- a/packages/playground/resolve/exports-path/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "resolve-exports-path",
- "private": true,
- "version": "1.0.0",
- "exports": {
- ".": {
- "import": "./main.js",
- "require": "./cjs.js"
- },
- "./deep.js": "./deep.js",
- "./deep.json": "./deep.json",
- "./dir/": "./dir/",
- "./dir-mapped/*": {
- "import": "./dir/*",
- "require": "./dir-cjs/*"
- }
- }
-}
diff --git a/packages/playground/resolve/index.html b/packages/playground/resolve/index.html
deleted file mode 100644
index c0569345d86837..00000000000000
--- a/packages/playground/resolve/index.html
+++ /dev/null
@@ -1,227 +0,0 @@
-
Resolve
-
-
Utf8-bom import
-
fail
-
-
Deep import
-
Should show [2,4]:fail
-
-
Entry resolving with exports field
-
fail
-
-
Deep import with exports field
-
fail
-
-
Deep import with query with exports field
-
fail
-
-
Deep import with exports field + exposed directory
-
fail
-
-
Deep import with exports field + mapped directory
-
fail
-
-
Exports field env priority
-
fail
-
-
Resolve /index.*
-
fail
-
-
Resolve dir and file of the same name (should prioritize file)
-
fail
-
-
Resolve to non-duplicated file extension
-
fail
-
-
Don't add extensions to directory names
-
fail
-
-
- A ts module can import another ts module using its corresponding js file name
-
-
fail
-
-
- A ts module can import another tsx module using its corresponding jsx file
- name
-
-
fail
-
-
- A ts module can import another tsx module using its corresponding js file name
-
-
fail
-
-
Resolve file name containing dot
-
fail
-
-
Browser Field
-
fail
-
-
CSS Entry
-
-
-
Monorepo linked dep
-
-
-
Plugin resolved virtual file
-
-
-
Plugin resolved custom virtual file
-
-
-
Inline package
-
-
-
resolve.extensions
-
-
-
resolve.mainFields
-
-
-
resolve.conditions
-
-
-
resolve package that contains # in path
-
-
-
-
-
diff --git a/packages/playground/resolve/inline-package/package.json b/packages/playground/resolve/inline-package/package.json
deleted file mode 100644
index bbbb6b0b0381d8..00000000000000
--- a/packages/playground/resolve/inline-package/package.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "inline-package",
- "private": true,
- "version": "0.0.0",
- "sideEffects": false,
- "main": "./inline"
-}
diff --git a/packages/playground/resolve/package.json b/packages/playground/resolve/package.json
deleted file mode 100644
index 5e0f53b4c8468a..00000000000000
--- a/packages/playground/resolve/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "test-resolve",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "@babel/runtime": "^7.16.0",
- "es5-ext": "0.10.53",
- "normalize.css": "^8.0.1",
- "resolve-browser-field": "link:./browser-field",
- "resolve-custom-condition": "link:./custom-condition",
- "resolve-custom-main-field": "link:./custom-main-field",
- "resolve-exports-env": "link:./exports-env",
- "resolve-exports-path": "link:./exports-path",
- "resolve-linked": "workspace:*"
- }
-}
diff --git a/packages/playground/resolve/ts-extension/index.ts b/packages/playground/resolve/ts-extension/index.ts
deleted file mode 100644
index bdb326f8778e64..00000000000000
--- a/packages/playground/resolve/ts-extension/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { msg } from './hello.js'
-import { msgJsx } from './hellojsx.jsx'
-import { msgTsx } from './hellotsx.js'
-
-export { msg, msgJsx, msgTsx }
diff --git a/packages/playground/resolve/vite.config.js b/packages/playground/resolve/vite.config.js
deleted file mode 100644
index be1b75e431383a..00000000000000
--- a/packages/playground/resolve/vite.config.js
+++ /dev/null
@@ -1,44 +0,0 @@
-const virtualFile = '@virtual-file'
-const virtualId = '\0' + virtualFile
-
-const customVirtualFile = '@custom-virtual-file'
-const { a } = require('./config-dep')
-
-module.exports = {
- resolve: {
- extensions: ['.mjs', '.js', '.es', '.ts'],
- mainFields: ['custom', 'module'],
- conditions: ['custom']
- },
- define: {
- VITE_CONFIG_DEP_TEST: a
- },
- plugins: [
- {
- name: 'virtual-module',
- resolveId(id) {
- if (id === virtualFile) {
- return virtualId
- }
- },
- load(id) {
- if (id === virtualId) {
- return `export const msg = "[success] from conventional virtual file"`
- }
- }
- },
- {
- name: 'custom-resolve',
- resolveId(id) {
- if (id === customVirtualFile) {
- return id
- }
- },
- load(id) {
- if (id === customVirtualFile) {
- return `export const msg = "[success] from custom virtual file"`
- }
- }
- }
- ]
-}
diff --git a/packages/playground/shims.d.ts b/packages/playground/shims.d.ts
deleted file mode 100644
index ced8fb1ad585ae..00000000000000
--- a/packages/playground/shims.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare module 'css-color-names' {
- const colors: Record
- export default colors
-}
-
-declare module '*.vue' {
- import type { ComponentOptions } from 'vue'
- const component: ComponentOptions
- export default component
-}
diff --git a/packages/playground/ssr-deps/__tests__/serve.js b/packages/playground/ssr-deps/__tests__/serve.js
deleted file mode 100644
index 5ba5724f2b7a94..00000000000000
--- a/packages/playground/ssr-deps/__tests__/serve.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9530)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- const { createServer } = require(path.resolve(root, 'server.js'))
- const { app, vite } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- if (vite) {
- await vite.close()
- }
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/ssr-deps/__tests__/ssr-deps.spec.ts b/packages/playground/ssr-deps/__tests__/ssr-deps.spec.ts
deleted file mode 100644
index 8a201c9eb87455..00000000000000
--- a/packages/playground/ssr-deps/__tests__/ssr-deps.spec.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { port } from './serve'
-
-const url = `http://localhost:${port}`
-
-/**
- * test for #5809
- *
- * NOTE: This test will always succeed now, unless the temporary workaround for Jest can be removed
- * See https://github.com/vitejs/vite/pull/5197#issuecomment-938054077
- */
-test('msg should be encrypted', async () => {
- await page.goto(url)
- expect(await page.textContent('.encrypted-msg')).not.toMatch(
- 'Secret Message!'
- )
-})
-
-test('msg read by fs/promises', async () => {
- await page.goto(url)
- expect(await page.textContent('.file-message')).toMatch('File Content!')
-})
-
-test('msg from primitive export', async () => {
- await page.goto(url)
- expect(await page.textContent('.primitive-export-message')).toMatch(
- 'Hello World!'
- )
-})
-
-test('msg from TS transpiled exports', async () => {
- await page.goto(url)
- expect(await page.textContent('.ts-default-export-message')).toMatch(
- 'Hello World!'
- )
- expect(await page.textContent('.ts-named-export-message')).toMatch(
- 'Hello World!'
- )
-})
-
-test('msg from Object.assign exports', async () => {
- await page.goto(url)
- expect(await page.textContent('.object-assigned-exports-message')).toMatch(
- 'Hello World!'
- )
-})
-
-test('msg from forwarded exports', async () => {
- await page.goto(url)
- expect(await page.textContent('.forwarded-export-message')).toMatch(
- 'Hello World!'
- )
-})
-
-test('msg from define properties exports', async () => {
- await page.goto(url)
- expect(await page.textContent('.define-properties-exports-msg')).toMatch(
- 'Hello World!'
- )
-})
-
-test('msg from define property exports', async () => {
- await page.goto(url)
- expect(await page.textContent('.define-property-exports-msg')).toMatch(
- 'Hello World!'
- )
-})
-
-test('msg from only object assigned exports', async () => {
- await page.goto(url)
- expect(await page.textContent('.only-object-assigned-exports-msg')).toMatch(
- 'Hello World!'
- )
-})
diff --git a/packages/playground/ssr-deps/define-properties-exports/package.json b/packages/playground/ssr-deps/define-properties-exports/package.json
deleted file mode 100644
index 3cf10f8cced539..00000000000000
--- a/packages/playground/ssr-deps/define-properties-exports/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "define-properties-exports",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/define-property-exports/index.js b/packages/playground/ssr-deps/define-property-exports/index.js
deleted file mode 100644
index 4506dd6200051e..00000000000000
--- a/packages/playground/ssr-deps/define-property-exports/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Object.defineProperty(exports, 'hello', {
- value() {
- return 'Hello World!'
- }
-})
diff --git a/packages/playground/ssr-deps/define-property-exports/package.json b/packages/playground/ssr-deps/define-property-exports/package.json
deleted file mode 100644
index 38ef7fdf5f410a..00000000000000
--- a/packages/playground/ssr-deps/define-property-exports/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "define-property-exports",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/forwarded-export/package.json b/packages/playground/ssr-deps/forwarded-export/package.json
deleted file mode 100644
index 1a0a62e0b4472d..00000000000000
--- a/packages/playground/ssr-deps/forwarded-export/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "forwarded-export",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/index.html b/packages/playground/ssr-deps/index.html
deleted file mode 100644
index b1e884efaab01a..00000000000000
--- a/packages/playground/ssr-deps/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- SSR Dependencies
-
-
- SSR Dependencies
-
-
-
diff --git a/packages/playground/ssr-deps/object-assigned-exports/index.js b/packages/playground/ssr-deps/object-assigned-exports/index.js
deleted file mode 100644
index d6510e38f3a36f..00000000000000
--- a/packages/playground/ssr-deps/object-assigned-exports/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-Object.defineProperty(exports, '__esModule', { value: true })
-
-const obj = {
- hello() {
- return 'Hello World!'
- }
-}
-
-Object.assign(exports, obj)
diff --git a/packages/playground/ssr-deps/object-assigned-exports/package.json b/packages/playground/ssr-deps/object-assigned-exports/package.json
deleted file mode 100644
index a385dc9b7ec1b7..00000000000000
--- a/packages/playground/ssr-deps/object-assigned-exports/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "object-assigned-exports",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/only-object-assigned-exports/index.js b/packages/playground/ssr-deps/only-object-assigned-exports/index.js
deleted file mode 100644
index b6a4ab368b133d..00000000000000
--- a/packages/playground/ssr-deps/only-object-assigned-exports/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Object.assign(exports, {
- hello() {
- return 'Hello World!'
- }
-})
diff --git a/packages/playground/ssr-deps/only-object-assigned-exports/package.json b/packages/playground/ssr-deps/only-object-assigned-exports/package.json
deleted file mode 100644
index 22a071b59e411d..00000000000000
--- a/packages/playground/ssr-deps/only-object-assigned-exports/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "only-object-assigned-exports",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/package.json b/packages/playground/ssr-deps/package.json
deleted file mode 100644
index 7af243c3b4769a..00000000000000
--- a/packages/playground/ssr-deps/package.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "test-ssr-deps",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "node server",
- "serve": "cross-env NODE_ENV=production node server",
- "debug": "node --inspect-brk server",
- "postinstall": "ts-node ../../../scripts/patchFileDeps.ts"
- },
- "dependencies": {
- "bcrypt": "^5.0.1",
- "define-properties-exports": "file:./define-properties-exports",
- "define-property-exports": "file:./define-property-exports",
- "forwarded-export": "file:./forwarded-export",
- "object-assigned-exports": "file:./object-assigned-exports",
- "only-object-assigned-exports": "file:./only-object-assigned-exports",
- "primitive-export": "file:./primitive-export",
- "read-file-content": "file:./read-file-content",
- "require-absolute": "file:./require-absolute",
- "ts-transpiled-exports": "file:./ts-transpiled-exports"
- },
- "devDependencies": {
- "cross-env": "^7.0.3",
- "express": "^4.17.1"
- }
-}
diff --git a/packages/playground/ssr-deps/primitive-export/package.json b/packages/playground/ssr-deps/primitive-export/package.json
deleted file mode 100644
index d86685f6b9a8f1..00000000000000
--- a/packages/playground/ssr-deps/primitive-export/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "primitive-export",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/read-file-content/index.js b/packages/playground/ssr-deps/read-file-content/index.js
deleted file mode 100644
index c8761b3b4734c1..00000000000000
--- a/packages/playground/ssr-deps/read-file-content/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const path = require('path')
-
-module.exports = async function readFileContent(filePath) {
- const fs =
- process.versions.node.split('.')[0] >= '14'
- ? require('fs/promises')
- : require('fs').promises
- return await fs.readFile(path.resolve(filePath), 'utf-8')
-}
diff --git a/packages/playground/ssr-deps/read-file-content/package.json b/packages/playground/ssr-deps/read-file-content/package.json
deleted file mode 100644
index 6145e7821f7067..00000000000000
--- a/packages/playground/ssr-deps/read-file-content/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "read-file-content",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/require-absolute/index.js b/packages/playground/ssr-deps/require-absolute/index.js
deleted file mode 100644
index c2f844f3e2f6ed..00000000000000
--- a/packages/playground/ssr-deps/require-absolute/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const path = require('path')
-
-module.exports.hello = () => require(path.resolve(__dirname, './foo.js')).hello
diff --git a/packages/playground/ssr-deps/require-absolute/package.json b/packages/playground/ssr-deps/require-absolute/package.json
deleted file mode 100644
index 352f550e184745..00000000000000
--- a/packages/playground/ssr-deps/require-absolute/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "require-absolute",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-deps/server.js b/packages/playground/ssr-deps/server.js
deleted file mode 100644
index 89a64ae51fdc94..00000000000000
--- a/packages/playground/ssr-deps/server.js
+++ /dev/null
@@ -1,68 +0,0 @@
-// @ts-check
-const fs = require('fs')
-const path = require('path')
-const express = require('express')
-
-const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
-
-async function createServer(
- root = process.cwd(),
- isProd = process.env.NODE_ENV === 'production'
-) {
- const resolve = (p) => path.resolve(__dirname, p)
-
- const app = express()
-
- /**
- * @type {import('vite').ViteDevServer}
- */
- const vite = await require('vite').createServer({
- root,
- logLevel: isTest ? 'error' : 'info',
- server: {
- middlewareMode: 'ssr',
- watch: {
- // During tests we edit the files too fast and sometimes chokidar
- // misses change events, so enforce polling for consistency
- usePolling: true,
- interval: 100
- }
- }
- })
- // use vite's connect instance as middleware
- app.use(vite.middlewares)
-
- app.use('*', async (req, res) => {
- try {
- const url = req.originalUrl
-
- let template
- template = fs.readFileSync(resolve('index.html'), 'utf-8')
- template = await vite.transformIndexHtml(url, template)
- const render = (await vite.ssrLoadModule('/src/app.js')).render
-
- const appHtml = await render(url, __dirname)
-
- const html = template.replace(``, appHtml)
-
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- } catch (e) {
- vite && vite.ssrFixStacktrace(e)
- console.log(e.stack)
- res.status(500).end(e.stack)
- }
- })
-
- return { app, vite }
-}
-
-if (!isTest) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/ssr-deps/src/app.js b/packages/playground/ssr-deps/src/app.js
deleted file mode 100644
index 9646cdcf2bf688..00000000000000
--- a/packages/playground/ssr-deps/src/app.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import path from 'path'
-import readFileContent from 'read-file-content'
-import primitiveExport from 'primitive-export'
-import tsDefaultExport, { hello as tsNamedExport } from 'ts-transpiled-exports'
-import objectAssignedExports from 'object-assigned-exports'
-import forwardedExport from 'forwarded-export'
-import bcrypt from 'bcrypt'
-import definePropertiesExports from 'define-properties-exports'
-import definePropertyExports from 'define-property-exports'
-import onlyObjectAssignedExports from 'only-object-assigned-exports'
-import requireAbsolute from 'require-absolute'
-
-export async function render(url, rootDir) {
- let html = ''
-
- const encryptedMsg = await bcrypt.hash('Secret Message!', 10)
- html += `\nencrypted message: ${encryptedMsg}
`
-
- const fileContent = await readFileContent(path.resolve(rootDir, 'message'))
- html += `\nmsg read via fs/promises: ${fileContent}
`
-
- html += `\nmessage from primitive export: ${primitiveExport}
`
-
- const tsDefaultExportMessage = tsDefaultExport()
- html += `\nmessage from ts-default-export: ${tsDefaultExportMessage}
`
-
- const tsNamedExportMessage = tsNamedExport()
- html += `\nmessage from ts-named-export: ${tsNamedExportMessage}
`
-
- const objectAssignedExportsMessage = objectAssignedExports.hello()
- html += `\nmessage from object-assigned-exports: ${objectAssignedExportsMessage}
`
-
- const forwardedExportMessage = forwardedExport.hello()
- html += `\nmessage from forwarded-export: ${forwardedExportMessage}
`
-
- const definePropertiesExportsMsg = definePropertiesExports.hello()
- html += `\nmessage from define-properties-exports: ${definePropertiesExportsMsg}
`
-
- const definePropertyExportsMsg = definePropertyExports.hello()
- html += `\nmessage from define-property-exports: ${definePropertyExportsMsg}
`
-
- const onlyObjectAssignedExportsMessage = onlyObjectAssignedExports.hello()
- html += `\nmessage from only-object-assigned-exports: ${onlyObjectAssignedExportsMessage}
`
-
- const requireAbsoluteMessage = requireAbsolute.hello()
- html += `\nmessage from require-absolute: ${requireAbsoluteMessage}
`
-
- return html + '\n'
-}
diff --git a/packages/playground/ssr-deps/ts-transpiled-exports/package.json b/packages/playground/ssr-deps/ts-transpiled-exports/package.json
deleted file mode 100644
index 7dbeff43974e42..00000000000000
--- a/packages/playground/ssr-deps/ts-transpiled-exports/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "ts-transpiled-exports",
- "private": true,
- "version": "0.0.0"
-}
diff --git a/packages/playground/ssr-html/__tests__/serve.js b/packages/playground/ssr-html/__tests__/serve.js
deleted file mode 100644
index 5ba5724f2b7a94..00000000000000
--- a/packages/playground/ssr-html/__tests__/serve.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9530)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- const { createServer } = require(path.resolve(root, 'server.js'))
- const { app, vite } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- if (vite) {
- await vite.close()
- }
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/ssr-html/__tests__/ssr-html.spec.ts b/packages/playground/ssr-html/__tests__/ssr-html.spec.ts
deleted file mode 100644
index e34b8a91fc3421..00000000000000
--- a/packages/playground/ssr-html/__tests__/ssr-html.spec.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { port } from './serve'
-import fetch from 'node-fetch'
-
-const url = `http://localhost:${port}`
-
-describe('injected inline scripts', () => {
- test('no injected inline scripts are present', async () => {
- await page.goto(url)
- const inlineScripts = await page.$$eval('script', (nodes) =>
- nodes.filter((n) => !n.getAttribute('src') && n.innerHTML)
- )
- expect(inlineScripts).toHaveLength(0)
- })
-
- test('injected script proxied correctly', async () => {
- await page.goto(url)
- const proxiedScripts = await page.$$eval('script', (nodes) =>
- nodes
- .filter((n) => {
- const src = n.getAttribute('src')
- if (!src) return false
- return src.includes('?html-proxy&index')
- })
- .map((n) => n.getAttribute('src'))
- )
-
- // assert at least 1 proxied script exists
- expect(proxiedScripts).not.toHaveLength(0)
-
- const scriptContents = await Promise.all(
- proxiedScripts.map((src) => fetch(url + src).then((res) => res.text()))
- )
-
- // all proxied scripts return code
- for (const code of scriptContents) {
- expect(code).toBeTruthy()
- }
- })
-})
diff --git a/packages/playground/ssr-html/index.html b/packages/playground/ssr-html/index.html
deleted file mode 100644
index c37dcc7e366ae8..00000000000000
--- a/packages/playground/ssr-html/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
- SSR HTML
-
-
- SSR Dynamic HTML
-
-
diff --git a/packages/playground/ssr-html/package.json b/packages/playground/ssr-html/package.json
deleted file mode 100644
index a14756422a8b28..00000000000000
--- a/packages/playground/ssr-html/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "test-ssr-html",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "node server",
- "serve": "cross-env NODE_ENV=production node server",
- "debug": "node --inspect-brk server"
- },
- "dependencies": {},
- "devDependencies": {
- "cross-env": "^7.0.3",
- "express": "^4.17.1"
- }
-}
diff --git a/packages/playground/ssr-html/server.js b/packages/playground/ssr-html/server.js
deleted file mode 100644
index ad115f1be01163..00000000000000
--- a/packages/playground/ssr-html/server.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// @ts-check
-const fs = require('fs')
-const path = require('path')
-const express = require('express')
-
-const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
-
-const DYNAMIC_SCRIPTS = `
-
-
-`
-
-async function createServer(
- root = process.cwd(),
- isProd = process.env.NODE_ENV === 'production'
-) {
- const resolve = (p) => path.resolve(__dirname, p)
-
- const app = express()
-
- /**
- * @type {import('vite').ViteDevServer}
- */
- let vite
- vite = await require('vite').createServer({
- root,
- logLevel: isTest ? 'error' : 'info',
- server: {
- middlewareMode: 'ssr',
- watch: {
- // During tests we edit the files too fast and sometimes chokidar
- // misses change events, so enforce polling for consistency
- usePolling: true,
- interval: 100
- }
- }
- })
- // use vite's connect instance as middleware
- app.use(vite.middlewares)
-
- app.use('*', async (req, res) => {
- try {
- let [url] = req.originalUrl.split('?')
- if (url.endsWith('/')) url += 'index.html'
-
- const htmlLoc = resolve(`.${url}`)
- let html = fs.readFileSync(htmlLoc, 'utf8')
- html = html.replace('', `${DYNAMIC_SCRIPTS}`)
- html = await vite.transformIndexHtml(url, html)
-
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- } catch (e) {
- vite && vite.ssrFixStacktrace(e)
- console.log(e.stack)
- res.status(500).end(e.stack)
- }
- })
-
- return { app, vite }
-}
-
-if (!isTest) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/ssr-pug/__tests__/serve.js b/packages/playground/ssr-pug/__tests__/serve.js
deleted file mode 100644
index 5ba5724f2b7a94..00000000000000
--- a/packages/playground/ssr-pug/__tests__/serve.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9530)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- const { createServer } = require(path.resolve(root, 'server.js'))
- const { app, vite } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- if (vite) {
- await vite.close()
- }
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/ssr-pug/package.json b/packages/playground/ssr-pug/package.json
deleted file mode 100644
index e2282b20565c1b..00000000000000
--- a/packages/playground/ssr-pug/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "test-ssr-pug",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "node server",
- "serve": "cross-env NODE_ENV=production node server",
- "debug": "node --inspect-brk server"
- },
- "devDependencies": {
- "cross-env": "^7.0.3",
- "express": "^4.17.1",
- "pug": "^3.0.2"
- }
-}
diff --git a/packages/playground/ssr-pug/server.js b/packages/playground/ssr-pug/server.js
deleted file mode 100644
index 3cea5c48dde00b..00000000000000
--- a/packages/playground/ssr-pug/server.js
+++ /dev/null
@@ -1,76 +0,0 @@
-// @ts-check
-const path = require('path')
-const pug = require('pug')
-const express = require('express')
-
-const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
-
-const DYNAMIC_SCRIPTS = `
-
-
-`
-
-async function createServer(
- root = process.cwd(),
- isProd = process.env.NODE_ENV === 'production'
-) {
- const resolve = (p) => path.resolve(__dirname, p)
-
- const app = express()
-
- /**
- * @type {import('vite').ViteDevServer}
- */
- let vite
- vite = await require('vite').createServer({
- root,
- logLevel: isTest ? 'error' : 'info',
- server: {
- middlewareMode: 'ssr',
- watch: {
- // During tests we edit the files too fast and sometimes chokidar
- // misses change events, so enforce polling for consistency
- usePolling: true,
- interval: 100
- }
- }
- })
- // use vite's connect instance as middleware
- app.use(vite.middlewares)
-
- app.use('*', async (req, res) => {
- try {
- let [url] = req.originalUrl.split('?')
- url = url.replace(/\.html$/, '.pug')
- if (url.endsWith('/')) url += 'index.pug'
-
- const htmlLoc = resolve(`.${url}`)
- let html = pug.renderFile(htmlLoc)
- html = html.replace('', `${DYNAMIC_SCRIPTS}`)
- html = await vite.transformIndexHtml(url, html)
-
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- } catch (e) {
- vite && vite.ssrFixStacktrace(e)
- console.log(e.stack)
- res.status(500).end(e.stack)
- }
- })
-
- return { app, vite }
-}
-
-if (!isTest) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/ssr-pug/src/app.js b/packages/playground/ssr-pug/src/app.js
deleted file mode 100644
index 5b0175bb863d70..00000000000000
--- a/packages/playground/ssr-pug/src/app.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const p = document.createElement('p')
-p.innerHTML = '✅ Dynamically injected script from file'
-document.body.appendChild(p)
diff --git a/packages/playground/ssr-react/__tests__/serve.js b/packages/playground/ssr-react/__tests__/serve.js
deleted file mode 100644
index 1bc028c03dc27c..00000000000000
--- a/packages/playground/ssr-react/__tests__/serve.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9528)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- if (isProd) {
- // build first
- const { build } = require('vite')
- // client build
- await build({
- root,
- logLevel: 'silent', // exceptions are logged by Jest
- build: {
- target: 'esnext',
- minify: false,
- ssrManifest: true,
- outDir: 'dist/client'
- }
- })
- // server build
- await build({
- root,
- logLevel: 'silent',
- build: {
- target: 'esnext',
- ssr: 'src/entry-server.jsx',
- outDir: 'dist/server'
- }
- })
- }
-
- const { createServer } = require(path.resolve(root, 'server.js'))
- const { app, vite } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- if (vite) {
- await vite.close()
- }
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/ssr-react/__tests__/ssr-react.spec.ts b/packages/playground/ssr-react/__tests__/ssr-react.spec.ts
deleted file mode 100644
index 2235d4ae4d0edf..00000000000000
--- a/packages/playground/ssr-react/__tests__/ssr-react.spec.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { editFile, untilUpdated } from '../../testUtils'
-import { port } from './serve'
-import fetch from 'node-fetch'
-
-const url = `http://localhost:${port}`
-
-test('/env', async () => {
- await page.goto(url + '/env')
- expect(await page.textContent('h1')).toMatch('default message here')
-
- // raw http request
- const envHtml = await (await fetch(url + '/env')).text()
- expect(envHtml).toMatch('API_KEY_qwertyuiop')
-})
-
-test('/about', async () => {
- await page.goto(url + '/about')
- expect(await page.textContent('h1')).toMatch('About')
- // should not have hydration mismatch
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('Expected server HTML')
- })
-
- // raw http request
- const aboutHtml = await (await fetch(url + '/about')).text()
- expect(aboutHtml).toMatch('About')
-})
-
-test('/', async () => {
- await page.goto(url)
- expect(await page.textContent('h1')).toMatch('Home')
- // should not have hydration mismatch
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('Expected server HTML')
- })
-
- // raw http request
- const html = await (await fetch(url)).text()
- expect(html).toMatch('Home')
-})
-
-test('hmr', async () => {
- editFile('src/pages/Home.jsx', (code) =>
- code.replace('Home', 'changed')
- )
- await untilUpdated(() => page.textContent('h1'), 'changed')
-})
-
-test('client navigation', async () => {
- await untilUpdated(() => page.textContent('a[href="/about"]'), 'About')
- await page.click('a[href="/about"]')
- await untilUpdated(() => page.textContent('h1'), 'About')
- editFile('src/pages/About.jsx', (code) =>
- code.replace('About', 'changed')
- )
- await untilUpdated(() => page.textContent('h1'), 'changed')
-})
-
-test(`circular dependecies modules doesn't throw`, async () => {
- await page.goto(url)
- expect(await page.textContent('.circ-dep-init')).toMatch(
- 'circ-dep-init-a circ-dep-init-b'
- )
-})
diff --git a/packages/playground/ssr-react/index.html b/packages/playground/ssr-react/index.html
deleted file mode 100644
index 1c891c04355068..00000000000000
--- a/packages/playground/ssr-react/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Vite App
-
-
-
-
-
-
diff --git a/packages/playground/ssr-react/package.json b/packages/playground/ssr-react/package.json
deleted file mode 100644
index a05bcc08806f3b..00000000000000
--- a/packages/playground/ssr-react/package.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "test-ssr-react",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "node server",
- "build": "npm run build:client && npm run build:server",
- "build:client": "vite build --outDir dist/client",
- "build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server",
- "generate": "vite build --outDir dist/static && npm run build:server && node prerender",
- "serve": "cross-env NODE_ENV=production node server",
- "debug": "node --inspect-brk server"
- },
- "dependencies": {
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
- "react-router": "^5.2.1",
- "react-router-dom": "^5.3.0"
- },
- "devDependencies": {
- "@vitejs/plugin-react": "workspace:*",
- "compression": "^1.7.4",
- "cross-env": "^7.0.3",
- "express": "^4.17.1",
- "serve-static": "^1.14.1"
- }
-}
diff --git a/packages/playground/ssr-react/prerender.js b/packages/playground/ssr-react/prerender.js
deleted file mode 100644
index ac88ef632ec6f5..00000000000000
--- a/packages/playground/ssr-react/prerender.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Pre-render the app into static HTML.
-// run `yarn generate` and then `dist/static` can be served as a static site.
-
-const fs = require('fs')
-const path = require('path')
-
-const toAbsolute = (p) => path.resolve(__dirname, p)
-
-const template = fs.readFileSync(toAbsolute('dist/static/index.html'), 'utf-8')
-const { render } = require('./dist/server/entry-server.js')
-
-// determine routes to pre-render from src/pages
-const routesToPrerender = fs
- .readdirSync(toAbsolute('src/pages'))
- .map((file) => {
- const name = file.replace(/\.jsx$/, '').toLowerCase()
- return name === 'home' ? `/` : `/${name}`
- })
-
-;(async () => {
- // pre-render each route...
- for (const url of routesToPrerender) {
- const context = {}
- const appHtml = await render(url, context)
-
- const html = template.replace(``, appHtml)
-
- const filePath = `dist/static${url === '/' ? '/index' : url}.html`
- fs.writeFileSync(toAbsolute(filePath), html)
- console.log('pre-rendered:', filePath)
- }
-})()
diff --git a/packages/playground/ssr-react/server.js b/packages/playground/ssr-react/server.js
deleted file mode 100644
index 1876439c18fa88..00000000000000
--- a/packages/playground/ssr-react/server.js
+++ /dev/null
@@ -1,96 +0,0 @@
-// @ts-check
-const fs = require('fs')
-const path = require('path')
-const express = require('express')
-
-const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
-
-process.env.MY_CUSTOM_SECRET = 'API_KEY_qwertyuiop'
-
-async function createServer(
- root = process.cwd(),
- isProd = process.env.NODE_ENV === 'production'
-) {
- const resolve = (p) => path.resolve(__dirname, p)
-
- const indexProd = isProd
- ? fs.readFileSync(resolve('dist/client/index.html'), 'utf-8')
- : ''
-
- const app = express()
-
- /**
- * @type {import('vite').ViteDevServer}
- */
- let vite
- if (!isProd) {
- vite = await require('vite').createServer({
- root,
- logLevel: isTest ? 'error' : 'info',
- server: {
- middlewareMode: 'ssr',
- watch: {
- // During tests we edit the files too fast and sometimes chokidar
- // misses change events, so enforce polling for consistency
- usePolling: true,
- interval: 100
- }
- }
- })
- // use vite's connect instance as middleware
- app.use(vite.middlewares)
- } else {
- app.use(require('compression')())
- app.use(
- require('serve-static')(resolve('dist/client'), {
- index: false
- })
- )
- }
-
- app.use('*', async (req, res) => {
- try {
- const url = req.originalUrl
-
- let template, render
- if (!isProd) {
- // always read fresh template in dev
- template = fs.readFileSync(resolve('index.html'), 'utf-8')
- template = await vite.transformIndexHtml(url, template)
- render = (await vite.ssrLoadModule('/src/entry-server.jsx')).render
- } else {
- template = indexProd
- render = require('./dist/server/entry-server.js').render
- }
-
- const context = {}
- const appHtml = render(url, context)
-
- if (context.url) {
- // Somewhere a `` was rendered
- return res.redirect(301, context.url)
- }
-
- const html = template.replace(``, appHtml)
-
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- } catch (e) {
- !isProd && vite.ssrFixStacktrace(e)
- console.log(e.stack)
- res.status(500).end(e.stack)
- }
- })
-
- return { app, vite }
-}
-
-if (!isTest) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/ssr-react/src/App.jsx b/packages/playground/ssr-react/src/App.jsx
deleted file mode 100644
index 1c598add666efb..00000000000000
--- a/packages/playground/ssr-react/src/App.jsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Link, Route, Switch } from 'react-router-dom'
-
-// Auto generates routes from files under ./pages
-// https://vitejs.dev/guide/features.html#glob-import
-const pages = import.meta.globEager('./pages/*.jsx')
-
-const routes = Object.keys(pages).map((path) => {
- const name = path.match(/\.\/pages\/(.*)\.jsx$/)[1]
- return {
- name,
- path: name === 'Home' ? '/' : `/${name.toLowerCase()}`,
- component: pages[path].default
- }
-})
-
-export function App() {
- return (
- <>
-
-
- {routes.map(({ name, path }) => {
- return (
-
- {name}
-
- )
- })}
-
-
-
- {routes.map(({ path, component: RouteComp }) => {
- return (
-
-
-
- )
- })}
-
- >
- )
-}
diff --git a/packages/playground/ssr-react/src/add.js b/packages/playground/ssr-react/src/add.js
deleted file mode 100644
index a0e419e9cfcacf..00000000000000
--- a/packages/playground/ssr-react/src/add.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { multiply } from './multiply'
-
-export function add(a, b) {
- return a + b
-}
-
-export function addAndMultiply(a, b, c) {
- return multiply(add(a, b), c)
-}
diff --git a/packages/playground/ssr-react/src/entry-client.jsx b/packages/playground/ssr-react/src/entry-client.jsx
deleted file mode 100644
index 8757bdc929d0e4..00000000000000
--- a/packages/playground/ssr-react/src/entry-client.jsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import ReactDOM from 'react-dom'
-import { BrowserRouter } from 'react-router-dom'
-import { App } from './App'
-
-ReactDOM.hydrate(
-
-
- ,
- document.getElementById('app')
-)
diff --git a/packages/playground/ssr-react/src/entry-server.jsx b/packages/playground/ssr-react/src/entry-server.jsx
deleted file mode 100644
index 56d4810d11ba3c..00000000000000
--- a/packages/playground/ssr-react/src/entry-server.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import ReactDOMServer from 'react-dom/server'
-import { StaticRouter } from 'react-router-dom'
-import { App } from './App'
-
-export function render(url, context) {
- return ReactDOMServer.renderToString(
-
-
-
- )
-}
diff --git a/packages/playground/ssr-react/src/forked-deadlock/README.md b/packages/playground/ssr-react/src/forked-deadlock/README.md
deleted file mode 100644
index 798c8c429ee9e4..00000000000000
--- a/packages/playground/ssr-react/src/forked-deadlock/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-This test aim to check for a particular type of circular dependency that causes tricky deadlocks, **deadlocks with forked imports stack**
-
-```
-A -> B means: B is imported by A and B has A in its stack
-A ... B means: A is waiting for B to ssrLoadModule()
-
-H -> X ... Y
-H -> X -> Y ... B
-H -> A ... B
-H -> A -> B ... X
-```
-
-### Forked deadlock description:
-
-```
-[X] is waiting for [Y] to resolve
- ↑ ↳ is waiting for [A] to resolve
- │ ↳ is waiting for [B] to resolve
- │ ↳ is waiting for [X] to resolve
- └────────────────────────────────────────────────────────────────────────┘
-```
-
-This may seems a traditional deadlock, but the thing that makes this special is the import stack of each module:
-
-```
-[X] stack:
- [H]
-```
-
-```
-[Y] stack:
- [X]
- [H]
-```
-
-```
-[A] stack:
- [H]
-```
-
-```
-[B] stack:
- [A]
- [H]
-```
-
-Even if `[X]` is imported by `[B]`, `[B]` is not in `[X]`'s stack because it's imported by `[H]` in first place then it's stack is only composed by `[H]`. `[H]` **forks** the imports **stack** and this make hard to be found.
-
-### Fix description
-
-Vite, when imports `[X]`, should check whether `[X]` is already pending and if it is, it must check that, when it was imported in first place, the stack of `[X]` doesn't have any module in common with the current module; in this case `[B]` has the module `[H]` is common with `[X]` and i can assume that a deadlock is going to happen.
diff --git a/packages/playground/ssr-react/src/multiply.js b/packages/playground/ssr-react/src/multiply.js
deleted file mode 100644
index 94f43efbff58bd..00000000000000
--- a/packages/playground/ssr-react/src/multiply.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { add } from './add'
-
-export function multiply(a, b) {
- return a * b
-}
-
-export function multiplyAndAdd(a, b, c) {
- return add(multiply(a, b), c)
-}
diff --git a/packages/playground/ssr-react/src/pages/About.jsx b/packages/playground/ssr-react/src/pages/About.jsx
deleted file mode 100644
index 0fe4de69078504..00000000000000
--- a/packages/playground/ssr-react/src/pages/About.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { addAndMultiply } from '../add'
-import { multiplyAndAdd } from '../multiply'
-
-export default function About() {
- return (
- <>
- About
- {addAndMultiply(1, 2, 3)}
- {multiplyAndAdd(1, 2, 3)}
- >
- )
-}
diff --git a/packages/playground/ssr-react/src/pages/Env.jsx b/packages/playground/ssr-react/src/pages/Env.jsx
deleted file mode 100644
index 1102990f11c8cb..00000000000000
--- a/packages/playground/ssr-react/src/pages/Env.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function Env() {
- let msg = 'default message here'
- try {
- msg = process.env.MY_CUSTOM_SECRET || msg
- } catch {}
- return {msg}
-}
diff --git a/packages/playground/ssr-react/src/pages/Home.jsx b/packages/playground/ssr-react/src/pages/Home.jsx
deleted file mode 100644
index d1f4944810cc98..00000000000000
--- a/packages/playground/ssr-react/src/pages/Home.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { addAndMultiply } from '../add'
-import { multiplyAndAdd } from '../multiply'
-import { commonModuleExport } from '../forked-deadlock/common-module'
-import { getValueAB } from '../circular-dep-init/circular-dep-init'
-
-export default function Home() {
- commonModuleExport()
-
- return (
- <>
- Home
- {addAndMultiply(1, 2, 3)}
- {multiplyAndAdd(1, 2, 3)}
- {getValueAB()}
- >
- )
-}
diff --git a/packages/playground/ssr-react/vite.config.js b/packages/playground/ssr-react/vite.config.js
deleted file mode 100644
index bcc1369313cc5a..00000000000000
--- a/packages/playground/ssr-react/vite.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const react = require('@vitejs/plugin-react')
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- plugins: [react()],
- build: {
- minify: false
- }
-}
diff --git a/packages/playground/ssr-vue/__tests__/serve.js b/packages/playground/ssr-vue/__tests__/serve.js
deleted file mode 100644
index 1e220fed9630e4..00000000000000
--- a/packages/playground/ssr-vue/__tests__/serve.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9527)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- if (isProd) {
- // build first
- const { build } = require('vite')
- // client build
- await build({
- root,
- logLevel: 'silent', // exceptions are logged by Jest
- build: {
- target: 'esnext',
- minify: false,
- ssrManifest: true,
- outDir: 'dist/client'
- }
- })
- // server build
- await build({
- root,
- logLevel: 'silent',
- build: {
- target: 'esnext',
- ssr: 'src/entry-server.js',
- outDir: 'dist/server'
- }
- })
- }
-
- const { createServer } = require(path.resolve(root, 'server.js'))
- const { app, vite } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- if (vite) {
- await vite.close()
- }
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts b/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
deleted file mode 100644
index 952e287a7f12aa..00000000000000
--- a/packages/playground/ssr-vue/__tests__/ssr-vue.spec.ts
+++ /dev/null
@@ -1,182 +0,0 @@
-import { editFile, getColor, isBuild, untilUpdated } from '../../testUtils'
-import { port } from './serve'
-import fetch from 'node-fetch'
-import { resolve } from 'path'
-
-const url = `http://localhost:${port}`
-
-test('vuex can be import succeed by named import', async () => {
- await page.goto(url + '/store')
- expect(await page.textContent('h1')).toMatch('bar')
-
- // raw http request
- const storeHtml = await (await fetch(url + '/store')).text()
- expect(storeHtml).toMatch('bar')
-})
-
-test('/about', async () => {
- await page.goto(url + '/about')
- expect(await page.textContent('h1')).toMatch('About')
- // should not have hydration mismatch
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('mismatch')
- })
-
- // fetch sub route
- const aboutHtml = await (await fetch(url + '/about')).text()
- expect(aboutHtml).toMatch('About')
- if (isBuild) {
- // assert correct preload directive generation for async chunks and CSS
- expect(aboutHtml).not.toMatch(
- /link rel="modulepreload".*?href="\/assets\/Home\.\w{8}\.js"/
- )
- expect(aboutHtml).not.toMatch(
- /link rel="stylesheet".*?href="\/assets\/Home\.\w{8}\.css"/
- )
- expect(aboutHtml).toMatch(
- /link rel="modulepreload".*?href="\/assets\/About\.\w{8}\.js"/
- )
- expect(aboutHtml).toMatch(
- /link rel="stylesheet".*?href="\/assets\/About\.\w{8}\.css"/
- )
- }
-})
-
-test('/external', async () => {
- await page.goto(url + '/external')
- expect(await page.textContent('div')).toMatch(
- 'Example external component content'
- )
- // should not have hydration mismatch
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('mismatch')
- })
-
- // fetch sub route
- const externalHtml = await (await fetch(url + '/external')).text()
- expect(externalHtml).toMatch('Example external component content')
- if (isBuild) {
- // assert correct preload directive generation for async chunks and CSS
- expect(externalHtml).not.toMatch(
- /link rel="modulepreload".*?href="\/assets\/Home\.\w{8}\.js"/
- )
- expect(externalHtml).not.toMatch(
- /link rel="stylesheet".*?href="\/assets\/Home\.\w{8}\.css"/
- )
- expect(externalHtml).toMatch(
- /link rel="modulepreload".*?href="\/assets\/External\.\w{8}\.js"/
- )
- }
-})
-
-test('/', async () => {
- await page.goto(url)
- expect(await page.textContent('h1')).toMatch('Home')
- // should not have hydration mismatch
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('mismatch')
- })
-
- const html = await (await fetch(url)).text()
- expect(html).toMatch('Home')
- if (isBuild) {
- // assert correct preload directive generation for async chunks and CSS
- expect(html).toMatch(
- /link rel="modulepreload".*?href="\/assets\/Home\.\w{8}\.js"/
- )
- expect(html).toMatch(
- /link rel="stylesheet".*?href="\/assets\/Home\.\w{8}\.css"/
- )
- // JSX component preload registration
- expect(html).toMatch(
- /link rel="modulepreload".*?href="\/assets\/Foo\.\w{8}\.js"/
- )
- expect(html).toMatch(
- /link rel="stylesheet".*?href="\/assets\/Foo\.\w{8}\.css"/
- )
- expect(html).not.toMatch(
- /link rel="modulepreload".*?href="\/assets\/About\.\w{8}\.js"/
- )
- expect(html).not.toMatch(
- /link rel="stylesheet".*?href="\/assets\/About\.\w{8}\.css"/
- )
- }
-})
-
-test('css', async () => {
- if (isBuild) {
- expect(await getColor('h1')).toBe('green')
- expect(await getColor('.jsx')).toBe('blue')
- } else {
- // During dev, the CSS is loaded from async chunk and we may have to wait
- // when the test runs concurrently.
- await untilUpdated(() => getColor('h1'), 'green')
- await untilUpdated(() => getColor('.jsx'), 'blue')
- }
-})
-
-test('asset', async () => {
- // should have no 404s
- browserLogs.forEach((msg) => {
- expect(msg).not.toMatch('404')
- })
- const img = await page.$('img')
- expect(await img.getAttribute('src')).toMatch(
- isBuild ? /\/assets\/logo\.\w{8}\.png/ : '/src/assets/logo.png'
- )
-})
-
-test('jsx', async () => {
- expect(await page.textContent('.jsx')).toMatch('from JSX')
-})
-
-test('virtual module', async () => {
- expect(await page.textContent('.virtual')).toMatch('hi')
-})
-
-test('nested virtual module', async () => {
- expect(await page.textContent('.nested-virtual')).toMatch('[success]')
-})
-
-test('hydration', async () => {
- expect(await page.textContent('button')).toMatch('0')
- await page.click('button')
- expect(await page.textContent('button')).toMatch('1')
-})
-
-test('hmr', async () => {
- editFile('src/pages/Home.vue', (code) => code.replace('Home', 'changed'))
- await untilUpdated(() => page.textContent('h1'), 'changed')
-})
-
-test('client navigation', async () => {
- await untilUpdated(() => page.textContent('a[href="/about"]'), 'About')
- await page.click('a[href="/about"]')
- await untilUpdated(() => page.textContent('h1'), 'About')
- editFile('src/pages/About.vue', (code) => code.replace('About', 'changed'))
- await untilUpdated(() => page.textContent('h1'), 'changed')
- await page.click('a[href="/"]')
- await untilUpdated(() => page.textContent('a[href="/"]'), 'Home')
-})
-
-test('import.meta.url', async () => {
- await page.goto(url)
- expect(await page.textContent('.protocol')).toEqual('file:')
-})
-
-test('dynamic css file should be preloaded', async () => {
- if (isBuild) {
- await page.goto(url)
- const homeHtml = await (await fetch(url)).text()
- const re = /link rel="modulepreload".*?href="\/assets\/(Home\.\w{8}\.js)"/
- const filename = re.exec(homeHtml)[1]
- const manifest = require(resolve(
- process.cwd(),
- './packages/temp/ssr-vue/dist/client/ssr-manifest.json'
- ))
- const depFile = manifest[filename]
- for (const file of depFile) {
- expect(homeHtml).toMatch(file)
- }
- }
-})
diff --git a/packages/playground/ssr-vue/dep-import-type/deep/index.d.ts b/packages/playground/ssr-vue/dep-import-type/deep/index.d.ts
deleted file mode 100644
index 39df3b83f7e9b8..00000000000000
--- a/packages/playground/ssr-vue/dep-import-type/deep/index.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-export interface Foo {}
diff --git a/packages/playground/ssr-vue/dep-import-type/package.json b/packages/playground/ssr-vue/dep-import-type/package.json
deleted file mode 100644
index 935f28eb7f7157..00000000000000
--- a/packages/playground/ssr-vue/dep-import-type/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "dep-import-type",
- "private": true,
- "version": "0.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/ssr-vue/example-external-component/ExampleExternalComponent.vue b/packages/playground/ssr-vue/example-external-component/ExampleExternalComponent.vue
deleted file mode 100644
index 55f3cea40e0399..00000000000000
--- a/packages/playground/ssr-vue/example-external-component/ExampleExternalComponent.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Example external component content
-
diff --git a/packages/playground/ssr-vue/example-external-component/index.js b/packages/playground/ssr-vue/example-external-component/index.js
deleted file mode 100644
index 8fc72c3aee0652..00000000000000
--- a/packages/playground/ssr-vue/example-external-component/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import ExampleExternalComponent from './ExampleExternalComponent.vue'
-
-export default ExampleExternalComponent
diff --git a/packages/playground/ssr-vue/example-external-component/package.json b/packages/playground/ssr-vue/example-external-component/package.json
deleted file mode 100644
index 302e7fd4d9ff05..00000000000000
--- a/packages/playground/ssr-vue/example-external-component/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "example-external-component",
- "private": true,
- "version": "0.0.0",
- "main": "index.js"
-}
diff --git a/packages/playground/ssr-vue/index.html b/packages/playground/ssr-vue/index.html
deleted file mode 100644
index 17b46a2f7a2267..00000000000000
--- a/packages/playground/ssr-vue/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
- Vite App
-
-
-
-
-
-
-
diff --git a/packages/playground/ssr-vue/package.json b/packages/playground/ssr-vue/package.json
deleted file mode 100644
index 4a385336a97603..00000000000000
--- a/packages/playground/ssr-vue/package.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "name": "test-ssr-vue",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "node server",
- "build": "npm run build:client && npm run build:server",
- "build:noExternal": "npm run build:client && npm run build:server:noExternal",
- "build:client": "vite build --ssrManifest --outDir dist/client",
- "build:server": "vite build --ssr src/entry-server.js --outDir dist/server",
- "build:server:noExternal": "vite build --config vite.config.noexternal.js --ssr src/entry-server.js --outDir dist/server",
- "generate": "vite build --ssrManifest --outDir dist/static && npm run build:server && node prerender",
- "serve": "cross-env NODE_ENV=production node server",
- "debug": "node --inspect-brk server"
- },
- "dependencies": {
- "example-external-component": "file:example-external-component",
- "vue": "^3.2.25",
- "vue-router": "^4.0.0",
- "vuex": "^4.0.2"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*",
- "@vitejs/plugin-vue-jsx": "workspace:*",
- "compression": "^1.7.4",
- "cross-env": "^7.0.3",
- "dep-import-type": "link:./dep-import-type",
- "express": "^4.17.1",
- "serve-static": "^1.14.1"
- }
-}
diff --git a/packages/playground/ssr-vue/prerender.js b/packages/playground/ssr-vue/prerender.js
deleted file mode 100644
index c4158dbe3357a9..00000000000000
--- a/packages/playground/ssr-vue/prerender.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Pre-render the app into static HTML.
-// run `npm run generate` and then `dist/static` can be served as a static site.
-
-const fs = require('fs')
-const path = require('path')
-
-const toAbsolute = (p) => path.resolve(__dirname, p)
-
-const manifest = require('./dist/static/ssr-manifest.json')
-const template = fs.readFileSync(toAbsolute('dist/static/index.html'), 'utf-8')
-const { render } = require('./dist/server/entry-server.js')
-
-// determine routes to pre-render from src/pages
-const routesToPrerender = fs
- .readdirSync(toAbsolute('src/pages'))
- .map((file) => {
- const name = file.replace(/\.vue$/, '').toLowerCase()
- return name === 'home' ? `/` : `/${name}`
- })
-
-;(async () => {
- // pre-render each route...
- for (const url of routesToPrerender) {
- const [appHtml, preloadLinks] = await render(url, manifest)
-
- const html = template
- .replace(``, preloadLinks)
- .replace(``, appHtml)
-
- const filePath = `dist/static${url === '/' ? '/index' : url}.html`
- fs.writeFileSync(toAbsolute(filePath), html)
- console.log('pre-rendered:', filePath)
- }
-
- // done, delete ssr manifest
- fs.unlinkSync(toAbsolute('dist/static/ssr-manifest.json'))
-})()
diff --git a/packages/playground/ssr-vue/server.js b/packages/playground/ssr-vue/server.js
deleted file mode 100644
index 642f274647294f..00000000000000
--- a/packages/playground/ssr-vue/server.js
+++ /dev/null
@@ -1,95 +0,0 @@
-// @ts-check
-const fs = require('fs')
-const path = require('path')
-const express = require('express')
-
-const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
-
-async function createServer(
- root = process.cwd(),
- isProd = process.env.NODE_ENV === 'production'
-) {
- const resolve = (p) => path.resolve(__dirname, p)
-
- const indexProd = isProd
- ? fs.readFileSync(resolve('dist/client/index.html'), 'utf-8')
- : ''
-
- const manifest = isProd
- ? // @ts-ignore
- require('./dist/client/ssr-manifest.json')
- : {}
-
- const app = express()
-
- /**
- * @type {import('vite').ViteDevServer}
- */
- let vite
- if (!isProd) {
- vite = await require('vite').createServer({
- root,
- logLevel: isTest ? 'error' : 'info',
- server: {
- middlewareMode: 'ssr',
- watch: {
- // During tests we edit the files too fast and sometimes chokidar
- // misses change events, so enforce polling for consistency
- usePolling: true,
- interval: 100
- }
- }
- })
- // use vite's connect instance as middleware
- app.use(vite.middlewares)
- } else {
- app.use(require('compression')())
- app.use(
- require('serve-static')(resolve('dist/client'), {
- index: false
- })
- )
- }
-
- app.use('*', async (req, res) => {
- try {
- const url = req.originalUrl
-
- let template, render
- if (!isProd) {
- // always read fresh template in dev
- template = fs.readFileSync(resolve('index.html'), 'utf-8')
- template = await vite.transformIndexHtml(url, template)
- render = (await vite.ssrLoadModule('/src/entry-server.js')).render
- } else {
- template = indexProd
- render = require('./dist/server/entry-server.js').render
- }
-
- const [appHtml, preloadLinks] = await render(url, manifest)
-
- const html = template
- .replace(``, preloadLinks)
- .replace(``, appHtml)
-
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
- } catch (e) {
- vite && vite.ssrFixStacktrace(e)
- console.log(e.stack)
- res.status(500).end(e.stack)
- }
- })
-
- return { app, vite }
-}
-
-if (!isTest) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/ssr-vue/src/App.vue b/packages/playground/ssr-vue/src/App.vue
deleted file mode 100644
index dc8bfca16a59ab..00000000000000
--- a/packages/playground/ssr-vue/src/App.vue
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
- Home |
- About
-
-
-
-
-
-
-
-
-
diff --git a/packages/playground/ssr-vue/src/assets/button.css b/packages/playground/ssr-vue/src/assets/button.css
deleted file mode 100644
index 8e1ebc58c0891f..00000000000000
--- a/packages/playground/ssr-vue/src/assets/button.css
+++ /dev/null
@@ -1,15 +0,0 @@
-.btn {
- background-color: #65b587;
- border-radius: 8px;
- border-style: none;
- box-sizing: border-box;
- cursor: pointer;
- display: inline-block;
- font-size: 14px;
- font-weight: 500;
- height: 40px;
- line-height: 20px;
- list-style: none;
- outline: none;
- padding: 10px 16px;
-}
diff --git a/packages/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff b/packages/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff
deleted file mode 100644
index e7da6663fe5e47..00000000000000
Binary files a/packages/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff and /dev/null differ
diff --git a/packages/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff2 b/packages/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff2
deleted file mode 100644
index 8559dfde38986e..00000000000000
Binary files a/packages/playground/ssr-vue/src/assets/fonts/Inter-Italic.woff2 and /dev/null differ
diff --git a/packages/playground/ssr-vue/src/assets/logo.png b/packages/playground/ssr-vue/src/assets/logo.png
deleted file mode 100644
index f3d2503fc2a44b..00000000000000
Binary files a/packages/playground/ssr-vue/src/assets/logo.png and /dev/null differ
diff --git a/packages/playground/ssr-vue/src/components/Foo.jsx b/packages/playground/ssr-vue/src/components/Foo.jsx
deleted file mode 100644
index 427815b2d252d2..00000000000000
--- a/packages/playground/ssr-vue/src/components/Foo.jsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { defineComponent } from 'vue'
-import './foo.css'
-
-// named exports w/ variable declaration: ok
-export const Foo = defineComponent({
- name: 'foo',
- setup() {
- return () => from JSX
- }
-})
diff --git a/packages/playground/ssr-vue/src/components/ImportType.vue b/packages/playground/ssr-vue/src/components/ImportType.vue
deleted file mode 100644
index 144d36bc34e7ec..00000000000000
--- a/packages/playground/ssr-vue/src/components/ImportType.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-
- import type should be removed without side-effect
-
-
-
diff --git a/packages/playground/ssr-vue/src/components/button.js b/packages/playground/ssr-vue/src/components/button.js
deleted file mode 100644
index 3b39f53fd96c47..00000000000000
--- a/packages/playground/ssr-vue/src/components/button.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { createVNode, defineComponent } from 'vue'
-import '../assets/button.css'
-
-export default defineComponent({
- setup() {
- return () => {
- return createVNode(
- 'div',
- {
- class: 'btn'
- },
- 'dynamicBtn'
- )
- }
- }
-})
diff --git a/packages/playground/ssr-vue/src/components/foo.css b/packages/playground/ssr-vue/src/components/foo.css
deleted file mode 100644
index f8baa0d15b90d3..00000000000000
--- a/packages/playground/ssr-vue/src/components/foo.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.jsx {
- color: blue;
-}
diff --git a/packages/playground/ssr-vue/src/entry-client.js b/packages/playground/ssr-vue/src/entry-client.js
deleted file mode 100644
index 842acce7dc685b..00000000000000
--- a/packages/playground/ssr-vue/src/entry-client.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { createApp } from './main'
-
-const { app, router } = createApp()
-
-// wait until router is ready before mounting to ensure hydration match
-router.isReady().then(() => {
- app.mount('#app')
-})
diff --git a/packages/playground/ssr-vue/src/entry-server.js b/packages/playground/ssr-vue/src/entry-server.js
deleted file mode 100644
index 0f4e47711c17a1..00000000000000
--- a/packages/playground/ssr-vue/src/entry-server.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { createApp } from './main'
-import { renderToString } from 'vue/server-renderer'
-import path, { basename } from 'path'
-
-export async function render(url, manifest) {
- const { app, router } = createApp()
-
- // set the router to the desired URL before rendering
- router.push(url)
- await router.isReady()
-
- // passing SSR context object which will be available via useSSRContext()
- // @vitejs/plugin-vue injects code into a component's setup() that registers
- // itself on ctx.modules. After the render, ctx.modules would contain all the
- // components that have been instantiated during this render call.
- const ctx = {}
- const html = await renderToString(app, ctx)
-
- // the SSR manifest generated by Vite contains module -> chunk/asset mapping
- // which we can then use to determine what files need to be preloaded for this
- // request.
- const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
- return [html, preloadLinks]
-}
-
-function renderPreloadLinks(modules, manifest) {
- let links = ''
- const seen = new Set()
- modules.forEach((id) => {
- const files = manifest[id]
- if (files) {
- files.forEach((file) => {
- if (!seen.has(file)) {
- seen.add(file)
- const filename = basename(file)
- if (manifest[filename]) {
- for (const depFile of manifest[filename]) {
- links += renderPreloadLink(depFile)
- seen.add(depFile)
- }
- }
- links += renderPreloadLink(file)
- }
- })
- }
- })
- return links
-}
-
-function renderPreloadLink(file) {
- if (file.endsWith('.js')) {
- return ` `
- } else if (file.endsWith('.css')) {
- return ` `
- } else if (file.endsWith('.woff')) {
- return ` `
- } else if (file.endsWith('.woff2')) {
- return ` `
- } else if (file.endsWith('.gif')) {
- return ` `
- } else if (file.endsWith('.jpg') || file.endsWith('.jpeg')) {
- return ` `
- } else if (file.endsWith('.png')) {
- return ` `
- } else {
- // TODO
- return ''
- }
-}
diff --git a/packages/playground/ssr-vue/src/main.js b/packages/playground/ssr-vue/src/main.js
deleted file mode 100644
index dbf4287b0baf3c..00000000000000
--- a/packages/playground/ssr-vue/src/main.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import App from './App.vue'
-import { createSSRApp } from 'vue'
-import { createRouter } from './router'
-
-// SSR requires a fresh app instance per request, therefore we export a function
-// that creates a fresh app instance. If using Vuex, we'd also be creating a
-// fresh store here.
-export function createApp() {
- const app = createSSRApp(App)
- const router = createRouter()
- app.use(router)
- return { app, router }
-}
diff --git a/packages/playground/ssr-vue/src/pages/About.vue b/packages/playground/ssr-vue/src/pages/About.vue
deleted file mode 100644
index 2c8589f7ff109a..00000000000000
--- a/packages/playground/ssr-vue/src/pages/About.vue
+++ /dev/null
@@ -1,30 +0,0 @@
-
- {{ msg }}
- {{ url }}
- CommonButton
-
-
-
-
-
diff --git a/packages/playground/ssr-vue/src/pages/External.vue b/packages/playground/ssr-vue/src/pages/External.vue
deleted file mode 100644
index ffdcd03b85be84..00000000000000
--- a/packages/playground/ssr-vue/src/pages/External.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/packages/playground/ssr-vue/src/pages/Home.vue b/packages/playground/ssr-vue/src/pages/Home.vue
deleted file mode 100644
index 32a33882cc2324..00000000000000
--- a/packages/playground/ssr-vue/src/pages/Home.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
- Home
-
-
-
- count is: {{ state.count }}
-
- msg from virtual module: {{ foo.msg }}
- this will be styled with a font-face
- {{ state.url }}
- {{ state.protocol }}
- msg from nested virtual module: {{ virtualMsg }}
- CommonButton
-
-
-
-
-
-
-
-
diff --git a/packages/playground/ssr-vue/src/pages/Store.vue b/packages/playground/ssr-vue/src/pages/Store.vue
deleted file mode 100644
index df4d6b302d8474..00000000000000
--- a/packages/playground/ssr-vue/src/pages/Store.vue
+++ /dev/null
@@ -1,24 +0,0 @@
-
- {{ foo }}
-
-
-
-
-
diff --git a/packages/playground/ssr-vue/src/router.js b/packages/playground/ssr-vue/src/router.js
deleted file mode 100644
index b80b76b0bf4e2a..00000000000000
--- a/packages/playground/ssr-vue/src/router.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import {
- createMemoryHistory,
- createRouter as _createRouter,
- createWebHistory
-} from 'vue-router'
-
-// Auto generates routes from vue files under ./pages
-// https://vitejs.dev/guide/features.html#glob-import
-const pages = import.meta.glob('./pages/*.vue')
-
-const routes = Object.keys(pages).map((path) => {
- const name = path.match(/\.\/pages(.*)\.vue$/)[1].toLowerCase()
- return {
- path: name === '/home' ? '/' : name,
- component: pages[path] // () => import('./pages/*.vue')
- }
-})
-
-export function createRouter() {
- return _createRouter({
- // use appropriate history implementation for server/client
- // import.meta.env.SSR is injected by Vite.
- history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
- routes
- })
-}
diff --git a/packages/playground/ssr-vue/vite.config.js b/packages/playground/ssr-vue/vite.config.js
deleted file mode 100644
index 0adfa551b3b134..00000000000000
--- a/packages/playground/ssr-vue/vite.config.js
+++ /dev/null
@@ -1,49 +0,0 @@
-const vuePlugin = require('@vitejs/plugin-vue')
-const vueJsx = require('@vitejs/plugin-vue-jsx')
-const virtualFile = '@virtual-file'
-const virtualId = '\0' + virtualFile
-const nestedVirtualFile = '@nested-virtual-file'
-const nestedVirtualId = '\0' + nestedVirtualFile
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- plugins: [
- vuePlugin(),
- vueJsx(),
- {
- name: 'virtual',
- resolveId(id) {
- if (id === '@foo') {
- return id
- }
- },
- load(id) {
- if (id === '@foo') {
- return `export default { msg: 'hi' }`
- }
- }
- },
- {
- name: 'virtual-module',
- resolveId(id) {
- if (id === virtualFile) {
- return virtualId
- } else if (id === nestedVirtualFile) {
- return nestedVirtualId
- }
- },
- load(id) {
- if (id === virtualId) {
- return `export { msg } from "@nested-virtual-file";`
- } else if (id === nestedVirtualId) {
- return `export const msg = "[success] from conventional virtual file"`
- }
- }
- }
- ],
- build: {
- minify: false
- }
-}
diff --git a/packages/playground/ssr-vue/vite.config.noexternal.js b/packages/playground/ssr-vue/vite.config.noexternal.js
deleted file mode 100644
index ac74bf1430e94e..00000000000000
--- a/packages/playground/ssr-vue/vite.config.noexternal.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const config = require('./vite.config.js')
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = Object.assign(config, {
- ssr: {
- noExternal: /./
- },
- resolve: {
- // necessary because vue.ssrUtils is only exported on cjs modules
- alias: [
- {
- find: '@vue/runtime-dom',
- replacement: '@vue/runtime-dom/dist/runtime-dom.cjs.js'
- },
- {
- find: '@vue/runtime-core',
- replacement: '@vue/runtime-core/dist/runtime-core.cjs.js'
- }
- ]
- }
-})
diff --git a/packages/playground/ssr-webworker/__tests__/serve.js b/packages/playground/ssr-webworker/__tests__/serve.js
deleted file mode 100644
index f4f207b85026c6..00000000000000
--- a/packages/playground/ssr-webworker/__tests__/serve.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-const path = require('path')
-
-const port = (exports.port = 9528)
-
-/**
- * @param {string} root
- * @param {boolean} isProd
- */
-exports.serve = async function serve(root, isProd) {
- // we build first, regardless of whether it's prod/build mode
- // because Vite doesn't support the concept of a "webworker server"
- const { build } = require('vite')
-
- // worker build
- await build({
- root,
- logLevel: 'silent',
- build: {
- target: 'esnext',
- ssr: 'src/entry-worker.jsx',
- outDir: 'dist/worker'
- }
- })
-
- const { createServer } = require(path.resolve(root, 'worker.js'))
- const { app } = await createServer(root, isProd)
-
- return new Promise((resolve, reject) => {
- try {
- const server = app.listen(port, () => {
- resolve({
- // for test teardown
- async close() {
- await new Promise((resolve) => {
- server.close(resolve)
- })
- }
- })
- })
- } catch (e) {
- reject(e)
- }
- })
-}
diff --git a/packages/playground/ssr-webworker/__tests__/ssr-webworker.spec.ts b/packages/playground/ssr-webworker/__tests__/ssr-webworker.spec.ts
deleted file mode 100644
index 30d2bb93e495b1..00000000000000
--- a/packages/playground/ssr-webworker/__tests__/ssr-webworker.spec.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { port } from './serve'
-
-const url = `http://localhost:${port}`
-
-test('/', async () => {
- await page.goto(url + '/')
- expect(await page.textContent('h1')).toMatch('hello from webworker')
- expect(await page.textContent('.linked')).toMatch('dep from upper directory')
- expect(await page.textContent('.external')).toMatch('object')
-})
diff --git a/packages/playground/ssr-webworker/package.json b/packages/playground/ssr-webworker/package.json
deleted file mode 100644
index a7ebdf27ea22aa..00000000000000
--- a/packages/playground/ssr-webworker/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "test-ssr-webworker",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "DEV=1 node worker",
- "build:worker": "vite build --ssr src/entry-worker.jsx --outDir dist/worker"
- },
- "dependencies": {
- "react": "^17.0.2"
- },
- "devDependencies": {
- "miniflare": "^1.4.1",
- "resolve-linked": "workspace:*"
- }
-}
diff --git a/packages/playground/ssr-webworker/src/entry-worker.jsx b/packages/playground/ssr-webworker/src/entry-worker.jsx
deleted file mode 100644
index c885657b18a6d3..00000000000000
--- a/packages/playground/ssr-webworker/src/entry-worker.jsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { msg as linkedMsg } from 'resolve-linked'
-import React from 'react'
-
-addEventListener('fetch', function (event) {
- return event.respondWith(
- new Response(
- `
- hello from webworker
- ${linkedMsg}
- ${typeof React}
- `,
- {
- headers: {
- 'content-type': 'text/html'
- }
- }
- )
- )
-})
diff --git a/packages/playground/ssr-webworker/vite.config.js b/packages/playground/ssr-webworker/vite.config.js
deleted file mode 100644
index 80cc1784cdc565..00000000000000
--- a/packages/playground/ssr-webworker/vite.config.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- build: {
- minify: false
- },
- resolve: {
- dedupe: ['react']
- },
- ssr: {
- target: 'webworker',
- noExternal: true
- },
- plugins: [
- {
- config() {
- return {
- ssr: {
- noExternal: ['this-should-not-replace-the-boolean']
- }
- }
- }
- }
- ]
-}
diff --git a/packages/playground/ssr-webworker/worker.js b/packages/playground/ssr-webworker/worker.js
deleted file mode 100644
index 09725aaa9d71bb..00000000000000
--- a/packages/playground/ssr-webworker/worker.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// @ts-check
-const path = require('path')
-const { Miniflare } = require('miniflare')
-
-const isDev = process.env.DEV
-
-async function createServer(root = process.cwd()) {
- const mf = new Miniflare({
- scriptPath: path.resolve(root, 'dist/worker/entry-worker.js')
- })
-
- const app = mf.createServer()
-
- return { app }
-}
-
-if (isDev) {
- createServer().then(({ app }) =>
- app.listen(3000, () => {
- console.log('http://localhost:3000')
- })
- )
-}
-
-// for test use
-exports.createServer = createServer
diff --git a/packages/playground/tailwind/__test__/tailwind.spec.ts b/packages/playground/tailwind/__test__/tailwind.spec.ts
deleted file mode 100644
index 47f6b7ccf49037..00000000000000
--- a/packages/playground/tailwind/__test__/tailwind.spec.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { isBuild, editFile, untilUpdated, getColor } from '../../testUtils'
-
-test('should render', async () => {
- expect(await page.textContent('#pagetitle')).toBe('|Page title|')
-})
-
-if (!isBuild) {
- test('regenerate CSS and HMR (glob pattern)', async () => {
- browserLogs.length = 0
- const el = await page.$('#pagetitle')
- const el2 = await page.$('#helloroot')
-
- expect(await getColor(el)).toBe('rgb(11, 22, 33)')
-
- editFile('src/views/Page.vue', (code) =>
- code.replace('|Page title|', '|Page title updated|')
- )
- await untilUpdated(() => el.textContent(), '|Page title updated|')
-
- expect(browserLogs).toMatchObject([
- '[vite] css hot updated: /index.css',
- '[vite] hot updated: /src/views/Page.vue'
- ])
-
- browserLogs.length = 0
-
- editFile('src/components/HelloWorld.vue', (code) =>
- code.replace('text-gray-800', 'text-[rgb(10,20,30)]')
- )
-
- await untilUpdated(() => getColor(el2), 'rgb(10, 20, 30)')
-
- expect(browserLogs).toMatchObject([
- '[vite] css hot updated: /index.css',
- '[vite] hot updated: /src/components/HelloWorld.vue'
- ])
-
- browserLogs.length = 0
- })
-
- test('regenerate CSS and HMR (relative path)', async () => {
- browserLogs.length = 0
- const el = await page.$('h1')
-
- expect(await getColor(el)).toBe('black')
-
- editFile('src/App.vue', (code) =>
- code.replace('text-black', 'text-[rgb(11,22,33)]')
- )
-
- await untilUpdated(() => getColor(el), 'rgb(11, 22, 33)')
-
- expect(browserLogs).toMatchObject([
- '[vite] css hot updated: /index.css',
- '[vite] hot updated: /src/App.vue'
- ])
-
- browserLogs.length = 0
- })
-}
diff --git a/packages/playground/tailwind/index.html b/packages/playground/tailwind/index.html
deleted file mode 100644
index 5165c9f6325cde..00000000000000
--- a/packages/playground/tailwind/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
- Vite App
-
-
-
-
-
-
-
diff --git a/packages/playground/tailwind/package.json b/packages/playground/tailwind/package.json
deleted file mode 100644
index ff79908d386e96..00000000000000
--- a/packages/playground/tailwind/package.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "name": "test-tailwind",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "autoprefixer": "^10.4.0",
- "tailwindcss": "^2.2.19",
- "vue": "^3.2.25",
- "vue-router": "^4.0.0"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*"
- }
-}
diff --git a/packages/playground/tailwind/postcss.config.js b/packages/playground/tailwind/postcss.config.js
deleted file mode 100644
index b73493f7f96fae..00000000000000
--- a/packages/playground/tailwind/postcss.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// postcss.config.js
-module.exports = {
- plugins: {
- tailwindcss: { config: __dirname + '/tailwind.config.js' },
- autoprefixer: {}
- }
-}
diff --git a/packages/playground/tailwind/public/favicon.ico b/packages/playground/tailwind/public/favicon.ico
deleted file mode 100644
index df36fcfb72584e..00000000000000
Binary files a/packages/playground/tailwind/public/favicon.ico and /dev/null differ
diff --git a/packages/playground/tailwind/src/App.vue b/packages/playground/tailwind/src/App.vue
deleted file mode 100644
index 25835fc414a06f..00000000000000
--- a/packages/playground/tailwind/src/App.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
Tailwind app
- {{ foo }}
-
-
-
-
-
diff --git a/packages/playground/tailwind/src/assets/logo.png b/packages/playground/tailwind/src/assets/logo.png
deleted file mode 100644
index f3d2503fc2a44b..00000000000000
Binary files a/packages/playground/tailwind/src/assets/logo.png and /dev/null differ
diff --git a/packages/playground/tailwind/src/components/HelloWorld.vue b/packages/playground/tailwind/src/components/HelloWorld.vue
deleted file mode 100644
index 1ea9fdf2573697..00000000000000
--- a/packages/playground/tailwind/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- HelloWorld - {{ count }}
-
-
-
-
diff --git a/packages/playground/tailwind/src/main.js b/packages/playground/tailwind/src/main.js
deleted file mode 100644
index 78494e75b4741d..00000000000000
--- a/packages/playground/tailwind/src/main.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { createApp } from 'vue'
-import App from './App.vue'
-import router from './router'
-// import '../index.css';
-
-createApp(App).use(router).mount('#app')
diff --git a/packages/playground/tailwind/src/router.ts b/packages/playground/tailwind/src/router.ts
deleted file mode 100644
index 32f1a47b40540d..00000000000000
--- a/packages/playground/tailwind/src/router.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { createWebHistory, createRouter } from 'vue-router'
-import Page from './views/Page.vue'
-
-const history = createWebHistory()
-
-const routeur = createRouter({
- history: history,
- routes: [
- {
- path: '/',
- component: Page
- }
- ]
-})
-
-export default routeur
diff --git a/packages/playground/tailwind/src/views/Page.vue b/packages/playground/tailwind/src/views/Page.vue
deleted file mode 100644
index 764a2a18e54fdb..00000000000000
--- a/packages/playground/tailwind/src/views/Page.vue
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
|Page title|
-
{{ val }}
-
- Tailwind style
-
-
-
-
-
-
diff --git a/packages/playground/tailwind/tailwind.config.js b/packages/playground/tailwind/tailwind.config.js
deleted file mode 100644
index 64a2b1a2499bb4..00000000000000
--- a/packages/playground/tailwind/tailwind.config.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = {
- mode: 'jit',
- purge: [
- // Before editing this section, make sure no paths are matching with `/src/App.vue`
- // Look https://github.com/vitejs/vite/pull/6959 for more details
- __dirname + '/src/{components,views}/**/*.vue',
- __dirname + '/src/App.vue'
- ],
- darkMode: false, // or 'media' or 'class'
- theme: {
- extend: {}
- },
- variants: {
- extend: {}
- },
- plugins: []
-}
diff --git a/packages/playground/tailwind/vite.config.ts b/packages/playground/tailwind/vite.config.ts
deleted file mode 100644
index e7c50bf3c3ae78..00000000000000
--- a/packages/playground/tailwind/vite.config.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
-
-export default defineConfig({
- resolve: {
- alias: {
- '/@': __dirname
- }
- },
- plugins: [vue()],
- build: {
- // to make tests faster
- minify: false
- },
- server: {
- // This option caused issues with HMR,
- // although it should not affect the build
- origin: 'http://localhost:8080/'
- }
-})
diff --git a/packages/playground/testUtils.ts b/packages/playground/testUtils.ts
deleted file mode 100644
index 0c8186d4ed121d..00000000000000
--- a/packages/playground/testUtils.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-// test utils used in e2e tests for playgrounds.
-// this can be directly imported in any playground tests as 'testUtils', e.g.
-// `import { getColor } from 'testUtils'`
-
-import fs from 'fs'
-import path from 'path'
-import colors from 'css-color-names'
-import type { ElementHandle } from 'playwright-chromium'
-import type { Manifest } from 'vite'
-
-export function slash(p: string): string {
- return p.replace(/\\/g, '/')
-}
-
-export const isBuild = !!process.env.VITE_TEST_BUILD
-
-const testPath = expect.getState().testPath
-const testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1]
-export const testDir = path.resolve(__dirname, '../../packages/temp', testName)
-export const workspaceRoot = path.resolve(__dirname, '../../')
-
-const hexToNameMap: Record = {}
-Object.keys(colors).forEach((color) => {
- hexToNameMap[colors[color]] = color
-})
-
-function componentToHex(c: number): string {
- const hex = c.toString(16)
- return hex.length === 1 ? '0' + hex : hex
-}
-
-function rgbToHex(rgb: string): string {
- const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/)
- if (match) {
- const [_, rs, gs, bs] = match
- return (
- '#' +
- componentToHex(parseInt(rs, 10)) +
- componentToHex(parseInt(gs, 10)) +
- componentToHex(parseInt(bs, 10))
- )
- } else {
- return '#000000'
- }
-}
-
-const timeout = (n: number) => new Promise((r) => setTimeout(r, n))
-
-async function toEl(el: string | ElementHandle): Promise {
- if (typeof el === 'string') {
- return await page.$(el)
- }
- return el
-}
-
-export async function getColor(el: string | ElementHandle): Promise {
- el = await toEl(el)
- const rgb = await el.evaluate((el) => getComputedStyle(el as Element).color)
- return hexToNameMap[rgbToHex(rgb)] ?? rgb
-}
-
-export async function getBg(el: string | ElementHandle): Promise {
- el = await toEl(el)
- return el.evaluate((el) => getComputedStyle(el as Element).backgroundImage)
-}
-
-export async function getBgColor(el: string | ElementHandle): Promise {
- el = await toEl(el)
- return el.evaluate((el) => getComputedStyle(el as Element).backgroundColor)
-}
-
-export function readFile(filename: string): string {
- return fs.readFileSync(path.resolve(testDir, filename), 'utf-8')
-}
-
-export function editFile(
- filename: string,
- replacer: (str: string) => string,
- runInBuild: boolean = false
-): void {
- if (isBuild && !runInBuild) return
- filename = path.resolve(testDir, filename)
- const content = fs.readFileSync(filename, 'utf-8')
- const modified = replacer(content)
- fs.writeFileSync(filename, modified)
-}
-
-export function addFile(filename: string, content: string): void {
- fs.writeFileSync(path.resolve(testDir, filename), content)
-}
-
-export function removeFile(filename: string): void {
- fs.unlinkSync(path.resolve(testDir, filename))
-}
-
-export function listAssets(base = ''): string[] {
- const assetsDir = path.join(testDir, 'dist', base, 'assets')
- return fs.readdirSync(assetsDir)
-}
-
-export function findAssetFile(match: string | RegExp, base = ''): string {
- const assetsDir = path.join(testDir, 'dist', base, 'assets')
- const files = fs.readdirSync(assetsDir)
- const file = files.find((file) => {
- return file.match(match)
- })
- return file ? fs.readFileSync(path.resolve(assetsDir, file), 'utf-8') : ''
-}
-
-export function readManifest(base = ''): Manifest {
- return JSON.parse(
- fs.readFileSync(path.join(testDir, 'dist', base, 'manifest.json'), 'utf-8')
- )
-}
-
-/**
- * Poll a getter until the value it returns includes the expected value.
- */
-export async function untilUpdated(
- poll: () => string | Promise,
- expected: string,
- runInBuild = false
-): Promise {
- if (isBuild && !runInBuild) return
- const maxTries = process.env.CI ? 100 : 50
- for (let tries = 0; tries < maxTries; tries++) {
- const actual = (await poll()) ?? ''
- if (actual.indexOf(expected) > -1 || tries === maxTries - 1) {
- expect(actual).toMatch(expected)
- break
- } else {
- await timeout(50)
- }
- }
-}
-
-/**
- * Send the rebuild complete message in build watch
- */
-export { notifyRebuildComplete } from '../../scripts/jestPerTestSetup'
diff --git a/packages/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts b/packages/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts
deleted file mode 100644
index 699f658da6a255..00000000000000
--- a/packages/playground/tsconfig-json-load-error/__tests__/tsconfig-json-load-error.spec.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { editFile, isBuild, readFile, untilUpdated } from '../../testUtils'
-
-if (isBuild) {
- test('should throw an error on build', () => {
- const buildError = beforeAllError
- expect(buildError).toBeTruthy()
- expect(buildError.message).toMatch(
- /^parsing .* failed: SyntaxError: Unexpected token } in JSON at position \d+$/
- )
- beforeAllError = null // got expected error, null it here so testsuite does not fail from rethrow in afterAll
- })
-
- test('should not output files to dist', () => {
- let err
- try {
- readFile('dist/index.html')
- } catch (e) {
- err = e
- }
- expect(err).toBeTruthy()
- expect(err.code).toBe('ENOENT')
- })
-} else {
- test('should log 500 error in browser for malformed tsconfig', () => {
- // don't test for actual complete message as this might be locale dependant. chrome does log 500 consistently though
- expect(browserLogs.find((x) => x.includes('500'))).toBeTruthy()
- expect(browserLogs).not.toContain('tsconfig error fixed, file loaded')
- })
-
- test('should show error overlay for tsconfig error', async () => {
- const errorOverlay = await page.waitForSelector('vite-error-overlay')
- expect(errorOverlay).toBeTruthy()
- const message = await errorOverlay.$$eval('.message-body', (m) => {
- return m[0].innerHTML
- })
- // use regex with variable filename and position values because they are different on win
- expect(message).toMatch(
- /^parsing .* failed: SyntaxError: Unexpected token } in JSON at position \d+$/
- )
- })
-
- test('should reload when tsconfig is changed', async () => {
- await editFile('has-error/tsconfig.json', (content) => {
- return content.replace('"compilerOptions":', '"compilerOptions":{}')
- })
- await untilUpdated(() => {
- return browserLogs.find((x) => x === 'tsconfig error fixed, file loaded')
- }, 'tsconfig error fixed, file loaded')
- })
-}
diff --git a/packages/playground/tsconfig-json-load-error/index.html b/packages/playground/tsconfig-json-load-error/index.html
deleted file mode 100644
index a59cd7cce619a7..00000000000000
--- a/packages/playground/tsconfig-json-load-error/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Vite App
-
-
-
-
-
-
diff --git a/packages/playground/tsconfig-json-load-error/package.json b/packages/playground/tsconfig-json-load-error/package.json
deleted file mode 100644
index b02c6e5ee5ab53..00000000000000
--- a/packages/playground/tsconfig-json-load-error/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "tsconfig-json-load-error",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- }
-}
diff --git a/packages/playground/tsconfig-json-load-error/tsconfig.json b/packages/playground/tsconfig-json-load-error/tsconfig.json
deleted file mode 100644
index e91cdec493e28f..00000000000000
--- a/packages/playground/tsconfig-json-load-error/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "compilerOptions": {
- "target": "ESNext",
- "module": "ESNext",
- "lib": ["ESNext", "DOM"],
- "moduleResolution": "Node",
- "strict": true,
- "sourceMap": true,
- "resolveJsonModule": true,
- "esModuleInterop": true,
- "noEmit": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noImplicitReturns": true,
-
- "useDefineForClassFields": true,
- "importsNotUsedAsValues": "preserve"
- },
- "include": ["./src"]
-}
diff --git a/packages/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts b/packages/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts
deleted file mode 100644
index 0cd6af909f045b..00000000000000
--- a/packages/playground/tsconfig-json/__tests__/tsconfig-json.spec.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import path from 'path'
-import fs from 'fs'
-import { transformWithEsbuild } from 'vite'
-
-test('should respected each `tsconfig.json`s compilerOptions', () => {
- // main side effect should be called (because of `"importsNotUsedAsValues": "preserve"`)
- expect(browserLogs).toContain('main side effect')
- // main base setter should not be called (because of `"useDefineForClassFields": true"`)
- expect(browserLogs).not.toContain('data setter in MainBase')
-
- // nested side effect should not be called (because "importsNotUsedAsValues" is not set, defaults to "remove")
- expect(browserLogs).not.toContain('nested side effect')
- // nested base setter should be called (because of `"useDefineForClassFields": false"`)
- expect(browserLogs).toContain('data setter in NestedBase')
-
- // nested-with-extends side effect should be called (because "importsNotUsedAsValues" is extended from the main tsconfig.json, which is "preserve")
- expect(browserLogs).toContain('nested-with-extends side effect')
- // nested-with-extends base setter should be called (because of `"useDefineForClassFields": false"`)
- expect(browserLogs).toContain('data setter in NestedWithExtendsBase')
-})
-
-describe('transformWithEsbuild', () => {
- test('merge tsconfigRaw object', async () => {
- const main = path.resolve(__dirname, '../src/main.ts')
- const mainContent = fs.readFileSync(main, 'utf-8')
- const result = await transformWithEsbuild(mainContent, main, {
- tsconfigRaw: {
- compilerOptions: {
- useDefineForClassFields: false
- }
- }
- })
- // "importsNotUsedAsValues": "preserve" from tsconfig.json should still work
- expect(result.code).toContain('import "./not-used-type";')
- })
-
- test('overwrite tsconfigRaw string', async () => {
- const main = path.resolve(__dirname, '../src/main.ts')
- const mainContent = fs.readFileSync(main, 'utf-8')
- const result = await transformWithEsbuild(mainContent, main, {
- tsconfigRaw: `{
- "compilerOptions": {
- "useDefineForClassFields": false
- }
- }`
- })
- // "importsNotUsedAsValues": "preserve" from tsconfig.json should not be read
- // and defaults to "remove"
- expect(result.code).not.toContain('import "./not-used-type";')
- })
-
- test('preserveValueImports', async () => {
- const main = path.resolve(__dirname, '../src/main.ts')
- const mainContent = fs.readFileSync(main, 'utf-8')
- const result = await transformWithEsbuild(mainContent, main, {
- tsconfigRaw: {
- compilerOptions: {
- useDefineForClassFields: false,
- preserveValueImports: true
- }
- }
- })
- // "importsNotUsedAsValues": "preserve" from tsconfig.json should still work
- expect(result.code).toContain(
- 'import { MainTypeOnlyClass } from "./not-used-type";'
- )
- })
-})
diff --git a/packages/playground/tsconfig-json/index.html b/packages/playground/tsconfig-json/index.html
deleted file mode 100644
index a59cd7cce619a7..00000000000000
--- a/packages/playground/tsconfig-json/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Vite App
-
-
-
-
-
-
diff --git a/packages/playground/tsconfig-json/nested/tsconfig.json b/packages/playground/tsconfig-json/nested/tsconfig.json
deleted file mode 100644
index 23b6b8779f649a..00000000000000
--- a/packages/playground/tsconfig-json/nested/tsconfig.json
+++ /dev/null
@@ -1,21 +0,0 @@
-// prettier-ignore
-{
- "include": ["./"],
- "compilerOptions": {
- "target": "ESNext",
- "module": "ESNext",
- "lib": ["ESNext", "DOM"],
- "moduleResolution": "Node",
- "strict": true,
- "sourceMap": true,
- "resolveJsonModule": true,
- "esModuleInterop": true,
- "noEmit": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noImplicitReturns": true,
-
- /* tsconfig.json should support comments and trailing comma */
- "useDefineForClassFields": false,
- }
-}
diff --git a/packages/playground/tsconfig-json/package.json b/packages/playground/tsconfig-json/package.json
deleted file mode 100644
index c4248463facdb9..00000000000000
--- a/packages/playground/tsconfig-json/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "tsconfig-json",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- }
-}
diff --git a/packages/playground/tsconfig-json/src/main.ts b/packages/playground/tsconfig-json/src/main.ts
deleted file mode 100644
index 6ae1fe03b7d023..00000000000000
--- a/packages/playground/tsconfig-json/src/main.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// @ts-nocheck
-import '../nested/main'
-import '../nested-with-extends/main'
-
-// eslint-disable-next-line @typescript-eslint/consistent-type-imports
-import { MainTypeOnlyClass } from './not-used-type'
-
-class MainBase {
- set data(value: string) {
- console.log('data setter in MainBase')
- }
-}
-class MainDerived extends MainBase {
- // No longer triggers a 'console.log'
- // when using 'useDefineForClassFields'.
- data = 10
-
- foo?: MainTypeOnlyClass
-}
-
-const d = new MainDerived()
diff --git a/packages/playground/tsconfig-json/tsconfig.json b/packages/playground/tsconfig-json/tsconfig.json
deleted file mode 100644
index e91cdec493e28f..00000000000000
--- a/packages/playground/tsconfig-json/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "compilerOptions": {
- "target": "ESNext",
- "module": "ESNext",
- "lib": ["ESNext", "DOM"],
- "moduleResolution": "Node",
- "strict": true,
- "sourceMap": true,
- "resolveJsonModule": true,
- "esModuleInterop": true,
- "noEmit": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noImplicitReturns": true,
-
- "useDefineForClassFields": true,
- "importsNotUsedAsValues": "preserve"
- },
- "include": ["./src"]
-}
diff --git a/packages/playground/tsconfig.json b/packages/playground/tsconfig.json
deleted file mode 100644
index d60edb9f78c801..00000000000000
--- a/packages/playground/tsconfig.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "include": ["."],
- "exclude": ["**/dist/**"],
- "compilerOptions": {
- "target": "es2019",
- "outDir": "dist",
- "allowJs": true,
- "esModuleInterop": true,
- "moduleResolution": "node",
- "baseUrl": ".",
- "jsx": "preserve",
- "types": ["vite/client", "jest", "node"]
- }
-}
diff --git a/packages/playground/vue-jsx/Comp.tsx b/packages/playground/vue-jsx/Comp.tsx
deleted file mode 100644
index fe8add4d428a2c..00000000000000
--- a/packages/playground/vue-jsx/Comp.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { defineComponent, ref } from 'vue'
-
-const Default = defineComponent(() => {
- const count = ref(3)
- const inc = () => count.value++
-
- return () => (
-
- default tsx {count.value}
-
- )
-})
-
-export default Default
diff --git a/packages/playground/vue-jsx/Comps.jsx b/packages/playground/vue-jsx/Comps.jsx
deleted file mode 100644
index e5cc405a77581b..00000000000000
--- a/packages/playground/vue-jsx/Comps.jsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { defineComponent, ref } from 'vue'
-
-export const Named = defineComponent(() => {
- const count = ref(0)
- const inc = () => count.value++
-
- return () => (
-
- named {count.value}
-
- )
-})
-
-const NamedSpec = defineComponent(() => {
- const count = ref(1)
- const inc = () => count.value++
-
- return () => (
-
- named specifier {count.value}
-
- )
-})
-export { NamedSpec }
-
-export default defineComponent(() => {
- const count = ref(2)
- const inc = () => count.value++
-
- return () => (
-
- default {count.value}
-
- )
-})
diff --git a/packages/playground/vue-jsx/OtherExt.tesx b/packages/playground/vue-jsx/OtherExt.tesx
deleted file mode 100644
index 7ae585a014c566..00000000000000
--- a/packages/playground/vue-jsx/OtherExt.tesx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { defineComponent } from 'vue'
-
-const Default = defineComponent(() => {
- return () => (
- Other Ext
- )
-})
-
-export default Default
diff --git a/packages/playground/vue-jsx/Query.jsx b/packages/playground/vue-jsx/Query.jsx
deleted file mode 100644
index 60de93eafb7b1c..00000000000000
--- a/packages/playground/vue-jsx/Query.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { defineComponent, ref } from 'vue'
-
-export default defineComponent(() => {
- const count = ref(6)
- const inc = () => count.value++
-
- return () => (
-
- import with query transform fail
-
- )
-})
diff --git a/packages/playground/vue-jsx/Script.vue b/packages/playground/vue-jsx/Script.vue
deleted file mode 100644
index 2689ed2dfe6ffb..00000000000000
--- a/packages/playground/vue-jsx/Script.vue
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/packages/playground/vue-jsx/SrcImport.jsx b/packages/playground/vue-jsx/SrcImport.jsx
deleted file mode 100644
index dc775be205af73..00000000000000
--- a/packages/playground/vue-jsx/SrcImport.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { defineComponent, ref } from 'vue'
-
-export default defineComponent(() => {
- const count = ref(5)
- const inc = () => count.value++
-
- return () => (
-
- src import {count.value}
-
- )
-})
diff --git a/packages/playground/vue-jsx/SrcImport.vue b/packages/playground/vue-jsx/SrcImport.vue
deleted file mode 100644
index 89f6fb3eb77e2b..00000000000000
--- a/packages/playground/vue-jsx/SrcImport.vue
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/packages/playground/vue-jsx/__tests__/vue-jsx.spec.ts b/packages/playground/vue-jsx/__tests__/vue-jsx.spec.ts
deleted file mode 100644
index 999fdc19af51ec..00000000000000
--- a/packages/playground/vue-jsx/__tests__/vue-jsx.spec.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { editFile, isBuild, untilUpdated } from 'testUtils'
-
-test('should render', async () => {
- expect(await page.textContent('.named')).toMatch('0')
- expect(await page.textContent('.named-specifier')).toMatch('1')
- expect(await page.textContent('.default')).toMatch('2')
- expect(await page.textContent('.default-tsx')).toMatch('3')
- expect(await page.textContent('.script')).toMatch('4')
- expect(await page.textContent('.src-import')).toMatch('5')
- expect(await page.textContent('.jsx-with-query')).toMatch('6')
- expect(await page.textContent('.other-ext')).toMatch('Other Ext')
-})
-
-test('should update', async () => {
- await page.click('.named')
- expect(await page.textContent('.named')).toMatch('1')
- await page.click('.named-specifier')
- expect(await page.textContent('.named-specifier')).toMatch('2')
- await page.click('.default')
- expect(await page.textContent('.default')).toMatch('3')
- await page.click('.default-tsx')
- expect(await page.textContent('.default-tsx')).toMatch('4')
- await page.click('.script')
- expect(await page.textContent('.script')).toMatch('5')
- await page.click('.src-import')
- expect(await page.textContent('.src-import')).toMatch('6')
- await page.click('.jsx-with-query')
- expect(await page.textContent('.jsx-with-query')).toMatch('7')
-})
-
-if (!isBuild) {
- test('hmr: named export', async () => {
- editFile('Comps.jsx', (code) =>
- code.replace('named {count', 'named updated {count')
- )
- await untilUpdated(() => page.textContent('.named'), 'named updated 0')
-
- // affect all components in same file
- expect(await page.textContent('.named-specifier')).toMatch('1')
- expect(await page.textContent('.default')).toMatch('2')
- // should not affect other components from different file
- expect(await page.textContent('.default-tsx')).toMatch('4')
- })
-
- test('hmr: named export via specifier', async () => {
- editFile('Comps.jsx', (code) =>
- code.replace('named specifier {count', 'named specifier updated {count')
- )
- await untilUpdated(
- () => page.textContent('.named-specifier'),
- 'named specifier updated 1'
- )
-
- // affect all components in same file
- expect(await page.textContent('.default')).toMatch('2')
- // should not affect other components on the page
- expect(await page.textContent('.default-tsx')).toMatch('4')
- })
-
- test('hmr: default export', async () => {
- editFile('Comps.jsx', (code) =>
- code.replace('default {count', 'default updated {count')
- )
- await untilUpdated(() => page.textContent('.default'), 'default updated 2')
-
- // should not affect other components on the page
- expect(await page.textContent('.default-tsx')).toMatch('4')
- })
-
- test('hmr: named export via specifier', async () => {
- // update another component
- await page.click('.named')
- expect(await page.textContent('.named')).toMatch('1')
-
- editFile('Comp.tsx', (code) =>
- code.replace('default tsx {count', 'default tsx updated {count')
- )
- await untilUpdated(
- () => page.textContent('.default-tsx'),
- 'default tsx updated 3'
- )
-
- // should not affect other components on the page
- expect(await page.textContent('.named')).toMatch('1')
- })
-
- test('hmr: script in .vue', async () => {
- editFile('Script.vue', (code) =>
- code.replace('script {count', 'script updated {count')
- )
- await untilUpdated(() => page.textContent('.script'), 'script updated 4')
-
- expect(await page.textContent('.src-import')).toMatch('6')
- })
-
- test('hmr: src import in .vue', async () => {
- await page.click('.script')
- editFile('SrcImport.jsx', (code) =>
- code.replace('src import {count', 'src import updated {count')
- )
- await untilUpdated(
- () => page.textContent('.src-import'),
- 'src import updated 5'
- )
-
- expect(await page.textContent('.script')).toMatch('5')
- })
-
- test('hmr: setup jsx in .vue', async () => {
- editFile('setup-syntax-jsx.vue', (code) =>
- code.replace('let count = ref(100)', 'let count = ref(1000)')
- )
- await untilUpdated(() => page.textContent('.setup-jsx'), '1000')
- })
-}
diff --git a/packages/playground/vue-jsx/index.html b/packages/playground/vue-jsx/index.html
deleted file mode 100644
index a285a008c13a9e..00000000000000
--- a/packages/playground/vue-jsx/index.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/packages/playground/vue-jsx/main.jsx b/packages/playground/vue-jsx/main.jsx
deleted file mode 100644
index e304e7788e49e7..00000000000000
--- a/packages/playground/vue-jsx/main.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { createApp } from 'vue'
-import { Named, NamedSpec, default as Default } from './Comps'
-import { default as TsxDefault } from './Comp'
-import OtherExt from './OtherExt.tesx'
-import JsxScript from './Script.vue'
-import JsxSrcImport from './SrcImport.vue'
-import JsxSetupSyntax from './setup-syntax-jsx.vue'
-// eslint-disable-next-line
-import JsxWithQuery from './Query.jsx?query=true'
-
-function App() {
- return (
- <>
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-createApp(App).mount('#app')
diff --git a/packages/playground/vue-jsx/package.json b/packages/playground/vue-jsx/package.json
deleted file mode 100644
index 4b2135906b2833..00000000000000
--- a/packages/playground/vue-jsx/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "test-vue-jsx",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "vue": "^3.2.25"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*",
- "@vitejs/plugin-vue-jsx": "workspace:*"
- }
-}
diff --git a/packages/playground/vue-jsx/setup-syntax-jsx.vue b/packages/playground/vue-jsx/setup-syntax-jsx.vue
deleted file mode 100644
index 0b16be7e773280..00000000000000
--- a/packages/playground/vue-jsx/setup-syntax-jsx.vue
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- {{ count }}
-
-
diff --git a/packages/playground/vue-jsx/vite.config.js b/packages/playground/vue-jsx/vite.config.js
deleted file mode 100644
index d6eb84e05f4e4a..00000000000000
--- a/packages/playground/vue-jsx/vite.config.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const vueJsxPlugin = require('@vitejs/plugin-vue-jsx')
-const vuePlugin = require('@vitejs/plugin-vue')
-
-/**
- * @type {import('vite').UserConfig}
- */
-module.exports = {
- plugins: [
- vueJsxPlugin({
- include: [/\.tesx$/, /\.[jt]sx$/]
- }),
- vuePlugin(),
- {
- name: 'jsx-query-plugin',
- transform(code, id) {
- if (id.includes('?query=true')) {
- return `
-import { createVNode as _createVNode } from "vue";
-import { defineComponent, ref } from 'vue';
-export default defineComponent(() => {
- const count = ref(6);
-
- const inc = () => count.value++;
-
- return () => _createVNode("button", {
- "class": "jsx-with-query",
- "onClick": inc
- }, [count.value]);
-});
-`
- }
- }
- }
- ],
- build: {
- // to make tests faster
- minify: false
- }
-}
diff --git a/packages/playground/vue-lib/__tests__/serve.js b/packages/playground/vue-lib/__tests__/serve.js
deleted file mode 100644
index 73f89eee44ea3e..00000000000000
--- a/packages/playground/vue-lib/__tests__/serve.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// @ts-check
-// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
-// the default e2e test serve behavior
-
-exports.serve = async function serve() {
- // do nothing, skip default behavior
-}
diff --git a/packages/playground/vue-lib/__tests__/vue-lib.spec.ts b/packages/playground/vue-lib/__tests__/vue-lib.spec.ts
deleted file mode 100644
index 0504160f17d2f0..00000000000000
--- a/packages/playground/vue-lib/__tests__/vue-lib.spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { build } from 'vite'
-import path from 'path'
-import type { OutputChunk, RollupOutput } from 'rollup'
-
-describe('vue component library', () => {
- test('should output tree shakeable css module code', async () => {
- // Build lib
- await build({
- logLevel: 'silent',
- configFile: path.resolve(__dirname, '../vite.config.lib.ts')
- })
- // Build app
- const { output } = (await build({
- logLevel: 'silent',
- configFile: path.resolve(__dirname, '../vite.config.consumer.ts')
- })) as RollupOutput
- const { code } = output.find(
- (e) => e.type === 'chunk' && e.isEntry
- ) as OutputChunk
- // Unused css module should be treeshaked
- expect(code).toContain('styleA') // styleA is used by CompA
- expect(code).not.toContain('styleB') // styleB is not used
- })
-})
diff --git a/packages/playground/vue-lib/index.html b/packages/playground/vue-lib/index.html
deleted file mode 100644
index e016cf7d760797..00000000000000
--- a/packages/playground/vue-lib/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/packages/playground/vue-lib/package.json b/packages/playground/vue-lib/package.json
deleted file mode 100644
index df82cf48b62a9c..00000000000000
--- a/packages/playground/vue-lib/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "test-vue-lib",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev-consumer": "vite --config ./vite.config.consumer.ts",
- "build-lib": "vite build --config ./vite.config.lib.ts",
- "build-consumer": "vite build --config ./vite.config.consumer.ts"
- },
- "dependencies": {
- "vue": "^3.2.25"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*"
- }
-}
diff --git a/packages/playground/vue-lib/src-consumer/index.ts b/packages/playground/vue-lib/src-consumer/index.ts
deleted file mode 100644
index ac0f65e2a3ed9d..00000000000000
--- a/packages/playground/vue-lib/src-consumer/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-// @ts-ignore
-/* eslint-disable node/no-missing-import */
-import { CompA } from '../dist/lib/my-vue-lib.es'
-import '../dist/lib/style.css'
-import { createApp } from 'vue'
-
-const app = createApp(CompA)
-app.mount('#app')
diff --git a/packages/playground/vue-lib/src-lib/CompA.vue b/packages/playground/vue-lib/src-lib/CompA.vue
deleted file mode 100644
index dac9298b3bedf4..00000000000000
--- a/packages/playground/vue-lib/src-lib/CompA.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-
- CompA
-
-
diff --git a/packages/playground/vue-lib/src-lib/CompB.vue b/packages/playground/vue-lib/src-lib/CompB.vue
deleted file mode 100644
index cca30168fb6753..00000000000000
--- a/packages/playground/vue-lib/src-lib/CompB.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-
- CompB
-
-
diff --git a/packages/playground/vue-lib/src-lib/index.ts b/packages/playground/vue-lib/src-lib/index.ts
deleted file mode 100644
index f83abd4ec72118..00000000000000
--- a/packages/playground/vue-lib/src-lib/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default as CompA } from './CompA.vue'
-export { default as CompB } from './CompB.vue'
diff --git a/packages/playground/vue-lib/vite.config.consumer.ts b/packages/playground/vue-lib/vite.config.consumer.ts
deleted file mode 100644
index 9e75b5cfbeabcb..00000000000000
--- a/packages/playground/vue-lib/vite.config.consumer.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
-
-export default defineConfig({
- root: __dirname,
- build: {
- outDir: 'dist/consumer'
- },
- plugins: [vue()]
-})
diff --git a/packages/playground/vue-lib/vite.config.lib.ts b/packages/playground/vue-lib/vite.config.lib.ts
deleted file mode 100644
index a888382d008a8c..00000000000000
--- a/packages/playground/vue-lib/vite.config.lib.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import path from 'path'
-import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
-
-export default defineConfig({
- root: __dirname,
- build: {
- outDir: 'dist/lib',
- lib: {
- entry: path.resolve(__dirname, 'src-lib/index.ts'),
- name: 'MyVueLib',
- formats: ['es'],
- fileName: (format) => `my-vue-lib.${format}.js`
- },
- rollupOptions: {
- external: ['vue'],
- output: {
- globals: { vue: 'Vue' }
- }
- }
- },
- plugins: [vue()]
-})
diff --git a/packages/playground/vue/Assets.vue b/packages/playground/vue/Assets.vue
deleted file mode 100644
index 875ac1b243b393..00000000000000
--- a/packages/playground/vue/Assets.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-
- Template Static Asset Reference
-
- Relative
-
-
-
- Absolute
-
-
-
- Absolute import from public dir
-
-
-
- Relative URL in style
-
-
-
- SVG Fragment reference
-
-
-
-
-
diff --git a/packages/playground/vue/AsyncComponent.vue b/packages/playground/vue/AsyncComponent.vue
deleted file mode 100644
index 4e66630c4d2edd..00000000000000
--- a/packages/playground/vue/AsyncComponent.vue
+++ /dev/null
@@ -1,15 +0,0 @@
-
- Async Component
- Testing TLA and for await compatibility with esbuild
- ab == {{ test }}
-
-
-
diff --git a/packages/playground/vue/CssModules.vue b/packages/playground/vue/CssModules.vue
deleted file mode 100644
index f7897e2e57f652..00000000000000
--- a/packages/playground/vue/CssModules.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-
- CSS Modules
-
- <style module> - this should be blue
-
{{ $style }}
-
-
- CSS - this should be orange
-
{{ mod }}
-
-
-
-
-
-
diff --git a/packages/playground/vue/CustomBlock.vue b/packages/playground/vue/CustomBlock.vue
deleted file mode 100644
index 0a7b3901693154..00000000000000
--- a/packages/playground/vue/CustomBlock.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-
- Custom Blocks
- {{ t('hello') }}
-
-
-
-
-
-en:
- hello: 'hello,vite!'
-ja:
- hello: 'こんにちは、vite!'
-
diff --git a/packages/playground/vue/CustomBlockPlugin.ts b/packages/playground/vue/CustomBlockPlugin.ts
deleted file mode 100644
index 4f5def023902bc..00000000000000
--- a/packages/playground/vue/CustomBlockPlugin.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import type { Plugin } from 'vite'
-
-export const vueI18nPlugin: Plugin = {
- name: 'vue-i18n',
- transform(code, id) {
- if (!/vue&type=i18n/.test(id)) {
- return
- }
- if (/\.ya?ml$/.test(id)) {
- code = JSON.stringify(require('js-yaml').load(code.trim()))
- }
- return {
- code: `export default Comp => {
- Comp.i18n = ${code}
- }`,
- map: { mappings: '' }
- }
- }
-}
diff --git a/packages/playground/vue/CustomElement.ce.vue b/packages/playground/vue/CustomElement.ce.vue
deleted file mode 100644
index 58d94650d1a74a..00000000000000
--- a/packages/playground/vue/CustomElement.ce.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-
- Custom Element
-
- {{ label }}: {{ state.count }}
-
-
-
-
-
-
diff --git a/packages/playground/vue/Hmr.vue b/packages/playground/vue/Hmr.vue
deleted file mode 100644
index 5535467af3858f..00000000000000
--- a/packages/playground/vue/Hmr.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-
- HMR
- Click the button then edit this message. The count should be preserved.
- count is {{ count }}
-
-
-
-
-
diff --git a/packages/playground/vue/Main.vue b/packages/playground/vue/Main.vue
deleted file mode 100644
index d10ae401f7aa8e..00000000000000
--- a/packages/playground/vue/Main.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
- Vue SFCs
- {{ time as string }}
-
-
-
-
-
-
-
-
-
-
- this should be red
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/playground/vue/Node.vue b/packages/playground/vue/Node.vue
deleted file mode 100644
index 246442d29f522c..00000000000000
--- a/packages/playground/vue/Node.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
- this is node
-
diff --git a/packages/playground/vue/PreProcessors.vue b/packages/playground/vue/PreProcessors.vue
deleted file mode 100644
index ddb636678e8cdd..00000000000000
--- a/packages/playground/vue/PreProcessors.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-
-h2.pre-processors Pre-Processors
-p.pug
- | This is rendered from <template lang="pug">
- | and styled with <style lang="sass">. It should be megenta.
-p.pug-less
- | This is rendered from <template lang="pug">
- | and styled with <style lang="less">. It should be green.
-p.pug-stylus
- | This is rendered from <template lang="pug">
- | and styled with <style lang="stylus">. It should be orange.
-SlotComponent
- template(v-slot:test-slot)
- div.pug-slot slot content
-
-
-
-
-
-
-
-
-
diff --git a/packages/playground/vue/ReactivityTransform.vue b/packages/playground/vue/ReactivityTransform.vue
deleted file mode 100644
index 0dc2b09343d641..00000000000000
--- a/packages/playground/vue/ReactivityTransform.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- Reactivity Transform
- Prop foo: {{ bar }}
- {{ a }}
-
diff --git a/packages/playground/vue/ScanDep.vue b/packages/playground/vue/ScanDep.vue
deleted file mode 100644
index 17b398beab1cd2..00000000000000
--- a/packages/playground/vue/ScanDep.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-
- Scan Deps from <script setup lang=ts> blocks
- {{ typeof debounce === 'function' ? 'ok' : 'error' }}
-
-
-
diff --git a/packages/playground/vue/Slotted.vue b/packages/playground/vue/Slotted.vue
deleted file mode 100644
index fb25a9c5100215..00000000000000
--- a/packages/playground/vue/Slotted.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
:slotted
-
-
-
-
-
diff --git a/packages/playground/vue/Syntax.vue b/packages/playground/vue/Syntax.vue
deleted file mode 100644
index de100226922c55..00000000000000
--- a/packages/playground/vue/Syntax.vue
+++ /dev/null
@@ -1,14 +0,0 @@
-
- Syntax Support
- {{ a?.b }}
-
-
-
diff --git a/packages/playground/vue/__tests__/vue.spec.ts b/packages/playground/vue/__tests__/vue.spec.ts
deleted file mode 100644
index 63680d6f021684..00000000000000
--- a/packages/playground/vue/__tests__/vue.spec.ts
+++ /dev/null
@@ -1,244 +0,0 @@
-import { editFile, getBg, getColor, isBuild, untilUpdated } from 'testUtils'
-
-test('should render', async () => {
- expect(await page.textContent('h1')).toMatch('Vue SFCs')
-})
-
-test('should update', async () => {
- expect(await page.textContent('.hmr-inc')).toMatch('count is 0')
- await page.click('.hmr-inc')
- expect(await page.textContent('.hmr-inc')).toMatch('count is 1')
-})
-
-test('template/script latest syntax support', async () => {
- expect(await page.textContent('.syntax')).toBe('baz')
-})
-
-test('should remove comments in prod', async () => {
- expect(await page.innerHTML('.comments')).toBe(isBuild ? `` : ``)
-})
-
-test(':slotted', async () => {
- expect(await getColor('.slotted')).toBe('red')
-})
-
-describe('dep scan', () => {
- test('scan deps from
-
diff --git a/packages/playground/vue/package.json b/packages/playground/vue/package.json
deleted file mode 100644
index f493e9028b6ec3..00000000000000
--- a/packages/playground/vue/package.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "name": "test-vue",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "dependencies": {
- "lodash-es": "^4.17.21",
- "vue": "^3.2.25"
- },
- "devDependencies": {
- "@vitejs/plugin-vue": "workspace:*",
- "js-yaml": "^4.1.0",
- "less": "^4.1.2",
- "pug": "^3.0.2",
- "sass": "^1.43.4",
- "stylus": "^0.55.0"
- }
-}
diff --git a/packages/playground/vue/public/favicon.ico b/packages/playground/vue/public/favicon.ico
deleted file mode 100644
index df36fcfb72584e..00000000000000
Binary files a/packages/playground/vue/public/favicon.ico and /dev/null differ
diff --git a/packages/playground/vue/setup-import-template/SetupImportTemplate.vue b/packages/playground/vue/setup-import-template/SetupImportTemplate.vue
deleted file mode 100644
index d7fb119e3cfdc0..00000000000000
--- a/packages/playground/vue/setup-import-template/SetupImportTemplate.vue
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
diff --git a/packages/playground/vue/setup-import-template/template.html b/packages/playground/vue/setup-import-template/template.html
deleted file mode 100644
index 414069f2e9e929..00000000000000
--- a/packages/playground/vue/setup-import-template/template.html
+++ /dev/null
@@ -1,2 +0,0 @@
-Setup Import Template
-{{ count }}
diff --git a/packages/playground/vue/src-import/SrcImport.vue b/packages/playground/vue/src-import/SrcImport.vue
deleted file mode 100644
index d70e1f48a84331..00000000000000
--- a/packages/playground/vue/src-import/SrcImport.vue
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/packages/playground/vue/src-import/script.ts b/packages/playground/vue/src-import/script.ts
deleted file mode 100644
index 54e6e35db41f46..00000000000000
--- a/packages/playground/vue/src-import/script.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { defineComponent } from 'vue'
-import SrcImportStyle from './srcImportStyle.vue'
-import SrcImportStyle2 from './srcImportStyle2.vue'
-
-export default defineComponent({
- components: {
- SrcImportStyle,
- SrcImportStyle2
- },
- setup() {
- return {
- msg: 'hello from script src!'
- }
- }
-})
diff --git a/packages/playground/vue/src-import/srcImportStyle.vue b/packages/playground/vue/src-import/srcImportStyle.vue
deleted file mode 100644
index de91769858fe93..00000000000000
--- a/packages/playground/vue/src-import/srcImportStyle.vue
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
- {{ msg }}
-
-
diff --git a/packages/playground/vue/src-import/srcImportStyle2.vue b/packages/playground/vue/src-import/srcImportStyle2.vue
deleted file mode 100644
index 1e0f327413103e..00000000000000
--- a/packages/playground/vue/src-import/srcImportStyle2.vue
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- This should be tan
-
diff --git a/packages/playground/vue/src-import/style.css b/packages/playground/vue/src-import/style.css
deleted file mode 100644
index 49ab2d93176f4f..00000000000000
--- a/packages/playground/vue/src-import/style.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.src-imports-style {
- color: tan;
-}
diff --git a/packages/playground/vue/src-import/style2.css b/packages/playground/vue/src-import/style2.css
deleted file mode 100644
index 8c93cb983cc09d..00000000000000
--- a/packages/playground/vue/src-import/style2.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.src-imports-script {
- color: #0088ff;
-}
diff --git a/packages/playground/vue/src-import/template.html b/packages/playground/vue/src-import/template.html
deleted file mode 100644
index 6b55c545daac6a..00000000000000
--- a/packages/playground/vue/src-import/template.html
+++ /dev/null
@@ -1,5 +0,0 @@
-SFC Src Imports
-{{ msg }}
-This should be tan
-
-
diff --git a/packages/playground/vue/vite.config.ts b/packages/playground/vue/vite.config.ts
deleted file mode 100644
index f99a68ce8b6b10..00000000000000
--- a/packages/playground/vue/vite.config.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { defineConfig, splitVendorChunkPlugin } from 'vite'
-import vuePlugin from '@vitejs/plugin-vue'
-import { vueI18nPlugin } from './CustomBlockPlugin'
-
-export default defineConfig({
- resolve: {
- alias: {
- '/@': __dirname
- }
- },
- plugins: [
- vuePlugin({
- reactivityTransform: true
- }),
- splitVendorChunkPlugin(),
- vueI18nPlugin
- ],
- build: {
- // to make tests faster
- minify: false,
- rollupOptions: {
- output: {
- // Test splitVendorChunkPlugin composition
- manualChunks(id) {
- if (id.includes('src-import')) {
- return 'src-import'
- }
- }
- }
- }
- },
- css: {
- modules: {
- localsConvention: 'camelCaseOnly'
- }
- }
-})
diff --git a/packages/playground/wasm/__tests__/wasm.spec.ts b/packages/playground/wasm/__tests__/wasm.spec.ts
deleted file mode 100644
index 112617212251fa..00000000000000
--- a/packages/playground/wasm/__tests__/wasm.spec.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { untilUpdated } from '../../testUtils'
-
-test('should work when inlined', async () => {
- await page.click('.inline-wasm .run')
- await untilUpdated(() => page.textContent('.inline-wasm .result'), '42')
-})
-
-test('should work when output', async () => {
- await page.click('.output-wasm .run')
- await untilUpdated(() => page.textContent('.output-wasm .result'), '24')
-})
-
-test('should work when wasm in worker', async () => {
- await untilUpdated(() => page.textContent('.worker-wasm .result'), '3')
-})
diff --git a/packages/playground/wasm/index.html b/packages/playground/wasm/index.html
deleted file mode 100644
index ecb0b66e913fbb..00000000000000
--- a/packages/playground/wasm/index.html
+++ /dev/null
@@ -1,54 +0,0 @@
-Web Assembly
-
-
-
When wasm is inline, result should be 42
- Click to run
-
-
-
-
-
When wasm is output, result should be 24
- Click to run
-
-
-
-
-
worker wasm
-
-
-
-
diff --git a/packages/playground/wasm/package.json b/packages/playground/wasm/package.json
deleted file mode 100644
index 9d903be37887b8..00000000000000
--- a/packages/playground/wasm/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "name": "test-wasm",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- }
-}
diff --git a/packages/playground/wasm/vite.config.ts b/packages/playground/wasm/vite.config.ts
deleted file mode 100644
index 43833d2f95d302..00000000000000
--- a/packages/playground/wasm/vite.config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { defineConfig } from 'vite'
-export default defineConfig({
- build: {
- // make can no emit light.wasm
- // and emit add.wasm
- assetsInlineLimit: 80
- }
-})
diff --git a/packages/playground/wasm/worker.js b/packages/playground/wasm/worker.js
deleted file mode 100644
index a483865bd42bff..00000000000000
--- a/packages/playground/wasm/worker.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import init from './add.wasm'
-init().then((exports) => {
- // eslint-disable-next-line no-undef
- self.postMessage({ result: exports.add(1, 2) })
-})
diff --git a/packages/playground/worker/__tests__/worker.spec.ts b/packages/playground/worker/__tests__/worker.spec.ts
deleted file mode 100644
index 6d93e810c0c510..00000000000000
--- a/packages/playground/worker/__tests__/worker.spec.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import fs from 'fs'
-import path from 'path'
-import { untilUpdated, isBuild, testDir } from '../../testUtils'
-import type { Page } from 'playwright-chromium'
-
-test('normal', async () => {
- await page.click('.ping')
- await untilUpdated(() => page.textContent('.pong'), 'pong')
- await untilUpdated(
- () => page.textContent('.mode'),
- isBuild ? 'production' : 'development'
- )
- await untilUpdated(
- () => page.textContent('.bundle-with-plugin'),
- 'worker bundle with plugin success!'
- )
-})
-
-test('TS output', async () => {
- await page.click('.ping-ts-output')
- await untilUpdated(() => page.textContent('.pong-ts-output'), 'pong')
-})
-
-test('inlined', async () => {
- await page.click('.ping-inline')
- await untilUpdated(() => page.textContent('.pong-inline'), 'pong')
-})
-
-const waitSharedWorkerTick = (
- (resolvedSharedWorkerCount: number) => async (page: Page) => {
- await untilUpdated(async () => {
- const count = await page.textContent('.tick-count')
- // ignore the initial 0
- return count === '1' ? 'page loaded' : ''
- }, 'page loaded')
- // test.concurrent sequential is not guaranteed
- // force page to wait to ensure two pages overlap in time
- resolvedSharedWorkerCount++
- if (resolvedSharedWorkerCount < 2) return
-
- await untilUpdated(() => {
- return resolvedSharedWorkerCount === 2 ? 'all pages loaded' : ''
- }, 'all pages loaded')
- }
-)(0)
-
-test.concurrent.each([[true], [false]])('shared worker', async (doTick) => {
- if (doTick) {
- await page.click('.tick-shared')
- }
- await waitSharedWorkerTick(page)
-})
-
-test('worker emitted', async () => {
- await untilUpdated(() => page.textContent('.nested-worker'), 'pong')
- await untilUpdated(
- () => page.textContent('.nested-worker-dynamic-import'),
- '"msg":"pong"'
- )
-})
-
-if (isBuild) {
- const assetsDir = path.resolve(testDir, 'dist/assets')
- // assert correct files
- test('inlined code generation', async () => {
- const files = fs.readdirSync(assetsDir)
- expect(files.length).toBe(11)
- const index = files.find((f) => f.includes('index'))
- const content = fs.readFileSync(path.resolve(assetsDir, index), 'utf-8')
- const worker = files.find((f) => f.includes('my-worker'))
- const workerContent = fs.readFileSync(
- path.resolve(assetsDir, worker),
- 'utf-8'
- )
-
- // worker should have all imports resolved and no exports
- expect(workerContent).not.toMatch(`import`)
- expect(workerContent).not.toMatch(`export`)
- // chunk
- expect(content).toMatch(`new Worker("/assets`)
- expect(content).toMatch(`new SharedWorker("/assets`)
- // inlined
- expect(content).toMatch(`(window.URL||window.webkitURL).createObjectURL`)
- expect(content).toMatch(`window.Blob`)
- })
-}
-
-test('classic worker is run', async () => {
- expect(await page.textContent('.classic-worker')).toMatch('A classic')
- expect(await page.textContent('.classic-shared-worker')).toMatch('A classic')
-})
diff --git a/packages/playground/worker/classic-worker.js b/packages/playground/worker/classic-worker.js
deleted file mode 100644
index bb6f9c3f49fc84..00000000000000
--- a/packages/playground/worker/classic-worker.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// prettier-ignore
-function text(el, text) {
- document.querySelector(el).textContent = text
-}
-
-const classicWorker = new Worker(
- new URL('./newUrl/classic-worker.js', import.meta.url) /* , */ ,
- // test comment
-
-)
-
-classicWorker.addEventListener('message', ({ data }) => {
- text('.classic-worker', data)
-})
-classicWorker.postMessage('ping')
-
-const classicSharedWorker = new SharedWorker(
- new URL('./newUrl/classic-shared-worker.js', import.meta.url),
- {
- type: 'classic'
- }
-)
-classicSharedWorker.port.addEventListener('message', (ev) => {
- text(
- '.classic-shared-worker',
- ev.data
- )
-})
-classicSharedWorker.port.start()
diff --git a/packages/playground/worker/index.html b/packages/playground/worker/index.html
deleted file mode 100644
index b3525da299ff5a..00000000000000
--- a/packages/playground/worker/index.html
+++ /dev/null
@@ -1,132 +0,0 @@
-Expected values:
-Ping
-
- Response from worker:
-
-bundle-with-plugin:
-
-Ping Inline Worker
-Response from inline worker:
-
-Ping Possible Compiled TS Worker
-
- Response from worker imported from code that might be compiled TS:
-
-
-
-Tick Shared Worker
-
- Tick from shared worker, it syncs between pages:
- 0
-
-
-new Worker(new Url('path', import.meta.url), { type: 'module' })
-
-
-new SharedWorker(new Url('path', import.meta.url), { type: 'module' })
-
-
-nested worker
-
-
-new Worker(new Url('path', import.meta.url))
-
-
-new Worker(new Url('path', import.meta.url), { type: 'classic' })
-
-
-
diff --git a/packages/playground/worker/my-shared-worker.ts b/packages/playground/worker/my-shared-worker.ts
deleted file mode 100644
index cd5b24f265b955..00000000000000
--- a/packages/playground/worker/my-shared-worker.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-let count = 0
-const ports = new Set()
-
-onconnect = (event) => {
- const port = event.ports[0]
- ports.add(port)
- port.postMessage(count)
- port.onmessage = (message) => {
- if (message.data === 'tick') {
- count++
- ports.forEach((p) => {
- p.postMessage(count)
- })
- }
- }
-}
diff --git a/packages/playground/worker/my-worker.ts b/packages/playground/worker/my-worker.ts
deleted file mode 100644
index 550382be72c331..00000000000000
--- a/packages/playground/worker/my-worker.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { msg, mode } from './workerImport'
-import { bundleWithPlugin } from './test-plugin'
-
-self.onmessage = (e) => {
- if (e.data === 'ping') {
- self.postMessage({ msg, mode, bundleWithPlugin })
- }
-}
diff --git a/packages/playground/worker/newUrl/classic-shared-worker.js b/packages/playground/worker/newUrl/classic-shared-worker.js
deleted file mode 100644
index 462e49dfa8847f..00000000000000
--- a/packages/playground/worker/newUrl/classic-shared-worker.js
+++ /dev/null
@@ -1,6 +0,0 @@
-importScripts('/classic.js')
-
-self.onconnect = (event) => {
- const port = event.ports[0]
- port.postMessage(self.constant)
-}
diff --git a/packages/playground/worker/newUrl/classic-worker.js b/packages/playground/worker/newUrl/classic-worker.js
deleted file mode 100644
index 865810c76fbf85..00000000000000
--- a/packages/playground/worker/newUrl/classic-worker.js
+++ /dev/null
@@ -1,5 +0,0 @@
-importScripts('/classic.js')
-
-self.addEventListener('message', () => {
- self.postMessage(self.constant)
-})
diff --git a/packages/playground/worker/newUrl/url-shared-worker.js b/packages/playground/worker/newUrl/url-shared-worker.js
deleted file mode 100644
index f52de169243056..00000000000000
--- a/packages/playground/worker/newUrl/url-shared-worker.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import constant from './module'
-
-self.onconnect = (event) => {
- const port = event.ports[0]
- port.postMessage(constant)
-}
diff --git a/packages/playground/worker/newUrl/url-worker.js b/packages/playground/worker/newUrl/url-worker.js
deleted file mode 100644
index afd91bfe613dc2..00000000000000
--- a/packages/playground/worker/newUrl/url-worker.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import constant from './module'
-
-self.postMessage(constant)
diff --git a/packages/playground/worker/package.json b/packages/playground/worker/package.json
deleted file mode 100644
index 131df8c4cbf336..00000000000000
--- a/packages/playground/worker/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "test-worker",
- "private": true,
- "version": "0.0.0",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "debug": "node --inspect-brk ../../vite/bin/vite",
- "preview": "vite preview"
- },
- "devDependencies": {
- "@vitejs/plugin-vue-jsx": "workspace:*"
- }
-}
diff --git a/packages/playground/worker/possible-ts-output-worker.mjs b/packages/playground/worker/possible-ts-output-worker.mjs
deleted file mode 100644
index 2bcce3faa8a50e..00000000000000
--- a/packages/playground/worker/possible-ts-output-worker.mjs
+++ /dev/null
@@ -1,7 +0,0 @@
-import { msg, mode } from './workerImport'
-
-self.onmessage = (e) => {
- if (e.data === 'ping') {
- self.postMessage({ msg, mode })
- }
-}
diff --git a/packages/playground/worker/sub-worker.js b/packages/playground/worker/sub-worker.js
deleted file mode 100644
index ab64b3667099bb..00000000000000
--- a/packages/playground/worker/sub-worker.js
+++ /dev/null
@@ -1,13 +0,0 @@
-self.onmessage = (event) => {
- if (event.data === 'ping') {
- self.postMessage('pong')
- }
-}
-const data = import('./workerImport')
-data.then((data) => {
- const { mode, msg } = data
- self.postMessage({
- mode,
- msg
- })
-})
diff --git a/packages/playground/worker/test-plugin.tsx b/packages/playground/worker/test-plugin.tsx
deleted file mode 100644
index 15b6b94f460bc3..00000000000000
--- a/packages/playground/worker/test-plugin.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export const bundleWithPlugin: string = 'worker bundle with plugin success!'
diff --git a/packages/playground/worker/vite.config.ts b/packages/playground/worker/vite.config.ts
deleted file mode 100644
index 6cef7d9cea0bed..00000000000000
--- a/packages/playground/worker/vite.config.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import vueJsx from '@vitejs/plugin-vue-jsx'
-import { defineConfig } from 'vite'
-
-export default defineConfig({
- worker: {
- format: 'es',
- plugins: [vueJsx()]
- }
-})
diff --git a/packages/playground/worker/worker-nested-worker.js b/packages/playground/worker/worker-nested-worker.js
deleted file mode 100644
index 6d4d1e4969005f..00000000000000
--- a/packages/playground/worker/worker-nested-worker.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import SubWorker from './sub-worker?worker'
-
-const subWorker = new SubWorker()
-
-self.onmessage = (event) => {
- if (event.data === 'ping') {
- subWorker.postMessage('ping')
- }
-}
-
-subWorker.onmessage = (event) => {
- self.postMessage(event.data)
-}
diff --git a/packages/plugin-legacy/CHANGELOG.md b/packages/plugin-legacy/CHANGELOG.md
index e92ca1e12357fe..9f4875dd1db7fb 100644
--- a/packages/plugin-legacy/CHANGELOG.md
+++ b/packages/plugin-legacy/CHANGELOG.md
@@ -1,291 +1,1028 @@
-## [1.7.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.7.0...plugin-legacy@1.7.1) (2022-02-11)
+## [8.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@8.0.0...plugin-legacy@8.0.1) (2026-03-26)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#21988](https://github.com/vitejs/vite/issues/21988)) ([9b7d150](https://github.com/vitejs/vite/commit/9b7d15002a75474421bc5636238336d88c7fe2f3))
+* **legacy:** workaround safari 15 error caching bug ([#22028](https://github.com/vitejs/vite/issues/22028)) ([eb65d63](https://github.com/vitejs/vite/commit/eb65d6351876dfad672fc6a7ebb3eeebe5ba9f23))
+
+### Miscellaneous Chores
+
+* add changelog rearrange script ([#21835](https://github.com/vitejs/vite/issues/21835)) ([efef073](https://github.com/vitejs/vite/commit/efef073a6f71be0330bd72784654ed8b8dd60cbf))
+* **deps:** update rolldown-related dependencies ([#21787](https://github.com/vitejs/vite/issues/21787)) ([1af1d3a](https://github.com/vitejs/vite/commit/1af1d3a3a4fd62fa581392b2dec9052efe8485b3))
+
+## [8.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@7.2.1...plugin-legacy@8.0.0) (2026-03-12)
+
+### ⚠ BREAKING CHANGES
+
+* **legacy:** bump modern browser threshold to `import.meta.resolve` support (#21662)
+
+### Features
+
+* **legacy:** bump modern browser threshold to `import.meta.resolve` support ([#21662](https://github.com/vitejs/vite/issues/21662)) ([cc50822](https://github.com/vitejs/vite/commit/cc50822161f43ac60b932f2b4f63129bdb5590dc))
+* the epic `rolldown-vite` merge ([#21189](https://github.com/vitejs/vite/issues/21189)) ([4a7f8d4](https://github.com/vitejs/vite/commit/4a7f8d43e6b14b89fef278c3ea86f9e3f64b7fc2))
### Bug Fixes
-* require Vite 2.8.0 ([#6272](https://github.com/vitejs/vite/issues/6272)) ([#6869](https://github.com/vitejs/vite/issues/6869)) ([997b8f1](https://github.com/vitejs/vite/commit/997b8f11cb156cc374ae991875a09534b5489a93))
+* **deps:** update all non-major dependencies ([#21691](https://github.com/vitejs/vite/issues/21691)) ([521fdc0](https://github.com/vitejs/vite/commit/521fdc0ced51ddee7f728e6f891f36ebc6c0e1ce))
+* **deps:** update all non-major dependencies ([#21786](https://github.com/vitejs/vite/issues/21786)) ([eaa4352](https://github.com/vitejs/vite/commit/eaa4352af8f8658e3a10a9945ad9c227fcb2f28a))
+* **legacy:** skip preload helper in legacy chunks ([#21607](https://github.com/vitejs/vite/issues/21607)) ([0f2b7ae](https://github.com/vitejs/vite/commit/0f2b7aed683657f62bdce4a2fbb2883cd3af7d67))
+* **deps:** update all non-major dependencies ([#21488](https://github.com/vitejs/vite/issues/21488)) ([2b32ca2](https://github.com/vitejs/vite/commit/2b32ca24fe9d742901c2cb5c88e6b1fd734f8c73))
+* **deps:** update all non-major dependencies ([#21540](https://github.com/vitejs/vite/issues/21540)) ([9ebaeaa](https://github.com/vitejs/vite/commit/9ebaeaac094db996b1d12665052633c20ac8a9cf))
+* **legacy:** use `prebuilt-chunk` for polyfill chunks ([#21498](https://github.com/vitejs/vite/issues/21498)) ([7999843](https://github.com/vitejs/vite/commit/79998430eb7cd549a7fb803882447c4a15b11899))
+* **deps:** update all non-major dependencies ([#21231](https://github.com/vitejs/vite/issues/21231)) ([859789c](https://github.com/vitejs/vite/commit/859789c856412dfa67969232ddda1df754febf40))
+* **deps:** update all non-major dependencies ([#21389](https://github.com/vitejs/vite/issues/21389)) ([30f48df](https://github.com/vitejs/vite/commit/30f48df33ec9e9bd0b8164461eede5574398370b))
+
+### Documentation
+
+* **legacy:** update stale description ([#21610](https://github.com/vitejs/vite/issues/21610)) ([9037c29](https://github.com/vitejs/vite/commit/9037c29ec608716fe7c6803cbb5cb0dfe8746e4a))
+### Miscellaneous Chores
+* **legacy:** update peer dep Vite to 8 ([4595aeb](https://github.com/vitejs/vite/commit/4595aeb5b84126ed855ed5a25cd9959adc2d141c))
+* **deps:** update dependency tsdown to ^0.20.3 ([#21593](https://github.com/vitejs/vite/issues/21593)) ([e3f6ac9](https://github.com/vitejs/vite/commit/e3f6ac9326bf7c31a916e7c5dd90277de4c262c0))
+* **deps:** update rolldown-related dependencies ([#21487](https://github.com/vitejs/vite/issues/21487)) ([5863e51](https://github.com/vitejs/vite/commit/5863e513fab6b481cfb42da86202f9db728c077d))
+* cleanup changelog ([#21202](https://github.com/vitejs/vite/issues/21202)) ([8c8c56e](https://github.com/vitejs/vite/commit/8c8c56e1eb465e6dcd0c1b40f187228edc0e2be4))
+* **deps:** update dependency tsdown to ^0.17.4 ([#21284](https://github.com/vitejs/vite/issues/21284)) ([43f061a](https://github.com/vitejs/vite/commit/43f061adc677d40ce226de4dd07ee9a1f5e4ca73))
+* **deps:** update dependency tsdown to ^0.18.4 ([#21344](https://github.com/vitejs/vite/issues/21344)) ([964c718](https://github.com/vitejs/vite/commit/964c718a382ff46ec1f906d7d6bc3f135a6dcd3f))
+* **deps:** update rolldown-related dependencies ([#21230](https://github.com/vitejs/vite/issues/21230)) ([9349446](https://github.com/vitejs/vite/commit/9349446e9344bd81ccfb37af482f479cd1b59bbc))
+* **deps:** update rolldown-related dependencies ([#21390](https://github.com/vitejs/vite/issues/21390)) ([be9dd4e](https://github.com/vitejs/vite/commit/be9dd4e08d899f9ed27f2bdcb81bf27d018377a6))
+* **legacy:** add metadata for vite-plugin-registry ([#21453](https://github.com/vitejs/vite/issues/21453)) ([2723c6c](https://github.com/vitejs/vite/commit/2723c6c849a0022e682b7fdccb7c5d109177e1c3))
-# [1.7.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.4...plugin-legacy@1.7.0) (2022-02-09)
+### Code Refactoring
+* use `import.meta.dirname` everywhere ([#21509](https://github.com/vitejs/vite/issues/21509)) ([7becf5f](https://github.com/vitejs/vite/commit/7becf5f8fe9041cff60f495ef975faaba68f9eb2))
+
+### Beta Changelogs
+
+#### [8.0.0-beta.3](https://github.com/vitejs/vite/compare/plugin-legacy@8.0.0-beta.2...plugin-legacy@8.0.0-beta.3) (2026-02-12)
+
+See [8.0.0-beta.3 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@8.0.0-beta.3/packages/plugin-legacy/CHANGELOG.md)
+
+#### [8.0.0-beta.2](https://github.com/vitejs/vite/compare/plugin-legacy@8.0.0-beta.1...plugin-legacy@8.0.0-beta.2) (2026-02-03)
+
+See [8.0.0-beta.2 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@8.0.0-beta.2/packages/plugin-legacy/CHANGELOG.md)
+
+#### [8.0.0-beta.1](https://github.com/vitejs/vite/compare/plugin-legacy@8.0.0-beta.0...plugin-legacy@8.0.0-beta.1) (2026-01-22)
+
+See [8.0.0-beta.1 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@8.0.0-beta.1/packages/plugin-legacy/CHANGELOG.md)
+
+#### [8.0.0-beta.0](https://github.com/vitejs/vite/compare/plugin-legacy@7.2.1...plugin-legacy@8.0.0-beta.0) (2025-12-03)
+
+See [8.0.0-beta.0 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@8.0.0-beta.0/packages/plugin-legacy/CHANGELOG.md)
+
+## [7.2.1](https://github.com/vitejs/vite/compare/plugin-legacy@7.2.0...plugin-legacy@7.2.1) (2025-08-07)
+### Features
+* **legacy:** update plugin-legacy code for rolldown-vite ([6401a49](https://github.com/vitejs/vite/commit/6401a49d53fbff8a4a519bb2ad107b8a223250d6))
+
+## [7.2.0](https://github.com/vitejs/vite/compare/plugin-legacy@7.1.0...plugin-legacy@7.2.0) (2025-08-07)
### Bug Fixes
-* don't force terser on non-legacy (fix [#6266](https://github.com/vitejs/vite/issues/6266)) ([#6272](https://github.com/vitejs/vite/issues/6272)) ([1da104e](https://github.com/vitejs/vite/commit/1da104e8597e2965313e8cd582d032bca551e4ee))
-* **legacy:** fix conflict with the modern build on css emitting ([#6584](https://github.com/vitejs/vite/issues/6584)) ([f48255e](https://github.com/vitejs/vite/commit/f48255e6e0058e973b949fb4a2372974f0480e11)), closes [#3296](https://github.com/vitejs/vite/issues/3296) [#3317](https://github.com/vitejs/vite/issues/3317) [/github.com/vitejs/vite/commit/6bce1081991501f3779bff1a81e5dd1e63e5d38e#diff-2cfbd4f4d8c32727cd8e1a561cffbde0b384a3ce0789340440e144f9d64c10f6R262-R263](https://github.com//github.com/vitejs/vite/commit/6bce1081991501f3779bff1a81e5dd1e63e5d38e/issues/diff-2cfbd4f4d8c32727cd8e1a561cffbde0b384a3ce0789340440e144f9d64c10f6R262-R263)
+* **deps:** update all non-major dependencies ([#20537](https://github.com/vitejs/vite/issues/20537)) ([fc9a9d3](https://github.com/vitejs/vite/commit/fc9a9d3f1493caa3d614f64e0a61fd5684f0928b))
+* **legacy:** `modernTargets` should set `build.target` ([#20393](https://github.com/vitejs/vite/issues/20393)) ([76c5e40](https://github.com/vitejs/vite/commit/76c5e40864f42bb33ee7ea9184e32d5156fa1a4a))
+
+### Miscellaneous Chores
+
+* **deps:** update rolldown-related dependencies ([#20441](https://github.com/vitejs/vite/issues/20441)) ([f689d61](https://github.com/vitejs/vite/commit/f689d613429ae9452c74f8bc482d8cc2584ea6b8))
+* **deps:** update rolldown-related dependencies ([#20536](https://github.com/vitejs/vite/issues/20536)) ([8be2787](https://github.com/vitejs/vite/commit/8be278748a92b128c49a24619d8d537dd2b08ceb))
+
+## [7.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@7.0.1...plugin-legacy@7.1.0) (2025-07-22)
+### Features
+
+* **legacy:** add rolldown-vite support ([#20417](https://github.com/vitejs/vite/issues/20417)) ([ab62ca4](https://github.com/vitejs/vite/commit/ab62ca4897aa969fb72508656da2eae7fb3906be))
+
+## [7.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@7.0.0...plugin-legacy@7.0.1) (2025-07-17)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#20324](https://github.com/vitejs/vite/issues/20324)) ([3e81af3](https://github.com/vitejs/vite/commit/3e81af38a80c7617aba6bf3300d8b4267570f9cf))
+* **deps:** update all non-major dependencies ([#20366](https://github.com/vitejs/vite/issues/20366)) ([43ac73d](https://github.com/vitejs/vite/commit/43ac73da27b3907c701e95e6a7d28fde659729ec))
+* **deps:** update all non-major dependencies ([#20406](https://github.com/vitejs/vite/issues/20406)) ([1a1cc8a](https://github.com/vitejs/vite/commit/1a1cc8a435a21996255b3e5cc75ed4680de2a7f3))
+* **legacy:** don't lower CSS if legacy chunks are not generated ([#20392](https://github.com/vitejs/vite/issues/20392)) ([d2c81f7](https://github.com/vitejs/vite/commit/d2c81f7c13030c08becd8a768182074eedb87333))
+
+### Performance Improvements
+
+* **legacy:** skip lowering when detecting polyfills ([#20387](https://github.com/vitejs/vite/issues/20387)) ([7cc0338](https://github.com/vitejs/vite/commit/7cc0338de3a67597956af58e931e46e7913c063b))
+### Miscellaneous Chores
+* **deps:** update rolldown-related dependencies ([#20323](https://github.com/vitejs/vite/issues/20323)) ([30d2f1b](https://github.com/vitejs/vite/commit/30d2f1b38c72387ffdca3ee4746730959a020b59))
+* group commits by category in changelog ([#20310](https://github.com/vitejs/vite/issues/20310)) ([41e83f6](https://github.com/vitejs/vite/commit/41e83f62b1adb65f5af4c1ec006de1c845437edc))
-## [1.6.4](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.3...plugin-legacy@1.6.4) (2021-12-07)
+### Code Refactoring
+* **legacy:** use Rollup type export from Vite ([#20335](https://github.com/vitejs/vite/issues/20335)) ([d62dc33](https://github.com/vitejs/vite/commit/d62dc3321db05d91e74facff51799496ce8601f3))
+* use `foo.endsWith("bar")` instead of `/bar$/.test(foo)` ([#20413](https://github.com/vitejs/vite/issues/20413)) ([862e192](https://github.com/vitejs/vite/commit/862e192d21f66039635a998724bdc6b94fd293a0))
+## [7.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@6.1.1...plugin-legacy@7.0.0) (2025-06-24)
-## [1.6.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.2...plugin-legacy@1.6.3) (2021-11-22)
+### ⚠ BREAKING CHANGES
+* **legacy:** remove `location.protocol!="file:"` condition for modern android webview (#20179)
+* bump required node version to 20.19+, 22.12+ and remove cjs build (#20032)
+* remove node 18 support (#19972)
### Bug Fixes
-* **build:** resolve `rollupOptions.input` paths ([#5601](https://github.com/vitejs/vite/issues/5601)) ([5b6b016](https://github.com/vitejs/vite/commit/5b6b01693720290e8998b2613f0dcb2d699ee84f))
+* **deps:** update all non-major dependencies ([#20141](https://github.com/vitejs/vite/issues/20141)) ([89ca65b](https://github.com/vitejs/vite/commit/89ca65ba1d849046dccdea52e9eca980f331be26))
+* **deps:** update all non-major dependencies ([#20181](https://github.com/vitejs/vite/issues/20181)) ([d91d4f7](https://github.com/vitejs/vite/commit/d91d4f7ad55edbcb4a51fc23376cbff89f776d30))
+* **legacy:** remove `location.protocol!="file:"` condition for modern android webview ([#20179](https://github.com/vitejs/vite/issues/20179)) ([a6d5997](https://github.com/vitejs/vite/commit/a6d599718ee109798e8f552e317f175513d157e7))
+* **deps:** update all non-major dependencies ([#19953](https://github.com/vitejs/vite/issues/19953)) ([ac8e1fb](https://github.com/vitejs/vite/commit/ac8e1fb289a06fc0671dab1f4ef68e508e34360e))
+
+### Miscellaneous Chores
+
+* **deps:** update rolldown-related dependencies ([#20270](https://github.com/vitejs/vite/issues/20270)) ([f7377c3](https://github.com/vitejs/vite/commit/f7377c3eae6323bd3237ff5de5ae55c879fe7325))
+* **legacy:** update peer dep Vite to 7 ([8ff13cd](https://github.com/vitejs/vite/commit/8ff13cdba1c57284eb8f4586b52f814fcf5afcdf))
+* **deps:** update rolldown-related dependencies ([#20140](https://github.com/vitejs/vite/issues/20140)) ([0387447](https://github.com/vitejs/vite/commit/03874471e3de14e7d2f474ecb354499e7f5eb418))
+* **deps:** update rolldown-related dependencies ([#20182](https://github.com/vitejs/vite/issues/20182)) ([6172f41](https://github.com/vitejs/vite/commit/6172f410b44cbae8d052997bb1819a6197a4d397))
+* remove node 18 support ([#19972](https://github.com/vitejs/vite/issues/19972)) ([00b8a98](https://github.com/vitejs/vite/commit/00b8a98f36376804437e1342265453915ae613de))
+* use tsdown ([#20065](https://github.com/vitejs/vite/issues/20065)) ([d488efd](https://github.com/vitejs/vite/commit/d488efda95ff40f63684194d51858f84c3d05379))
+
+### Code Refactoring
+* bump required node version to 20.19+, 22.12+ and remove cjs build ([#20032](https://github.com/vitejs/vite/issues/20032)) ([2b80243](https://github.com/vitejs/vite/commit/2b80243fada75378e80475028fdcc78f4432bd6f))
+### Beta Changelogs
-## [1.6.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.1...plugin-legacy@1.6.2) (2021-10-11)
+#### [7.0.0-beta.1](https://github.com/vitejs/vite/compare/plugin-legacy@7.0.0-beta.0...plugin-legacy@7.0.0-beta.1) (2025-06-17)
+
+See [7.0.0-beta.1 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@7.0.0-beta.1/packages/plugin-legacy/CHANGELOG.md)
+
+#### [7.0.0-beta.0](https://github.com/vitejs/vite/compare/plugin-legacy@6.1.1...plugin-legacy@7.0.0-beta.0) (2025-06-02)
+
+See [7.0.0-beta.0 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@7.0.0-beta.0/packages/plugin-legacy/CHANGELOG.md)
+
+## [6.1.1](https://github.com/vitejs/vite/compare/plugin-legacy@6.1.0...plugin-legacy@6.1.1) (2025-04-28)
+### Bug Fixes
+* **legacy:** use unbuild 3.4 for now ([#19928](https://github.com/vitejs/vite/issues/19928)) ([96f73d1](https://github.com/vitejs/vite/commit/96f73d16c8501013be57aee1c8a2353a56460281))
+## [6.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@6.0.2...plugin-legacy@6.1.0) (2025-04-16)
### Features
-* add `build.cssTarget` option ([#5132](https://github.com/vitejs/vite/issues/5132)) ([b17444f](https://github.com/vitejs/vite/commit/b17444fd97b02bc54410c8575e7d3cb25e4058c2)), closes [#4746](https://github.com/vitejs/vite/issues/4746) [#5070](https://github.com/vitejs/vite/issues/5070) [#4930](https://github.com/vitejs/vite/issues/4930)
+* **legacy:** add 'assumptions' option ([#19719](https://github.com/vitejs/vite/issues/19719)) ([d1d99c9](https://github.com/vitejs/vite/commit/d1d99c9220989ce903dea9cae6c3608f57f377ea))
+* **legacy:** add sourcemapBaseUrl support ([#19281](https://github.com/vitejs/vite/issues/19281)) ([a92c74b](https://github.com/vitejs/vite/commit/a92c74b088a253257c01596bd7c67e0f8fa39512))
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#19555](https://github.com/vitejs/vite/issues/19555)) ([f612e0f](https://github.com/vitejs/vite/commit/f612e0fdf6810317b61fcca1ded125755f261d78))
+* **deps:** update all non-major dependencies ([#19613](https://github.com/vitejs/vite/issues/19613)) ([363d691](https://github.com/vitejs/vite/commit/363d691b4995d72f26a14eb59ed88a9483b1f931))
+* **deps:** update all non-major dependencies ([#19649](https://github.com/vitejs/vite/issues/19649)) ([f4e712f](https://github.com/vitejs/vite/commit/f4e712ff861f8a9504594a4a5e6d35a7547e5a7e))
-## [1.6.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.0...plugin-legacy@1.6.1) (2021-10-05)
+### Code Refactoring
+* restore endsWith usage ([#19554](https://github.com/vitejs/vite/issues/19554)) ([6113a96](https://github.com/vitejs/vite/commit/6113a9670cc9b7d29fe0bffe033f7823e36ded00))
+## [6.0.2](https://github.com/vitejs/vite/compare/plugin-legacy@6.0.1...plugin-legacy@6.0.2) (2025-02-25)
### Bug Fixes
-* **plugin-legacy:** use terser as the default minifier ([#5168](https://github.com/vitejs/vite/issues/5168)) ([9ee7234](https://github.com/vitejs/vite/commit/9ee72343884a7d679767833f7a659bbca6b96595))
+* **deps:** update all non-major dependencies ([#19392](https://github.com/vitejs/vite/issues/19392)) ([60456a5](https://github.com/vitejs/vite/commit/60456a54fe90872dbd4bed332ecbd85bc88deb92))
+* **deps:** update all non-major dependencies ([#19440](https://github.com/vitejs/vite/issues/19440)) ([ccac73d](https://github.com/vitejs/vite/commit/ccac73d9d0e92c7232f09207d1d6b893e823ed8e))
+* **legacy:** warn if plugin-legacy is passed to `worker.plugins` ([#19079](https://github.com/vitejs/vite/issues/19079)) ([171f2fb](https://github.com/vitejs/vite/commit/171f2fbe0afe09eeb49f5f29f9ecd845c39a8401))
+
+### Miscellaneous Chores
+
+* fix typos ([#19398](https://github.com/vitejs/vite/issues/19398)) ([b44e3d4](https://github.com/vitejs/vite/commit/b44e3d43db65babe1c32e143964add02e080dc15))
+
+## [6.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@6.0.0...plugin-legacy@6.0.1) (2025-02-05)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#18853](https://github.com/vitejs/vite/issues/18853)) ([5c02236](https://github.com/vitejs/vite/commit/5c0223636fa277d5daeb4d93c3f32d9f3cd69fc5))
+* **deps:** update all non-major dependencies ([#18967](https://github.com/vitejs/vite/issues/18967)) ([d88d000](https://github.com/vitejs/vite/commit/d88d0004a8e891ca6026d356695e0b319caa7fce))
+* **deps:** update all non-major dependencies ([#19098](https://github.com/vitejs/vite/issues/19098)) ([8639538](https://github.com/vitejs/vite/commit/8639538e6498d1109da583ad942c1472098b5919))
+* **deps:** update all non-major dependencies ([#19190](https://github.com/vitejs/vite/issues/19190)) ([f2c07db](https://github.com/vitejs/vite/commit/f2c07dbfc874b46f6e09bb04996d0514663e4544))
+* **deps:** update all non-major dependencies ([#19296](https://github.com/vitejs/vite/issues/19296)) ([2bea7ce](https://github.com/vitejs/vite/commit/2bea7cec4b7fddbd5f2fb6090a7eaf5ae7ca0f1b))
+* **legacy:** build respect `hashCharacters` config ([#19262](https://github.com/vitejs/vite/issues/19262)) ([3aa10b7](https://github.com/vitejs/vite/commit/3aa10b7d618b178aec0f027b1f5fcd3353d2b166))
+* **legacy:** import babel once ([#19152](https://github.com/vitejs/vite/issues/19152)) ([282496d](https://github.com/vitejs/vite/commit/282496daaca43494feceaa59809f6ceafd62dedd))
+
+### Reverts
+
+* update moduleResolution value casing ([#18409](https://github.com/vitejs/vite/issues/18409)) ([#18774](https://github.com/vitejs/vite/issues/18774)) ([b0fc6e3](https://github.com/vitejs/vite/commit/b0fc6e3c2591a30360d3714263cf7cc0e2acbfdf))
+
+## [6.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@5.4.3...plugin-legacy@6.0.0) (2024-11-26)
+### ⚠ BREAKING CHANGES
+* drop node 21 support in version ranges (#18729)
+### Features
+
+* drop node 21 support in version ranges ([#18729](https://github.com/vitejs/vite/issues/18729)) ([a384d8f](https://github.com/vitejs/vite/commit/a384d8fd39162190675abcfea31ba657383a3d03))
+
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#18484](https://github.com/vitejs/vite/issues/18484)) ([2ec12df](https://github.com/vitejs/vite/commit/2ec12df98d07eb4c986737e86a4a9f8066724658))
+* **deps:** update all non-major dependencies ([#18691](https://github.com/vitejs/vite/issues/18691)) ([f005461](https://github.com/vitejs/vite/commit/f005461ecce89ada21cb0c021f7af460b5479736))
-# [1.6.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.3...plugin-legacy@1.6.0) (2021-09-29)
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#18562](https://github.com/vitejs/vite/issues/18562)) ([fb227ec](https://github.com/vitejs/vite/commit/fb227ec4402246b5a13e274c881d9de6dd8082dd))
+* **legacy:** bump terser peer dep to ^5.16 ([#18772](https://github.com/vitejs/vite/issues/18772)) ([3f6d5fe](https://github.com/vitejs/vite/commit/3f6d5fed8739f30cddb821a680576d93b3a60bba))
+* **legacy:** update peer dep Vite to 6 ([#18771](https://github.com/vitejs/vite/issues/18771)) ([63c62b3](https://github.com/vitejs/vite/commit/63c62b3059b589a51d1673bfdcefdb0b4e87c089))
+* **plugin-legacy:** add type module in package.json ([#18535](https://github.com/vitejs/vite/issues/18535)) ([28cefca](https://github.com/vitejs/vite/commit/28cefcaf2861b72901abe1f047d9ec6298b745f8))
+* upgrade to unbuild v3 rc ([#18502](https://github.com/vitejs/vite/issues/18502)) ([ddd5c5d](https://github.com/vitejs/vite/commit/ddd5c5d00ff7894462a608841560883f9c771f22))
+## [5.4.3](https://github.com/vitejs/vite/compare/plugin-legacy@5.4.2...plugin-legacy@5.4.3) (2024-10-25)
### Bug Fixes
-* **deps:** update all non-major dependencies ([#4545](https://github.com/vitejs/vite/issues/4545)) ([a44fd5d](https://github.com/vitejs/vite/commit/a44fd5d38679da0be2536103e83af730cda73a95))
-* esbuild minification and renderLegacyChunks false ([#5054](https://github.com/vitejs/vite/issues/5054)) ([ed384cf](https://github.com/vitejs/vite/commit/ed384cfeff9e3ccb0fdbb07ec91758308da66226))
-* normalize internal plugin names ([#4976](https://github.com/vitejs/vite/issues/4976)) ([37f0b2f](https://github.com/vitejs/vite/commit/37f0b2fff74109d381513ed052a32b43655ee11d))
-* **plugin-legacy:** fix type errors ([#4762](https://github.com/vitejs/vite/issues/4762)) ([5491143](https://github.com/vitejs/vite/commit/5491143be0b4214d2dab91076a85739d6d106481))
+* **deps:** update all non-major dependencies ([#18170](https://github.com/vitejs/vite/issues/18170)) ([c8aea5a](https://github.com/vitejs/vite/commit/c8aea5ae0af90dc6796ef3bdd612d1eb819f157b))
+* **deps:** update all non-major dependencies ([#18292](https://github.com/vitejs/vite/issues/18292)) ([5cac054](https://github.com/vitejs/vite/commit/5cac0544dca2764f0114aac38e9922a0c13d7ef4))
+* **deps:** update all non-major dependencies ([#18345](https://github.com/vitejs/vite/issues/18345)) ([5552583](https://github.com/vitejs/vite/commit/5552583a2272cd4208b30ad60e99d984e34645f0))
+* **legacy:** generate sourcemap for polyfill chunks ([#18250](https://github.com/vitejs/vite/issues/18250)) ([f311ff3](https://github.com/vitejs/vite/commit/f311ff3c2b19636457c3023095ef32ab9a96b84a))
+
+### Performance Improvements
+* use `crypto.hash` when available ([#18317](https://github.com/vitejs/vite/issues/18317)) ([2a14884](https://github.com/vitejs/vite/commit/2a148844cf2382a5377b75066351f00207843352))
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#17945](https://github.com/vitejs/vite/issues/17945)) ([cfb621e](https://github.com/vitejs/vite/commit/cfb621e7a5a3e24d710a9af156e6855e73caf891))
+* **deps:** update all non-major dependencies ([#18050](https://github.com/vitejs/vite/issues/18050)) ([7cac03f](https://github.com/vitejs/vite/commit/7cac03fa5197a72d2e2422bd0243a85a9a18abfc))
+* **deps:** update all non-major dependencies ([#18404](https://github.com/vitejs/vite/issues/18404)) ([802839d](https://github.com/vitejs/vite/commit/802839d48335a69eb15f71f2cd816d0b6e4d3556))
+* enable some eslint rules ([#18084](https://github.com/vitejs/vite/issues/18084)) ([e9a2746](https://github.com/vitejs/vite/commit/e9a2746ca77473b1814fd05db3d299c074135fe5))
+* remove stale TODOs ([#17866](https://github.com/vitejs/vite/issues/17866)) ([e012f29](https://github.com/vitejs/vite/commit/e012f296df583bd133d26399397bd4ae49de1497))
+* update license copyright ([#18278](https://github.com/vitejs/vite/issues/18278)) ([56eb869](https://github.com/vitejs/vite/commit/56eb869a67551a257d20cba00016ea59b1e1a2c4))
+
+## [5.4.2](https://github.com/vitejs/vite/compare/plugin-legacy@5.4.1...plugin-legacy@5.4.2) (2024-08-15)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#17430](https://github.com/vitejs/vite/issues/17430)) ([4453d35](https://github.com/vitejs/vite/commit/4453d3578b343d16a8a5298bf154f280088968d9))
+* **deps:** update all non-major dependencies ([#17494](https://github.com/vitejs/vite/issues/17494)) ([bf123f2](https://github.com/vitejs/vite/commit/bf123f2c6242424a3648cf9234281fd9ff44e3d5))
+* **deps:** update all non-major dependencies ([#17629](https://github.com/vitejs/vite/issues/17629)) ([93281b0](https://github.com/vitejs/vite/commit/93281b0e09ff8b00e21c24b80ed796db89cbc1ef))
+* **deps:** update all non-major dependencies ([#17780](https://github.com/vitejs/vite/issues/17780)) ([e408542](https://github.com/vitejs/vite/commit/e408542748edebd93dba07f21e3fd107725cadca))
+* handle encoded base paths ([#17577](https://github.com/vitejs/vite/issues/17577)) ([720447e](https://github.com/vitejs/vite/commit/720447ee725046323387f661341d44e2ad390f41))
+
+### Performance Improvements
+
+* improve regex performance ([#17789](https://github.com/vitejs/vite/issues/17789)) ([952bae3](https://github.com/vitejs/vite/commit/952bae3efcbd871fc3df5b1947060de7ebdafa36))
+
+### Documentation
+
+* rename cdnjs link ([#17565](https://github.com/vitejs/vite/issues/17565)) ([61357f6](https://github.com/vitejs/vite/commit/61357f67cdb8eca2c551150a1f0329e272f4da62))
+
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#17820](https://github.com/vitejs/vite/issues/17820)) ([bb2f8bb](https://github.com/vitejs/vite/commit/bb2f8bb55fdd64e4f16831ff98921c221a5e734a))
+* extend commit hash ([#17709](https://github.com/vitejs/vite/issues/17709)) ([4fc9b64](https://github.com/vitejs/vite/commit/4fc9b6424c27aca8004c368b69991a56264e4fdb))
+
+## [5.4.1](https://github.com/vitejs/vite/compare/plugin-legacy@5.4.0...plugin-legacy@5.4.1) (2024-05-30)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#17321](https://github.com/vitejs/vite/issues/17321)) ([4a89766](https://github.com/vitejs/vite/commit/4a89766d838527c144f14e842211100b16792018))
+* **plugin-legacy:** group discovered polyfills by output ([#17347](https://github.com/vitejs/vite/issues/17347)) ([c735cc7](https://github.com/vitejs/vite/commit/c735cc7895b34dd760f57145a00ddc1da7526b8c))
+* **plugin-legacy:** improve deterministic polyfills discovery ([#16566](https://github.com/vitejs/vite/issues/16566)) ([48edfcd](https://github.com/vitejs/vite/commit/48edfcd91386a28817cbe5a361beb87c7d17f490))
+
+### Documentation
+
+* **plugin-legacy:** update outdated warning about `modernPolyfills` ([#17335](https://github.com/vitejs/vite/issues/17335)) ([e6a70b7](https://github.com/vitejs/vite/commit/e6a70b7c2d8a23cd2c3a20fbd9c33f199fdc3944))
+
+### Miscellaneous Chores
+
+* **deps:** remove unused deps ([#17329](https://github.com/vitejs/vite/issues/17329)) ([5a45745](https://github.com/vitejs/vite/commit/5a457454bfee1892b0d58c4b1c401cfb15986097))
+* **deps:** update all non-major dependencies ([#16722](https://github.com/vitejs/vite/issues/16722)) ([b45922a](https://github.com/vitejs/vite/commit/b45922a91d4a73c27f78f26e369b7b1fd8d800e3))
+
+## [5.4.0](https://github.com/vitejs/vite/compare/plugin-legacy@5.3.2...plugin-legacy@5.4.0) (2024-05-08)
### Features
-* **plugin-legacy:** add externalSystemJS option ([#5024](https://github.com/vitejs/vite/issues/5024)) ([60b6f55](https://github.com/vitejs/vite/commit/60b6f5595a00cbf014a30d57721081eb79436a97))
+* **plugin-legacy:** support `additionalModernPolyfills` ([#16514](https://github.com/vitejs/vite/issues/16514)) ([2322657](https://github.com/vitejs/vite/commit/232265783670563e34cf96240bf0e383a3653e6c))
+
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#16258](https://github.com/vitejs/vite/issues/16258)) ([7caef42](https://github.com/vitejs/vite/commit/7caef4216e16d9ac71e38598a9ecedce2281d42f))
+* **deps:** update all non-major dependencies ([#16376](https://github.com/vitejs/vite/issues/16376)) ([58a2938](https://github.com/vitejs/vite/commit/58a2938a9766981fdc2ed89bec8ff1c96cae0716))
+* **deps:** update all non-major dependencies ([#16488](https://github.com/vitejs/vite/issues/16488)) ([2d50be2](https://github.com/vitejs/vite/commit/2d50be2a5424e4f4c22774652ed313d2a232f8af))
+* **deps:** update all non-major dependencies ([#16549](https://github.com/vitejs/vite/issues/16549)) ([2d6a13b](https://github.com/vitejs/vite/commit/2d6a13b0aa1f3860482dac2ce260cfbb0713033f))
+* **legacy:** modern polyfill autodetection was not injecting enough polyfills ([#16367](https://github.com/vitejs/vite/issues/16367)) ([4af9f97](https://github.com/vitejs/vite/commit/4af9f97cade9fdb349e4928871bbf15c190f9e2b))
+### Documentation
-## [1.5.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.2...plugin-legacy@1.5.3) (2021-09-07)
+* **legacy:** update `modernTargets` option default value description ([#16491](https://github.com/vitejs/vite/issues/16491)) ([7171837](https://github.com/vitejs/vite/commit/7171837abbf8634be2c2e9c32d5dc6a8cbf31e0d))
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#16131](https://github.com/vitejs/vite/issues/16131)) ([a862ecb](https://github.com/vitejs/vite/commit/a862ecb941a432b6e3bab62331012e4b53ddd4e8))
+
+## [5.3.2](https://github.com/vitejs/vite/compare/plugin-legacy@5.3.1...plugin-legacy@5.3.2) (2024-03-08)
### Bug Fixes
-* **plugin-legacy:** fix regression introduced in [#4536](https://github.com/vitejs/vite/issues/4536) ([#4861](https://github.com/vitejs/vite/issues/4861)) ([fdc3212](https://github.com/vitejs/vite/commit/fdc3212474ff951e7e67810eca6cfb3ef1ed9ea2))
-* **plugin-legacy:** skip in SSR build ([#4536](https://github.com/vitejs/vite/issues/4536)) ([1f068fc](https://github.com/vitejs/vite/commit/1f068fcec38fc07c34e75a19821064386e460907))
+* **plugin-legacy:** dynamic import browserslist-to-esbuild ([#16011](https://github.com/vitejs/vite/issues/16011)) ([42fd11c](https://github.com/vitejs/vite/commit/42fd11c1c6d37402bd15ba816fbf65dbed3abe55))
+* **plugin-legacy:** replace `esbuild-plugin-browserslist` with `browserslist-to-esbuild` ([#15988](https://github.com/vitejs/vite/issues/15988)) ([37af8a7](https://github.com/vitejs/vite/commit/37af8a7be417f1fb2cf9a0d5e9ad90b76ff211b4))
+* **plugin-legacy:** respect modernTargets option if renderLegacyChunks disabled ([#15789](https://github.com/vitejs/vite/issues/15789)) ([0813531](https://github.com/vitejs/vite/commit/081353179a4029d8aedaf3dfd78b95d95b757668))
+## [5.3.1](https://github.com/vitejs/vite/compare/plugin-legacy@5.3.0...plugin-legacy@5.3.1) (2024-02-21)
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#15675](https://github.com/vitejs/vite/issues/15675)) ([4d9363a](https://github.com/vitejs/vite/commit/4d9363ad6bc460fe2da811cb48b036e53b8cfc75))
+* **deps:** update all non-major dependencies ([#15803](https://github.com/vitejs/vite/issues/15803)) ([e0a6ef2](https://github.com/vitejs/vite/commit/e0a6ef2b9e6f1df8c5e71efab6182b7cf662d18d))
+* **deps:** update all non-major dependencies ([#15959](https://github.com/vitejs/vite/issues/15959)) ([571a3fd](https://github.com/vitejs/vite/commit/571a3fde438d60540cfeba132e24646badf5ff2f))
-## [1.5.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.1...plugin-legacy@1.5.2) (2021-09-01)
+## [5.3.0](https://github.com/vitejs/vite/compare/plugin-legacy@5.2.0...plugin-legacy@5.3.0) (2024-01-25)
+### Features
+* **legacy:** build file name optimization ([#15115](https://github.com/vitejs/vite/issues/15115)) ([39f435d](https://github.com/vitejs/vite/commit/39f435d8ce329870754f33509e9fdbb61883c9fc))
+* **legacy:** support any separator before hash in fileNames ([#15170](https://github.com/vitejs/vite/issues/15170)) ([ecab41a](https://github.com/vitejs/vite/commit/ecab41a7f8ee09c43e7ace6ef20d2f8693a5978a))
+* **plugin-legacy:** add `modernTargets` option ([#15506](https://github.com/vitejs/vite/issues/15506)) ([cf56507](https://github.com/vitejs/vite/commit/cf56507dbfd41c4af63de511a320971668d5204f))
### Bug Fixes
-* **plugin-legacy:** avoid executing blank dynamic import ([#4767](https://github.com/vitejs/vite/issues/4767)) ([de71408](https://github.com/vitejs/vite/commit/de7140853140029a3f48600b60e700464e7662b5)), closes [#4568](https://github.com/vitejs/vite/issues/4568)
+* **deps:** update all non-major dependencies ([#15233](https://github.com/vitejs/vite/issues/15233)) ([ad3adda](https://github.com/vitejs/vite/commit/ad3adda7215c33874a07cbd4d430fcffe4c85dce))
+* **deps:** update all non-major dependencies ([#15304](https://github.com/vitejs/vite/issues/15304)) ([bb07f60](https://github.com/vitejs/vite/commit/bb07f605cca698a81f1b4606ddefb34485069dd1))
+* **deps:** update all non-major dependencies ([#15375](https://github.com/vitejs/vite/issues/15375)) ([ab56227](https://github.com/vitejs/vite/commit/ab56227d89c92bfa781264e1474ed522892e3b8f))
+### Documentation
+* fix commit id collision ([#15105](https://github.com/vitejs/vite/issues/15105)) ([0654d1b](https://github.com/vitejs/vite/commit/0654d1b52448db4d7a9b69aee6aad9e015481452))
+* fix dead link ([#15700](https://github.com/vitejs/vite/issues/15700)) ([aa7916a](https://github.com/vitejs/vite/commit/aa7916a5c2d580cdd9968fc221826ddd17443bac))
-## [1.5.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.0...plugin-legacy@1.5.1) (2021-08-03)
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#15145](https://github.com/vitejs/vite/issues/15145)) ([7ff2c0a](https://github.com/vitejs/vite/commit/7ff2c0afe8c6b6901385af829f2e7e80c1fe344c))
+## [5.2.0](https://github.com/vitejs/vite/compare/plugin-legacy@5.1.0...plugin-legacy@5.2.0) (2023-11-22)
### Bug Fixes
-* **deps:** update all non-major dependencies ([#4468](https://github.com/vitejs/vite/issues/4468)) ([cd54a22](https://github.com/vitejs/vite/commit/cd54a22b8d5048f5d25a9954697f49b6d65dcc1f))
-* **plugin-legacy:** bake-in Promise polyfill, fix [#4414](https://github.com/vitejs/vite/issues/4414) ([#4440](https://github.com/vitejs/vite/issues/4440)) ([024a2de](https://github.com/vitejs/vite/commit/024a2de63df60a4037f9f7b13a0bc6e2b0d41fb6))
+* **plugin-legacy:** syntax error in variable `detectModernBrowserCode` ([#15095](https://github.com/vitejs/vite/issues/15095)) ([1c605ff](https://github.com/vitejs/vite/commit/1c605ffe9b56dcf731f341a687b1d5b55bba48c6))
+
+### Tests
+
+* **legacy:** add a test to checks all inline snippets are valid JS ([#15098](https://github.com/vitejs/vite/issues/15098)) ([1b9ca66](https://github.com/vitejs/vite/commit/1b9ca66b6d777bd4a03165512de5d65d83a2f25b))
+
+## [5.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@5.0.0...plugin-legacy@5.1.0) (2023-11-21)
+### Bug Fixes
+* **legacy:** preserve async generator function invocation ([#15021](https://github.com/vitejs/vite/issues/15021)) ([47551a6](https://github.com/vitejs/vite/commit/47551a6f32eace64a4f5b669f997892c5ab867af))
+### Documentation
-# [1.5.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.4...plugin-legacy@1.5.0) (2021-07-27)
+* **legacy:** clarify that csp hashes could change between minors ([#15057](https://github.com/vitejs/vite/issues/15057)) ([cd35330](https://github.com/vitejs/vite/commit/cd353306eefa9787c07b257c8c7f3f68e0949240))
+## [5.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@4.1.1...plugin-legacy@5.0.0) (2023-11-16)
+
+### ⚠ BREAKING CHANGES
+
+* **plugin-legacy:** bump vite peer dep (#15004)
+* **legacy:** should rename `x.[hash].js` to `x-legacy.[hash].js` (#11599)
+* **legacy:** remove `ignoreBrowserslistConfig` option (#14429)
+* bump minimum node version to 18 (#14030)
+
+### Features
+
+* **plugin-legacy:** bump vite peer dep ([#15004](https://github.com/vitejs/vite/issues/15004)) ([3c92c7b](https://github.com/vitejs/vite/commit/3c92c7bca23616f156b70311b149cbc1af59d40b))
+* **legacy:** export `Options` ([#14933](https://github.com/vitejs/vite/issues/14933)) ([071bfc8](https://github.com/vitejs/vite/commit/071bfc8e18ebe3981bded8e7bab605bd473d72b9))
+* bump minimum node version to 18 ([#14030](https://github.com/vitejs/vite/issues/14030)) ([2c1a45c](https://github.com/vitejs/vite/commit/2c1a45c86cab6ecf02abb6e50385f773d5ed568e))
### Bug Fixes
-* **deps:** update all non-major dependencies ([#4387](https://github.com/vitejs/vite/issues/4387)) ([2f900ba](https://github.com/vitejs/vite/commit/2f900ba4d4ad8061e0046898e8d1de3129e7f784))
-* **plugin-legacy:** legacy fallback for dynamic import ([#3885](https://github.com/vitejs/vite/issues/3885)) ([fc6d8f1](https://github.com/vitejs/vite/commit/fc6d8f1d3efe836f17f3c45375dd3749128b8137))
+* **deps:** update all non-major dependencies ([#14635](https://github.com/vitejs/vite/issues/14635)) ([21017a9](https://github.com/vitejs/vite/commit/21017a9408643cbc7204215ecc5a3fdaf74dc81e))
+* **deps:** update all non-major dependencies ([#14729](https://github.com/vitejs/vite/issues/14729)) ([d5d96e7](https://github.com/vitejs/vite/commit/d5d96e712788bc762d9c135bc84628dbcfc7fb58))
+* **deps:** update all non-major dependencies ([#14883](https://github.com/vitejs/vite/issues/14883)) ([e5094e5](https://github.com/vitejs/vite/commit/e5094e5bf2aee3516d04ce35ba2fb27e70ea9858))
+* **deps:** update all non-major dependencies ([#14961](https://github.com/vitejs/vite/issues/14961)) ([0bb3995](https://github.com/vitejs/vite/commit/0bb3995a7d2245ef1cc7b2ed8a5242e33af16874))
+* **plugin-legacy:** add invoke to modern detector to avoid terser treeshaking ([#14968](https://github.com/vitejs/vite/issues/14968)) ([4033a32](https://github.com/vitejs/vite/commit/4033a320d6809c9a0c2552f0ef2bf686c63aa35a))
+* **deps:** update all non-major dependencies ([#14510](https://github.com/vitejs/vite/issues/14510)) ([eb204fd](https://github.com/vitejs/vite/commit/eb204fd3c5bffb6c6fb40f562f762e426fbaf183))
+* **legacy:** fix broken build when renderModernChunks=false & polyfills=false (fix [#14324](https://github.com/vitejs/vite/issues/14324)) ([#14346](https://github.com/vitejs/vite/issues/14346)) ([27e5b11](https://github.com/vitejs/vite/commit/27e5b1114ce653b5716cd175aed9e2775da2f97a))
+* **legacy:** should rename `x.[hash].js` to `x-legacy.[hash].js` ([#11599](https://github.com/vitejs/vite/issues/11599)) ([e7d7a6f](https://github.com/vitejs/vite/commit/e7d7a6f4ee095bca4ed4fddf387a9ff06fcea7bb))
+* **deps:** update all non-major dependencies ([#14460](https://github.com/vitejs/vite/issues/14460)) ([b77bff0](https://github.com/vitejs/vite/commit/b77bff0b93ba9449f63c8373ecf82289a39832a0))
+* **legacy:** add guard to modern polyfill chunk ([#13719](https://github.com/vitejs/vite/issues/13719)) ([945dc4d](https://github.com/vitejs/vite/commit/945dc4de52e64a1a8f6e2451fadf6aba7e460234))
+* **legacy:** modern polyfill autodetection was injecting more polyfills than needed ([#14428](https://github.com/vitejs/vite/issues/14428)) ([1c2e941](https://github.com/vitejs/vite/commit/1c2e941d03621a4b77d9dfca8841e336b716691c))
+* **legacy:** suppress babel warning during polyfill scan ([#14425](https://github.com/vitejs/vite/issues/14425)) ([aae3a83](https://github.com/vitejs/vite/commit/aae3a83b5fb49bbd9f174cfeac66f00483829da4))
+* **plugin-legacy:** ensure correct typing for node esm ([#13892](https://github.com/vitejs/vite/issues/13892)) ([d914a9d](https://github.com/vitejs/vite/commit/d914a9d79adfe0aed2ee5d69f6f6d1e80b613b98))
+* **deps:** update all non-major dependencies ([#14092](https://github.com/vitejs/vite/issues/14092)) ([68638f7](https://github.com/vitejs/vite/commit/68638f7b0b04ddfdf35dc8686c6a022aadbb9453))
+
+### Performance Improvements
+
+* use magic-string hires boundary for sourcemaps ([#13971](https://github.com/vitejs/vite/issues/13971)) ([b9a8d65](https://github.com/vitejs/vite/commit/b9a8d65fd64d101ea596bc98a0aea0f95674a95a))
+
+### Documentation
+* **legacy:** correct `modernPolyfills` description ([#14233](https://github.com/vitejs/vite/issues/14233)) ([a57f388](https://github.com/vitejs/vite/commit/a57f388f53bdcbcacd7585724b43953c32e6806e))
+* **plugin-legacy:** fix typo ([#13936](https://github.com/vitejs/vite/issues/13936)) ([28ddd43](https://github.com/vitejs/vite/commit/28ddd43906825db9e1ffa030551e8f975d97f3a9))
+### Miscellaneous Chores
-## [1.4.4](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.3...plugin-legacy@1.4.4) (2021-07-12)
+* **deps:** update dependency eslint-plugin-regexp to v2 ([#14730](https://github.com/vitejs/vite/issues/14730)) ([0a7c753](https://github.com/vitejs/vite/commit/0a7c75305a312161979eaf13d7b48d784bdb6b76))
+* **deps:** update all non-major dependencies ([#13938](https://github.com/vitejs/vite/issues/13938)) ([a1b519e](https://github.com/vitejs/vite/commit/a1b519e2c71593b6b4286c2f0bd8bfe2e0ad046d))
+* **eslint:** allow type annotations ([#13920](https://github.com/vitejs/vite/issues/13920)) ([d1264fd](https://github.com/vitejs/vite/commit/d1264fd34313a2da80af8dadbeab1c8e6013bb10))
+* upgrade babel and release-scripts ([#14330](https://github.com/vitejs/vite/issues/14330)) ([b361ffa](https://github.com/vitejs/vite/commit/b361ffa6724d9191fc6a581acfeab5bc3ebbd931))
+### Code Refactoring
+* **legacy:** remove `ignoreBrowserslistConfig` option ([#14429](https://github.com/vitejs/vite/issues/14429)) ([941bb16](https://github.com/vitejs/vite/commit/941bb1610c9c9576e0b5738c9075b3eb2f16a357))
+
+### Beta Changelogs
+
+#### [5.0.0-beta.3](https://github.com/vitejs/vite/compare/plugin-legacy@5.0.0-beta.2...plugin-legacy@5.0.0-beta.3) (2023-11-14)
+
+See [5.0.0-beta.3 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@5.0.0-beta.3/packages/plugin-legacy/CHANGELOG.md)
+
+#### [5.0.0-beta.2](https://github.com/vitejs/vite/compare/plugin-legacy@5.0.0-beta.1...plugin-legacy@5.0.0-beta.2) (2023-10-09)
+
+See [5.0.0-beta.2 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@5.0.0-beta.2/packages/plugin-legacy/CHANGELOG.md)
+
+#### [5.0.0-beta.1](https://github.com/vitejs/vite/compare/plugin-legacy@5.0.0-beta.0...plugin-legacy@5.0.0-beta.1) (2023-09-25)
+
+See [5.0.0-beta.1 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@5.0.0-beta.1/packages/plugin-legacy/CHANGELOG.md)
+
+#### [5.0.0-beta.0](https://github.com/vitejs/vite/compare/plugin-legacy@4.1.1...plugin-legacy@5.0.0-beta.0) (2023-09-19)
+
+See [5.0.0-beta.0 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@5.0.0-beta.0/packages/plugin-legacy/CHANGELOG.md)
+
+## [4.1.1](https://github.com/vitejs/vite/compare/plugin-legacy@4.1.0...plugin-legacy@4.1.1) (2023-07-20)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#13758](https://github.com/vitejs/vite/issues/13758)) ([8ead116](https://github.com/vitejs/vite/commit/8ead11648514ae4975bf4328d6e15bd4dd42e45e))
+* **deps:** update all non-major dependencies ([#13872](https://github.com/vitejs/vite/issues/13872)) ([975a631](https://github.com/vitejs/vite/commit/975a631ec7c2373354aeeac6bc2977f24b54d13d))
+
+## [4.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@4.0.5...plugin-legacy@4.1.0) (2023-07-06)
### Features
-* allow entryFileNames, chunkFileNames functions for legacy ([#4122](https://github.com/vitejs/vite/issues/4122)) ([df29bff](https://github.com/vitejs/vite/commit/df29bfff44ad7f2e822f92935d0ca9c99f15b67e))
+* **plugin-legacy:** add option to output only legacy builds ([#10139](https://github.com/vitejs/vite/issues/10139)) ([931b24f](https://github.com/vitejs/vite/commit/931b24f5eac4b9b5422a235782ca13baa9a99563))
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#13701](https://github.com/vitejs/vite/issues/13701)) ([02c6bc3](https://github.com/vitejs/vite/commit/02c6bc38645ce18f9e1c8a71421fb8aad7081688))
+## [4.0.5](https://github.com/vitejs/vite/compare/plugin-legacy@4.0.4...plugin-legacy@4.0.5) (2023-06-21)
+### Bug Fixes
-## [1.4.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.2...plugin-legacy@1.4.3) (2021-06-27)
+* **deps:** update all non-major dependencies ([#13059](https://github.com/vitejs/vite/issues/13059)) ([123ef4c](https://github.com/vitejs/vite/commit/123ef4c47c611ebd99d8b41c89c547422aea9c1d))
+* **deps:** update all non-major dependencies ([#13488](https://github.com/vitejs/vite/issues/13488)) ([bd09248](https://github.com/vitejs/vite/commit/bd09248e50ae50ec57b9a72efe0a27aa397ec2e1))
+### Documentation
+* **legacy:** add test case to ensure correct csp hashes in readme.md ([#13384](https://github.com/vitejs/vite/issues/13384)) ([bf0cd25](https://github.com/vitejs/vite/commit/bf0cd25adb3b8bb5d53433ba9323d0a95e9f756a))
+
+### Miscellaneous Chores
+
+* add funding field ([#13585](https://github.com/vitejs/vite/issues/13585)) ([2501627](https://github.com/vitejs/vite/commit/250162775031a8798f67e8be71fd226a79c9831b))
+* **deps:** update all non-major dependencies ([#13553](https://github.com/vitejs/vite/issues/13553)) ([3ea0534](https://github.com/vitejs/vite/commit/3ea05342d41277baf11a73763f082e6e75c46a8f))
+
+## [4.0.4](https://github.com/vitejs/vite/compare/plugin-legacy@4.0.3...plugin-legacy@4.0.4) (2023-05-24)
### Bug Fixes
-* don't force polyfillDynamicImport if renderLegacyChunks is false ([#3695](https://github.com/vitejs/vite/issues/3695)) ([#3774](https://github.com/vitejs/vite/issues/3774)) ([d2a51ca](https://github.com/vitejs/vite/commit/d2a51ca4eda2ca9f99d9a066836d76d2253cfc24))
-* **deps:** update all non-major dependencies ([#3878](https://github.com/vitejs/vite/issues/3878)) ([a66a805](https://github.com/vitejs/vite/commit/a66a8053e9520d20bcf95fce870570c5195bcc91))
-* **plugin-legacy:** chunk may not exist ([#3886](https://github.com/vitejs/vite/issues/3886)) ([dd5931d](https://github.com/vitejs/vite/commit/dd5931d9c1cf382849047332b2c3409755ceebf5))
+* **legacy:** import `@babel/preset-env` ([#12961](https://github.com/vitejs/vite/issues/12961)) ([d53c650](https://github.com/vitejs/vite/commit/d53c650a69aeb43efd99b210ccc3a5606f2fc31b))
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#12805](https://github.com/vitejs/vite/issues/12805)) ([5731ac9](https://github.com/vitejs/vite/commit/5731ac9caaef629e892e20394f0cc73c565d9a87))
-## [1.4.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.1...plugin-legacy@1.4.2) (2021-06-22)
+## [4.0.3](https://github.com/vitejs/vite/compare/plugin-legacy@4.0.2...plugin-legacy@4.0.3) (2023-04-25)
+### Features
+* **plugin-legacy:** support file protocol ([#8524](https://github.com/vitejs/vite/issues/8524)) ([7a87ff4](https://github.com/vitejs/vite/commit/7a87ff4b0950012ad0d85b05fe36b17e1ee2ee76))
### Bug Fixes
-* **deps:** update all non-major dependencies ([#3791](https://github.com/vitejs/vite/issues/3791)) ([74d409e](https://github.com/vitejs/vite/commit/74d409eafca8d74ec4a6ece621ea2895bc1f2a32))
-* **plugin-legacy:** wrap chunks in IIFE ([#3783](https://github.com/vitejs/vite/issues/3783)) ([9abdb81](https://github.com/vitejs/vite/commit/9abdb8137ef54dd095e7bc47ae6a1ccf490fd196))
+* **deps:** update all non-major dependencies ([#12389](https://github.com/vitejs/vite/issues/12389)) ([3e60b77](https://github.com/vitejs/vite/commit/3e60b778b0ed178a83f674031f5edb123e6c123c))
+
+### Code Refactoring
+* **eslint:** migrate to `eslint-plugin-n` ([#12895](https://github.com/vitejs/vite/issues/12895)) ([62ebe28](https://github.com/vitejs/vite/commit/62ebe28d4023c1f67578b1977edd3371f44f475a))
+## [4.0.2](https://github.com/vitejs/vite/compare/plugin-legacy@4.0.1...plugin-legacy@4.0.2) (2023-03-16)
+### Bug Fixes
-## [1.4.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.0...plugin-legacy@1.4.1) (2021-06-01)
+* **deps:** update all non-major dependencies ([#12036](https://github.com/vitejs/vite/issues/12036)) ([48150f2](https://github.com/vitejs/vite/commit/48150f2ea4d7ff8e3b67f15239ae05f5be317436))
+* **plugin-legacy:** no `build.target` override on SSR build ([#12171](https://github.com/vitejs/vite/issues/12171)) ([a1019f8](https://github.com/vitejs/vite/commit/a1019f80a5d5b6242d8fb0975994ce1ae6e78e94))
+### Documentation
+* **plugin-legacy:** outdated csp hash (fix [#12112](https://github.com/vitejs/vite/issues/12112)) ([#12118](https://github.com/vitejs/vite/issues/12118)) ([5f7f5dc](https://github.com/vitejs/vite/commit/5f7f5dcb0c006012631c1d5df61d79307d9097f4))
+
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#12299](https://github.com/vitejs/vite/issues/12299)) ([b41336e](https://github.com/vitejs/vite/commit/b41336e450b093fb3e454806ec4245ebad7ba9c5))
+* **deps:** update rollup to 3.17.2 ([#12110](https://github.com/vitejs/vite/issues/12110)) ([e54ffbd](https://github.com/vitejs/vite/commit/e54ffbdcbd5d90d560a1bda7a140de046019fcf5))
+
+## [4.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@4.0.0...plugin-legacy@4.0.1) (2023-02-02)
### Bug Fixes
-* **plugin-legacy:** respect custom filenames formats, fix [#2356](https://github.com/vitejs/vite/issues/2356) ([#2641](https://github.com/vitejs/vite/issues/2641)) ([d852731](https://github.com/vitejs/vite/commit/d852731622a1c009d15a5172343fc166c4bb5cb7))
+* **legacy:** fix browserslist import, close https://github.com/vitejs/vite/issues/11898 ([#11899](https://github.com/vitejs/vite/issues/11899)) ([9241d08](https://github.com/vitejs/vite/commit/9241d0895b37c658a2dccfd961958c0c5238a49b))
+## [4.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@3.0.1...plugin-legacy@4.0.0) (2023-02-02)
+### ⚠ BREAKING CHANGES
+* **legacy:** bump modern target to support async generator (#11896)
+* **plugin-legacy:** support browserslist and update default target (#11318)
-# [1.4.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.4...plugin-legacy@1.4.0) (2021-05-17)
+### Features
+* **legacy:** bump modern target to support async generator ([#11896](https://github.com/vitejs/vite/issues/11896)) ([55b9711](https://github.com/vitejs/vite/commit/55b971139557f65f249f5385b580fa45946cb1d3))
### Bug Fixes
-* **plugin-legacy:** turn off babel loose mode ([#3406](https://github.com/vitejs/vite/issues/3406)) ([5348c02](https://github.com/vitejs/vite/commit/5348c02f58bde36c412dbfe36c3ad37772bf83e5))
-* restore dynamic-import-polyfill ([#3434](https://github.com/vitejs/vite/issues/3434)) ([4112c5d](https://github.com/vitejs/vite/commit/4112c5d103673b83c50d446096086617dfaac5a3))
+* **deps:** update all non-major dependencies ([#11846](https://github.com/vitejs/vite/issues/11846)) ([5d55083](https://github.com/vitejs/vite/commit/5d5508311f9856de69babd72dc4de0e7c21c7ae8))
+* **plugin-legacy:** legacy sourcemap not generate (fix [#11693](https://github.com/vitejs/vite/issues/11693)) ([#11841](https://github.com/vitejs/vite/issues/11841)) ([2ff5930](https://github.com/vitejs/vite/commit/2ff5930e02d80d6254037281b4c62b8e489d63ba))
+* **plugin-legacy:** support browserslist and update default target ([#11318](https://github.com/vitejs/vite/issues/11318)) ([d5b8f86](https://github.com/vitejs/vite/commit/d5b8f8615e880e854a3e1105e3193c24cc964f30))
+* typo ([#11283](https://github.com/vitejs/vite/issues/11283)) ([bf234a6](https://github.com/vitejs/vite/commit/bf234a63b46f0dc7a629abe0d69863ac15c381e1))
+
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#11419](https://github.com/vitejs/vite/issues/11419)) ([896475d](https://github.com/vitejs/vite/commit/896475dc6c7e5f1168e21d556201a61659552617))
+* **deps:** update all non-major dependencies ([#11787](https://github.com/vitejs/vite/issues/11787)) ([271394f](https://github.com/vitejs/vite/commit/271394fc7157a08b19f22d3751c8ec6e69f0bd5f))
+* enable `@typescript-eslint/ban-ts-comment` ([#11326](https://github.com/vitejs/vite/issues/11326)) ([e58a4f0](https://github.com/vitejs/vite/commit/e58a4f00e201e9c0d43ddda51ccac7b612d58650))
+* update packages' (vite, vite-legacy) keywords ([#11402](https://github.com/vitejs/vite/issues/11402)) ([a56bc34](https://github.com/vitejs/vite/commit/a56bc3434e9d4bc7f9d580ae630ccc633e7d436a))
+### Code Refactoring
-## [1.3.4](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.3...plugin-legacy@1.3.4) (2021-05-11)
+* **plugin-legacy:** optimize cspHashes array ([#11734](https://github.com/vitejs/vite/issues/11734)) ([b1a8e58](https://github.com/vitejs/vite/commit/b1a8e5856db91df264a7d1e40bf847dde5eb0981))
+## [3.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@3.0.0...plugin-legacy@3.0.1) (2022-12-09)
+### Miscellaneous Chores
+
+* udpate vite and plugins to stable ([#11278](https://github.com/vitejs/vite/issues/11278)) ([026f41e](https://github.com/vitejs/vite/commit/026f41e87e6eb89491c88f62952d7a094f810811))
+
+## [3.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.3.1...plugin-legacy@3.0.0) (2022-12-09)
+
+### Features
+
+* align default chunk and asset file names with rollup ([#10927](https://github.com/vitejs/vite/issues/10927)) ([cc2adb3](https://github.com/vitejs/vite/commit/cc2adb39254d6de139bc3dfad976430c03250b27))
+* rollup 3 ([#9870](https://github.com/vitejs/vite/issues/9870)) ([beb7166](https://github.com/vitejs/vite/commit/beb716695d5dd11fd9f3d7350c1c807dfa37a216))
### Bug Fixes
-* **plugin-legacy:** move polyfills in plugin post, fixes [#2786](https://github.com/vitejs/vite/issues/2786) and [#2781](https://github.com/vitejs/vite/issues/2781) ([#3023](https://github.com/vitejs/vite/issues/3023)) ([43150e3](https://github.com/vitejs/vite/commit/43150e352d164744e2fda766927053439bdf7db9))
-* **plugin-legacy:** require Vite 2.0.0 final ([#3265](https://github.com/vitejs/vite/issues/3265)) ([e395dee](https://github.com/vitejs/vite/commit/e395deeb0f11ebb1bcebe69233adebaad10f77eb))
+* **deps:** update all non-major dependencies ([#10804](https://github.com/vitejs/vite/issues/10804)) ([f686afa](https://github.com/vitejs/vite/commit/f686afa6d3bc0f501b936dcbc2c4552c865fa3f9))
+* **deps:** update all non-major dependencies ([#11091](https://github.com/vitejs/vite/issues/11091)) ([073a4bf](https://github.com/vitejs/vite/commit/073a4bfe2642a4dda2183a9dfecac864524893e1))
+* support polyfill import paths containing an escaping char (e.g. '\') ([#10859](https://github.com/vitejs/vite/issues/10859)) ([7ac2535](https://github.com/vitejs/vite/commit/7ac2535cfc1eb276237a66f9776f9cda3db1148a))
+
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#11182](https://github.com/vitejs/vite/issues/11182)) ([8b83089](https://github.com/vitejs/vite/commit/8b830899ef0ce4ebe257ed18222543f60b775832))
+* enable prettier trailing commas ([#11167](https://github.com/vitejs/vite/issues/11167)) ([134ce68](https://github.com/vitejs/vite/commit/134ce6817984bad0f5fb043481502531fee9b1db))
+* **deps:** update all non-major dependencies ([#10910](https://github.com/vitejs/vite/issues/10910)) ([f6ad607](https://github.com/vitejs/vite/commit/f6ad607d2430a44ea7dc71ecd3c44c1e8bf8446f))
+* **deps:** update all non-major dependencies ([#11006](https://github.com/vitejs/vite/issues/11006)) ([96f2e98](https://github.com/vitejs/vite/commit/96f2e98f6a196652962ccb5f2fa6195c050c463f))
+### Beta Changelogs
-## [1.3.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.2...plugin-legacy@1.3.3) (2021-05-03)
+#### [3.0.0-alpha.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.3.1...plugin-legacy@3.0.0-alpha.0) (2022-11-30)
+See [3.0.0-alpha.0 changelog](https://github.com/vitejs/vite/blob/plugin-legacy@3.0.0-alpha.0/packages/plugin-legacy/CHANGELOG.md)
+## [2.3.1](https://github.com/vitejs/vite/compare/plugin-legacy@2.3.0...plugin-legacy@2.3.1) (2022-11-07)
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#10725](https://github.com/vitejs/vite/issues/10725)) ([22cfad8](https://github.com/vitejs/vite/commit/22cfad87c824e717b6c616129f3b579be2e979b2))
+
+## [2.3.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.3.0-beta.0...plugin-legacy@2.3.0) (2022-10-26)
### Bug Fixes
-* **plugin-legacy:** correct log level to error ([#3241](https://github.com/vitejs/vite/issues/3241)) ([474fe9a](https://github.com/vitejs/vite/commit/474fe9a3abbdf4845447eaab821d2ba51fda6207))
-* ignore babelrc in legacy plugin ([#2801](https://github.com/vitejs/vite/issues/2801)) ([d466ad0](https://github.com/vitejs/vite/commit/d466ad0a095859a895fd4cb85f425ad4c4583e4e))
+* **deps:** update all non-major dependencies ([#10610](https://github.com/vitejs/vite/issues/10610)) ([bb95467](https://github.com/vitejs/vite/commit/bb954672e3ee863e5cb37fa78167e5fc6df9ae4e))
+
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#10393](https://github.com/vitejs/vite/issues/10393)) ([f519423](https://github.com/vitejs/vite/commit/f519423170fafeee9d58aeb2052cb3bc224f25f8))
+* **deps:** update all non-major dependencies ([#10488](https://github.com/vitejs/vite/issues/10488)) ([15aa827](https://github.com/vitejs/vite/commit/15aa827283d6cbf9f55c02d6d8a3fe43dbd792e4))
+## [2.3.0-beta.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.2.0...plugin-legacy@2.3.0-beta.0) (2022-10-05)
+### Bug Fixes
-## [1.3.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.1...plugin-legacy@1.3.2) (2021-03-27)
+* **deps:** update all non-major dependencies ([#10160](https://github.com/vitejs/vite/issues/10160)) ([6233c83](https://github.com/vitejs/vite/commit/6233c830201085d869fbbd2a7e622a59272e0f43))
+* **deps:** update all non-major dependencies ([#10246](https://github.com/vitejs/vite/issues/10246)) ([81d4d04](https://github.com/vitejs/vite/commit/81d4d04c37b805843ea83075d1c0819c31726c4e))
+* **deps:** update all non-major dependencies ([#10316](https://github.com/vitejs/vite/issues/10316)) ([a38b450](https://github.com/vitejs/vite/commit/a38b450441eea02a680b80ac0624126ba6abe3f7))
+* **legacy:** don't force set `build.target` when `renderLegacyChunks=false` (fixes [#10201](https://github.com/vitejs/vite/issues/10201)) ([#10220](https://github.com/vitejs/vite/issues/10220)) ([7f548e8](https://github.com/vitejs/vite/commit/7f548e874a2fb2b09f08fe123bb3ebc10aa2f54b))
+### Code Refactoring
+* **types:** bundle client types ([#9966](https://github.com/vitejs/vite/issues/9966)) ([da632bf](https://github.com/vitejs/vite/commit/da632bf36f561c0fc4031830721a7d4d86135efb))
+
+## [2.2.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.1.0...plugin-legacy@2.2.0) (2022-09-19)
### Bug Fixes
-* typo in plugin-legacy ([#2651](https://github.com/vitejs/vite/issues/2651)) ([9a2ce75](https://github.com/vitejs/vite/commit/9a2ce7580772cb783a9f8fda7e45e4a9adacbec2))
+* **deps:** update all non-major dependencies ([#10077](https://github.com/vitejs/vite/issues/10077)) ([caf00c8](https://github.com/vitejs/vite/commit/caf00c8c7a5c81a92182116ffa344b34ce4c3b5e))
+* **deps:** update all non-major dependencies ([#9985](https://github.com/vitejs/vite/issues/9985)) ([855f2f0](https://github.com/vitejs/vite/commit/855f2f077eb8dc41b395bccecb6a5b836eb526a9))
+* **plugin-legacy:** force set `build.target` ([#10072](https://github.com/vitejs/vite/issues/10072)) ([a13a7eb](https://github.com/vitejs/vite/commit/a13a7eb4165d38ce0ab6eefd4e4d38104ce63699))
+
+### Documentation
+
+* **plugin-legacy:** fix Vite default target ([#10158](https://github.com/vitejs/vite/issues/10158)) ([62ff788](https://github.com/vitejs/vite/commit/62ff7887870392f0cce2a40b3cc5d1b7c48a9a47))
+
+## [2.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.1.0-beta.0...plugin-legacy@2.1.0) (2022-09-05)
+## [2.1.0-beta.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.1...plugin-legacy@2.1.0-beta.0) (2022-08-29)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#9888](https://github.com/vitejs/vite/issues/9888)) ([e35a58b](https://github.com/vitejs/vite/commit/e35a58ba46f906feea8ab46886c3306257c61560))
+* **plugin-legacy:** prevent global process.env.NODE_ENV mutation ([#9741](https://github.com/vitejs/vite/issues/9741)) ([a8279af](https://github.com/vitejs/vite/commit/a8279af608657861b64af5980942cced0b04c8ac))
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#9675](https://github.com/vitejs/vite/issues/9675)) ([4e56e87](https://github.com/vitejs/vite/commit/4e56e87623501109198e019ebe809872528ab088))
+* **deps:** update all non-major dependencies ([#9778](https://github.com/vitejs/vite/issues/9778)) ([aceaefc](https://github.com/vitejs/vite/commit/aceaefc19eaa05c76b8a7adec035a0e4b33694c6))
-## [1.3.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.0...plugin-legacy@1.3.1) (2021-02-15)
+### Code Refactoring
+* **legacy:** build polyfill chunk ([#9639](https://github.com/vitejs/vite/issues/9639)) ([7ba0c9f](https://github.com/vitejs/vite/commit/7ba0c9f60e147a0039d2607a10c55e4feecd4bee))
+* **legacy:** remove code for Vite 2 ([#9640](https://github.com/vitejs/vite/issues/9640)) ([b1bbc5b](https://github.com/vitejs/vite/commit/b1bbc5bcc01bfc9b5923e9e58d744c594799a873))
+## [2.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.0...plugin-legacy@2.0.1) (2022-08-11)
### Bug Fixes
-* **plugin-legacy:** prevent constant folding for import.meta.env.LEGACY ([bace724](https://github.com/vitejs/vite/commit/bace7244e776b3f4c9dd7e3ff1885df668cbcb87)), closes [#1999](https://github.com/vitejs/vite/issues/1999)
-* **plugin-legacy:** use correct string length in legacy env replacement ([#2015](https://github.com/vitejs/vite/issues/2015)) ([7f48086](https://github.com/vitejs/vite/commit/7f4808634f57ca8f4be3b455cc4fb8016acdc4fd))
+* **deps:** update all non-major dependencies ([#9176](https://github.com/vitejs/vite/issues/9176)) ([31d3b70](https://github.com/vitejs/vite/commit/31d3b70672ea8759a8d7ff1993d64bb4f0e30fab))
+* **deps:** update all non-major dependencies ([#9575](https://github.com/vitejs/vite/issues/9575)) ([8071325](https://github.com/vitejs/vite/commit/80713256d0dd5716e42086fb617e96e9e92c3675))
+* **legacy:** skip esbuild transform for systemjs ([#9635](https://github.com/vitejs/vite/issues/9635)) ([ac16abd](https://github.com/vitejs/vite/commit/ac16abda0a3f96daa61507bda666ade5867ec909))
+* mention that Node.js 13/15 support is dropped (fixes [#9113](https://github.com/vitejs/vite/issues/9113)) ([#9116](https://github.com/vitejs/vite/issues/9116)) ([2826303](https://github.com/vitejs/vite/commit/2826303bd253e20df2746f84f6a7c06cb5cf3d6b))
+
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#9347](https://github.com/vitejs/vite/issues/9347)) ([2fcb027](https://github.com/vitejs/vite/commit/2fcb0272442664c395322acfc7899ab6a32bd86c))
+* **deps:** update all non-major dependencies ([#9478](https://github.com/vitejs/vite/issues/9478)) ([c530d16](https://github.com/vitejs/vite/commit/c530d168309557c7a254128364f07f7b4f017e14))
+* fix code typos ([#9033](https://github.com/vitejs/vite/issues/9033)) ([ed02861](https://github.com/vitejs/vite/commit/ed0286186b24748ec7bfa336f83c382363a22f1d))
+## [2.0.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.0-beta.1...plugin-legacy@2.0.0) (2022-07-13)
+### Documentation
+* cleanup changes ([#8989](https://github.com/vitejs/vite/issues/8989)) ([07aef1b](https://github.com/vitejs/vite/commit/07aef1b4c02a64732b31b00591d2d9d9c8025aab))
-# [1.3.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.3...plugin-legacy@1.3.0) (2021-02-11)
+### Miscellaneous Chores
+* 3.0 release notes and bump peer deps ([#9072](https://github.com/vitejs/vite/issues/9072)) ([427ba26](https://github.com/vitejs/vite/commit/427ba26fa720a11530d135b2ee39876fc9a778fb))
+* **deps:** update all non-major dependencies ([#9022](https://github.com/vitejs/vite/issues/9022)) ([6342140](https://github.com/vitejs/vite/commit/6342140e6ac7e033ca83d3494f94ea20ca2eaf07))
+## [2.0.0-beta.1](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.0-beta.0...plugin-legacy@2.0.0-beta.1) (2022-07-06)
### Features
-* **plugin-legacy:** inject import.meta.env.LEGACY ([416f190](https://github.com/vitejs/vite/commit/416f190aa92f06a06f3ded403fb1e4cb8729256d))
+* experimental.renderBuiltUrl (revised build base options) ([#8762](https://github.com/vitejs/vite/issues/8762)) ([895a7d6](https://github.com/vitejs/vite/commit/895a7d66bc93beaf18ebcbee23b00fda9ca4c33c))
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#8802](https://github.com/vitejs/vite/issues/8802)) ([a4a634d](https://github.com/vitejs/vite/commit/a4a634d6a08f8b54f052cfc2cc1b60c1bca6d48a))
-## [1.2.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.2...plugin-legacy@1.2.3) (2021-02-01)
+### Miscellaneous Chores
+* use `tsx` directly instead of indirect `esno` ([#8773](https://github.com/vitejs/vite/issues/8773)) ([f018f13](https://github.com/vitejs/vite/commit/f018f135ffa5a2885c063d4509d39958c788120c))
+## [2.0.0-beta.0](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.0-alpha.2...plugin-legacy@2.0.0-beta.0) (2022-06-21)
### Features
-* **plugin-legacy:** use compact output when transpiling legacy chunks ([045e519](https://github.com/vitejs/vite/commit/045e519d51fbce94bddb60793e9e99311acc5aa2)), closes [#1828](https://github.com/vitejs/vite/issues/1828)
+* bump minimum node version to 14.18.0 ([#8662](https://github.com/vitejs/vite/issues/8662)) ([8a05432](https://github.com/vitejs/vite/commit/8a05432e6dcc0e11d78c7b029e7340fa47fceb92))
+* experimental.buildAdvancedBaseOptions ([#8450](https://github.com/vitejs/vite/issues/8450)) ([8ef7333](https://github.com/vitejs/vite/commit/8ef733368fd6618a252e44616f7577f593fd4fbc))
+
+### Bug Fixes
+* **plugin-legacy:** prevent esbuild injecting arrow function ([#8660](https://github.com/vitejs/vite/issues/8660)) ([c0e74e5](https://github.com/vitejs/vite/commit/c0e74e5f687b8f2bb308acb51cd94c31aea2808b))
+### Miscellaneous Chores
-## [1.2.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.1...plugin-legacy@1.2.2) (2021-01-25)
+* **deps:** update all non-major dependencies ([#8669](https://github.com/vitejs/vite/issues/8669)) ([628863d](https://github.com/vitejs/vite/commit/628863dc6120804cc1af8bda2ea98e802ded0e84))
+* use node prefix ([#8309](https://github.com/vitejs/vite/issues/8309)) ([60721ac](https://github.com/vitejs/vite/commit/60721ac53a1bf326d1cac097f23642faede234ff))
+## [2.0.0-alpha.2](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.0-alpha.1...plugin-legacy@2.0.0-alpha.2) (2022-06-19)
+### ⚠ BREAKING CHANGES
+
+* make terser an optional dependency (#8049)
### Bug Fixes
-* **plugin-legacy:** throw error when using esbuild minify with legacy plugin ([8fb2511](https://github.com/vitejs/vite/commit/8fb2511a02c163d85f60dfab0bef104756768e35))
+* **build:** use crossorigin for nomodule ([#8322](https://github.com/vitejs/vite/issues/8322)) ([7f59989](https://github.com/vitejs/vite/commit/7f59989ec1fee7f8b71d297169589e010d3b84e3))
+* **deps:** update all non-major dependencies ([#8281](https://github.com/vitejs/vite/issues/8281)) ([c68db4d](https://github.com/vitejs/vite/commit/c68db4d7ad2c1baee41f280b34ae89a85ba0373d))
+* **deps:** update all non-major dependencies ([#8391](https://github.com/vitejs/vite/issues/8391)) ([842f995](https://github.com/vitejs/vite/commit/842f995ca69600c4c06c46d202fe713b80373418))
+* **plugin-legacy:** disable babel.compact when minify is disabled ([#8244](https://github.com/vitejs/vite/issues/8244)) ([742188c](https://github.com/vitejs/vite/commit/742188cc04526439060bdac7125237c20463d5a5))
+* **plugin-legacy:** don't include SystemJS in modern polyfills ([#6902](https://github.com/vitejs/vite/issues/6902)) ([eb47b1e](https://github.com/vitejs/vite/commit/eb47b1e2580cd6f8285dadba8f943e1b667ec390))
+* **plugin-legacy:** empty base makes import fail (fixes [#4212](https://github.com/vitejs/vite/issues/4212)) ([#8387](https://github.com/vitejs/vite/issues/8387)) ([1a16f12](https://github.com/vitejs/vite/commit/1a16f123e0781449c511af2d0112b8c4639972f1))
+* **plugin-legacy:** modern polyfill latest features (fixes [#8399](https://github.com/vitejs/vite/issues/8399)) ([#8408](https://github.com/vitejs/vite/issues/8408)) ([ed25817](https://github.com/vitejs/vite/commit/ed2581778baff3201f47866799f006a490a7e35b))
+* **plugin-legacy:** prevent failed to load module ([#8285](https://github.com/vitejs/vite/issues/8285)) ([d671811](https://github.com/vitejs/vite/commit/d67181195aec99ee6aa71bd8fdb69f1f09f57c9d))
+* **plugin-legacy:** respect `entryFileNames` for polyfill chunks ([#8247](https://github.com/vitejs/vite/issues/8247)) ([baa9632](https://github.com/vitejs/vite/commit/baa9632a2c2befafdfde0f131f84f247fa8b6478))
+
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#8474](https://github.com/vitejs/vite/issues/8474)) ([6d0ede7](https://github.com/vitejs/vite/commit/6d0ede7c60aaa4c010207a047bf30a2b87b5049f))
+* enable `@typescript-eslint/explicit-module-boundary-types` ([#8372](https://github.com/vitejs/vite/issues/8372)) ([104caf9](https://github.com/vitejs/vite/commit/104caf95ecd8cdf2d21ca7171931622b52fd74ff))
+* update major deps ([#8572](https://github.com/vitejs/vite/issues/8572)) ([0e20949](https://github.com/vitejs/vite/commit/0e20949dbf0ba38bdaefbf32a36764fe29858e20))
+* use `esno` to replace `ts-node` ([#8162](https://github.com/vitejs/vite/issues/8162)) ([c18a5f3](https://github.com/vitejs/vite/commit/c18a5f36410e418aaf8309102f1cacf7aef31b43))
+
+### Code Refactoring
+
+* make terser an optional dependency ([#8049](https://github.com/vitejs/vite/issues/8049)) ([164f528](https://github.com/vitejs/vite/commit/164f528838f3a146c82d68992d38316b9214f9b8))
+* **plugin-legacy:** improve default polyfill ([#8312](https://github.com/vitejs/vite/issues/8312)) ([4370d91](https://github.com/vitejs/vite/commit/4370d9123da20c586938753d9f606d84907334c9))
+## [2.0.0-alpha.1](https://github.com/vitejs/vite/compare/plugin-legacy@2.0.0-alpha.0...plugin-legacy@2.0.0-alpha.1) (2022-05-19)
+### ⚠ BREAKING CHANGES
+
+* bump targets (#8045)
+* relative base (#7644)
### Features
-* default vendor chunk splitting ([f6b58a0](https://github.com/vitejs/vite/commit/f6b58a0f535b1c26f9c1dfda74c28c685402c3c9))
-* support `base` option during dev, deprecate `build.base` ([#1556](https://github.com/vitejs/vite/issues/1556)) ([809d4bd](https://github.com/vitejs/vite/commit/809d4bd3bf62d3bc6b35f182178922d2ab2175f1))
+* relative base ([#7644](https://github.com/vitejs/vite/issues/7644)) ([09648c2](https://github.com/vitejs/vite/commit/09648c220a67852c38da0ba742501a15837e16c2))
+
+### Bug Fixes
+
+* **plugin-legacy:** fail to get the fileName ([#5250](https://github.com/vitejs/vite/issues/5250)) ([c7fc1d4](https://github.com/vitejs/vite/commit/c7fc1d4a532eae7b519bd70c6eba701e23b0635a))
+* rewrite CJS specific funcs/vars in plugins ([#8227](https://github.com/vitejs/vite/issues/8227)) ([9baa70b](https://github.com/vitejs/vite/commit/9baa70b788ec0b0fc419db30d627567242c6af7d))
+
+### Documentation
+
+* use latest core-js unpkg link ([#8190](https://github.com/vitejs/vite/issues/8190)) ([102b678](https://github.com/vitejs/vite/commit/102b678335ba74ac8f0ab94c8c49cba97e836e6d))
+
+### Build System
+
+* bump targets ([#8045](https://github.com/vitejs/vite/issues/8045)) ([66efd69](https://github.com/vitejs/vite/commit/66efd69a399fd73284cc7a3bffc904e154291a14))
+
+## [2.0.0-alpha.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.8.2...plugin-legacy@2.0.0-alpha.0) (2022-05-13)
+### ⚠ BREAKING CHANGES
+
+* remove node v12 support (#7833)
+
+### Documentation
+
+* **plugin-legacy:** remove regenerator-runtime note ([#8007](https://github.com/vitejs/vite/issues/8007)) ([834efe9](https://github.com/vitejs/vite/commit/834efe94fe2c26fcdeabcc34a667dcc6a52326ee))
+
+### Miscellaneous Chores
+
+* bump minors and rebuild lock ([#8074](https://github.com/vitejs/vite/issues/8074)) ([aeb5b74](https://github.com/vitejs/vite/commit/aeb5b7436df5a4d7cf0ee1a9f6f110d00ef7aac1))
+* **deps:** use `esno` to replace `ts-node` ([#8152](https://github.com/vitejs/vite/issues/8152)) ([2363bd3](https://github.com/vitejs/vite/commit/2363bd3e5443aad43351ac16400b5a6ab7e0ef83))
+* revert vitejs/vite[#8152](https://github.com/vitejs/vite/issues/8152) ([#8161](https://github.com/vitejs/vite/issues/8161)) ([85b8b55](https://github.com/vitejs/vite/commit/85b8b55c0d39f53581047f622717d4a009c594f6))
+* update plugins peer deps ([d57c23c](https://github.com/vitejs/vite/commit/d57c23ca9b59491160017cea996fdbff4216263c))
+* use `unbuild` to bundle plugins ([#8139](https://github.com/vitejs/vite/issues/8139)) ([638b168](https://github.com/vitejs/vite/commit/638b1686288ad685243d34cd9f1db3814f4db1c0))
+
+### Build System
+* remove node v12 support ([#7833](https://github.com/vitejs/vite/issues/7833)) ([eeac2d2](https://github.com/vitejs/vite/commit/eeac2d2e217ddbca79d5b1dfde9bb5097e821b6a))
+## [1.8.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.8.1...plugin-legacy@1.8.2) (2022-05-02)
+### Miscellaneous Chores
-## [1.2.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.0...plugin-legacy@1.2.1) (2021-01-14)
+* **deps:** update all non-major dependencies ([#7780](https://github.com/vitejs/vite/issues/7780)) ([eba9d05](https://github.com/vitejs/vite/commit/eba9d05d7adbb5d4dd25f14b085b15eb3488dfe4))
+* **deps:** update all non-major dependencies ([#7847](https://github.com/vitejs/vite/issues/7847)) ([e29d1d9](https://github.com/vitejs/vite/commit/e29d1d92f7810c5160aac2f1e56f7b03bfa4c933))
+* **deps:** update all non-major dependencies ([#7949](https://github.com/vitejs/vite/issues/7949)) ([b877d30](https://github.com/vitejs/vite/commit/b877d30a05691bb6ea2da4e67b931a5a3d32809f))
+### Code Refactoring
+* **legacy:** remove unneeded dynamic import var init code ([#7759](https://github.com/vitejs/vite/issues/7759)) ([12a4e7d](https://github.com/vitejs/vite/commit/12a4e7d8bbf06d35d6fcc0135dcb76fd06a57c22))
+
+## [1.8.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.8.0...plugin-legacy@1.8.1) (2022-04-13)
### Bug Fixes
-* **plugin-legacy:** respect config.build.assetsDir ([#1532](https://github.com/vitejs/vite/issues/1532)) ([3e7ad3f](https://github.com/vitejs/vite/commit/3e7ad3fa26a6149b44b2e522648cbda1009e4888)), closes [#1530](https://github.com/vitejs/vite/issues/1530)
+* **deps:** update all non-major dependencies ([#7668](https://github.com/vitejs/vite/issues/7668)) ([485263c](https://github.com/vitejs/vite/commit/485263cdca78e5b30fce77f1af9862b3ea3d76f1))
+
+### Documentation
+
+* **legacy:** note works in build only ([#7596](https://github.com/vitejs/vite/issues/7596)) ([f26b14a](https://github.com/vitejs/vite/commit/f26b14a0d8f4c909cb8cf3188684333b488c0714))
+
+## [1.8.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.7.0...plugin-legacy@1.8.0) (2022-03-30)
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#6782](https://github.com/vitejs/vite/issues/6782)) ([e38be3e](https://github.com/vitejs/vite/commit/e38be3e6ca7bf79319d5d7188e1d347b1d6091ef))
+* **deps:** update all non-major dependencies ([#7392](https://github.com/vitejs/vite/issues/7392)) ([b63fc3b](https://github.com/vitejs/vite/commit/b63fc3bbdaf59358b89a0844c264deea1b25c034))
+* **plugin-legacy:** always fallback legacy build when CSP ([#6535](https://github.com/vitejs/vite/issues/6535)) ([a118a1d](https://github.com/vitejs/vite/commit/a118a1d98c63028ddc8b2b3389b8cfa58d771e76))
+* **plugin-legacy:** polyfill latest features ([#7514](https://github.com/vitejs/vite/issues/7514)) ([cb388e2](https://github.com/vitejs/vite/commit/cb388e2dfd39fab751d0656a811c39f8440c48e2))
+* **plugin-legacy:** require Vite 2.8.0 ([#6272](https://github.com/vitejs/vite/issues/6272)) ([#6869](https://github.com/vitejs/vite/issues/6869)) ([997b8f1](https://github.com/vitejs/vite/commit/997b8f11cb156cc374ae991875a09534b5489a93))
+### Documentation
-# [1.2.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.1.1...plugin-legacy@1.2.0) (2021-01-11)
+* **vite-legacy:** Note about using `regenerator-runtime` in Content Security Policy environment ([#7234](https://github.com/vitejs/vite/issues/7234)) ([0fd6422](https://github.com/vitejs/vite/commit/0fd64223304442bb483c55d818fcf808b7ffbaa8))
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#6905](https://github.com/vitejs/vite/issues/6905)) ([839665c](https://github.com/vitejs/vite/commit/839665c5985101c1765f0d68cf429ac96157d062))
+
+## [1.7.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.4...plugin-legacy@1.7.0) (2022-02-09)
### Bug Fixes
-* **plugin-html:** typo in the Safari 10 nomodule snippet ([#1483](https://github.com/vitejs/vite/issues/1483)) ([e5576c3](https://github.com/vitejs/vite/commit/e5576c32c08214766c8bac5458dfcad8301d3a1a))
+* don't force terser on non-legacy (fix [#6266](https://github.com/vitejs/vite/issues/6266)) ([#6272](https://github.com/vitejs/vite/issues/6272)) ([1da104e](https://github.com/vitejs/vite/commit/1da104e8597e2965313e8cd582d032bca551e4ee))
+* **legacy:** fix conflict with the modern build on css emitting ([#6584](https://github.com/vitejs/vite/issues/6584)) ([f48255e](https://github.com/vitejs/vite/commit/f48255e6e0058e973b949fb4a2372974f0480e11)), closes [#3296](https://github.com/vitejs/vite/issues/3296) [#3317](https://github.com/vitejs/vite/issues/3317)
+
+### Miscellaneous Chores
+
+* convert scripts to TS ([#6160](https://github.com/vitejs/vite/issues/6160)) ([15b6f1b](https://github.com/vitejs/vite/commit/15b6f1ba82731c16b19e00ca3b28b1a898caa4d4))
+* **deps:** update all non-major dependencies ([#5879](https://github.com/vitejs/vite/issues/5879)) ([aab303f](https://github.com/vitejs/vite/commit/aab303f7bd333307c77363259f97a310762c4848))
+* **deps:** update all non-major dependencies ([#6185](https://github.com/vitejs/vite/issues/6185)) ([b45f4ad](https://github.com/vitejs/vite/commit/b45f4ad9f1336d1e88d271d7aca9498dde2e5013))
+* **deps:** update all non-major dependencies ([#6357](https://github.com/vitejs/vite/issues/6357)) ([a272c07](https://github.com/vitejs/vite/commit/a272c07e5c442f54e4439a4f3a9da0ebb10f73c9))
+* prefer type imports ([#5835](https://github.com/vitejs/vite/issues/5835)) ([7186857](https://github.com/vitejs/vite/commit/71868579058512b51991718655e089a78b99d39c))
+* properly parse process.env.DEBUG in plugin-legacy ([#6545](https://github.com/vitejs/vite/issues/6545)) ([155fd11](https://github.com/vitejs/vite/commit/155fd11b783eddd33f0cd7e411eea40a3585217a))
+## [1.6.4](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.3...plugin-legacy@1.6.4) (2021-12-07)
+### Miscellaneous Chores
+* use cjs extension with scripts ([#5877](https://github.com/vitejs/vite/issues/5877)) ([775baba](https://github.com/vitejs/vite/commit/775babac40da546b01b8b8cbd7dff32b5cfcee6d))
+
+## [1.6.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.2...plugin-legacy@1.6.3) (2021-11-22)
+### Bug Fixes
+
+* **build:** resolve `rollupOptions.input` paths ([#5601](https://github.com/vitejs/vite/issues/5601)) ([5b6b016](https://github.com/vitejs/vite/commit/5b6b01693720290e8998b2613f0dcb2d699ee84f))
+
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#5679](https://github.com/vitejs/vite/issues/5679)) ([09f4d57](https://github.com/vitejs/vite/commit/09f4d57e12de46ffc5fa151c218f31845ad4b471))
+* **deps:** update all non-major dependencies ([#5783](https://github.com/vitejs/vite/issues/5783)) ([eee9406](https://github.com/vitejs/vite/commit/eee940678b76966647b543e1f10fdb113da64b21))
+* **deps:** update non critical deps ([#5569](https://github.com/vitejs/vite/issues/5569)) ([09e2a5f](https://github.com/vitejs/vite/commit/09e2a5f16f36d84e95448a9ae819cec5faeb41f3))
+* **deps:** update plugins ([#5462](https://github.com/vitejs/vite/issues/5462)) ([50b5e2e](https://github.com/vitejs/vite/commit/50b5e2ea2605ba392fa622c9419b8cb5da69e0d2))
+
+## [1.6.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.1...plugin-legacy@1.6.2) (2021-10-11)
### Features
-* **plugin-legacy:** support additionalLegacyPolyfills ([ca25896](https://github.com/vitejs/vite/commit/ca258962957c32df0196f30267c3d77b544aacbd)), closes [#1475](https://github.com/vitejs/vite/issues/1475)
+* add `build.cssTarget` option ([#5132](https://github.com/vitejs/vite/issues/5132)) ([b17444f](https://github.com/vitejs/vite/commit/b17444fd97b02bc54410c8575e7d3cb25e4058c2)), closes [#4746](https://github.com/vitejs/vite/issues/4746) [#5070](https://github.com/vitejs/vite/issues/5070) [#4930](https://github.com/vitejs/vite/issues/4930)
+## [1.6.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.6.0...plugin-legacy@1.6.1) (2021-10-05)
+### Bug Fixes
+* **plugin-legacy:** use terser as the default minifier ([#5168](https://github.com/vitejs/vite/issues/5168)) ([9ee7234](https://github.com/vitejs/vite/commit/9ee72343884a7d679767833f7a659bbca6b96595))
-## [1.1.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.1.0...plugin-legacy@1.1.1) (2021-01-09)
+## [1.6.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.3...plugin-legacy@1.6.0) (2021-09-29)
+### Features
+* **plugin-legacy:** add externalSystemJS option ([#5024](https://github.com/vitejs/vite/issues/5024)) ([60b6f55](https://github.com/vitejs/vite/commit/60b6f5595a00cbf014a30d57721081eb79436a97))
### Bug Fixes
-* **plugin-legacy:** add `index.d.ts` at publish ([#1457](https://github.com/vitejs/vite/issues/1457)) ([dce2456](https://github.com/vitejs/vite/commit/dce245629651edab9719127deaf07ecfbcf20c5f))
+* **deps:** update all non-major dependencies ([#4545](https://github.com/vitejs/vite/issues/4545)) ([a44fd5d](https://github.com/vitejs/vite/commit/a44fd5d38679da0be2536103e83af730cda73a95))
+* esbuild minification and renderLegacyChunks false ([#5054](https://github.com/vitejs/vite/issues/5054)) ([ed384cf](https://github.com/vitejs/vite/commit/ed384cfeff9e3ccb0fdbb07ec91758308da66226))
+* normalize internal plugin names ([#4976](https://github.com/vitejs/vite/issues/4976)) ([37f0b2f](https://github.com/vitejs/vite/commit/37f0b2fff74109d381513ed052a32b43655ee11d))
+* **plugin-legacy:** fix type errors ([#4762](https://github.com/vitejs/vite/issues/4762)) ([5491143](https://github.com/vitejs/vite/commit/5491143be0b4214d2dab91076a85739d6d106481))
+
+### Miscellaneous Chores
+
+* **deps:** update all non-major dependencies ([#4992](https://github.com/vitejs/vite/issues/4992)) ([5274c2e](https://github.com/vitejs/vite/commit/5274c2e9fbfd7d80392b5d0c9daacbe2d7237649))
+* **deps:** update all non-major dependencies ([#5100](https://github.com/vitejs/vite/issues/5100)) ([b2ae627](https://github.com/vitejs/vite/commit/b2ae627c74ee8aeff82c80d3461ae3004d0d8369))
+* prettier format ([#5121](https://github.com/vitejs/vite/issues/5121)) ([16fc894](https://github.com/vitejs/vite/commit/16fc8942e2b2181d78359cdc37e85a17be031af4))
+
+## [1.5.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.2...plugin-legacy@1.5.3) (2021-09-07)
+### Bug Fixes
+* **plugin-legacy:** fix regression introduced in [#4536](https://github.com/vitejs/vite/issues/4536) ([#4861](https://github.com/vitejs/vite/issues/4861)) ([fdc3212](https://github.com/vitejs/vite/commit/fdc3212474ff951e7e67810eca6cfb3ef1ed9ea2))
+* **plugin-legacy:** skip in SSR build ([#4536](https://github.com/vitejs/vite/issues/4536)) ([1f068fc](https://github.com/vitejs/vite/commit/1f068fcec38fc07c34e75a19821064386e460907))
+
+## [1.5.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.1...plugin-legacy@1.5.2) (2021-09-01)
+### Bug Fixes
+
+* **plugin-legacy:** avoid executing blank dynamic import ([#4767](https://github.com/vitejs/vite/issues/4767)) ([de71408](https://github.com/vitejs/vite/commit/de7140853140029a3f48600b60e700464e7662b5)), closes [#4568](https://github.com/vitejs/vite/issues/4568)
+### Documentation
+
+* include algorithm in Content Security Policy hash ([#4690](https://github.com/vitejs/vite/issues/4690)) ([6815edd](https://github.com/vitejs/vite/commit/6815eddbb94f60bf0c08f91ee9404d93357eb602))
+
+## [1.5.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.5.0...plugin-legacy@1.5.1) (2021-08-03)
+### Bug Fixes
+
+* **deps:** update all non-major dependencies ([#4468](https://github.com/vitejs/vite/issues/4468)) ([cd54a22](https://github.com/vitejs/vite/commit/cd54a22b8d5048f5d25a9954697f49b6d65dcc1f))
+* **plugin-legacy:** bake-in Promise polyfill, fix [#4414](https://github.com/vitejs/vite/issues/4414) ([#4440](https://github.com/vitejs/vite/issues/4440)) ([024a2de](https://github.com/vitejs/vite/commit/024a2de63df60a4037f9f7b13a0bc6e2b0d41fb6))
-# [1.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.0.1...plugin-legacy@1.1.0) (2021-01-07)
+## [1.5.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.4...plugin-legacy@1.5.0) (2021-07-27)
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#4387](https://github.com/vitejs/vite/issues/4387)) ([2f900ba](https://github.com/vitejs/vite/commit/2f900ba4d4ad8061e0046898e8d1de3129e7f784))
+* **plugin-legacy:** legacy fallback for dynamic import ([#3885](https://github.com/vitejs/vite/issues/3885)) ([fc6d8f1](https://github.com/vitejs/vite/commit/fc6d8f1d3efe836f17f3c45375dd3749128b8137))
+## [1.4.4](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.3...plugin-legacy@1.4.4) (2021-07-12)
### Features
-* use constant inline script + provide CSP hashes ([72107cd](https://github.com/vitejs/vite/commit/72107cdcdb7241e9fadd67528abb14f54b1c901d))
+* allow entryFileNames, chunkFileNames functions for legacy ([#4122](https://github.com/vitejs/vite/issues/4122)) ([df29bff](https://github.com/vitejs/vite/commit/df29bfff44ad7f2e822f92935d0ca9c99f15b67e))
+### Miscellaneous Chores
+* **deps:** update all non-major dependencies ([#4117](https://github.com/vitejs/vite/issues/4117)) ([e30ce56](https://github.com/vitejs/vite/commit/e30ce56861a154389a47e679c46a93680aae1325))
+* improve prettier config ([#4154](https://github.com/vitejs/vite/issues/4154)) ([98d95e3](https://github.com/vitejs/vite/commit/98d95e3e11bbc43e410b213b682e315b9344d2d7))
-## [1.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.0.0...plugin-legacy@1.0.1) (2021-01-07)
+## [1.4.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.2...plugin-legacy@1.4.3) (2021-06-27)
+### Bug Fixes
+* **deps:** update all non-major dependencies ([#3878](https://github.com/vitejs/vite/issues/3878)) ([a66a805](https://github.com/vitejs/vite/commit/a66a8053e9520d20bcf95fce870570c5195bcc91))
+* don't force polyfillDynamicImport if renderLegacyChunks is false ([#3695](https://github.com/vitejs/vite/issues/3695)) ([#3774](https://github.com/vitejs/vite/issues/3774)) ([d2a51ca](https://github.com/vitejs/vite/commit/d2a51ca4eda2ca9f99d9a066836d76d2253cfc24))
+* **plugin-legacy:** chunk may not exist ([#3886](https://github.com/vitejs/vite/issues/3886)) ([dd5931d](https://github.com/vitejs/vite/commit/dd5931d9c1cf382849047332b2c3409755ceebf5))
+## [1.4.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.1...plugin-legacy@1.4.2) (2021-06-22)
### Bug Fixes
-* **plugin-legacy:** avoid esbuild transform on legacy chunks ([7734105](https://github.com/vitejs/vite/commit/7734105093c6dabf64da6bfc11486aa9ac62efea))
-* add promise polyfill if not used in bundle ([b72db4e](https://github.com/vitejs/vite/commit/b72db4e3ec5ca8974ea2f1913d5611f73c0978b5))
+* **deps:** update all non-major dependencies ([#3791](https://github.com/vitejs/vite/issues/3791)) ([74d409e](https://github.com/vitejs/vite/commit/74d409eafca8d74ec4a6ece621ea2895bc1f2a32))
+* **plugin-legacy:** wrap chunks in IIFE ([#3783](https://github.com/vitejs/vite/issues/3783)) ([9abdb81](https://github.com/vitejs/vite/commit/9abdb8137ef54dd095e7bc47ae6a1ccf490fd196))
+## [1.4.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.4.0...plugin-legacy@1.4.1) (2021-06-01)
+### Bug Fixes
-### Performance Improvements
+* **plugin-legacy:** respect custom filenames formats, fix [#2356](https://github.com/vitejs/vite/issues/2356) ([#2641](https://github.com/vitejs/vite/issues/2641)) ([d852731](https://github.com/vitejs/vite/commit/d852731622a1c009d15a5172343fc166c4bb5cb7))
-* use @babel/standalone + lazy load ([b2f98fb](https://github.com/vitejs/vite/commit/b2f98fba0215ef171af2abc62e3aefc35f00d168))
+## [1.4.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.4...plugin-legacy@1.4.0) (2021-05-17)
+### Bug Fixes
+* **plugin-legacy:** turn off babel loose mode ([#3406](https://github.com/vitejs/vite/issues/3406)) ([5348c02](https://github.com/vitejs/vite/commit/5348c02f58bde36c412dbfe36c3ad37772bf83e5))
+* restore dynamic-import-polyfill ([#3434](https://github.com/vitejs/vite/issues/3434)) ([4112c5d](https://github.com/vitejs/vite/commit/4112c5d103673b83c50d446096086617dfaac5a3))
+### Documentation
-# 1.0.0 (2021-01-07)
+* **plugin-legacy:** add note about IE11, close [#3362](https://github.com/vitejs/vite/issues/3362) ([#3389](https://github.com/vitejs/vite/issues/3389)) ([b0b62f9](https://github.com/vitejs/vite/commit/b0b62f96d7d4b04d3bfea683feff84c8b31f1eca))
+## [1.3.4](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.3...plugin-legacy@1.3.4) (2021-05-11)
+### Bug Fixes
+
+* **plugin-legacy:** move polyfills in plugin post, fixes [#2786](https://github.com/vitejs/vite/issues/2786) and [#2781](https://github.com/vitejs/vite/issues/2781) ([#3023](https://github.com/vitejs/vite/issues/3023)) ([43150e3](https://github.com/vitejs/vite/commit/43150e352d164744e2fda766927053439bdf7db9))
+* **plugin-legacy:** require Vite 2.0.0 final ([#3265](https://github.com/vitejs/vite/issues/3265)) ([e395dee](https://github.com/vitejs/vite/commit/e395deeb0f11ebb1bcebe69233adebaad10f77eb))
+
+## [1.3.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.2...plugin-legacy@1.3.3) (2021-05-03)
+### Bug Fixes
+
+* ignore babelrc in legacy plugin ([#2801](https://github.com/vitejs/vite/issues/2801)) ([d466ad0](https://github.com/vitejs/vite/commit/d466ad0a095859a895fd4cb85f425ad4c4583e4e))
+* **plugin-legacy:** correct log level to error ([#3241](https://github.com/vitejs/vite/issues/3241)) ([474fe9a](https://github.com/vitejs/vite/commit/474fe9a3abbdf4845447eaab821d2ba51fda6207))
+
+### Miscellaneous Chores
+
+* Add `repository.directory` to `packages/**/package.json` ([#2687](https://github.com/vitejs/vite/issues/2687)) ([0ecff94](https://github.com/vitejs/vite/commit/0ecff9417ee0ccfea9132fb9df39eb4398c11eaf))
+
+### Tests
+
+* fix timeout hiding runtime build error ([#3185](https://github.com/vitejs/vite/issues/3185)) ([978d991](https://github.com/vitejs/vite/commit/978d991293f5b8e1a4197ac4e3aee4fa2e838a88))
+
+## [1.3.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.1...plugin-legacy@1.3.2) (2021-03-27)
+### Bug Fixes
+
+* typo in plugin-legacy ([#2651](https://github.com/vitejs/vite/issues/2651)) ([9a2ce75](https://github.com/vitejs/vite/commit/9a2ce7580772cb783a9f8fda7e45e4a9adacbec2))
+
+### Miscellaneous Chores
+
+* **plugin-legacy:** upgrade @babel/standalone to 7.13.12 ([#2649](https://github.com/vitejs/vite/issues/2649)) ([4b89f5b](https://github.com/vitejs/vite/commit/4b89f5b97e715cff9078a528d06ef1255dfff293))
+* **plugin-legacy:** upgrade @babel/standalone to 7.13.6 ([#2198](https://github.com/vitejs/vite/issues/2198)) ([609f8aa](https://github.com/vitejs/vite/commit/609f8aa726d81a4094b60d3e0374c78238837a07))
+
+## [1.3.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.3.0...plugin-legacy@1.3.1) (2021-02-15)
+### Bug Fixes
+
+* **plugin-legacy:** prevent constant folding for import.meta.env.LEGACY ([bace724](https://github.com/vitejs/vite/commit/bace7244e776b3f4c9dd7e3ff1885df668cbcb87)), closes [#1999](https://github.com/vitejs/vite/issues/1999)
+* **plugin-legacy:** use correct string length in legacy env replacement ([#2015](https://github.com/vitejs/vite/issues/2015)) ([7f48086](https://github.com/vitejs/vite/commit/7f4808634f57ca8f4be3b455cc4fb8016acdc4fd))
+
+### Miscellaneous Chores
+
+* **plugin-legacy:** typo ([#2004](https://github.com/vitejs/vite/issues/2004)) [skip ci] ([5225253](https://github.com/vitejs/vite/commit/5225253bc9730b2db1a8b62642cd1496053fce6e))
+
+## [1.3.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.3...plugin-legacy@1.3.0) (2021-02-11)
+### Features
+
+* **plugin-legacy:** inject import.meta.env.LEGACY ([416f190](https://github.com/vitejs/vite/commit/416f190aa92f06a06f3ded403fb1e4cb8729256d))
+
+### Code Refactoring
+
+* remove unused ast, adjust logic ([859fed6](https://github.com/vitejs/vite/commit/859fed6780e2412e64e27aa841a7de0aa2986728))
+
+## [1.2.3](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.2...plugin-legacy@1.2.3) (2021-02-01)
+### Features
+
+* **plugin-legacy:** use compact output when transpiling legacy chunks ([045e519](https://github.com/vitejs/vite/commit/045e519d51fbce94bddb60793e9e99311acc5aa2)), closes [#1828](https://github.com/vitejs/vite/issues/1828)
+
+### Code Refactoring
+
+* **plugin-legacy:** improve polyfill import removal strategy ([e40e6b2](https://github.com/vitejs/vite/commit/e40e6b29e62acf8300de5cca16e376bfeb27bc5e))
+
+## [1.2.2](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.1...plugin-legacy@1.2.2) (2021-01-25)
+### Features
+
+* default vendor chunk splitting ([f6b58a0](https://github.com/vitejs/vite/commit/f6b58a0f535b1c26f9c1dfda74c28c685402c3c9))
+* support `base` option during dev, deprecate `build.base` ([#1556](https://github.com/vitejs/vite/issues/1556)) ([809d4bd](https://github.com/vitejs/vite/commit/809d4bd3bf62d3bc6b35f182178922d2ab2175f1))
+
+### Bug Fixes
+
+* **plugin-legacy:** throw error when using esbuild minify with legacy plugin ([8fb2511](https://github.com/vitejs/vite/commit/8fb2511a02c163d85f60dfab0bef104756768e35))
+
+## [1.2.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.2.0...plugin-legacy@1.2.1) (2021-01-14)
+### Bug Fixes
+
+* **plugin-legacy:** respect config.build.assetsDir ([#1532](https://github.com/vitejs/vite/issues/1532)) ([3e7ad3f](https://github.com/vitejs/vite/commit/3e7ad3fa26a6149b44b2e522648cbda1009e4888)), closes [#1530](https://github.com/vitejs/vite/issues/1530)
+
+## [1.2.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.1.1...plugin-legacy@1.2.0) (2021-01-11)
+### Features
+
+* **plugin-legacy:** support additionalLegacyPolyfills ([ca25896](https://github.com/vitejs/vite/commit/ca258962957c32df0196f30267c3d77b544aacbd)), closes [#1475](https://github.com/vitejs/vite/issues/1475)
+
+### Bug Fixes
+
+* **plugin-html:** typo in the Safari 10 nomodule snippet ([#1483](https://github.com/vitejs/vite/issues/1483)) ([e5576c3](https://github.com/vitejs/vite/commit/e5576c32c08214766c8bac5458dfcad8301d3a1a))
+
+## [1.1.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.1.0...plugin-legacy@1.1.1) (2021-01-09)
+### Bug Fixes
+
+* **plugin-legacy:** add `index.d.ts` at publish ([#1457](https://github.com/vitejs/vite/issues/1457)) ([dce2456](https://github.com/vitejs/vite/commit/dce245629651edab9719127deaf07ecfbcf20c5f))
+
+### Documentation
+
+* **plugin-legacy:** fix typos ([#1422](https://github.com/vitejs/vite/issues/1422)) [skip ci] ([16cf3d0](https://github.com/vitejs/vite/commit/16cf3d0cc758591c2a44abbf10b7f2fd21d5ef99))
+* Typo in plugin-legacy README ([#1455](https://github.com/vitejs/vite/issues/1455)) [skip ci] ([4647e07](https://github.com/vitejs/vite/commit/4647e072cb89d6eac648a66314f26cb6b65c68b4))
+
+### Miscellaneous Chores
+* add version badge for plugins [skip ci] ([62925eb](https://github.com/vitejs/vite/commit/62925eb4da2ce2053e3d28068db5423e2e66ae3d))
+
+## [1.1.0](https://github.com/vitejs/vite/compare/plugin-legacy@1.0.1...plugin-legacy@1.1.0) (2021-01-07)
+### Features
+
+* use constant inline script + provide CSP hashes ([72107cd](https://github.com/vitejs/vite/commit/72107cdcdb7241e9fadd67528abb14f54b1c901d))
+
+## [1.0.1](https://github.com/vitejs/vite/compare/plugin-legacy@1.0.0...plugin-legacy@1.0.1) (2021-01-07)
### Features
* **plugin-legacy:** @vitejs/plugin-legacy ([8c34870](https://github.com/vitejs/vite/commit/8c34870040f8c2f4be7d00245a3683f9e64d894e))
+### Bug Fixes
+
+* add promise polyfill if not used in bundle ([b72db4e](https://github.com/vitejs/vite/commit/b72db4e3ec5ca8974ea2f1913d5611f73c0978b5))
+* **plugin-legacy:** avoid esbuild transform on legacy chunks ([7734105](https://github.com/vitejs/vite/commit/7734105093c6dabf64da6bfc11486aa9ac62efea))
+
+### Performance Improvements
+
+* use @babel/standalone + lazy load ([b2f98fb](https://github.com/vitejs/vite/commit/b2f98fba0215ef171af2abc62e3aefc35f00d168))
+
+### Documentation
+* **plugin-legacy:** fix typo ([#1411](https://github.com/vitejs/vite/issues/1411)) ([3321111](https://github.com/vitejs/vite/commit/3321111c77f33ccb9bc06bfa84f6d3fc27902a6e))
+### Miscellaneous Chores
+
+* add plugin-legacy version requirement ([3b7a07a](https://github.com/vitejs/vite/commit/3b7a07ac5c7422b01577a26b2ca5b8e1e7001fa3))
+* changelog for plugin-legacy [skip ci] ([52ac81a](https://github.com/vitejs/vite/commit/52ac81a14298bf41e11f3bc0d2ae870d67b5ae9d))
+
+# 1.0.0 (2021-01-07)
+
+
+### Features
+
+* **plugin-legacy:** @vitejs/plugin-legacy ([8c34870](https://github.com/vitejs/vite/commit/8c34870040f8c2f4be7d00245a3683f9e64d894e))
diff --git a/packages/plugin-legacy/LICENSE b/packages/plugin-legacy/LICENSE
new file mode 100644
index 00000000000000..b7e97ecb6aa4dd
--- /dev/null
+++ b/packages/plugin-legacy/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019-present, VoidZero Inc. and Vite contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/plugin-legacy/README.md b/packages/plugin-legacy/README.md
index b5bf572e53fa2c..80925d1d790838 100644
--- a/packages/plugin-legacy/README.md
+++ b/packages/plugin-legacy/README.md
@@ -1,8 +1,6 @@
# @vitejs/plugin-legacy [](https://npmjs.com/package/@vitejs/plugin-legacy)
-**Note: this plugin requires `vite@^2.0.0`**.
-
-Vite's default browser support baseline is [Native ESM](https://caniuse.com/es6-module). This plugin provides support for legacy browsers that do not support native ESM.
+Vite's minimum browser support target is [native ESM dynamic import](https://caniuse.com/es6-module-dynamic-import), and [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta). This plugin provides support for legacy browsers that do not support those features when building for production.
By default, this plugin will:
@@ -10,7 +8,7 @@ By default, this plugin will:
- Generate a polyfill chunk including SystemJS runtime, and any necessary polyfills determined by specified browser targets and **actual usage** in the bundle.
-- Inject `
diff --git a/packages/vite/src/node/__tests__/packages/child/index.js b/packages/vite/src/node/__tests__/packages/child/index.js
new file mode 100644
index 00000000000000..186b120756be19
--- /dev/null
+++ b/packages/vite/src/node/__tests__/packages/child/index.js
@@ -0,0 +1 @@
+export default true
diff --git a/packages/vite/src/node/__tests__/packages/child/package.json b/packages/vite/src/node/__tests__/packages/child/package.json
new file mode 100644
index 00000000000000..77e2aa64615b63
--- /dev/null
+++ b/packages/vite/src/node/__tests__/packages/child/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "@vitejs/child",
+ "type": "module",
+ "main": "./index.js"
+}
diff --git a/packages/vite/src/node/__tests__/packages/module/package.json b/packages/vite/src/node/__tests__/packages/module/package.json
new file mode 100644
index 00000000000000..67756e1d2c410e
--- /dev/null
+++ b/packages/vite/src/node/__tests__/packages/module/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "mylib",
+ "type": "module"
+}
diff --git a/packages/vite/src/node/__tests__/packages/package.json b/packages/vite/src/node/__tests__/packages/package.json
new file mode 100644
index 00000000000000..bd6442dcacf7c9
--- /dev/null
+++ b/packages/vite/src/node/__tests__/packages/package.json
@@ -0,0 +1,3 @@
+{
+ "name": "named-testing-package"
+}
diff --git a/packages/vite/src/node/__tests__/packages/parent/index.ts b/packages/vite/src/node/__tests__/packages/parent/index.ts
new file mode 100644
index 00000000000000..747305283cadb2
--- /dev/null
+++ b/packages/vite/src/node/__tests__/packages/parent/index.ts
@@ -0,0 +1,6 @@
+// @ts-expect-error not typed
+import child from '@vitejs/child'
+
+export default {
+ child,
+}
diff --git a/packages/vite/src/node/__tests__/packages/parent/package.json b/packages/vite/src/node/__tests__/packages/parent/package.json
new file mode 100644
index 00000000000000..d966448a0560a8
--- /dev/null
+++ b/packages/vite/src/node/__tests__/packages/parent/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@vitejs/parent",
+ "type": "module",
+ "main": "./index.ts",
+ "dependencies": {
+ "@vitejs/child": "link:../child"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/__snapshots__/license.spec.ts.snap b/packages/vite/src/node/__tests__/plugins/__snapshots__/license.spec.ts.snap
new file mode 100644
index 00000000000000..d93b6c9515daa3
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/__snapshots__/license.spec.ts.snap
@@ -0,0 +1,47 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`json 1`] = `
+"[
+ {
+ "name": "@vitejs/test-dep-licence-cc0",
+ "version": "0.0.0",
+ "identifier": "CC0-1.0",
+ "text": "CC0 1.0 Universal\\n\\n..."
+ },
+ {
+ "name": "@vitejs/test-dep-license-mit",
+ "version": "0.0.0",
+ "identifier": "MIT",
+ "text": "MIT License\\n\\nCopyright (c) ..."
+ },
+ {
+ "name": "@vitejs/test-dep-nested-license-isc",
+ "version": "0.0.0",
+ "identifier": "ISC",
+ "text": "Copyright (c) ..."
+ }
+]"
+`;
+
+exports[`markdown 1`] = `
+"# Licenses
+
+The app bundles dependencies which contain the following licenses:
+
+## @vitejs/test-dep-licence-cc0 - 0.0.0 (CC0-1.0)
+
+CC0 1.0 Universal
+
+...
+
+## @vitejs/test-dep-license-mit - 0.0.0 (MIT)
+
+MIT License
+
+Copyright (c) ...
+
+## @vitejs/test-dep-nested-license-isc - 0.0.0 (ISC)
+
+Copyright (c) ...
+"
+`;
diff --git a/packages/vite/src/node/__tests__/plugins/assetImportMetaUrl.spec.ts b/packages/vite/src/node/__tests__/plugins/assetImportMetaUrl.spec.ts
new file mode 100644
index 00000000000000..38355b38fe6b31
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/assetImportMetaUrl.spec.ts
@@ -0,0 +1,65 @@
+import { describe, expect, test } from 'vitest'
+import { parseAst } from 'rollup/parseAst'
+import { assetImportMetaUrlPlugin } from '../../plugins/assetImportMetaUrl'
+import { resolveConfig } from '../../config'
+import { PartialEnvironment } from '../../baseEnvironment'
+
+async function createAssetImportMetaurlPluginTransform() {
+ const config = await resolveConfig({ configFile: false }, 'serve')
+ const instance = assetImportMetaUrlPlugin(config)
+ const environment = new PartialEnvironment('client', config)
+
+ return async (code: string) => {
+ // @ts-expect-error transform.handler should exist
+ const result = await instance.transform.handler.call(
+ { environment, parse: parseAst },
+ code,
+ 'foo.ts',
+ )
+ return result?.code || result
+ }
+}
+
+describe('assetImportMetaUrlPlugin', async () => {
+ const transform = await createAssetImportMetaurlPluginTransform()
+
+ test('variable between /', async () => {
+ expect(
+ await transform('new URL(`./foo/${dir}/index.js`, import.meta.url)'),
+ ).toMatchInlineSnapshot(
+ `"new URL((import.meta.glob("./foo/*/index.js", {"eager":true,"import":"default","query":"?url"}))[\`./foo/\${dir}/index.js\`], import.meta.url)"`,
+ )
+ })
+
+ test('variable before non-/', async () => {
+ expect(
+ await transform('new URL(`./foo/${dir}.js`, import.meta.url)'),
+ ).toMatchInlineSnapshot(
+ `"new URL((import.meta.glob("./foo/*.js", {"eager":true,"import":"default","query":"?url"}))[\`./foo/\${dir}.js\`], import.meta.url)"`,
+ )
+ })
+
+ test('two variables', async () => {
+ expect(
+ await transform('new URL(`./foo/${dir}${file}.js`, import.meta.url)'),
+ ).toMatchInlineSnapshot(
+ `"new URL((import.meta.glob("./foo/*.js", {"eager":true,"import":"default","query":"?url"}))[\`./foo/\${dir}\${file}.js\`], import.meta.url)"`,
+ )
+ })
+
+ test('two variables between /', async () => {
+ expect(
+ await transform(
+ 'new URL(`./foo/${dir}${dir2}/index.js`, import.meta.url)',
+ ),
+ ).toMatchInlineSnapshot(
+ `"new URL((import.meta.glob("./foo/*/index.js", {"eager":true,"import":"default","query":"?url"}))[\`./foo/\${dir}\${dir2}/index.js\`], import.meta.url)"`,
+ )
+ })
+
+ test('ignore starting with a variable', async () => {
+ expect(
+ await transform('new URL(`${file}.js`, import.meta.url)'),
+ ).toMatchInlineSnapshot(`"new URL(\`\${file}.js\`, import.meta.url)"`)
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/css.spec.ts b/packages/vite/src/node/__tests__/plugins/css.spec.ts
index 539ec2f1af1810..9f06607b0648f1 100644
--- a/packages/vite/src/node/__tests__/plugins/css.spec.ts
+++ b/packages/vite/src/node/__tests__/plugins/css.spec.ts
@@ -1,18 +1,33 @@
-import { cssUrlRE, cssPlugin } from '../../plugins/css'
+import path from 'node:path'
+import { describe, expect, test } from 'vitest'
+import type { InternalModuleFormat } from 'rolldown'
+import MagicString from 'magic-string'
import { resolveConfig } from '../../config'
-import fs from 'fs'
-import path from 'path'
+import type { InlineConfig } from '../../config'
+import {
+ convertTargets,
+ cssPlugin,
+ cssUrlRE,
+ getEmptyChunkReplacer,
+ hoistAtRules,
+ injectInlinedCSS,
+ preprocessCSS,
+ resolveLibCssFilename,
+} from '../../plugins/css'
+import { PartialEnvironment } from '../../baseEnvironment'
+
+const dirname = import.meta.dirname
describe('search css url function', () => {
test('some spaces before it', () => {
expect(
- cssUrlRE.test("list-style-image: url('../images/bullet.jpg');")
+ cssUrlRE.test("list-style-image: url('../images/bullet.jpg');"),
).toBe(true)
})
test('no space after colon', () => {
expect(cssUrlRE.test("list-style-image:url('../images/bullet.jpg');")).toBe(
- true
+ true,
)
})
@@ -24,93 +39,762 @@ describe('search css url function', () => {
expect(
cssUrlRE.test(`@function svg-url($string) {
@return "";
- }`)
+ }`),
).toBe(false)
})
test('after parenthesis', () => {
expect(
cssUrlRE.test(
- 'mask-image: image(url(mask.png), skyblue, linear-gradient(rgba(0, 0, 0, 1.0), transparent));'
- )
+ 'mask-image: image(url(mask.png), skyblue, linear-gradient(rgba(0, 0, 0, 1.0), transparent));',
+ ),
).toBe(true)
})
test('after comma', () => {
expect(
cssUrlRE.test(
- 'mask-image: image(skyblue,url(mask.png), linear-gradient(rgba(0, 0, 0, 1.0), transparent));'
- )
+ 'mask-image: image(skyblue,url(mask.png), linear-gradient(rgba(0, 0, 0, 1.0), transparent));',
+ ),
).toBe(true)
})
+
+ test('should capture the full url with escaped parentheses', () => {
+ const css = 'background-image: url(public/awkward-name\\)2.png);'
+ const match = cssUrlRE.exec(css)
+ expect(match?.[1].trim()).toBe('public/awkward-name\\)2.png')
+ })
})
-describe('css path resolutions', () => {
- const mockedProjectPath = path.join(process.cwd(), '/foo/bar/project')
- const mockedBarCssRelativePath = '/css/bar.module.css'
- const mockedFooCssRelativePath = '/css/foo.module.css'
+describe('css modules', () => {
+ test('css module compose/from path resolutions', async () => {
+ const { transform } = await createCssPluginTransform({
+ configFile: false,
+ resolve: {
+ alias: [
+ {
+ find: '@',
+ replacement: path.join(
+ import.meta.dirname,
+ './fixtures/css-module-compose',
+ ),
+ },
+ ],
+ },
+ })
- test('cssmodule compose/from path resolutions', async () => {
- const config = await resolveConfig(
- {
- resolve: {
- alias: [
- {
- find: '@',
- replacement: mockedProjectPath
- }
- ]
- }
+ const result = await transform(
+ `\
+.foo {
+position: fixed;
+composes: bar from '@/css/bar.module.css';
+}`,
+ '/css/foo.module.css',
+ )
+
+ expect(result.code).toMatchInlineSnapshot(
+ `
+ "._bar_1b4ow_1 {
+ display: block;
+ background: #f0f;
+ }
+ ._foo_86148_1 {
+ position: fixed;
+ }"
+ `,
+ )
+ })
+
+ test('custom generateScopedName', async () => {
+ const { transform } = await createCssPluginTransform({
+ configFile: false,
+ css: {
+ modules: {
+ generateScopedName: 'custom__[hash:base64:5]',
+ },
},
- 'serve'
+ })
+ const css = `\
+.foo {
+ color: red;
+}`
+ const result1 = await transform(css, '/foo.module.css') // server
+ const result2 = await transform(css, '/foo.module.css?direct') // client
+ expect(result1.code).toBe(result2.code)
+ })
+
+ test('custom generateScopedName with lightningcss', async () => {
+ const { transform } = await createCssPluginTransform({
+ configFile: false,
+ css: {
+ modules: {
+ generateScopedName: 'custom__[hash:base64:5]',
+ },
+ transformer: 'lightningcss',
+ },
+ })
+ const css = `\
+.foo {
+ color: red;
+}`
+ const result1 = await transform(css, '/foo.module.css') // server
+ const result2 = await transform(css, '/foo.module.css?direct') // client
+ expect(result1.code).toBe(result2.code)
+ })
+})
+
+describe('hoist @ rules', () => {
+ test('hoist @import', async () => {
+ const css = `.foo{color:red;}@import "bla";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(`@import "bla";.foo{color:red;}`)
+ })
+
+ test('hoist @import url with semicolon', async () => {
+ const css = `.foo{color:red;}@import url("bla;bla");`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(`@import url("bla;bla");.foo{color:red;}`)
+ })
+
+ test('hoist @import url data with semicolon', async () => {
+ const css = `.foo{color:red;}@import url(data:image/png;base64,iRxVB0);`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(
+ `@import url(data:image/png;base64,iRxVB0);.foo{color:red;}`,
)
+ })
- const { transform, buildStart } = cssPlugin(config)
+ test('hoist @import with semicolon in quotes', async () => {
+ const css = `.foo{color:red;}@import "bla;bar";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(`@import "bla;bar";.foo{color:red;}`)
+ })
- await buildStart.call({})
+ test('hoist @charset', async () => {
+ const css = `.foo{color:red;}@charset "utf-8";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(`@charset "utf-8";.foo{color:red;}`)
+ })
- const mockFs = jest
- .spyOn(fs, 'readFile')
- // @ts-ignore jest.spyOn not recognize overrided `fs.readFile` definition.
- .mockImplementationOnce((p, encoding, callback) => {
- expect(p).toBe(path.join(mockedProjectPath, mockedBarCssRelativePath))
- expect(encoding).toBe('utf-8')
- callback(
- null,
- Buffer.from(`
-.bar {
- display: block;
- background: #f0f;
+ test('hoist one @charset only', async () => {
+ const css = `.foo{color:red;}@charset "utf-8";@charset "utf-8";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(`@charset "utf-8";.foo{color:red;}`)
+ })
+
+ test('hoist @import and @charset', async () => {
+ const css = `.foo{color:red;}@import "bla";@charset "utf-8";.bar{color:green;}@import "baz";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(
+ `@charset "utf-8";@import "bla";@import "baz";.foo{color:red;}.bar{color:green;}`,
+ )
+ })
+
+ test('dont hoist @import in comments', async () => {
+ const css = `.foo{color:red;}/* @import "bla"; */@import "bar";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(`@import "bar";.foo{color:red;}/* @import "bla"; */`)
+ })
+
+ test('dont hoist @charset in comments', async () => {
+ const css = `.foo{color:red;}/* @charset "utf-8"; */@charset "utf-8";`
+ const result = await hoistAtRules(css)
+ expect(result).toBe(
+ `@charset "utf-8";.foo{color:red;}/* @charset "utf-8"; */`,
+ )
+ })
+
+ test('dont hoist @import and @charset in comments', async () => {
+ const css = `
+.foo{color:red;}
+/*
+ @import "bla";
+*/
+@charset "utf-8";
+/*
+ @charset "utf-8";
+ @import "bar";
+*/
+@import "baz";`
+ const result = await hoistAtRules(css)
+ expect(result).toMatchInlineSnapshot(`
+ "@charset "utf-8";@import "baz";
+ .foo{color:red;}
+ /*
+ @import "bla";
+ */
+
+ /*
+ @charset "utf-8";
+ @import "bar";
+ */
+ "
+ `)
+ })
+})
+
+async function createCssPluginTransform(inlineConfig: InlineConfig = {}) {
+ const config = await resolveConfig(inlineConfig, 'serve')
+ const environment = new PartialEnvironment('client', config)
+
+ const { transform, buildStart } = cssPlugin(config)
+
+ // @ts-expect-error buildStart is function
+ await buildStart.call({})
+
+ return {
+ async transform(code: string, id: string) {
+ // @ts-expect-error transform.handler is function
+ return await transform.handler.call(
+ {
+ addWatchFile() {
+ return
+ },
+ environment,
+ },
+ code,
+ id,
+ )
+ },
+ }
}
- `)
- )
- })
- const { code } = await transform.call(
+describe('convertTargets', () => {
+ test('basic cases', () => {
+ expect(convertTargets('es2018')).toStrictEqual({
+ chrome: 4128768,
+ edge: 5177344,
+ firefox: 3801088,
+ safari: 786432,
+ ios_saf: 786432,
+ opera: 3276800,
+ })
+ expect(convertTargets(['safari13.1', 'ios13', 'node14'])).toStrictEqual({
+ ios_saf: 851968,
+ safari: 852224,
+ })
+ })
+
+ test('supports es6 as an alias of es2015', () => {
+ expect(convertTargets('es6')).toStrictEqual(convertTargets('es2015'))
+ })
+})
+
+describe('getEmptyChunkReplacer', () => {
+ test('replaces import call', () => {
+ const code = `\
+import "some-module";
+import "pure_css_chunk.js";
+import "other-module";`
+
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'es')
+ const replaced = replacer(code)
+ expect(replaced.length).toBe(code.length)
+ expect(replaced).toMatchInlineSnapshot(`
+ "import "some-module";
+ /* empty css */
+ import "other-module";"
+ `)
+ })
+
+ test('replaces import call without new lines', () => {
+ const code = `import "some-module";import "pure_css_chunk.js";import "other-module";`
+
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'es')
+ const replaced = replacer(code)
+ expect(replaced.length).toBe(code.length)
+ expect(replaced).toMatchInlineSnapshot(
+ `"import "some-module";/* empty css */import "other-module";"`,
+ )
+ })
+
+ test('replaces require call', () => {
+ const code = `\
+require("some-module");
+require("pure_css_chunk.js");
+require("other-module");`
+
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs')
+ const replaced = replacer(code)
+ expect(replaced.length).toBe(code.length)
+ expect(replaced).toMatchInlineSnapshot(`
+ "require("some-module");
+ ;/* empty css */
+ require("other-module");"
+ `)
+ })
+
+ test('replaces require call in minified code without new lines', () => {
+ const code = `require("some-module");require("pure_css_chunk.js");require("other-module");`
+
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs')
+ const replaced = replacer(code)
+ expect(replaced.length).toBe(code.length)
+ expect(replaced).toMatchInlineSnapshot(
+ `"require("some-module");;/* empty css */require("other-module");"`,
+ )
+ })
+
+ test('replaces require call in minified code that uses comma operator', () => {
+ const code =
+ 'require("some-module"),require("pure_css_chunk.js"),require("other-module");'
+
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs')
+ const newCode = replacer(code)
+ expect(newCode.length).toBe(code.length)
+ expect(newCode).toMatchInlineSnapshot(
+ `"require("some-module"),/* empty css */require("other-module");"`,
+ )
+ // So there should be no pure css chunk anymore
+ expect(newCode).not.toContain('pure_css_chunk.js')
+ })
+
+ test('replaces require call in minified code that uses comma operator 2', () => {
+ const code = 'require("pure_css_chunk.js"),console.log();'
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs')
+ const newCode = replacer(code)
+ expect(newCode.length).toBe(code.length)
+ expect(newCode).toMatchInlineSnapshot(
+ `"/* empty css */console.log();"`,
+ )
+ expect(newCode).not.toContain('pure_css_chunk.js')
+ })
+
+ test('replaces require call in minified code that uses comma operator followed by assignment', () => {
+ const code =
+ 'require("some-module"),require("pure_css_chunk.js");const v=require("other-module");'
+
+ const replacer = getEmptyChunkReplacer(['pure_css_chunk.js'], 'cjs')
+ const newCode = replacer(code)
+ expect(newCode.length).toBe(code.length)
+ expect(newCode).toMatchInlineSnapshot(
+ `"require("some-module");/* empty css */const v=require("other-module");"`,
+ )
+ expect(newCode).not.toContain('pure_css_chunk.js')
+ })
+})
+
+describe('preprocessCSS', () => {
+ test('works', async () => {
+ const resolvedConfig = await resolveConfig({ configFile: false }, 'serve')
+ const result = await preprocessCSS(
+ `\
+.foo {
+ color:red;
+ background: url(./foo.png);
+}`,
+ 'foo.css',
+ resolvedConfig,
+ )
+ expect(result.code).toMatchInlineSnapshot(`
+ ".foo {
+ color:red;
+ background: url(./foo.png);
+ }"
+ `)
+ })
+
+ test('works with lightningcss', async () => {
+ const resolvedConfig = await resolveConfig(
{
- addWatchFile() {
- return
- }
+ configFile: false,
+ css: { transformer: 'lightningcss' },
},
- `
+ 'serve',
+ )
+ const result = await preprocessCSS(
+ `\
.foo {
- position: fixed;
- composes: bar from '@${mockedBarCssRelativePath}';
-}
- `,
- path.join(mockedProjectPath, mockedFooCssRelativePath)
+ color: red;
+ background: url(./foo.png);
+}`,
+ 'foo.css',
+ resolvedConfig,
)
+ expect(result.code).toMatchInlineSnapshot(`
+ ".foo {
+ color: red;
+ background: url("./foo.png");
+ }
+ "
+ `)
+ })
+})
- expect(code).toBe(`
-._bar_soicv_2 {
- display: block;
- background: #f0f;
-}
-._foo_sctn3_2 {
- position: fixed;
-}
+describe('resolveLibCssFilename', () => {
+ test('use name from package.json', () => {
+ const filename = resolveLibCssFilename(
+ {
+ entry: 'mylib.js',
+ },
+ path.resolve(dirname, '../packages/name'),
+ )
+ expect(filename).toBe('mylib.css')
+ })
+
+ test('set cssFileName', () => {
+ const filename = resolveLibCssFilename(
+ {
+ entry: 'mylib.js',
+ cssFileName: 'style',
+ },
+ path.resolve(dirname, '../packages/noname'),
+ )
+ expect(filename).toBe('style.css')
+ })
+
+ test('use fileName if set', () => {
+ const filename = resolveLibCssFilename(
+ {
+ entry: 'mylib.js',
+ fileName: 'custom-name',
+ },
+ path.resolve(dirname, '../packages/name'),
+ )
+ expect(filename).toBe('custom-name.css')
+ })
+
+ test('use fileName if set and has array entry', () => {
+ const filename = resolveLibCssFilename(
+ {
+ entry: ['mylib.js', 'mylib2.js'],
+ fileName: 'custom-name',
+ },
+ path.resolve(dirname, '../packages/name'),
+ )
+ expect(filename).toBe('custom-name.css')
+ })
+})
+
+describe('injectInlinedCSS', () => {
+ function getInlinedCSSInjectedCode(
+ code: string,
+ format: InternalModuleFormat,
+ ) {
+ const s = new MagicString(code)
+ injectInlinedCSS(
+ s,
+ {
+ error(e) {
+ throw e
+ },
+ },
+ code,
+ format,
+ 'injectCSS();',
+ )
+ return s.toString()
+ }
+
+ test('should inject CSS for iife without exports from esm', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function() {
+
+"use strict";
+
+//#region src/index.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+})();
+
+//#endregion
+})();`,
+ 'iife',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function() {
+
+ "use strict";injectCSS();
+
+ //#region src/index.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+ })();
+
+ //#endregion
+ })();"
+ `)
+ })
+
+ test('should inject helper for iife without exports from cjs', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function() {
+
+
+//#region src/index.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+})();
+
+//#endregion
+})();`,
+ 'iife',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function() {injectCSS();
+
+
+ //#region src/index.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+ })();
+
+ //#endregion
+ })();"
+ `)
+ })
+
+ test('should inject helper for iife with exports', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `var lib = (function(exports) {
+
+
+//#region entry.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+})();
+const foo = "foo";
+
+//#endregion
+exports.foo = foo;
+return exports;
+})({});`,
+ 'iife',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "var lib = (function(exports) {injectCSS();
+
+
+ //#region entry.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+ })();
+ const foo = "foo";
+
+ //#endregion
+ exports.foo = foo;
+ return exports;
+ })({});"
+ `)
+ })
+
+ test('should inject helper for iife with nested name', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `this.nested = this.nested || {};
+this.nested.lib = (function(exports) {
+
+Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+//#region a.ts
+ const foo = "foo";
+
+//#endregion
+exports.foo = foo;
+return exports;
+})({});`,
+ 'iife',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "this.nested = this.nested || {};
+ this.nested.lib = (function(exports) {injectCSS();
+
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+ //#region a.ts
+ const foo = "foo";
+
+ //#endregion
+ exports.foo = foo;
+ return exports;
+ })({});"
+ `)
+ })
+
+ test('should inject helper for umd without exports', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function(factory) {
+
+ typeof define === 'function' && define.amd ? define([], factory) :
+ factory();
+})(function() {
+
+//#region entry.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+})();
+
+//#endregion
+});`,
+ 'umd',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function(factory) {
+
+ typeof define === 'function' && define.amd ? define([], factory) :
+ factory();
+ })(function() {injectCSS();
+
+ //#region entry.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+ })();
+
+ //#endregion
+ });"
+ `)
+ })
+
+ test('should inject helper for umd with exports', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function(global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.lib = {})));
+})(this, function(exports) {
+
+//#region entry.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+})();
+const foo = "foo";
+
+//#endregion
+exports.foo = foo;
+});`,
+ 'umd',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function(global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.lib = {})));
+ })(this, function(exports) {injectCSS();
+
+ //#region entry.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+ })();
+ const foo = "foo";
+
+ //#endregion
+ exports.foo = foo;
+ });"
`)
+ })
+
+ test('should inject helper for umd with only default export', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function(global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define([], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.lib = factory()));
+})(this, function() {
+
+//#region entry.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+})();
+var index_default = "foo";
+
+//#endregion
+return index_default;
+});`,
+ 'umd',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function(global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define([], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.lib = factory()));
+ })(this, function() {injectCSS();
+
+ //#region entry.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo");
+ })();
+ var index_default = "foo";
+
+ //#endregion
+ return index_default;
+ });"
+ `)
+ })
+
+ test('should inject helper for umd with nested name', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function(global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.nested = global.nested || {},global.nested.lib = {})));
+})(this, function(exports) {
+Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+//#region a.ts
+ const foo = "foo";
+
+//#endregion
+exports.foo = foo;
+});`,
+ 'umd',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function(global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.nested = global.nested || {},global.nested.lib = {})));
+ })(this, function(exports) {injectCSS();
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
- mockFs.mockReset()
+ //#region a.ts
+ const foo = "foo";
+
+ //#endregion
+ exports.foo = foo;
+ });"
+ `)
+ })
+
+ test('should inject multiple helpers', async () => {
+ const result = getInlinedCSSInjectedCode(
+ `(function() {
+
+"use strict";
+
+//#region src/index.js
+(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo", { ..."foo" });
+})();
+
+//#endregion
+})();`,
+ 'iife',
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "(function() {
+
+ "use strict";injectCSS();
+
+ //#region src/index.js
+ (async () => {
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
+ console.log("foo", { ..."foo" });
+ })();
+
+ //#endregion
+ })();"
+ `)
})
})
diff --git a/packages/vite/src/node/__tests__/plugins/define.spec.ts b/packages/vite/src/node/__tests__/plugins/define.spec.ts
index 9a65f8f3a51cea..68bd23aaa92cde 100644
--- a/packages/vite/src/node/__tests__/plugins/define.spec.ts
+++ b/packages/vite/src/node/__tests__/plugins/define.spec.ts
@@ -1,39 +1,195 @@
+import { describe, expect, test } from 'vitest'
+import { rolldown } from 'rolldown'
import { definePlugin } from '../../plugins/define'
import { resolveConfig } from '../../config'
+import { PartialEnvironment } from '../../baseEnvironment'
async function createDefinePluginTransform(
define: Record = {},
- build = true,
- ssr = false
+ isSsrDev = false,
) {
- const config = await resolveConfig({ define }, build ? 'build' : 'serve')
+ const ssr = isSsrDev
+ const build = !isSsrDev
+ const config = await resolveConfig(
+ { configFile: false, define, environments: { ssr: {} } },
+ build ? 'build' : 'serve',
+ )
const instance = definePlugin(config)
+ const environment = new PartialEnvironment(ssr ? 'ssr' : 'client', config)
+
return async (code: string) => {
- const result = await instance.transform.call({}, code, 'foo.ts', { ssr })
- return result?.code || result
+ if (isSsrDev) {
+ // @ts-expect-error transform.handler should exist
+ const result = await instance.transform.handler.call(
+ { environment },
+ code,
+ 'foo.ts',
+ )
+ return result?.code || result
+ } else {
+ const bundler = await rolldown({
+ input: 'entry.js',
+ plugins: [
+ {
+ name: 'test',
+ resolveId(id) {
+ if (id === 'entry.js') {
+ return '\0' + id
+ }
+ },
+ load(id) {
+ if (id === '\0entry.js') {
+ return code
+ }
+ },
+ },
+ {
+ name: 'native:define',
+ options: (definePlugin(config).options! as any).bind({
+ environment,
+ }),
+ },
+ ],
+ experimental: {
+ attachDebugInfo: 'none',
+ },
+ })
+ return (await bundler.generate()).output[0].code
+ }
}
}
-describe('definePlugin', () => {
+describe('definePlugin (SSR dev)', () => {
+ const createJsDefinePluginTransform = (define: Record = {}) =>
+ createDefinePluginTransform(define, true)
+
+ test('replaces custom define', async () => {
+ const transform = await createJsDefinePluginTransform({
+ __APP_VERSION__: JSON.stringify('1.0'),
+ })
+ expect(await transform('export const version = __APP_VERSION__ ;')).toBe(
+ 'export const version = "1.0";\n',
+ )
+ expect(await transform('export const version = __APP_VERSION__;')).toBe(
+ 'export const version = "1.0";\n',
+ )
+ })
+
+ test('should not replace if not defined', async () => {
+ const transform = await createJsDefinePluginTransform({
+ __APP_VERSION__: JSON.stringify('1.0'),
+ })
+ expect(await transform('export const version = "1.0";')).toBe(undefined)
+ expect(
+ await transform('export const version = import.meta.SOMETHING'),
+ ).toBe(undefined)
+ })
+
+ test('replace import.meta.env when it is a invalid json', async () => {
+ const transform = await createJsDefinePluginTransform({
+ 'import.meta.env.LEGACY': '__VITE_IS_LEGACY__',
+ })
+
+ expect(
+ await transform(
+ 'export const isLegacy = import.meta.env.LEGACY;\nimport.meta.env.UNDEFINED && console.log(import.meta.env.UNDEFINED);',
+ ),
+ ).toMatchInlineSnapshot(`
+ "export const isLegacy = __VITE_IS_LEGACY__;
+ import.meta.env.UNDEFINED && console.log(import.meta.env.UNDEFINED);
+ "
+ `)
+ })
+})
+
+describe('native definePlugin', () => {
test('replaces custom define', async () => {
const transform = await createDefinePluginTransform({
- __APP_VERSION__: JSON.stringify('1.0')
+ __APP_VERSION__: JSON.stringify('1.0'),
})
- expect(await transform('const version = __APP_VERSION__ ;')).toBe(
- 'const version = "1.0" ;'
+ expect(await transform('export const version = __APP_VERSION__;')).toBe(
+ 'const version = "1.0";\nexport { version };\n',
+ )
+ expect(await transform('export const version = __APP_VERSION__ ;')).toBe(
+ 'const version = "1.0";\nexport { version };\n',
)
- expect(await transform('const version = __APP_VERSION__;')).toBe(
- 'const version = "1.0";'
+ })
+
+ test('should not replace if not defined', async () => {
+ const transform = await createDefinePluginTransform({
+ __APP_VERSION__: JSON.stringify('1.0'),
+ })
+ expect(await transform('export const version = "1.0";')).toBe(
+ 'const version = "1.0";\nexport { version };\n',
)
+ expect(
+ await transform('export const version = import.meta.SOMETHING'),
+ ).toBe('const version = import.meta.SOMETHING;\nexport { version };\n')
})
test('replaces import.meta.env.SSR with false', async () => {
const transform = await createDefinePluginTransform()
- expect(await transform('const isSSR = import.meta.env.SSR ;')).toBe(
- 'const isSSR = false ;'
+ expect(await transform('export const isSSR = import.meta.env.SSR;')).toBe(
+ 'const isSSR = false;\nexport { isSSR };\n',
+ )
+ })
+
+ test('preserve import.meta.hot with override', async () => {
+ // assert that the default behavior is to replace import.meta.hot with undefined
+ const transform = await createDefinePluginTransform()
+ expect(await transform('export const hot = import.meta.hot;')).toBe(
+ 'const hot = void 0;\nexport { hot };\n',
+ )
+ // assert that we can specify a user define to preserve import.meta.hot
+ const overrideTransform = await createDefinePluginTransform({
+ 'import.meta.hot': 'import.meta.hot',
+ })
+ expect(await overrideTransform('export const hot = import.meta.hot;')).toBe(
+ 'const hot = import.meta.hot;\nexport { hot };\n',
+ )
+ })
+
+ test('replace import.meta.env.UNKNOWN with undefined', async () => {
+ const transform = await createDefinePluginTransform()
+ expect(await transform('export const foo = import.meta.env.UNKNOWN;')).toBe(
+ 'const foo = void 0;\nexport { foo };\n',
+ )
+ })
+
+ test('leave import.meta.env["UNKNOWN"] to runtime', async () => {
+ const transform = await createDefinePluginTransform()
+ expect(
+ await transform('export const foo = import.meta.env["UNKNOWN"];'),
+ ).toMatch(/const foo = .*\["UNKNOWN"\];\nexport \{ foo \};\n/s)
+ })
+
+ test('preserve import.meta.env.UNKNOWN with override', async () => {
+ const transform = await createDefinePluginTransform({
+ 'import.meta.env.UNKNOWN': 'import.meta.env.UNKNOWN',
+ })
+ expect(await transform('export const foo = import.meta.env.UNKNOWN;')).toBe(
+ 'const foo = import.meta.env.UNKNOWN;\nexport { foo };\n',
+ )
+ })
+
+ test('replace import.meta.env when it is a invalid json', async () => {
+ const transform = await createDefinePluginTransform({
+ 'import.meta.env.LEGACY': '__VITE_IS_LEGACY__',
+ })
+
+ expect(
+ await transform(
+ 'export const isLegacy = import.meta.env.LEGACY;\nimport.meta.env.UNDEFINED && console.log(import.meta.env.UNDEFINED);',
+ ),
+ ).toMatchInlineSnapshot(
+ `"const isLegacy = __VITE_IS_LEGACY__;\nexport { isLegacy };\n"`,
)
- expect(await transform('const isSSR = import.meta.env.SSR;')).toBe(
- 'const isSSR = false;'
+ })
+
+ test('replace bare import.meta.env', async () => {
+ const transform = await createDefinePluginTransform()
+ expect(await transform('export const env = import.meta.env;')).toMatch(
+ /const env = .*;\nexport \{ env \};\n/s,
)
})
})
diff --git a/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap
new file mode 100644
index 00000000000000..3d6a6910422d7c
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/__snapshots__/parse.spec.ts.snap
@@ -0,0 +1,23 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`parse positives > ? in url 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mo\\\\?ds/*.js", {"query":"?url","import":"*"})), \`./mo?ds/\${base ?? foo}.js\`)"`;
+
+exports[`parse positives > ? in variables 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?raw","import":"*"})), \`./mods/\${base ?? foo}.js\`)"`;
+
+exports[`parse positives > ? in worker 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mo\\\\?ds/*.js", {"query":"?worker","import":"*"})), \`./mo?ds/\${base ?? foo}.js\`)"`;
+
+exports[`parse positives > alias path 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js")), \`./mods/\${base}.js\`)"`;
+
+exports[`parse positives > alias path with multi ../ 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("../../*.js")), \`../../\${base}.js\`)"`;
+
+exports[`parse positives > basic 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js")), \`./mods/\${base}.js\`)"`;
+
+exports[`parse positives > with ../ and itself 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("../dynamicImportVar/*.js")), \`./\${name}.js\`)"`;
+
+exports[`parse positives > with multi ../ and itself 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("../../plugins/dynamicImportVar/*.js")), \`./\${name}.js\`)"`;
+
+exports[`parse positives > with query 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?foo=bar"})), \`./mods/\${base}.js\`)"`;
+
+exports[`parse positives > with query raw 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?raw","import":"*"})), \`./mods/\${base}.js\`)"`;
+
+exports[`parse positives > with query url 1`] = `"__variableDynamicImportRuntimeHelper((import.meta.glob("./mods/*.js", {"query":"?url","import":"*"})), \`./mods/\${base}.js\`)"`;
diff --git a/packages/vite/src/node/__tests__/plugins/dynamicImportVar/mods/hello.js b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/mods/hello.js
new file mode 100644
index 00000000000000..67900ef0999962
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/mods/hello.js
@@ -0,0 +1,3 @@
+export function hello() {
+ return 'hello'
+}
diff --git a/packages/vite/src/node/__tests__/plugins/dynamicImportVar/mods/hi.js b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/mods/hi.js
new file mode 100644
index 00000000000000..45d3506803b2b6
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/mods/hi.js
@@ -0,0 +1,3 @@
+export function hi() {
+ return 'hi'
+}
diff --git a/packages/vite/src/node/__tests__/plugins/dynamicImportVar/parse.spec.ts b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/parse.spec.ts
new file mode 100644
index 00000000000000..d9aeb9c24bea17
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/dynamicImportVar/parse.spec.ts
@@ -0,0 +1,71 @@
+import { resolve } from 'node:path'
+import { describe, expect, it } from 'vitest'
+import { transformDynamicImport } from '../../../plugins/dynamicImportVars'
+import { normalizePath } from '../../../utils'
+import { isWindows } from '../../../../shared/utils'
+
+const dirname = import.meta.dirname
+
+async function run(input: string) {
+ const { glob, rawPattern } =
+ (await transformDynamicImport(
+ input,
+ normalizePath(resolve(dirname, 'index.js')),
+ (id) =>
+ id
+ .replace('@', resolve(dirname, './mods/'))
+ .replace('#', resolve(dirname, '../../')),
+ dirname,
+ )) || {}
+ return `__variableDynamicImportRuntimeHelper(${glob}, \`${rawPattern}\`)`
+}
+
+describe('parse positives', () => {
+ it('basic', async () => {
+ expect(await run('`./mods/${base}.js`')).toMatchSnapshot()
+ })
+
+ it('alias path', async () => {
+ expect(await run('`@/${base}.js`')).toMatchSnapshot()
+ })
+
+ it('alias path with multi ../', async () => {
+ expect(await run('`#/${base}.js`')).toMatchSnapshot()
+ })
+
+ it('with query', async () => {
+ expect(await run('`./mods/${base}.js?foo=bar`')).toMatchSnapshot()
+ })
+
+ it('with query raw', async () => {
+ expect(await run('`./mods/${base}.js?raw`')).toMatchSnapshot()
+ })
+
+ it('with query url', async () => {
+ expect(await run('`./mods/${base}.js?url`')).toMatchSnapshot()
+ })
+
+ it('? in variables', async () => {
+ expect(await run('`./mods/${base ?? foo}.js?raw`')).toMatchSnapshot()
+ })
+
+ // ? is not escaped on windows (? cannot be used as a filename on windows)
+ it.skipIf(isWindows)('? in url', async () => {
+ expect(await run('`./mo?ds/${base ?? foo}.js?url`')).toMatchSnapshot()
+ })
+
+ // ? is not escaped on windows (? cannot be used as a filename on windows)
+ it.skipIf(isWindows)('? in worker', async () => {
+ expect(await run('`./mo?ds/${base ?? foo}.js?worker`')).toMatchSnapshot()
+ })
+
+ it('with ../ and itself', async () => {
+ expect(await run('`../dynamicImportVar/${name}.js`')).toMatchSnapshot()
+ })
+
+ it('with multi ../ and itself', async () => {
+ expect(
+ await run('`../../plugins/dynamicImportVar/${name}.js`'),
+ ).toMatchSnapshot()
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts b/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts
new file mode 100644
index 00000000000000..79cd8b4f3da6a0
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/esbuild.spec.ts
@@ -0,0 +1,436 @@
+import path from 'node:path'
+import { describe, expect, test } from 'vitest'
+import type { ResolvedConfig, UserConfig } from '../../config'
+import {
+ injectEsbuildHelpers,
+ resolveEsbuildTranspileOptions,
+ transformWithEsbuild,
+} from '../../plugins/esbuild'
+import { normalizePath } from '../../utils'
+
+describe('resolveEsbuildTranspileOptions', () => {
+ test('resolve default', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ target: 'es2020',
+ minify: 'esbuild',
+ },
+ esbuild: {
+ keepNames: true,
+ },
+ }),
+ 'es',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: 'es2020',
+ format: 'esm',
+ keepNames: true,
+ minify: true,
+ treeShaking: true,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+
+ test('resolve esnext no minify', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ target: 'esnext',
+ minify: false,
+ },
+ esbuild: {
+ keepNames: true,
+ },
+ }),
+ 'es',
+ )
+ expect(options).toEqual(null)
+ })
+
+ test('resolve specific minify options', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ minify: 'esbuild',
+ },
+ esbuild: {
+ keepNames: true,
+ minifyIdentifiers: false,
+ },
+ }),
+ 'es',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: undefined,
+ format: 'esm',
+ keepNames: true,
+ minify: false,
+ minifyIdentifiers: false,
+ minifySyntax: true,
+ minifyWhitespace: true,
+ treeShaking: true,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+
+ test('resolve no minify', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ target: 'es2020',
+ minify: false,
+ },
+ esbuild: {
+ keepNames: true,
+ },
+ }),
+ 'es',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: 'es2020',
+ format: 'esm',
+ keepNames: true,
+ minify: false,
+ minifyIdentifiers: false,
+ minifySyntax: false,
+ minifyWhitespace: false,
+ treeShaking: false,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+
+ test('resolve es lib', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ minify: 'esbuild',
+ lib: {
+ entry: './somewhere.js',
+ },
+ },
+ esbuild: {
+ keepNames: true,
+ },
+ }),
+ 'es',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: undefined,
+ format: 'esm',
+ keepNames: true,
+ minify: false,
+ minifyIdentifiers: true,
+ minifySyntax: true,
+ minifyWhitespace: false,
+ treeShaking: true,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+
+ test('resolve cjs lib', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ minify: 'esbuild',
+ lib: {
+ entry: './somewhere.js',
+ },
+ },
+ esbuild: {
+ keepNames: true,
+ },
+ }),
+ 'cjs',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: undefined,
+ format: 'cjs',
+ keepNames: true,
+ minify: true,
+ treeShaking: true,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+
+ test('resolve es lib with specific minify options', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ minify: 'esbuild',
+ lib: {
+ entry: './somewhere.js',
+ },
+ },
+ esbuild: {
+ keepNames: true,
+ minifyIdentifiers: true,
+ minifyWhitespace: true,
+ },
+ }),
+ 'es',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: undefined,
+ format: 'esm',
+ keepNames: true,
+ minify: false,
+ minifyIdentifiers: true,
+ minifySyntax: true,
+ minifyWhitespace: false,
+ treeShaking: true,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+
+ test('resolve cjs lib with specific minify options', () => {
+ const options = resolveEsbuildTranspileOptions(
+ defineResolvedConfig({
+ build: {
+ minify: 'esbuild',
+ lib: {
+ entry: './somewhere.js',
+ },
+ },
+ esbuild: {
+ keepNames: true,
+ minifyIdentifiers: true,
+ minifySyntax: false,
+ treeShaking: true,
+ },
+ }),
+ 'cjs',
+ )
+ expect(options).toEqual({
+ loader: 'js',
+ target: undefined,
+ format: 'cjs',
+ keepNames: true,
+ minify: false,
+ minifyIdentifiers: true,
+ minifySyntax: false,
+ minifyWhitespace: true,
+ treeShaking: true,
+ supported: {
+ 'dynamic-import': true,
+ 'import-meta': true,
+ },
+ })
+ })
+})
+
+describe('transformWithEsbuild', () => {
+ test('not throw on inline sourcemap', async () => {
+ const result = await transformWithEsbuild(`const foo = 'bar'`, '', {
+ sourcemap: 'inline',
+ })
+ expect(result?.code).toBeTruthy()
+ expect(result?.map).toBeTruthy()
+ })
+
+ test('correctly overrides TS configuration and applies automatic transform', async () => {
+ const jsxImportSource = 'bar'
+ const result = await transformWithEsbuild(
+ 'const foo = () => <>>',
+ 'baz.jsx',
+ {
+ tsconfigRaw: {
+ compilerOptions: {
+ jsx: 'preserve',
+ },
+ },
+ jsx: 'automatic',
+ jsxImportSource,
+ },
+ )
+ expect(result?.code).toContain(`${jsxImportSource}/jsx-runtime`)
+ expect(result?.code).toContain('/* @__PURE__ */')
+ })
+
+ test('correctly overrides TS configuration and preserves code', async () => {
+ const foo = 'const foo = () => <>>'
+ const result = await transformWithEsbuild(foo, 'baz.jsx', {
+ tsconfigRaw: {
+ compilerOptions: {
+ jsx: 'react-jsx',
+ },
+ },
+ jsx: 'preserve',
+ })
+ expect(result?.code).toContain(foo)
+ })
+
+ test('correctly overrides TS configuration and transforms code', async () => {
+ const jsxFactory = 'h',
+ jsxFragment = 'bar'
+ const result = await transformWithEsbuild(
+ 'const foo = () => <>>',
+ 'baz.jsx',
+ {
+ tsconfigRaw: {
+ compilerOptions: {
+ jsxFactory: 'g',
+ jsxFragmentFactory: 'foo',
+ jsxImportSource: 'baz',
+ },
+ },
+ jsx: 'transform',
+ jsxFactory,
+ jsxFragment,
+ },
+ )
+ expect(result?.code).toContain(
+ `/* @__PURE__ */ ${jsxFactory}(${jsxFragment}, null)`,
+ )
+ })
+
+ describe('useDefineForClassFields', async () => {
+ const transformClassCode = async (
+ target: string,
+ tsconfigCompilerOptions: {
+ target?: string
+ useDefineForClassFields?: boolean
+ },
+ ) => {
+ const result = await transformWithEsbuild(
+ `
+ class foo {
+ bar = 'bar'
+ }
+ `,
+ normalizePath(path.resolve(import.meta.dirname, 'bar.ts')),
+ {
+ target,
+ tsconfigRaw: { compilerOptions: tsconfigCompilerOptions },
+ },
+ )
+ return result?.code
+ }
+
+ const [
+ defineForClassFieldsTrueTransformedCode,
+ defineForClassFieldsTrueLowerTransformedCode,
+ defineForClassFieldsFalseTransformedCode,
+ ] = await Promise.all([
+ transformClassCode('esnext', {
+ useDefineForClassFields: true,
+ }),
+ transformClassCode('es2021', {
+ useDefineForClassFields: true,
+ }),
+ transformClassCode('esnext', {
+ useDefineForClassFields: false,
+ }),
+ ])
+
+ test('target: esnext and tsconfig.target: esnext => true', async () => {
+ const actual = await transformClassCode('esnext', {
+ target: 'esnext',
+ })
+ expect(actual).toBe(defineForClassFieldsTrueTransformedCode)
+ })
+
+ test('target: es2021 and tsconfig.target: esnext => true', async () => {
+ const actual = await transformClassCode('es2021', {
+ target: 'esnext',
+ })
+ expect(actual).toBe(defineForClassFieldsTrueLowerTransformedCode)
+ })
+
+ test('target: es2021 and tsconfig.target: es2021 => false', async () => {
+ const actual = await transformClassCode('es2021', {
+ target: 'es2021',
+ })
+ expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
+ })
+
+ test('target: esnext and tsconfig.target: es2021 => false', async () => {
+ const actual = await transformClassCode('esnext', {
+ target: 'es2021',
+ })
+ expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
+ })
+
+ test('target: es2022 and tsconfig.target: es2022 => true', async () => {
+ const actual = await transformClassCode('es2022', {
+ target: 'es2022',
+ })
+ expect(actual).toBe(defineForClassFieldsTrueTransformedCode)
+ })
+
+ test('target: es2022 and tsconfig.target: undefined => false', async () => {
+ const actual = await transformClassCode('es2022', {
+ target: undefined,
+ })
+ expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
+ })
+ })
+})
+
+describe('injectEsbuildHelpers', () => {
+ test('injects helpers in IIFE format', () => {
+ const esbuildCode =
+ 'var $=function(){};var MyLib=(function(){"use strict";return 42;})()'
+ const result = injectEsbuildHelpers(esbuildCode, 'iife')
+ expect(result).toBe(
+ 'var MyLib=(function(){"use strict";var $=function(){};return 42;})()',
+ )
+ })
+
+ test('injects helpers in IIFE format (pre esbuild 0.25.9)', () => {
+ const esbuildCode =
+ 'var $=function(){};var MyLib=function(){"use strict";return 42;}()'
+ const result = injectEsbuildHelpers(esbuildCode, 'iife')
+ expect(result).toBe(
+ 'var MyLib=function(){"use strict";var $=function(){};return 42;}()',
+ )
+ })
+
+ test('injects helpers in UMD format', () => {
+ const esbuildCode =
+ 'var $=function(){};(function(global){"use strict";return 42;})'
+ const result = injectEsbuildHelpers(esbuildCode, 'umd')
+ expect(result).toBe(
+ '(function(global){"use strict";var $=function(){};return 42;})',
+ )
+ })
+
+ test('handles helpers with special characters', () => {
+ const esbuildCode =
+ 'var $$=function(){};var MyLib=(function(){"use strict";return 42;})()'
+ const result = injectEsbuildHelpers(esbuildCode, 'iife')
+ expect(result).toContain('"use strict";var $$=function(){};')
+ })
+})
+
+/**
+ * Helper for `resolveEsbuildTranspileOptions` to created resolved config with types.
+ * Note: The function only uses `build.target`, `build.minify` and `esbuild` options.
+ */
+function defineResolvedConfig(config: UserConfig): ResolvedConfig {
+ return config as any
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/css-module-compose/css/bar.module.css b/packages/vite/src/node/__tests__/plugins/fixtures/css-module-compose/css/bar.module.css
new file mode 100644
index 00000000000000..fa163ddd5179cc
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/css-module-compose/css/bar.module.css
@@ -0,0 +1,4 @@
+.bar {
+ display: block;
+ background: #f0f;
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/index.js b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/index.js
new file mode 100644
index 00000000000000..b33537e2d6da91
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/index.js
@@ -0,0 +1,4 @@
+// Avoid to be inlined completely: https://github.com/rolldown/rolldown/issues/8100
+console.log()
+
+export default 'ok'
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/licence b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/licence
new file mode 100644
index 00000000000000..7837407e70efb5
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/licence
@@ -0,0 +1,3 @@
+CC0 1.0 Universal
+
+...
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/package.json b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/package.json
new file mode 100644
index 00000000000000..9e4c8b22a55c6f
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-licence-cc0/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "@vitejs/test-dep-licence-cc0",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "license": "CC0-1.0"
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/index.js b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/index.js
new file mode 100644
index 00000000000000..42bd75ecce8bd7
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/index.js
@@ -0,0 +1,3 @@
+import nestedDep from '@vitejs/test-dep-nested-license-isc'
+
+export default 'ok' + nestedDep
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/license b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/license
new file mode 100644
index 00000000000000..1732da241e5252
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/license
@@ -0,0 +1,3 @@
+MIT License
+
+Copyright (c) ...
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/package.json b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/package.json
new file mode 100644
index 00000000000000..4b4a49eb85ca76
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-license-mit/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "@vitejs/test-dep-license-mit",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "license": "MIT",
+ "dependencies": {
+ "@vitejs/test-dep-nested-license-isc": "file:../dep-nested-license-isc"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/LICENSE b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/LICENSE
new file mode 100644
index 00000000000000..40ce705e530b07
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/LICENSE
@@ -0,0 +1 @@
+Copyright (c) ...
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/index.js b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/index.js
new file mode 100644
index 00000000000000..b33537e2d6da91
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/index.js
@@ -0,0 +1,4 @@
+// Avoid to be inlined completely: https://github.com/rolldown/rolldown/issues/8100
+console.log()
+
+export default 'ok'
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/package.json b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/package.json
new file mode 100644
index 00000000000000..70b3745d3dc0ef
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/dep-nested-license-isc/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "@vitejs/test-dep-nested-license-isc",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "license": "ISC"
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/index.html b/packages/vite/src/node/__tests__/plugins/fixtures/license/index.html
new file mode 100644
index 00000000000000..b0825ecb300d5b
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/index.html
@@ -0,0 +1,5 @@
+
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/license/package.json b/packages/vite/src/node/__tests__/plugins/fixtures/license/package.json
new file mode 100644
index 00000000000000..4e06638e94f0cc
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/license/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "@vitejs/test-license",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "dependencies": {
+ "@vitejs/test-dep-license-mit": "file:./dep-license-mit",
+ "@vitejs/test-dep-licence-cc0": "file:./dep-licence-cc0"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/decorator-metadata/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/decorator-metadata/tsconfig.json
new file mode 100644
index 00000000000000..6dacb8cc2c548f
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/decorator-metadata/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true
+ }
+}
diff --git a/packages/playground/dynamic-import/mxd.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/empty/tsconfig.json
similarity index 100%
rename from packages/playground/dynamic-import/mxd.json
rename to packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/empty/tsconfig.json
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-complex-options/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-complex-options/tsconfig.json
new file mode 100644
index 00000000000000..a224293f4e48ac
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-complex-options/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "jsx": "react-jsx"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-preserve/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-preserve/tsconfig.json
new file mode 100644
index 00000000000000..186ad251537010
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-preserve/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "jsx": "preserve"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-react-jsx/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-react-jsx/tsconfig.json
new file mode 100644
index 00000000000000..a6377dd1adcf1b
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/jsx-react-jsx/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "compilerOptions": {
+ "jsxFactory": "g",
+ "jsxFragmentFactory": "foo",
+ "jsxImportSource": "baz"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-es2021/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-es2021/tsconfig.json
new file mode 100644
index 00000000000000..ad0827577c94d1
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-es2021/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "target": "es2021"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-es2022/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-es2022/tsconfig.json
new file mode 100644
index 00000000000000..f75c15e1d689bb
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-es2022/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "target": "es2022"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-esnext/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-esnext/tsconfig.json
new file mode 100644
index 00000000000000..07b9f80cb05196
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/target-esnext/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "target": "esnext"
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/use-define-false/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/use-define-false/tsconfig.json
new file mode 100644
index 00000000000000..28eb978d59d0fe
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/use-define-false/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "useDefineForClassFields": false
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/use-define-true/tsconfig.json b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/use-define-true/tsconfig.json
new file mode 100644
index 00000000000000..de4dca88946c03
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/oxc-tsconfigs/use-define-true/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "useDefineForClassFields": true
+ }
+}
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/worker-url/entry.js b/packages/vite/src/node/__tests__/plugins/fixtures/worker-url/entry.js
new file mode 100644
index 00000000000000..be5e130714116b
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/worker-url/entry.js
@@ -0,0 +1,2 @@
+import workerUrl from './worker?worker&url'
+console.log(workerUrl)
diff --git a/packages/vite/src/node/__tests__/plugins/fixtures/worker-url/worker.js b/packages/vite/src/node/__tests__/plugins/fixtures/worker-url/worker.js
new file mode 100644
index 00000000000000..28771be77b65d6
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/fixtures/worker-url/worker.js
@@ -0,0 +1,3 @@
+/* global self */
+const msg = 'hello from worker'
+self.postMessage(msg)
diff --git a/packages/vite/src/node/__tests__/plugins/hooks.spec.ts b/packages/vite/src/node/__tests__/plugins/hooks.spec.ts
new file mode 100644
index 00000000000000..0ceb0497d3bc10
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/hooks.spec.ts
@@ -0,0 +1,348 @@
+import path from 'node:path'
+import { describe, expect, onTestFinished, test } from 'vitest'
+import { build } from '../../build'
+import type { Plugin } from '../../plugin'
+import { resolveConfig } from '../../config'
+import { createServer } from '../../server'
+import { preview } from '../../preview'
+import { promiseWithResolvers } from '../../../shared/utils'
+
+const resolveConfigWithPlugin = (
+ plugin: Plugin,
+ command: 'serve' | 'build' = 'serve',
+) => {
+ return resolveConfig(
+ { configFile: false, plugins: [plugin], logLevel: 'error' },
+ command,
+ )
+}
+
+const ENTRY_ID = 'entry.js'
+const RESOLVED_ENTRY_ID = `\0${ENTRY_ID}`
+const resolveEntryPlugin: Plugin = {
+ name: 'resolve-entry.js',
+ resolveId(id) {
+ if (id === ENTRY_ID) {
+ return RESOLVED_ENTRY_ID
+ }
+ },
+ load(id) {
+ if (id === RESOLVED_ENTRY_ID) {
+ return 'export default {}'
+ }
+ },
+}
+
+const createServerWithPlugin = async (plugin: Plugin) => {
+ const server = await createServer({
+ configFile: false,
+ root: import.meta.dirname,
+ plugins: [plugin, resolveEntryPlugin],
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ ws: false,
+ },
+ })
+ onTestFinished(() => server.close())
+ return server
+}
+
+const createPreviewServerWithPlugin = async (plugin: Plugin) => {
+ const server = await preview({
+ configFile: false,
+ root: import.meta.dirname,
+ plugins: [
+ {
+ name: 'mock-preview',
+ configurePreviewServer({ httpServer }) {
+ // NOTE: make httpServer.listen no-op to avoid starting a server
+ httpServer.listen = () => {
+ const lastListener = httpServer.listeners('listening').at(-1)!
+ lastListener.call(httpServer)
+ return httpServer as any
+ }
+ },
+ },
+ plugin,
+ ],
+ logLevel: 'error',
+ })
+ onTestFinished(() => server.close())
+ return server
+}
+
+const buildWithPlugin = async (plugin: Plugin) => {
+ await build({
+ root: path.resolve(import.meta.dirname, '../packages/build-project'),
+ logLevel: 'error',
+ build: {
+ write: false,
+ },
+ plugins: [plugin, resolveEntryPlugin],
+ })
+}
+
+describe('supports plugin context', () => {
+ test('config hook', async () => {
+ expect.assertions(4)
+
+ await resolveConfigWithPlugin({
+ name: 'test',
+ config() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ // @ts-expect-error watchMode should not exist in types
+ expect(this.meta.watchMode).toBeUndefined()
+ },
+ })
+ })
+
+ test('configEnvironment hook', async () => {
+ expect.assertions(4)
+
+ await resolveConfigWithPlugin({
+ name: 'test',
+ configEnvironment(name) {
+ if (name !== 'client') return
+
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ // @ts-expect-error watchMode should not exist in types
+ expect(this.meta.watchMode).toBeUndefined()
+ },
+ })
+ })
+
+ test('configResolved hook', async () => {
+ expect.assertions(4)
+
+ await resolveConfigWithPlugin({
+ name: 'test',
+ configResolved() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(true)
+ },
+ })
+ })
+
+ test('configureServer hook', async () => {
+ expect.assertions(4)
+
+ await createServerWithPlugin({
+ name: 'test',
+ configureServer() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(true)
+ },
+ })
+ })
+
+ test('configurePreviewServer hook', async () => {
+ expect.assertions(4)
+
+ await createPreviewServerWithPlugin({
+ name: 'test',
+ configurePreviewServer() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(false)
+ },
+ })
+ })
+
+ test('transformIndexHtml hook in dev', async () => {
+ expect.assertions(4)
+
+ const server = await createServerWithPlugin({
+ name: 'test',
+ transformIndexHtml() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(true)
+ },
+ })
+ await server.transformIndexHtml('/index.html', '')
+ })
+
+ test('transformIndexHtml hook in build', async () => {
+ expect.assertions(4)
+
+ await buildWithPlugin({
+ name: 'test',
+ transformIndexHtml() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(false)
+ },
+ })
+ })
+
+ test('handleHotUpdate hook', async () => {
+ expect.assertions(4)
+
+ const { promise, resolve } = promiseWithResolvers()
+ const server = await createServerWithPlugin({
+ name: 'test',
+ handleHotUpdate() {
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(true)
+ resolve()
+ },
+ })
+ server.watcher.emit(
+ 'change',
+ path.resolve(import.meta.dirname, 'index.html'),
+ )
+
+ await promise
+ })
+
+ test('hotUpdate hook', async () => {
+ expect.assertions(4)
+
+ const { promise, resolve } = promiseWithResolvers()
+ const server = await createServerWithPlugin({
+ name: 'test',
+ hotUpdate() {
+ if (this.environment.name !== 'client') return
+
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ environment: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(true)
+ resolve()
+ },
+ })
+ server.watcher.emit(
+ 'change',
+ path.resolve(import.meta.dirname, 'index.html'),
+ )
+
+ await promise
+ })
+
+ test('transform hook in dev', async () => {
+ expect.assertions(4)
+
+ const server = await createServerWithPlugin({
+ name: 'test',
+ transform(_code, id) {
+ if (id !== RESOLVED_ENTRY_ID) return
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(true)
+ },
+ })
+ await server.transformRequest(ENTRY_ID)
+ await server.close()
+ })
+
+ test('transform hook in build', async () => {
+ expect.assertions(4)
+
+ await buildWithPlugin({
+ name: 'test',
+ transform(_code, id) {
+ if (id !== RESOLVED_ENTRY_ID) return
+ expect(this).toMatchObject({
+ debug: expect.any(Function),
+ info: expect.any(Function),
+ warn: expect.any(Function),
+ error: expect.any(Function),
+ meta: expect.any(Object),
+ })
+ expect(this.meta.rollupVersion).toBeTypeOf('string')
+ expect(this.meta.viteVersion).toBeTypeOf('string')
+ expect(this.meta.watchMode).toBe(false)
+ },
+ })
+ })
+
+ test('this.fs is supported in dev', async () => {
+ expect.hasAssertions()
+
+ const server = await createServerWithPlugin({
+ name: 'test',
+ resolveId(id) {
+ if (id !== ENTRY_ID) return
+ expect(this.fs.readFile).toBeTypeOf('function')
+ },
+ })
+ await server.transformRequest(ENTRY_ID)
+ await server.close()
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/import.spec.ts b/packages/vite/src/node/__tests__/plugins/import.spec.ts
index f0341e81b50f3c..d464b77e2482d0 100644
--- a/packages/vite/src/node/__tests__/plugins/import.spec.ts
+++ b/packages/vite/src/node/__tests__/plugins/import.spec.ts
@@ -1,127 +1,186 @@
+import { beforeEach, describe, expect, test, vi } from 'vitest'
import { transformCjsImport } from '../../plugins/importAnalysis'
-describe('transformCjsImport', () => {
- const url = './node_modules/.vite/react.js'
- const rawUrl = 'react'
+describe('runTransform', () => {
+ const config: any = {
+ command: 'serve',
+ logger: {
+ warn: vi.fn(),
+ },
+ }
+
+ function runTransformCjsImport(importExp: string, isNodeMode: boolean) {
+ const result = transformCjsImport(
+ importExp,
+ './node_modules/.vite/deps/react.js',
+ 'react',
+ 0,
+ 'modA',
+ isNodeMode,
+ config,
+ )
+ if (result === undefined) return undefined
+ const joined = result.hoistedAssignments
+ ? `${result.hoistedAssignments}; ${result.importLine}`
+ : result.importLine
+ expect(joined.split('\n').length, 'result line count').toBe(
+ importExp.split('\n').length,
+ )
+ return joined.replaceAll(';', ';\n')
+ }
+
+ beforeEach(() => {
+ config.logger.warn.mockClear()
+ })
test('import specifier', () => {
expect(
- transformCjsImport(
- 'import { useState, Component } from "react"',
- url,
- rawUrl,
- 0
- )
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const useState = __vite__cjsImport0_react["useState"]; ' +
- 'const Component = __vite__cjsImport0_react["Component"]'
- )
+ runTransformCjsImport(
+ 'import { useState, Component, "👋" as fake } from "react"',
+ false,
+ ),
+ ).toMatchInlineSnapshot(`
+ "const useState = __vite__cjsImport0_react["useState"];
+ const Component = __vite__cjsImport0_react["Component"];
+ const fake = __vite__cjsImport0_react["👋"];
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
+ expect(
+ runTransformCjsImport(
+ 'import { useState, Component, "👋" as fake } from "react"',
+ true,
+ ),
+ ).toMatchInlineSnapshot(`
+ "const useState = __vite__cjsImport0_react["useState"];
+ const Component = __vite__cjsImport0_react["Component"];
+ const fake = __vite__cjsImport0_react["👋"];
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
})
test('import default specifier', () => {
- expect(
- transformCjsImport('import React from "react"', url, rawUrl, 0)
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react'
- )
+ expect(runTransformCjsImport('import React from "react"', false))
+ .toMatchInlineSnapshot(`
+ "const React = !__vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react : __vite__cjsImport0_react.default;
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
+ expect(runTransformCjsImport('import React from "react"', true))
+ .toMatchInlineSnapshot(`
+ "const React = __vite__cjsImport0_react;
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
expect(
- transformCjsImport(
- 'import { default as React } from "react"',
- url,
- rawUrl,
- 0
- )
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react'
- )
+ runTransformCjsImport('import { default as React } from "react"', false),
+ ).toMatchInlineSnapshot(`
+ "const React = !__vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react : __vite__cjsImport0_react.default;
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
})
test('import all specifier', () => {
- expect(
- transformCjsImport('import * as react from "react"', url, rawUrl, 0)
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const react = __vite__cjsImport0_react'
- )
+ expect(runTransformCjsImport('import * as react from "react"', false))
+ .toMatchInlineSnapshot(`
+ "const react = ((m, n) => n || !m?.__esModule ? { ...typeof m === "object" && !Array.isArray(m) || typeof m === "function" ? m : {}, default: m} : m)(__vite__cjsImport0_react, 0);
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
+ expect(runTransformCjsImport('import * as react from "react"', true))
+ .toMatchInlineSnapshot(`
+ "const react = ((m, n) => n || !m?.__esModule ? { ...typeof m === "object" && !Array.isArray(m) || typeof m === "function" ? m : {}, default: m} : m)(__vite__cjsImport0_react, 1);
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
})
test('export all specifier', () => {
- expect(transformCjsImport('export * from "react"', url, rawUrl, 0)).toBe(
- undefined
+ expect(
+ runTransformCjsImport('export * from "react"', false),
+ ).toMatchInlineSnapshot(`undefined`)
+ expect(
+ runTransformCjsImport('export * from "react"', true),
+ ).toMatchInlineSnapshot(`undefined`)
+
+ expect(config.logger.warn).toBeCalledWith(
+ expect.stringContaining(`export * from "react"\` in modA`),
)
expect(
- transformCjsImport('export * as react from "react"', url, rawUrl, 0)
- ).toBe(undefined)
+ runTransformCjsImport('export * as react from "react"', false),
+ ).toMatchInlineSnapshot(`undefined`)
+
+ expect(config.logger.warn).toBeCalledTimes(2)
})
test('export name specifier', () => {
expect(
- transformCjsImport(
- 'export { useState, Component } from "react"',
- url,
- rawUrl,
- 0
- )
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const useState = __vite__cjsImport0_react["useState"]; ' +
- 'const Component = __vite__cjsImport0_react["Component"]; ' +
- 'export { useState, Component }'
- )
+ runTransformCjsImport(
+ 'export { useState, Component, "👋" } from "react"',
+ false,
+ ),
+ ).toMatchInlineSnapshot(`
+ "const __vite__cjsExportI_useState = __vite__cjsImport0_react["useState"];
+ const __vite__cjsExportI_Component = __vite__cjsImport0_react["Component"];
+ const __vite__cjsExportL_1d0452e3 = __vite__cjsImport0_react["👋"];
+ export { __vite__cjsExportI_useState as useState, __vite__cjsExportI_Component as Component, __vite__cjsExportL_1d0452e3 as "👋" };
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
+ expect(
+ runTransformCjsImport(
+ 'export { useState, Component, "👋" } from "react"',
+ true,
+ ),
+ ).toMatchInlineSnapshot(`
+ "const __vite__cjsExportI_useState = __vite__cjsImport0_react["useState"];
+ const __vite__cjsExportI_Component = __vite__cjsImport0_react["Component"];
+ const __vite__cjsExportL_1d0452e3 = __vite__cjsImport0_react["👋"];
+ export { __vite__cjsExportI_useState as useState, __vite__cjsExportI_Component as Component, __vite__cjsExportL_1d0452e3 as "👋" };
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
expect(
- transformCjsImport(
- 'export { useState as useStateAlias, Component as ComponentAlias } from "react"',
- url,
- rawUrl,
- 0
- )
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const useStateAlias = __vite__cjsImport0_react["useState"]; ' +
- 'const ComponentAlias = __vite__cjsImport0_react["Component"]; ' +
- 'export { useStateAlias, ComponentAlias }'
- )
+ runTransformCjsImport(
+ 'export { useState as useStateAlias, Component as ComponentAlias, "👋" as "👍" } from "react"',
+ false,
+ ),
+ ).toMatchInlineSnapshot(`
+ "const __vite__cjsExportI_useStateAlias = __vite__cjsImport0_react["useState"];
+ const __vite__cjsExportI_ComponentAlias = __vite__cjsImport0_react["Component"];
+ const __vite__cjsExportL_5d57d39e = __vite__cjsImport0_react["👋"];
+ export { __vite__cjsExportI_useStateAlias as useStateAlias, __vite__cjsExportI_ComponentAlias as ComponentAlias, __vite__cjsExportL_5d57d39e as "👍" };
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
})
test('export default specifier', () => {
- expect(
- transformCjsImport('export { default } from "react"', url, rawUrl, 0)
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const __vite__cjsExportDefault_0 = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react; ' +
- 'export default __vite__cjsExportDefault_0'
- )
+ expect(runTransformCjsImport('export { default } from "react"', false))
+ .toMatchInlineSnapshot(`
+ "const __vite__cjsExportDefault_0 = !__vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react : __vite__cjsImport0_react.default;
+ export default __vite__cjsExportDefault_0;
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
+ expect(runTransformCjsImport('export { default } from "react"', true))
+ .toMatchInlineSnapshot(`
+ "const __vite__cjsExportDefault_0 = __vite__cjsImport0_react;
+ export default __vite__cjsExportDefault_0;
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
expect(
- transformCjsImport(
- 'export { default as React} from "react"',
- url,
- rawUrl,
- 0
- )
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react; ' +
- 'export { React }'
- )
+ runTransformCjsImport('export { default as React} from "react"', false),
+ ).toMatchInlineSnapshot(`
+ "const __vite__cjsExportI_React = !__vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react : __vite__cjsImport0_react.default;
+ export { __vite__cjsExportI_React as React };
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
expect(
- transformCjsImport(
+ runTransformCjsImport(
'export { Component as default } from "react"',
- url,
- rawUrl,
- 0
- )
- ).toBe(
- 'import __vite__cjsImport0_react from "./node_modules/.vite/react.js"; ' +
- 'const __vite__cjsExportDefault_0 = __vite__cjsImport0_react["Component"]; ' +
- 'export default __vite__cjsExportDefault_0'
- )
+ false,
+ ),
+ ).toMatchInlineSnapshot(`
+ "const __vite__cjsExportDefault_0 = __vite__cjsImport0_react["Component"];
+ export default __vite__cjsExportDefault_0;
+ import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js""
+ `)
})
})
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.spec.ts.snap b/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.spec.ts.snap
new file mode 100644
index 00000000000000..a28e45055d25a0
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/__snapshots__/fixture.spec.ts.snap
@@ -0,0 +1,215 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`fixture > transform 1`] = `
+"import * as __vite_glob_3_0 from "./modules/a.ts";import * as __vite_glob_3_1 from "./modules/b.ts";import * as __vite_glob_3_2 from "./modules/index.ts";import * as __vite_glob_5_0 from "./modules/a.ts";import * as __vite_glob_5_1 from "./modules/b.ts";import * as __vite_glob_5_2 from "./modules/index.ts";import { name as __vite_glob_9_0 } from "./modules/a.ts";import { name as __vite_glob_9_1 } from "./modules/b.ts";import { name as __vite_glob_9_2 } from "./modules/index.ts";import { name as __vite_glob_11_0 } from "./modules/a.ts";import { name as __vite_glob_11_1 } from "./modules/b.ts";import { name as __vite_glob_11_2 } from "./modules/index.ts";import { default as __vite_glob_15_0 } from "./modules/a.ts?raw";import { default as __vite_glob_15_1 } from "./modules/b.ts?raw";import * as __vite_glob_28_0 from "./.foo/test.ts";import "../../../../../../types/importMeta";
+export const basic = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts"),"./modules/index.ts": () => import("./modules/index.ts")});
+export const basicWithObjectKeys = Object.keys({"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0});
+export const basicWithObjectValues = Object.values([() => import("./modules/a.ts"),() => import("./modules/b.ts"),() => import("./modules/index.ts")]);
+export const basicEager = /* #__PURE__ */ Object.assign({"./modules/a.ts": __vite_glob_3_0,"./modules/b.ts": __vite_glob_3_1,"./modules/index.ts": __vite_glob_3_2
+
+});
+export const basicEagerWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0
+
+}
+);
+export const basicEagerWithObjectValues = Object.values(
+ [__vite_glob_5_0,__vite_glob_5_1,__vite_glob_5_2
+
+]
+);
+export const ignore = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts")});
+export const ignoreWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0}
+);
+export const ignoreWithObjectValues = Object.values(
+ [() => import("./modules/a.ts"),() => import("./modules/b.ts")]
+);
+export const namedEager = /* #__PURE__ */ Object.assign({"./modules/a.ts": __vite_glob_9_0,"./modules/b.ts": __vite_glob_9_1,"./modules/index.ts": __vite_glob_9_2
+
+
+});
+export const namedEagerWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0
+
+
+}
+);
+export const namedEagerWithObjectValues = Object.values(
+ [__vite_glob_11_0,__vite_glob_11_1,__vite_glob_11_2
+
+
+]
+);
+export const namedDefault = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts").then(m => m["default"]),"./modules/b.ts": () => import("./modules/b.ts").then(m => m["default"]),"./modules/index.ts": () => import("./modules/index.ts").then(m => m["default"])
+
+});
+export const namedDefaultWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0
+
+}
+);
+export const namedDefaultWithObjectValues = Object.values(
+ [() => import("./modules/a.ts").then(m => m["default"]),() => import("./modules/b.ts").then(m => m["default"]),() => import("./modules/index.ts").then(m => m["default"])
+
+]
+);
+export const eagerAs = /* #__PURE__ */ Object.assign({"./modules/a.ts": __vite_glob_15_0,"./modules/b.ts": __vite_glob_15_1
+
+
+});
+export const rawImportModule = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts?raw"),"./modules/b.ts": () => import("./modules/b.ts?raw")
+
+
+});
+export const excludeSelf = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts")
+
+
+
+
+
+});
+export const excludeSelfRaw = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts?raw")});
+export const customQueryString = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts?custom")});
+export const customQueryObject = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts?foo=bar&raw=true")
+
+
+
+
+});
+export const parent = /* #__PURE__ */ Object.assign({
+
+
+});
+export const rootMixedRelative = /* #__PURE__ */ Object.assign({"/fixture-b/a.ts": () => import("../fixture-b/a.ts?url").then(m => m["default"]),"/fixture-b/b.ts": () => import("../fixture-b/b.ts?url").then(m => m["default"]),"/fixture-b/index.ts": () => import("../fixture-b/index.ts?url").then(m => m["default"]),"/fixture.spec.ts": () => import("../fixture.spec.ts?url").then(m => m["default"]),"/parse.spec.ts": () => import("../parse.spec.ts?url").then(m => m["default"]),"/utils.spec.ts": () => import("../utils.spec.ts?url").then(m => m["default"])
+
+
+});
+export const cleverCwd1 = /* #__PURE__ */ Object.assign({"./node_modules/framework/pages/hello.page.js": () => import("./node_modules/framework/pages/hello.page.js")
+
+});
+export const cleverCwd2 = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts"),"../fixture-b/a.ts": () => import("../fixture-b/a.ts"),"../fixture-b/b.ts": () => import("../fixture-b/b.ts")
+
+
+
+});
+export const customBase = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts"),"./modules/index.ts": () => import("./modules/index.ts"),"./sibling.ts": () => import("./sibling.ts")});
+export const customRootBase = /* #__PURE__ */ Object.assign({"./a.ts": () => import("/fixture-b/a.ts"),"./b.ts": () => import("/fixture-b/b.ts"),"./index.ts": () => import("/fixture-b/index.ts")
+
+});
+export const customBaseParent = /* #__PURE__ */ Object.assign({"../fixture-b/a.ts": () => import("/fixture-b/a.ts"),"../fixture-b/b.ts": () => import("/fixture-b/b.ts"),"../fixture-b/index.ts": () => import("/fixture-b/index.ts")
+
+});
+export const dotFolder = /* #__PURE__ */ Object.assign({"./.foo/test.ts": __vite_glob_28_0});
+"
+`;
+
+exports[`fixture > transform with restoreQueryExtension 1`] = `
+"import * as __vite_glob_3_0 from "./modules/a.ts";import * as __vite_glob_3_1 from "./modules/b.ts";import * as __vite_glob_3_2 from "./modules/index.ts";import * as __vite_glob_5_0 from "./modules/a.ts";import * as __vite_glob_5_1 from "./modules/b.ts";import * as __vite_glob_5_2 from "./modules/index.ts";import { name as __vite_glob_9_0 } from "./modules/a.ts";import { name as __vite_glob_9_1 } from "./modules/b.ts";import { name as __vite_glob_9_2 } from "./modules/index.ts";import { name as __vite_glob_11_0 } from "./modules/a.ts";import { name as __vite_glob_11_1 } from "./modules/b.ts";import { name as __vite_glob_11_2 } from "./modules/index.ts";import { default as __vite_glob_15_0 } from "./modules/a.ts?raw";import { default as __vite_glob_15_1 } from "./modules/b.ts?raw";import * as __vite_glob_28_0 from "./.foo/test.ts";import "../../../../../../types/importMeta";
+export const basic = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts"),"./modules/index.ts": () => import("./modules/index.ts")});
+export const basicWithObjectKeys = Object.keys({"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0});
+export const basicWithObjectValues = Object.values([() => import("./modules/a.ts"),() => import("./modules/b.ts"),() => import("./modules/index.ts")]);
+export const basicEager = /* #__PURE__ */ Object.assign({"./modules/a.ts": __vite_glob_3_0,"./modules/b.ts": __vite_glob_3_1,"./modules/index.ts": __vite_glob_3_2
+
+});
+export const basicEagerWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0
+
+}
+);
+export const basicEagerWithObjectValues = Object.values(
+ [__vite_glob_5_0,__vite_glob_5_1,__vite_glob_5_2
+
+]
+);
+export const ignore = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts")});
+export const ignoreWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0}
+);
+export const ignoreWithObjectValues = Object.values(
+ [() => import("./modules/a.ts"),() => import("./modules/b.ts")]
+);
+export const namedEager = /* #__PURE__ */ Object.assign({"./modules/a.ts": __vite_glob_9_0,"./modules/b.ts": __vite_glob_9_1,"./modules/index.ts": __vite_glob_9_2
+
+
+});
+export const namedEagerWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0
+
+
+}
+);
+export const namedEagerWithObjectValues = Object.values(
+ [__vite_glob_11_0,__vite_glob_11_1,__vite_glob_11_2
+
+
+]
+);
+export const namedDefault = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts").then(m => m["default"]),"./modules/b.ts": () => import("./modules/b.ts").then(m => m["default"]),"./modules/index.ts": () => import("./modules/index.ts").then(m => m["default"])
+
+});
+export const namedDefaultWithObjectKeys = Object.keys(
+ {"./modules/a.ts": 0,"./modules/b.ts": 0,"./modules/index.ts": 0
+
+}
+);
+export const namedDefaultWithObjectValues = Object.values(
+ [() => import("./modules/a.ts").then(m => m["default"]),() => import("./modules/b.ts").then(m => m["default"]),() => import("./modules/index.ts").then(m => m["default"])
+
+]
+);
+export const eagerAs = /* #__PURE__ */ Object.assign({"./modules/a.ts": __vite_glob_15_0,"./modules/b.ts": __vite_glob_15_1
+
+
+});
+export const rawImportModule = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts?raw"),"./modules/b.ts": () => import("./modules/b.ts?raw")
+
+
+});
+export const excludeSelf = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts")
+
+
+
+
+
+});
+export const excludeSelfRaw = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts?raw")});
+export const customQueryString = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts?custom&lang.ts")});
+export const customQueryObject = /* #__PURE__ */ Object.assign({"./sibling.ts": () => import("./sibling.ts?foo=bar&raw=true&lang.ts")
+
+
+
+
+});
+export const parent = /* #__PURE__ */ Object.assign({
+
+
+});
+export const rootMixedRelative = /* #__PURE__ */ Object.assign({"/fixture-b/a.ts": () => import("../fixture-b/a.ts?url&lang.ts").then(m => m["default"]),"/fixture-b/b.ts": () => import("../fixture-b/b.ts?url&lang.ts").then(m => m["default"]),"/fixture-b/index.ts": () => import("../fixture-b/index.ts?url&lang.ts").then(m => m["default"]),"/fixture.spec.ts": () => import("../fixture.spec.ts?url&lang.ts").then(m => m["default"]),"/parse.spec.ts": () => import("../parse.spec.ts?url&lang.ts").then(m => m["default"]),"/utils.spec.ts": () => import("../utils.spec.ts?url&lang.ts").then(m => m["default"])
+
+
+});
+export const cleverCwd1 = /* #__PURE__ */ Object.assign({"./node_modules/framework/pages/hello.page.js": () => import("./node_modules/framework/pages/hello.page.js")
+
+});
+export const cleverCwd2 = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts"),"../fixture-b/a.ts": () => import("../fixture-b/a.ts"),"../fixture-b/b.ts": () => import("../fixture-b/b.ts")
+
+
+
+});
+export const customBase = /* #__PURE__ */ Object.assign({"./modules/a.ts": () => import("./modules/a.ts"),"./modules/b.ts": () => import("./modules/b.ts"),"./modules/index.ts": () => import("./modules/index.ts"),"./sibling.ts": () => import("./sibling.ts")});
+export const customRootBase = /* #__PURE__ */ Object.assign({"./a.ts": () => import("/fixture-b/a.ts"),"./b.ts": () => import("/fixture-b/b.ts"),"./index.ts": () => import("/fixture-b/index.ts")
+
+});
+export const customBaseParent = /* #__PURE__ */ Object.assign({"../fixture-b/a.ts": () => import("/fixture-b/a.ts"),"../fixture-b/b.ts": () => import("/fixture-b/b.ts"),"../fixture-b/index.ts": () => import("/fixture-b/index.ts")
+
+});
+export const dotFolder = /* #__PURE__ */ Object.assign({"./.foo/test.ts": __vite_glob_28_0});
+"
+`;
+
+exports[`fixture > virtual modules 1`] = `
+"/* #__PURE__ */ Object.assign({"/modules/a.ts": () => import("/modules/a.ts"),"/modules/b.ts": () => import("/modules/b.ts"),"/modules/index.ts": () => import("/modules/index.ts")})
+/* #__PURE__ */ Object.assign({"/../fixture-b/a.ts": () => import("/../fixture-b/a.ts"),"/../fixture-b/b.ts": () => import("/../fixture-b/b.ts"),"/../fixture-b/index.ts": () => import("/../fixture-b/index.ts")})
+/* #__PURE__ */ Object.assign({"./a.ts": () => import("/modules/a.ts"),"./b.ts": () => import("/modules/b.ts"),"./index.ts": () => import("/modules/index.ts")})"
+`;
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/.foo/test.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/.foo/test.ts
new file mode 100644
index 00000000000000..63239baf13bdde
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/.foo/test.ts
@@ -0,0 +1 @@
+export const msg = 'dot-folder-test'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/.gitignore b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/.gitignore
new file mode 100644
index 00000000000000..2b9b8877da603f
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/.gitignore
@@ -0,0 +1 @@
+!/node_modules/
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/index.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/index.ts
new file mode 100644
index 00000000000000..6b3fdbaeefb23b
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/index.ts
@@ -0,0 +1,126 @@
+// NOTE: `#types/importMeta` does not work as `src/node/__tests__/package.json` shadows the root
+// `package.json` and does not declare the subpath import.
+import '../../../../../../types/importMeta'
+
+export interface ModuleType {
+ name: string
+}
+
+export const basic = import.meta.glob('./modules/*.ts')
+// prettier-ignore
+export const basicWithObjectKeys = Object.keys(import.meta.glob('./modules/*.ts'))
+// prettier-ignore
+export const basicWithObjectValues = Object.values(import.meta.glob('./modules/*.ts'))
+
+export const basicEager = import.meta.glob('./modules/*.ts', {
+ eager: true,
+})
+export const basicEagerWithObjectKeys = Object.keys(
+ import.meta.glob('./modules/*.ts', {
+ eager: true,
+ }),
+)
+export const basicEagerWithObjectValues = Object.values(
+ import.meta.glob('./modules/*.ts', {
+ eager: true,
+ }),
+)
+
+export const ignore = import.meta.glob(['./modules/*.ts', '!**/index.ts'])
+export const ignoreWithObjectKeys = Object.keys(
+ import.meta.glob(['./modules/*.ts', '!**/index.ts']),
+)
+export const ignoreWithObjectValues = Object.values(
+ import.meta.glob(['./modules/*.ts', '!**/index.ts']),
+)
+
+export const namedEager = import.meta.glob('./modules/*.ts', {
+ eager: true,
+ import: 'name',
+})
+export const namedEagerWithObjectKeys = Object.keys(
+ import.meta.glob('./modules/*.ts', {
+ eager: true,
+ import: 'name',
+ }),
+)
+export const namedEagerWithObjectValues = Object.values(
+ import.meta.glob('./modules/*.ts', {
+ eager: true,
+ import: 'name',
+ }),
+)
+
+export const namedDefault = import.meta.glob('./modules/*.ts', {
+ import: 'default',
+})
+export const namedDefaultWithObjectKeys = Object.keys(
+ import.meta.glob('./modules/*.ts', {
+ import: 'default',
+ }),
+)
+export const namedDefaultWithObjectValues = Object.values(
+ import.meta.glob('./modules/*.ts', {
+ import: 'default',
+ }),
+)
+
+export const eagerAs = import.meta.glob(
+ ['./modules/*.ts', '!**/index.ts'],
+ { eager: true, query: '?raw', import: 'default' },
+)
+
+export const rawImportModule = import.meta.glob(
+ ['./modules/*.ts', '!**/index.ts'],
+ { query: '?raw', import: '*' },
+)
+
+export const excludeSelf = import.meta.glob(
+ './*.ts',
+ // for test: annotation contain ")"
+ /*
+ * for test: annotation contain ")"
+ * */
+)
+export const excludeSelfRaw = import.meta.glob('./*.ts', { query: '?raw' })
+
+export const customQueryString = import.meta.glob('./*.ts', { query: 'custom' })
+
+export const customQueryObject = import.meta.glob('./*.ts', {
+ query: {
+ foo: 'bar',
+ raw: true,
+ },
+})
+
+export const parent = import.meta.glob('../../playground/src/*.ts', {
+ query: '?url',
+ import: 'default',
+})
+
+export const rootMixedRelative = import.meta.glob(
+ ['/*.ts', '../fixture-b/*.ts'],
+ { query: '?url', import: 'default' },
+)
+
+export const cleverCwd1 = import.meta.glob(
+ './node_modules/framework/**/*.page.js',
+)
+
+export const cleverCwd2 = import.meta.glob([
+ './modules/*.ts',
+ '../fixture-b/*.ts',
+ '!**/index.ts',
+])
+
+export const customBase = import.meta.glob('./**/*.ts', { base: './' })
+
+export const customRootBase = import.meta.glob('./**/*.ts', {
+ base: '/fixture-b',
+})
+
+export const customBaseParent = import.meta.glob('/fixture-b/**/*.ts', {
+ base: '/fixture-a',
+})
+
+export const dotFolder = import.meta.glob('./.foo/*.ts', { eager: true })
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/a.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/a.ts
new file mode 100644
index 00000000000000..facd48a0875e65
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/a.ts
@@ -0,0 +1 @@
+export const name = 'a'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/b.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/b.ts
new file mode 100644
index 00000000000000..0b1eb38d9087a2
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/b.ts
@@ -0,0 +1 @@
+export const name = 'b'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/index.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/index.ts
new file mode 100644
index 00000000000000..25b59ae7d30714
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/modules/index.ts
@@ -0,0 +1,6 @@
+export { name as a } from './a'
+export { name as b } from './b'
+
+export const name = 'index'
+
+export default 'indexDefault'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/node_modules/framework/pages/hello.page.js b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/node_modules/framework/pages/hello.page.js
new file mode 100644
index 00000000000000..cbe518a8e79477
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/node_modules/framework/pages/hello.page.js
@@ -0,0 +1,4 @@
+// A fake Page file. (This technique of globbing into `node_modules/`
+// is used by vite-plugin-ssr frameworks and Hydrogen.)
+
+export const a = 1
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/sibling.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/sibling.ts
new file mode 100644
index 00000000000000..b286816bf5d63a
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-a/sibling.ts
@@ -0,0 +1 @@
+export const name = 'I am your sibling!'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/a.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/a.ts
new file mode 100644
index 00000000000000..facd48a0875e65
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/a.ts
@@ -0,0 +1 @@
+export const name = 'a'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/b.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/b.ts
new file mode 100644
index 00000000000000..0b1eb38d9087a2
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/b.ts
@@ -0,0 +1 @@
+export const name = 'b'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/index.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/index.ts
new file mode 100644
index 00000000000000..39bdbfd1a8befb
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture-b/index.ts
@@ -0,0 +1,2 @@
+export { name as a } from './a'
+export { name as b } from './b'
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/fixture.spec.ts b/packages/vite/src/node/__tests__/plugins/importGlob/fixture.spec.ts
new file mode 100644
index 00000000000000..74736f7dc640ac
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/fixture.spec.ts
@@ -0,0 +1,80 @@
+import { resolve } from 'node:path'
+import { promises as fs } from 'node:fs'
+import { describe, expect, it } from 'vitest'
+import { transformGlobImport } from '../../../plugins/importMetaGlob'
+import { transformWithEsbuild } from '../../../plugins/esbuild'
+
+describe('fixture', async () => {
+ const resolveId = (id: string) => id
+ const root = import.meta.dirname
+
+ it('transform', async () => {
+ const id = resolve(import.meta.dirname, './fixture-a/index.ts')
+ const code = (
+ await transformWithEsbuild(await fs.readFile(id, 'utf-8'), id)
+ ).code
+
+ expect(
+ (await transformGlobImport(code, id, root, resolveId))?.s.toString(),
+ ).toMatchSnapshot()
+ })
+
+ it('preserve line count', async () => {
+ const getTransformedLineCount = async (code: string) =>
+ (await transformGlobImport(code, 'virtual:module', root, resolveId))?.s
+ .toString()
+ .split('\n').length
+
+ expect(await getTransformedLineCount("import.meta.glob('./*.js')")).toBe(1)
+ expect(
+ await getTransformedLineCount(
+ `
+ import.meta.glob(
+ './*.js'
+ )
+ `.trim(),
+ ),
+ ).toBe(3)
+ })
+
+ it('virtual modules', async () => {
+ const root = resolve(import.meta.dirname, './fixture-a')
+ const code = [
+ "import.meta.glob('/modules/*.ts')",
+ "import.meta.glob(['/../fixture-b/*.ts'])",
+ "import.meta.glob(['./*.ts'], { base: '/modules' })",
+ ].join('\n')
+ expect(
+ (
+ await transformGlobImport(code, 'virtual:module', root, resolveId)
+ )?.s.toString(),
+ ).toMatchSnapshot()
+
+ try {
+ await transformGlobImport(
+ "import.meta.glob('./modules/*.ts')",
+ 'virtual:module',
+ root,
+ resolveId,
+ )
+ expect('no error').toBe('should throw an error')
+ } catch (err) {
+ expect(err).toMatchInlineSnapshot(
+ "[Error: In virtual modules, all globs must start with '/']",
+ )
+ }
+ })
+
+ it('transform with restoreQueryExtension', async () => {
+ const id = resolve(import.meta.dirname, './fixture-a/index.ts')
+ const code = (
+ await transformWithEsbuild(await fs.readFile(id, 'utf-8'), id)
+ ).code
+
+ expect(
+ (
+ await transformGlobImport(code, id, root, resolveId, true)
+ )?.s.toString(),
+ ).toMatchSnapshot()
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/parse.spec.ts b/packages/vite/src/node/__tests__/plugins/importGlob/parse.spec.ts
new file mode 100644
index 00000000000000..3be198b1282977
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/parse.spec.ts
@@ -0,0 +1,417 @@
+import { describe, expect, it } from 'vitest'
+import { parseImportGlob } from '../../../plugins/importMetaGlob'
+
+async function run(input: string) {
+ const items = await parseImportGlob(
+ input,
+ process.cwd(),
+ process.cwd(),
+ (id) => id,
+ )
+ return items.map((i) => ({
+ globs: i.globs,
+ options: i.options,
+ start: i.start,
+ }))
+}
+
+async function runError(input: string) {
+ try {
+ await run(input)
+ } catch (e) {
+ return e
+ }
+}
+
+describe('parse positives', async () => {
+ it('basic', async () => {
+ expect(
+ await run(`
+ import.meta.glob('./modules/*.ts')
+ `),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "./modules/*.ts",
+ ],
+ "options": {},
+ "start": 5,
+ },
+ ]
+ `)
+ })
+
+ it('array', async () => {
+ expect(
+ await run(`
+ import.meta.glob(['./modules/*.ts', './dir/*.{js,ts}'])
+ `),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "./modules/*.ts",
+ "./dir/*.{js,ts}",
+ ],
+ "options": {},
+ "start": 5,
+ },
+ ]
+ `)
+ })
+
+ it('options with multilines', async () => {
+ expect(
+ await run(`
+ import.meta.glob([
+ './modules/*.ts',
+ "!./dir/*.{js,ts}"
+ ], {
+ eager: true,
+ import: 'named'
+ })
+ `),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "./modules/*.ts",
+ "!./dir/*.{js,ts}",
+ ],
+ "options": {
+ "eager": true,
+ "import": "named",
+ },
+ "start": 5,
+ },
+ ]
+ `)
+ })
+
+ it('options with multilines', async () => {
+ expect(
+ await run(`
+ const modules = import.meta.glob(
+ '/dir/**'
+ // for test: annotation contain ")"
+ /*
+ * for test: annotation contain ")"
+ * */
+ )
+ `),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/dir/**",
+ ],
+ "options": {},
+ "start": 21,
+ },
+ ]
+ `)
+ })
+
+ it('options query', async () => {
+ expect(
+ await run(`
+ const modules = import.meta.glob(
+ '/dir/**',
+ {
+ query: {
+ foo: 'bar',
+ raw: true,
+ }
+ }
+ )
+ `),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/dir/**",
+ ],
+ "options": {
+ "query": "?foo=bar&raw=true",
+ },
+ "start": 21,
+ },
+ ]
+ `)
+ })
+
+ it('options with base', async () => {
+ expect(
+ await run(`
+ import.meta.glob('./**/dir/*.md', {
+ base: './path/to/base'
+ })
+ `),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "./**/dir/*.md",
+ ],
+ "options": {
+ "base": "./path/to/base",
+ },
+ "start": 5,
+ },
+ ]
+ `)
+ })
+
+ it('object properties - 1', async () => {
+ expect(
+ await run(`
+ export const pageFiles = {
+ '.page': import.meta.glob('/**/*.page.*([a-zA-Z0-9])')
+};`),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/**/*.page.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 47,
+ },
+ ]
+`)
+ })
+
+ it('object properties - 2', async () => {
+ expect(
+ await run(`
+ export const pageFiles = {
+ '.page': import.meta.glob('/**/*.page.*([a-zA-Z0-9])'),
+};`),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/**/*.page.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 47,
+ },
+ ]
+`)
+ })
+
+ it('object properties - 3', async () => {
+ expect(
+ await run(`
+ export const pageFiles = {
+ '.page.client': import.meta.glob('/**/*.page.client.*([a-zA-Z0-9])'),
+ '.page.server': import.meta.glob('/**/*.page.server.*([a-zA-Z0-9])'),
+};`),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/**/*.page.client.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 54,
+ },
+ {
+ "globs": [
+ "/**/*.page.server.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 130,
+ },
+ ]
+`)
+ })
+
+ it('array item', async () => {
+ expect(
+ await run(`
+ export const pageFiles = [
+ import.meta.glob('/**/*.page.client.*([a-zA-Z0-9])'),
+ import.meta.glob('/**/*.page.server.*([a-zA-Z0-9])'),
+ ]`),
+ ).toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/**/*.page.client.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 38,
+ },
+ {
+ "globs": [
+ "/**/*.page.server.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 98,
+ },
+ ]
+ `)
+ })
+})
+
+describe('parse negatives', async () => {
+ it('syntax error', async () => {
+ expect(await runError('import.meta.glob(')).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Close parenthesis not found]',
+ )
+ })
+
+ it('empty', async () => {
+ expect(await runError('import.meta.glob()')).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Expected 1-2 arguments, but got 0]',
+ )
+ })
+
+ it('3 args', async () => {
+ expect(
+ await runError('import.meta.glob("", {}, {})'),
+ ).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Expected 1-2 arguments, but got 3]',
+ )
+ })
+
+ it('in string', async () => {
+ expect(await runError('"import.meta.glob()"')).toBeUndefined()
+ })
+
+ it('variable', async () => {
+ expect(await runError('import.meta.glob(hey)')).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Could only use literals]',
+ )
+ })
+
+ it('template', async () => {
+ expect(
+ await runError('import.meta.glob(`hi ${hey}`)'),
+ ).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Expected glob to be a string, but got dynamic template literal]',
+ )
+ })
+
+ it('template with unicode', async () => {
+ expect(await run('import.meta.glob(`/\u0068\u0065\u006c\u006c\u006f`)'))
+ .toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/hello",
+ ],
+ "options": {},
+ "start": 0,
+ },
+ ]
+ `)
+ })
+
+ it('template without expressions', async () => {
+ expect(await run('import.meta.glob(`/**/*.page.client.*([a-zA-Z0-9])`)'))
+ .toMatchInlineSnapshot(`
+ [
+ {
+ "globs": [
+ "/**/*.page.client.*([a-zA-Z0-9])",
+ ],
+ "options": {},
+ "start": 0,
+ },
+ ]
+ `)
+ })
+
+ it('be string', async () => {
+ expect(await runError('import.meta.glob(1)')).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Expected glob to be a string, but got "number"]',
+ )
+ })
+
+ it('be array variable', async () => {
+ expect(await runError('import.meta.glob([hey])')).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Could only use literals]',
+ )
+ expect(
+ await runError('import.meta.glob(["1", hey])'),
+ ).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Could only use literals]',
+ )
+ })
+
+ it('options', async () => {
+ expect(
+ await runError('import.meta.glob("hey", hey)'),
+ ).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Expected the second argument to be an object literal, but got "Identifier"]',
+ )
+ expect(await runError('import.meta.glob("hey", [])')).toMatchInlineSnapshot(
+ '[Error: Invalid glob import syntax: Expected the second argument to be an object literal, but got "ArrayExpression"]',
+ )
+ })
+
+ it('options props', async () => {
+ expect(
+ await runError('import.meta.glob("hey", { hey: 1 })'),
+ ).toMatchInlineSnapshot('[Error: Unknown glob option "hey"]')
+ expect(
+ await runError('import.meta.glob("hey", { import: hey })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Vite is unable to parse the glob options as the value is not static]',
+ )
+ expect(
+ await runError('import.meta.glob("hey", { eager: 123 })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Expected glob option "eager" to be of type boolean, but got number]',
+ )
+ })
+
+ it('options query', async () => {
+ expect(
+ await runError('import.meta.glob("./*.js", { as: "raw", query: "hi" })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Options "as" and "query" cannot be used together]',
+ )
+ expect(
+ await runError('import.meta.glob("./*.js", { query: 123 })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Expected glob option "query" to be of type object or string, but got number]',
+ )
+ expect(
+ await runError('import.meta.glob("./*.js", { query: { foo: {} } })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Expected glob option "query.foo" to be of type string, number, or boolean, but got object]',
+ )
+ expect(
+ await runError('import.meta.glob("./*.js", { query: { foo: hey } })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Vite is unable to parse the glob options as the value is not static]',
+ )
+ expect(
+ await runError(
+ 'import.meta.glob("./*.js", { query: { foo: 123, ...a } })',
+ ),
+ ).toMatchInlineSnapshot(
+ '[Error: Vite is unable to parse the glob options as the value is not static]',
+ )
+ })
+
+ it('options base', async () => {
+ expect(
+ await runError('import.meta.glob("./*.js", { base: 1 })'),
+ ).toMatchInlineSnapshot(
+ '[Error: Expected glob option "base" to be of type string, but got number]',
+ )
+ expect(
+ await runError('import.meta.glob("./*.js", { base: "foo" })'),
+ ).toMatchInlineSnapshot(
+ "[Error: Option \"base\" must start with '/', './' or '../', but got \"foo\"]",
+ )
+ expect(
+ await runError('import.meta.glob("./*.js", { base: "!/foo" })'),
+ ).toMatchInlineSnapshot('[Error: Option "base" cannot start with "!"]')
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/importGlob/utils.spec.ts b/packages/vite/src/node/__tests__/plugins/importGlob/utils.spec.ts
new file mode 100644
index 00000000000000..bd91c6165f798e
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/importGlob/utils.spec.ts
@@ -0,0 +1,31 @@
+import { describe, expect, it } from 'vitest'
+import { getCommonBase } from '../../../plugins/importMetaGlob'
+
+describe('getCommonBase()', async () => {
+ it('basic', () => {
+ expect(getCommonBase(['/a/b/*.js', '/a/c/*.js'])).toBe('/a')
+ })
+ it('common base', () => {
+ expect(getCommonBase(['/a/b/**/*.vue', '/a/b/**/*.jsx'])).toBe('/a/b')
+ })
+ it('static file', () => {
+ expect(
+ getCommonBase(['/a/b/**/*.vue', '/a/b/**/*.jsx', '/a/b/foo.js']),
+ ).toBe('/a/b')
+ expect(getCommonBase(['/a/b/**/*.vue', '/a/b/**/*.jsx', '/a/foo.js'])).toBe(
+ '/a',
+ )
+ })
+ it('correct `scan()`', () => {
+ expect(getCommonBase(['/a/*.vue'])).toBe('/a')
+ expect(getCommonBase(['/a/some.vue'])).toBe('/a')
+ expect(getCommonBase(['/a/b/**/c/foo.vue', '/a/b/c/**/*.jsx'])).toBe('/a/b')
+ })
+ it('single', () => {
+ expect(getCommonBase(['/a/b/c/*.vue'])).toBe('/a/b/c')
+ expect(getCommonBase(['/a/b/c/foo.vue'])).toBe('/a/b/c')
+ })
+ it('no common base', () => {
+ expect(getCommonBase(['/a/b/*.js', '/c/a/b/*.js'])).toBe('/')
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/index.spec.ts b/packages/vite/src/node/__tests__/plugins/index.spec.ts
new file mode 100644
index 00000000000000..e507a173defd1a
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/index.spec.ts
@@ -0,0 +1,169 @@
+import { RUNTIME_MODULE_ID } from 'rolldown'
+import { exactRegex } from 'rolldown/filter'
+import { afterAll, describe, expect, test, vi } from 'vitest'
+import { type InlineConfig, type Plugin, build, createServer } from '../..'
+
+const getConfigWithPlugin = (
+ plugins: Plugin[],
+ input?: string[],
+): InlineConfig => {
+ return {
+ configFile: false,
+ server: { middlewareMode: true, ws: false },
+ optimizeDeps: { noDiscovery: true, include: [] },
+ build: { rollupOptions: { input }, write: false },
+ plugins,
+ logLevel: 'silent',
+ }
+}
+
+describe('hook filter with plugin container', async () => {
+ const resolveId = vi.fn()
+ const load = vi.fn()
+ const transformWithId = vi.fn()
+ const transformWithCode = vi.fn()
+ const any = expect.toSatisfy(() => true) // anything including undefined and null
+ const config = getConfigWithPlugin([
+ {
+ name: 'test',
+ resolveId: {
+ filter: { id: /\.js$/ },
+ handler: resolveId,
+ },
+ load: {
+ filter: { id: '**/*.js' },
+ handler: load,
+ },
+ transform: {
+ filter: { id: '**/*.js' },
+ handler: transformWithId,
+ },
+ },
+ {
+ name: 'test2',
+ transform: {
+ filter: { code: 'import.meta' },
+ handler: transformWithCode,
+ },
+ },
+ ])
+ const server = await createServer(config)
+ afterAll(async () => {
+ await server.close()
+ })
+ const pluginContainer = server.environments.ssr.pluginContainer
+
+ test('resolveId', async () => {
+ await pluginContainer.resolveId('foo.js')
+ await pluginContainer.resolveId('foo.ts')
+ expect(resolveId).toHaveBeenCalledTimes(1)
+ expect(resolveId).toHaveBeenCalledWith('foo.js', any, any)
+ })
+
+ test('load', async () => {
+ await pluginContainer.load('foo.js')
+ await pluginContainer.load('foo.ts')
+ expect(load).toHaveBeenCalledTimes(1)
+ expect(load).toHaveBeenCalledWith('foo.js', any)
+ })
+
+ test('transform', async () => {
+ await server.environments.ssr.moduleGraph.ensureEntryFromUrl('foo.js')
+ await server.environments.ssr.moduleGraph.ensureEntryFromUrl('foo.ts')
+
+ await pluginContainer.transform('import_meta', 'foo.js')
+ await pluginContainer.transform('import.meta', 'foo.ts')
+ expect(transformWithId).toHaveBeenCalledTimes(1)
+ expect(transformWithId).toHaveBeenCalledWith(
+ expect.stringContaining('import_meta'),
+ 'foo.js',
+ any,
+ )
+ expect(transformWithCode).toHaveBeenCalledTimes(1)
+ expect(transformWithCode).toHaveBeenCalledWith(
+ expect.stringContaining('import.meta'),
+ 'foo.ts',
+ any,
+ )
+ })
+})
+
+describe('hook filter with build', async () => {
+ const resolveId = vi.fn()
+ const load = vi.fn()
+ const transformWithId = vi.fn()
+ const transformWithCode = vi.fn()
+ const any = expect.anything()
+ const config = getConfigWithPlugin(
+ [
+ {
+ name: 'test',
+ resolveId: {
+ filter: { id: /\.js$/ },
+ handler: resolveId,
+ },
+ load: {
+ filter: { id: '**/*.js' },
+ handler: load,
+ },
+ transform: {
+ filter: {
+ id: {
+ include: '**/*.js',
+ exclude: exactRegex(RUNTIME_MODULE_ID),
+ },
+ },
+ handler: transformWithId,
+ },
+ },
+ {
+ name: 'test2',
+ transform: {
+ filter: { code: 'import.meta' },
+ handler: transformWithCode,
+ },
+ },
+ {
+ name: 'resolver',
+ resolveId(id) {
+ return id
+ },
+ load(id) {
+ if (id === 'foo.js') {
+ return 'import "foo.ts"\n' + 'import_meta'
+ }
+ if (id === 'foo.ts') {
+ return 'import.meta'
+ }
+ },
+ },
+ ],
+ ['foo.js', 'foo.ts'],
+ )
+ await build(config)
+
+ test('resolveId', async () => {
+ expect(resolveId).toHaveBeenCalledTimes(1)
+ expect(resolveId).toHaveBeenCalledWith('foo.js', undefined, any)
+ })
+
+ test('load', async () => {
+ expect(load).toHaveBeenCalledTimes(1)
+ expect(load).toHaveBeenCalledWith('foo.js', any)
+ })
+
+ test('transform', async () => {
+ expect(transformWithId).toHaveBeenCalledTimes(1)
+ expect(transformWithId).toHaveBeenCalledWith(
+ expect.stringContaining('import_meta'),
+ 'foo.js',
+ any,
+ )
+ expect(transformWithCode).toHaveBeenCalledTimes(1)
+ expect(transformWithCode).toHaveBeenCalledWith(
+ expect.stringContaining('import.meta'),
+ 'foo.ts',
+ any,
+ )
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/license.spec.ts b/packages/vite/src/node/__tests__/plugins/license.spec.ts
new file mode 100644
index 00000000000000..67876e7b900bbe
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/license.spec.ts
@@ -0,0 +1,38 @@
+import { fileURLToPath } from 'node:url'
+import type { OutputAsset, RollupOutput } from 'rollup'
+import { expect, test } from 'vitest'
+import { build } from '../../build'
+
+test('markdown', async () => {
+ const result = (await build({
+ root: fileURLToPath(new URL('./fixtures/license', import.meta.url)),
+ logLevel: 'silent',
+ build: {
+ write: false,
+ license: true,
+ },
+ })) as RollupOutput
+ const licenseAsset = result.output.find(
+ (asset) => asset.fileName === '.vite/license.md',
+ ) as OutputAsset | undefined
+ expect(licenseAsset).toBeDefined()
+ expect(licenseAsset?.source).toMatchSnapshot()
+})
+
+test('json', async () => {
+ const result = (await build({
+ root: fileURLToPath(new URL('./fixtures/license', import.meta.url)),
+ logLevel: 'silent',
+ build: {
+ write: false,
+ license: {
+ fileName: '.vite/license.json',
+ },
+ },
+ })) as RollupOutput
+ const licenseAsset = result.output.find(
+ (asset) => asset.fileName === '.vite/license.json',
+ ) as OutputAsset | undefined
+ expect(licenseAsset).toBeDefined()
+ expect(licenseAsset?.source).toMatchSnapshot()
+})
diff --git a/packages/vite/src/node/__tests__/plugins/modulePreloadPolyfill/__snapshots__/modulePreloadPolyfill.spec.ts.snap b/packages/vite/src/node/__tests__/plugins/modulePreloadPolyfill/__snapshots__/modulePreloadPolyfill.spec.ts.snap
new file mode 100644
index 00000000000000..5da7df74894dfc
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/modulePreloadPolyfill/__snapshots__/modulePreloadPolyfill.spec.ts.snap
@@ -0,0 +1,38 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`load > doesn't load modulepreload polyfill when format is cjs 1`] = `""`;
+
+exports[`load > loads modulepreload polyfill 1`] = `
+"//#region \\0vite/modulepreload-polyfill.js
+(function polyfill() {
+ const relList = document.createElement("link").relList;
+ if (relList && relList.supports && relList.supports("modulepreload")) return;
+ for (const link of document.querySelectorAll("link[rel=\\"modulepreload\\"]")) processPreload(link);
+ new MutationObserver((mutations) => {
+ for (const mutation of mutations) {
+ if (mutation.type !== "childList") continue;
+ for (const node of mutation.addedNodes) if (node.tagName === "LINK" && node.rel === "modulepreload") processPreload(node);
+ }
+ }).observe(document, {
+ childList: true,
+ subtree: true
+ });
+ function getFetchOpts(link) {
+ const fetchOpts = {};
+ if (link.integrity) fetchOpts.integrity = link.integrity;
+ if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy;
+ if (link.crossOrigin === "use-credentials") fetchOpts.credentials = "include";
+ else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit";
+ else fetchOpts.credentials = "same-origin";
+ return fetchOpts;
+ }
+ function processPreload(link) {
+ if (link.ep) return;
+ link.ep = true;
+ const fetchOpts = getFetchOpts(link);
+ fetch(link.href, fetchOpts);
+ }
+})();
+//#endregion
+"
+`;
diff --git a/packages/vite/src/node/__tests__/plugins/modulePreloadPolyfill/modulePreloadPolyfill.spec.ts b/packages/vite/src/node/__tests__/plugins/modulePreloadPolyfill/modulePreloadPolyfill.spec.ts
new file mode 100644
index 00000000000000..6eb0c1598b113d
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/modulePreloadPolyfill/modulePreloadPolyfill.spec.ts
@@ -0,0 +1,53 @@
+import { describe, it } from 'vitest'
+import type { ModuleFormat, RolldownOutput } from 'rolldown'
+import { build } from '../../../build'
+import { modulePreloadPolyfillId } from '../../../plugins/modulePreloadPolyfill'
+
+const buildProject = ({ format = 'es' as ModuleFormat } = {}) =>
+ build({
+ logLevel: 'silent',
+ build: {
+ write: false,
+ rollupOptions: {
+ input: 'main.js',
+ output: {
+ format,
+ },
+ treeshake: {
+ moduleSideEffects: false,
+ },
+ },
+ minify: false,
+ },
+ plugins: [
+ {
+ name: 'test',
+ resolveId(id) {
+ if (id === 'main.js') {
+ return `\0${id}`
+ }
+ },
+ load(id) {
+ if (id === '\0main.js') {
+ return `import '${modulePreloadPolyfillId}'`
+ }
+ },
+ },
+ ],
+ }) as Promise
+
+describe('load', () => {
+ it('loads modulepreload polyfill', async ({ expect }) => {
+ const { output } = await buildProject()
+ expect(output).toHaveLength(1)
+ expect(output[0].code).toMatchSnapshot()
+ })
+
+ it("doesn't load modulepreload polyfill when format is cjs", async ({
+ expect,
+ }) => {
+ const { output } = await buildProject({ format: 'cjs' })
+ expect(output).toHaveLength(1)
+ expect(output[0].code).toMatchSnapshot()
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/oxc.spec.ts b/packages/vite/src/node/__tests__/plugins/oxc.spec.ts
new file mode 100644
index 00000000000000..99093cdd95b61d
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/oxc.spec.ts
@@ -0,0 +1,156 @@
+import path from 'node:path'
+import { describe, expect, test } from 'vitest'
+import { transformWithOxc } from '../../plugins/oxc'
+
+describe('transformWithOxc', () => {
+ test('correctly overrides TS configuration and applies automatic transform', async () => {
+ const jsxImportSource = 'bar'
+ const result = await transformWithOxc(
+ 'const foo = () => <>>',
+ path.resolve(
+ import.meta.dirname,
+ './fixtures/oxc-tsconfigs/jsx-preserve/baz.jsx',
+ ),
+ {
+ jsx: {
+ runtime: 'automatic',
+ importSource: jsxImportSource,
+ },
+ },
+ )
+ expect(result?.code).toContain(`${jsxImportSource}/jsx-runtime`)
+ expect(result?.code).toContain('/* @__PURE__ */')
+ })
+
+ test('correctly overrides TS configuration and preserves code', async () => {
+ const foo = 'const foo = () => <>>'
+ const result = await transformWithOxc(
+ foo,
+ path.resolve(
+ import.meta.dirname,
+ './fixtures/oxc-tsconfigs/jsx-react-jsx/baz.jsx',
+ ),
+ {
+ jsx: 'preserve',
+ },
+ )
+ expect(result?.code).toContain(foo)
+ })
+
+ test('correctly overrides TS configuration and transforms code', async () => {
+ const jsxFactory = 'h',
+ jsxFragment = 'bar'
+ const result = await transformWithOxc(
+ 'const foo = () => <>>',
+ path.resolve(
+ import.meta.dirname,
+ './fixtures/oxc-tsconfigs/jsx-complex-options/baz.jsx',
+ ),
+ {
+ jsx: {
+ runtime: 'classic',
+ pragma: jsxFactory,
+ pragmaFrag: jsxFragment,
+ },
+ },
+ )
+ expect(result?.code).toContain(
+ `/* @__PURE__ */ ${jsxFactory}(${jsxFragment}, null)`,
+ )
+ })
+
+ describe('useDefineForClassFields', async () => {
+ const transformClassCode = async (target: string, tsconfigDir: string) => {
+ const result = await transformWithOxc(
+ `
+ class foo {
+ bar = 'bar'
+ }
+ `,
+ path.resolve(import.meta.dirname, tsconfigDir, './bar.ts'),
+ { target },
+ )
+ return result?.code
+ }
+
+ const [
+ defineForClassFieldsTrueTransformedCode,
+ defineForClassFieldsTrueLowerTransformedCode,
+ defineForClassFieldsFalseTransformedCode,
+ ] = await Promise.all([
+ transformClassCode('esnext', './fixtures/oxc-tsconfigs/use-define-true'),
+ transformClassCode('es2021', './fixtures/oxc-tsconfigs/use-define-true'),
+ transformClassCode('esnext', './fixtures/oxc-tsconfigs/use-define-false'),
+ ])
+
+ test('target: esnext and tsconfig.target: esnext => true', async () => {
+ const actual = await transformClassCode(
+ 'esnext',
+ './fixtures/oxc-tsconfigs/target-esnext',
+ )
+ expect(actual).toBe(defineForClassFieldsTrueTransformedCode)
+ })
+
+ test('target: es2021 and tsconfig.target: esnext => true', async () => {
+ const actual = await transformClassCode(
+ 'es2021',
+ './fixtures/oxc-tsconfigs/target-esnext',
+ )
+ expect(actual).toBe(defineForClassFieldsTrueLowerTransformedCode)
+ })
+
+ test('target: es2021 and tsconfig.target: es2021 => false', async () => {
+ const actual = await transformClassCode(
+ 'es2021',
+ './fixtures/oxc-tsconfigs/target-es2021',
+ )
+ expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
+ })
+
+ test('target: esnext and tsconfig.target: es2021 => false', async () => {
+ const actual = await transformClassCode(
+ 'esnext',
+ './fixtures/oxc-tsconfigs/target-es2021',
+ )
+ expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
+ })
+
+ test('target: es2022 and tsconfig.target: es2022 => true', async () => {
+ const actual = await transformClassCode(
+ 'es2022',
+ './fixtures/oxc-tsconfigs/target-es2022',
+ )
+ expect(actual).toBe(defineForClassFieldsTrueTransformedCode)
+ })
+
+ test('target: es2022 and tsconfig.target: undefined => false', async () => {
+ const actual = await transformClassCode(
+ 'es2022',
+ './fixtures/oxc-tsconfigs/empty',
+ )
+ expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
+ })
+ })
+
+ test('supports emitDecoratorMetadata: true', async () => {
+ const result = await transformWithOxc(
+ `
+ function LogMethod(target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
+ console.log(target, propertyKey, descriptor);
+ }
+
+ class Demo {
+ @LogMethod
+ public foo(bar: number) {}
+ }
+
+ const demo = new Demo();
+ `,
+ path.resolve(
+ import.meta.dirname,
+ './fixtures/oxc-tsconfigs/decorator-metadata/bar.ts',
+ ),
+ )
+ expect(result?.code).toContain('_decorateMetadata("design:type"')
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/pluginFilter.spec.ts b/packages/vite/src/node/__tests__/plugins/pluginFilter.spec.ts
new file mode 100644
index 00000000000000..4cb30a1f3d4ddc
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/pluginFilter.spec.ts
@@ -0,0 +1,343 @@
+import util from 'node:util'
+import path from 'node:path'
+import { describe, expect, test } from 'vitest'
+import type { ModuleTypeFilter } from 'rolldown'
+import {
+ type StringFilter,
+ createCodeFilter,
+ createFilterForTransform,
+ createIdFilter,
+} from '../../plugins/pluginFilter'
+
+describe('createIdFilter', () => {
+ const filters = [
+ { inputFilter: undefined, cases: undefined },
+ {
+ inputFilter: 'foo.js',
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ { id: '\0foo.js', expected: false },
+ { id: '\0' + path.resolve('foo.js'), expected: false },
+ ],
+ },
+ {
+ inputFilter: ['foo.js'],
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: 'foo.js' },
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: '*.js' },
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: /\.js$/ },
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: /\/foo\.js$/ },
+ cases: [
+ { id: 'a/foo.js', expected: true },
+ ...(process.platform === 'win32'
+ ? [{ id: 'a\\foo.js', expected: true }]
+ : []),
+ { id: 'a_foo.js', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: [/\.js$/] },
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: { exclude: 'foo.js' },
+ cases: [
+ { id: 'foo.js', expected: false },
+ { id: 'foo.ts', expected: true },
+ ],
+ },
+ {
+ inputFilter: { exclude: '*.js' },
+ cases: [
+ { id: 'foo.js', expected: false },
+ { id: 'foo.ts', expected: true },
+ ],
+ },
+ {
+ inputFilter: { exclude: /\.js$/ },
+ cases: [
+ { id: 'foo.js', expected: false },
+ { id: 'foo.ts', expected: true },
+ ],
+ },
+ {
+ inputFilter: { exclude: [/\.js$/] },
+ cases: [
+ { id: 'foo.js', expected: false },
+ { id: 'foo.ts', expected: true },
+ ],
+ },
+ {
+ inputFilter: { include: 'foo.js', exclude: 'bar.js' },
+ cases: [
+ { id: 'foo.js', expected: true },
+ { id: 'bar.js', expected: false },
+ { id: 'baz.js', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: '*.js', exclude: 'foo.*' },
+ cases: [
+ { id: 'foo.js', expected: false }, // exclude has higher priority
+ { id: 'bar.js', expected: true },
+ { id: 'foo.ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: '/virtual/foo',
+ cases: [{ id: '/virtual/foo', expected: true }],
+ },
+ ]
+
+ for (const filter of filters) {
+ test(`${util.inspect(filter.inputFilter)}`, () => {
+ const idFilter = createIdFilter(filter.inputFilter, '')
+ if (!filter.cases) {
+ expect(idFilter).toBeUndefined()
+ return
+ }
+ expect(idFilter).not.toBeUndefined()
+
+ for (const testCase of filter.cases) {
+ const { id, expected } = testCase
+ expect(idFilter!(id), id).toBe(expected)
+ }
+ })
+ }
+})
+
+describe('createCodeFilter', () => {
+ const filters = [
+ { inputFilter: undefined, cases: undefined },
+ {
+ inputFilter: 'import.meta',
+ cases: [
+ { code: 'import.meta', expected: true },
+ { code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: ['import.meta'],
+ cases: [
+ { code: 'import.meta', expected: true },
+ { code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: 'import.meta' },
+ cases: [
+ { code: 'import.meta', expected: true },
+ { code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: /import\.\w+/ },
+ cases: [
+ { code: 'import.meta', expected: true },
+ { code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: [/import\.\w+/] },
+ cases: [
+ { code: 'import.meta', expected: true },
+ { code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: { exclude: 'import.meta' },
+ cases: [
+ { code: 'import.meta', expected: false },
+ { code: 'import_meta', expected: true },
+ ],
+ },
+ {
+ inputFilter: { exclude: /import\.\w+/ },
+ cases: [
+ { code: 'import.meta', expected: false },
+ { code: 'import_meta', expected: true },
+ ],
+ },
+ {
+ inputFilter: { exclude: [/import\.\w+/] },
+ cases: [
+ { code: 'import.meta', expected: false },
+ { code: 'import_meta', expected: true },
+ ],
+ },
+ {
+ inputFilter: { include: 'import.meta', exclude: 'import_meta' },
+ cases: [
+ { code: 'import.meta', expected: true },
+ { code: 'import_meta', expected: false },
+ { code: 'importmeta', expected: false },
+ ],
+ },
+ {
+ inputFilter: { include: /import\.\w+/, exclude: /\w+\.meta/ },
+ cases: [
+ { code: 'import.meta', expected: false }, // exclude has higher priority
+ { code: 'import.foo', expected: true },
+ { code: 'foo.meta', expected: false },
+ ],
+ },
+ ]
+
+ for (const filter of filters) {
+ test(`${util.inspect(filter.inputFilter)}`, () => {
+ const codeFilter = createCodeFilter(filter.inputFilter)
+ if (!filter.cases) {
+ expect(codeFilter).toBeUndefined()
+ return
+ }
+ expect(codeFilter).not.toBeUndefined()
+
+ for (const testCase of filter.cases) {
+ const { code, expected } = testCase
+ expect(codeFilter!(code), code).toBe(expected)
+ }
+ })
+ }
+})
+
+describe('createFilterForTransform', () => {
+ type Filters = {
+ inputFilter: [
+ idFilter: StringFilter | undefined,
+ codeFilter: StringFilter | undefined,
+ moduleTypeFilter?: ModuleTypeFilter | undefined,
+ ]
+ cases:
+ | {
+ id: string
+ code: string
+ moduleType?: string
+ expected: boolean
+ }[]
+ | undefined
+ }[]
+ const filters: Filters = [
+ { inputFilter: [undefined, undefined], cases: undefined },
+ {
+ inputFilter: ['*.js', undefined],
+ cases: [
+ { id: 'foo.js', code: 'foo', expected: true },
+ { id: 'foo.ts', code: 'foo', expected: false },
+ ],
+ },
+ {
+ inputFilter: [undefined, 'import.meta'],
+ cases: [
+ { id: 'foo.js', code: 'import.meta', expected: true },
+ { id: 'foo.js', code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: [{ exclude: '*.js' }, 'import.meta'],
+ cases: [
+ { id: 'foo.js', code: 'import.meta', expected: false },
+ { id: 'foo.js', code: 'import_meta', expected: false },
+ { id: 'foo.ts', code: 'import.meta', expected: true },
+ { id: 'foo.ts', code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: [{ include: 'foo.ts', exclude: '*.js' }, 'import.meta'],
+ cases: [
+ { id: 'foo.js', code: 'import.meta', expected: false },
+ { id: 'foo.js', code: 'import_meta', expected: false },
+ { id: 'foo.ts', code: 'import.meta', expected: true },
+ { id: 'foo.ts', code: 'import_meta', expected: false },
+ ],
+ },
+ {
+ inputFilter: [
+ { include: 'a*', exclude: '*b' },
+ { include: 'a', exclude: 'b' },
+ ],
+ cases: [
+ { id: 'ab', code: '', expected: false },
+ { id: 'a', code: 'b', expected: false },
+ { id: 'a', code: '', expected: false },
+ { id: 'c', code: 'a', expected: false },
+ { id: 'a', code: 'a', expected: true },
+ ],
+ },
+ {
+ inputFilter: [{ include: 'a*', exclude: '*b' }, { exclude: 'b' }],
+ cases: [
+ { id: 'ab', code: '', expected: false },
+ { id: 'a', code: 'b', expected: false },
+ { id: 'a', code: '', expected: true },
+ { id: 'c', code: 'a', expected: false },
+ { id: 'a', code: 'a', expected: true },
+ ],
+ },
+ {
+ inputFilter: [undefined, undefined, ['js']],
+ cases: [
+ { id: 'foo.js', code: 'foo', moduleType: 'js', expected: true },
+ { id: 'foo.ts', code: 'foo', moduleType: 'ts', expected: false },
+ ],
+ },
+ {
+ inputFilter: [undefined, undefined, { include: ['js'] }],
+ cases: [
+ { id: 'foo.js', code: 'foo', moduleType: 'js', expected: true },
+ { id: 'foo.ts', code: 'foo', moduleType: 'ts', expected: false },
+ ],
+ },
+ ]
+
+ for (const filter of filters) {
+ test(`${util.inspect(filter.inputFilter)}`, () => {
+ const [idFilter, codeFilter, moduleTypeFilter] = filter.inputFilter
+ const filterForTransform = createFilterForTransform(
+ idFilter,
+ codeFilter,
+ moduleTypeFilter,
+ '',
+ )
+ if (!filter.cases) {
+ expect(filterForTransform).toBeUndefined()
+ return
+ }
+ expect(filterForTransform).not.toBeUndefined()
+
+ for (const testCase of filter.cases) {
+ const { id, code, moduleType, expected } = testCase
+ expect(
+ filterForTransform!(id, code, moduleType ?? 'js'),
+ util.inspect({ id, code, moduleType }),
+ ).toBe(expected)
+ }
+ })
+ }
+})
diff --git a/packages/vite/src/node/__tests__/plugins/terser.spec.ts b/packages/vite/src/node/__tests__/plugins/terser.spec.ts
new file mode 100644
index 00000000000000..cd3e8e86ec0a03
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/terser.spec.ts
@@ -0,0 +1,74 @@
+import { resolve } from 'node:path'
+import { describe, expect, test } from 'vitest'
+import { build } from 'vite'
+import type { RollupOutput } from 'rollup'
+import type { TerserOptions } from '../../plugins/terser'
+
+describe('terser', () => {
+ const run = async (terserOptions: TerserOptions) => {
+ const result = (await build({
+ root: resolve(import.meta.dirname, '../packages/build-project'),
+ logLevel: 'silent',
+ build: {
+ write: false,
+ minify: 'terser',
+ terserOptions,
+ },
+ plugins: [
+ {
+ name: 'test',
+ resolveId(id) {
+ if (id === 'entry.js') {
+ return '\0' + id
+ }
+ },
+ load(id) {
+ if (id === '\0entry.js') {
+ return `
+ const foo = 1;
+ console.log(foo);
+ const bar = { hello: 1, ["world"]: 2 };
+ console.log(bar.hello + bar["world"]);
+ `
+ }
+ },
+ },
+ ],
+ })) as RollupOutput
+ return result.output[0].code
+ }
+
+ test('basic', async () => {
+ await run({})
+ })
+
+ test('nth', async () => {
+ const resultCode = await run({
+ mangle: {
+ nth_identifier: {
+ get: (n) => {
+ return 'prefix_' + n.toString()
+ },
+ },
+ },
+ })
+ expect(resultCode).toContain('prefix_')
+ })
+
+ test('nameCache', async () => {
+ const nameCache = {}
+
+ await run({
+ compress: false,
+ mangle: {
+ properties: {
+ keep_quoted: true,
+ },
+ },
+ nameCache,
+ })
+
+ expect(nameCache).toHaveProperty('props.props.$hello')
+ expect(nameCache).not.toHaveProperty('props.props.$world')
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/wasm.spec.ts b/packages/vite/src/node/__tests__/plugins/wasm.spec.ts
new file mode 100644
index 00000000000000..85e2a9760cb129
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/wasm.spec.ts
@@ -0,0 +1,42 @@
+import { describe, expect, test } from 'vitest'
+import { assetUrlRE } from '../../plugins/asset'
+
+describe('wasm plugin assetUrlRE usage', () => {
+ // Regression test: assetUrlRE has the /g flag, which makes .test()
+ // stateful via lastIndex. When the wasm plugin called assetUrlRE.test()
+ // on consecutive asset URLs without resetting lastIndex, the second call
+ // would fail because lastIndex was already past the end of the string.
+ // This caused a non-deterministic bug where ~half of wasm files would
+ // miss the __VITE_ASSET__ -> __VITE_WASM_INIT__ replacement in SSR
+ // builds, leading to ENOENT errors at runtime.
+ test('assetUrlRE.test() fails on second call without lastIndex reset', () => {
+ const url1 = '__VITE_ASSET__abc123__'
+ const url2 = '__VITE_ASSET__def456__'
+
+ // Simulate the bug: two consecutive .test() calls without resetting lastIndex
+ assetUrlRE.lastIndex = 0
+ expect(assetUrlRE.test(url1)).toBe(true)
+ // After the first successful match, lastIndex is advanced past the string
+ expect(assetUrlRE.lastIndex).toBeGreaterThan(0)
+ // The second call fails because lastIndex > url2.length
+ expect(assetUrlRE.test(url2)).toBe(false)
+
+ // Clean up
+ assetUrlRE.lastIndex = 0
+ })
+
+ test('assetUrlRE.test() succeeds on consecutive calls with lastIndex reset', () => {
+ const url1 = '__VITE_ASSET__abc123__'
+ const url2 = '__VITE_ASSET__def456__'
+
+ assetUrlRE.lastIndex = 0
+ expect(assetUrlRE.test(url1)).toBe(true)
+
+ // The fix: reset lastIndex before the next call
+ assetUrlRE.lastIndex = 0
+ expect(assetUrlRE.test(url2)).toBe(true)
+
+ // Clean up
+ assetUrlRE.lastIndex = 0
+ })
+})
diff --git a/packages/vite/src/node/__tests__/plugins/worker.spec.ts b/packages/vite/src/node/__tests__/plugins/worker.spec.ts
new file mode 100644
index 00000000000000..7fc52aa7f6c608
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/worker.spec.ts
@@ -0,0 +1,47 @@
+import { resolve } from 'node:path'
+import { expect, test } from 'vitest'
+import type { OutputChunk, RolldownOutput } from 'rolldown'
+import { build } from '../../build'
+
+const fixturesDir = resolve(import.meta.dirname, 'fixtures')
+
+test('?worker&url should produce the same hash in client and SSR builds', async () => {
+ const root = resolve(fixturesDir, 'worker-url')
+
+ const clientResult = (await build({
+ root,
+ logLevel: 'silent',
+ build: {
+ write: false,
+ rolldownOptions: {
+ input: resolve(root, 'entry.js'),
+ },
+ },
+ })) as RolldownOutput
+
+ const ssrResult = (await build({
+ root,
+ logLevel: 'silent',
+ build: {
+ write: false,
+ ssr: resolve(root, 'entry.js'),
+ },
+ })) as RolldownOutput
+
+ // Extract the worker URL from both builds.
+ // The entry chunk will contain the worker asset URL as a string.
+ const clientEntry = clientResult.output.find(
+ (o): o is OutputChunk => o.type === 'chunk' && o.isEntry,
+ )!
+ const ssrEntry = ssrResult.output.find(
+ (o): o is OutputChunk => o.type === 'chunk' && o.isEntry,
+ )!
+
+ const workerUrlPattern = /assets\/worker-[\w-]+\.js/g
+ const clientWorkerUrls = clientEntry.code.match(workerUrlPattern) ?? []
+ const ssrWorkerUrls = ssrEntry.code.match(workerUrlPattern) ?? []
+
+ expect(clientWorkerUrls.length).toBeGreaterThan(0)
+ expect(ssrWorkerUrls.length).toBeGreaterThan(0)
+ expect(ssrWorkerUrls).toEqual(clientWorkerUrls)
+})
diff --git a/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts b/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts
new file mode 100644
index 00000000000000..348e3e39be0147
--- /dev/null
+++ b/packages/vite/src/node/__tests__/plugins/workerImportMetaUrl.spec.ts
@@ -0,0 +1,256 @@
+import { describe, expect, test } from 'vitest'
+import { parseAst } from 'rollup/parseAst'
+import { workerImportMetaUrlPlugin } from '../../plugins/workerImportMetaUrl'
+import { resolveConfig } from '../../config'
+import { PartialEnvironment } from '../../baseEnvironment'
+
+async function createWorkerImportMetaUrlPluginTransform() {
+ const config = await resolveConfig({ configFile: false }, 'serve')
+ const instance = workerImportMetaUrlPlugin(config)
+ const environment = new PartialEnvironment('client', config)
+
+ return async (code: string) => {
+ // @ts-expect-error transform.handler should exist
+ const result = await instance.transform.handler.call(
+ { environment, parse: parseAst },
+ code,
+ 'foo.ts',
+ )
+ return result?.code || result
+ }
+}
+
+describe('workerImportMetaUrlPlugin', async () => {
+ const transform = await createWorkerImportMetaUrlPluginTransform()
+
+ test('without worker options', async () => {
+ expect(
+ await transform('new Worker(new URL("./worker.js", import.meta.url))'),
+ ).toMatchInlineSnapshot(
+ `"new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=classic", '' + import.meta.url))"`,
+ )
+ })
+
+ test('with shared worker', async () => {
+ expect(
+ await transform(
+ 'new SharedWorker(new URL("./worker.js", import.meta.url))',
+ ),
+ ).toMatchInlineSnapshot(
+ `"new SharedWorker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=classic", '' + import.meta.url))"`,
+ )
+ })
+
+ test('with static worker options and identifier properties', async () => {
+ expect(
+ await transform(
+ 'new Worker(new URL("./worker.js", import.meta.url), { type: "module", name: "worker1" })',
+ ),
+ ).toMatchInlineSnapshot(
+ `"new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url), { type: "module", name: "worker1" })"`,
+ )
+ })
+
+ test('with static worker options and literal properties', async () => {
+ expect(
+ await transform(
+ 'new Worker(new URL("./worker.js", import.meta.url), { "type": "module", "name": "worker1" })',
+ ),
+ ).toMatchInlineSnapshot(
+ `"new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url), { "type": "module", "name": "worker1" })"`,
+ )
+ })
+
+ test('with dynamic name field in worker options', async () => {
+ expect(
+ await transform(
+ 'const id = 1; new Worker(new URL("./worker.js", import.meta.url), { name: "worker" + id })',
+ ),
+ ).toMatchInlineSnapshot(
+ `"const id = 1; new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=classic", '' + import.meta.url), { name: "worker" + id })"`,
+ )
+ })
+
+ test('with interpolated dynamic name field in worker options', async () => {
+ expect(
+ await transform(
+ 'const id = 1; new Worker(new URL("./worker.js", import.meta.url), { name: `worker-${id}` })',
+ ),
+ ).toMatchInlineSnapshot(
+ `"const id = 1; new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=classic", '' + import.meta.url), { name: \`worker-\${id}\` })"`,
+ )
+ })
+
+ test('with dynamic name field and static type in worker options', async () => {
+ expect(
+ await transform(
+ 'const id = 1; new Worker(new URL("./worker.js", import.meta.url), { name: "worker" + id, type: "module" })',
+ ),
+ ).toMatchInlineSnapshot(
+ `"const id = 1; new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url), { name: "worker" + id, type: "module" })"`,
+ )
+ })
+
+ test('with interpolated dynamic name field and static type in worker options', async () => {
+ expect(
+ await transform(
+ 'const id = 1; new Worker(new URL("./worker.js", import.meta.url), { name: `worker-${id}`, type: "module" })',
+ ),
+ ).toMatchInlineSnapshot(
+ `"const id = 1; new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url), { name: \`worker-\${id}\`, type: "module" })"`,
+ )
+ })
+
+ test('with parenthesis inside of worker options', async () => {
+ expect(
+ await transform(
+ 'const worker = new Worker(new URL("./worker.js", import.meta.url), { name: genName(), type: "module"})',
+ ),
+ ).toMatchInlineSnapshot(
+ `"const worker = new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url), { name: genName(), type: "module"})"`,
+ )
+ })
+
+ test('with multi-line code and worker options', async () => {
+ expect(
+ await transform(`
+const worker = new Worker(new URL("./worker.js", import.meta.url), {
+ name: genName(),
+ type: "module",
+ },
+)
+
+worker.addEventListener('message', (ev) => text('.simple-worker-url', JSON.stringify(ev.data)))
+`),
+ ).toMatchInlineSnapshot(`
+ "
+ const worker = new Worker(new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url), {
+ name: genName(),
+ type: "module",
+ },
+ )
+
+ worker.addEventListener('message', (ev) => text('.simple-worker-url', JSON.stringify(ev.data)))
+ "
+ `)
+ })
+
+ test('trailing comma', async () => {
+ expect(
+ await transform(`
+new Worker(
+ new URL('./worker.js', import.meta.url),
+ {
+ type: 'module'
+ }, // },
+)
+`),
+ ).toMatchInlineSnapshot(`
+ "
+ new Worker(
+ new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url),
+ {
+ type: 'module'
+ }, // },
+ )
+ "
+ `)
+ })
+
+ test('throws an error when non-static worker options are provided', async () => {
+ await expect(
+ transform(
+ 'new Worker(new URL("./worker.js", import.meta.url), myWorkerOptions)',
+ ),
+ ).rejects.toThrow(
+ 'Vite is unable to parse the worker options as the value is not static. To ignore this error, please use /* @vite-ignore */ in the worker options.',
+ )
+ })
+
+ test('throws an error when worker options are not an object', async () => {
+ await expect(
+ transform(
+ 'new Worker(new URL("./worker.js", import.meta.url), "notAnObject")',
+ ),
+ ).rejects.toThrow('Expected worker options to be an object, got string')
+ })
+
+ test('throws an error when non-literal type field in worker options', async () => {
+ await expect(
+ transform(
+ 'const type = "module"; new Worker(new URL("./worker.js", import.meta.url), { type })',
+ ),
+ ).rejects.toThrow(
+ 'Expected worker options type property to be a literal value.',
+ )
+ })
+
+ test('throws an error when spread operator used without the type field', async () => {
+ await expect(
+ transform(
+ 'const options = { name: "worker1" }; new Worker(new URL("./worker.js", import.meta.url), { ...options })',
+ ),
+ ).rejects.toThrow(
+ 'Expected object spread to be used before the definition of the type property. Vite needs a static value for the type property to correctly infer it.',
+ )
+ })
+
+ test('throws an error when spread operator used after definition of type field', async () => {
+ await expect(
+ transform(
+ 'const options = { name: "worker1" }; new Worker(new URL("./worker.js", import.meta.url), { type: "module", ...options })',
+ ),
+ ).rejects.toThrow(
+ 'Expected object spread to be used before the definition of the type property. Vite needs a static value for the type property to correctly infer it.',
+ )
+ })
+
+ test('find closing parenthesis correctly', async () => {
+ expect(
+ await transform(
+ `(() => { new Worker(new URL('./worker', import.meta.url)); repro({ test: "foo", }); })();`,
+ ),
+ ).toMatchInlineSnapshot(
+ `"(() => { new Worker(new URL(/* @vite-ignore */ "/worker?worker_file&type=classic", '' + import.meta.url)); repro({ test: "foo", }); })();"`,
+ )
+ expect(
+ await transform(
+ `repro(new Worker(new URL('./worker', import.meta.url)), { type: "module" })`,
+ ),
+ ).toMatchInlineSnapshot(
+ `"repro(new Worker(new URL(/* @vite-ignore */ "/worker?worker_file&type=classic", '' + import.meta.url)), { type: "module" })"`,
+ )
+ })
+
+ test('with multi-line new URL and trailing comma', async () => {
+ expect(
+ await transform(`new Worker(
+ new URL(
+ "./worker.js",
+ import.meta.url,
+ )
+)`),
+ ).toMatchInlineSnapshot(`
+ "new Worker(
+ new URL(/* @vite-ignore */ "/worker.js?worker_file&type=classic", '' + import.meta.url)
+ )"
+ `)
+ })
+
+ test('with multi-line new URL, trailing comma, and worker options', async () => {
+ expect(
+ await transform(`const worker = new Worker(
+ new URL(
+ "./worker.js",
+ import.meta.url,
+ ),
+ { type: "module" },
+)`),
+ ).toMatchInlineSnapshot(`
+ "const worker = new Worker(
+ new URL(/* @vite-ignore */ "/worker.js?worker_file&type=module", '' + import.meta.url),
+ { type: "module" },
+ )"
+ `)
+ })
+})
diff --git a/packages/vite/src/node/__tests__/resolve.spec.ts b/packages/vite/src/node/__tests__/resolve.spec.ts
new file mode 100644
index 00000000000000..4c5465b00833ef
--- /dev/null
+++ b/packages/vite/src/node/__tests__/resolve.spec.ts
@@ -0,0 +1,313 @@
+import { join } from 'node:path'
+import { describe, expect, onTestFinished, test, vi } from 'vitest'
+import { createServer } from '../server'
+import { createServerModuleRunner } from '../ssr/runtime/serverModuleRunner'
+import type { EnvironmentOptions, InlineConfig } from '../config'
+import { build } from '../build'
+
+describe('import and resolveId', () => {
+ async function createTestServer() {
+ const server = await createServer({
+ configFile: false,
+ root: import.meta.dirname,
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ ws: false,
+ },
+ })
+ onTestFinished(() => server.close())
+ const runner = createServerModuleRunner(server.environments.ssr, {
+ hmr: {
+ logger: false,
+ },
+ sourcemapInterceptor: false,
+ })
+ return { server, runner }
+ }
+
+ test('import first', async () => {
+ const { server, runner } = await createTestServer()
+ const mod = await runner.import(
+ '/fixtures/test-dep-conditions-app/entry-with-module',
+ )
+ const resolved = await server.environments.ssr.pluginContainer.resolveId(
+ '@vitejs/test-dep-conditions/with-module',
+ )
+ expect([mod.default, resolved?.id]).toEqual([
+ 'dir/index.default.js',
+ expect.stringContaining('dir/index.module.js'),
+ ])
+ })
+
+ test('resolveId first', async () => {
+ const { server, runner } = await createTestServer()
+ const resolved = await server.environments.ssr.pluginContainer.resolveId(
+ '@vitejs/test-dep-conditions/with-module',
+ )
+ const mod = await runner.import(
+ '/fixtures/test-dep-conditions-app/entry-with-module',
+ )
+ expect([mod.default, resolved?.id]).toEqual([
+ 'dir/index.default.js',
+ expect.stringContaining('dir/index.module.js'),
+ ])
+ })
+})
+
+describe('file url', () => {
+ const fileUrl = new URL('./fixtures/file-url/entry.js', import.meta.url)
+
+ function getConfig(): InlineConfig {
+ return {
+ configFile: false,
+ root: join(import.meta.dirname, 'fixtures/file-url'),
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ },
+ plugins: [
+ {
+ name: 'virtual-file-url',
+ resolveId(source) {
+ if (source.startsWith('virtual:test-dep/')) {
+ return '\0' + source
+ }
+ },
+ load(id) {
+ if (id === '\0virtual:test-dep/static') {
+ return `
+ import * as dep from ${JSON.stringify(fileUrl.href)};
+ export default dep;
+ `
+ }
+ if (id === '\0virtual:test-dep/static-postfix') {
+ return `
+ import * as dep from ${JSON.stringify(fileUrl.href + '?query=test')};
+ export default dep;
+ `
+ }
+ if (id === '\0virtual:test-dep/non-static') {
+ return `
+ const dep = await import(/* @vite-ignore */ String(${JSON.stringify(fileUrl.href)}));
+ export default dep;
+ `
+ }
+ if (id === '\0virtual:test-dep/non-static-postfix') {
+ return `
+ const dep = await import(/* @vite-ignore */ String(${JSON.stringify(fileUrl.href + '?query=test')}));
+ export default dep;
+ `
+ }
+ },
+ },
+ ],
+ }
+ }
+
+ test('dev', async () => {
+ const server = await createServer(getConfig())
+ onTestFinished(() => server.close())
+
+ const runner = createServerModuleRunner(server.environments.ssr, {
+ hmr: {
+ logger: false,
+ },
+ sourcemapInterceptor: false,
+ })
+
+ const mod = await runner.import('/entry.js')
+ expect(mod.default).toEqual('ok')
+
+ const mod2 = await runner.import(fileUrl.href)
+ expect(mod2).toBe(mod)
+
+ const mod3 = await runner.import('virtual:test-dep/static')
+ expect(mod3.default).toBe(mod)
+
+ const mod4 = await runner.import('virtual:test-dep/non-static')
+ expect(mod4.default).toBe(mod)
+
+ const mod5 = await runner.import(fileUrl.href + '?query=test')
+ expect(mod5).toEqual(mod)
+ expect(mod5).not.toBe(mod)
+
+ const mod6 = await runner.import('virtual:test-dep/static-postfix')
+ expect(mod6.default).toEqual(mod)
+ expect(mod6.default).not.toBe(mod)
+ expect(mod6.default).toBe(mod5)
+
+ const mod7 = await runner.import('virtual:test-dep/non-static-postfix')
+ expect(mod7.default).toEqual(mod)
+ expect(mod7.default).not.toBe(mod)
+ expect(mod7.default).toBe(mod5)
+ })
+
+ describe('environment builtins', () => {
+ function getConfig(
+ targetEnv: 'client' | 'ssr' | string,
+ builtins: NonNullable['builtins'],
+ ): InlineConfig {
+ return {
+ configFile: false,
+ root: join(import.meta.dirname, 'fixtures/file-url'),
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ },
+ environments: {
+ [targetEnv]: {
+ resolve: {
+ builtins,
+ },
+ },
+ },
+ }
+ }
+
+ async function run({
+ builtins,
+ targetEnv = 'custom',
+ testEnv = 'custom',
+ idToResolve,
+ }: {
+ builtins?: NonNullable['builtins']
+ targetEnv?: 'client' | 'ssr' | string
+ testEnv?: 'client' | 'ssr' | string
+ idToResolve: string
+ }) {
+ const server = await createServer(getConfig(targetEnv, builtins))
+ vi.spyOn(server.config.logger, 'warn').mockImplementationOnce(
+ (message) => {
+ throw new Error(message)
+ },
+ )
+ onTestFinished(() => server.close())
+
+ return server.environments[testEnv]?.pluginContainer.resolveId(
+ idToResolve,
+ )
+ }
+
+ test('declared builtin string', async () => {
+ const resolved = await run({
+ builtins: ['my-env:custom-builtin'],
+ idToResolve: 'my-env:custom-builtin',
+ })
+ expect(resolved?.external).toBe(true)
+ })
+
+ test('declared builtin regexp', async () => {
+ const resolved = await run({
+ builtins: [/^my-env:\w/],
+ idToResolve: 'my-env:custom-builtin',
+ })
+ expect(resolved?.external).toBe(true)
+ })
+
+ test('non declared builtin', async () => {
+ const resolved = await run({
+ builtins: [
+ /* empty */
+ ],
+ idToResolve: 'my-env:custom-builtin',
+ })
+ expect(resolved).toBeNull()
+ })
+
+ test('non declared node builtin', async () => {
+ await expect(
+ run({
+ builtins: [
+ /* empty */
+ ],
+ idToResolve: 'node:fs',
+ }),
+ ).rejects.toThrowError(
+ /warning: Automatically externalized node built-in module "node:fs"/,
+ )
+ })
+
+ test('default to node-like builtins', async () => {
+ const resolved = await run({
+ idToResolve: 'node:fs',
+ })
+ expect(resolved?.external).toBe(true)
+ })
+
+ test('default to node-like builtins for ssr environment', async () => {
+ const resolved = await run({
+ idToResolve: 'node:fs',
+ testEnv: 'ssr',
+ })
+ expect(resolved?.external).toBe(true)
+ })
+
+ test('no default to node-like builtins for client environment', async () => {
+ const resolved = await run({
+ idToResolve: 'node:fs',
+ testEnv: 'client',
+ })
+ expect(resolved?.id).toEqual('__vite-browser-external:node:fs')
+ })
+
+ test('no builtins overriding for client environment', async () => {
+ const resolved = await run({
+ idToResolve: 'node:fs',
+ testEnv: 'client',
+ targetEnv: 'client',
+ })
+ expect(resolved?.id).toEqual('__vite-browser-external:node:fs')
+ })
+
+ test('declared node builtin', async () => {
+ const resolved = await run({
+ builtins: [/^node:/],
+ idToResolve: 'node:fs',
+ })
+ expect(resolved?.external).toBe(true)
+ })
+
+ test('declared builtin string in different environment', async () => {
+ const resolved = await run({
+ builtins: ['my-env:custom-builtin'],
+ idToResolve: 'my-env:custom-builtin',
+ targetEnv: 'custom',
+ testEnv: 'ssr',
+ })
+ expect(resolved).toBe(null)
+ })
+ })
+
+ test('build', async () => {
+ await build({
+ ...getConfig(),
+ build: {
+ ssr: true,
+ outDir: 'dist/basic',
+ rollupOptions: {
+ input: { index: fileUrl.href },
+ },
+ },
+ })
+ const mod1 = await import(
+ join(import.meta.dirname, 'fixtures/file-url/dist/basic/index.js')
+ )
+ expect(mod1.default).toBe('ok')
+
+ await build({
+ ...getConfig(),
+ build: {
+ ssr: true,
+ outDir: 'dist/virtual',
+ rollupOptions: {
+ input: { index: 'virtual:test-dep/static' },
+ },
+ },
+ })
+ const mod2 = await import(
+ join(import.meta.dirname, 'fixtures/file-url/dist/virtual/index.js')
+ )
+ expect(mod2.default.default).toBe('ok')
+ })
+})
diff --git a/packages/vite/src/node/__tests__/runnerImport.spec.ts b/packages/vite/src/node/__tests__/runnerImport.spec.ts
new file mode 100644
index 00000000000000..4b646d8edf3acf
--- /dev/null
+++ b/packages/vite/src/node/__tests__/runnerImport.spec.ts
@@ -0,0 +1,78 @@
+import { resolve } from 'node:path'
+import { describe, expect, test } from 'vitest'
+import { loadConfigFromFile } from 'vite'
+import { runnerImport } from '../ssr/runnerImport'
+import { slash } from '../../shared/utils'
+
+// eslint-disable-next-line n/no-unsupported-features/node-builtins
+const isTypeStrippingSupported = !!process.features.typescript
+
+describe('importing files using inlined environment', () => {
+ const fixture = (name: string) =>
+ resolve(import.meta.dirname, './fixtures/runner-import', name)
+
+ test('importing a basic file works', async () => {
+ const { module } = await runnerImport<
+ typeof import('./fixtures/runner-import/basic')
+ >(fixture('basic'))
+ expect(module.test).toEqual({
+ field: true,
+ })
+ })
+
+ test("cannot import cjs, 'runnerImport' doesn't support CJS syntax at all", async () => {
+ await expect(() =>
+ runnerImport(
+ fixture('cjs.js'),
+ ),
+ ).rejects.toThrow('module is not defined')
+ })
+
+ test('can import vite config', async () => {
+ const { module, dependencies } = await runnerImport<
+ typeof import('./fixtures/runner-import/vite.config')
+ >(fixture('vite.config'))
+ expect(module.default).toEqual({
+ root: './test',
+ plugins: [
+ {
+ name: 'test',
+ },
+ ],
+ })
+ expect(dependencies).toEqual([slash(fixture('plugin.ts'))])
+ })
+
+ test('can import vite config that imports a TS external module', async () => {
+ const { module, dependencies } = await runnerImport<
+ typeof import('./fixtures/runner-import/vite.config.outside-pkg-import.mjs')
+ >(fixture('vite.config.outside-pkg-import.mts'))
+
+ expect(module.default.__injected).toBe(true)
+ expect(dependencies).toEqual([
+ slash(resolve(import.meta.dirname, './packages/parent/index.ts')),
+ ])
+
+ // confirm that it fails with a bundle approach
+ if (!isTypeStrippingSupported) {
+ await expect(async () => {
+ const root = resolve(import.meta.dirname, './fixtures/runner-import')
+ await loadConfigFromFile(
+ { mode: 'production', command: 'serve' },
+ resolve(root, './vite.config.outside-pkg-import.mts'),
+ root,
+ 'silent',
+ )
+ }).rejects.toThrow('Unknown file extension ".ts"')
+ }
+ })
+
+ test('dynamic import', async () => {
+ const { module } = await runnerImport(fixture('dynamic-import.ts'))
+ await expect(() => module.default()).rejects.toMatchInlineSnapshot(
+ `[Error: Vite module runner has been closed.]`,
+ )
+ // const dep = await module.default();
+ // expect(dep.default).toMatchInlineSnapshot(`"ok"`)
+ })
+})
diff --git a/packages/vite/src/node/__tests__/scan.spec.ts b/packages/vite/src/node/__tests__/scan.spec.ts
index db11bcc45b284c..b79930393913d3 100644
--- a/packages/vite/src/node/__tests__/scan.spec.ts
+++ b/packages/vite/src/node/__tests__/scan.spec.ts
@@ -1,5 +1,14 @@
-import { scriptRE, commentRE, importsRE } from '../optimizer/scan'
+import path from 'node:path'
+import { describe, expect, test } from 'vitest'
+import {
+ commentRE,
+ devToScanEnvironment,
+ importsRE,
+ scanImports,
+ scriptRE,
+} from '../optimizer/scan'
import { multilineCommentsRE, singlelineCommentsRE } from '../utils'
+import { createServer, createServerModuleRunner } from '..'
describe('optimizer-scan:script-test', () => {
const scriptContent = `import { defineComponent } from 'vue'
@@ -13,15 +22,15 @@ describe('optimizer-scan:script-test', () => {
test('component return value test', () => {
scriptRE.lastIndex = 0
const [, tsOpenTag, tsContent] = scriptRE.exec(
- ``
- )
+ ``,
+ )!
expect(tsOpenTag).toEqual('`
- )
+ ``,
+ )!
expect(openTag).toEqual(' -->
- `.replace(commentRE, '')
+ `.replace(commentRE, ''),
)
expect(ret).toEqual(null)
})
@@ -44,25 +53,29 @@ describe('optimizer-scan:script-test', () => {
scriptRE.lastIndex = 0
ret = scriptRE.exec(
- ` `
+ ` `,
)
expect(ret).toBe(null)
scriptRE.lastIndex = 0
ret = scriptRE.exec(
- ` content `
+ ` content `,
)
expect(ret).toBe(null)
})
test('ordinary script tag test', () => {
scriptRE.lastIndex = 0
- const [, tag, content] = scriptRE.exec(``)
+ const [, tag, content] = scriptRE.exec(
+ ``,
+ )!
expect(tag).toEqual('`)
+ const [, tag1, content1] = scriptRE.exec(
+ ``,
+ )!
expect(tag1).toEqual('
- const filePath = id.replace(normalizePath(config.root), '')
- addToHTMLProxyCache(
- config,
- filePath,
- inlineModuleIndex,
- contents
+ if (node.nodeName === 'script') {
+ const { src, srcSourceCodeLocation, isModule, isAsync, isIgnored } =
+ getScriptInfo(node)
+
+ if (isIgnored) {
+ removeViteIgnoreAttr(s, node.sourceCodeLocation!)
+ } else {
+ const url = src && src.value
+ const isPublicFile = !!(url && checkPublicFile(url, config))
+ if (isPublicFile) {
+ // referencing public dir url, prefix with base
+ overwriteAttrValue(
+ s,
+ srcSourceCodeLocation!,
+ partialEncodeURIPath(toOutputPublicFilePath(url)),
)
- js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"`
- shouldRemove = true
}
- everyScriptIsAsync &&= isAsync
- someScriptsAreAsync ||= isAsync
- someScriptsAreDefer ||= !isAsync
- } else if (url && !isPublicFile) {
- if (!isExcludedUrl(url)) {
- config.logger.warn(
- `
+ const filePath = id.replace(normalizePath(config.root), '')
+ addToHTMLProxyCache(config, filePath, inlineModuleIndex, {
+ code: contents,
+ })
+ js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"`
+ shouldRemove = true
+ }
+
+ everyScriptIsAsync &&= isAsync
+ someScriptsAreAsync ||= isAsync
+ someScriptsAreDefer ||= !isAsync
+ } else if (url && !isPublicFile) {
+ if (!isExcludedUrl(url)) {
+ config.logger.warn(
+ ` asset
for (const { start, end, url } of scriptUrls) {
- if (!isExcludedUrl(url)) {
- s.overwrite(start, end, await urlToBuiltUrl(url, id, config, this))
- } else if (checkPublicFile(url, config)) {
- s.overwrite(start, end, config.base + url.slice(1))
+ if (checkPublicFile(url, config)) {
+ s.update(
+ start,
+ end,
+ partialEncodeURIPath(toOutputPublicFilePath(url)),
+ )
+ } else if (!isExcludedUrl(url)) {
+ s.update(
+ start,
+ end,
+ partialEncodeURIPath(await urlToBuiltUrl(this, url, id)),
+ )
+ }
+ }
+
+ // ignore if its url can't be resolved
+ const resolvedStyleUrls = await Promise.all(
+ styleUrls.map(async (styleUrl) => ({
+ ...styleUrl,
+ resolved: await this.resolve(styleUrl.url, id),
+ })),
+ )
+ for (const { start, end, url, resolved } of resolvedStyleUrls) {
+ if (resolved == null) {
+ config.logger.warnOnce(
+ `\n${url} doesn't exist at build time, it will remain unchanged to be resolved at runtime`,
+ )
+ const importExpression = `\nimport ${JSON.stringify(url)}`
+ js = js.replace(importExpression, '')
+ } else {
+ s.remove(start, end)
}
}
- processedHtml.set(id, s.toString())
+ processedHtml(this).set(id, s.toString())
// inject module preload polyfill only when configured and needed
+ const { modulePreload } = this.environment.config.build
if (
- config.build.polyfillModulePreload &&
+ modulePreload !== false &&
+ modulePreload.polyfill &&
(someScriptsAreAsync || someScriptsAreDefer)
) {
js = `import "${modulePreloadPolyfillId}";\n${js}`
}
- return js
- }
+ await Promise.all(setModuleSideEffectPromises)
+
+ // Force rollup to keep this module from being shared between other entry points.
+ // If the resulting chunk is empty, it will be removed in generateBundle.
+ return { code: js, moduleSideEffects: 'no-treeshake' }
+ },
},
async generateBundle(options, bundle) {
- const analyzedChunk: Map = new Map()
+ const analyzedImportedCssFiles = new Map()
+ const inlineEntryChunk = new Set()
const getImportedChunks = (
chunk: OutputChunk,
- seen: Set = new Set()
- ): OutputChunk[] => {
- const chunks: OutputChunk[] = []
+ seen: Set = new Set(),
+ ): (OutputChunk | string)[] => {
+ const chunks: (OutputChunk | string)[] = []
chunk.imports.forEach((file) => {
const importee = bundle[file]
- if (importee?.type === 'chunk' && !seen.has(file)) {
- seen.add(file)
+ if (importee) {
+ if (importee.type === 'chunk' && !seen.has(file)) {
+ seen.add(file)
- // post-order traversal
- chunks.push(...getImportedChunks(importee, seen))
- chunks.push(importee)
+ // post-order traversal
+ chunks.push(...getImportedChunks(importee, seen))
+ chunks.push(importee)
+ }
+ } else {
+ // external imports
+ chunks.push(file)
}
})
return chunks
}
const toScriptTag = (
- chunk: OutputChunk,
- isAsync: boolean
+ chunkOrUrl: OutputChunk | string,
+ toOutputPath: (filename: string) => string,
+ isAsync: boolean,
): HtmlTagDescriptor => ({
tag: 'script',
attrs: {
...(isAsync ? { async: true } : {}),
type: 'module',
+ // crossorigin must be set not only for serving assets in a different origin
+ // but also to make it possible to preload the script using ` `.
+ // `
diff --git a/packages/vite/src/node/server/middlewares/hostCheck.ts b/packages/vite/src/node/server/middlewares/hostCheck.ts
new file mode 100644
index 00000000000000..af366de279b61a
--- /dev/null
+++ b/packages/vite/src/node/server/middlewares/hostCheck.ts
@@ -0,0 +1,62 @@
+import { hostValidationMiddleware as originalHostValidationMiddleware } from 'host-validation-middleware'
+import type { Connect } from '#dep-types/connect'
+import type { ResolvedPreviewOptions, ResolvedServerOptions } from '../..'
+
+export function getAdditionalAllowedHosts(
+ resolvedServerOptions: Pick,
+ resolvedPreviewOptions: Pick,
+): string[] {
+ const list = []
+
+ // allow host option by default as that indicates that the user is
+ // expecting Vite to respond on that host
+ if (
+ typeof resolvedServerOptions.host === 'string' &&
+ resolvedServerOptions.host
+ ) {
+ list.push(resolvedServerOptions.host)
+ }
+ if (
+ typeof resolvedServerOptions.hmr === 'object' &&
+ resolvedServerOptions.hmr.host
+ ) {
+ list.push(resolvedServerOptions.hmr.host)
+ }
+ if (
+ typeof resolvedPreviewOptions.host === 'string' &&
+ resolvedPreviewOptions.host
+ ) {
+ list.push(resolvedPreviewOptions.host)
+ }
+
+ // allow server origin by default as that indicates that the user is
+ // expecting Vite to respond on that host
+ if (resolvedServerOptions.origin) {
+ // some frameworks may pass the origin as a placeholder, so it's not
+ // possible to parse as URL, so use a try-catch here as a best effort
+ try {
+ const serverOriginUrl = new URL(resolvedServerOptions.origin)
+ list.push(serverOriginUrl.hostname)
+ } catch {}
+ }
+
+ return list
+}
+
+export function hostValidationMiddleware(
+ allowedHosts: string[],
+ isPreview: boolean,
+): Connect.NextHandleFunction {
+ return originalHostValidationMiddleware({
+ // Freeze the array to allow caching
+ allowedHosts: Object.freeze([...allowedHosts]),
+ generateErrorMessage(hostname) {
+ const hostnameWithQuotes = JSON.stringify(hostname)
+ const optionName = `${isPreview ? 'preview' : 'server'}.allowedHosts`
+ return (
+ `Blocked request. This host (${hostnameWithQuotes}) is not allowed.\n` +
+ `To allow this host, add ${hostnameWithQuotes} to \`${optionName}\` in vite.config.js.`
+ )
+ },
+ })
+}
diff --git a/packages/vite/src/node/server/middlewares/htmlFallback.ts b/packages/vite/src/node/server/middlewares/htmlFallback.ts
new file mode 100644
index 00000000000000..24d08f9537171c
--- /dev/null
+++ b/packages/vite/src/node/server/middlewares/htmlFallback.ts
@@ -0,0 +1,91 @@
+import path from 'node:path'
+import fs from 'node:fs'
+import type { Connect } from '#dep-types/connect'
+import { createDebugger, joinUrlSegments } from '../../utils'
+import { cleanUrl } from '../../../shared/utils'
+import type { DevEnvironment } from '../environment'
+import { FullBundleDevEnvironment } from '../environments/fullBundleEnvironment'
+
+const debug = createDebugger('vite:html-fallback')
+
+export function htmlFallbackMiddleware(
+ root: string,
+ spaFallback: boolean,
+ clientEnvironment?: DevEnvironment,
+): Connect.NextHandleFunction {
+ const memoryFiles =
+ clientEnvironment instanceof FullBundleDevEnvironment
+ ? clientEnvironment.memoryFiles
+ : undefined
+
+ function checkFileExists(relativePath: string) {
+ return (
+ memoryFiles?.has(
+ relativePath.slice(1), // remove first /
+ ) ?? fs.existsSync(path.join(root, relativePath))
+ )
+ }
+
+ // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
+ return function viteHtmlFallbackMiddleware(req, _res, next) {
+ if (
+ // Only accept GET or HEAD
+ (req.method !== 'GET' && req.method !== 'HEAD') ||
+ // Exclude default favicon requests
+ req.url === '/favicon.ico' ||
+ // Require Accept: text/html or */*
+ !(
+ req.headers.accept === undefined || // equivalent to `Accept: */*`
+ req.headers.accept === '' || // equivalent to `Accept: */*`
+ req.headers.accept.includes('text/html') ||
+ req.headers.accept.includes('*/*')
+ )
+ ) {
+ return next()
+ }
+
+ const url = cleanUrl(req.url!)
+ let pathname
+ try {
+ pathname = decodeURIComponent(url)
+ } catch {
+ // ignore malformed URI
+ return next()
+ }
+
+ // .html files are not handled by serveStaticMiddleware
+ // so we need to check if the file exists
+ if (pathname.endsWith('.html')) {
+ if (checkFileExists(pathname)) {
+ debug?.(`Rewriting ${req.method} ${req.url} to ${url}`)
+ req.url = url
+ return next()
+ }
+ }
+ // trailing slash should check for fallback index.html
+ else if (pathname.endsWith('/')) {
+ if (checkFileExists(joinUrlSegments(pathname, 'index.html'))) {
+ const newUrl = url + 'index.html'
+ debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`)
+ req.url = newUrl
+ return next()
+ }
+ }
+ // non-trailing slash should check for fallback .html
+ else {
+ if (checkFileExists(pathname + '.html')) {
+ const newUrl = url + '.html'
+ debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`)
+ req.url = newUrl
+ return next()
+ }
+ }
+
+ if (spaFallback) {
+ debug?.(`Rewriting ${req.method} ${req.url} to /index.html`)
+ req.url = '/index.html'
+ }
+
+ next()
+ }
+}
diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts
index 3c76d94c526930..0e9c48518cac45 100644
--- a/packages/vite/src/node/server/middlewares/indexHtml.ts
+++ b/packages/vite/src/node/server/middlewares/indexHtml.ts
@@ -1,35 +1,107 @@
-import fs from 'fs'
-import path from 'path'
+import fs from 'node:fs'
+import fsp from 'node:fs/promises'
+import path from 'node:path'
import MagicString from 'magic-string'
-import type { AttributeNode, ElementNode } from '@vue/compiler-dom'
-import { NodeTypes } from '@vue/compiler-dom'
-import type { Connect } from 'types/connect'
+import type { SourceMapInput } from 'rolldown'
+import type { DefaultTreeAdapterMap, Token } from 'parse5'
+import type { Connect } from '#dep-types/connect'
import type { IndexHtmlTransformHook } from '../../plugins/html'
import {
addToHTMLProxyCache,
applyHtmlTransforms,
- assetAttrsConfig,
+ extractImportExpressionFromClassicScript,
+ findNeedTransformStyleAttribute,
getScriptInfo,
+ htmlEnvHook,
+ htmlProxyResult,
+ injectCspNonceMetaTagHook,
+ injectNonceAttributeTagHook,
+ nodeIsElement,
+ overwriteAttrValue,
+ postImportMapHook,
+ preImportMapHook,
+ removeViteIgnoreAttr,
resolveHtmlTransforms,
- traverseHtml
+ traverseHtml,
} from '../../plugins/html'
-import type { ResolvedConfig, ViteDevServer } from '../..'
+import type { PreviewServer, ResolvedConfig, ViteDevServer } from '../..'
import { send } from '../send'
import { CLIENT_PUBLIC_PATH, FS_PREFIX } from '../../constants'
-import { cleanUrl, fsPathFromId, normalizePath, injectQuery } from '../../utils'
-import type { ModuleGraph } from '../moduleGraph'
+import {
+ ensureWatchedFile,
+ fsPathFromId,
+ getHash,
+ injectQuery,
+ isCSSRequest,
+ isDevServer,
+ isJSRequest,
+ isParentDirectory,
+ joinUrlSegments,
+ normalizePath,
+ processSrcSetSync,
+ stripBase,
+} from '../../utils'
+import { checkPublicFile } from '../../publicDir'
+import { getCodeWithSourcemap, injectSourcesContent } from '../sourcemap'
+import { cleanUrl, unwrapId, wrapId } from '../../../shared/utils'
+import { getNodeAssetAttributes } from '../../assetSource'
+import {
+ BasicMinimalPluginContext,
+ basePluginContextMeta,
+} from '../pluginContainer'
+import { FullBundleDevEnvironment } from '../environments/fullBundleEnvironment'
+import { getHmrImplementation } from '../../plugins/clientInjections'
+import { checkLoadingAccess, respondWithAccessDenied } from './static'
-export function createDevHtmlTransformFn(
- server: ViteDevServer
-): (url: string, html: string, originalUrl: string) => Promise {
- const [preHooks, postHooks] = resolveHtmlTransforms(server.config.plugins)
+interface AssetNode {
+ start: number
+ end: number
+ code: string
+}
+
+interface InlineStyleAttribute {
+ index: number
+ location: Token.Location
+ code: string
+}
- return (url: string, html: string, originalUrl: string): Promise => {
- return applyHtmlTransforms(html, [...preHooks, devHtmlHook, ...postHooks], {
+export function createDevHtmlTransformFn(
+ config: ResolvedConfig,
+): (
+ server: ViteDevServer,
+ url: string,
+ html: string,
+ originalUrl?: string,
+) => Promise {
+ const [preHooks, normalHooks, postHooks] = resolveHtmlTransforms(
+ config.plugins,
+ )
+ const transformHooks = [
+ preImportMapHook(config),
+ injectCspNonceMetaTagHook(config),
+ ...preHooks,
+ htmlEnvHook(config),
+ devHtmlHook,
+ ...normalHooks,
+ ...postHooks,
+ injectNonceAttributeTagHook(config),
+ postImportMapHook(),
+ ]
+ const pluginContext = new BasicMinimalPluginContext(
+ { ...basePluginContextMeta, watchMode: true },
+ config.logger,
+ )
+ return (
+ server: ViteDevServer,
+ url: string,
+ html: string,
+ originalUrl?: string,
+ ): Promise => {
+ return applyHtmlTransforms(html, transformHooks, pluginContext, {
path: url,
filename: getHtmlFilename(url, server),
server,
- originalUrl
+ originalUrl,
})
}
}
@@ -38,130 +110,318 @@ function getHtmlFilename(url: string, server: ViteDevServer) {
if (url.startsWith(FS_PREFIX)) {
return decodeURIComponent(fsPathFromId(url))
} else {
- return decodeURIComponent(path.join(server.config.root, url.slice(1)))
+ return decodeURIComponent(
+ normalizePath(path.join(server.config.root, url.slice(1))),
+ )
}
}
-const startsWithSingleSlashRE = /^\/(?!\/)/
+function shouldPreTransform(url: string, config: ResolvedConfig) {
+ return (
+ !checkPublicFile(url, config) && (isJSRequest(url) || isCSSRequest(url))
+ )
+}
+
+const wordCharRE = /\w/
+
+function isBareRelative(url: string) {
+ return wordCharRE.test(url[0]) && !url.includes(':')
+}
+
const processNodeUrl = (
- node: AttributeNode,
- s: MagicString,
+ url: string,
+ useSrcSetReplacer: boolean,
config: ResolvedConfig,
htmlPath: string,
originalUrl?: string,
- moduleGraph?: ModuleGraph
-) => {
- let url = node.value?.content || ''
+ server?: ViteDevServer,
+ isClassicScriptLink?: boolean,
+): string => {
+ // prefix with base (dev only, base is never relative)
+ const replacer = (url: string) => {
+ if (
+ (url[0] === '/' && url[1] !== '/') ||
+ // #3230 if some request url (localhost:3000/a/b) return to fallback html, the relative assets
+ // path will add `/a/` prefix, it will caused 404.
+ //
+ // skip if url contains `:` as it implies a url protocol or Windows path that we don't want to replace.
+ //
+ // rewrite `./index.js` -> `localhost:5173/a/index.js`.
+ // rewrite `../index.js` -> `localhost:5173/index.js`.
+ // rewrite `relative/index.js` -> `localhost:5173/a/relative/index.js`.
+ ((url[0] === '.' || isBareRelative(url)) &&
+ originalUrl &&
+ originalUrl !== '/' &&
+ htmlPath === '/index.html')
+ ) {
+ url = path.posix.join(config.base, url)
+ }
+
+ let preTransformUrl: string | undefined
- if (moduleGraph) {
- const mod = moduleGraph.urlToModuleMap.get(url)
- if (mod && mod.lastHMRTimestamp > 0) {
- url = injectQuery(url, `t=${mod.lastHMRTimestamp}`)
+ if (!isClassicScriptLink && shouldPreTransform(url, config)) {
+ if (url[0] === '/' && url[1] !== '/') {
+ preTransformUrl = url
+ } else if (url[0] === '.' || isBareRelative(url)) {
+ preTransformUrl = path.posix.join(
+ config.base,
+ path.posix.dirname(htmlPath),
+ url,
+ )
+ }
}
+
+ if (server) {
+ const mod = server.environments.client.moduleGraph.urlToModuleMap.get(
+ preTransformUrl || url,
+ )
+ if (mod && mod.lastHMRTimestamp > 0) {
+ url = injectQuery(url, `t=${mod.lastHMRTimestamp}`)
+ }
+ }
+
+ if (server && preTransformUrl) {
+ try {
+ preTransformUrl = decodeURI(preTransformUrl)
+ } catch {
+ // Malformed uri. Skip pre-transform.
+ return url
+ }
+ preTransformRequest(server, preTransformUrl, config.decodedBase)
+ }
+
+ return url
}
- if (startsWithSingleSlashRE.test(url)) {
- // prefix with base
- s.overwrite(
- node.value!.loc.start.offset,
- node.value!.loc.end.offset,
- `"${config.base + url.slice(1)}"`
- )
- } else if (
- url.startsWith('.') &&
- originalUrl &&
- originalUrl !== '/' &&
- htmlPath === '/index.html'
- ) {
- // #3230 if some request url (localhost:3000/a/b) return to fallback html, the relative assets
- // path will add `/a/` prefix, it will caused 404.
- // rewrite before `./index.js` -> `localhost:3000/a/index.js`.
- // rewrite after `../index.js` -> `localhost:3000/index.js`.
- s.overwrite(
- node.value!.loc.start.offset,
- node.value!.loc.end.offset,
- `"${path.posix.join(
- path.posix.relative(originalUrl, '/'),
- url.slice(1)
- )}"`
- )
- }
+
+ const processedUrl = useSrcSetReplacer
+ ? processSrcSetSync(url, ({ url }) => replacer(url))
+ : replacer(url)
+ return processedUrl
}
const devHtmlHook: IndexHtmlTransformHook = async (
html,
- { path: htmlPath, server, originalUrl }
+ { path: htmlPath, filename, server, originalUrl },
) => {
- const { config, moduleGraph } = server!
+ const { config, watcher } = server!
const base = config.base || '/'
+ const decodedBase = config.decodedBase || '/'
+
+ let proxyModulePath: string
+ let proxyModuleUrl: string
+
+ const trailingSlash = htmlPath.endsWith('/')
+ if (!trailingSlash && fs.existsSync(filename)) {
+ proxyModulePath = htmlPath
+ proxyModuleUrl = proxyModulePath
+ } else {
+ // There are users of vite.transformIndexHtml calling it with url '/'
+ // for SSR integrations #7993, filename is root for this case
+ // A user may also use a valid name for a virtual html file
+ // Mark the path as virtual in both cases so sourcemaps aren't processed
+ // and ids are properly handled
+ const validPath = `${htmlPath}${trailingSlash ? 'index.html' : ''}`
+ proxyModulePath = `\0${validPath}`
+ proxyModuleUrl = wrapId(proxyModulePath)
+ }
+ proxyModuleUrl = joinUrlSegments(decodedBase, proxyModuleUrl)
const s = new MagicString(html)
let inlineModuleIndex = -1
- const filePath = cleanUrl(htmlPath)
+ // The key to the proxyHtml cache is decoded, as it will be compared
+ // against decoded URLs by the HTML plugins.
+ const proxyCacheUrl = decodeURI(
+ cleanUrl(proxyModulePath).replace(normalizePath(config.root), ''),
+ )
+ const styleUrl: AssetNode[] = []
+ const inlineStyles: InlineStyleAttribute[] = []
+ const inlineModulePaths: string[] = []
- const addInlineModule = (node: ElementNode, ext: 'js' | 'css') => {
+ const addInlineModule = (
+ node: DefaultTreeAdapterMap['element'],
+ ext: 'js',
+ ) => {
inlineModuleIndex++
- const url = filePath.replace(normalizePath(config.root), '')
+ const contentNode = node.childNodes[0] as DefaultTreeAdapterMap['textNode']
- const contents = node.children
- .map((child: any) => child.content || '')
- .join('')
+ const code = contentNode.value
- // add HTML Proxy to Map
- addToHTMLProxyCache(config, url, inlineModuleIndex, contents)
+ let map: SourceMapInput | undefined
+ if (proxyModulePath[0] !== '\0') {
+ map = new MagicString(html)
+ .snip(
+ contentNode.sourceCodeLocation!.startOffset,
+ contentNode.sourceCodeLocation!.endOffset,
+ )
+ .generateMap({ hires: 'boundary' })
+ map.sources = [filename]
+ map.file = filename
+ }
- // inline js module. convert to src="proxy"
- const modulePath = `${
- config.base + htmlPath.slice(1)
- }?html-proxy&index=${inlineModuleIndex}.${ext}`
+ // add HTML Proxy to Map
+ addToHTMLProxyCache(config, proxyCacheUrl, inlineModuleIndex, { code, map })
- // invalidate the module so the newly cached contents will be served
- const module = server?.moduleGraph.getModuleById(modulePath)
- if (module) {
- server?.moduleGraph.invalidateModule(module)
- }
+ // inline js module. convert to src="proxy" (dev only, base is never relative)
+ const modulePath = `${proxyModuleUrl}?html-proxy&index=${inlineModuleIndex}.${ext}`
+ inlineModulePaths.push(modulePath)
- s.overwrite(
- node.loc.start.offset,
- node.loc.end.offset,
- ``
+ s.update(
+ node.sourceCodeLocation!.startOffset,
+ node.sourceCodeLocation!.endOffset,
+ ``,
)
+ preTransformRequest(server!, modulePath, decodedBase)
}
- await traverseHtml(html, htmlPath, (node) => {
- if (node.type !== NodeTypes.ELEMENT) {
+ await traverseHtml(html, filename, config.logger.warn, (node) => {
+ if (!nodeIsElement(node)) {
return
}
// script tags
- if (node.tag === 'script') {
- const { src, isModule } = getScriptInfo(node)
+ if (node.nodeName === 'script') {
+ const { src, srcSourceCodeLocation, isModule, isIgnored } =
+ getScriptInfo(node)
- if (src) {
- processNodeUrl(src, s, config, htmlPath, originalUrl, moduleGraph)
- } else if (isModule) {
+ if (isIgnored) {
+ removeViteIgnoreAttr(s, node.sourceCodeLocation!)
+ } else if (src) {
+ const processedUrl = processNodeUrl(
+ src.value,
+ /* useSrcSetReplacer */ false,
+ config,
+ htmlPath,
+ originalUrl,
+ server,
+ !isModule,
+ )
+ if (processedUrl !== src.value) {
+ overwriteAttrValue(s, srcSourceCodeLocation!, processedUrl)
+ }
+ } else if (isModule && node.childNodes.length) {
addInlineModule(node, 'js')
+ } else if (node.childNodes.length) {
+ const scriptNode = node.childNodes[
+ node.childNodes.length - 1
+ ] as DefaultTreeAdapterMap['textNode']
+ for (const {
+ url,
+ start,
+ end,
+ } of extractImportExpressionFromClassicScript(scriptNode)) {
+ const processedUrl = processNodeUrl(
+ url,
+ false,
+ config,
+ htmlPath,
+ originalUrl,
+ )
+ if (processedUrl !== url) {
+ s.update(start, end, processedUrl)
+ }
+ }
}
}
- if (node.tag === 'style' && node.children.length) {
- addInlineModule(node, 'css')
+ const inlineStyle = findNeedTransformStyleAttribute(node)
+ if (inlineStyle) {
+ inlineModuleIndex++
+ inlineStyles.push({
+ index: inlineModuleIndex,
+ location: inlineStyle.location!,
+ code: inlineStyle.attr.value,
+ })
+ }
+
+ if (node.nodeName === 'style' && node.childNodes.length) {
+ const children = node.childNodes[0] as DefaultTreeAdapterMap['textNode']
+ styleUrl.push({
+ start: children.sourceCodeLocation!.startOffset,
+ end: children.sourceCodeLocation!.endOffset,
+ code: children.value,
+ })
}
// elements with [href/src] attrs
- const assetAttrs = assetAttrsConfig[node.tag]
- if (assetAttrs) {
- for (const p of node.props) {
- if (
- p.type === NodeTypes.ATTRIBUTE &&
- p.value &&
- assetAttrs.includes(p.name)
- ) {
- processNodeUrl(p, s, config, htmlPath, originalUrl)
+ const assetAttributes = getNodeAssetAttributes(node)
+ for (const attr of assetAttributes) {
+ if (attr.type === 'remove') {
+ s.remove(attr.location.startOffset, attr.location.endOffset)
+ } else {
+ const processedUrl = processNodeUrl(
+ attr.value,
+ attr.type === 'srcset',
+ config,
+ htmlPath,
+ originalUrl,
+ )
+ if (processedUrl !== attr.value) {
+ overwriteAttrValue(s, attr.location, processedUrl)
}
}
}
})
+ // invalidate the module so the newly cached contents will be served
+ const clientModuleGraph = server?.environments.client.moduleGraph
+ if (clientModuleGraph) {
+ await Promise.all(
+ inlineModulePaths.map(async (url) => {
+ const module = await clientModuleGraph.getModuleByUrl(url)
+ if (module) {
+ clientModuleGraph.invalidateModule(module)
+ }
+ }),
+ )
+ }
+
+ await Promise.all([
+ ...styleUrl.map(async ({ start, end, code }, index) => {
+ const url = `${proxyModulePath}?html-proxy&direct&index=${index}.css`
+
+ // ensure module in graph after successful load
+ const mod =
+ await server!.environments.client.moduleGraph.ensureEntryFromUrl(
+ url,
+ false,
+ )
+ ensureWatchedFile(watcher, mod.file, config.root)
+
+ const result =
+ await server!.environments.client.pluginContainer.transform(
+ code,
+ mod.id!,
+ )
+ let content = ''
+ if (result.map && 'version' in result.map) {
+ if (result.map.mappings) {
+ await injectSourcesContent(result.map, proxyModulePath, config.logger)
+ }
+ content = getCodeWithSourcemap('css', result.code, result.map)
+ } else {
+ content = result.code
+ }
+ s.overwrite(start, end, content)
+ }),
+ ...inlineStyles.map(async ({ index, location, code }) => {
+ // will transform with css plugin and cache result with css-post plugin
+ const url = `${proxyModulePath}?html-proxy&inline-css&style-attr&index=${index}.css`
+
+ const mod =
+ await server!.environments.client.moduleGraph.ensureEntryFromUrl(
+ url,
+ false,
+ )
+ ensureWatchedFile(watcher, mod.file, config.root)
+
+ await server?.environments.client.pluginContainer.transform(code, mod.id!)
+
+ const hash = getHash(cleanUrl(mod.id!))
+ const result = htmlProxyResult.get(`${hash}_${index}`)
+ overwriteAttrValue(s, location, result ?? '')
+ }),
+ ])
+
html = s.toString()
return {
@@ -171,17 +431,24 @@ const devHtmlHook: IndexHtmlTransformHook = async (
tag: 'script',
attrs: {
type: 'module',
- src: path.posix.join(base, CLIENT_PUBLIC_PATH)
+ src: path.posix.join(base, CLIENT_PUBLIC_PATH),
},
- injectTo: 'head-prepend'
- }
- ]
+ injectTo: 'head-prepend',
+ },
+ ],
}
}
export function indexHtmlMiddleware(
- server: ViteDevServer
+ root: string,
+ server: ViteDevServer | PreviewServer,
): Connect.NextHandleFunction {
+ const isDev = isDevServer(server)
+ const fullBundleEnv =
+ isDev && server.environments.client instanceof FullBundleDevEnvironment
+ ? server.environments.client
+ : undefined
+
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return async function viteIndexHtmlMiddleware(req, res, next) {
if (res.writableEnded) {
@@ -189,16 +456,82 @@ export function indexHtmlMiddleware(
}
const url = req.url && cleanUrl(req.url)
- // spa-fallback always redirects to /index.html
+ // htmlFallbackMiddleware appends '.html' to URLs
if (url?.endsWith('.html') && req.headers['sec-fetch-dest'] !== 'script') {
- const filename = getHtmlFilename(url, server)
- if (fs.existsSync(filename)) {
+ if (fullBundleEnv) {
+ const pathname = decodeURIComponent(url)
+ const filePath = pathname.slice(1) // remove first /
+
+ let file = fullBundleEnv.memoryFiles.get(filePath)
+ if (!file && fullBundleEnv.memoryFiles.size !== 0) {
+ return next()
+ }
+ const secFetchDest = req.headers['sec-fetch-dest']
+ if (
+ [
+ 'document',
+ 'iframe',
+ 'frame',
+ 'fencedframe',
+ '',
+ undefined,
+ ].includes(secFetchDest) &&
+ ((await fullBundleEnv.triggerBundleRegenerationIfStale()) ||
+ file === undefined)
+ ) {
+ file = { source: await generateFallbackHtml(server as ViteDevServer) }
+ }
+ if (!file) {
+ return next()
+ }
+
+ const html =
+ typeof file.source === 'string'
+ ? file.source
+ : Buffer.from(file.source)
+ const headers = isDev
+ ? server.config.server.headers
+ : server.config.preview.headers
+ return send(req, res, html, 'html', { headers, etag: file.etag })
+ }
+
+ let filePath: string
+ if (isDev && url.startsWith(FS_PREFIX)) {
+ filePath = decodeURIComponent(fsPathFromId(url))
+ } else {
+ filePath = normalizePath(
+ path.resolve(path.join(root, decodeURIComponent(url))),
+ )
+ }
+
+ if (isDev) {
+ const servingAccessResult = checkLoadingAccess(server.config, filePath)
+ if (servingAccessResult === 'denied') {
+ return respondWithAccessDenied(filePath, server, res)
+ }
+ if (servingAccessResult === 'fallback') {
+ return next()
+ }
+ servingAccessResult satisfies 'allowed'
+ } else {
+ // `server.fs` options does not apply to the preview server.
+ // But we should disallow serving files outside the output directory.
+ if (!isParentDirectory(root, filePath)) {
+ return next()
+ }
+ }
+
+ if (fs.existsSync(filePath)) {
+ const headers = isDev
+ ? server.config.server.headers
+ : server.config.preview.headers
+
try {
- let html = fs.readFileSync(filename, 'utf-8')
- html = await server.transformIndexHtml(url, html, req.originalUrl)
- return send(req, res, html, 'html', {
- headers: server.config.server.headers
- })
+ let html = await fsp.readFile(filePath, 'utf-8')
+ if (isDev) {
+ html = await server.transformIndexHtml(url, html, req.originalUrl)
+ }
+ return send(req, res, html, 'html', { headers })
} catch (e) {
return next(e)
}
@@ -207,3 +540,80 @@ export function indexHtmlMiddleware(
next()
}
}
+
+// NOTE: We usually don't prefix `url` and `base` with `decoded`, but in this file particularly
+// we're dealing with mixed encoded/decoded paths often, so we make this explicit for now.
+function preTransformRequest(
+ server: ViteDevServer,
+ decodedUrl: string,
+ decodedBase: string,
+) {
+ if (!server.config.server.preTransformRequests) return
+
+ // transform all url as non-ssr as html includes client-side assets only
+ decodedUrl = unwrapId(stripBase(decodedUrl, decodedBase))
+ server.warmupRequest(decodedUrl)
+}
+
+async function generateFallbackHtml(server: ViteDevServer) {
+ const hmrRuntime = await getHmrImplementation(server.config)
+ return /* html */ `
+
+
+
+ ', '<\\/script>')}
+
+
+
+
+
+
Bundling in progress
+
The page will automatically reload when ready.
+
+
+
+
+`
+}
diff --git a/packages/vite/src/node/server/middlewares/memoryFiles.ts b/packages/vite/src/node/server/middlewares/memoryFiles.ts
new file mode 100644
index 00000000000000..152989eb031cb7
--- /dev/null
+++ b/packages/vite/src/node/server/middlewares/memoryFiles.ts
@@ -0,0 +1,52 @@
+import * as mrmime from 'mrmime'
+import type { Connect } from '#dep-types/connect'
+import { cleanUrl } from '../../../shared/utils'
+import type { ViteDevServer } from '..'
+import { FullBundleDevEnvironment } from '../environments/fullBundleEnvironment'
+
+export function memoryFilesMiddleware(
+ server: ViteDevServer,
+): Connect.NextHandleFunction {
+ const memoryFiles =
+ server.environments.client instanceof FullBundleDevEnvironment
+ ? server.environments.client.memoryFiles
+ : undefined
+ if (!memoryFiles) {
+ throw new Error('memoryFilesMiddleware can only be used for fullBundleMode')
+ }
+ const headers = server.config.server.headers
+
+ return function viteMemoryFilesMiddleware(req, res, next) {
+ const cleanedUrl = cleanUrl(req.url!)
+ if (cleanedUrl.endsWith('.html')) {
+ return next()
+ }
+
+ const pathname = decodeURIComponent(cleanedUrl)
+ const filePath = pathname.slice(1) // remove first /
+
+ const file = memoryFiles.get(filePath)
+ if (file) {
+ if (file.etag) {
+ if (req.headers['if-none-match'] === file.etag) {
+ res.statusCode = 304
+ res.end()
+ return
+ }
+ res.setHeader('Etag', file.etag)
+ }
+
+ const mime = mrmime.lookup(filePath)
+ if (mime) {
+ res.setHeader('Content-Type', mime)
+ }
+
+ for (const name in headers) {
+ res.setHeader(name, headers[name]!)
+ }
+
+ return res.end(file.source)
+ }
+ next()
+ }
+}
diff --git a/packages/vite/src/node/server/middlewares/notFound.ts b/packages/vite/src/node/server/middlewares/notFound.ts
new file mode 100644
index 00000000000000..38bf93b4a6df17
--- /dev/null
+++ b/packages/vite/src/node/server/middlewares/notFound.ts
@@ -0,0 +1,9 @@
+import type { Connect } from '#dep-types/connect'
+
+export function notFoundMiddleware(): Connect.NextHandleFunction {
+ // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
+ return function vite404Middleware(_, res) {
+ res.statusCode = 404
+ res.end()
+ }
+}
diff --git a/packages/vite/src/node/server/middlewares/proxy.ts b/packages/vite/src/node/server/middlewares/proxy.ts
index aa1100f13d5229..aade3270ff29fc 100644
--- a/packages/vite/src/node/server/middlewares/proxy.ts
+++ b/packages/vite/src/node/server/middlewares/proxy.ts
@@ -1,15 +1,14 @@
-import type * as http from 'http'
-import { createDebugger, isObject } from '../../utils'
-import httpProxy from 'http-proxy'
-import { HMR_HEADER } from '../ws'
-import type { Connect } from 'types/connect'
-import type { HttpProxy } from 'types/http-proxy'
+import type * as http from 'node:http'
+import * as httpProxy from 'http-proxy-3'
import colors from 'picocolors'
-import type { ResolvedConfig } from '../..'
+import type { Connect } from '#dep-types/connect'
+import { createDebugger } from '../../utils'
+import type { CommonServerOptions, ResolvedConfig } from '../..'
+import type { HttpServer } from '..'
const debug = createDebugger('vite:proxy')
-export interface ProxyOptions extends HttpProxy.ServerOptions {
+export interface ProxyOptions extends httpProxy.ServerOptions {
/**
* rewrite path
*/
@@ -17,61 +16,168 @@ export interface ProxyOptions extends HttpProxy.ServerOptions {
/**
* configure the proxy server (e.g. listen to events)
*/
- configure?: (proxy: HttpProxy.Server, options: ProxyOptions) => void
+ configure?: (proxy: httpProxy.ProxyServer, options: ProxyOptions) => void
/**
* webpack-dev-server style bypass function
*/
bypass?: (
req: http.IncomingMessage,
- res: http.ServerResponse,
- options: ProxyOptions
- ) => void | null | undefined | false | string
+ /** undefined for WebSocket upgrade requests */
+ res: http.ServerResponse | undefined,
+ options: ProxyOptions,
+ ) =>
+ | void
+ | null
+ | undefined
+ | false
+ | string
+ | Promise
+ /**
+ * rewrite the Origin header of a WebSocket request to match the target
+ *
+ * **Exercise caution as rewriting the Origin can leave the proxying open to [CSRF attacks](https://owasp.org/www-community/attacks/csrf).**
+ */
+ rewriteWsOrigin?: boolean | undefined
+}
+
+const rewriteOriginHeader = (
+ proxyReq: http.ClientRequest,
+ options: ProxyOptions,
+ config: ResolvedConfig,
+) => {
+ // Browsers may send Origin headers even with same-origin
+ // requests. It is common for WebSocket servers to check the Origin
+ // header, so if rewriteWsOrigin is true we change the Origin to match
+ // the target URL.
+ if (options.rewriteWsOrigin) {
+ const { target } = options
+
+ if (proxyReq.headersSent) {
+ config.logger.warn(
+ colors.yellow(
+ `Unable to rewrite Origin header as headers are already sent.`,
+ ),
+ )
+ return
+ }
+
+ if (proxyReq.getHeader('origin') && target) {
+ const changedOrigin =
+ typeof target === 'object'
+ ? `${target.protocol ?? 'http:'}//${target.host}`
+ : target
+
+ proxyReq.setHeader('origin', changedOrigin)
+ }
+ }
}
export function proxyMiddleware(
- httpServer: http.Server | null,
- config: ResolvedConfig
+ httpServer: HttpServer | null,
+ options: NonNullable,
+ config: ResolvedConfig,
): Connect.NextHandleFunction {
- const options = config.server.proxy!
-
// lazy require only when proxy is used
- const proxies: Record = {}
+ const proxies: Record = {}
Object.keys(options).forEach((context) => {
let opts = options[context]
+ if (!opts) {
+ return
+ }
if (typeof opts === 'string') {
- opts = { target: opts, changeOrigin: true } as ProxyOptions
+ opts = { target: opts, changeOrigin: true }
}
- const proxy = httpProxy.createProxyServer(opts) as HttpProxy.Server
-
- proxy.on('error', (err) => {
- config.logger.error(`${colors.red(`http proxy error:`)}\n${err.stack}`, {
- timestamp: true,
- error: err
- })
- })
+ const proxy = httpProxy.createProxyServer(opts)
if (opts.configure) {
opts.configure(proxy, opts)
}
+
+ proxy.on('error', (err, _req, res) => {
+ // When it is ws proxy, res is net.Socket
+ if ('req' in res) {
+ config.logger.error(
+ `${colors.red(`http proxy error: ${res.req.url}`)}\n${err.stack}`,
+ {
+ timestamp: true,
+ error: err,
+ },
+ )
+ if (!res.headersSent && !res.writableEnded) {
+ res
+ .writeHead(502, {
+ 'Content-Type': 'text/plain',
+ })
+ .end()
+ }
+ } else {
+ config.logger.error(`${colors.red(`ws proxy error:`)}\n${err.stack}`, {
+ timestamp: true,
+ error: err,
+ })
+ res.end()
+ }
+ })
+
+ proxy.on('proxyReqWs', (proxyReq, _req, socket, options) => {
+ rewriteOriginHeader(proxyReq, options, config)
+
+ socket.on('error', (err) => {
+ config.logger.error(
+ `${colors.red(`ws proxy socket error:`)}\n${err.stack}`,
+ {
+ timestamp: true,
+ error: err,
+ },
+ )
+ })
+ })
+
// clone before saving because http-proxy mutates the options
proxies[context] = [proxy, { ...opts }]
})
if (httpServer) {
- httpServer.on('upgrade', (req, socket, head) => {
+ httpServer.on('upgrade', async (req, socket, head) => {
const url = req.url!
for (const context in proxies) {
if (doesProxyContextMatchUrl(context, url)) {
const [proxy, opts] = proxies[context]
if (
- (opts.ws || opts.target?.toString().startsWith('ws:')) &&
- req.headers['sec-websocket-protocol'] !== HMR_HEADER
+ opts.ws ||
+ opts.target?.toString().startsWith('ws:') ||
+ opts.target?.toString().startsWith('wss:')
) {
+ if (opts.bypass) {
+ try {
+ const bypassResult = await opts.bypass(req, undefined, opts)
+ if (typeof bypassResult === 'string') {
+ debug?.(`bypass: ${req.url} -> ${bypassResult}`)
+ req.url = bypassResult
+ return
+ }
+ if (bypassResult === false) {
+ debug?.(`bypass: ${req.url} -> 404`)
+ socket.end('HTTP/1.1 404 Not Found\r\n\r\n', '')
+ return
+ }
+ } catch (err) {
+ config.logger.error(
+ `${colors.red(`ws proxy bypass error:`)}\n${err.stack}`,
+ {
+ timestamp: true,
+ error: err,
+ },
+ )
+ return
+ }
+ }
+
if (opts.rewrite) {
req.url = opts.rewrite(url)
}
- debug(`${req.url} -> ws ${opts.target}`)
+ debug?.(`${req.url} -> ws ${opts.target}`)
proxy.ws(req, socket, head)
return
}
@@ -81,30 +187,36 @@ export function proxyMiddleware(
}
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
- return function viteProxyMiddleware(req, res, next) {
+ return async function viteProxyMiddleware(req, res, next) {
const url = req.url!
for (const context in proxies) {
if (doesProxyContextMatchUrl(context, url)) {
const [proxy, opts] = proxies[context]
- const options: HttpProxy.ServerOptions = {}
+ const options: httpProxy.ServerOptions = {}
if (opts.bypass) {
- const bypassResult = opts.bypass(req, res, opts)
- if (typeof bypassResult === 'string') {
- req.url = bypassResult
- debug(`bypass: ${req.url} -> ${bypassResult}`)
- return next()
- } else if (isObject(bypassResult)) {
- Object.assign(options, bypassResult)
- debug(`bypass: ${req.url} use modified options: %O`, options)
- return next()
- } else if (bypassResult === false) {
- debug(`bypass: ${req.url} -> 404`)
- return res.end(404)
+ try {
+ const bypassResult = await opts.bypass(req, res, opts)
+ if (typeof bypassResult === 'string') {
+ debug?.(`bypass: ${req.url} -> ${bypassResult}`)
+ req.url = bypassResult
+ if (res.writableEnded) {
+ return
+ }
+ return next()
+ }
+ if (bypassResult === false) {
+ debug?.(`bypass: ${req.url} -> 404`)
+ res.statusCode = 404
+ return res.end()
+ }
+ } catch (e) {
+ debug?.(`bypass: ${req.url} -> ${e}`)
+ return next(e)
}
}
- debug(`${req.url} -> ${opts.target || opts.forward}`)
+ debug?.(`${req.url} -> ${opts.target || opts.forward}`)
if (opts.rewrite) {
req.url = opts.rewrite(req.url!)
}
@@ -118,7 +230,7 @@ export function proxyMiddleware(
function doesProxyContextMatchUrl(context: string, url: string): boolean {
return (
- (context.startsWith('^') && new RegExp(context).test(url)) ||
+ (context[0] === '^' && new RegExp(context).test(url)) ||
url.startsWith(context)
)
}
diff --git a/packages/vite/src/node/server/middlewares/rejectInvalidRequest.ts b/packages/vite/src/node/server/middlewares/rejectInvalidRequest.ts
new file mode 100644
index 00000000000000..fba9dbfc1d5adf
--- /dev/null
+++ b/packages/vite/src/node/server/middlewares/rejectInvalidRequest.ts
@@ -0,0 +1,23 @@
+import type { Connect } from '#dep-types/connect'
+
+/**
+ * disallows request that contains `#` in the URL
+ */
+export function rejectInvalidRequestMiddleware(): Connect.NextHandleFunction {
+ // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
+ return function viteRejectInvalidRequestMiddleware(req, res, next) {
+ // HTTP spec does not allow `#` in the request-target
+ // (HTTP 1.1: https://datatracker.ietf.org/doc/html/rfc9112#section-3.2)
+ // (HTTP 2: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1-2.4.1)
+ // But Node.js allows those requests.
+ // Our middlewares don't expect `#` to be included in `req.url`, especially the `server.fs.deny` checks.
+ if (req.url?.includes('#')) {
+ // HTTP 1.1 spec recommends sending 400 Bad Request
+ // (https://datatracker.ietf.org/doc/html/rfc9112#section-3.2-4)
+ res.writeHead(400)
+ res.end()
+ return
+ }
+ return next()
+ }
+}
diff --git a/packages/vite/src/node/server/middlewares/rejectNoCorsRequest.ts b/packages/vite/src/node/server/middlewares/rejectNoCorsRequest.ts
new file mode 100644
index 00000000000000..a45de17486399d
--- /dev/null
+++ b/packages/vite/src/node/server/middlewares/rejectNoCorsRequest.ts
@@ -0,0 +1,42 @@
+import type { Connect } from '#dep-types/connect'
+
+/**
+ * A middleware that rejects no-cors mode requests that are not same-origin.
+ *
+ * We should avoid untrusted sites to load the script to avoid attacks like GHSA-4v9v-hfq4-rm2v.
+ * This is because:
+ * - the path of HMR patch files / entry point files can be predictable
+ * - the HMR patch files may not include ESM syntax
+ * (if they include ESM syntax, loading as a classic script would fail)
+ * - the HMR runtime in the browser has the list of all loaded modules
+ *
+ * https://github.com/webpack/webpack-dev-server/security/advisories/GHSA-4v9v-hfq4-rm2v
+ * https://green.sapphi.red/blog/local-server-security-best-practices#_2-using-xssi-and-modifying-the-prototype
+ * https://green.sapphi.red/blog/local-server-security-best-practices#properly-check-the-request-origin
+ */
+export function rejectNoCorsRequestMiddleware(): Connect.NextHandleFunction {
+ // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
+ return function viteRejectNoCorsRequestMiddleware(req, res, next) {
+ // While we can set Cross-Origin-Resource-Policy header instead of rejecting requests,
+ // we choose to reject the request to be safer in case the request handler has any side-effects.
+ if (
+ req.headers['sec-fetch-mode'] === 'no-cors' &&
+ req.headers['sec-fetch-site'] !== 'same-origin' &&
+ // we only need to block classic script requests
+ req.headers['sec-fetch-dest'] === 'script'
+ ) {
+ // Send a JavaScript code instead of 403 so that the error is shown in the devtools
+ // If we send 403, the browser will avoid loading the body of the response
+ // and just show "Failed to load" error without the detailed message.
+ res.setHeader('Content-Type', 'text/javascript')
+ res.end(
+ `throw new Error(${JSON.stringify(
+ '[Vite] Cross-origin requests for classic scripts must be made with CORS mode enabled.' +
+ ' Make sure to set the "crossorigin" attribute on your `
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/pre-source-mapped-file.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/pre-source-mapped-file.js
new file mode 100644
index 00000000000000..39e70293b51975
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/pre-source-mapped-file.js
@@ -0,0 +1,9 @@
+import 'node:path'
+export default function testStack() {
+ innerTestStack()
+}
+import 'node:util'
+function innerTestStack() {
+ throw new Error('__TEST_STACK_TRANSPILED_INLINE__')
+}
+//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidHJhbnNwaWxlZC1pbmxpbmUudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCAnbm9kZTpwYXRoJ1xuXG5leHBvcnQgdHlwZSBEdW1teSA9IHtcbiAgZm9vOiBcImZvb1wiLFxufVxuXG4vKipcbiAqIGR1bW15XG4gKiBkdW1teVxuICovXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB0ZXN0U3RhY2soKSB7XG4gIGlubmVyVGVzdFN0YWNrKClcbn1cblxuaW1wb3J0ICdub2RlOnV0aWwnXG5cbi8qKlxuICogYmFyXG4gKiBiYXJcbiAqL1xuZnVuY3Rpb24gaW5uZXJUZXN0U3RhY2soKSB7XG4gIHRocm93IG5ldyBFcnJvcignX19URVNUX1NUQUNLX1RSQU5TUElMRURfSU5MSU5FX18nKVxufSJdLAogICJtYXBwaW5ncyI6ICJBQUFBLE9BQU87QUFVUCx3QkFBd0IsWUFBWTtBQUNsQyxpQkFBZTtBQUNqQjtBQUVBLE9BQU87QUFNUCxTQUFTLGlCQUFpQjtBQUN4QixRQUFNLElBQUksTUFBTSxrQ0FBa0M7QUFDcEQ7IiwKICAibmFtZXMiOiBbXQp9Cg==
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/simple.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/simple.js
new file mode 100644
index 00000000000000..a1d9deff4c396b
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/simple.js
@@ -0,0 +1,3 @@
+export const test = 'I am initialized'
+
+import.meta.hot?.accept()
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/string-literal-sourcemap.ts b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/string-literal-sourcemap.ts
new file mode 100644
index 00000000000000..296ebc2a016162
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/string-literal-sourcemap.ts
@@ -0,0 +1,12 @@
+// This file contains sourceMappingURL pattern in string literals
+// which should not crash the module runner
+
+const text = '//# sourceMappingURL=data:application/json;base64,invalidbase64'
+
+export function getMessage() {
+ return text
+}
+
+export function throwError() {
+ throw new Error('Test error for stacktrace')
+}
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/test.css b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/test.css
new file mode 100644
index 00000000000000..6446ebfd427495
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/test.css
@@ -0,0 +1,3 @@
+.test {
+ color: red;
+}
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/test.module.css b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/test.module.css
new file mode 100644
index 00000000000000..6446ebfd427495
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/test.module.css
@@ -0,0 +1,3 @@
+.test {
+ color: red;
+}
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/throws-error-method.ts b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/throws-error-method.ts
new file mode 100644
index 00000000000000..3f5c23e4c01de4
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/throws-error-method.ts
@@ -0,0 +1,7 @@
+interface Foo {
+ bar: string
+}
+
+export function throwError(foo?: Foo): void {
+ throw new Error('method error')
+}
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/top-level-object.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/top-level-object.js
new file mode 100644
index 00000000000000..41e65b00b5c451
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/top-level-object.js
@@ -0,0 +1,2 @@
+const Object = 'my-object'
+export { Object }
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/virtual.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/virtual.js
new file mode 100644
index 00000000000000..cda3c077b24c05
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/virtual.js
@@ -0,0 +1,4 @@
+import { msg as msg0 } from 'virtual0:test'
+import { msg } from 'virtual:test'
+
+export { msg0, msg }
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/worker.invoke.mjs b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/worker.invoke.mjs
new file mode 100644
index 00000000000000..cf6856a0f46e9c
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/worker.invoke.mjs
@@ -0,0 +1,49 @@
+// @ts-check
+
+import { BroadcastChannel, parentPort } from 'node:worker_threads'
+import {
+ ESModulesEvaluator,
+ ModuleRunner,
+ createNodeImportMeta,
+} from 'vite/module-runner'
+import { createBirpc } from 'birpc'
+
+if (!parentPort) {
+ throw new Error('File "worker.js" must be run in a worker thread')
+}
+
+/** @type {import('worker_threads').MessagePort} */
+const pPort = parentPort
+
+/** @type {import('birpc').BirpcReturn<{ invoke: (data: any) => any }>} */
+const rpc = createBirpc(
+ {},
+ {
+ post: (data) => pPort.postMessage(data),
+ on: (data) => pPort.on('message', data),
+ },
+)
+
+const runner = new ModuleRunner(
+ {
+ transport: {
+ invoke(data) {
+ return rpc.invoke(data)
+ },
+ },
+ createImportMeta: createNodeImportMeta,
+ hmr: false,
+ },
+ new ESModulesEvaluator(),
+)
+
+const channel = new BroadcastChannel('vite-worker:invoke')
+channel.onmessage = async (message) => {
+ try {
+ const mod = await runner.import(message.data.id)
+ channel.postMessage({ result: mod.default })
+ } catch (e) {
+ channel.postMessage({ error: e.stack })
+ }
+}
+parentPort.postMessage('ready')
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/worker.mjs b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/worker.mjs
new file mode 100644
index 00000000000000..4a9851c3aa198c
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/worker.mjs
@@ -0,0 +1,45 @@
+// @ts-check
+
+import { BroadcastChannel, parentPort } from 'node:worker_threads'
+import {
+ ESModulesEvaluator,
+ ModuleRunner,
+ createNodeImportMeta,
+} from 'vite/module-runner'
+
+if (!parentPort) {
+ throw new Error('File "worker.js" must be run in a worker thread')
+}
+
+/** @type {import('worker_threads').MessagePort} */
+const pPort = parentPort
+
+/** @type {import('vite/module-runner').ModuleRunnerTransport} */
+const messagePortTransport = {
+ connect({ onMessage, onDisconnection }) {
+ pPort.on('message', onMessage)
+ pPort.on('close', onDisconnection)
+ },
+ send(data) {
+ pPort.postMessage(data)
+ },
+}
+
+const runner = new ModuleRunner(
+ {
+ transport: messagePortTransport,
+ createImportMeta: createNodeImportMeta,
+ },
+ new ESModulesEvaluator(),
+)
+
+const channel = new BroadcastChannel('vite-worker')
+channel.onmessage = async (message) => {
+ try {
+ const mod = await runner.import(message.data.id)
+ channel.postMessage({ result: mod.default })
+ } catch (e) {
+ channel.postMessage({ error: e.stack })
+ }
+}
+parentPort.postMessage('ready')
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/package.json b/packages/vite/src/node/ssr/runtime/__tests__/package.json
new file mode 100644
index 00000000000000..07d326ae1a5d0b
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "@vitejs/unit-runtime",
+ "private": true,
+ "version": "0.0.0",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "dependencies": {
+ "@vitejs/cjs-external": "link:./fixtures/cjs-external",
+ "@vitejs/esm-external": "link:./fixtures/esm-external",
+ "test-dep-invalid-exports": "file:./fixtures/invalid-package/deps/test-dep-invalid-exports",
+ "tinyspy": "2.2.0",
+ "birpc": "^0.2.19"
+ }
+}
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-hmr.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-hmr.spec.ts
new file mode 100644
index 00000000000000..997df1f12095b7
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/server-hmr.spec.ts
@@ -0,0 +1,41 @@
+import { describe, expect } from 'vitest'
+import { createModuleRunnerTester } from './utils'
+
+describe(
+ 'module runner hmr works as expected',
+ async () => {
+ const it = await createModuleRunnerTester({
+ server: {
+ // override watch options because it's disabled by default
+ watch: {},
+ },
+ })
+
+ it('hmr options are defined', async ({ runner }) => {
+ expect(runner.hmrClient).toBeDefined()
+
+ const mod = await runner.import('/fixtures/hmr.js')
+ expect(mod).toHaveProperty('hmr')
+ expect(mod.hmr).toHaveProperty('accept')
+ })
+
+ it('correctly populates hmr client', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/d')
+ expect(mod.d).toBe('a')
+
+ const fixtureC = '/fixtures/c.ts'
+ const fixtureD = '/fixtures/d.ts'
+
+ expect(runner.hmrClient!.hotModulesMap.size).toBe(2)
+ expect(runner.hmrClient!.dataMap.size).toBe(2)
+ expect(runner.hmrClient!.ctxToListenersMap.size).toBe(2)
+
+ for (const fixture of [fixtureC, fixtureD]) {
+ expect(runner.hmrClient!.hotModulesMap.has(fixture)).toBe(true)
+ expect(runner.hmrClient!.dataMap.has(fixture)).toBe(true)
+ expect(runner.hmrClient!.ctxToListenersMap.has(fixture)).toBe(true)
+ }
+ })
+ },
+ process.env.CI ? 50_00 : 5_000,
+)
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-no-hmr.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-no-hmr.spec.ts
new file mode 100644
index 00000000000000..d4cf03c756c565
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/server-no-hmr.spec.ts
@@ -0,0 +1,20 @@
+import { describe, expect } from 'vitest'
+import { createModuleRunnerTester } from './utils'
+
+describe('module runner hmr works as expected', async () => {
+ const it = await createModuleRunnerTester({
+ server: {
+ // override watch options because it's disabled by default
+ watch: {},
+ hmr: false,
+ },
+ })
+
+ it("hmr client is not defined if it's disabled", async ({ runner }) => {
+ expect(runner.hmrClient).toBeUndefined()
+
+ const mod = await runner.import('/fixtures/hmr.js')
+ expect(mod).toHaveProperty('hmr')
+ expect(mod.hmr).toBeUndefined()
+ })
+})
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts
new file mode 100644
index 00000000000000..292868d608bb71
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts
@@ -0,0 +1,642 @@
+import { existsSync, readdirSync } from 'node:fs'
+import { posix, resolve, win32 } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { setTimeout } from 'node:timers/promises'
+import { describe, expect, it, vi } from 'vitest'
+import { isWindows } from '../../../../shared/utils'
+import type { ExternalFetchResult } from '../../../../shared/invokeMethods'
+import { createServer } from '../../../server'
+import {
+ createRunnableDevEnvironment,
+ isRunnableDevEnvironment,
+} from '../../../server/environments/runnableEnvironment'
+import type { HMRLogger } from '../../../../../dist/node/module-runner'
+import { createModuleRunnerTester } from './utils'
+
+const _URL = URL
+
+describe('module runner initialization', async () => {
+ const it = await createModuleRunnerTester({
+ resolve: {
+ external: ['tinyglobby'],
+ noExternal: ['@oxc-project/runtime'],
+ },
+ })
+
+ it('correctly runs ssr code', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/simple.js')
+ expect(mod.test).toEqual('I am initialized')
+
+ // loads the same module if id is a file url
+ const fileUrl = new _URL('./fixtures/simple.js', import.meta.url)
+ const mod2 = await runner.import(fileUrl.toString())
+ expect(mod).toBe(mod2)
+
+ // loads the same module if id is a file path
+ const filePath = fileURLToPath(fileUrl)
+ const mod3 = await runner.import(filePath)
+ expect(mod).toBe(mod3)
+ })
+
+ it('can load virtual modules as an entry point', async ({ runner }) => {
+ const mod = await runner.import('virtual:test')
+ expect(mod.msg).toBe('virtual')
+
+ // already resolved id works similar to `transformRequest`
+ expect(await runner.import(`\0virtual:normal`)).toMatchInlineSnapshot(`
+ {
+ "default": "ok",
+ }
+ `)
+
+ // escaped virtual module id works
+ expect(await runner.import(`/@id/__x00__virtual:normal`))
+ .toMatchInlineSnapshot(`
+ {
+ "default": "ok",
+ }
+ `)
+
+ // timestamp query works
+ expect(await runner.import(`virtual:normal?t=${Date.now()}`))
+ .toMatchInlineSnapshot(`
+ {
+ "default": "ok",
+ }
+ `)
+
+ // other arbitrary queries don't work
+ await expect(() =>
+ runner.import('virtual:normal?abcd=1234'),
+ ).rejects.toMatchObject({
+ message: expect.stringContaining(
+ 'Failed to load url virtual:normal?abcd=1234',
+ ),
+ })
+ })
+
+ it('css is loaded correctly', async ({ runner }) => {
+ const css = await runner.import('/fixtures/test.css')
+ expect(css.default).toBe(undefined)
+ const module = await runner.import('/fixtures/test.module.css')
+ expect(module).toMatchObject({
+ default: {
+ test: expect.stringMatching(/^_test_/),
+ },
+ test: expect.stringMatching(/^_test_/),
+ })
+ })
+
+ it('assets are loaded correctly', async ({ runner }) => {
+ const assets = await runner.import('/fixtures/assets.js')
+ expect(assets).toMatchObject({
+ mov: '/fixtures/assets/placeholder.mov',
+ txt: '/fixtures/assets/placeholder.txt',
+ png: '/fixtures/assets/placeholder.png',
+ webp: '/fixtures/assets/placeholder.webp',
+ })
+ })
+
+ it('ids with Vite queries are loaded correctly', async ({ runner }) => {
+ const raw = await runner.import('/fixtures/simple.js?raw')
+ expect(raw.default).toMatchInlineSnapshot(`
+ "export const test = 'I am initialized'
+
+ import.meta.hot?.accept()
+ "
+ `)
+ const url = await runner.import('/fixtures/simple.js?url')
+ expect(url.default).toMatchInlineSnapshot(`"/fixtures/simple.js"`)
+ const inline = await runner.import('/fixtures/test.css?inline')
+ expect(inline.default).toMatchInlineSnapshot(`
+ ".test {
+ color: red;
+ }
+ "
+ `)
+ })
+
+ it('modules with query strings are treated as different modules', async ({
+ runner,
+ }) => {
+ const modSimple = await runner.import('/fixtures/simple.js')
+ const modUrl = await runner.import('/fixtures/simple.js?url')
+ expect(modSimple).not.toBe(modUrl)
+ expect(modUrl.default).toBe('/fixtures/simple.js')
+ })
+
+ it('exports is not modifiable', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/simple.js')
+ expect(Object.isSealed(mod)).toBe(true)
+ expect(() => {
+ mod.test = 'I am modified'
+ }).toThrowErrorMatchingInlineSnapshot(
+ `[TypeError: Cannot set property test of [object Module] which has only a getter]`,
+ )
+ expect(() => {
+ delete mod.test
+ }).toThrowErrorMatchingInlineSnapshot(
+ `[TypeError: Cannot delete property 'test' of [object Module]]`,
+ )
+ expect(() => {
+ Object.defineProperty(mod, 'test', { value: 'I am modified' })
+ }).toThrowErrorMatchingInlineSnapshot(
+ `[TypeError: Cannot redefine property: test]`,
+ )
+ expect(() => {
+ mod.other = 'I am added'
+ }).toThrowErrorMatchingInlineSnapshot(
+ `[TypeError: Cannot add property other, object is not extensible]`,
+ )
+ })
+
+ it('throws the same error', async ({ runner }) => {
+ expect.assertions(3)
+ const s = Symbol()
+ try {
+ await runner.import('/fixtures/has-error.js')
+ } catch (e) {
+ expect(e[s]).toBeUndefined()
+ e[s] = true
+ expect(e[s]).toBe(true)
+ }
+
+ try {
+ await runner.import('/fixtures/has-error.js')
+ } catch (e) {
+ expect(e[s]).toBe(true)
+ }
+ })
+
+ it('importing external cjs library checks exports', async ({ runner }) => {
+ await expect(() => runner.import('/fixtures/cjs-external-non-existing.js'))
+ .rejects.toThrowErrorMatchingInlineSnapshot(`
+ [SyntaxError: [vite] Named export 'nonExisting' not found. The requested module '@vitejs/cjs-external' is a CommonJS module, which may not support all module.exports as named exports.
+ CommonJS modules can always be imported via the default export, for example using:
+
+ import pkg from '@vitejs/cjs-external';
+ const {nonExisting} = pkg;
+ ]
+ `)
+ // subsequent imports of the same external package should not throw if imports are correct
+ await expect(
+ runner.import('/fixtures/cjs-external-existing.js'),
+ ).resolves.toMatchObject({
+ result: 'world',
+ })
+ })
+
+ it('importing external esm library checks exports', async ({ runner }) => {
+ await expect(() =>
+ runner.import('/fixtures/esm-external-non-existing.js'),
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
+ `[SyntaxError: [vite] The requested module '@vitejs/esm-external' does not provide an export named 'nonExisting']`,
+ )
+ // subsequent imports of the same external package should not throw if imports are correct
+ await expect(
+ runner.import('/fixtures/esm-external-existing.js'),
+ ).resolves.toMatchObject({
+ result: 'world',
+ })
+ })
+
+ it("dynamic import doesn't produce duplicates", async ({ runner }) => {
+ const mod = await runner.import('/fixtures/dynamic-import.js')
+ const modules = await mod.initialize()
+ // toBe checks that objects are actually the same, not just structurally
+ // using toEqual here would be a mistake because it check the structural difference
+ expect(modules.static).toBe(modules.dynamicProcessed)
+ expect(modules.static).toBe(modules.dynamicRelative)
+ expect(modules.static).toBe(modules.dynamicAbsolute)
+ expect(modules.static).toBe(modules.dynamicAbsoluteExtension)
+ expect(modules.static).toBe(modules.dynamicAbsoluteFull)
+ expect(modules.static).toBe(modules.dynamicFileUrl)
+ })
+
+ it('correctly imports a virtual module', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/virtual.js')
+ expect(mod.msg0).toBe('virtual0')
+ expect(mod.msg).toBe('virtual')
+ })
+
+ it('importing package from node_modules', async ({ runner }) => {
+ const mod = (await runner.import(
+ '/fixtures/installed.js',
+ )) as typeof import('tinyspy')
+ const fn = mod.spy()
+ fn()
+ expect(fn.called).toBe(true)
+ })
+
+ it('importing native node package', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/native.js')
+ expect(mod.readdirSync).toBe(readdirSync)
+ expect(mod.existsSync).toBe(existsSync)
+ })
+
+ it('correctly resolves module url', async ({ runner, server }) => {
+ const { meta } = await runner.import('/fixtures/basic')
+ const basicUrl = new _URL('./fixtures/basic.js', import.meta.url).toString()
+ expect(meta.url).toBe(basicUrl)
+
+ const filename = meta.filename!
+ const dirname = meta.dirname!
+
+ if (isWindows) {
+ const cwd = process.cwd()
+ const drive = `${cwd[0].toUpperCase()}:\\`
+ const root = server.config.root.replace(/\\/g, '/')
+
+ expect(filename.startsWith(drive)).toBe(true)
+ expect(dirname.startsWith(drive)).toBe(true)
+
+ expect(filename).toBe(win32.join(root, '.\\fixtures\\basic.js'))
+ expect(dirname).toBe(win32.join(root, '.\\fixtures'))
+ } else {
+ const root = server.config.root
+
+ expect(posix.join(root, './fixtures/basic.js')).toBe(filename)
+ expect(posix.join(root, './fixtures')).toBe(dirname)
+ }
+ })
+
+ it(`no maximum call stack error ModuleRunner.isCircularImport`, async ({
+ runner,
+ }) => {
+ // entry.js ⇔ entry-cyclic.js
+ // ⇓
+ // action.js
+ const mod = await runner.import('/fixtures/cyclic/entry')
+ await mod.setupCyclic()
+ const action = await mod.importAction('/fixtures/cyclic/action')
+ expect(action).toBeDefined()
+ })
+
+ it('this of the exported function should be undefined', async ({
+ runner,
+ }) => {
+ const mod = await runner.import('/fixtures/no-this/importer.js')
+ expect(mod.result).toBe(undefined)
+ })
+
+ it.for([
+ '/fixtures/cyclic2/test1/index.js',
+ '/fixtures/cyclic2/test2/index.js',
+ '/fixtures/cyclic2/test3/index.js',
+ '/fixtures/cyclic2/test4/index.js',
+ ] as const)(`cyclic %s`, async (entry, { runner }) => {
+ const mod = await runner.import(entry)
+ expect({ ...mod }).toEqual({
+ dep1: {
+ ok: true,
+ },
+ dep2: {
+ ok: true,
+ },
+ })
+ })
+
+ it(`cyclic invalid 1`, async ({ runner }) => {
+ // Node also fails but with a different message
+ // $ node packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test5/index.js
+ // ReferenceError: Cannot access 'dep1' before initialization
+ await expect(() =>
+ runner.import('/fixtures/cyclic2/test5/index.js'),
+ ).rejects.toMatchInlineSnapshot(
+ `[TypeError: Cannot read properties of undefined (reading 'ok')]`,
+ )
+ })
+
+ it(`cyclic invalid 2`, async ({ runner }) => {
+ // It should be an error but currently `undefined` fallback.
+ expect(
+ await runner.import('/fixtures/cyclic2/test6/index.js'),
+ ).toMatchInlineSnapshot(
+ `
+ {
+ "dep1": "dep1: dep2: undefined",
+ }
+ `,
+ )
+ })
+
+ it(`cyclic with mixed import and re-export`, async ({ runner }) => {
+ const mod = await runner.import('/fixtures/cyclic2/test7/Ion.js')
+ expect(mod).toMatchInlineSnapshot(`
+ {
+ "IonTypes": {
+ "BLOB": "Blob",
+ },
+ "dom": {
+ "Blob": "Blob",
+ },
+ }
+ `)
+ })
+
+ it(`execution order with mixed import and re-export`, async ({
+ runner,
+ onTestFinished,
+ }) => {
+ const spy = vi.spyOn(console, 'log').mockImplementation(() => {})
+ onTestFinished(() => spy.mockRestore())
+
+ await runner.import('/fixtures/execution-order-re-export/index.js')
+ expect(spy.mock.calls.map((v) => v[0])).toMatchInlineSnapshot(`
+ [
+ "dep1",
+ "dep2",
+ ]
+ `)
+ })
+
+ it(`live binding (export default function f)`, async ({ runner }) => {
+ const mod = await runner.import('/fixtures/live-binding/test1/index.js')
+ expect(mod.default).toMatchInlineSnapshot(`
+ [
+ 2,
+ 3,
+ ]
+ `)
+ })
+
+ it(`live binding (export default f)`, async ({ runner }) => {
+ const mod = await runner.import('/fixtures/live-binding/test2/index.js')
+ expect(mod.default).toMatchInlineSnapshot(`
+ [
+ 1,
+ 1,
+ ]
+ `)
+ })
+
+ it(`live binding (export { f as default })`, async ({ runner }) => {
+ const mod = await runner.import('/fixtures/live-binding/test3/index.js')
+ expect(mod.default).toMatchInlineSnapshot(`
+ [
+ 2,
+ 3,
+ ]
+ `)
+ })
+
+ it(`live binding (export default class C)`, async ({ runner }) => {
+ const mod = await runner.import('/fixtures/live-binding/test4/index.js')
+ expect(mod.default).toMatchInlineSnapshot(`
+ [
+ 2,
+ 3,
+ ]
+ `)
+ })
+
+ it(`export default getter is hoisted`, async ({ runner }) => {
+ // Node error is `ReferenceError: Cannot access 'dep' before initialization`
+ // It should be an error but currently `undefined` fallback.
+ expect(
+ await runner.import('/fixtures/cyclic2/test9/index.js'),
+ ).toMatchInlineSnapshot(
+ `
+ {
+ "default": undefined,
+ }
+ `,
+ )
+ })
+
+ it('oxc runtime helpers are loadable', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/oxc-runtime-helper.ts')
+ expect(mod.result).toMatchInlineSnapshot(`
+ ""
+ `)
+ })
+
+ it(`handle Object variable`, async ({ runner }) => {
+ const mod = await runner.import('/fixtures/top-level-object.js')
+ expect(mod).toMatchInlineSnapshot(`
+ {
+ "Object": "my-object",
+ }
+ `)
+ })
+})
+
+describe('optimize-deps', async () => {
+ const it = await createModuleRunnerTester({
+ cacheDir: 'node_modules/.vite-test',
+ ssr: {
+ noExternal: true,
+ optimizeDeps: {
+ include: ['@vitejs/cjs-external'],
+ },
+ },
+ })
+
+ it('optimized dep as entry', async ({ runner }) => {
+ const mod = await runner.import('@vitejs/cjs-external')
+ expect(mod.default.hello()).toMatchInlineSnapshot(`"world"`)
+ })
+})
+
+describe('resolveId absolute path entry', async () => {
+ const it = await createModuleRunnerTester({
+ plugins: [
+ {
+ name: 'test-resolevId',
+ enforce: 'pre',
+ resolveId(source) {
+ if (
+ source ===
+ posix.join(this.environment.config.root, 'fixtures/basic.js')
+ ) {
+ return '\0virtual:basic'
+ }
+ },
+ load(id) {
+ if (id === '\0virtual:basic') {
+ return `export const name = "virtual:basic"`
+ }
+ },
+ },
+ ],
+ })
+
+ it('ssrLoadModule', async ({ server }) => {
+ const mod = await server.ssrLoadModule(
+ posix.join(server.config.root, 'fixtures/basic.js'),
+ )
+ expect(mod.name).toMatchInlineSnapshot(`"virtual:basic"`)
+ })
+
+ it('runner', async ({ server, runner }) => {
+ const mod = await runner.import(
+ posix.join(server.config.root, 'fixtures/basic.js'),
+ )
+ expect(mod.name).toMatchInlineSnapshot(`"virtual:basic"`)
+ })
+})
+
+describe('virtual module hmr', async () => {
+ let state = 'init'
+
+ const it = await createModuleRunnerTester({
+ plugins: [
+ {
+ name: 'test-resolevId',
+ enforce: 'pre',
+ resolveId(source) {
+ if (source === 'virtual:test') {
+ return '\0' + source
+ }
+ },
+ load(id) {
+ if (id === '\0virtual:test') {
+ return `export default ${JSON.stringify(state)}`
+ }
+ },
+ },
+ ],
+ })
+
+ it('full reload', async ({ server, runner }) => {
+ const mod = await runner.import('virtual:test')
+ expect(mod.default).toBe('init')
+ state = 'reloaded'
+ server.environments.ssr.moduleGraph.invalidateAll()
+ server.environments.ssr.hot.send({ type: 'full-reload' })
+ await vi.waitFor(() => {
+ const mod = runner.evaluatedModules.getModuleById('\0virtual:test')
+ expect(mod?.exports.default).toBe('reloaded')
+ })
+ })
+
+ it("the external module's ID and file are resolved correctly", async ({
+ server,
+ runner,
+ }) => {
+ await runner.import(
+ posix.join(server.config.root, 'fixtures/import-external.ts'),
+ )
+ const moduleNode = runner.evaluatedModules.getModuleByUrl('tinyglobby')!
+ const meta = moduleNode.meta as ExternalFetchResult
+ if (process.platform === 'win32') {
+ expect(meta.externalize).toMatch(/^file:\/\/\/\w:\//) // file:///C:/
+ expect(moduleNode.id).toMatch(/^\w:\//) // C:/
+ expect(moduleNode.file).toMatch(/^\w:\//) // C:/
+ } else {
+ expect(meta.externalize).toMatch(/^file:\/\/\//) // file:///
+ expect(moduleNode.id).toMatch(/^\//) // /
+ expect(moduleNode.file).toMatch(/^\//) // /
+ }
+ })
+})
+
+describe('invalid package', async () => {
+ const it = await createModuleRunnerTester({
+ environments: {
+ ssr: {
+ resolve: {
+ noExternal: true,
+ },
+ },
+ },
+ })
+
+ it('can catch resolve error on runtime', async ({ runner }) => {
+ const mod = await runner.import('./fixtures/invalid-package/test.js')
+ expect(await mod.test()).toMatchInlineSnapshot(`
+ {
+ "data": [Error: Failed to resolve entry for package "test-dep-invalid-exports". The package may have incorrect main/module/exports specified in its package.json.],
+ "ok": false,
+ }
+ `)
+ })
+})
+
+describe('full-reload during close', () => {
+ it('does not error when server closes during full-reload re-import', async () => {
+ const errors: (string | Error)[] = []
+ const logger: HMRLogger = {
+ error: (msg) => errors.push(msg),
+ debug: () => {},
+ }
+
+ const server = await createServer({
+ root: import.meta.dirname,
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ watch: null,
+ ws: false,
+ },
+ optimizeDeps: {
+ disabled: true,
+ noDiscovery: true,
+ },
+ environments: {
+ ssr: {
+ dev: {
+ createEnvironment(name, config) {
+ return createRunnableDevEnvironment(name, config, {
+ runnerOptions: { hmr: { logger } },
+ })
+ },
+ },
+ },
+ },
+ plugins: [
+ {
+ name: 'test-slow-virtual',
+ enforce: 'pre',
+ resolveId(source) {
+ if (source === 'virtual:slow') return '\0virtual:slow'
+ },
+ async load(id) {
+ if (id === '\0virtual:slow') {
+ await setTimeout(10)
+ return `export default "ok"`
+ }
+ },
+ },
+ ],
+ })
+
+ const env = server.environments.ssr
+ if (!isRunnableDevEnvironment(env)) {
+ throw new Error('expected RunnableDevEnvironment')
+ }
+
+ const mod = await env.runner.import('virtual:slow')
+ expect(mod.default).toBe('ok')
+
+ // re-import will run async via the HMR queue
+ env.moduleGraph.invalidateAll()
+ env.hot.send({ type: 'full-reload' })
+
+ // server.close() -> environment.close() -> runner.close() ->
+ // transport.disconnect() rejects the pending fetchModule RPC
+ await server.close()
+ await setTimeout(100) // Give the HMR handler time to settle
+
+ expect(
+ errors.some((e) => e.toString().includes('transport was disconnected')),
+ ).toBe(false)
+ })
+})
+
+describe('server.fs check', async () => {
+ const it = await createModuleRunnerTester({
+ server: {
+ fs: {
+ allow: [resolve(import.meta.dirname, './fixtures/circular')],
+ },
+ },
+ })
+
+ it('it is not applied to the server module runner', async ({ runner }) => {
+ const mod = await runner.import('/fixtures/basic.js')
+ expect(mod.name).toBe('basic')
+ })
+})
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-source-maps.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-source-maps.spec.ts
new file mode 100644
index 00000000000000..6caeea61be17b1
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/server-source-maps.spec.ts
@@ -0,0 +1,177 @@
+import { runInThisContext } from 'node:vm'
+import { resolve } from 'node:path'
+import { describe, expect } from 'vitest'
+import type { ViteDevServer } from '../../..'
+import type { ModuleRunnerContext } from '../../../../module-runner'
+import { ESModulesEvaluator } from '../../../../module-runner'
+import {
+ createFixtureEditor,
+ createModuleRunnerTester,
+ resolvePath,
+} from './utils'
+
+describe('module runner initialization', async () => {
+ const it = await createModuleRunnerTester(
+ {},
+ {
+ sourcemapInterceptor: 'prepareStackTrace',
+ },
+ )
+
+ const getError = async (cb: () => void): Promise => {
+ try {
+ await cb()
+ expect.unreachable()
+ } catch (err) {
+ return err
+ }
+ }
+ const serializeStack = (server: ViteDevServer, err: Error) => {
+ return err.stack!.split('\n')[1].replace(server.config.root, '')
+ }
+ const serializeStackDeep = (server: ViteDevServer, err: Error) => {
+ return err
+ .stack!.split('\n')
+ .map((s) => s.replace(server.config.root, ''))
+ }
+
+ it('source maps are correctly applied to stack traces', async ({
+ runner,
+ server,
+ }) => {
+ expect.assertions(3)
+ const topLevelError = await getError(() =>
+ runner.import('/fixtures/has-error.js'),
+ )
+ expect(serializeStack(server, topLevelError)).toBe(
+ ' at /fixtures/has-error.js:2:7',
+ )
+
+ const methodError = await getError(async () => {
+ const mod = await runner.import('/fixtures/throws-error-method.ts')
+ mod.throwError()
+ })
+ expect(serializeStack(server, methodError)).toBe(
+ ' at Module.throwError (/fixtures/throws-error-method.ts:6:9)',
+ )
+
+ const fixtureEditor = createFixtureEditor()
+
+ // simulate HMR
+ fixtureEditor.editFile(
+ resolvePath(import.meta.url, './fixtures/throws-error-method.ts'),
+ (code) => '\n\n\n\n\n' + code + '\n',
+ )
+ runner.evaluatedModules.clear()
+ server.environments.ssr.moduleGraph.invalidateAll()
+
+ const methodErrorNew = await getError(async () => {
+ const mod = await runner.import('/fixtures/throws-error-method.ts')
+ mod.throwError()
+ })
+
+ expect(serializeStack(server, methodErrorNew)).toBe(
+ ' at Module.throwError (/fixtures/throws-error-method.ts:11:9)',
+ )
+ })
+
+ it('stacktrace column on first line', async ({ runner, server }) => {
+ // column is off by "use strict"
+ const topLevelError = await getError(() =>
+ runner.import('/fixtures/has-error-first.js'),
+ )
+ expect(serializeStack(server, topLevelError)).toBe(
+ ' at /fixtures/has-error-first.js:1:18',
+ )
+
+ const topLevelErrorTs = await getError(() =>
+ runner.import('/fixtures/has-error-first-comment.ts'),
+ )
+ expect(serializeStack(server, topLevelErrorTs)).toBe(
+ ' at /fixtures/has-error-first-comment.ts:2:7',
+ )
+ })
+
+ it('deep stacktrace', async ({ runner, server }) => {
+ const methodError = await getError(async () => {
+ const mod = await runner.import('/fixtures/has-error-deep.ts')
+ mod.main()
+ })
+ expect(serializeStackDeep(server, methodError).slice(0, 3)).toEqual([
+ 'Error: crash',
+ ' at crash (/fixtures/has-error-deep.ts:2:9)',
+ ' at Module.main (/fixtures/has-error-deep.ts:6:3)',
+ ])
+ })
+
+ it('should not crash when sourceMappingURL pattern appears in string literals', async ({
+ runner,
+ server,
+ }) => {
+ const mod = await runner.import('/fixtures/string-literal-sourcemap.ts')
+ expect(mod.getMessage()).toBe(
+ '//# sourceMappingURL=data:application/json;base64,invalidbase64',
+ )
+ const error = await getError(() => mod.throwError())
+ expect(error.message).toBe('Test error for stacktrace')
+ expect(serializeStackDeep(server, error).slice(0, 2)).toEqual([
+ 'Error: Test error for stacktrace',
+ ' at Module.throwError (/fixtures/string-literal-sourcemap.ts:11:9)',
+ ])
+ })
+
+ it('should correctly pickup the url from sources', async ({
+ server,
+ runner,
+ }) => {
+ const mod = await runner.import('/fixtures/pre-source-mapped-file.js')
+ const error = await getError(() => mod.default())
+ // The error stack shows "transpiled-inline.ts" because it is specified in the source map's "sources" field.
+ // The file itself does not exist on the file system, but we should still respect "sources".
+ // If source maps handling breaks, the stack trace will point to "transpiled-inline.js" instead, which would be a bug.
+ expect(serializeStackDeep(server, error).slice(0, 3))
+ .toMatchInlineSnapshot(`
+ [
+ "Error: __TEST_STACK_TRANSPILED_INLINE__",
+ " at innerTestStack (/fixtures/transpiled-inline.ts:22:9)",
+ " at Module.testStack (/fixtures/transpiled-inline.ts:12:3)",
+ ]
+ `)
+ })
+})
+
+describe('module runner with node:vm executor', async () => {
+ class Evaluator extends ESModulesEvaluator {
+ async runInlinedModule(_: ModuleRunnerContext, __: string) {
+ // Mimics VitestModuleEvaluator
+ const initModule = runInThisContext(
+ '() => { throw new Error("example")}',
+ {
+ lineOffset: 0,
+ columnOffset: -100,
+ filename: resolve(import.meta.dirname, 'fixtures/a.ts'),
+ },
+ )
+
+ initModule()
+ }
+ }
+
+ const it = await createModuleRunnerTester(
+ {},
+ {
+ sourcemapInterceptor: 'prepareStackTrace',
+ evaluator: new Evaluator(),
+ },
+ )
+
+ it('should not crash when error stacktrace contains negative column', async ({
+ runner,
+ }) => {
+ const error = await runner.import('/fixtures/a.ts').catch((err) => err)
+
+ expect(() =>
+ error.stack.includes('.stack access triggers the bug'),
+ ).not.toThrow()
+ })
+})
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.invoke.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.invoke.spec.ts
new file mode 100644
index 00000000000000..8ba892f9c5539a
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.invoke.spec.ts
@@ -0,0 +1,125 @@
+import { BroadcastChannel, Worker } from 'node:worker_threads'
+import path from 'node:path'
+import { afterAll, beforeAll, describe, expect, it } from 'vitest'
+import type { BirpcReturn } from 'birpc'
+import { createBirpc } from 'birpc'
+import { DevEnvironment } from '../../..'
+import { type ViteDevServer, createServer } from '../../../server'
+
+describe('running module runner inside a worker and using the ModuleRunnerTransport#invoke API', () => {
+ let worker: Worker
+ let server: ViteDevServer
+ let rpc: BirpcReturn<
+ unknown,
+ { invoke: (data: any) => Promise<{ result: any } | { error: any }> }
+ >
+ let handleInvoke: (data: any) => Promise<{ result: any } | { error: any }>
+
+ beforeAll(async () => {
+ worker = new Worker(
+ new URL('./fixtures/worker.invoke.mjs', import.meta.url),
+ {
+ stdout: true,
+ },
+ )
+ await new Promise((resolve, reject) => {
+ worker.on('message', () => resolve())
+ worker.on('error', reject)
+ })
+ server = await createServer({
+ root: import.meta.dirname,
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ watch: null,
+ hmr: {
+ port: 9610,
+ },
+ fs: {
+ allow: [path.resolve(import.meta.dirname, './fixtures')],
+ },
+ },
+ environments: {
+ worker: {
+ dev: {
+ createEnvironment: (name, config) => {
+ return new DevEnvironment(name, config, {
+ hot: false,
+ })
+ },
+ },
+ },
+ },
+ })
+ handleInvoke = (data: any) =>
+ server.environments.worker.hot.handleInvoke(data)
+ rpc = createBirpc(
+ {
+ invoke: (data: any) => handleInvoke(data),
+ },
+ {
+ post: (data) => worker.postMessage(data),
+ on: (data) => worker.on('message', data),
+ },
+ )
+ })
+
+ afterAll(async () => {
+ await Promise.allSettled([server.close(), worker.terminate()])
+ rpc.$close()
+ })
+
+ async function run(id: string) {
+ const channel = new BroadcastChannel('vite-worker:invoke')
+ return new Promise((resolve, reject) => {
+ channel.onmessage = (event) => {
+ try {
+ resolve(event.data)
+ } catch (e) {
+ reject(e)
+ }
+ }
+ channel.postMessage({ id })
+ })
+ }
+
+ it('correctly runs ssr code', async () => {
+ const output = await run('./fixtures/default-string.ts')
+ expect(output).toStrictEqual({
+ result: 'hello world',
+ })
+ })
+
+ it('triggers an error', async () => {
+ handleInvoke = async () => ({ error: new Error('This is an Invoke Error') })
+ const output = await run('dummy')
+ expect(output).not.toHaveProperty('result')
+ expect(output.error).toContain('Error: This is an Invoke Error')
+ })
+
+ it('triggers an unknown error', async () => {
+ handleInvoke = async () => ({ error: 'a string instead of an error' })
+ const output = await run('dummy')
+ expect(output).not.toHaveProperty('result')
+ expect(output.error).toContain('Error: Unknown invoke error')
+ })
+
+ it('resolves builtin module without server round-trip', async () => {
+ handleInvoke = (data: any) =>
+ server.environments.worker.hot.handleInvoke(data)
+
+ const output = await run('./fixtures/builtin-import.ts')
+ expect(output).toHaveProperty('result')
+ expect(output.result).toBe('baz.txt')
+ expect(output.error).toBeUndefined()
+ })
+
+ it('server.fs check is applied to the custom transport by default', async () => {
+ handleInvoke = (data: any) =>
+ server.environments.worker.hot.handleInvoke(data)
+
+ const output = await run('./fixture-outside.js')
+ expect(output).toHaveProperty('error')
+ expect(output.error).toContain('Failed to load url')
+ })
+})
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.spec.ts
new file mode 100644
index 00000000000000..49759b955c57c4
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.spec.ts
@@ -0,0 +1,160 @@
+import { BroadcastChannel, Worker } from 'node:worker_threads'
+import path from 'node:path'
+import { describe, expect, it, onTestFinished } from 'vitest'
+import type { HotChannel, HotChannelListener, HotPayload } from 'vite'
+import { DevEnvironment } from '../../..'
+import { createServer } from '../../../server'
+
+const createWorkerTransport = (worker: Worker): HotChannel => {
+ const handlerToWorkerListener = new WeakMap<
+ HotChannelListener,
+ (value: HotPayload) => void
+ >()
+ const client = {
+ send(payload: HotPayload) {
+ worker.postMessage(payload)
+ },
+ }
+
+ return {
+ send: (data) => worker.postMessage(data),
+ on: (event: string, handler: HotChannelListener) => {
+ // client is already connected
+ if (event === 'vite:client:connect') return
+ if (event === 'vite:client:disconnect') {
+ const listener = () => {
+ handler(undefined, client)
+ }
+ handlerToWorkerListener.set(handler, listener)
+ worker.on('exit', listener)
+ return
+ }
+
+ const listener = (value: HotPayload) => {
+ if (value.type === 'custom' && value.event === event) {
+ handler(value.data, client)
+ }
+ }
+ handlerToWorkerListener.set(handler, listener)
+ worker.on('message', listener)
+ },
+ off: (event, handler: HotChannelListener) => {
+ if (event === 'vite:client:connect') return
+ if (event === 'vite:client:disconnect') {
+ const listener = handlerToWorkerListener.get(handler)
+ if (listener) {
+ worker.off('exit', listener)
+ handlerToWorkerListener.delete(handler)
+ }
+ return
+ }
+
+ const listener = handlerToWorkerListener.get(handler)
+ if (listener) {
+ worker.off('message', listener)
+ handlerToWorkerListener.delete(handler)
+ }
+ },
+ }
+}
+
+describe('running module runner inside a worker', () => {
+ it('correctly runs ssr code', async () => {
+ expect.assertions(1)
+ const worker = new Worker(
+ new URL('./fixtures/worker.mjs', import.meta.url),
+ {
+ stdout: true,
+ },
+ )
+ await new Promise((resolve, reject) => {
+ worker.on('message', () => resolve())
+ worker.on('error', reject)
+ })
+ const server = await createServer({
+ root: import.meta.dirname,
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ watch: null,
+ hmr: {
+ port: 9609,
+ },
+ },
+ environments: {
+ worker: {
+ dev: {
+ createEnvironment: (name, config) => {
+ return new DevEnvironment(name, config, {
+ hot: false,
+ transport: createWorkerTransport(worker),
+ })
+ },
+ },
+ },
+ },
+ })
+ onTestFinished(async () => {
+ await Promise.allSettled([server.close(), worker.terminate()])
+ })
+ const channel = new BroadcastChannel('vite-worker')
+ return new Promise((resolve, reject) => {
+ channel.onmessage = (event) => {
+ try {
+ expect(event.data).toEqual({
+ result: 'hello world',
+ })
+ } catch (e) {
+ reject(e)
+ } finally {
+ resolve()
+ }
+ }
+ channel.postMessage({ id: './fixtures/default-string.ts' })
+ })
+ })
+
+ it('server.fs check is applied to the custom transport by default', async () => {
+ const worker = new Worker(
+ new URL('./fixtures/worker.mjs', import.meta.url),
+ { stdout: true },
+ )
+ await new Promise((resolve, reject) => {
+ worker.on('message', () => resolve())
+ worker.on('error', reject)
+ })
+ const server = await createServer({
+ root: import.meta.dirname,
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ watch: null,
+ hmr: {
+ port: 9609,
+ },
+ fs: {
+ allow: [path.resolve(import.meta.dirname, './fixtures')],
+ },
+ },
+ environments: {
+ worker: {
+ dev: {
+ createEnvironment: (name, config) => {
+ return new DevEnvironment(name, config, {
+ hot: false,
+ transport: createWorkerTransport(worker),
+ })
+ },
+ },
+ },
+ },
+ })
+ onTestFinished(async () => {
+ await Promise.allSettled([server.close(), worker.terminate()])
+ })
+
+ await expect(
+ server.environments.worker.transformRequest('./fixture-outside.js'),
+ ).rejects.toThrow('Failed to load url')
+ })
+})
diff --git a/packages/vite/src/node/ssr/runtime/__tests__/utils.ts b/packages/vite/src/node/ssr/runtime/__tests__/utils.ts
new file mode 100644
index 00000000000000..8ebe6708224d9c
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/__tests__/utils.ts
@@ -0,0 +1,131 @@
+import fs from 'node:fs'
+import { dirname, resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import type { TestAPI } from 'vitest'
+import { afterEach, beforeEach, onTestFinished, test } from 'vitest'
+import type { ModuleRunner } from 'vite/module-runner'
+import type { ServerModuleRunnerOptions } from '../serverModuleRunner'
+import type { ViteDevServer } from '../../../server'
+import type { InlineConfig } from '../../../config'
+import { createServer } from '../../../server'
+import { createServerModuleRunner } from '../serverModuleRunner'
+import type { DevEnvironment } from '../../../server/environment'
+
+interface TestClient {
+ server: ViteDevServer
+ runner: ModuleRunner
+ environment: DevEnvironment
+}
+
+export async function createModuleRunnerTester(
+ config: InlineConfig = {},
+ runnerConfig: ServerModuleRunnerOptions = {},
+): Promise> {
+ function waitForWatcher(server: ViteDevServer) {
+ return new Promise((resolve) => {
+ if ((server.watcher as any)._readyEmitted) {
+ resolve()
+ } else {
+ server.watcher.once('ready', () => resolve())
+ }
+ })
+ }
+
+ beforeEach(async (t) => {
+ // @ts-ignore
+ globalThis.__HMR__ = {}
+
+ t.server = await createServer({
+ root: import.meta.dirname,
+ logLevel: 'error',
+ server: {
+ middlewareMode: true,
+ watch: null,
+ ws: false,
+ },
+ ssr: {
+ external: ['@vitejs/cjs-external', '@vitejs/esm-external'],
+ },
+ optimizeDeps: {
+ disabled: true,
+ noDiscovery: true,
+ include: [],
+ },
+ plugins: [
+ {
+ name: 'vite-plugin-virtual',
+ resolveId(id) {
+ if (id === 'virtual0:test') {
+ return `\0virtual:test`
+ }
+ if (id === 'virtual:test') {
+ return 'virtual:test'
+ }
+ if (id === 'virtual:normal') {
+ return '\0' + id
+ }
+ },
+ load(id) {
+ if (id === `\0virtual:test`) {
+ return `export const msg = 'virtual0'`
+ }
+ if (id === `virtual:test`) {
+ return `export const msg = 'virtual'`
+ }
+ if (id === '\0virtual:normal') {
+ return 'export default "ok"'
+ }
+ },
+ },
+ ...(config.plugins ?? []),
+ ],
+ ...config,
+ })
+ t.environment = t.server.environments.ssr
+ t.runner = createServerModuleRunner(t.environment, {
+ hmr: {
+ logger: false,
+ },
+ // don't override by default so Vitest source maps are correct
+ sourcemapInterceptor: false,
+ ...runnerConfig,
+ })
+ if (config.server?.watch) {
+ await waitForWatcher(t.server)
+ }
+ })
+
+ afterEach(async (t) => {
+ await t.runner.close()
+ await t.server.close()
+ })
+
+ return test as TestAPI
+}
+
+type FixtureEditor = {
+ editFile: (file: string, callback: (content: string) => string) => void
+}
+
+export function createFixtureEditor(): FixtureEditor {
+ const originalFiles = new Map()
+ onTestFinished(() => {
+ originalFiles.forEach((content, file) => {
+ fs.writeFileSync(file, content, 'utf-8')
+ })
+ originalFiles.clear()
+ })
+
+ return {
+ editFile(file, callback) {
+ const content = fs.readFileSync(file, 'utf-8')
+ if (!originalFiles.has(file)) originalFiles.set(file, content)
+ fs.writeFileSync(file, callback(content), 'utf-8')
+ },
+ }
+}
+
+export function resolvePath(baseUrl: string, path: string): string {
+ const filename = fileURLToPath(baseUrl)
+ return resolve(dirname(filename), path).replace(/\\/g, '/')
+}
diff --git a/packages/vite/src/node/ssr/runtime/serverModuleRunner.ts b/packages/vite/src/node/ssr/runtime/serverModuleRunner.ts
new file mode 100644
index 00000000000000..71a3610f4b6c0e
--- /dev/null
+++ b/packages/vite/src/node/ssr/runtime/serverModuleRunner.ts
@@ -0,0 +1,147 @@
+import { existsSync, readFileSync } from 'node:fs'
+import { ModuleRunner, createNodeImportMeta } from 'vite/module-runner'
+import type {
+ ModuleEvaluator,
+ ModuleRunnerHmr,
+ ModuleRunnerOptions,
+} from 'vite/module-runner'
+import type { HotPayload } from '#types/hmrPayload'
+import type { DevEnvironment } from '../../server/environment'
+import type {
+ HotChannelClient,
+ NormalizedServerHotChannel,
+} from '../../server/hmr'
+import type { ModuleRunnerTransport } from '../../../shared/moduleRunnerTransport'
+
+/**
+ * @experimental
+ */
+export interface ServerModuleRunnerOptions extends Omit<
+ ModuleRunnerOptions,
+ 'root' | 'fetchModule' | 'hmr' | 'transport'
+> {
+ /**
+ * Disable HMR or configure HMR logger.
+ */
+ hmr?:
+ | false
+ | {
+ logger?: ModuleRunnerHmr['logger']
+ }
+ /**
+ * Provide a custom module evaluator. This controls how the code is executed.
+ */
+ evaluator?: ModuleEvaluator
+}
+
+function createHMROptions(
+ environment: DevEnvironment,
+ options: ServerModuleRunnerOptions,
+) {
+ if (environment.config.server.hmr === false || options.hmr === false) {
+ return false
+ }
+ if (!('api' in environment.hot)) return false
+ return {
+ logger: options.hmr?.logger,
+ }
+}
+
+const prepareStackTrace = {
+ retrieveFile(id: string) {
+ if (existsSync(id)) {
+ return readFileSync(id, 'utf-8')
+ }
+ },
+}
+
+function resolveSourceMapOptions(options: ServerModuleRunnerOptions) {
+ if (options.sourcemapInterceptor != null) {
+ if (options.sourcemapInterceptor === 'prepareStackTrace') {
+ return prepareStackTrace
+ }
+ if (typeof options.sourcemapInterceptor === 'object') {
+ return { ...prepareStackTrace, ...options.sourcemapInterceptor }
+ }
+ return options.sourcemapInterceptor
+ }
+ if (typeof process !== 'undefined' && 'setSourceMapsEnabled' in process) {
+ return 'node'
+ }
+ return prepareStackTrace
+}
+
+export const createServerModuleRunnerTransport = (options: {
+ channel: NormalizedServerHotChannel
+}): ModuleRunnerTransport => {
+ const hmrClient: HotChannelClient = {
+ send: (payload: HotPayload) => {
+ if (payload.type !== 'custom') {
+ throw new Error(
+ 'Cannot send non-custom events from the client to the server.',
+ )
+ }
+ options.channel.send(payload)
+ },
+ }
+
+ let handler: ((data: HotPayload) => void) | undefined
+
+ return {
+ connect({ onMessage }) {
+ options.channel.api!.outsideEmitter.on('send', onMessage)
+ options.channel.api!.innerEmitter.emit(
+ 'vite:client:connect',
+ undefined,
+ hmrClient,
+ )
+ onMessage({ type: 'connected' })
+ handler = onMessage
+ },
+ disconnect() {
+ if (handler) {
+ options.channel.api!.outsideEmitter.off('send', handler)
+ }
+ options.channel.api!.innerEmitter.emit(
+ 'vite:client:disconnect',
+ undefined,
+ hmrClient,
+ )
+ },
+ send(payload) {
+ if (payload.type !== 'custom') {
+ throw new Error(
+ 'Cannot send non-custom events from the server to the client.',
+ )
+ }
+ options.channel.api!.innerEmitter.emit(
+ payload.event,
+ payload.data,
+ hmrClient,
+ )
+ },
+ }
+}
+
+/**
+ * Create an instance of the Vite SSR runtime that support HMR.
+ * @experimental
+ */
+export function createServerModuleRunner(
+ environment: DevEnvironment,
+ options: ServerModuleRunnerOptions = {},
+): ModuleRunner {
+ const hmr = createHMROptions(environment, options)
+ return new ModuleRunner(
+ {
+ ...options,
+ transport: createServerModuleRunnerTransport({
+ channel: environment.hot as NormalizedServerHotChannel,
+ }),
+ hmr,
+ createImportMeta: createNodeImportMeta,
+ sourcemapInterceptor: resolveSourceMapOptions(options),
+ },
+ options.evaluator,
+ )
+}
diff --git a/packages/vite/src/node/ssr/ssrExternal.ts b/packages/vite/src/node/ssr/ssrExternal.ts
deleted file mode 100644
index 800a2307dabae6..00000000000000
--- a/packages/vite/src/node/ssr/ssrExternal.ts
+++ /dev/null
@@ -1,219 +0,0 @@
-import fs from 'fs'
-import path from 'path'
-import type { InternalResolveOptions } from '../plugins/resolve'
-import { tryNodeResolve } from '../plugins/resolve'
-import {
- createDebugger,
- isDefined,
- lookupFile,
- normalizePath,
- resolveFrom
-} from '../utils'
-import type { Logger, ResolvedConfig } from '..'
-import { createFilter } from '@rollup/pluginutils'
-
-const debug = createDebugger('vite:ssr-external')
-
-/**
- * Converts "parent > child" syntax to just "child"
- */
-export function stripNesting(packages: string[]) {
- return packages.map((s) => {
- const arr = s.split('>')
- return arr[arr.length - 1].trim()
- })
-}
-
-/**
- * Heuristics for determining whether a dependency should be externalized for
- * server-side rendering.
- */
-export function resolveSSRExternal(
- config: ResolvedConfig,
- knownImports: string[]
-): string[] {
- // strip nesting since knownImports may be passed in from optimizeDeps which
- // supports a "parent > child" syntax
- knownImports = stripNesting(knownImports)
-
- const ssrConfig = config.ssr
- if (ssrConfig?.noExternal === true) {
- return []
- }
-
- const ssrExternals: Set = new Set()
- const seen: Set