From 7eeb6f2f97abf5dfc71c225b9cff9779baf2ed2f Mon Sep 17 00:00:00 2001 From: Alex <8125011+alex-kinokon@users.noreply.github.com> Date: Thu, 24 Oct 2024 03:44:36 -0400 Subject: [PATCH 001/156] fix: add typing to `CSSOptions.preprocessorOptions` (#18001) Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Co-authored-by: bluwy --- packages/vite/src/node/plugins/css.ts | 181 ++++++++++-------- packages/vite/src/types/shims.d.ts | 5 - .../vite/types/cssPreprocessorOptions.d.ts | 43 +++++ 3 files changed, 142 insertions(+), 87 deletions(-) create mode 100644 packages/vite/types/cssPreprocessorOptions.d.ts diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 2dddeb710d1864..778698bf670dff 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -27,6 +27,12 @@ import { formatMessages, transform } from 'esbuild' import type { RawSourceMap } from '@ampproject/remapping' import { WorkerWithFallback } from 'artichokie' import { globSync } from 'tinyglobby' +import type { + LessPreprocessorBaseOptions, + SassLegacyPreprocessBaseOptions, + SassModernPreprocessBaseOptions, + StylusPreprocessorBaseOptions, +} from 'types/cssPreprocessorOptions' import { getCodeWithSourcemap, injectSourcesContent } from '../server/sourcemap' import type { EnvironmentModuleNode } from '../server/moduleGraph' import { @@ -111,7 +117,14 @@ export interface CSSOptions { * In addition to options specific to each processors, Vite supports `additionalData` option. * The `additionalData` option can be used to inject extra code for each style content. */ - preprocessorOptions?: Record + preprocessorOptions?: { + scss?: SassPreprocessorOptions + sass?: SassPreprocessorOptions + less?: LessPreprocessorOptions + styl?: StylusPreprocessorOptions + stylus?: StylusPreprocessorOptions + } + /** * If this option is set, preprocessors will run in workers when possible. * `true` means the number of CPUs minus 1. @@ -1135,32 +1148,15 @@ async function compileCSSPreprocessors( const atImportResolvers = getAtImportResolvers( environment.getTopLevelConfig(), ) - - const preProcessor = workerController[lang] - let opts = (preprocessorOptions && preprocessorOptions[lang]) || {} - // support @import from node dependencies by default - switch (lang) { - case PreprocessLang.scss: - case PreprocessLang.sass: - opts = { - includePaths: ['node_modules'], - alias: config.resolve.alias, - ...opts, - } - break - case PreprocessLang.less: - case PreprocessLang.styl: - case PreprocessLang.stylus: - opts = { - paths: ['node_modules'], - alias: config.resolve.alias, - ...opts, - } + const opts = { + ...((preprocessorOptions && preprocessorOptions[lang]) || {}), + alias: config.resolve.alias, + // important: set this for relative import resolving + filename: cleanUrl(id), + enableSourcemap: devSourcemap ?? false, } - // important: set this for relative import resolving - opts.filename = cleanUrl(id) - opts.enableSourcemap = devSourcemap ?? false + const preProcessor = workerController[lang] const preprocessResult = await preProcessor( environment, code, @@ -1977,52 +1973,43 @@ type PreprocessorAdditionalData = | PreprocessorAdditionalDataResult | Promise) -type StylePreprocessorOptions = { - [key: string]: any +type SassPreprocessorOptions = { + additionalData?: PreprocessorAdditionalData +} & ( + | ({ api?: 'legacy' } & SassLegacyPreprocessBaseOptions) + | ({ api: 'modern' | 'modern-compiler' } & SassModernPreprocessBaseOptions) +) + +type LessPreprocessorOptions = { additionalData?: PreprocessorAdditionalData +} & LessPreprocessorBaseOptions + +type StylusPreprocessorOptions = { + additionalData?: PreprocessorAdditionalData +} & StylusPreprocessorBaseOptions + +type StylePreprocessorInternalOptions = { maxWorkers?: number | true filename: string alias: Alias[] enableSourcemap: boolean } -type SassStylePreprocessorOptions = StylePreprocessorOptions & - Omit, 'data' | 'file' | 'outFile'> & { - api?: 'legacy' | 'modern' | 'modern-compiler' - } +type SassStylePreprocessorInternalOptions = StylePreprocessorInternalOptions & + SassPreprocessorOptions -type StylusStylePreprocessorOptions = StylePreprocessorOptions & { - define?: Record -} +type LessStylePreprocessorInternalOptions = StylePreprocessorInternalOptions & + LessPreprocessorOptions -type StylePreprocessor = { - process: ( - environment: PartialEnvironment, - source: string, - root: string, - options: StylePreprocessorOptions, - resolvers: CSSAtImportResolvers, - ) => StylePreprocessorResults | Promise - close: () => void -} +type StylusStylePreprocessorInternalOptions = StylePreprocessorInternalOptions & + StylusPreprocessorOptions -type SassStylePreprocessor = { +type StylePreprocessor = { process: ( environment: PartialEnvironment, source: string, root: string, - options: SassStylePreprocessorOptions, - resolvers: CSSAtImportResolvers, - ) => StylePreprocessorResults | Promise - close: () => void -} - -type StylusStylePreprocessor = { - process: ( - environment: PartialEnvironment, - source: string, - root: string, - options: StylusStylePreprocessorOptions, + options: Options, resolvers: CSSAtImportResolvers, ) => StylePreprocessorResults | Promise close: () => void @@ -2176,7 +2163,10 @@ const makeScssWorker = ( sassPath: string, data: string, // additionalData can a function that is not cloneable but it won't be used - options: SassStylePreprocessorOptions & { additionalData: undefined }, + options: SassStylePreprocessorInternalOptions & { + api: 'legacy' + additionalData: undefined + }, ) => { // eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker const sass: typeof Sass = require(sassPath) @@ -2204,6 +2194,8 @@ const makeScssWorker = ( } const finalOptions: Sass.LegacyOptions<'async'> = { + // support @import from node dependencies by default + includePaths: ['node_modules'], ...options, data, file: options.filename, @@ -2287,7 +2279,10 @@ const makeModernScssWorker = ( sassPath: string, data: string, // additionalData can a function that is not cloneable but it won't be used - options: SassStylePreprocessorOptions & { additionalData: undefined }, + options: SassStylePreprocessorInternalOptions & { + api: 'modern' + additionalData: undefined + }, ) => { // eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker const sass: typeof Sass = require(sassPath) @@ -2453,8 +2448,15 @@ type ScssWorkerResult = { const scssProcessor = ( maxWorkers: number | undefined, -): SassStylePreprocessor => { - const workerMap = new Map>() +): StylePreprocessor => { + const workerMap = new Map< + unknown, + ReturnType< + | typeof makeScssWorker + | typeof makeModernScssWorker + | typeof makeModernCompilerScssWorker + > + >() return { close() { @@ -2511,6 +2513,7 @@ const scssProcessor = ( const result = await worker.run( sassPackage.path, data, + // @ts-expect-error the correct worker is selected for `options.type` optionsWithoutAdditionalData, ) const deps = result.stats.includedFiles.map((f) => cleanScssBugUrl(f)) @@ -2706,7 +2709,9 @@ const makeLessWorker = ( lessPath: string, content: string, // additionalData can a function that is not cloneable but it won't be used - options: StylePreprocessorOptions & { additionalData: undefined }, + options: LessStylePreprocessorInternalOptions & { + additionalData: undefined + }, ) => { // eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker const nodeLess: typeof Less = require(lessPath) @@ -2715,6 +2720,8 @@ const makeLessWorker = ( options.filename, ) const result = await nodeLess.render(content, { + // support @import from node dependencies by default + paths: ['node_modules'], ...options, plugins: [viteResolverPlugin, ...(options.plugins || [])], ...(options.enableSourcemap @@ -2734,7 +2741,7 @@ const makeLessWorker = ( shouldUseFake(_lessPath, _content, options) { // plugins are a function and is not serializable // in that case, fallback to running in main thread - return options.plugins?.length > 0 + return !!options.plugins && options.plugins.length > 0 }, max: maxWorkers, }, @@ -2742,7 +2749,9 @@ const makeLessWorker = ( return worker } -const lessProcessor = (maxWorkers: number | undefined): StylePreprocessor => { +const lessProcessor = ( + maxWorkers: number | undefined, +): StylePreprocessor => { const workerMap = new Map>() return { @@ -2820,12 +2829,18 @@ const makeStylWorker = (maxWorkers: number | undefined) => { content: string, root: string, // additionalData can a function that is not cloneable but it won't be used - options: StylusStylePreprocessorOptions & { additionalData: undefined }, + options: StylusStylePreprocessorInternalOptions & { + additionalData: undefined + }, ) => { // eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker const nodeStylus: typeof Stylus = require(stylusPath) - const ref = nodeStylus(content, options) + const ref = nodeStylus(content, { + // support @import from node dependencies by default + paths: ['node_modules'], + ...options, + }) if (options.define) { for (const key in options.define) { ref.define(key, options.define[key]) @@ -2864,7 +2879,7 @@ const makeStylWorker = (maxWorkers: number | undefined) => { const stylProcessor = ( maxWorkers: number | undefined, -): StylusStylePreprocessor => { +): StylePreprocessor => { const workerMap = new Map>() return { @@ -2981,21 +2996,23 @@ const createPreprocessorWorkerController = (maxWorkers: number | undefined) => { const less = lessProcessor(maxWorkers) const styl = stylProcessor(maxWorkers) - const sassProcess: StylePreprocessor['process'] = ( - environment, - source, - root, - options, - resolvers, - ) => { - return scss.process( - environment, - source, - root, - { ...options, indentedSyntax: true, syntax: 'indented' }, - resolvers, - ) - } + const sassProcess: StylePreprocessor['process'] = + (environment, source, root, options, resolvers) => { + let opts: SassStylePreprocessorInternalOptions + if (options.api === 'modern' || options.api === 'modern-compiler') { + opts = { ...options, syntax: 'indented' as const } + } else { + const narrowedOptions = + options as SassStylePreprocessorInternalOptions & { + api?: 'legacy' + } + opts = { + ...narrowedOptions, + indentedSyntax: true, + } + } + return scss.process(environment, source, root, opts, resolvers) + } const close = () => { less.close() diff --git a/packages/vite/src/types/shims.d.ts b/packages/vite/src/types/shims.d.ts index 3a8c070e3ee5f0..00123f17d682d2 100644 --- a/packages/vite/src/types/shims.d.ts +++ b/packages/vite/src/types/shims.d.ts @@ -32,11 +32,6 @@ declare module 'postcss-import' { export = plugin } -// LESS' types somewhat references this which doesn't make sense in Node, -// so we have to shim it -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -declare interface HTMLLinkElement {} - // eslint-disable-next-line no-var declare var __vite_profile_session: import('node:inspector').Session | undefined // eslint-disable-next-line no-var diff --git a/packages/vite/types/cssPreprocessorOptions.d.ts b/packages/vite/types/cssPreprocessorOptions.d.ts new file mode 100644 index 00000000000000..5064ac18f02a63 --- /dev/null +++ b/packages/vite/types/cssPreprocessorOptions.d.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ + +// @ts-ignore `sass` may not be installed +import type Sass from 'sass' +// @ts-ignore `less` may not be installed +import type Less from 'less' +// @ts-ignore `less` may not be installed +import type Stylus from 'stylus' + +/* eslint-enable @typescript-eslint/ban-ts-comment */ + +export type SassLegacyPreprocessBaseOptions = Omit< + Sass.LegacyStringOptions<'async'>, + | 'data' + | 'file' + | 'outFile' + | 'sourceMap' + | 'omitSourceMapUrl' + | 'sourceMapEmbed' + | 'sourceMapRoot' +> + +export type SassModernPreprocessBaseOptions = Omit< + Sass.StringOptions<'async'>, + 'url' | 'sourceMap' +> + +export type LessPreprocessorBaseOptions = Omit< + Less.Options, + 'sourceMap' | 'filename' +> + +export type StylusPreprocessorBaseOptions = Omit< + Stylus.RenderOptions, + 'filename' +> & { define?: Record } + +declare global { + // LESS' types somewhat references this which doesn't make sense in Node, + // so we have to shim it + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + interface HTMLLinkElement {} +} From 56b71768f3ee498962fba898804086299382bb59 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 24 Oct 2024 16:24:47 +0800 Subject: [PATCH 002/156] fix(dev): prevent double URL encoding in server.open on macOS (#18443) --- packages/vite/src/node/server/openBrowser.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/vite/src/node/server/openBrowser.ts b/packages/vite/src/node/server/openBrowser.ts index 101cd567ad14c4..eebf4b0ad350fe 100644 --- a/packages/vite/src/node/server/openBrowser.ts +++ b/packages/vite/src/node/server/openBrowser.ts @@ -96,9 +96,7 @@ async function startBrowserProcess( if (openedBrowser) { // Try our best to reuse existing tab with AppleScript await execAsync( - `osascript openChrome.applescript "${encodeURI( - url, - )}" "${openedBrowser}"`, + `osascript openChrome.applescript "${url}" "${openedBrowser}"`, { cwd: join(VITE_PACKAGE_DIR, 'bin'), }, From 0a4427fc44b9b2075225bf8a9f1d88a8a428a217 Mon Sep 17 00:00:00 2001 From: Yusuke Mizuno <38448411+hairihou@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:30:09 +0900 Subject: [PATCH 003/156] fix(create-vite): add tsBuildInfoFile option (#18435) --- packages/create-vite/template-preact-ts/tsconfig.app.json | 1 + packages/create-vite/template-preact-ts/tsconfig.node.json | 1 + packages/create-vite/template-qwik-ts/tsconfig.app.json | 1 + packages/create-vite/template-qwik-ts/tsconfig.node.json | 1 + packages/create-vite/template-react-ts/tsconfig.app.json | 1 + packages/create-vite/template-react-ts/tsconfig.node.json | 1 + packages/create-vite/template-solid-ts/tsconfig.app.json | 1 + packages/create-vite/template-solid-ts/tsconfig.node.json | 1 + packages/create-vite/template-vue-ts/tsconfig.app.json | 1 + packages/create-vite/template-vue-ts/tsconfig.node.json | 1 + 10 files changed, 10 insertions(+) diff --git a/packages/create-vite/template-preact-ts/tsconfig.app.json b/packages/create-vite/template-preact-ts/tsconfig.app.json index 634dfa3cfcf6de..00a7cc1e9e6de0 100644 --- a/packages/create-vite/template-preact-ts/tsconfig.app.json +++ b/packages/create-vite/template-preact-ts/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", diff --git a/packages/create-vite/template-preact-ts/tsconfig.node.json b/packages/create-vite/template-preact-ts/tsconfig.node.json index 9dad70185e2dda..abcd7f0dacddc7 100644 --- a/packages/create-vite/template-preact-ts/tsconfig.node.json +++ b/packages/create-vite/template-preact-ts/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "target": "ES2022", "lib": ["ES2023"], "module": "ESNext", diff --git a/packages/create-vite/template-qwik-ts/tsconfig.app.json b/packages/create-vite/template-qwik-ts/tsconfig.app.json index 0052c0d84a6c61..0d65579a463d96 100644 --- a/packages/create-vite/template-qwik-ts/tsconfig.app.json +++ b/packages/create-vite/template-qwik-ts/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", diff --git a/packages/create-vite/template-qwik-ts/tsconfig.node.json b/packages/create-vite/template-qwik-ts/tsconfig.node.json index 9dad70185e2dda..abcd7f0dacddc7 100644 --- a/packages/create-vite/template-qwik-ts/tsconfig.node.json +++ b/packages/create-vite/template-qwik-ts/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "target": "ES2022", "lib": ["ES2023"], "module": "ESNext", diff --git a/packages/create-vite/template-react-ts/tsconfig.app.json b/packages/create-vite/template-react-ts/tsconfig.app.json index 5a2def4b7a3dd0..f867de0dd1a838 100644 --- a/packages/create-vite/template-react-ts/tsconfig.app.json +++ b/packages/create-vite/template-react-ts/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], diff --git a/packages/create-vite/template-react-ts/tsconfig.node.json b/packages/create-vite/template-react-ts/tsconfig.node.json index 9dad70185e2dda..abcd7f0dacddc7 100644 --- a/packages/create-vite/template-react-ts/tsconfig.node.json +++ b/packages/create-vite/template-react-ts/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "target": "ES2022", "lib": ["ES2023"], "module": "ESNext", diff --git a/packages/create-vite/template-solid-ts/tsconfig.app.json b/packages/create-vite/template-solid-ts/tsconfig.app.json index c6ab232ad54098..c811fcbb39008c 100644 --- a/packages/create-vite/template-solid-ts/tsconfig.app.json +++ b/packages/create-vite/template-solid-ts/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", diff --git a/packages/create-vite/template-solid-ts/tsconfig.node.json b/packages/create-vite/template-solid-ts/tsconfig.node.json index 9dad70185e2dda..abcd7f0dacddc7 100644 --- a/packages/create-vite/template-solid-ts/tsconfig.node.json +++ b/packages/create-vite/template-solid-ts/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "target": "ES2022", "lib": ["ES2023"], "module": "ESNext", diff --git a/packages/create-vite/template-vue-ts/tsconfig.app.json b/packages/create-vite/template-vue-ts/tsconfig.app.json index 909eec5c6cf9c4..cb88a5a6c26b25 100644 --- a/packages/create-vite/template-vue-ts/tsconfig.app.json +++ b/packages/create-vite/template-vue-ts/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", diff --git a/packages/create-vite/template-vue-ts/tsconfig.node.json b/packages/create-vite/template-vue-ts/tsconfig.node.json index 9dad70185e2dda..abcd7f0dacddc7 100644 --- a/packages/create-vite/template-vue-ts/tsconfig.node.json +++ b/packages/create-vite/template-vue-ts/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "target": "ES2022", "lib": ["ES2023"], "module": "ESNext", From f1d3bf74cc7f12e759442fd7111d07e2c0262a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 24 Oct 2024 19:01:59 +0900 Subject: [PATCH 004/156] fix!: default `build.cssMinify` to `'esbuild'` for SSR (#15637) --- docs/config/build-options.md | 2 +- docs/guide/migration.md | 2 ++ packages/vite/src/node/build.ts | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 9bff0380a6a02c..891b2e958a9db6 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -130,7 +130,7 @@ In this case, you need to set `build.cssTarget` to `chrome61` to prevent vite fr ## build.cssMinify - **Type:** `boolean | 'esbuild' | 'lightningcss'` -- **Default:** the same as [`build.minify`](#build-minify) +- **Default:** the same as [`build.minify`](#build-minify) for client, `'esbuild'` for SSR This option allows users to override CSS minification specifically instead of defaulting to `build.minify`, so you can configure minification for JS and CSS separately. Vite uses `esbuild` by default to minify CSS. Set the option to `'lightningcss'` to use [Lightning CSS](https://lightningcss.dev/minification.html) instead. If selected, it can be configured using [`css.lightningcss`](./shared-options.md#css-lightningcss). diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 97277aab9c3b07..f4c11d580c9164 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -24,6 +24,8 @@ Vite 6 also introduces a new default value for `json.stringify` which is `'auto' There are other breaking changes which only affect few users. +- [[#15637] fix!: default `build.cssMinify` to `'esbuild'` for SSR](https://github.com/vitejs/vite/pull/15637) + - [`build.cssMinify`](/config/build-options#build-cssminify) is now enabled by default even for SSR builds. - [[#18209] refactor!: bump minimal terser version to 5.16.0](https://github.com/vitejs/vite/pull/18209) - Minimal supported terser version for [`build.minify: 'terser'`](/config/build-options#build-minify) was bumped to 5.16.0 from 5.4.0. - [[#18243] chore(deps)!: migrate `fast-glob` to `tinyglobby`](https://github.com/vitejs/vite/pull/18243) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index e3f170a4b5358d..2f5c51a963dce8 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -369,7 +369,7 @@ export function resolveBuildEnvironmentOptions( cssCodeSplit: !raw.lib, sourcemap: false, rollupOptions: {}, - minify: raw.ssr ? false : 'esbuild', + minify: consumer === 'server' ? false : 'esbuild', terserOptions: {}, write: true, emptyOutDir: null, @@ -434,7 +434,7 @@ export function resolveBuildEnvironmentOptions( } if (resolved.cssMinify == null) { - resolved.cssMinify = !!resolved.minify + resolved.cssMinify = consumer === 'server' ? 'esbuild' : !!resolved.minify } return resolved From 8cc8e51042833c58d91ad42ba96c140ff7aee9ab Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Thu, 24 Oct 2024 11:14:48 +0100 Subject: [PATCH 005/156] chore: replace deprecated `File` type with `RunnerTestFile` (#18448) --- playground/vitestSetup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/vitestSetup.ts b/playground/vitestSetup.ts index 2b2385209b0e32..a1a986d9883554 100644 --- a/playground/vitestSetup.ts +++ b/playground/vitestSetup.ts @@ -21,7 +21,7 @@ import { } from 'vite' import type { Browser, Page } from 'playwright-chromium' import type { RollupError, RollupWatcher, RollupWatcherEvent } from 'rollup' -import type { File } from 'vitest' +import type { RunnerTestFile } from 'vitest' import { beforeAll, inject } from 'vitest' // #region env @@ -81,7 +81,7 @@ export function setViteUrl(url: string): void { // #endregion beforeAll(async (s) => { - const suite = s as File + const suite = s as RunnerTestFile testPath = suite.filepath! testName = slash(testPath).match(/playground\/([\w-]+)\//)?.[1] From f2957c84f69c14c882809889fbd0fc66b97ca3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 24 Oct 2024 19:24:53 +0900 Subject: [PATCH 006/156] refactor: use `originalFileNames`/`names` (#18240) --- package.json | 2 +- packages/vite/package.json | 2 +- packages/vite/src/node/plugins/asset.ts | 17 +- packages/vite/src/node/plugins/css.ts | 11 +- packages/vite/src/node/plugins/html.ts | 3 +- packages/vite/src/node/plugins/manifest.ts | 47 ++- packages/vite/src/node/plugins/worker.ts | 8 +- pnpm-lock.yaml | 318 ++++++++++++--------- 8 files changed, 225 insertions(+), 183 deletions(-) diff --git a/package.json b/package.json index a48ecc09c66669..921cf3d0aa0fc9 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "playwright-chromium": "^1.48.1", "prettier": "3.3.3", "rimraf": "^5.0.10", - "rollup": "^4.22.5", + "rollup": "^4.23.0", "rollup-plugin-esbuild": "^6.1.1", "simple-git-hooks": "^2.11.1", "tslib": "^2.8.0", diff --git a/packages/vite/package.json b/packages/vite/package.json index 672b89b7eee8f9..8627051032314d 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -87,7 +87,7 @@ "dependencies": { "esbuild": "^0.24.0", "postcss": "^8.4.47", - "rollup": "^4.22.5" + "rollup": "^4.23.0" }, "optionalDependencies": { "fsevents": "~2.3.3" diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 2b13308645a0d3..2eaec3d8042320 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -38,17 +38,8 @@ const jsSourceMapRE = /\.[cm]?js\.map$/ const assetCache = new WeakMap>() -// chunk.name is the basename for the asset ignoring the directory structure -// For the manifest, we need to preserve the original file path and isEntry -// for CSS assets. We keep a map from referenceId to this information. -export interface GeneratedAssetMeta { - originalFileName: string - isEntry?: boolean -} -export const generatedAssetsMap = new WeakMap< - Environment, - Map ->() +/** a set of referenceId for entry CSS assets for each environment */ +export const cssEntriesMap = new WeakMap>() // add own dictionary entry by directly assigning mrmime export function registerCustomMime(): void { @@ -146,7 +137,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { buildStart() { assetCache.set(this.environment, new Map()) - generatedAssetsMap.set(this.environment, new Map()) + cssEntriesMap.set(this.environment, new Set()) }, resolveId(id) { @@ -384,8 +375,6 @@ async function fileToBuiltUrl( originalFileName, source: content, }) - generatedAssetsMap.get(environment)!.set(referenceId, { originalFileName }) - url = `__VITE_ASSET__${referenceId}__${postfix ? `$_${postfix}__` : ``}` } diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 778698bf670dff..2fa8a4281a82d0 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -83,9 +83,9 @@ import type { DevEnvironment } from '..' import { addToHTMLProxyTransformResult } from './html' import { assetUrlRE, + cssEntriesMap, fileToDevUrl, fileToUrl, - generatedAssetsMap, publicAssetUrlCache, publicAssetUrlRE, publicFileToBuiltUrl, @@ -447,7 +447,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { assetFileNames({ type: 'asset', name: cssAssetName, + names: [cssAssetName], originalFileName: null, + originalFileNames: [], source: '/* vite internal call, ignore */', }), ) @@ -576,8 +578,6 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { }, async renderChunk(code, chunk, opts) { - const generatedAssets = generatedAssetsMap.get(this.environment)! - let chunkCSS = '' // the chunk is empty if it's a dynamic entry chunk that only contains a CSS import const isJsChunkEmpty = code === '' && !chunk.isEntry @@ -736,7 +736,6 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { originalFileName, source: content, }) - generatedAssets.set(referenceId, { originalFileName }) const filename = this.getFileName(referenceId) chunk.viteMetadata!.importedAssets.add(cleanUrl(filename)) @@ -794,7 +793,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { originalFileName, source: chunkCSS, }) - generatedAssets.set(referenceId, { originalFileName, isEntry }) + if (isEntry) { + cssEntriesMap.get(this.environment)!.add(referenceId) + } chunk.viteMetadata!.importedCss.add(this.getFileName(referenceId)) } else if (this.environment.config.consumer === 'client') { // legacy build and inline css diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 6616c0f35ead41..f660ed1d00d9b3 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -869,7 +869,8 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // inject css link when cssCodeSplit is false if (!this.environment.config.build.cssCodeSplit) { const cssChunk = Object.values(bundle).find( - (chunk) => chunk.type === 'asset' && chunk.name === 'style.css', + (chunk) => + chunk.type === 'asset' && chunk.names.includes('style.css'), ) as OutputAsset | undefined if (cssChunk) { result = injectToHead(result, [ diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index 5ea9779633df37..69538f3704829c 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -8,7 +8,7 @@ import type { import type { Plugin } from '../plugin' import { normalizePath, sortObjectKeys } from '../utils' import { usePerEnvironmentState } from '../environment' -import { generatedAssetsMap } from './asset' +import { cssEntriesMap } from './asset' const endsWithJSRE = /\.[cm]?js$/ @@ -127,18 +127,15 @@ export function manifestPlugin(): Plugin { return manifestChunk } - const assets = generatedAssetsMap.get(this.environment)! - const entryCssAssetFileNames = new Set() - for (const [id, asset] of assets.entries()) { - if (asset.isEntry) { - try { - const fileName = this.getFileName(id) - entryCssAssetFileNames.add(fileName) - } catch { - // The asset was generated as part of a different output option. - // It was already handled during the previous run of this plugin. - assets.delete(id) - } + const entryCssReferenceIds = cssEntriesMap.get(this.environment)! + const entryCssAssetFileNames = new Set(entryCssReferenceIds) + for (const id of entryCssReferenceIds) { + try { + const fileName = this.getFileName(id) + entryCssAssetFileNames.add(fileName) + } catch { + // The asset was generated as part of a different output option. + // It was already handled during the previous run of this plugin. } } @@ -148,28 +145,24 @@ export function manifestPlugin(): Plugin { const chunk = bundle[file] if (chunk.type === 'chunk') { manifest[getChunkName(chunk)] = createChunk(chunk) - } else if (chunk.type === 'asset' && typeof chunk.name === 'string') { + } else if (chunk.type === 'asset' && chunk.names.length > 0) { // Add every unique asset to the manifest, keyed by its original name - const src = chunk.originalFileName ?? chunk.name + const src = + chunk.originalFileNames.length > 0 + ? chunk.originalFileNames[0] + : chunk.names[0] const isEntry = entryCssAssetFileNames.has(chunk.fileName) const asset = createAsset(chunk, src, isEntry) // If JS chunk and asset chunk are both generated from the same source file, // prioritize JS chunk as it contains more information const file = manifest[src]?.file - if (file && endsWithJSRE.test(file)) continue - - manifest[src] = asset - fileNameToAsset.set(chunk.fileName, asset) - } - } + if (!(file && endsWithJSRE.test(file))) { + manifest[src] = asset + fileNameToAsset.set(chunk.fileName, asset) + } - // Add deduplicated assets to the manifest - for (const [referenceId, { originalFileName }] of assets.entries()) { - if (!manifest[originalFileName]) { - const fileName = this.getFileName(referenceId) - const asset = fileNameToAsset.get(fileName) - if (asset) { + for (const originalFileName of chunk.originalFileNames.slice(1)) { manifest[originalFileName] = asset } } diff --git a/packages/vite/src/node/plugins/worker.ts b/packages/vite/src/node/plugins/worker.ts index a480752e3f7451..b20defb42bbac6 100644 --- a/packages/vite/src/node/plugins/worker.ts +++ b/packages/vite/src/node/plugins/worker.ts @@ -23,7 +23,9 @@ import { fileToUrl } from './asset' type WorkerBundleAsset = { fileName: string + /** @deprecated */ originalFileName: string | null + originalFileNames: string[] source: string | Uint8Array } @@ -122,6 +124,7 @@ async function bundleWorkerEntry( saveEmitWorkerAsset(config, { fileName: outputChunk.fileName, originalFileName: null, + originalFileNames: [], source: outputChunk.code, }) } @@ -159,6 +162,7 @@ function emitSourcemapForWorkerEntry( saveEmitWorkerAsset(config, { fileName: mapFileName, originalFileName: null, + originalFileNames: [], source: data, }) } @@ -193,6 +197,7 @@ export async function workerFileToUrl( saveEmitWorkerAsset(config, { fileName, originalFileName: null, + originalFileNames: [], source: outputChunk.code, }) workerMap.bundle.set(id, fileName) @@ -468,8 +473,9 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin { this.emitFile({ type: 'asset', fileName: asset.fileName, - originalFileName: asset.originalFileName, source: asset.source, + // NOTE: fileName is already generated when bundling the worker + // so no need to pass originalFileNames/names }) }) workerMap.assets.clear() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 496fb78cc3ac2d..0365cbc5686d6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,11 +113,11 @@ importers: specifier: ^5.0.10 version: 5.0.10 rollup: - specifier: ^4.22.5 - version: 4.22.5 + specifier: ^4.23.0 + version: 4.23.0 rollup-plugin-esbuild: specifier: ^6.1.1 - version: 6.1.1(esbuild@0.24.0)(rollup@4.22.5) + version: 6.1.1(esbuild@0.24.0)(rollup@4.23.0) simple-git-hooks: specifier: ^2.11.1 version: 2.11.1 @@ -234,8 +234,8 @@ importers: specifier: ^8.4.47 version: 8.4.47 rollup: - specifier: ^4.22.5 - version: 4.22.5 + specifier: ^4.23.0 + version: 4.23.0 optionalDependencies: fsevents: specifier: ~2.3.3 @@ -255,22 +255,22 @@ importers: version: 1.0.0-next.25 '@rollup/plugin-alias': specifier: ^5.1.1 - version: 5.1.1(rollup@4.22.5) + version: 5.1.1(rollup@4.23.0) '@rollup/plugin-commonjs': specifier: ^28.0.1 - version: 28.0.1(rollup@4.22.5) + version: 28.0.1(rollup@4.23.0) '@rollup/plugin-dynamic-import-vars': specifier: ^2.1.4 - version: 2.1.4(rollup@4.22.5) + version: 2.1.4(rollup@4.23.0) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.22.5) + version: 6.1.0(rollup@4.23.0) '@rollup/plugin-node-resolve': specifier: 15.3.0 - version: 15.3.0(rollup@4.22.5) + version: 15.3.0(rollup@4.23.0) '@rollup/pluginutils': specifier: ^5.1.2 - version: 5.1.3(rollup@4.22.5) + version: 5.1.3(rollup@4.23.0) '@types/escape-html': specifier: ^1.0.4 version: 1.0.4 @@ -378,13 +378,13 @@ importers: version: 2.0.2 rollup-plugin-dts: specifier: ^6.1.1 - version: 6.1.1(rollup@4.22.5)(typescript@5.6.2) + version: 6.1.1(rollup@4.23.0)(typescript@5.6.2) rollup-plugin-esbuild: specifier: ^6.1.1 - version: 6.1.1(esbuild@0.24.0)(rollup@4.22.5) + version: 6.1.1(esbuild@0.24.0)(rollup@4.23.0) rollup-plugin-license: specifier: ^3.5.3 - version: 3.5.3(picomatch@4.0.2)(rollup@4.22.5) + version: 3.5.3(picomatch@4.0.2)(rollup@4.23.0) sass: specifier: ^1.80.3 version: 1.80.3 @@ -3037,6 +3037,15 @@ packages: '@polka/url@1.0.0-next.24': resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} + '@rollup/plugin-alias@5.1.0': + resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -3082,6 +3091,15 @@ packages: rollup: optional: true + '@rollup/plugin-node-resolve@15.2.3': + resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/plugin-node-resolve@15.3.0': resolution: {integrity: sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==} engines: {node: '>=14.0.0'} @@ -3100,6 +3118,15 @@ packages: rollup: optional: true + '@rollup/pluginutils@5.1.2': + resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/pluginutils@5.1.3': resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} engines: {node: '>=14.0.0'} @@ -3109,83 +3136,83 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.22.5': - resolution: {integrity: sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==} + '@rollup/rollup-android-arm-eabi@4.23.0': + resolution: {integrity: sha512-8OR+Ok3SGEMsAZispLx8jruuXw0HVF16k+ub2eNXKHDmdxL4cf9NlNpAzhlOhNyXzKDEJuFeq0nZm+XlNb1IFw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.22.5': - resolution: {integrity: sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==} + '@rollup/rollup-android-arm64@4.23.0': + resolution: {integrity: sha512-rEFtX1nP8gqmLmPZsXRMoLVNB5JBwOzIAk/XAcEPuKrPa2nPJ+DuGGpfQUR0XjRm8KjHfTZLpWbKXkA5BoFL3w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.22.5': - resolution: {integrity: sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==} + '@rollup/rollup-darwin-arm64@4.23.0': + resolution: {integrity: sha512-ZbqlMkJRMMPeapfaU4drYHns7Q5MIxjM/QeOO62qQZGPh9XWziap+NF9fsqPHT0KzEL6HaPspC7sOwpgyA3J9g==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.22.5': - resolution: {integrity: sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==} + '@rollup/rollup-darwin-x64@4.23.0': + resolution: {integrity: sha512-PfmgQp78xx5rBCgn2oYPQ1rQTtOaQCna0kRaBlc5w7RlA3TDGGo7m3XaptgitUZ54US9915i7KeVPHoy3/W8tA==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.22.5': - resolution: {integrity: sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==} + '@rollup/rollup-linux-arm-gnueabihf@4.23.0': + resolution: {integrity: sha512-WAeZfAAPus56eQgBioezXRRzArAjWJGjNo/M+BHZygUcs9EePIuGI1Wfc6U/Ki+tMW17FFGvhCfYnfcKPh18SA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.22.5': - resolution: {integrity: sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==} + '@rollup/rollup-linux-arm-musleabihf@4.23.0': + resolution: {integrity: sha512-v7PGcp1O5XKZxKX8phTXtmJDVpE20Ub1eF6w9iMmI3qrrPak6yR9/5eeq7ziLMrMTjppkkskXyxnmm00HdtXjA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.22.5': - resolution: {integrity: sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==} + '@rollup/rollup-linux-arm64-gnu@4.23.0': + resolution: {integrity: sha512-nAbWsDZ9UkU6xQiXEyXBNHAKbzSAi95H3gTStJq9UGiS1v+YVXwRHcQOQEF/3CHuhX5BVhShKoeOf6Q/1M+Zhg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.22.5': - resolution: {integrity: sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==} + '@rollup/rollup-linux-arm64-musl@4.23.0': + resolution: {integrity: sha512-5QT/Di5FbGNPaVw8hHO1wETunwkPuZBIu6W+5GNArlKHD9fkMHy7vS8zGHJk38oObXfWdsuLMogD4sBySLJ54g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.22.5': - resolution: {integrity: sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==} + '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': + resolution: {integrity: sha512-Sefl6vPyn5axzCsO13r1sHLcmPuiSOrKIImnq34CBurntcJ+lkQgAaTt/9JkgGmaZJ+OkaHmAJl4Bfd0DmdtOQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.22.5': - resolution: {integrity: sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==} + '@rollup/rollup-linux-riscv64-gnu@4.23.0': + resolution: {integrity: sha512-o4QI2KU/QbP7ZExMse6ULotdV3oJUYMrdx3rBZCgUF3ur3gJPfe8Fuasn6tia16c5kZBBw0aTmaUygad6VB/hQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.22.5': - resolution: {integrity: sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==} + '@rollup/rollup-linux-s390x-gnu@4.23.0': + resolution: {integrity: sha512-+bxqx+V/D4FGrpXzPGKp/SEZIZ8cIW3K7wOtcJAoCrmXvzRtmdUhYNbgd+RztLzfDEfA2WtKj5F4tcbNPuqgeg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.22.5': - resolution: {integrity: sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==} + '@rollup/rollup-linux-x64-gnu@4.23.0': + resolution: {integrity: sha512-I/eXsdVoCKtSgK9OwyQKPAfricWKUMNCwJKtatRYMmDo5N859tbO3UsBw5kT3dU1n6ZcM1JDzPRSGhAUkxfLxw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.22.5': - resolution: {integrity: sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==} + '@rollup/rollup-linux-x64-musl@4.23.0': + resolution: {integrity: sha512-4ZoDZy5ShLbbe1KPSafbFh1vbl0asTVfkABC7eWqIs01+66ncM82YJxV2VtV3YVJTqq2P8HMx3DCoRSWB/N3rw==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.22.5': - resolution: {integrity: sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==} + '@rollup/rollup-win32-arm64-msvc@4.23.0': + resolution: {integrity: sha512-+5Ky8dhft4STaOEbZu3/NU4QIyYssKO+r1cD3FzuusA0vO5gso15on7qGzKdNXnc1gOrsgCqZjRw1w+zL4y4hQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.22.5': - resolution: {integrity: sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==} + '@rollup/rollup-win32-ia32-msvc@4.23.0': + resolution: {integrity: sha512-0SPJk4cPZQhq9qA1UhIRumSE3+JJIBBjtlGl5PNC///BoaByckNZd53rOYD0glpTkYFBQSt7AkMeLVPfx65+BQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.22.5': - resolution: {integrity: sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==} + '@rollup/rollup-win32-x64-msvc@4.23.0': + resolution: {integrity: sha512-lqCK5GQC8fNo0+JvTSxcG7YB1UKYp8yrNLhsArlvPWN+16ovSZgoehlVHg6X0sSWPUkpjRBR5TuR12ZugowZ4g==} cpu: [x64] os: [win32] @@ -4055,6 +4082,10 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -5086,6 +5117,10 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + is-core-module@2.14.0: resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} engines: {node: '>= 0.4'} @@ -6225,13 +6260,13 @@ packages: peerDependencies: rollup: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 - rollup@3.29.4: - resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true - rollup@4.22.5: - resolution: {integrity: sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==} + rollup@4.23.0: + resolution: {integrity: sha512-vXB4IT9/KLDrS2WRXmY22sVB2wTsTwkpxjB8Q3mnakTENcYw3FRmfdYDy/acNmls+lHmDazgrRjK/yQ6hQAtwA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -8447,28 +8482,30 @@ snapshots: '@polka/url@1.0.0-next.24': {} - '@rollup/plugin-alias@5.1.1(rollup@3.29.4)': + '@rollup/plugin-alias@5.1.0(rollup@3.29.5)': + dependencies: + slash: 4.0.0 optionalDependencies: - rollup: 3.29.4 + rollup: 3.29.5 - '@rollup/plugin-alias@5.1.1(rollup@4.22.5)': + '@rollup/plugin-alias@5.1.1(rollup@4.23.0)': optionalDependencies: - rollup: 4.22.5 + rollup: 4.23.0 - '@rollup/plugin-commonjs@25.0.4(rollup@3.29.4)': + '@rollup/plugin-commonjs@25.0.4(rollup@3.29.5)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@3.29.4) + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) commondir: 1.0.1 estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 magic-string: 0.27.0 optionalDependencies: - rollup: 3.29.4 + rollup: 3.29.5 - '@rollup/plugin-commonjs@28.0.1(rollup@4.22.5)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.23.0)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.22.5) + '@rollup/pluginutils': 5.1.3(rollup@4.23.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.0(picomatch@4.0.2) @@ -8476,119 +8513,128 @@ snapshots: magic-string: 0.30.12 picomatch: 4.0.2 optionalDependencies: - rollup: 4.22.5 + rollup: 4.23.0 - '@rollup/plugin-dynamic-import-vars@2.1.4(rollup@4.22.5)': + '@rollup/plugin-dynamic-import-vars@2.1.4(rollup@4.23.0)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.22.5) + '@rollup/pluginutils': 5.1.3(rollup@4.23.0) astring: 1.8.6 estree-walker: 2.0.2 magic-string: 0.30.12 tinyglobby: 0.2.9 optionalDependencies: - rollup: 4.22.5 + rollup: 4.23.0 - '@rollup/plugin-json@6.1.0(rollup@3.29.4)': + '@rollup/plugin-json@6.1.0(rollup@3.29.5)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@3.29.4) + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) optionalDependencies: - rollup: 3.29.4 + rollup: 3.29.5 - '@rollup/plugin-json@6.1.0(rollup@4.22.5)': + '@rollup/plugin-json@6.1.0(rollup@4.23.0)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.22.5) + '@rollup/pluginutils': 5.1.3(rollup@4.23.0) optionalDependencies: - rollup: 4.22.5 + rollup: 4.23.0 - '@rollup/plugin-node-resolve@15.3.0(rollup@3.29.4)': + '@rollup/plugin-node-resolve@15.2.3(rollup@3.29.5)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@3.29.4) + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) '@types/resolve': 1.20.2 deepmerge: 4.2.2 + is-builtin-module: 3.2.1 is-module: 1.0.0 resolve: 1.22.8 optionalDependencies: - rollup: 3.29.4 + rollup: 3.29.5 - '@rollup/plugin-node-resolve@15.3.0(rollup@4.22.5)': + '@rollup/plugin-node-resolve@15.3.0(rollup@4.23.0)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.22.5) + '@rollup/pluginutils': 5.1.3(rollup@4.23.0) '@types/resolve': 1.20.2 deepmerge: 4.2.2 is-module: 1.0.0 resolve: 1.22.8 optionalDependencies: - rollup: 4.22.5 + rollup: 4.23.0 - '@rollup/plugin-replace@5.0.2(rollup@3.29.4)': + '@rollup/plugin-replace@5.0.2(rollup@3.29.5)': dependencies: - '@rollup/pluginutils': 5.1.3(rollup@3.29.4) + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) magic-string: 0.27.0 optionalDependencies: - rollup: 3.29.4 + rollup: 3.29.5 - '@rollup/pluginutils@5.1.3(rollup@3.29.4)': + '@rollup/pluginutils@5.1.2(rollup@3.29.5)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@5.1.3(rollup@3.29.5)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 3.29.4 + rollup: 3.29.5 - '@rollup/pluginutils@5.1.3(rollup@4.22.5)': + '@rollup/pluginutils@5.1.3(rollup@4.23.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.22.5 + rollup: 4.23.0 - '@rollup/rollup-android-arm-eabi@4.22.5': + '@rollup/rollup-android-arm-eabi@4.23.0': optional: true - '@rollup/rollup-android-arm64@4.22.5': + '@rollup/rollup-android-arm64@4.23.0': optional: true - '@rollup/rollup-darwin-arm64@4.22.5': + '@rollup/rollup-darwin-arm64@4.23.0': optional: true - '@rollup/rollup-darwin-x64@4.22.5': + '@rollup/rollup-darwin-x64@4.23.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.22.5': + '@rollup/rollup-linux-arm-gnueabihf@4.23.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.22.5': + '@rollup/rollup-linux-arm-musleabihf@4.23.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.22.5': + '@rollup/rollup-linux-arm64-gnu@4.23.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.22.5': + '@rollup/rollup-linux-arm64-musl@4.23.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.22.5': + '@rollup/rollup-linux-powerpc64le-gnu@4.23.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.22.5': + '@rollup/rollup-linux-riscv64-gnu@4.23.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.22.5': + '@rollup/rollup-linux-s390x-gnu@4.23.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.22.5': + '@rollup/rollup-linux-x64-gnu@4.23.0': optional: true - '@rollup/rollup-linux-x64-musl@4.22.5': + '@rollup/rollup-linux-x64-musl@4.23.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.22.5': + '@rollup/rollup-win32-arm64-msvc@4.23.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.22.5': + '@rollup/rollup-win32-ia32-msvc@4.23.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.22.5': + '@rollup/rollup-win32-x64-msvc@4.23.0': optional: true '@sec-ant/readable-stream@0.4.1': {} @@ -9533,6 +9579,8 @@ snapshots: buffer-from@1.1.2: {} + builtin-modules@3.3.0: {} + bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 @@ -10723,6 +10771,10 @@ snapshots: dependencies: binary-extensions: 2.2.0 + is-builtin-module@3.2.1: + dependencies: + builtin-modules: 3.3.0 + is-core-module@2.14.0: dependencies: hasown: 2.0.2 @@ -11925,34 +11977,34 @@ snapshots: dependencies: glob: 10.4.5 - rollup-plugin-dts@6.1.1(rollup@3.29.4)(typescript@5.6.2): + rollup-plugin-dts@6.1.1(rollup@3.29.5)(typescript@5.6.2): dependencies: magic-string: 0.30.12 - rollup: 3.29.4 + rollup: 3.29.5 typescript: 5.6.2 optionalDependencies: '@babel/code-frame': 7.25.7 - rollup-plugin-dts@6.1.1(rollup@4.22.5)(typescript@5.6.2): + rollup-plugin-dts@6.1.1(rollup@4.23.0)(typescript@5.6.2): dependencies: magic-string: 0.30.12 - rollup: 4.22.5 + rollup: 4.23.0 typescript: 5.6.2 optionalDependencies: '@babel/code-frame': 7.25.7 - rollup-plugin-esbuild@6.1.1(esbuild@0.24.0)(rollup@4.22.5): + rollup-plugin-esbuild@6.1.1(esbuild@0.24.0)(rollup@4.23.0): dependencies: - '@rollup/pluginutils': 5.1.3(rollup@4.22.5) + '@rollup/pluginutils': 5.1.3(rollup@4.23.0) debug: 4.3.7 es-module-lexer: 1.5.4 esbuild: 0.24.0 get-tsconfig: 4.7.5 - rollup: 4.22.5 + rollup: 4.23.0 transitivePeerDependencies: - supports-color - rollup-plugin-license@3.5.3(picomatch@4.0.2)(rollup@4.22.5): + rollup-plugin-license@3.5.3(picomatch@4.0.2)(rollup@4.23.0): dependencies: commenting: 1.1.0 fdir: 6.3.0(picomatch@4.0.2) @@ -11960,36 +12012,36 @@ snapshots: magic-string: 0.30.12 moment: 2.30.1 package-name-regex: 2.0.6 - rollup: 4.22.5 + rollup: 4.23.0 spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 transitivePeerDependencies: - picomatch - rollup@3.29.4: + rollup@3.29.5: optionalDependencies: fsevents: 2.3.3 - rollup@4.22.5: + rollup@4.23.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.22.5 - '@rollup/rollup-android-arm64': 4.22.5 - '@rollup/rollup-darwin-arm64': 4.22.5 - '@rollup/rollup-darwin-x64': 4.22.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.22.5 - '@rollup/rollup-linux-arm-musleabihf': 4.22.5 - '@rollup/rollup-linux-arm64-gnu': 4.22.5 - '@rollup/rollup-linux-arm64-musl': 4.22.5 - '@rollup/rollup-linux-powerpc64le-gnu': 4.22.5 - '@rollup/rollup-linux-riscv64-gnu': 4.22.5 - '@rollup/rollup-linux-s390x-gnu': 4.22.5 - '@rollup/rollup-linux-x64-gnu': 4.22.5 - '@rollup/rollup-linux-x64-musl': 4.22.5 - '@rollup/rollup-win32-arm64-msvc': 4.22.5 - '@rollup/rollup-win32-ia32-msvc': 4.22.5 - '@rollup/rollup-win32-x64-msvc': 4.22.5 + '@rollup/rollup-android-arm-eabi': 4.23.0 + '@rollup/rollup-android-arm64': 4.23.0 + '@rollup/rollup-darwin-arm64': 4.23.0 + '@rollup/rollup-darwin-x64': 4.23.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.23.0 + '@rollup/rollup-linux-arm-musleabihf': 4.23.0 + '@rollup/rollup-linux-arm64-gnu': 4.23.0 + '@rollup/rollup-linux-arm64-musl': 4.23.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.23.0 + '@rollup/rollup-linux-riscv64-gnu': 4.23.0 + '@rollup/rollup-linux-s390x-gnu': 4.23.0 + '@rollup/rollup-linux-x64-gnu': 4.23.0 + '@rollup/rollup-linux-x64-musl': 4.23.0 + '@rollup/rollup-win32-arm64-msvc': 4.23.0 + '@rollup/rollup-win32-ia32-msvc': 4.23.0 + '@rollup/rollup-win32-x64-msvc': 4.23.0 fsevents: 2.3.3 run-applescript@7.0.0: {} @@ -12575,12 +12627,12 @@ snapshots: unbuild@2.0.0(sass@1.80.3)(typescript@5.6.2): dependencies: - '@rollup/plugin-alias': 5.1.1(rollup@3.29.4) - '@rollup/plugin-commonjs': 25.0.4(rollup@3.29.4) - '@rollup/plugin-json': 6.1.0(rollup@3.29.4) - '@rollup/plugin-node-resolve': 15.3.0(rollup@3.29.4) - '@rollup/plugin-replace': 5.0.2(rollup@3.29.4) - '@rollup/pluginutils': 5.1.3(rollup@3.29.4) + '@rollup/plugin-alias': 5.1.0(rollup@3.29.5) + '@rollup/plugin-commonjs': 25.0.4(rollup@3.29.5) + '@rollup/plugin-json': 6.1.0(rollup@3.29.5) + '@rollup/plugin-node-resolve': 15.2.3(rollup@3.29.5) + '@rollup/plugin-replace': 5.0.2(rollup@3.29.5) + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) chalk: 5.3.0 citty: 0.1.4 consola: 3.2.3 @@ -12595,8 +12647,8 @@ snapshots: pathe: 1.1.2 pkg-types: 1.2.0 pretty-bytes: 6.1.1 - rollup: 3.29.4 - rollup-plugin-dts: 6.1.1(rollup@3.29.4)(typescript@5.6.2) + rollup: 3.29.5 + rollup-plugin-dts: 6.1.1(rollup@3.29.5)(typescript@5.6.2) scule: 1.0.0 untyped: 1.4.0 optionalDependencies: From d23a493cc4b54a2e2b2c1337b3b1f0c9b1be311e Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 24 Oct 2024 18:26:47 +0800 Subject: [PATCH 007/156] feat(css)!: load postcss config within workspace root only (#18440) --- docs/config/shared-options.md | 2 +- packages/vite/src/node/plugins/css.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/config/shared-options.md b/docs/config/shared-options.md index b803e36cc166ca..0ea4aeeb63bcd9 100644 --- a/docs/config/shared-options.md +++ b/docs/config/shared-options.md @@ -217,7 +217,7 @@ Inline PostCSS config or a custom directory to search PostCSS config from (defau For inline PostCSS config, it expects the same format as `postcss.config.js`. But for `plugins` property, only [array format](https://github.com/postcss/postcss-load-config/blob/main/README.md#array) can be used. -The search is done using [postcss-load-config](https://github.com/postcss/postcss-load-config) and only the supported config file names are loaded. +The search is done using [postcss-load-config](https://github.com/postcss/postcss-load-config) and only the supported config file names are loaded. Config files outside the workspace root (or the [project root](/guide/#index-html-and-project-root) if no workspace is found) are not searched by default. You can specify a custom path outside of the root to load the specific config file instead if needed. Note if an inline config is provided, Vite will not search for other PostCSS config sources. diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 2fa8a4281a82d0..958c923e146831 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -79,7 +79,8 @@ import { createBackCompatIdResolver } from '../idResolver' import type { ResolveIdFn } from '../idResolver' import { PartialEnvironment } from '../baseEnvironment' import type { TransformPluginContext } from '../server/pluginContainer' -import type { DevEnvironment } from '..' +import { searchForWorkspaceRoot } from '../server/searchRoot' +import { type DevEnvironment } from '..' import { addToHTMLProxyTransformResult } from './html' import { assetUrlRE, @@ -1637,7 +1638,8 @@ async function resolvePostcssConfig( } else { const searchPath = typeof inlineOptions === 'string' ? inlineOptions : config.root - result = postcssrc({}, searchPath).catch((e) => { + const stopDir = searchForWorkspaceRoot(config.root) + result = postcssrc({}, searchPath, { stopDir }).catch((e) => { if (!e.message.includes('No PostCSS Config found')) { if (e instanceof Error) { const { name, message, stack } = e From 0ab20a3ee26eacf302415b3087732497d0a2f358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 24 Oct 2024 21:04:10 +0900 Subject: [PATCH 008/156] fix(ssr): inject identity function at the top (#18449) --- .../node/ssr/__tests__/ssrTransform.spec.ts | 48 ++++++++++++++----- packages/vite/src/node/ssr/ssrTransform.ts | 5 +- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index f78432517a8a00..fa8bd6e52a302c 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -26,8 +26,8 @@ test('named import', async () => { `import { ref } from 'vue';function foo() { return ref(0) }`, ), ).toMatchInlineSnapshot(` - "const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref"]}); - const __vite_ssr_identity__ = v => v; + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref"]}); function foo() { return __vite_ssr_identity__(__vite_ssr_import_0__.ref)(0) }" `) }) @@ -38,8 +38,8 @@ test('named import: arbitrary module namespace specifier', async () => { `import { "some thing" as ref } from 'vue';function foo() { return ref(0) }`, ), ).toMatchInlineSnapshot(` - "const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["some thing"]}); - const __vite_ssr_identity__ = v => v; + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["some thing"]}); function foo() { return __vite_ssr_identity__(__vite_ssr_import_0__["some thing"])(0) }" `) }) @@ -223,8 +223,8 @@ test('do not rewrite method definition', async () => { `import { fn } from 'vue';class A { fn() { fn() } }`, ) expect(result?.code).toMatchInlineSnapshot(` - "const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["fn"]}); - const __vite_ssr_identity__ = v => v; + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["fn"]}); class A { fn() { __vite_ssr_identity__(__vite_ssr_import_0__.fn)() } }" `) expect(result?.deps).toEqual(['vue']) @@ -536,8 +536,8 @@ test('overwrite bindings', async () => { `function g() { const f = () => { const inject = true }; console.log(inject) }\n`, ), ).toMatchInlineSnapshot(` - "const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["inject"]}); - const __vite_ssr_identity__ = v => v; + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["inject"]}); const a = { inject: __vite_ssr_import_0__.inject } const b = { test: __vite_ssr_import_0__.inject } function c() { const { test: inject } = { test: true }; console.log(inject) } @@ -862,8 +862,8 @@ test('jsx', async () => { const result = await transformWithEsbuild(code, id) expect(await ssrTransformSimpleCode(result.code, '/foo.jsx')) .toMatchInlineSnapshot(` - "const __vite_ssr_import_0__ = await __vite_ssr_import__("react", {"importedNames":["default"]}); - const __vite_ssr_identity__ = v => v; + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("react", {"importedNames":["default"]}); const __vite_ssr_import_1__ = await __vite_ssr_import__("foo", {"importedNames":["Foo","Slot"]}); @@ -955,8 +955,8 @@ foo()`, ), ).toMatchInlineSnapshot(` "#!/usr/bin/env node - const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]}); const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]}); __vite_ssr_identity__(__vite_ssr_import_0__.foo)()" `) @@ -992,8 +992,8 @@ export class Test { };`.trim() expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(` - "const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]}); - const __vite_ssr_identity__ = v => v; + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]}); if (false) { const foo = 'foo' @@ -1183,3 +1183,25 @@ console.log(foo + 2) " `) }) + +test('identity function is declared before used', async () => { + expect( + await ssrTransformSimpleCode(` +import { foo } from './foo' +export default foo() +export * as bar from './bar' +console.log(bar) + `), + ).toMatchInlineSnapshot(` + "const __vite_ssr_identity__ = v => v; + const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["foo"]}); + + + __vite_ssr_exports__.default = __vite_ssr_identity__(__vite_ssr_import_0__.foo)() + const __vite_ssr_import_1__ = await __vite_ssr_import__("./bar"); + + Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }}); + console.log(bar) + " + `) +}) diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 1e5a398a91e151..a93b2c9d8a73ba 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -99,7 +99,8 @@ async function ssrTransformScript( const declaredConst = new Set() // hoist at the start of the file, after the hashbang - let hoistIndex = hashbangRE.exec(code)?.[0].length ?? 0 + const fileStartIndex = hashbangRE.exec(code)?.[0].length ?? 0 + let hoistIndex = fileStartIndex function defineImport( index: number, @@ -375,7 +376,7 @@ async function ssrTransformScript( }) if (injectIdentityFunction) { - s.prependLeft(hoistIndex, `const ${ssrIdentityFunction} = v => v;\n`) + s.prependLeft(fileStartIndex, `const ${ssrIdentityFunction} = v => v;\n`) } let map = s.generateMap({ hires: 'boundary' }) From 91a1acb12058d7f8ea357b7564992936eed62bc7 Mon Sep 17 00:00:00 2001 From: bluwy Date: Thu, 24 Oct 2024 23:41:59 +0800 Subject: [PATCH 009/156] release: v6.0.0-beta.5 --- packages/vite/CHANGELOG.md | 16 ++++++++++++++++ packages/vite/package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index ea6d5e972ddfec..c3b64519f5e956 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,19 @@ +## 6.0.0-beta.5 (2024-10-24) + +* fix: add typing to `CSSOptions.preprocessorOptions` (#18001) ([7eeb6f2](https://github.com/vitejs/vite/commit/7eeb6f2f97abf5dfc71c225b9cff9779baf2ed2f)), closes [#18001](https://github.com/vitejs/vite/issues/18001) +* fix(dev): prevent double URL encoding in server.open on macOS (#18443) ([56b7176](https://github.com/vitejs/vite/commit/56b71768f3ee498962fba898804086299382bb59)), closes [#18443](https://github.com/vitejs/vite/issues/18443) +* fix(preview): set resolvedUrls null after close (#18445) ([65014a3](https://github.com/vitejs/vite/commit/65014a32ef618619c5a34b729d67340d9253bdd5)), closes [#18445](https://github.com/vitejs/vite/issues/18445) +* fix(ssr): inject identity function at the top (#18449) ([0ab20a3](https://github.com/vitejs/vite/commit/0ab20a3ee26eacf302415b3087732497d0a2f358)), closes [#18449](https://github.com/vitejs/vite/issues/18449) +* fix(ssr): preserve source maps for hoisted imports (fix #16355) (#16356) ([8e382a6](https://github.com/vitejs/vite/commit/8e382a6a1fed2cd41051b81f9cd9c94b484352a5)), closes [#16355](https://github.com/vitejs/vite/issues/16355) [#16356](https://github.com/vitejs/vite/issues/16356) +* feat(css)!: load postcss config within workspace root only (#18440) ([d23a493](https://github.com/vitejs/vite/commit/d23a493cc4b54a2e2b2c1337b3b1f0c9b1be311e)), closes [#18440](https://github.com/vitejs/vite/issues/18440) +* feat(json)!: add `json.stringify: 'auto'` and make that the default (#18303) ([b80daa7](https://github.com/vitejs/vite/commit/b80daa7c0970645dca569d572892648f66c6799c)), closes [#18303](https://github.com/vitejs/vite/issues/18303) +* fix!: default `build.cssMinify` to `'esbuild'` for SSR (#15637) ([f1d3bf7](https://github.com/vitejs/vite/commit/f1d3bf74cc7f12e759442fd7111d07e2c0262a67)), closes [#15637](https://github.com/vitejs/vite/issues/15637) +* refactor: use `originalFileNames`/`names` (#18240) ([f2957c8](https://github.com/vitejs/vite/commit/f2957c84f69c14c882809889fbd0fc66b97ca3e9)), closes [#18240](https://github.com/vitejs/vite/issues/18240) +* test: fix test conflict (#18446) ([94cd1e6](https://github.com/vitejs/vite/commit/94cd1e6f95e2434d2b52b5c16d50fe0472214634)), closes [#18446](https://github.com/vitejs/vite/issues/18446) +* chore(deps): update dependency picomatch to v4 (#15876) ([3774881](https://github.com/vitejs/vite/commit/377488178a7ef372d9b76526bb01fd60b97f51df)), closes [#15876](https://github.com/vitejs/vite/issues/15876) + + + ## 6.0.0-beta.4 (2024-10-23) * refactor: use builder in `build` (#18432) ([cc61d16](https://github.com/vitejs/vite/commit/cc61d169a4826996f7b2289618c383f8c5c6d470)), closes [#18432](https://github.com/vitejs/vite/issues/18432) diff --git a/packages/vite/package.json b/packages/vite/package.json index 8627051032314d..0b4d6d8958ee38 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "6.0.0-beta.4", + "version": "6.0.0-beta.5", "type": "module", "license": "MIT", "author": "Evan You", From 7f9f8c6851d1eb49a72dcb6c134873148a2e81eb Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 25 Oct 2024 13:54:11 +0900 Subject: [PATCH 010/156] fix: use websocket to test server liveness before client reload (#17891) Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com> --- packages/vite/src/client/client.ts | 40 +++++----- packages/vite/src/node/server/ws.ts | 39 +++++++++- .../__tests__/client-reload.spec.ts | 75 +++++++++++++++++++ playground/client-reload/__tests__/serve.ts | 6 ++ playground/client-reload/index.html | 4 + playground/client-reload/package.json | 12 +++ playground/client-reload/vite.config.ts | 5 ++ playground/js-sourcemap/test-ssr-dev.js | 1 + playground/ssr-html/test-network-imports.js | 1 + .../ssr-html/test-stacktrace-runtime.js | 1 + playground/ssr-html/test-stacktrace.js | 1 + playground/test-utils.ts | 5 ++ pnpm-lock.yaml | 2 + 13 files changed, 170 insertions(+), 22 deletions(-) create mode 100644 playground/client-reload/__tests__/client-reload.spec.ts create mode 100644 playground/client-reload/__tests__/serve.ts create mode 100644 playground/client-reload/index.html create mode 100644 playground/client-reload/package.json create mode 100644 playground/client-reload/vite.config.ts diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index 3563454f72761a..d51dd73c6357f2 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -331,24 +331,28 @@ async function waitForSuccessfulPing( hostAndPath: string, ms = 1000, ) { - const pingHostProtocol = socketProtocol === 'wss' ? 'https' : 'http' - - const ping = async () => { - // A fetch on a websocket URL will return a successful promise with status 400, - // but will reject a networking error. - // When running on middleware mode, it returns status 426, and a cors error happens if mode is not no-cors - try { - await fetch(`${pingHostProtocol}://${hostAndPath}`, { - mode: 'no-cors', - headers: { - // Custom headers won't be included in a request with no-cors so (ab)use one of the - // safelisted headers to identify the ping request - Accept: 'text/x-vite-ping', - }, - }) - return true - } catch {} - return false + async function ping() { + const socket = new WebSocket( + `${socketProtocol}://${hostAndPath}`, + 'vite-ping', + ) + return new Promise((resolve) => { + function onOpen() { + resolve(true) + close() + } + function onError() { + resolve(false) + close() + } + function close() { + socket.removeEventListener('open', onOpen) + socket.removeEventListener('error', onError) + socket.close() + } + socket.addEventListener('open', onOpen) + socket.addEventListener('error', onError) + }) } if (await ping()) { diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index e54e061b52b352..c68ba6ce33cb13 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -133,7 +133,9 @@ export function createWebSocketServer( wss = new WebSocketServerRaw({ noServer: true }) hmrServerWsListener = (req, socket, head) => { if ( - req.headers['sec-websocket-protocol'] === HMR_HEADER && + [HMR_HEADER, 'vite-ping'].includes( + req.headers['sec-websocket-protocol']!, + ) && req.url === hmrBase ) { wss.handleUpgrade(req, socket as Socket, head, (ws) => { @@ -157,17 +159,46 @@ export function createWebSocketServer( }) res.end(body) }) as Parameters[1] + // vite dev server in middleware mode + // need to call ws listen manually if (httpsOptions) { wsHttpServer = createHttpsServer(httpsOptions, route) } else { wsHttpServer = createHttpServer(route) } - // vite dev server in middleware mode - // need to call ws listen manually - wss = new WebSocketServerRaw({ server: wsHttpServer }) + wss = new WebSocketServerRaw({ noServer: true }) + wsHttpServer.on('upgrade', (req, socket, head) => { + const protocol = req.headers['sec-websocket-protocol']! + if (protocol === 'vite-ping' && server && !server.listening) { + // reject connection to tell the vite/client that the server is not ready + // if the http server is not listening + // because the ws server listens before the http server listens + req.destroy() + return + } + wss.handleUpgrade(req, socket as Socket, head, (ws) => { + wss.emit('connection', ws, req) + }) + }) + wsHttpServer.on('error', (e: Error & { code: string }) => { + if (e.code === 'EADDRINUSE') { + config.logger.error( + colors.red(`WebSocket server error: Port is already in use`), + { error: e }, + ) + } else { + config.logger.error( + colors.red(`WebSocket server error:\n${e.stack || e.message}`), + { error: e }, + ) + } + }) } wss.on('connection', (socket) => { + if (socket.protocol === 'vite-ping') { + return + } socket.on('message', (raw) => { if (!customListeners.size) return let parsed: any diff --git a/playground/client-reload/__tests__/client-reload.spec.ts b/playground/client-reload/__tests__/client-reload.spec.ts new file mode 100644 index 00000000000000..0872a2e17d4294 --- /dev/null +++ b/playground/client-reload/__tests__/client-reload.spec.ts @@ -0,0 +1,75 @@ +import path from 'node:path' +import { type ServerOptions, type ViteDevServer, createServer } from 'vite' +import { afterEach, describe, expect, test } from 'vitest' +import { hmrPorts, isServe, page, ports } from '~utils' + +let server: ViteDevServer + +afterEach(async () => { + await server?.close() +}) + +async function testClientReload(serverOptions: ServerOptions) { + // start server + server = await createServer({ + root: path.resolve(import.meta.dirname, '..'), + logLevel: 'silent', + server: { + strictPort: true, + ...serverOptions, + }, + }) + + await server.listen() + const serverUrl = server.resolvedUrls.local[0] + + // open page and wait for connection + const connectedPromise = page.waitForEvent('console', { + predicate: (message) => message.text().includes('[vite] connected.'), + timeout: 5000, + }) + await page.goto(serverUrl) + await connectedPromise + + // input state + await page.locator('input').fill('hello') + + // restart and wait for reconnection after reload + const reConnectedPromise = page.waitForEvent('console', { + predicate: (message) => message.text().includes('[vite] connected.'), + timeout: 5000, + }) + await server.restart() + await reConnectedPromise + expect(await page.textContent('input')).toBe('') +} + +describe.runIf(isServe)('client-reload', () => { + test('default', async () => { + await testClientReload({ + port: ports['client-reload'], + }) + }) + + test('custom hmr port', async () => { + await testClientReload({ + port: ports['client-reload/hmr-port'], + hmr: { + port: hmrPorts['client-reload/hmr-port'], + }, + }) + }) + + test('custom hmr port and cross origin isolation', async () => { + await testClientReload({ + port: ports['client-reload/cross-origin'], + hmr: { + port: hmrPorts['client-reload/cross-origin'], + }, + headers: { + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin', + }, + }) + }) +}) diff --git a/playground/client-reload/__tests__/serve.ts b/playground/client-reload/__tests__/serve.ts new file mode 100644 index 00000000000000..1d33d8064a44b4 --- /dev/null +++ b/playground/client-reload/__tests__/serve.ts @@ -0,0 +1,6 @@ +// do nothing here since server is managed inside spec +export async function serve(): Promise<{ close(): Promise }> { + return { + close: () => Promise.resolve(), + } +} diff --git a/playground/client-reload/index.html b/playground/client-reload/index.html new file mode 100644 index 00000000000000..7e78f23e2d5f54 --- /dev/null +++ b/playground/client-reload/index.html @@ -0,0 +1,4 @@ + +

Test Client Reload

+ + diff --git a/playground/client-reload/package.json b/playground/client-reload/package.json new file mode 100644 index 00000000000000..a6fa570c64ffe7 --- /dev/null +++ b/playground/client-reload/package.json @@ -0,0 +1,12 @@ +{ + "name": "@vitejs/test-client-reload", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "debug": "node --inspect-brk ../../packages/vite/bin/vite", + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + } +} diff --git a/playground/client-reload/vite.config.ts b/playground/client-reload/vite.config.ts new file mode 100644 index 00000000000000..4c9c4be6ba0c82 --- /dev/null +++ b/playground/client-reload/vite.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + server: {}, +}) diff --git a/playground/js-sourcemap/test-ssr-dev.js b/playground/js-sourcemap/test-ssr-dev.js index c414f058517283..0e21a0ae80fe17 100644 --- a/playground/js-sourcemap/test-ssr-dev.js +++ b/playground/js-sourcemap/test-ssr-dev.js @@ -12,6 +12,7 @@ async function runTest() { server: { middlewareMode: true, hmr: false, + ws: false, }, define: { __testDefineObject: '{ "hello": "test" }', diff --git a/playground/ssr-html/test-network-imports.js b/playground/ssr-html/test-network-imports.js index 6e6a87d93d4219..d205acb12ee138 100644 --- a/playground/ssr-html/test-network-imports.js +++ b/playground/ssr-html/test-network-imports.js @@ -8,6 +8,7 @@ async function runTest(userRunner) { root: fileURLToPath(new URL('.', import.meta.url)), server: { middlewareMode: true, + ws: false, }, }) let mod diff --git a/playground/ssr-html/test-stacktrace-runtime.js b/playground/ssr-html/test-stacktrace-runtime.js index 0f4914dcbfe599..a53b7fbcdf8769 100644 --- a/playground/ssr-html/test-stacktrace-runtime.js +++ b/playground/ssr-html/test-stacktrace-runtime.js @@ -10,6 +10,7 @@ const server = await createServer({ root: fileURLToPath(new URL('.', import.meta.url)), server: { middlewareMode: true, + ws: false, }, }) diff --git a/playground/ssr-html/test-stacktrace.js b/playground/ssr-html/test-stacktrace.js index 327a8b4c423dae..85f48a82245ca7 100644 --- a/playground/ssr-html/test-stacktrace.js +++ b/playground/ssr-html/test-stacktrace.js @@ -29,6 +29,7 @@ const vite = await createServer({ logLevel: isTest ? 'error' : 'info', server: { middlewareMode: true, + ws: false, }, appType: 'custom', }) diff --git a/playground/test-utils.ts b/playground/test-utils.ts index 544bc31f8c0fa1..b0b43544c27f5b 100644 --- a/playground/test-utils.ts +++ b/playground/test-utils.ts @@ -47,6 +47,9 @@ export const ports = { 'css/dynamic-import': 5007, 'css/lightningcss-proxy': 5008, 'backend-integration': 5009, + 'client-reload': 5010, + 'client-reload/hmr-port': 5011, + 'client-reload/cross-origin': 5012, } export const hmrPorts = { 'optimize-missing-deps': 24680, @@ -58,6 +61,8 @@ export const hmrPorts = { 'css/lightningcss-proxy': 24686, json: 24687, 'ssr-conditions': 24688, + 'client-reload/hmr-port': 24689, + 'client-reload/cross-origin': 24690, } const hexToNameMap: Record = {} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0365cbc5686d6e..4321578f166af6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -530,6 +530,8 @@ importers: specifier: ^0.11.4 version: 0.11.4 + playground/client-reload: {} + playground/config/packages/entry: dependencies: '@vite/test-config-plugin-module-condition': From e51dc40b5907cf14d7aefaaf01fb8865a852ef15 Mon Sep 17 00:00:00 2001 From: Jonas Kuske Date: Fri, 25 Oct 2024 07:15:43 +0200 Subject: [PATCH 011/156] feat: read `sec-fetch-dest` header to detect JS in transform (#9981) Co-authored-by: bluwy --- packages/vite/src/node/server/middlewares/transform.ts | 1 + packages/vite/src/node/utils.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/vite/src/node/server/middlewares/transform.ts b/packages/vite/src/node/server/middlewares/transform.ts index d2cb48ef80acd0..402ef4f4cb632a 100644 --- a/packages/vite/src/node/server/middlewares/transform.ts +++ b/packages/vite/src/node/server/middlewares/transform.ts @@ -175,6 +175,7 @@ export function transformMiddleware( } if ( + req.headers['sec-fetch-dest'] === 'script' || isJSRequest(url) || isImportRequest(url) || isCSSRequest(url) || diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 62ec7da873efe5..fa50fc882e566e 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -271,6 +271,9 @@ export const isDataUrl = (url: string): boolean => dataUrlRE.test(url) export const virtualModuleRE = /^virtual-module:.*/ export const virtualModulePrefix = 'virtual-module:' +// NOTE: We should start relying on the "Sec-Fetch-Dest" header instead of this +// hardcoded list. We can eventually remove this function when the minimum version +// of browsers we support in dev all support this header. const knownJsSrcRE = /\.(?:[jt]sx?|m[jt]s|vue|marko|svelte|astro|imba|mdx)(?:$|\?)/ export const isJSRequest = (url: string): boolean => { From d4e0442f9d6adc70b72ea0713dc8abb4b1f75ae4 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 25 Oct 2024 16:49:27 +0900 Subject: [PATCH 012/156] feat(css)!: change default sass api to modern/modern-compiler (#17937) Co-authored-by: Bjorn Lu Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com> --- docs/config/shared-options.md | 9 ++++--- docs/guide/migration.md | 8 ++++++ packages/vite/src/node/plugins/css.ts | 25 +++++++------------ playground/backend-integration/vite.config.js | 7 ------ playground/css-sourcemap/vite.config.js | 3 --- playground/css/vite.config.js | 1 + .../multiple-entrypoints/vite.config.js | 7 ------ 7 files changed, 24 insertions(+), 36 deletions(-) diff --git a/docs/config/shared-options.md b/docs/config/shared-options.md index 0ea4aeeb63bcd9..34c07baff8e5d3 100644 --- a/docs/config/shared-options.md +++ b/docs/config/shared-options.md @@ -227,9 +227,12 @@ Note if an inline config is provided, Vite will not search for other PostCSS con Specify options to pass to CSS pre-processors. The file extensions are used as keys for the options. The supported options for each preprocessors can be found in their respective documentation: -- `sass`/`scss` - top level option `api: "legacy" | "modern" | "modern-compiler"` (default `"legacy"`) allows switching which sass API to use. For the best performance, it's recommended to use `api: "modern-compiler"` with `sass-embedded` package. [Options (legacy)](https://sass-lang.com/documentation/js-api/interfaces/LegacyStringOptions), [Options (modern)](https://sass-lang.com/documentation/js-api/interfaces/stringoptions/). -- `less` - [Options](https://lesscss.org/usage/#less-options). -- `styl`/`stylus` - Only [`define`](https://stylus-lang.com/docs/js.html#define-name-node) is supported, which can be passed as an object. +- `sass`/`scss`: + - Select the sass API to use with `api: "modern-compiler" | "modern" | "legacy"` (default `"modern-compiler"` if `sass-embedded` is installed, otherwise `"modern"`). For the best performance, it's recommended to use `api: "modern-compiler"` with the `sass-embedded` package. The `"legacy"` API is deprecated and will be removed in Vite 7. + - [Options (modern)](https://sass-lang.com/documentation/js-api/interfaces/stringoptions/) + - [Options (legacy)](https://sass-lang.com/documentation/js-api/interfaces/LegacyStringOptions). +- `less`: [Options](https://lesscss.org/usage/#less-options). +- `styl`/`stylus`: Only [`define`](https://stylus-lang.com/docs/js.html#define-name-node) is supported, which can be passed as an object. **Example:** diff --git a/docs/guide/migration.md b/docs/guide/migration.md index f4c11d580c9164..aab7b16c390d31 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -20,6 +20,14 @@ From Vite 6, even when `json.stringify: true` is set, `json.namedExports` is not Vite 6 also introduces a new default value for `json.stringify` which is `'auto'`, which will only stringify large JSON files. To disable this behavior, set `json.stringify: false`. +### Sass now uses modern API by default + +In Vite 5, the legacy API was used by default for Sass. Vite 5.4 added support for the modern API. + +From Vite 6, the modern API is used by default for Sass. If you wish to still use the legacy API, you can set [`css.preprocessorOptions.sass.api: 'legacy'` / `css.preprocessorOptions.scss.api: 'legacy'`](/config/shared-options#css-preprocessoroptions). But note that the legacy API support will be removed in Vite 7. + +To migrate to the modern API, see [the Sass documentation](https://sass-lang.com/documentation/breaking-changes/legacy-js-api/). + ## Advanced There are other breaking changes which only affect few users. diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 958c923e146831..1acab05082d114 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1979,8 +1979,8 @@ type PreprocessorAdditionalData = type SassPreprocessorOptions = { additionalData?: PreprocessorAdditionalData } & ( - | ({ api?: 'legacy' } & SassLegacyPreprocessBaseOptions) - | ({ api: 'modern' | 'modern-compiler' } & SassModernPreprocessBaseOptions) + | ({ api: 'legacy' } & SassLegacyPreprocessBaseOptions) + | ({ api?: 'modern' | 'modern-compiler' } & SassModernPreprocessBaseOptions) ) type LessPreprocessorOptions = { @@ -2469,9 +2469,9 @@ const scssProcessor = ( }, async process(environment, source, root, options, resolvers) { const sassPackage = loadSassPackage(root) - // TODO: change default in v6 - // options.api ?? sassPackage.name === "sass-embedded" ? "modern-compiler" : "modern"; - const api = options.api ?? 'legacy' + const api = + options.api ?? + (sassPackage.name === 'sass-embedded' ? 'modern-compiler' : 'modern') if (!workerMap.has(options.alias)) { workerMap.set( @@ -3001,18 +3001,11 @@ const createPreprocessorWorkerController = (maxWorkers: number | undefined) => { const sassProcess: StylePreprocessor['process'] = (environment, source, root, options, resolvers) => { - let opts: SassStylePreprocessorInternalOptions - if (options.api === 'modern' || options.api === 'modern-compiler') { - opts = { ...options, syntax: 'indented' as const } + const opts: SassStylePreprocessorInternalOptions = { ...options } + if (opts.api === 'legacy') { + opts.indentedSyntax = true } else { - const narrowedOptions = - options as SassStylePreprocessorInternalOptions & { - api?: 'legacy' - } - opts = { - ...narrowedOptions, - indentedSyntax: true, - } + opts.syntax = 'indented' } return scss.process(environment, source, root, opts, resolvers) } diff --git a/playground/backend-integration/vite.config.js b/playground/backend-integration/vite.config.js index 9824d40f9f3efb..9e0a914234b7bf 100644 --- a/playground/backend-integration/vite.config.js +++ b/playground/backend-integration/vite.config.js @@ -54,11 +54,4 @@ function BackendIntegrationExample() { export default defineConfig({ base: '/dev/', plugins: [BackendIntegrationExample()], - css: { - preprocessorOptions: { - scss: { - silenceDeprecations: ['legacy-js-api'], - }, - }, - }, }) diff --git a/playground/css-sourcemap/vite.config.js b/playground/css-sourcemap/vite.config.js index 4269c6735c86c8..e51cf320ad76e1 100644 --- a/playground/css-sourcemap/vite.config.js +++ b/playground/css-sourcemap/vite.config.js @@ -31,9 +31,6 @@ export default defineConfig({ } }, }, - sass: { - silenceDeprecations: ['legacy-js-api', 'import'], - }, }, }, build: { diff --git a/playground/css/vite.config.js b/playground/css/vite.config.js index 158a5130061b46..cbec6b9585b820 100644 --- a/playground/css/vite.config.js +++ b/playground/css/vite.config.js @@ -61,6 +61,7 @@ export default defineConfig({ }, preprocessorOptions: { scss: { + api: 'legacy', additionalData: `$injectedColor: orange;`, importer: [ function (url) { diff --git a/playground/multiple-entrypoints/vite.config.js b/playground/multiple-entrypoints/vite.config.js index c0d24577cca033..3202cebc0ce4aa 100644 --- a/playground/multiple-entrypoints/vite.config.js +++ b/playground/multiple-entrypoints/vite.config.js @@ -37,11 +37,4 @@ export default defineConfig({ }, }, }, - css: { - preprocessorOptions: { - scss: { - silenceDeprecations: ['legacy-js-api'], - }, - }, - }, }) From 5ead461b374d76ceb134063477eaf3f97fe3da97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 25 Oct 2024 18:54:18 +0900 Subject: [PATCH 013/156] fix: return the same instance of ModuleNode for the same EnvironmentModuleNode (#18455) --- .../vite/src/node/server/mixedModuleGraph.ts | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/server/mixedModuleGraph.ts b/packages/vite/src/node/server/mixedModuleGraph.ts index c133403cab67ae..d3dc951d87c804 100644 --- a/packages/vite/src/node/server/mixedModuleGraph.ts +++ b/packages/vite/src/node/server/mixedModuleGraph.ts @@ -220,6 +220,12 @@ export class ModuleGraph { fileToModulesMap: Map> + private moduleNodeCache = new DualWeakMap< + EnvironmentModuleNode, + EnvironmentModuleNode, + ModuleNode + >() + constructor(moduleGraphs: { client: () => EnvironmentModuleGraph ssr: () => EnvironmentModuleGraph @@ -452,8 +458,36 @@ export class ModuleGraph { clientModule?: EnvironmentModuleNode, ssrModule?: EnvironmentModuleNode, ): ModuleNode { - // ... - return new ModuleNode(this, clientModule, ssrModule) + const cached = this.moduleNodeCache.get(clientModule, ssrModule) + if (cached) { + return cached + } + + const moduleNode = new ModuleNode(this, clientModule, ssrModule) + this.moduleNodeCache.set(clientModule, ssrModule, moduleNode) + return moduleNode + } +} + +class DualWeakMap { + private map = new WeakMap>() + private undefinedKey = {} + + get(key1: K1 | undefined, key2: K2 | undefined): V | undefined { + const k1 = key1 ?? this.undefinedKey + const k2 = key2 ?? this.undefinedKey + return this.map.get(k1)?.get(k2) + } + + set(key1: K1 | undefined, key2: K2 | undefined, value: V): void { + const k1 = key1 ?? this.undefinedKey + const k2 = key2 ?? this.undefinedKey + if (!this.map.has(k1)) { + this.map.set(k1, new Map()) + } + + const m = this.map.get(k1)! + m.set(k2, value) } } From c4e7f34d2ecc7abc76836a7c5b46b4b616f60f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 25 Oct 2024 18:55:06 +0900 Subject: [PATCH 014/156] docs: migration guide for plugin-commonjs upgrade (#18456) --- docs/guide/migration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide/migration.md b/docs/guide/migration.md index aab7b16c390d31..05c5b5bcf4f72e 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -36,6 +36,8 @@ There are other breaking changes which only affect few users. - [`build.cssMinify`](/config/build-options#build-cssminify) is now enabled by default even for SSR builds. - [[#18209] refactor!: bump minimal terser version to 5.16.0](https://github.com/vitejs/vite/pull/18209) - Minimal supported terser version for [`build.minify: 'terser'`](/config/build-options#build-minify) was bumped to 5.16.0 from 5.4.0. +- [[#18231] chore(deps): update dependency @rollup/plugin-commonjs to v28](https://github.com/vitejs/vite/pull/18231) + - [`commonjsOptions.strictRequires`](https://github.com/rollup/plugins/blob/master/packages/commonjs/README.md#strictrequires) is now `true` by default (was `'auto'` before). - [[#18243] chore(deps)!: migrate `fast-glob` to `tinyglobby`](https://github.com/vitejs/vite/pull/18243) - Range braces (`{01..03}` ⇒ `['01', '02', '03']`) and incremental braces (`{2..8..2}` ⇒ `['2', '4', '6', '8']`) are no longer supported in globs. From c148676c90dc4823bc6bdeb8ba1e36386c5d9654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 25 Oct 2024 20:16:30 +0900 Subject: [PATCH 015/156] fix(manifest): non entry CSS chunk src was wrong (#18133) --- docs/guide/backend-integration.md | 8 ++++---- packages/vite/src/node/plugins/manifest.ts | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/guide/backend-integration.md b/docs/guide/backend-integration.md index 1bf6265f8267bd..5a6cb5ce73a88f 100644 --- a/docs/guide/backend-integration.md +++ b/docs/guide/backend-integration.md @@ -61,15 +61,15 @@ If you need a custom integration, you can follow the steps in this guide to conf ```json [.vite/manifest.json] { - "_shared-!~{003}~.js": { - "file": "assets/shared-ChJ_j-JJ.css", - "src": "_shared-!~{003}~.js" - }, "_shared-B7PI925R.js": { "file": "assets/shared-B7PI925R.js", "name": "shared", "css": ["assets/shared-ChJ_j-JJ.css"] }, + "_shared-ChJ_j-JJ.css": { + "file": "assets/shared-ChJ_j-JJ.css", + "src": "_shared-ChJ_j-JJ.css" + }, "baz.js": { "file": "assets/baz-B2H3sXNv.js", "name": "baz", diff --git a/packages/vite/src/node/plugins/manifest.ts b/packages/vite/src/node/plugins/manifest.ts index 69538f3704829c..c4c4499313e7b7 100644 --- a/packages/vite/src/node/plugins/manifest.ts +++ b/packages/vite/src/node/plugins/manifest.ts @@ -57,7 +57,10 @@ export function manifestPlugin(): Plugin { const buildOptions = this.environment.config.build function getChunkName(chunk: OutputChunk) { - return getChunkOriginalFileName(chunk, root, format) + return ( + getChunkOriginalFileName(chunk, root, format) ?? + `_` + path.basename(chunk.fileName) + ) } function getInternalImports(imports: string[]): string[] { @@ -150,7 +153,7 @@ export function manifestPlugin(): Plugin { const src = chunk.originalFileNames.length > 0 ? chunk.originalFileNames[0] - : chunk.names[0] + : '_' + path.basename(chunk.fileName) const isEntry = entryCssAssetFileNames.has(chunk.fileName) const asset = createAsset(chunk, src, isEntry) @@ -189,7 +192,7 @@ export function getChunkOriginalFileName( chunk: OutputChunk | RenderedChunk, root: string, format: InternalModuleFormat, -): string { +): string | undefined { if (chunk.facadeModuleId) { let name = normalizePath(path.relative(root, chunk.facadeModuleId)) if (format === 'system' && !chunk.name.includes('-legacy')) { @@ -198,7 +201,5 @@ export function getChunkOriginalFileName( name = name.slice(0, endPos) + `-legacy` + ext } return name.replace(/\0/g, '') - } else { - return `_` + path.basename(chunk.fileName) } } From c32837cf868f0fdb97a22a0be8c95c433f4069c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 25 Oct 2024 20:18:04 +0900 Subject: [PATCH 016/156] refactor(css): hide internal preprocessor types and expose types used for options (#18458) --- packages/vite/package.json | 1 + packages/vite/src/node/index.ts | 3 +++ packages/vite/src/node/plugins/css.ts | 8 ++++---- .../vite/types/{ => internal}/cssPreprocessorOptions.d.ts | 0 4 files changed, 8 insertions(+), 4 deletions(-) rename packages/vite/types/{ => internal}/cssPreprocessorOptions.d.ts (100%) diff --git a/packages/vite/package.json b/packages/vite/package.json index 0b4d6d8958ee38..6c6c31caceb9d5 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -40,6 +40,7 @@ "./types/*": { "types": "./types/*" }, + "./types/internal/*": null, "./package.json": "./package.json" }, "typesVersions": { diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 5aaff7552740ba..0e391a5d9a6f82 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -128,6 +128,9 @@ export type { CSSModulesOptions, PreprocessCSSResult, ResolvedCSSOptions, + SassPreprocessorOptions, + LessPreprocessorOptions, + StylusPreprocessorOptions, } from './plugins/css' export type { JsonOptions } from './plugins/json' export type { TransformOptions as EsbuildTransformOptions } from 'esbuild' diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 1acab05082d114..b02aa4c3a75143 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -32,7 +32,7 @@ import type { SassLegacyPreprocessBaseOptions, SassModernPreprocessBaseOptions, StylusPreprocessorBaseOptions, -} from 'types/cssPreprocessorOptions' +} from 'types/internal/cssPreprocessorOptions' import { getCodeWithSourcemap, injectSourcesContent } from '../server/sourcemap' import type { EnvironmentModuleNode } from '../server/moduleGraph' import { @@ -1976,18 +1976,18 @@ type PreprocessorAdditionalData = | PreprocessorAdditionalDataResult | Promise) -type SassPreprocessorOptions = { +export type SassPreprocessorOptions = { additionalData?: PreprocessorAdditionalData } & ( | ({ api: 'legacy' } & SassLegacyPreprocessBaseOptions) | ({ api?: 'modern' | 'modern-compiler' } & SassModernPreprocessBaseOptions) ) -type LessPreprocessorOptions = { +export type LessPreprocessorOptions = { additionalData?: PreprocessorAdditionalData } & LessPreprocessorBaseOptions -type StylusPreprocessorOptions = { +export type StylusPreprocessorOptions = { additionalData?: PreprocessorAdditionalData } & StylusPreprocessorBaseOptions diff --git a/packages/vite/types/cssPreprocessorOptions.d.ts b/packages/vite/types/internal/cssPreprocessorOptions.d.ts similarity index 100% rename from packages/vite/types/cssPreprocessorOptions.d.ts rename to packages/vite/types/internal/cssPreprocessorOptions.d.ts From e5ea63d2fc58eb8750729083497e6ef138b17b5c Mon Sep 17 00:00:00 2001 From: bluwy Date: Fri, 25 Oct 2024 21:11:56 +0800 Subject: [PATCH 017/156] release: plugin-legacy@5.4.3 --- packages/plugin-legacy/CHANGELOG.md | 16 ++++++++++++++++ packages/plugin-legacy/package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/plugin-legacy/CHANGELOG.md b/packages/plugin-legacy/CHANGELOG.md index ee0a4a7e8bdf62..51c322575dd105 100644 --- a/packages/plugin-legacy/CHANGELOG.md +++ b/packages/plugin-legacy/CHANGELOG.md @@ -1,3 +1,19 @@ +## 5.4.3 (2024-10-25) + +* chore: enable some eslint rules (#18084) ([e9a2746](https://github.com/vitejs/vite/commit/e9a2746ca77473b1814fd05db3d299c074135fe5)), closes [#18084](https://github.com/vitejs/vite/issues/18084) +* chore: remove stale TODOs (#17866) ([e012f29](https://github.com/vitejs/vite/commit/e012f296df583bd133d26399397bd4ae49de1497)), closes [#17866](https://github.com/vitejs/vite/issues/17866) +* chore: update license copyright (#18278) ([56eb869](https://github.com/vitejs/vite/commit/56eb869a67551a257d20cba00016ea59b1e1a2c4)), closes [#18278](https://github.com/vitejs/vite/issues/18278) +* chore(deps): update all non-major dependencies (#17945) ([cfb621e](https://github.com/vitejs/vite/commit/cfb621e7a5a3e24d710a9af156e6855e73caf891)), closes [#17945](https://github.com/vitejs/vite/issues/17945) +* chore(deps): update all non-major dependencies (#18050) ([7cac03f](https://github.com/vitejs/vite/commit/7cac03fa5197a72d2e2422bd0243a85a9a18abfc)), closes [#18050](https://github.com/vitejs/vite/issues/18050) +* chore(deps): update all non-major dependencies (#18404) ([802839d](https://github.com/vitejs/vite/commit/802839d48335a69eb15f71f2cd816d0b6e4d3556)), closes [#18404](https://github.com/vitejs/vite/issues/18404) +* fix(deps): update all non-major dependencies (#18170) ([c8aea5a](https://github.com/vitejs/vite/commit/c8aea5ae0af90dc6796ef3bdd612d1eb819f157b)), closes [#18170](https://github.com/vitejs/vite/issues/18170) +* fix(deps): update all non-major dependencies (#18292) ([5cac054](https://github.com/vitejs/vite/commit/5cac0544dca2764f0114aac38e9922a0c13d7ef4)), closes [#18292](https://github.com/vitejs/vite/issues/18292) +* fix(deps): update all non-major dependencies (#18345) ([5552583](https://github.com/vitejs/vite/commit/5552583a2272cd4208b30ad60e99d984e34645f0)), closes [#18345](https://github.com/vitejs/vite/issues/18345) +* fix(legacy): generate sourcemap for polyfill chunks (#18250) ([f311ff3](https://github.com/vitejs/vite/commit/f311ff3c2b19636457c3023095ef32ab9a96b84a)), closes [#18250](https://github.com/vitejs/vite/issues/18250) +* perf: use `crypto.hash` when available (#18317) ([2a14884](https://github.com/vitejs/vite/commit/2a148844cf2382a5377b75066351f00207843352)), closes [#18317](https://github.com/vitejs/vite/issues/18317) + + + ## 5.4.2 (2024-08-15) * chore: extend commit hash (#17709) ([4fc9b64](https://github.com/vitejs/vite/commit/4fc9b6424c27aca8004c368b69991a56264e4fdb)), closes [#17709](https://github.com/vitejs/vite/issues/17709) diff --git a/packages/plugin-legacy/package.json b/packages/plugin-legacy/package.json index 3b8cda837bc889..d8e886b7229e59 100644 --- a/packages/plugin-legacy/package.json +++ b/packages/plugin-legacy/package.json @@ -1,6 +1,6 @@ { "name": "@vitejs/plugin-legacy", - "version": "5.4.2", + "version": "5.4.3", "license": "MIT", "author": "Evan You", "files": [ From bd540d52eb609ca12dad8e2f3fe8011821bda878 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 25 Oct 2024 22:12:20 +0800 Subject: [PATCH 018/156] fix(plugins): noop if config hook returns same config reference (#18467) --- packages/vite/src/node/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 146f0110678f19..d6c40772bcec7a 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -1740,7 +1740,7 @@ async function runConfigHook( const handler = getHookHandler(hook) if (handler) { const res = await handler(conf, configEnv) - if (res) { + if (res && res !== conf) { conf = mergeConfig(conf, res) } } From 34fdb6bef558724330d2411b9666facef669b3a0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Sat, 26 Oct 2024 00:00:25 +0800 Subject: [PATCH 019/156] perf(css): skip style.css extraction if code-split css (#18470) --- packages/vite/src/node/plugins/css.ts | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index b02aa4c3a75143..c9824ee22ff3c5 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -867,8 +867,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { return } - function extractCss() { - let css = '' + // extract as single css bundle if no codesplit + if (!config.build.cssCodeSplit && !hasEmitted) { + let extractedCss = '' const collected = new Set() // will be populated in order they are used by entry points const dynamicImports = new Set() @@ -884,7 +885,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { dynamicImports.add(importName), ) // Then collect the styles of the current chunk (might overwrite some styles from previous imports) - css += chunkCSSMap.get(chunk.preliminaryFileName) ?? '' + extractedCss += chunkCSSMap.get(chunk.preliminaryFileName) ?? '' } // The bundle is guaranteed to be deterministic, if not then we have a bug in rollup. @@ -899,17 +900,16 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { collect(bundle[chunkName]) } - return css - } - let extractedCss = !hasEmitted && extractCss() - if (extractedCss) { - hasEmitted = true - extractedCss = await finalizeCss(extractedCss, true, config) - this.emitFile({ - name: cssBundleName, - type: 'asset', - source: extractedCss, - }) + // Finally, if there's any extracted CSS, we emit the asset + if (extractedCss) { + hasEmitted = true + extractedCss = await finalizeCss(extractedCss, true, config) + this.emitFile({ + name: cssBundleName, + type: 'asset', + source: extractedCss, + }) + } } // remove empty css chunks and their imports From 89f8303e727791aa7be6f35833a708b6a50e9120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sat, 26 Oct 2024 01:01:17 +0900 Subject: [PATCH 020/156] fix(css): make sass types work with sass-embedded (#18459) --- eslint.config.js | 9 ++++++- package.json | 12 +++++++++ .../internal/cssPreprocessorOptions.d.ts | 26 ++++++++++++++++--- pnpm-lock.yaml | 12 +++++++-- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 0c936aa7986ff1..d34a24adc7cdd3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -85,7 +85,14 @@ export default tseslint.config( 'n/no-extraneous-import': [ 'error', { - allowModules: ['vite', 'less', 'sass', 'vitest', 'unbuild'], + allowModules: [ + 'vite', + 'less', + 'sass', + 'sass-embedded', + 'vitest', + 'unbuild', + ], }, ], 'n/no-extraneous-require': [ diff --git a/package.json b/package.json index 921cf3d0aa0fc9..413f0e4ef51653 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,18 @@ "postcss", "search-insights" ] + }, + "packageExtensions": { + "sass-embedded": { + "peerDependencies": { + "source-map-js": "*" + }, + "peerDependenciesMeta": { + "source-map-js": { + "optional": true + } + } + } } }, "stackblitz": { diff --git a/packages/vite/types/internal/cssPreprocessorOptions.d.ts b/packages/vite/types/internal/cssPreprocessorOptions.d.ts index 5064ac18f02a63..de962a8851dea8 100644 --- a/packages/vite/types/internal/cssPreprocessorOptions.d.ts +++ b/packages/vite/types/internal/cssPreprocessorOptions.d.ts @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-ignore `sass` may not be installed -import type Sass from 'sass' +import type DartSass from 'sass' +// @ts-ignore `sass-embedded` may not be installed +import type SassEmbedded from 'sass-embedded' // @ts-ignore `less` may not be installed import type Less from 'less' // @ts-ignore `less` may not be installed @@ -9,8 +11,19 @@ import type Stylus from 'stylus' /* eslint-enable @typescript-eslint/ban-ts-comment */ +// https://github.com/type-challenges/type-challenges/issues/29285 +type IsAny = boolean extends (T extends never ? true : false) ? true : false + +type DartSassLegacyStringOptionsAsync = DartSass.LegacyStringOptions<'async'> +type SassEmbeddedLegacyStringOptionsAsync = + SassEmbedded.LegacyStringOptions<'async'> +type SassLegacyStringOptionsAsync = + IsAny extends false + ? DartSassLegacyStringOptionsAsync + : SassEmbeddedLegacyStringOptionsAsync + export type SassLegacyPreprocessBaseOptions = Omit< - Sass.LegacyStringOptions<'async'>, + SassLegacyStringOptionsAsync, | 'data' | 'file' | 'outFile' @@ -20,8 +33,15 @@ export type SassLegacyPreprocessBaseOptions = Omit< | 'sourceMapRoot' > +type DartSassStringOptionsAsync = DartSass.StringOptions<'async'> +type SassEmbeddedStringOptionsAsync = SassEmbedded.StringOptions<'async'> +type SassStringOptionsAsync = + IsAny extends false + ? DartSassStringOptionsAsync + : SassEmbeddedStringOptionsAsync + export type SassModernPreprocessBaseOptions = Omit< - Sass.StringOptions<'async'>, + SassStringOptionsAsync, 'url' | 'sourceMap' > diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4321578f166af6..9c685b48a591e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,8 @@ settings: overrides: vite: workspace:* +packageExtensionsChecksum: 632c5477927702fb9badd3e595784820 + patchedDependencies: acorn@8.13.0: hash: alg6ojsgkjjglvaj7cjzzaqhim @@ -390,7 +392,7 @@ importers: version: 1.80.3 sass-embedded: specifier: ^1.80.3 - version: 1.80.3 + version: 1.80.3(source-map-js@1.2.1) sirv: specifier: ^3.0.0 version: 3.0.0(patch_hash=plxlsciwiebyhal5sm4vtpekka) @@ -6416,6 +6418,11 @@ packages: resolution: {integrity: sha512-aTxTl4ToSAWg7ILFgAe+kMenj+zNlwHmHK/ZNPrOM8+HTef1Q6zuxolptYLijmHdZHKSMOkWYHgo5MMN6+GIyg==} engines: {node: '>=16.0.0'} hasBin: true + peerDependencies: + source-map-js: '*' + peerDependenciesMeta: + source-map-js: + optional: true sass@1.80.3: resolution: {integrity: sha512-ptDWyVmDMVielpz/oWy3YP3nfs7LpJTHIJZboMVs8GEC9eUmtZTZhMHlTW98wY4aEorDfjN38+Wr/XjskFWcfA==} @@ -12124,7 +12131,7 @@ snapshots: sass-embedded-win32-x64@1.80.3: optional: true - sass-embedded@1.80.3: + sass-embedded@1.80.3(source-map-js@1.2.1): dependencies: '@bufbuild/protobuf': 2.1.0 buffer-builder: 0.2.0 @@ -12154,6 +12161,7 @@ snapshots: sass-embedded-win32-arm64: 1.80.3 sass-embedded-win32-ia32: 1.80.3 sass-embedded-win32-x64: 1.80.3 + source-map-js: 1.2.1 sass@1.80.3: dependencies: From 993e71c4cb227bd8c347b918f52ccd83f85a645a Mon Sep 17 00:00:00 2001 From: Jonny Chen <31563977+Jonny-china@users.noreply.github.com> Date: Sat, 26 Oct 2024 16:50:50 +0800 Subject: [PATCH 021/156] fix(css): `cssCodeSplit` in `environments.xxx.build` is invalid (#18464) --- packages/vite/src/node/plugins/css.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index c9824ee22ff3c5..c371a867b88dd8 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -762,7 +762,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { pureCssChunks.add(chunk) } - if (config.build.cssCodeSplit) { + if (this.environment.config.build.cssCodeSplit) { if (opts.format === 'es' || opts.format === 'cjs') { const isEntry = chunk.isEntry && isPureCssChunk const cssFullAssetName = ensureFileExt(chunk.name, '.css') From 409fa5c9dee0e394bcdc3b111f5b2e4261131ca0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Sat, 26 Oct 2024 16:52:08 +0800 Subject: [PATCH 022/156] fix: handle warmup glob hang (#18462) --- packages/vite/package.json | 2 +- packages/vite/src/node/server/warmup.ts | 6 ++-- playground/backend-integration/package.json | 2 +- playground/css/package.json | 2 +- pnpm-lock.yaml | 34 ++++++++++++++------- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/vite/package.json b/packages/vite/package.json index 6c6c31caceb9d5..19cc3928519ab3 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -148,7 +148,7 @@ "source-map-support": "^0.5.21", "strip-ansi": "^7.1.0", "strip-literal": "^2.1.0", - "tinyglobby": "^0.2.9", + "tinyglobby": "^0.2.10", "tsconfck": "^3.1.4", "tslib": "^2.8.0", "types": "link:./types", diff --git a/packages/vite/src/node/server/warmup.ts b/packages/vite/src/node/server/warmup.ts index 418b17edafd6a3..341d360ece5ac2 100644 --- a/packages/vite/src/node/server/warmup.ts +++ b/packages/vite/src/node/server/warmup.ts @@ -70,10 +70,12 @@ function fileToUrl(file: string, root: string) { return '/' + normalizePath(url) } -function mapFiles(files: string[], root: string) { - return glob(files, { +async function mapFiles(files: string[], root: string) { + if (!files.length) return [] + return await glob(files, { absolute: true, cwd: root, expandDirectories: false, + ignore: ['**/.git/**', '**/node_modules/**'], }) } diff --git a/playground/backend-integration/package.json b/playground/backend-integration/package.json index 3de1b7be9367a6..a43c838d44e3cb 100644 --- a/playground/backend-integration/package.json +++ b/playground/backend-integration/package.json @@ -12,6 +12,6 @@ "devDependencies": { "sass": "^1.80.3", "tailwindcss": "^3.4.14", - "tinyglobby": "^0.2.9" + "tinyglobby": "^0.2.10" } } diff --git a/playground/css/package.json b/playground/css/package.json index 4523fa2b63b6c9..3593e5283e2246 100644 --- a/playground/css/package.json +++ b/playground/css/package.json @@ -26,7 +26,7 @@ "sass": "^1.80.3", "stylus": "^0.64.0", "sugarss": "^4.0.1", - "tinyglobby": "^0.2.9" + "tinyglobby": "^0.2.10" }, "imports": { "#imports": "./imports-field.css" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c685b48a591e5..c750eeffe50dd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -406,8 +406,8 @@ importers: specifier: ^2.1.0 version: 2.1.0 tinyglobby: - specifier: ^0.2.9 - version: 0.2.9 + specifier: ^0.2.10 + version: 0.2.10 tsconfck: specifier: ^3.1.4 version: 3.1.4(typescript@5.6.2) @@ -519,8 +519,8 @@ importers: specifier: ^3.4.14 version: 3.4.14(ts-node@10.9.2(@types/node@20.16.13)(typescript@5.6.2)) tinyglobby: - specifier: ^0.2.9 - version: 0.2.9 + specifier: ^0.2.10 + version: 0.2.10 playground/build-old: {} @@ -586,8 +586,8 @@ importers: specifier: ^4.0.1 version: 4.0.1(postcss@8.4.47) tinyglobby: - specifier: ^0.2.9 - version: 0.2.9 + specifier: ^0.2.10 + version: 0.2.10 playground/css-codesplit: {} @@ -4774,6 +4774,14 @@ packages: picomatch: optional: true + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + feed@4.2.2: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} @@ -6743,8 +6751,8 @@ packages: tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - tinyglobby@0.2.9: - resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} engines: {node: '>=12.0.0'} tinypool@1.0.0: @@ -8530,7 +8538,7 @@ snapshots: astring: 1.8.6 estree-walker: 2.0.2 magic-string: 0.30.12 - tinyglobby: 0.2.9 + tinyglobby: 0.2.10 optionalDependencies: rollup: 4.23.0 @@ -10400,6 +10408,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + feed@4.2.2: dependencies: xml-js: 1.6.11 @@ -12517,9 +12529,9 @@ snapshots: tinyexec@0.3.0: {} - tinyglobby@0.2.9: + tinyglobby@0.2.10: dependencies: - fdir: 6.4.0(picomatch@4.0.2) + fdir: 6.4.2(picomatch@4.0.2) picomatch: 4.0.2 tinypool@1.0.0: {} From 2ebe4b44430dd311028f72520ac977bb202ce50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sat, 26 Oct 2024 17:53:23 +0900 Subject: [PATCH 023/156] fix: set scripts imported by HTML moduleSideEffects=true (#18411) --- packages/vite/src/node/plugins/html.ts | 19 ++++++++++++++++++- packages/vite/src/node/plugins/resolve.ts | 9 +-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index f660ed1d00d9b3..cf6d3c3d96334e 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -108,7 +108,8 @@ export function htmlInlineProxyPlugin(config: ResolvedConfig): Plugin { const url = file.replace(normalizePath(config.root), '') const result = htmlProxyMap.get(config)!.get(url)?.[index] if (result) { - return result + // set moduleSideEffects to keep the module even if `treeshake.moduleSideEffects=false` is set + return { ...result, moduleSideEffects: true } } else { throw new Error(`No matching HTML proxy module found from ${id}`) } @@ -426,6 +427,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { return url } + const setModuleSideEffectPromises: Promise[] = [] await traverseHtml(html, id, (node) => { if (!nodeIsElement(node)) { return @@ -452,6 +454,19 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { if (isModule) { inlineModuleIndex++ if (url && !isExcludedUrl(url) && !isPublicFile) { + setModuleSideEffectPromises.push( + this.resolve(url, id) + .then((resolved) => { + if (!resolved) { + return Promise.reject() + } + return this.load(resolved) + }) + .then((mod) => { + // set this to keep the module even if `treeshake.moduleSideEffects=false` is set + mod.moduleSideEffects = true + }), + ) // + + +``` + +To opt-out of HTML processing on certain elements, you can add the `vite-ignore` attribute on the element, which can be useful when referencing external assets or CDN. + ## Vue Vite provides first-class Vue support: diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index cf6d3c3d96334e..cdf002b7a37d6e 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -211,11 +211,13 @@ export function getScriptInfo(node: DefaultTreeAdapterMap['element']): { sourceCodeLocation: Token.Location | undefined isModule: boolean isAsync: boolean + isIgnored: boolean } { let src: Token.Attribute | undefined let sourceCodeLocation: Token.Location | undefined let isModule = false let isAsync = false + let isIgnored = false for (const p of node.attrs) { if (p.prefix !== undefined) continue if (p.name === 'src') { @@ -227,9 +229,11 @@ export function getScriptInfo(node: DefaultTreeAdapterMap['element']): { isModule = true } else if (p.name === 'async') { isAsync = true + } else if (p.name === 'vite-ignore') { + isIgnored = true } } - return { src, sourceCodeLocation, isModule, isAsync } + return { src, sourceCodeLocation, isModule, isAsync, isIgnored } } const attrValueStartRE = /=\s*(.)/ @@ -260,6 +264,19 @@ export function overwriteAttrValue( return s } +export function removeViteIgnoreAttr( + s: MagicString, + sourceCodeLocation: Token.Location, +): MagicString { + const loc = (sourceCodeLocation as Token.LocationWithAttributes).attrs?.[ + 'vite-ignore' + ] + if (loc) { + s.remove(loc.startOffset, loc.endOffset) + } + return s +} + /** * Format parse5 @type {ParserError} to @type {RollupError} */ @@ -437,68 +454,72 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // script tags if (node.nodeName === 'script') { - const { src, sourceCodeLocation, isModule, isAsync } = + const { src, sourceCodeLocation, isModule, isAsync, isIgnored } = getScriptInfo(node) - const url = src && src.value - const isPublicFile = !!(url && checkPublicFile(url, config)) - if (isPublicFile) { - // referencing public dir url, prefix with base - overwriteAttrValue( - s, - sourceCodeLocation!, - partialEncodeURIPath(toOutputPublicFilePath(url)), - ) - } - - if (isModule) { - inlineModuleIndex++ - if (url && !isExcludedUrl(url) && !isPublicFile) { - setModuleSideEffectPromises.push( - this.resolve(url, id) - .then((resolved) => { - if (!resolved) { - return Promise.reject() - } - return this.load(resolved) - }) - .then((mod) => { - // set this to keep the module even if `treeshake.moduleSideEffects=false` is set - mod.moduleSideEffects = true - }), + 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, + sourceCodeLocation!, + partialEncodeURIPath(toOutputPublicFilePath(url)), ) - // + 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( + ` - 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( - ` - +

index.html (fallback)

+ +
External path:
+ + diff --git a/playground/html/vite.config.js b/playground/html/vite.config.js index cc3f6ba3a4ef34..d71da52096bb52 100644 --- a/playground/html/vite.config.js +++ b/playground/html/vite.config.js @@ -231,5 +231,30 @@ ${ }, }, }, + serveExternalPathPlugin(), ], }) + +/** @returns {import('vite').Plugin} */ +function serveExternalPathPlugin() { + const handler = (req, res, next) => { + if (req.url === '/external-path.js') { + res.setHeader('Content-Type', 'application/javascript') + res.end('document.querySelector(".external-path").textContent = "works"') + } else if (req.url === '/external-path.css') { + res.setHeader('Content-Type', 'text/css') + res.end('.external-path{color:red}') + } else { + next() + } + } + return { + name: 'serve-external-path', + configureServer(server) { + server.middlewares.use(handler) + }, + configurePreviewServer(server) { + server.middlewares.use(handler) + }, + } +} From 61cbf6f2cfcd5afc91fe0a0ad56abfc36a32f1ab Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 30 Oct 2024 16:13:01 +0800 Subject: [PATCH 048/156] feat(lib)!: use package name for css output file name (#18488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 / green --- docs/config/build-options.md | 22 ++++++++- docs/guide/build.md | 30 +++++++++++- docs/guide/migration.md | 20 ++++++++ .../src/node/__tests__/plugins/css.spec.ts | 49 +++++++++++++++++++ packages/vite/src/node/build.ts | 11 +++-- packages/vite/src/node/plugins/css.ts | 44 ++++++++++++++++- packages/vite/src/node/plugins/html.ts | 13 +++-- packages/vite/src/node/utils.ts | 4 ++ playground/lib/__tests__/lib.spec.ts | 38 ++++++++++++++ playground/lib/__tests__/serve.ts | 18 +++++++ playground/lib/src/css-entry-1.js | 3 ++ playground/lib/src/css-entry-2.js | 3 ++ playground/lib/src/entry-1.css | 3 ++ playground/lib/src/entry-2.css | 3 ++ playground/lib/vite.css-code-split.config.js | 16 ++++++ playground/lib/vite.css-multi-entry.config.js | 15 ++++++ .../lib/vite.css-single-entry.config.js | 12 +++++ 17 files changed, 290 insertions(+), 14 deletions(-) create mode 100644 playground/lib/src/css-entry-1.js create mode 100644 playground/lib/src/css-entry-2.js create mode 100644 playground/lib/src/entry-1.css create mode 100644 playground/lib/src/entry-2.css create mode 100644 playground/lib/vite.css-code-split.config.js create mode 100644 playground/lib/vite.css-multi-entry.config.js create mode 100644 playground/lib/vite.css-single-entry.config.js diff --git a/docs/config/build-options.md b/docs/config/build-options.md index 891b2e958a9db6..3419070bd474a6 100644 --- a/docs/config/build-options.md +++ b/docs/config/build-options.md @@ -162,10 +162,28 @@ Options to pass on to [@rollup/plugin-dynamic-import-vars](https://github.com/ro ## build.lib -- **Type:** `{ entry: string | string[] | { [entryAlias: string]: string }, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[], fileName?: string | ((format: ModuleFormat, entryName: string) => string) }` +- **Type:** `{ entry: string | string[] | { [entryAlias: string]: string }, name?: string, formats?: ('es' | 'cjs' | 'umd' | 'iife')[], fileName?: string | ((format: ModuleFormat, entryName: string) => string), cssFileName?: string }` - **Related:** [Library Mode](/guide/build#library-mode) -Build as a library. `entry` is required since the library cannot use HTML as entry. `name` is the exposed global variable and is required when `formats` includes `'umd'` or `'iife'`. Default `formats` are `['es', 'umd']`, or `['es', 'cjs']`, if multiple entries are used. `fileName` is the name of the package file output, default `fileName` is the name option of package.json, it can also be defined as function taking the `format` and `entryName` as arguments. +Build as a library. `entry` is required since the library cannot use HTML as entry. `name` is the exposed global variable and is required when `formats` includes `'umd'` or `'iife'`. Default `formats` are `['es', 'umd']`, or `['es', 'cjs']`, if multiple entries are used. + +`fileName` is the name of the package file output, which defaults to the `"name"` in `package.json`. It can also be defined as a function taking the `format` and `entryName` as arguments, and returning the file name. + +If your package imports CSS, `cssFileName` can be used to specify the name of the CSS file output. It defaults to the same value as `fileName` if it's set a string, otherwise it also falls back to the `"name"` in `package.json`. + +```js twoslash [vite.config.js] +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + lib: { + entry: ['src/main.js'], + fileName: (format, entryName) => `my-lib-${entryName}.${format}.js`, + cssFileName: 'my-lib-style', + }, + }, +}) +``` ## build.manifest diff --git a/docs/guide/build.md b/docs/guide/build.md index 6472e6a1f4f64d..62c595338896e8 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -200,7 +200,12 @@ import Bar from './Bar.vue' export { Foo, Bar } ``` -Running `vite build` with this config uses a Rollup preset that is oriented towards shipping libraries and produces two bundle formats: `es` and `umd` (configurable via `build.lib`): +Running `vite build` with this config uses a Rollup preset that is oriented towards shipping libraries and produces two bundle formats: + +- `es` and `umd` (for single entry) +- `es` and `cjs` (for multiple entries) + +The formats can be configured with the [`build.lib.formats`](/config/build-options.md#build-lib) option. ``` $ vite build @@ -251,6 +256,29 @@ Recommended `package.json` for your lib: ::: +### CSS support + +If your library imports any CSS, it will be bundled as a single CSS file besides the built JS files, e.g. `dist/my-lib.css`. The name defaults to `build.lib.fileName`, but can also be changed with [`build.lib.cssFileName`](/config/build-options.md#build-lib). + +You can export the CSS file in your `package.json` to be imported by users: + +```json {12} +{ + "name": "my-lib", + "type": "module", + "files": ["dist"], + "main": "./dist/my-lib.umd.cjs", + "module": "./dist/my-lib.js", + "exports": { + ".": { + "import": "./dist/my-lib.js", + "require": "./dist/my-lib.umd.cjs" + }, + "./style.css": "./dist/my-lib.css" + } +} +``` + ::: tip File Extensions If the `package.json` does not contain `"type": "module"`, Vite will generate different file extensions for Node.js compatibility. `.js` will become `.mjs` and `.cjs` will become `.js`. ::: diff --git a/docs/guide/migration.md b/docs/guide/migration.md index a48b9faccb8db4..5815e80505bd9a 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -32,6 +32,26 @@ From Vite 6, the modern API is used by default for Sass. If you wish to still us To migrate to the modern API, see [the Sass documentation](https://sass-lang.com/documentation/breaking-changes/legacy-js-api/). +### Customize CSS output file name in library mode + +In Vite 5, the CSS output file name in library mode was always `style.css` and cannot be easily changed through the Vite config. + +From Vite 6, the default file name now uses `"name"` in `package.json` similar to the JS output files. If [`build.lib.fileName`](/config/build-options.md#build-lib) is set with a string, the value will also be used for the CSS output file name. To explicitly set a different CSS file name, you can use the new [`build.lib.cssFileName`](/config/build-options.md#build-lib) to configure it. + +To migrate, if you had relied on the `style.css` file name, you should update references to it to the new name based on your package name. For example: + +```json [package.json] +{ + "name": "my-lib", + "exports": { + "./style.css": "./dist/style.css" // [!code --] + "./style.css": "./dist/my-lib.css" // [!code ++] + } +} +``` + +If you prefer to stick with `style.css` like in Vite 5, you can set `build.lib.cssFileName: 'style'` instead. + ## Advanced There are other breaking changes which only affect few users. diff --git a/packages/vite/src/node/__tests__/plugins/css.spec.ts b/packages/vite/src/node/__tests__/plugins/css.spec.ts index 5fdbbf45f05fa5..1d2428951e281f 100644 --- a/packages/vite/src/node/__tests__/plugins/css.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/css.spec.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import { fileURLToPath } from 'node:url' import { describe, expect, test } from 'vitest' import { resolveConfig } from '../../config' import type { InlineConfig } from '../../config' @@ -9,9 +10,12 @@ import { getEmptyChunkReplacer, hoistAtRules, preprocessCSS, + resolveLibCssFilename, } from '../../plugins/css' import { PartialEnvironment } from '../../baseEnvironment' +const __dirname = path.resolve(fileURLToPath(import.meta.url), '..') + describe('search css url function', () => { test('some spaces before it', () => { expect( @@ -369,3 +373,48 @@ describe('preprocessCSS', () => { `) }) }) + +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') + }) +}) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 51fcd22160c602..222d2feaef5307 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -44,6 +44,7 @@ import { copyDir, displayTime, emptyDir, + getPkgName, joinUrlSegments, normalizePath, partialEncodeURIPath, @@ -296,6 +297,12 @@ export interface LibraryOptions { * format as an argument. */ fileName?: string | ((format: ModuleFormat, entryName: string) => string) + /** + * The name of the CSS file output if the library imports CSS. Defaults to the + * same value as `build.lib.fileName` if it's set a string, otherwise it falls + * back to the name option of the project package.json. + */ + cssFileName?: string } export type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife' | 'system' @@ -879,10 +886,6 @@ function prepareOutDir( } } -function getPkgName(name: string) { - return name?.[0] === '@' ? name.split('/')[1] : name -} - type JsExt = 'js' | 'cjs' | 'mjs' function resolveOutputJsExtension( diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index ef59cf26a3f59c..9fde18e8bca0cb 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -41,6 +41,7 @@ import { toOutputFilePathInCss, toOutputFilePathInJS, } from '../build' +import type { LibraryOptions } from '../build' import { CLIENT_PUBLIC_PATH, CSS_LANGS_RE, @@ -60,6 +61,7 @@ import { generateCodeFrame, getHash, getPackageManagerCommand, + getPkgName, injectQuery, isDataUrl, isExternalUrl, @@ -81,6 +83,8 @@ import { PartialEnvironment } from '../baseEnvironment' import type { TransformPluginContext } from '../server/pluginContainer' import { searchForWorkspaceRoot } from '../server/searchRoot' import { type DevEnvironment } from '..' +import type { PackageCache } from '../packages' +import { findNearestPackageData } from '../packages' import { addToHTMLProxyTransformResult } from './html' import { assetUrlRE, @@ -213,7 +217,7 @@ const functionCallRE = /^[A-Z_][.\w-]*\(/i const transformOnlyRE = /[?&]transform-only\b/ const nonEscapedDoubleQuoteRe = /(? >() +// Used only if the config doesn't code-split CSS (builds a single CSS file) +export const cssBundleNameCache = new WeakMap() + const postcssConfigCache = new WeakMap< ResolvedConfig, PostCSSConfigResult | null | Promise @@ -428,6 +435,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // since output formats have no effect on the generated CSS. let hasEmitted = false let chunkCSSMap: Map + let cssBundleName: string const rollupOptionsOutput = config.build.rollupOptions.output const assetFileNames = ( @@ -464,6 +472,14 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { hasEmitted = false chunkCSSMap = new Map() codeSplitEmitQueue = createSerialPromiseQueue() + cssBundleName = config.build.lib + ? resolveLibCssFilename( + config.build.lib, + config.root, + config.packageCache, + ) + : defaultCssBundleName + cssBundleNameCache.set(config, cssBundleName) }, async transform(css, id) { @@ -1851,7 +1867,9 @@ async function minifyCSS( ...config.css?.lightningcss, targets: convertTargets(config.build.cssTarget), cssModules: undefined, - filename: cssBundleName, + // TODO: Pass actual filename here, which can also be passed to esbuild's + // `sourcefile` option below to improve error messages + filename: defaultCssBundleName, code: Buffer.from(css), minify: true, }) @@ -3255,3 +3273,25 @@ export const convertTargets = ( convertTargetsCache.set(esbuildTarget, targets) return targets } + +export function resolveLibCssFilename( + libOptions: LibraryOptions, + root: string, + packageCache?: PackageCache, +): string { + if (typeof libOptions.cssFileName === 'string') { + return `${libOptions.cssFileName}.css` + } else if (typeof libOptions.fileName === 'string') { + return `${libOptions.fileName}.css` + } + + const packageJson = findNearestPackageData(root, packageCache)?.data + const name = packageJson ? getPkgName(packageJson.name) : undefined + + if (!name) + throw new Error( + 'Name in package.json is required if option "build.lib.cssFileName" is not provided.', + ) + + return `${name}.css` +} diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index cdf002b7a37d6e..d8f7e3d2c52132 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -39,7 +39,7 @@ import { publicAssetUrlRE, urlToBuiltUrl, } from './asset' -import { isCSSRequest } from './css' +import { cssBundleNameCache, isCSSRequest } from './css' import { modulePreloadPolyfillId } from './modulePreloadPolyfill' interface ScriptAssetsUrl { @@ -909,10 +909,13 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // inject css link when cssCodeSplit is false if (!this.environment.config.build.cssCodeSplit) { - const cssChunk = Object.values(bundle).find( - (chunk) => - chunk.type === 'asset' && chunk.names.includes('style.css'), - ) as OutputAsset | undefined + const cssBundleName = cssBundleNameCache.get(config) + const cssChunk = + cssBundleName && + (Object.values(bundle).find( + (chunk) => + chunk.type === 'asset' && chunk.names.includes(cssBundleName), + ) as OutputAsset | undefined) if (cssChunk) { result = injectToHead(result, [ { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 8b234f20c8e316..a99d9e5403fd63 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -1314,6 +1314,10 @@ export function getNpmPackageName(importPath: string): string | null { } } +export function getPkgName(name: string): string | undefined { + return name?.[0] === '@' ? name.split('/')[1] : name +} + const escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g export function escapeRegex(str: string): string { return str.replace(escapeRegexRE, '\\$&') diff --git a/playground/lib/__tests__/lib.spec.ts b/playground/lib/__tests__/lib.spec.ts index af144de44e9c46..90fe833d9c30a1 100644 --- a/playground/lib/__tests__/lib.spec.ts +++ b/playground/lib/__tests__/lib.spec.ts @@ -90,6 +90,44 @@ describe.runIf(isBuild)('build', () => { expect(iife).toMatch('process.env.NODE_ENV') expect(umd).toMatch('process.env.NODE_ENV') }) + + test('single entry with css', () => { + const css = readFile('dist/css-single-entry/test-my-lib.css') + const js = readFile('dist/css-single-entry/test-my-lib.js') + const umd = readFile('dist/css-single-entry/test-my-lib.umd.cjs') + expect(css).toMatch('entry-1.css') + expect(js).toMatch('css-entry-1') + expect(umd).toContain('css-entry-1') + }) + + test('multi entry with css', () => { + const css = readFile('dist/css-multi-entry/test-my-lib.css') + const js1 = readFile('dist/css-multi-entry/css-entry-1.js') + const js2 = readFile('dist/css-multi-entry/css-entry-2.js') + const cjs1 = readFile('dist/css-multi-entry/css-entry-1.cjs') + const cjs2 = readFile('dist/css-multi-entry/css-entry-2.cjs') + expect(css).toMatch('entry-1.css') + expect(css).toMatch('entry-2.css') + expect(js1).toMatch('css-entry-1') + expect(js2).toMatch('css-entry-2') + expect(cjs1).toContain('css-entry-1') + expect(cjs2).toContain('css-entry-2') + }) + + test('multi entry with css and code split', () => { + const css1 = readFile('dist/css-code-split/css-entry-1.css') + const css2 = readFile('dist/css-code-split/css-entry-2.css') + const js1 = readFile('dist/css-code-split/css-entry-1.js') + const js2 = readFile('dist/css-code-split/css-entry-2.js') + const cjs1 = readFile('dist/css-code-split/css-entry-1.cjs') + const cjs2 = readFile('dist/css-code-split/css-entry-2.cjs') + expect(css1).toMatch('entry-1.css') + expect(css2).toMatch('entry-2.css') + expect(js1).toMatch('css-entry-1') + expect(js2).toMatch('css-entry-2') + expect(cjs1).toContain('css-entry-1') + expect(cjs2).toContain('css-entry-2') + }) }) test.runIf(isServe)('dev', async () => { diff --git a/playground/lib/__tests__/serve.ts b/playground/lib/__tests__/serve.ts index 009bc3711825a6..0401264ecb1ece 100644 --- a/playground/lib/__tests__/serve.ts +++ b/playground/lib/__tests__/serve.ts @@ -90,6 +90,24 @@ export async function serve(): Promise<{ close(): Promise }> { configFile: path.resolve(__dirname, '../vite.named-exports.config.js'), }) + await build({ + root: rootDir, + logLevel: 'warn', // output esbuild warns + configFile: path.resolve(__dirname, '../vite.css-single-entry.config.js'), + }) + + await build({ + root: rootDir, + logLevel: 'warn', // output esbuild warns + configFile: path.resolve(__dirname, '../vite.css-multi-entry.config.js'), + }) + + await build({ + root: rootDir, + logLevel: 'warn', // output esbuild warns + configFile: path.resolve(__dirname, '../vite.css-code-split.config.js'), + }) + // start static file server const serve = sirv(path.resolve(rootDir, 'dist')) const httpServer = http.createServer((req, res) => { diff --git a/playground/lib/src/css-entry-1.js b/playground/lib/src/css-entry-1.js new file mode 100644 index 00000000000000..b94766d8902a9e --- /dev/null +++ b/playground/lib/src/css-entry-1.js @@ -0,0 +1,3 @@ +import './entry-1.css' + +export default 'css-entry-1' diff --git a/playground/lib/src/css-entry-2.js b/playground/lib/src/css-entry-2.js new file mode 100644 index 00000000000000..fdb7d8ae56e453 --- /dev/null +++ b/playground/lib/src/css-entry-2.js @@ -0,0 +1,3 @@ +import './entry-2.css' + +export default 'css-entry-2' diff --git a/playground/lib/src/entry-1.css b/playground/lib/src/entry-1.css new file mode 100644 index 00000000000000..2587b2057f1013 --- /dev/null +++ b/playground/lib/src/entry-1.css @@ -0,0 +1,3 @@ +h1 { + content: 'entry-1.css'; +} diff --git a/playground/lib/src/entry-2.css b/playground/lib/src/entry-2.css new file mode 100644 index 00000000000000..e2c1e61158a22f --- /dev/null +++ b/playground/lib/src/entry-2.css @@ -0,0 +1,3 @@ +h2 { + content: 'entry-2.css'; +} diff --git a/playground/lib/vite.css-code-split.config.js b/playground/lib/vite.css-code-split.config.js new file mode 100644 index 00000000000000..140d6358abba47 --- /dev/null +++ b/playground/lib/vite.css-code-split.config.js @@ -0,0 +1,16 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + cssCodeSplit: true, + lib: { + entry: [ + fileURLToPath(new URL('src/css-entry-1.js', import.meta.url)), + fileURLToPath(new URL('src/css-entry-2.js', import.meta.url)), + ], + name: 'css-code-split', + }, + outDir: 'dist/css-code-split', + }, +}) diff --git a/playground/lib/vite.css-multi-entry.config.js b/playground/lib/vite.css-multi-entry.config.js new file mode 100644 index 00000000000000..9296799268ea60 --- /dev/null +++ b/playground/lib/vite.css-multi-entry.config.js @@ -0,0 +1,15 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + lib: { + entry: [ + fileURLToPath(new URL('src/css-entry-1.js', import.meta.url)), + fileURLToPath(new URL('src/css-entry-2.js', import.meta.url)), + ], + name: 'css-multi-entry', + }, + outDir: 'dist/css-multi-entry', + }, +}) diff --git a/playground/lib/vite.css-single-entry.config.js b/playground/lib/vite.css-single-entry.config.js new file mode 100644 index 00000000000000..7e16cfe42205a4 --- /dev/null +++ b/playground/lib/vite.css-single-entry.config.js @@ -0,0 +1,12 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + lib: { + entry: fileURLToPath(new URL('src/css-entry-1.js', import.meta.url)), + name: 'css-single-entry', + }, + outDir: 'dist/css-single-entry', + }, +}) From eccf663e35a17458425860895bb30b3b0613ea96 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 30 Oct 2024 16:14:44 +0800 Subject: [PATCH 049/156] fix(css)!: remove default import in ssr dev (#17922) Co-authored-by: patak <583075+patak-dev@users.noreply.github.com> --- packages/vite/src/node/plugins/css.ts | 7 +++---- .../src/node/ssr/runtime/__tests__/server-runtime.spec.ts | 7 +------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 9fde18e8bca0cb..2373ecf6ea50a3 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -539,13 +539,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { if (isDirectCSSRequest(id)) { return null } - // server only - if (this.environment.config.consumer !== 'client') { - return modulesCode || `export default ${JSON.stringify(css)}` - } if (inlined) { return `export default ${JSON.stringify(css)}` } + if (this.environment.config.consumer === 'server') { + return modulesCode || 'export {}' + } const cssContent = await getContentWithSourcemap(css) const code = [ 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 index 7c03610f73288e..4c47558c210517 100644 --- a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts +++ b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts @@ -32,12 +32,7 @@ describe('module runner initialization', async () => { it('css is loaded correctly', async ({ runner }) => { const css = await runner.import('/fixtures/test.css') - expect(css.default).toMatchInlineSnapshot(` - ".test { - color: red; - } - " - `) + expect(css.default).toBe(undefined) const module = await runner.import('/fixtures/test.module.css') expect(module).toMatchObject({ default: { From b9b01d57fdaf5d291c78a8156e17b534c8c51eb4 Mon Sep 17 00:00:00 2001 From: "Dominik G." Date: Wed, 30 Oct 2024 09:27:09 +0100 Subject: [PATCH 050/156] refactor: separate tsconfck caches per config in a weakmap (#17317) Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com> --- packages/vite/src/node/optimizer/index.ts | 10 +- packages/vite/src/node/optimizer/scan.ts | 4 +- packages/vite/src/node/plugins/esbuild.ts | 167 +++++++++++++--------- packages/vite/src/node/server/index.ts | 3 + 4 files changed, 109 insertions(+), 75 deletions(-) diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index f70a71f6ed8125..eb855065e449dd 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -1090,9 +1090,13 @@ export async function extractExportsData( debug?.( `Unable to parse: ${filePath}.\n Trying again with a ${loader} transform.`, ) - const transformed = await transformWithEsbuild(entryContent, filePath, { - loader, - }) + const transformed = await transformWithEsbuild( + entryContent, + filePath, + { loader }, + undefined, + environment.config, + ) parseResult = parse(transformed.code) usedJsxLoader = true } diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index 771222f8e5b9f7..36ddac29a30173 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -311,10 +311,10 @@ async function prepareEsbuildScanner( // Therefore, we use the closest tsconfig.json from the root to make it work in most cases. let tsconfigRaw = esbuildOptions.tsconfigRaw if (!tsconfigRaw && !esbuildOptions.tsconfig) { - const tsconfigResult = await loadTsconfigJsonForFile( + const { tsconfig } = await loadTsconfigJsonForFile( path.join(environment.config.root, '_dummy.js'), ) - if (tsconfigResult.compilerOptions?.experimentalDecorators) { + if (tsconfig.compilerOptions?.experimentalDecorators) { tsconfigRaw = { compilerOptions: { experimentalDecorators: true } } } } diff --git a/packages/vite/src/node/plugins/esbuild.ts b/packages/vite/src/node/plugins/esbuild.ts index c4d86432d5f320..6d3ffc86f33b06 100644 --- a/packages/vite/src/node/plugins/esbuild.ts +++ b/packages/vite/src/node/plugins/esbuild.ts @@ -11,6 +11,7 @@ import type { RawSourceMap } from '@ampproject/remapping' import type { InternalModuleFormat, SourceMap } from 'rollup' import type { TSConfckParseResult } from 'tsconfck' import { TSConfckCache, TSConfckParseError, parse } from 'tsconfck' +import type { FSWatcher } from 'dep-types/chokidar' import { combineSourcemaps, createDebugger, @@ -41,10 +42,6 @@ export const defaultEsbuildSupported = { 'import-meta': true, } -// TODO: rework to avoid caching the server for this module. -// If two servers are created in the same process, they will interfere with each other. -let server: ViteDevServer - export interface ESBuildOptions extends TransformOptions { include?: string | RegExp | string[] | RegExp[] exclude?: string | RegExp | string[] | RegExp[] @@ -83,6 +80,8 @@ export async function transformWithEsbuild( filename: string, options?: TransformOptions, inMap?: object, + config?: ResolvedConfig, + watcher?: FSWatcher, ): Promise { let loader = options?.loader @@ -123,14 +122,29 @@ export async function transformWithEsbuild( ] const compilerOptionsForFile: TSCompilerOptions = {} if (loader === 'ts' || loader === 'tsx') { - const loadedTsconfig = await loadTsconfigJsonForFile(filename) - const loadedCompilerOptions = loadedTsconfig.compilerOptions ?? {} + try { + const { tsconfig: loadedTsconfig, tsconfigFile } = + await loadTsconfigJsonForFile(filename, config) + // tsconfig could be out of root, make sure it is watched on dev + if (watcher && tsconfigFile && config) { + ensureWatchedFile(watcher, tsconfigFile, config.root) + } + const loadedCompilerOptions = loadedTsconfig.compilerOptions ?? {} - for (const field of meaningfulFields) { - if (field in loadedCompilerOptions) { - // @ts-expect-error TypeScript can't tell they are of the same type - compilerOptionsForFile[field] = loadedCompilerOptions[field] + for (const field of meaningfulFields) { + if (field in loadedCompilerOptions) { + // @ts-expect-error TypeScript can't tell they are of the same type + compilerOptionsForFile[field] = loadedCompilerOptions[field] + } } + } catch (e) { + if (e instanceof TSConfckParseError) { + // tsconfig could be out of root, make sure it is watched on dev + if (watcher && e.tsconfigFile && config) { + ensureWatchedFile(watcher, e.tsconfigFile, config.root) + } + } + throw e } } @@ -251,22 +265,23 @@ export function esbuildPlugin(config: ResolvedConfig): Plugin { }, } + let server: ViteDevServer + return { name: 'vite:esbuild', configureServer(_server) { server = _server - server.watcher - .on('add', reloadOnTsconfigChange) - .on('change', reloadOnTsconfigChange) - .on('unlink', reloadOnTsconfigChange) - }, - buildEnd() { - // recycle serve to avoid preventing Node self-exit (#6815) - server = null as any }, async transform(code, id) { if (filter(id) || filter(cleanUrl(id))) { - const result = await transformWithEsbuild(code, id, transformOptions) + const result = await transformWithEsbuild( + code, + id, + transformOptions, + undefined, + config, + server?.watcher, + ) if (result.warnings.length) { result.warnings.forEach((m) => { this.warn(prettifyMessage(m, code)) @@ -317,7 +332,13 @@ export const buildEsbuildPlugin = (config: ResolvedConfig): Plugin => { return null } - const res = await transformWithEsbuild(code, chunk.fileName, options) + const res = await transformWithEsbuild( + code, + chunk.fileName, + options, + undefined, + config, + ) if (config.build.lib) { // #7188, esbuild adds helpers out of the UMD and IIFE wrappers, and the @@ -448,63 +469,69 @@ function prettifyMessage(m: Message, code: string): string { return res + `\n` } -let tsconfckCache: TSConfckCache | undefined +let globalTSConfckCache: TSConfckCache | undefined +const tsconfckCacheMap = new WeakMap< + ResolvedConfig, + TSConfckCache +>() + +function getTSConfckCache(config?: ResolvedConfig) { + if (!config) { + return (globalTSConfckCache ??= new TSConfckCache()) + } + let cache = tsconfckCacheMap.get(config) + if (!cache) { + cache = new TSConfckCache() + tsconfckCacheMap.set(config, cache) + } + return cache +} export async function loadTsconfigJsonForFile( filename: string, -): Promise { - try { - if (!tsconfckCache) { - tsconfckCache = new TSConfckCache() - } - const result = await parse(filename, { - cache: tsconfckCache, - ignoreNodeModules: true, - }) - // tsconfig could be out of root, make sure it is watched on dev - if (server && result.tsconfigFile) { - ensureWatchedFile(server.watcher, result.tsconfigFile, server.config.root) - } - return result.tsconfig - } catch (e) { - if (e instanceof TSConfckParseError) { - // tsconfig could be out of root, make sure it is watched on dev - if (server && e.tsconfigFile) { - ensureWatchedFile(server.watcher, e.tsconfigFile, server.config.root) - } - } - throw e - } + config?: ResolvedConfig, +): Promise<{ tsconfigFile: string; tsconfig: TSConfigJSON }> { + const { tsconfig, tsconfigFile } = await parse(filename, { + cache: getTSConfckCache(config), + ignoreNodeModules: true, + }) + return { tsconfigFile, tsconfig } } -async function reloadOnTsconfigChange(changedFile: string) { - // server could be closed externally after a file change is detected - if (!server) return +export async function reloadOnTsconfigChange( + server: ViteDevServer, + changedFile: string, +): Promise { // any tsconfig.json that's added in the workspace could be closer to a code file than a previously cached one // any json file in the tsconfig cache could have been used to compile ts - if ( - path.basename(changedFile) === 'tsconfig.json' || - (changedFile.endsWith('.json') && - tsconfckCache?.hasParseResult(changedFile)) - ) { - server.config.logger.info( - `changed tsconfig file detected: ${changedFile} - Clearing cache and forcing full-reload to ensure TypeScript is compiled with updated config values.`, - { clear: server.config.clearScreen, timestamp: true }, - ) - - // clear module graph to remove code compiled with outdated config - server.moduleGraph.invalidateAll() - - // reset tsconfck so that recompile works with up2date configs - tsconfckCache?.clear() - - // server may not be available if vite config is updated at the same time - if (server) { - // force full reload - server.hot.send({ - type: 'full-reload', - path: '*', - }) + if (changedFile.endsWith('.json')) { + const cache = getTSConfckCache(server.config) + if ( + changedFile.endsWith('/tsconfig.json') || + cache.hasParseResult(changedFile) + ) { + server.config.logger.info( + `changed tsconfig file detected: ${changedFile} - Clearing cache and forcing full-reload to ensure TypeScript is compiled with updated config values.`, + { clear: server.config.clearScreen, timestamp: true }, + ) + + // TODO: more finegrained invalidation than the nuclear option below + + // clear module graph to remove code compiled with outdated config + for (const environment of Object.values(server.environments)) { + environment.moduleGraph.invalidateAll() + } + + // reset tsconfck cache so that recompile works with up2date configs + cache.clear() + + // reload environments + for (const environment of Object.values(server.environments)) { + environment.hot.send({ + type: 'full-reload', + path: '*', + }) + } } } } diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 25c43b47201cf1..7c463ba696f0c3 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -40,6 +40,7 @@ import { getFsUtils } from '../fsUtils' import { ssrLoadModule } from '../ssr/ssrModuleLoader' import { ssrFixStacktrace, ssrRewriteStacktrace } from '../ssr/ssrStacktrace' import { ssrTransform } from '../ssr/ssrTransform' +import { reloadOnTsconfigChange } from '../plugins/esbuild' import { bindCLIShortcuts } from '../shortcuts' import type { BindCLIShortcutsOptions } from '../shortcuts' import { @@ -761,6 +762,7 @@ export async function _createServer( const onFileAddUnlink = async (file: string, isUnlink: boolean) => { file = normalizePath(file) + reloadOnTsconfigChange(server, file) await pluginContainer.watchChange(file, { event: isUnlink ? 'delete' : 'create', @@ -794,6 +796,7 @@ export async function _createServer( watcher.on('change', async (file) => { file = normalizePath(file) + reloadOnTsconfigChange(server, file) await pluginContainer.watchChange(file, { event: 'update' }) // invalidate module graph cache on file change From 04f6736fd7ac3da22141929c01a151f5a6fe4e45 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Wed, 30 Oct 2024 16:55:58 +0800 Subject: [PATCH 051/156] feat: log complete config in debug mode (#18289) --- packages/vite/src/node/config.ts | 2 +- packages/vite/src/node/utils.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 57970a7a66d991..85d1f16c536ac6 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -89,7 +89,7 @@ import { resolveSSROptions } from './ssr' import { PartialEnvironment } from './baseEnvironment' import { createIdResolver } from './idResolver' -const debug = createDebugger('vite:config') +const debug = createDebugger('vite:config', { depth: 10 }) const promisifiedRealpath = promisify(fs.realpath) export interface ConfigEnv { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index a99d9e5403fd63..2a5faa6cc8079d 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -156,6 +156,7 @@ const DEBUG = process.env.DEBUG interface DebuggerOptions { onlyWhenFocused?: boolean | string + depth?: number } export type ViteDebugScope = `vite:${string}` @@ -165,7 +166,13 @@ export function createDebugger( options: DebuggerOptions = {}, ): debug.Debugger['log'] | undefined { const log = debug(namespace) - const { onlyWhenFocused } = options + const { onlyWhenFocused, depth } = options + + // @ts-expect-error - The log function is bound to inspectOpts, but the type is not reflected + if (depth && log.inspectOpts.depth == null) { + // @ts-expect-error - The log function is bound to inspectOpts, but the type is not reflected + log.inspectOpts.depth = options.depth + } let enabled = log.enabled if (enabled && onlyWhenFocused) { From 437795db8307ce4491d066bcaaa5bd9432193773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 30 Oct 2024 18:07:03 +0900 Subject: [PATCH 052/156] fix: use picomatch to align with tinyglobby (#18503) --- package.json | 1 - packages/vite/LICENSE.md | 70 ------------------- packages/vite/package.json | 1 - packages/vite/src/node/optimizer/resolve.ts | 10 +-- .../vite/src/node/plugins/importMetaGlob.ts | 51 +++++++------- pnpm-lock.yaml | 18 ----- 6 files changed, 29 insertions(+), 122 deletions(-) diff --git a/package.json b/package.json index 2fbf2181f843e5..28531ba3c77ae7 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "@types/estree": "^1.0.6", "@types/etag": "^1.8.3", "@types/less": "^3.0.6", - "@types/micromatch": "^4.0.9", "@types/node": "^20.17.1", "@types/picomatch": "^3.0.1", "@types/stylus": "^0.48.43", diff --git a/packages/vite/LICENSE.md b/packages/vite/LICENSE.md index 03c517032a947d..e49a891c494365 100644 --- a/packages/vite/LICENSE.md +++ b/packages/vite/LICENSE.md @@ -448,47 +448,6 @@ Repository: git+https://github.com/sapphi-red/artichokie.git --------------------------------------- -## braces, fill-range, is-number, micromatch -License: MIT -By: Jon Schlinkert, Brian Woodward, Elan Shanker, Eugene Sharygin, hemanth.hm -Repository: micromatch/braces - -License: MIT -By: Jon Schlinkert, Edo Rivai, Paul Miller, Rouven Weßling -Repository: jonschlinkert/fill-range - -License: MIT -By: Jon Schlinkert, Olsten Larck, Rouven Weßling -Repository: jonschlinkert/is-number - -License: MIT -By: Jon Schlinkert, Amila Welihinda, Bogdan Chadkin, Brian Woodward, Devon Govett, Elan Shanker, Fabrício Matté, Martin Kolárik, Olsten Larck, Paul Miller, Tom Byrer, Tyler Akins, Peter Bright, Kuba Juszczyk -Repository: micromatch/micromatch - -> The MIT License (MIT) -> -> Copyright (c) 2014-present, Jon Schlinkert. -> -> 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. - ---------------------------------------- - ## cac License: MIT By: egoist @@ -2085,35 +2044,6 @@ Repository: git+https://github.com/SuperchupuDev/tinyglobby.git --------------------------------------- -## to-regex-range -License: MIT -By: Jon Schlinkert, Rouven Weßling -Repository: micromatch/to-regex-range - -> The MIT License (MIT) -> -> Copyright (c) 2015-present, Jon Schlinkert. -> -> 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. - ---------------------------------------- - ## tsconfck License: MIT By: dominikg diff --git a/packages/vite/package.json b/packages/vite/package.json index ae9c46f8a8f2e6..fcb890ca4954e6 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -122,7 +122,6 @@ "launch-editor-middleware": "^2.9.1", "lightningcss": "^1.27.0", "magic-string": "^0.30.12", - "micromatch": "^4.0.8", "mlly": "^1.7.2", "mrmime": "^2.0.0", "nanoid": "^5.0.7", diff --git a/packages/vite/src/node/optimizer/resolve.ts b/packages/vite/src/node/optimizer/resolve.ts index af0ead43663301..f1dbc0d199e60d 100644 --- a/packages/vite/src/node/optimizer/resolve.ts +++ b/packages/vite/src/node/optimizer/resolve.ts @@ -1,5 +1,5 @@ import path from 'node:path' -import micromatch from 'micromatch' +import picomatch from 'picomatch' import { globSync } from 'tinyglobby' import type { ResolvedConfig } from '../config' import { escapeRegex, getNpmPackageName } from '../utils' @@ -59,7 +59,7 @@ export function expandGlobIds(id: string, config: ResolvedConfig): string[] { const exports = pkgData.data.exports // if package has exports field, get all possible export paths and apply - // glob on them with micromatch + // glob on them with picomatch if (exports) { if (typeof exports === 'string' || Array.isArray(exports)) { return [pkgName] @@ -136,9 +136,9 @@ export function expandGlobIds(id: string, config: ResolvedConfig): string[] { } } - const matched = micromatch(possibleExportPaths, pattern).map((match) => - path.posix.join(pkgName, match), - ) + const matched = possibleExportPaths + .filter(picomatch(pattern)) + .map((match) => path.posix.join(pkgName, match)) matched.unshift(pkgName) return matched } else { diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index 13d2afbe071628..b167bd16ef57d2 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -1,5 +1,5 @@ import { isAbsolute, posix } from 'node:path' -import micromatch from 'micromatch' +import picomatch from 'picomatch' import { stripLiteral } from 'strip-literal' import colors from 'picocolors' import type { @@ -24,8 +24,6 @@ import type { Logger } from '../logger' import { slash } from '../../shared/utils' import type { Environment } from '../environment' -const { isMatch, scan } = micromatch - export interface ParsedImportGlob { index: number globs: string[] @@ -43,7 +41,7 @@ interface ParsedGeneralImportGlobOptions extends GeneralImportGlobOptions { export function importGlobPlugin(config: ResolvedConfig): Plugin { const importGlobMaps = new Map< Environment, - Map + Map boolean>> >() return { @@ -67,18 +65,25 @@ export function importGlobPlugin(config: ResolvedConfig): Plugin { if (!importGlobMaps.has(this.environment)) { importGlobMaps.set(this.environment, new Map()) } - importGlobMaps.get(this.environment)!.set( - id, - allGlobs.map((globs) => { - const affirmed: string[] = [] - const negated: string[] = [] - - for (const glob of globs) { - ;(glob[0] === '!' ? negated : affirmed).push(glob) - } - return { affirmed, negated } - }), - ) + + const globMatchers = allGlobs.map((globs) => { + const affirmed: string[] = [] + const negated: string[] = [] + for (const glob of globs) { + ;(glob[0] === '!' ? negated : affirmed).push(glob) + } + const affirmedMatcher = picomatch(affirmed) + const negatedMatcher = picomatch(negated) + + return (file: string) => { + // (glob1 || glob2) && !(glob3 || glob4)... + return ( + (affirmed.length === 0 || affirmedMatcher(file)) && + !(negated.length > 0 && negatedMatcher(file)) + ) + } + }) + importGlobMaps.get(this.environment)!.set(id, globMatchers) return transformStableResult(result.s, id, config) } @@ -90,16 +95,8 @@ export function importGlobPlugin(config: ResolvedConfig): Plugin { if (!importGlobMap) return const modules: EnvironmentModuleNode[] = [] - for (const [id, allGlobs] of importGlobMap) { - // (glob1 || glob2) && !glob3 && !glob4... - if ( - allGlobs.some( - ({ affirmed, negated }) => - (!affirmed.length || - affirmed.some((glob) => isMatch(file, glob))) && - (!negated.length || negated.every((glob) => isMatch(file, glob))), - ) - ) { + for (const [id, globMatchers] of importGlobMap) { + if (globMatchers.some((matcher) => matcher(file))) { const mod = this.environment.moduleGraph.getModuleById(id) if (mod) modules.push(mod) } @@ -577,7 +574,7 @@ export function getCommonBase(globsResolved: string[]): null | string { const bases = globsResolved .filter((g) => g[0] !== '!') .map((glob) => { - let { base } = scan(glob) + let { base } = picomatch.scan(glob) // `scan('a/foo.js')` returns `base: 'a/foo.js'` if (posix.basename(base).includes('.')) base = posix.dirname(base) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0f94d00f04af9..1c1a5912f33697 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,9 +51,6 @@ importers: '@types/less': specifier: ^3.0.6 version: 3.0.6 - '@types/micromatch': - specifier: ^4.0.9 - version: 4.0.9 '@types/node': specifier: ^20.17.1 version: 20.17.1 @@ -326,9 +323,6 @@ importers: magic-string: specifier: ^0.30.12 version: 0.30.12 - micromatch: - specifier: ^4.0.8 - version: 4.0.8 mlly: specifier: ^1.7.2 version: 1.7.2 @@ -2998,9 +2992,6 @@ packages: '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/braces@3.0.4': - resolution: {integrity: sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -3055,9 +3046,6 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/micromatch@4.0.9': - resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==} - '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -8491,8 +8479,6 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 20.17.1 - '@types/braces@3.0.4': {} - '@types/connect@3.4.38': dependencies: '@types/node': 20.17.1 @@ -8554,10 +8540,6 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/micromatch@4.0.9': - dependencies: - '@types/braces': 3.0.4 - '@types/mime@1.3.5': {} '@types/mime@3.0.4': {} From 3c9836d96f118ff5748916241bc3871a54247ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 30 Oct 2024 18:08:19 +0900 Subject: [PATCH 053/156] feat!: proxy bypass with WebSocket (#18070) --- .../vite/src/node/server/middlewares/proxy.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/server/middlewares/proxy.ts b/packages/vite/src/node/server/middlewares/proxy.ts index 810f435cfa84a1..5f7c5b04c975fa 100644 --- a/packages/vite/src/node/server/middlewares/proxy.ts +++ b/packages/vite/src/node/server/middlewares/proxy.ts @@ -24,7 +24,8 @@ export interface ProxyOptions extends HttpProxy.ServerOptions { */ bypass?: ( req: http.IncomingMessage, - res: http.ServerResponse, + /** undefined for WebSocket upgrade requests */ + res: http.ServerResponse | undefined, options: ProxyOptions, ) => void | null | undefined | false | string /** @@ -167,6 +168,19 @@ export function proxyMiddleware( opts.target?.toString().startsWith('ws:') || opts.target?.toString().startsWith('wss:') ) { + if (opts.bypass) { + const bypassResult = opts.bypass(req, undefined, opts) + if (typeof bypassResult === 'string') { + req.url = bypassResult + debug?.(`bypass: ${req.url} -> ${bypassResult}`) + return + } else if (bypassResult === false) { + debug?.(`bypass: ${req.url} -> 404`) + socket.end('HTTP/1.1 404 Not Found\r\n\r\n', '') + return + } + } + if (opts.rewrite) { req.url = opts.rewrite(url) } From 25fe9e3b48e29d49e90d6aed5ec3825dceafec18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 30 Oct 2024 18:53:33 +0900 Subject: [PATCH 054/156] chore: fix moduleSideEffects in build script on Windows (#18518) --- packages/vite/rollup.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index 6cb63cc694e629..c8137469a7e4ab 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -44,6 +44,7 @@ const clientConfig = defineConfig({ const sharedNodeOptions = defineConfig({ treeshake: { moduleSideEffects(id, external) { + id = id.replaceAll('\\', '/') // These nested dependencies should be considered side-effect free // as it's not set within their package.json if ( From ad9d731526cd973a172ee361145f7cd1f1c7d95c Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:54:57 +0900 Subject: [PATCH 055/156] release: create-vite@5.5.5 --- packages/create-vite/CHANGELOG.md | 8 ++++++++ packages/create-vite/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/create-vite/CHANGELOG.md b/packages/create-vite/CHANGELOG.md index 64523c97fade05..9c65036a997e03 100644 --- a/packages/create-vite/CHANGELOG.md +++ b/packages/create-vite/CHANGELOG.md @@ -1,3 +1,11 @@ +## 5.5.5 (2024-10-30) + +* chore: upgrade to unbuild v3 rc (#18502) ([ddd5c5d](https://github.com/vitejs/vite/commit/ddd5c5d00ff7894462a608841560883f9c771f22)), closes [#18502](https://github.com/vitejs/vite/issues/18502) +* fix(create-vite): add tsBuildInfoFile option (#18435) ([0a4427f](https://github.com/vitejs/vite/commit/0a4427fc44b9b2075225bf8a9f1d88a8a428a217)), closes [#18435](https://github.com/vitejs/vite/issues/18435) +* fix(deps): update all non-major dependencies (#18484) ([2ec12df](https://github.com/vitejs/vite/commit/2ec12df98d07eb4c986737e86a4a9f8066724658)), closes [#18484](https://github.com/vitejs/vite/issues/18484) + + + ## 5.5.4 (2024-10-23) * chore: change Angular customCommand (#18425) ([b53db53](https://github.com/vitejs/vite/commit/b53db53df17c43602d61a24e9bf579267ee8eb6b)), closes [#18425](https://github.com/vitejs/vite/issues/18425) diff --git a/packages/create-vite/package.json b/packages/create-vite/package.json index 1d00d229c31f7b..e222a87a421ddb 100644 --- a/packages/create-vite/package.json +++ b/packages/create-vite/package.json @@ -1,6 +1,6 @@ { "name": "create-vite", - "version": "5.5.4", + "version": "5.5.5", "type": "module", "license": "MIT", "author": "Evan You", From 67e3e9542a35c9ad949651c3faa2f22d2ae50b0c Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:59:01 +0900 Subject: [PATCH 056/156] release: v6.0.0-beta.7 --- packages/vite/CHANGELOG.md | 24 ++++++++++++++++++++++++ packages/vite/package.json | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/vite/CHANGELOG.md b/packages/vite/CHANGELOG.md index ffb4a7a6592938..7109d120c3dc13 100644 --- a/packages/vite/CHANGELOG.md +++ b/packages/vite/CHANGELOG.md @@ -1,3 +1,27 @@ +## 6.0.0-beta.7 (2024-10-30) + +* chore: fix moduleSideEffects in build script on Windows (#18518) ([25fe9e3](https://github.com/vitejs/vite/commit/25fe9e3b48e29d49e90d6aed5ec3825dceafec18)), closes [#18518](https://github.com/vitejs/vite/issues/18518) +* chore: use premove instead of rimraf (#18499) ([f97a578](https://github.com/vitejs/vite/commit/f97a57893b3a7ddf11ca4c126b6be33cd2d9283b)), closes [#18499](https://github.com/vitejs/vite/issues/18499) +* feat!: proxy bypass with WebSocket (#18070) ([3c9836d](https://github.com/vitejs/vite/commit/3c9836d96f118ff5748916241bc3871a54247ad1)), closes [#18070](https://github.com/vitejs/vite/issues/18070) +* feat!: support `file://` resolution (#18422) ([6a7e313](https://github.com/vitejs/vite/commit/6a7e313754dce5faa5cd7c1e2343448cd7f3a2a2)), closes [#18422](https://github.com/vitejs/vite/issues/18422) +* feat!: update to chokidar v4 (#18453) ([192d555](https://github.com/vitejs/vite/commit/192d555f88bba7576e8a40cc027e8a11e006079c)), closes [#18453](https://github.com/vitejs/vite/issues/18453) +* feat(lib)!: use package name for css output file name (#18488) ([61cbf6f](https://github.com/vitejs/vite/commit/61cbf6f2cfcd5afc91fe0a0ad56abfc36a32f1ab)), closes [#18488](https://github.com/vitejs/vite/issues/18488) +* fix(css)!: remove default import in ssr dev (#17922) ([eccf663](https://github.com/vitejs/vite/commit/eccf663e35a17458425860895bb30b3b0613ea96)), closes [#17922](https://github.com/vitejs/vite/issues/17922) +* fix: `define` in environment config was not working (#18515) ([052799e](https://github.com/vitejs/vite/commit/052799e8939cfcdd7a7ff48daf45a766bf6cc546)), closes [#18515](https://github.com/vitejs/vite/issues/18515) +* fix: consider URLs with any protocol to be external (#17369) ([a0336bd](https://github.com/vitejs/vite/commit/a0336bd5197bb4427251be4c975e30fb596c658f)), closes [#17369](https://github.com/vitejs/vite/issues/17369) +* fix: use picomatch to align with tinyglobby (#18503) ([437795d](https://github.com/vitejs/vite/commit/437795db8307ce4491d066bcaaa5bd9432193773)), closes [#18503](https://github.com/vitejs/vite/issues/18503) +* fix(build): apply resolve.external/noExternal to server environments (#18495) ([5a967cb](https://github.com/vitejs/vite/commit/5a967cb596c7c4b0548be1d9025bc1e34b36169a)), closes [#18495](https://github.com/vitejs/vite/issues/18495) +* fix(config): remove error if require resolve to esm (#18437) ([f886f75](https://github.com/vitejs/vite/commit/f886f75396cdb5a43ec5377bbbaaffc0e8ae03e9)), closes [#18437](https://github.com/vitejs/vite/issues/18437) +* feat: log complete config in debug mode (#18289) ([04f6736](https://github.com/vitejs/vite/commit/04f6736fd7ac3da22141929c01a151f5a6fe4e45)), closes [#18289](https://github.com/vitejs/vite/issues/18289) +* feat(html): support `vite-ignore` attribute to opt-out of processing (#18494) ([d951310](https://github.com/vitejs/vite/commit/d9513104e21175e1d23e0f614df55cd53291ab4e)), closes [#18494](https://github.com/vitejs/vite/issues/18494) +* refactor: separate tsconfck caches per config in a weakmap (#17317) ([b9b01d5](https://github.com/vitejs/vite/commit/b9b01d57fdaf5d291c78a8156e17b534c8c51eb4)), closes [#17317](https://github.com/vitejs/vite/issues/17317) +* docs: add jsdocs to flags in BuilderOptions (#18516) ([1507068](https://github.com/vitejs/vite/commit/1507068b6d460cf54336fe7e8d3539fdb4564bfb)), closes [#18516](https://github.com/vitejs/vite/issues/18516) +* docs: missing changes guides (#18491) ([5da78a6](https://github.com/vitejs/vite/commit/5da78a6859f3b5c677d896144b915381e4497432)), closes [#18491](https://github.com/vitejs/vite/issues/18491) +* docs: update fs.deny default in JSDoc (#18514) ([1fcc83d](https://github.com/vitejs/vite/commit/1fcc83dd7ade429f889e4ce19d5c67b3e5b46419)), closes [#18514](https://github.com/vitejs/vite/issues/18514) +* build: reduce package size (#18517) ([b83f60b](https://github.com/vitejs/vite/commit/b83f60b159f3b6f4a61db180fa03cc5b20bd110f)), closes [#18517](https://github.com/vitejs/vite/issues/18517) + + + ## 6.0.0-beta.6 (2024-10-28) * fix: handle warmup glob hang (#18462) ([409fa5c](https://github.com/vitejs/vite/commit/409fa5c9dee0e394bcdc3b111f5b2e4261131ca0)), closes [#18462](https://github.com/vitejs/vite/issues/18462) diff --git a/packages/vite/package.json b/packages/vite/package.json index fcb890ca4954e6..1a108382cf4885 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -1,6 +1,6 @@ { "name": "vite", - "version": "6.0.0-beta.6", + "version": "6.0.0-beta.7", "type": "module", "license": "MIT", "author": "Evan You", From 3a46f97fbb699935c60b2f6912675296534312a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Wed, 30 Oct 2024 23:35:46 +0900 Subject: [PATCH 057/156] docs: migration guide for #18070 (#18519) --- docs/guide/migration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 5815e80505bd9a..1c08978b7335ad 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -58,6 +58,8 @@ There are other breaking changes which only affect few users. - [[#15637] fix!: default `build.cssMinify` to `'esbuild'` for SSR](https://github.com/vitejs/vite/pull/15637) - [`build.cssMinify`](/config/build-options#build-cssminify) is now enabled by default even for SSR builds. +- [[#18070] feat!: proxy bypass with WebSocket](https://github.com/vitejs/vite/pull/18070) + - `server.proxy[path].bypass` is now called for WebSocket upgrade requests and in that case, the `res` parameter will be `undefined`. - [[#18209] refactor!: bump minimal terser version to 5.16.0](https://github.com/vitejs/vite/pull/18209) - Minimal supported terser version for [`build.minify: 'terser'`](/config/build-options#build-minify) was bumped to 5.16.0 from 5.4.0. - [[#18231] chore(deps): update dependency @rollup/plugin-commonjs to v28](https://github.com/vitejs/vite/pull/18231) From 85bd0e9b0dc637c7645f2b56f93071d6e1ec149c Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 30 Oct 2024 15:48:44 +0100 Subject: [PATCH 058/156] fix: close watcher if it's disabled (#18521) --- packages/vite/src/node/server/index.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 7c463ba696f0c3..05edbf761cdf74 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -469,14 +469,17 @@ export async function _createServer( const watcher = chokidar.watch( // config file dependencies and env file might be outside of root - [ - root, - ...config.configFileDependencies, - ...getEnvFilesForMode(config.mode, config.envDir), - // Watch the public directory explicitly because it might be outside - // of the root directory. - ...(publicDir && publicFiles ? [publicDir] : []), - ], + // eslint-disable-next-line eqeqeq -- null means disabled + serverConfig.watch === null + ? [] + : [ + root, + ...config.configFileDependencies, + ...getEnvFilesForMode(config.mode, config.envDir), + // Watch the public directory explicitly because it might be outside + // of the root directory. + ...(publicDir && publicFiles ? [publicDir] : []), + ], resolvedWatchOptions, ) // If watch is turned off, patch `.add()` as a noop to prevent programmatically @@ -486,6 +489,7 @@ export async function _createServer( watcher.add = function () { return this } + await watcher.close() } const environments: Record = {} From 5286a90a3c1b693384f99903582a1f70b7b44945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 31 Oct 2024 00:55:25 +0900 Subject: [PATCH 059/156] fix: asset `new URL(,import.meta.url)` match (#18194) --- .../plugins/assetImportMetaUrl.spec.ts | 65 +++++++++++++++++++ .../src/node/plugins/assetImportMetaUrl.ts | 25 ++++--- 2 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 packages/vite/src/node/__tests__/plugins/assetImportMetaUrl.spec.ts 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..37dc870372da0f --- /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 should exist + const result = await instance.transform.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/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index 03686fc638cb9a..ba3e434d44d2a0 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -81,7 +81,7 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const templateLiteral = (ast as any).body[0].expression if (templateLiteral.expressions.length) { const pattern = buildGlobPattern(templateLiteral) - if (pattern.startsWith('**')) { + if (pattern.startsWith('*')) { // don't transform for patterns like this // because users won't intend to do that in most cases continue @@ -168,19 +168,18 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { function buildGlobPattern(ast: any) { let pattern = '' - let lastElementIndex = -1 - for (const exp of ast.expressions) { - for (let i = lastElementIndex + 1; i < ast.quasis.length; i++) { - const el = ast.quasis[i] - if (el.end < exp.start) { - pattern += el.value.raw - lastElementIndex = i - } + let lastIsGlob = false + for (let i = 0; i < ast.quasis.length; i++) { + const str = ast.quasis[i].value.raw + if (str) { + pattern += str + lastIsGlob = false + } + + if (ast.expressions[i] && !lastIsGlob) { + pattern += '*' + lastIsGlob = true } - pattern += '**' - } - for (let i = lastElementIndex + 1; i < ast.quasis.length; i++) { - pattern += ast.quasis[i].value.raw } return pattern } From aec5fdd72e3aeb2aa26796001b98f3f330be86d1 Mon Sep 17 00:00:00 2001 From: smeng9 <38666763+smeng9@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:03:50 -0700 Subject: [PATCH 060/156] fix(less): prevent rebasing `@import url(...)` (#17857) Co-authored-by: shaoyu Co-authored-by: bluwy --- packages/vite/src/node/plugins/css.ts | 8 +++++--- playground/css/__tests__/css.spec.ts | 2 ++ playground/css/index.html | 3 +++ playground/css/less/components/form.less | 2 ++ playground/css/less/ommer.less | 3 +++ 5 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 playground/css/less/ommer.less diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 2373ecf6ea50a3..1be9c414dbd0de 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -1682,10 +1682,11 @@ type CssUrlReplacer = ( ) => string | Promise // https://drafts.csswg.org/css-syntax-3/#identifier-code-point export const cssUrlRE = - /(?<=^|[^\w\-\u0080-\uffff])url\((\s*('[^']+'|"[^"]+")\s*|[^'")]+)\)/ + /(? { const imported = await page.$('.less') const atImport = await page.$('.less-at-import') const atImportAlias = await page.$('.less-at-import-alias') + const atImportUrlOmmer = await page.$('.less-at-import-url-ommer') const urlStartsWithVariable = await page.$('.less-url-starts-with-variable') expect(await getColor(imported)).toBe('blue') @@ -125,6 +126,7 @@ test('less', async () => { expect(await getBg(atImportAlias)).toMatch( isBuild ? /base64/ : '/nested/icon.png', ) + expect(await getColor(atImportUrlOmmer)).toBe('darkorange') expect(await getBg(urlStartsWithVariable)).toMatch( isBuild ? /ok-[-\w]+\.png/ : `${viteTestUrl}/ok.png`, ) diff --git a/playground/css/index.html b/playground/css/index.html index e179c208a7c82f..45525412570992 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -52,6 +52,9 @@

CSS

@import from Less: This should be darkslateblue and have bg image which url contains alias

+

+ @import url() from Less: This should be darkorange +

url starts with variable

diff --git a/playground/css/less/components/form.less b/playground/css/less/components/form.less index feaaea94ce1bba..99cdc4d5d1d118 100644 --- a/playground/css/less/components/form.less +++ b/playground/css/less/components/form.less @@ -1,3 +1,5 @@ +@import url('../../less/ommer.less'); + .form-box-data-uri { // data-uri() calls with relative paths should be replaced just like urls. background-image: data-uri('../images/backgrounds/form-select.svg'); diff --git a/playground/css/less/ommer.less b/playground/css/less/ommer.less new file mode 100644 index 00000000000000..a0ef4ce9dda922 --- /dev/null +++ b/playground/css/less/ommer.less @@ -0,0 +1,3 @@ +.less-at-import-url-ommer { + color: darkorange; +} From 3194a6a60714a3978f5e4b39d6223f32a8dc01ef Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 31 Oct 2024 10:13:21 +0800 Subject: [PATCH 061/156] fix(scss): improve error logs (#18522) Co-authored-by: Hiroshi Ogawa --- packages/vite/src/node/plugins/css.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 1be9c414dbd0de..e511255f69f8e5 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -2558,6 +2558,16 @@ const scssProcessor = ( e.message = `[sass] ${e.message}` e.id = e.file e.frame = e.formatted + // modern api lacks `line` and `column` property. extract from `e.span`. + // NOTE: the values are 0-based so +1 is required. + if (e.span?.start) { + e.line = e.span.start.line + 1 + e.column = e.span.start.column + 1 + // it also lacks `e.formatted`, so we shim with the message here since + // sass error messages have the frame already in them and we don't want + // to re-generate a new frame (same as legacy api) + e.frame = e.message + } return { code: '', error: e, deps: [] } } }, From a50ff6000bca46a6fe429f2c3a98c486ea5ebc8e Mon Sep 17 00:00:00 2001 From: patak <583075+patak-dev@users.noreply.github.com> Date: Thu, 31 Oct 2024 04:21:55 +0100 Subject: [PATCH 062/156] refactor: client-only top-level warmup (#18524) --- packages/vite/src/node/config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 85d1f16c536ac6..5caa09d3429542 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -966,15 +966,16 @@ export async function resolveConfig( // Merge default environment config values const defaultEnvironmentOptions = getDefaultEnvironmentOptions(config) // Some top level options only apply to the client environment - const defaultClientEnvironmentOptions = { + const defaultClientEnvironmentOptions: UserConfig = { ...defaultEnvironmentOptions, optimizeDeps: config.optimizeDeps, } - const defaultNonClientEnvironmentOptions = { + const defaultNonClientEnvironmentOptions: UserConfig = { ...defaultEnvironmentOptions, dev: { ...defaultEnvironmentOptions.dev, createEnvironment: undefined, + warmup: undefined, }, build: { ...defaultEnvironmentOptions.build, From 5d6dc491b6bb78613694eaf686e2e305b71af5e1 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 31 Oct 2024 12:10:00 +0800 Subject: [PATCH 063/156] fix(lib): only resolve css bundle name if have styles (#18530) --- packages/vite/src/node/plugins/css.ts | 28 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index e511255f69f8e5..dbe55e487616ba 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -435,7 +435,6 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { // since output formats have no effect on the generated CSS. let hasEmitted = false let chunkCSSMap: Map - let cssBundleName: string const rollupOptionsOutput = config.build.rollupOptions.output const assetFileNames = ( @@ -463,6 +462,21 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } } + function getCssBundleName() { + const cached = cssBundleNameCache.get(config) + if (cached) return cached + + const cssBundleName = config.build.lib + ? resolveLibCssFilename( + config.build.lib, + config.root, + config.packageCache, + ) + : defaultCssBundleName + cssBundleNameCache.set(config, cssBundleName) + return cssBundleName + } + return { name: 'vite:css-post', @@ -472,14 +486,6 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { hasEmitted = false chunkCSSMap = new Map() codeSplitEmitQueue = createSerialPromiseQueue() - cssBundleName = config.build.lib - ? resolveLibCssFilename( - config.build.lib, - config.root, - config.packageCache, - ) - : defaultCssBundleName - cssBundleNameCache.set(config, cssBundleName) }, async transform(css, id) { @@ -844,7 +850,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } } else { // resolve public URL from CSS paths, we need to use absolute paths - chunkCSS = resolveAssetUrlsInCss(chunkCSS, cssBundleName) + chunkCSS = resolveAssetUrlsInCss(chunkCSS, getCssBundleName()) // finalizeCss is called for the aggregated chunk in generateBundle chunkCSSMap.set(chunk.fileName, chunkCSS) @@ -918,7 +924,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { hasEmitted = true extractedCss = await finalizeCss(extractedCss, true, config) this.emitFile({ - name: cssBundleName, + name: getCssBundleName(), type: 'asset', source: extractedCss, }) From 94b085735372588d5f92c7f4a8cf68e8291f2db0 Mon Sep 17 00:00:00 2001 From: patak <583075+patak-dev@users.noreply.github.com> Date: Thu, 31 Oct 2024 05:13:20 +0100 Subject: [PATCH 064/156] refactor!: remove fs.cachedChecks option (#18493) --- docs/config/server-options.md | 8 - docs/guide/migration.md | 2 + packages/vite/src/node/fsUtils.ts | 433 ------------------ packages/vite/src/node/idResolver.ts | 2 - .../vite/src/node/plugins/importAnalysis.ts | 5 +- packages/vite/src/node/plugins/index.ts | 2 - packages/vite/src/node/plugins/preAlias.ts | 5 +- packages/vite/src/node/plugins/resolve.ts | 77 +++- packages/vite/src/node/server/index.ts | 20 +- .../node/server/middlewares/htmlFallback.ts | 10 +- .../src/node/server/middlewares/indexHtml.ts | 7 +- playground/html/vite.config.js | 3 - 12 files changed, 69 insertions(+), 505 deletions(-) delete mode 100644 packages/vite/src/node/fsUtils.ts diff --git a/docs/config/server-options.md b/docs/config/server-options.md index 32fbdb2a673a5c..780e45bf8d3e6c 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -329,14 +329,6 @@ export default defineConfig({ Blocklist for sensitive files being restricted to be served by Vite dev server. This will have higher priority than [`server.fs.allow`](#server-fs-allow). [picomatch patterns](https://github.com/micromatch/picomatch#globbing-features) are supported. -## server.fs.cachedChecks - -- **Type:** `boolean` -- **Default:** `false` -- **Experimental** - -Caches filenames of accessed directories to avoid repeated filesystem operations. Particularly in Windows, this could result in a performance boost. It is disabled by default due to edge cases when writing a file in a cached folder and immediately importing it. - ## server.origin - **Type:** `string` diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 1c08978b7335ad..3886d6875efc94 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -66,6 +66,8 @@ There are other breaking changes which only affect few users. - [`commonjsOptions.strictRequires`](https://github.com/rollup/plugins/blob/master/packages/commonjs/README.md#strictrequires) is now `true` by default (was `'auto'` before). - [[#18243] chore(deps)!: migrate `fast-glob` to `tinyglobby`](https://github.com/vitejs/vite/pull/18243) - Range braces (`{01..03}` ⇒ `['01', '02', '03']`) and incremental braces (`{2..8..2}` ⇒ `['2', '4', '6', '8']`) are no longer supported in globs. +- [[#18493] refactor!: remove fs.cachedChecks option](https://github.com/vitejs/vite/pull/18493) + - This opt-in optimization was removed due to edge cases when writing a file in a cached folder and immediately importing it. ## Migration from v4 diff --git a/packages/vite/src/node/fsUtils.ts b/packages/vite/src/node/fsUtils.ts deleted file mode 100644 index a295d4fc41adb6..00000000000000 --- a/packages/vite/src/node/fsUtils.ts +++ /dev/null @@ -1,433 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import type { FSWatcher } from 'dep-types/chokidar' -import type { ResolvedConfig } from './config' -import { - isInNodeModules, - normalizePath, - safeRealpathSync, - tryStatSync, -} from './utils' - -export interface FsUtils { - existsSync: (path: string) => boolean - isDirectory: (path: string) => boolean - - tryResolveRealFile: ( - path: string, - preserveSymlinks?: boolean, - ) => string | undefined - tryResolveRealFileWithExtensions: ( - path: string, - extensions: string[], - preserveSymlinks?: boolean, - ) => string | undefined - tryResolveRealFileOrType: ( - path: string, - preserveSymlinks?: boolean, - ) => { path?: string; type: 'directory' | 'file' } | undefined - - initWatcher?: (watcher: FSWatcher) => void -} - -// An implementation of fsUtils without caching -export const commonFsUtils: FsUtils = { - existsSync: fs.existsSync, - isDirectory, - - tryResolveRealFile, - tryResolveRealFileWithExtensions, - tryResolveRealFileOrType, -} - -const cachedFsUtilsMap = new WeakMap() -export function getFsUtils(config: ResolvedConfig): FsUtils { - let fsUtils = cachedFsUtilsMap.get(config) - if (!fsUtils) { - if ( - config.command !== 'serve' || - config.server.fs.cachedChecks !== true || - config.server.watch?.ignored || - process.versions.pnp - ) { - // cached fsUtils is only used in the dev server for now - // it is disabled by default due to potential edge cases when writing a file - // and reading it immediately - // It is also disabled when there aren't custom watcher ignored patterns configured - // and if yarn pnp isn't used - fsUtils = commonFsUtils - } else if ( - !config.resolve.preserveSymlinks && - config.root !== getRealPath(config.root) - ) { - fsUtils = commonFsUtils - } else { - fsUtils = createCachedFsUtils(config) - } - cachedFsUtilsMap.set(config, fsUtils) - } - return fsUtils -} - -type DirentsMap = Map - -type DirentCacheType = - | 'directory' - | 'file' - | 'symlink' - | 'error' - | 'directory_maybe_symlink' - | 'file_maybe_symlink' - -interface DirentCache { - dirents?: DirentsMap - type: DirentCacheType -} - -function readDirCacheSync(file: string): undefined | DirentsMap { - let dirents: fs.Dirent[] - try { - dirents = fs.readdirSync(file, { withFileTypes: true }) - } catch { - return - } - return direntsToDirentMap(dirents) -} - -function direntsToDirentMap(fsDirents: fs.Dirent[]): DirentsMap { - const dirents: DirentsMap = new Map() - for (const dirent of fsDirents) { - // We ignore non directory, file, and symlink entries - const type = dirent.isDirectory() - ? 'directory' - : dirent.isSymbolicLink() - ? 'symlink' - : dirent.isFile() - ? 'file' - : undefined - if (type) { - dirents.set(dirent.name, { type }) - } - } - return dirents -} - -function ensureFileMaybeSymlinkIsResolved( - direntCache: DirentCache, - filePath: string, -) { - if (direntCache.type !== 'file_maybe_symlink') return - - const isSymlink = fs - .lstatSync(filePath, { throwIfNoEntry: false }) - ?.isSymbolicLink() - direntCache.type = - isSymlink === undefined ? 'error' : isSymlink ? 'symlink' : 'file' -} - -function pathUntilPart(root: string, parts: string[], i: number): string { - let p = root - for (let k = 0; k < i; k++) p += '/' + parts[k] - return p -} - -export function createCachedFsUtils(config: ResolvedConfig): FsUtils { - const root = config.root // root is resolved and normalized, so it doesn't have a trailing slash - const rootDirPath = `${root}/` - const rootCache: DirentCache = { type: 'directory' } // dirents will be computed lazily - - const getDirentCacheSync = (parts: string[]): DirentCache | undefined => { - let direntCache: DirentCache = rootCache - for (let i = 0; i < parts.length; i++) { - if (direntCache.type === 'directory') { - let dirPath - if (!direntCache.dirents) { - dirPath = pathUntilPart(root, parts, i) - const dirents = readDirCacheSync(dirPath) - if (!dirents) { - direntCache.type = 'error' - return - } - direntCache.dirents = dirents - } - const nextDirentCache = direntCache.dirents!.get(parts[i]) - if (!nextDirentCache) { - return - } - if (nextDirentCache.type === 'directory_maybe_symlink') { - dirPath ??= pathUntilPart(root, parts, i + 1) - const isSymlink = fs - .lstatSync(dirPath, { throwIfNoEntry: false }) - ?.isSymbolicLink() - nextDirentCache.type = isSymlink ? 'symlink' : 'directory' - } - direntCache = nextDirentCache - } else if (direntCache.type === 'symlink') { - // early return if we encounter a symlink - return direntCache - } else if (direntCache.type === 'error') { - return direntCache - } else { - if (i !== parts.length - 1) { - return - } - if (direntCache.type === 'file_maybe_symlink') { - ensureFileMaybeSymlinkIsResolved( - direntCache, - pathUntilPart(root, parts, i), - ) - return direntCache - } else if (direntCache.type === 'file') { - return direntCache - } else { - return - } - } - } - return direntCache - } - - function getDirentCacheFromPath( - normalizedFile: string, - ): DirentCache | false | undefined { - // path.posix.normalize may return a path either with / or without / - if (normalizedFile[normalizedFile.length - 1] === '/') { - normalizedFile = normalizedFile.slice(0, -1) - } - if (normalizedFile === root) { - return rootCache - } - if (!normalizedFile.startsWith(rootDirPath)) { - return undefined - } - const pathFromRoot = normalizedFile.slice(rootDirPath.length) - const parts = pathFromRoot.split('/') - const direntCache = getDirentCacheSync(parts) - if (!direntCache || direntCache.type === 'error') { - return false - } - return direntCache - } - - function onPathAdd( - file: string, - type: 'directory_maybe_symlink' | 'file_maybe_symlink', - ) { - const direntCache = getDirentCacheFromPath( - normalizePath(path.dirname(file)), - ) - if ( - direntCache && - direntCache.type === 'directory' && - direntCache.dirents - ) { - direntCache.dirents.set(path.basename(file), { type }) - } - } - - function onPathUnlink(file: string) { - const direntCache = getDirentCacheFromPath( - normalizePath(path.dirname(file)), - ) - if ( - direntCache && - direntCache.type === 'directory' && - direntCache.dirents - ) { - direntCache.dirents.delete(path.basename(file)) - } - } - - return { - existsSync(file: string) { - if (isInNodeModules(file)) { - return fs.existsSync(file) - } - const normalizedFile = normalizePath(file) - const direntCache = getDirentCacheFromPath(normalizedFile) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return fs.existsSync(file) - } - return !!direntCache - }, - tryResolveRealFile( - file: string, - preserveSymlinks?: boolean, - ): string | undefined { - if (isInNodeModules(file)) { - return tryResolveRealFile(file, preserveSymlinks) - } - const normalizedFile = normalizePath(file) - const direntCache = getDirentCacheFromPath(normalizedFile) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return tryResolveRealFile(file, preserveSymlinks) - } - if (!direntCache || direntCache.type === 'directory') { - return - } - // We can avoid getRealPath even if preserveSymlinks is false because we know it's - // a file without symlinks in its path - return normalizedFile - }, - tryResolveRealFileWithExtensions( - file: string, - extensions: string[], - preserveSymlinks?: boolean, - ): string | undefined { - if (isInNodeModules(file)) { - return tryResolveRealFileWithExtensions( - file, - extensions, - preserveSymlinks, - ) - } - const normalizedFile = normalizePath(file) - const dirPath = path.posix.dirname(normalizedFile) - const direntCache = getDirentCacheFromPath(dirPath) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return tryResolveRealFileWithExtensions( - file, - extensions, - preserveSymlinks, - ) - } - if (!direntCache || direntCache.type !== 'directory') { - return - } - - if (!direntCache.dirents) { - const dirents = readDirCacheSync(dirPath) - if (!dirents) { - direntCache.type = 'error' - return - } - direntCache.dirents = dirents - } - - const base = path.posix.basename(normalizedFile) - for (const ext of extensions) { - const fileName = base + ext - const fileDirentCache = direntCache.dirents.get(fileName) - if (fileDirentCache) { - const filePath = dirPath + '/' + fileName - ensureFileMaybeSymlinkIsResolved(fileDirentCache, filePath) - if (fileDirentCache.type === 'symlink') { - // fallback to built-in fs for symlinked files - return tryResolveRealFile(filePath, preserveSymlinks) - } - if (fileDirentCache.type === 'file') { - return filePath - } - } - } - }, - tryResolveRealFileOrType( - file: string, - preserveSymlinks?: boolean, - ): { path?: string; type: 'directory' | 'file' } | undefined { - if (isInNodeModules(file)) { - return tryResolveRealFileOrType(file, preserveSymlinks) - } - const normalizedFile = normalizePath(file) - const direntCache = getDirentCacheFromPath(normalizedFile) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return tryResolveRealFileOrType(file, preserveSymlinks) - } - if (!direntCache) { - return - } - if (direntCache.type === 'directory') { - return { type: 'directory' } - } - // We can avoid getRealPath even if preserveSymlinks is false because we know it's - // a file without symlinks in its path - return { path: normalizedFile, type: 'file' } - }, - isDirectory(dirPath: string) { - if (isInNodeModules(dirPath)) { - return isDirectory(dirPath) - } - const direntCache = getDirentCacheFromPath(normalizePath(dirPath)) - if ( - direntCache === undefined || - (direntCache && direntCache.type === 'symlink') - ) { - // fallback to built-in fs for out-of-root and symlinked files - return isDirectory(dirPath) - } - return direntCache && direntCache.type === 'directory' - }, - - initWatcher(watcher: FSWatcher) { - watcher.on('add', (file) => { - onPathAdd(file, 'file_maybe_symlink') - }) - watcher.on('addDir', (dir) => { - onPathAdd(dir, 'directory_maybe_symlink') - }) - watcher.on('unlink', onPathUnlink) - watcher.on('unlinkDir', onPathUnlink) - }, - } -} - -function tryResolveRealFile( - file: string, - preserveSymlinks?: boolean, -): string | undefined { - const stat = tryStatSync(file) - if (stat?.isFile()) return getRealPath(file, preserveSymlinks) -} - -function tryResolveRealFileWithExtensions( - filePath: string, - extensions: string[], - preserveSymlinks?: boolean, -): string | undefined { - for (const ext of extensions) { - const res = tryResolveRealFile(filePath + ext, preserveSymlinks) - if (res) return res - } -} - -function tryResolveRealFileOrType( - file: string, - preserveSymlinks?: boolean, -): { path?: string; type: 'directory' | 'file' } | undefined { - const fileStat = tryStatSync(file) - if (fileStat?.isFile()) { - return { path: getRealPath(file, preserveSymlinks), type: 'file' } - } - if (fileStat?.isDirectory()) { - return { type: 'directory' } - } - return -} - -function getRealPath(resolved: string, preserveSymlinks?: boolean): string { - if (!preserveSymlinks) { - resolved = safeRealpathSync(resolved) - } - return normalizePath(resolved) -} - -function isDirectory(path: string): boolean { - const stat = tryStatSync(path) - return stat?.isDirectory() ?? false -} diff --git a/packages/vite/src/node/idResolver.ts b/packages/vite/src/node/idResolver.ts index 5923e985a844cc..24b93bca999468 100644 --- a/packages/vite/src/node/idResolver.ts +++ b/packages/vite/src/node/idResolver.ts @@ -5,7 +5,6 @@ import type { EnvironmentPluginContainer } from './server/pluginContainer' import { createEnvironmentPluginContainer } from './server/pluginContainer' import { resolvePlugin } from './plugins/resolve' import type { InternalResolveOptions } from './plugins/resolve' -import { getFsUtils } from './fsUtils' import type { Environment } from './environment' import type { PartialEnvironment } from './baseEnvironment' @@ -69,7 +68,6 @@ export function createIdResolver( preferRelative: false, tryIndex: true, ...options, - fsUtils: getFsUtils(config), // Ignore sideEffects and other computations as we only need the id idOnly: true, }), diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 8281c1bcbedd17..f13d920fb83647 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import fs from 'node:fs' import { performance } from 'node:perf_hooks' import colors from 'picocolors' import MagicString from 'magic-string' @@ -52,7 +53,6 @@ import { transformStableResult, urlRE, } from '../utils' -import { getFsUtils } from '../fsUtils' import { checkPublicFile } from '../publicDir' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' @@ -107,7 +107,6 @@ export function normalizeResolvedIdToUrl( ): string { const root = environment.config.root const depsOptimizer = environment.depsOptimizer - const fsUtils = getFsUtils(environment.getTopLevelConfig()) // normalize all imports into resolved URLs // e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'` @@ -121,7 +120,7 @@ export function normalizeResolvedIdToUrl( // We'll remove this as soon we're able to fix the react plugins. (resolved.id !== '/@react-refresh' && path.isAbsolute(resolved.id) && - fsUtils.existsSync(cleanUrl(resolved.id))) + fs.existsSync(cleanUrl(resolved.id))) ) { // an optimized deps may not yet exists in the filesystem, or // a regular file exists but is out of root: rewrite to absolute /@fs/ paths diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 1ce247db4bf1ab..3d0933d8e87ca5 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -4,7 +4,6 @@ import type { PluginHookUtils, ResolvedConfig } from '../config' import { isDepOptimizationDisabled } from '../optimizer' import type { HookHandler, Plugin, PluginWithRequiredHook } from '../plugin' import { watchPackageDataPlugin } from '../packages' -import { getFsUtils } from '../fsUtils' import { jsonPlugin } from './json' import { resolvePlugin } from './resolve' import { optimizedDepsPlugin } from './optimizedDeps' @@ -65,7 +64,6 @@ export async function resolvePlugins( isBuild, packageCache: config.packageCache, asSrc: true, - fsUtils: getFsUtils(config), optimizeDeps: true, externalize: true, }, diff --git a/packages/vite/src/node/plugins/preAlias.ts b/packages/vite/src/node/plugins/preAlias.ts index aa83bfe492a3a4..7422544bb98d79 100644 --- a/packages/vite/src/node/plugins/preAlias.ts +++ b/packages/vite/src/node/plugins/preAlias.ts @@ -1,4 +1,5 @@ import path from 'node:path' +import fs from 'node:fs' import type { Alias, AliasOptions, @@ -13,7 +14,6 @@ import { isOptimizable, moduleListContains, } from '../utils' -import { getFsUtils } from '../fsUtils' import { cleanUrl, withTrailingSlash } from '../../shared/utils' import { tryOptimizedResolve } from './resolve' @@ -23,7 +23,6 @@ import { tryOptimizedResolve } from './resolve' export function preAliasPlugin(config: ResolvedConfig): Plugin { const findPatterns = getAliasPatterns(config.resolve.alias) const isBuild = config.command === 'build' - const fsUtils = getFsUtils(config) return { name: 'vite:pre-alias', async resolveId(id, importer, options) { @@ -60,7 +59,7 @@ export function preAliasPlugin(config: ResolvedConfig): Plugin { const isVirtual = resolvedId === id || resolvedId.includes('\0') if ( !isVirtual && - fsUtils.existsSync(resolvedId) && + fs.existsSync(resolvedId) && !moduleListContains(optimizeDeps.exclude, id) && path.isAbsolute(resolvedId) && (isInNodeModules(resolvedId) || diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 5935a3c5996f0b..e998fc0a1c800c 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -38,8 +38,6 @@ import { optimizedDepInfoFromFile, optimizedDepInfoFromId } from '../optimizer' import type { DepsOptimizer } from '../optimizer' import type { DepOptimizationOptions, SSROptions } from '..' import type { PackageCache, PackageData } from '../packages' -import type { FsUtils } from '../fsUtils' -import { commonFsUtils } from '../fsUtils' import { shouldExternalize } from '../external' import { findNearestMainPackageData, @@ -113,7 +111,6 @@ interface ResolvePluginOptions { isBuild: boolean isProduction: boolean packageCache?: PackageCache - fsUtils?: FsUtils /** * src code mode also attempts the following: * - resolving /xxx as URLs @@ -629,13 +626,8 @@ function tryCleanFsResolve( ): string | undefined { const { tryPrefix, extensions, preserveSymlinks } = options - const fsUtils = options.fsUtils ?? commonFsUtils - // Optimization to get the real type or file type (directory, file, other) - const fileResult = fsUtils.tryResolveRealFileOrType( - file, - options.preserveSymlinks, - ) + const fileResult = tryResolveRealFileOrType(file, options.preserveSymlinks) if (fileResult?.path) return fileResult.path @@ -645,13 +637,13 @@ function tryCleanFsResolve( const possibleJsToTs = options.isFromTsImporter && isPossibleTsOutput(file) if (possibleJsToTs || options.extensions.length || tryPrefix) { const dirPath = path.dirname(file) - if (fsUtils.isDirectory(dirPath)) { + if (isDirectory(dirPath)) { if (possibleJsToTs) { // try resolve .js, .mjs, .cjs or .jsx import to typescript file const fileExt = path.extname(file) const fileName = file.slice(0, -fileExt.length) if ( - (res = fsUtils.tryResolveRealFile( + (res = tryResolveRealFile( fileName + fileExt.replace('js', 'ts'), preserveSymlinks, )) @@ -660,16 +652,13 @@ function tryCleanFsResolve( // for .js, also try .tsx if ( fileExt === '.js' && - (res = fsUtils.tryResolveRealFile( - fileName + '.tsx', - preserveSymlinks, - )) + (res = tryResolveRealFile(fileName + '.tsx', preserveSymlinks)) ) return res } if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( file, extensions, preserveSymlinks, @@ -680,11 +669,10 @@ function tryCleanFsResolve( if (tryPrefix) { const prefixed = `${dirPath}/${options.tryPrefix}${path.basename(file)}` - if ((res = fsUtils.tryResolveRealFile(prefixed, preserveSymlinks))) - return res + if ((res = tryResolveRealFile(prefixed, preserveSymlinks))) return res if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( prefixed, extensions, preserveSymlinks, @@ -702,7 +690,7 @@ function tryCleanFsResolve( if (!skipPackageJson) { let pkgPath = `${dirPath}/package.json` try { - if (fsUtils.existsSync(pkgPath)) { + if (fs.existsSync(pkgPath)) { if (!options.preserveSymlinks) { pkgPath = safeRealpathSync(pkgPath) } @@ -718,7 +706,7 @@ function tryCleanFsResolve( } if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( `${dirPath}/index`, extensions, preserveSymlinks, @@ -728,7 +716,7 @@ function tryCleanFsResolve( if (tryPrefix) { if ( - (res = fsUtils.tryResolveRealFileWithExtensions( + (res = tryResolveRealFileWithExtensions( `${dirPath}/${options.tryPrefix}index`, extensions, preserveSymlinks, @@ -1322,3 +1310,48 @@ function mapWithBrowserField( function equalWithoutSuffix(path: string, key: string, suffix: string) { return key.endsWith(suffix) && key.slice(0, -suffix.length) === path } + +function tryResolveRealFile( + file: string, + preserveSymlinks?: boolean, +): string | undefined { + const stat = tryStatSync(file) + if (stat?.isFile()) return getRealPath(file, preserveSymlinks) +} + +function tryResolveRealFileWithExtensions( + filePath: string, + extensions: string[], + preserveSymlinks?: boolean, +): string | undefined { + for (const ext of extensions) { + const res = tryResolveRealFile(filePath + ext, preserveSymlinks) + if (res) return res + } +} + +function tryResolveRealFileOrType( + file: string, + preserveSymlinks?: boolean, +): { path?: string; type: 'directory' | 'file' } | undefined { + const fileStat = tryStatSync(file) + if (fileStat?.isFile()) { + return { path: getRealPath(file, preserveSymlinks), type: 'file' } + } + if (fileStat?.isDirectory()) { + return { type: 'directory' } + } + return +} + +function getRealPath(resolved: string, preserveSymlinks?: boolean): string { + if (!preserveSymlinks) { + resolved = safeRealpathSync(resolved) + } + return normalizePath(resolved) +} + +function isDirectory(path: string): boolean { + const stat = tryStatSync(path) + return stat?.isDirectory() ?? false +} diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 05edbf761cdf74..ffc8577120a03d 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -36,7 +36,6 @@ import { setupSIGTERMListener, teardownSIGTERMListener, } from '../utils' -import { getFsUtils } from '../fsUtils' import { ssrLoadModule } from '../ssr/ssrModuleLoader' import { ssrFixStacktrace, ssrRewriteStacktrace } from '../ssr/ssrStacktrace' import { ssrTransform } from '../ssr/ssrTransform' @@ -218,14 +217,6 @@ export interface FileSystemServeOptions { * @default ['.env', '.env.*', '*.{crt,pem}', '**\/.git/**'] */ deny?: string[] - - /** - * Enable caching of fs calls. It is enabled by default if no custom watch ignored patterns are provided. - * - * @experimental - * @default undefined - */ - cachedChecks?: boolean } export type ServerHook = ( @@ -810,8 +801,6 @@ export async function _createServer( await onHMRUpdate('update', file) }) - getFsUtils(config).initWatcher?.(watcher) - watcher.on('add', (file) => { onFileAddUnlink(file, false) }) @@ -889,13 +878,7 @@ export async function _createServer( // html fallback if (config.appType === 'spa' || config.appType === 'mpa') { - middlewares.use( - htmlFallbackMiddleware( - root, - config.appType === 'spa', - getFsUtils(config), - ), - ) + middlewares.use(htmlFallbackMiddleware(root, config.appType === 'spa')) } // run post config hooks @@ -1095,7 +1078,6 @@ export function resolveServerOptions( strict: server.fs?.strict ?? true, allow: allowDirs, deny, - cachedChecks: server.fs?.cachedChecks, } if (server.origin?.endsWith('/')) { diff --git a/packages/vite/src/node/server/middlewares/htmlFallback.ts b/packages/vite/src/node/server/middlewares/htmlFallback.ts index a7f9d826a2c738..4dbfea3538c540 100644 --- a/packages/vite/src/node/server/middlewares/htmlFallback.ts +++ b/packages/vite/src/node/server/middlewares/htmlFallback.ts @@ -1,8 +1,7 @@ import path from 'node:path' +import fs from 'node:fs' import type { Connect } from 'dep-types/connect' import { createDebugger } from '../../utils' -import type { FsUtils } from '../../fsUtils' -import { commonFsUtils } from '../../fsUtils' import { cleanUrl } from '../../../shared/utils' const debug = createDebugger('vite:html-fallback') @@ -10,7 +9,6 @@ const debug = createDebugger('vite:html-fallback') export function htmlFallbackMiddleware( root: string, spaFallback: boolean, - fsUtils: FsUtils = commonFsUtils, ): Connect.NextHandleFunction { // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteHtmlFallbackMiddleware(req, _res, next) { @@ -37,7 +35,7 @@ export function htmlFallbackMiddleware( // so we need to check if the file exists if (pathname.endsWith('.html')) { const filePath = path.join(root, pathname) - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { debug?.(`Rewriting ${req.method} ${req.url} to ${url}`) req.url = url return next() @@ -46,7 +44,7 @@ export function htmlFallbackMiddleware( // trailing slash should check for fallback index.html else if (pathname[pathname.length - 1] === '/') { const filePath = path.join(root, pathname, 'index.html') - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { const newUrl = url + 'index.html' debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`) req.url = newUrl @@ -56,7 +54,7 @@ export function htmlFallbackMiddleware( // non-trailing slash should check for fallback .html else { const filePath = path.join(root, pathname + '.html') - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { const newUrl = url + '.html' debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`) req.url = newUrl diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 29dbfb415f7f98..27610bb502e5b8 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs' import fsp from 'node:fs/promises' import path from 'node:path' import MagicString from 'magic-string' @@ -40,7 +41,6 @@ import { processSrcSetSync, stripBase, } from '../../utils' -import { getFsUtils } from '../../fsUtils' import { checkPublicFile } from '../../publicDir' import { isCSSRequest } from '../../plugins/css' import { getCodeWithSourcemap, injectSourcesContent } from '../sourcemap' @@ -195,7 +195,7 @@ const devHtmlHook: IndexHtmlTransformHook = async ( let proxyModuleUrl: string const trailingSlash = htmlPath.endsWith('/') - if (!trailingSlash && getFsUtils(config).existsSync(filename)) { + if (!trailingSlash && fs.existsSync(filename)) { proxyModulePath = htmlPath proxyModuleUrl = proxyModulePath } else { @@ -441,7 +441,6 @@ export function indexHtmlMiddleware( server: ViteDevServer | PreviewServer, ): Connect.NextHandleFunction { const isDev = isDevServer(server) - const fsUtils = getFsUtils(server.config) // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return async function viteIndexHtmlMiddleware(req, res, next) { @@ -459,7 +458,7 @@ export function indexHtmlMiddleware( filePath = path.join(root, decodeURIComponent(url)) } - if (fsUtils.existsSync(filePath)) { + if (fs.existsSync(filePath)) { const headers = isDev ? server.config.server.headers : server.config.preview.headers diff --git a/playground/html/vite.config.js b/playground/html/vite.config.js index d71da52096bb52..484bfa9ce9a060 100644 --- a/playground/html/vite.config.js +++ b/playground/html/vite.config.js @@ -44,9 +44,6 @@ export default defineConfig({ }, server: { - fs: { - cachedChecks: false, - }, warmup: { clientFiles: ['./warmup/*'], }, From d297569b71717acc2b1f4ade06981ccf5d8e1829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 31 Oct 2024 13:15:02 +0900 Subject: [PATCH 065/156] ci: use Node 22 for macOS and Windows CI (#18531) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b271bc0178a38..22b19a32a3f449 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,9 +69,9 @@ jobs: include: # Active LTS + other OS - os: macos-latest - node_version: 20 + node_version: 22 - os: windows-latest - node_version: 20 + node_version: 22 fail-fast: false name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}" From 28cefcaf2861b72901abe1f047d9ec6298b745f8 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 31 Oct 2024 15:43:42 +0800 Subject: [PATCH 066/156] chore(plugin-legacy): add type module in package.json (#18535) --- packages/plugin-legacy/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-legacy/package.json b/packages/plugin-legacy/package.json index c989393cbe9aef..81e0c3efd7f83d 100644 --- a/packages/plugin-legacy/package.json +++ b/packages/plugin-legacy/package.json @@ -1,6 +1,7 @@ { "name": "@vitejs/plugin-legacy", "version": "5.4.3", + "type": "module", "license": "MIT", "author": "Evan You", "files": [ From 826c81a40bb25914d55cd2e96b548f1a2c384a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Thu, 31 Oct 2024 16:48:12 +0900 Subject: [PATCH 067/156] fix: allow nested dependency selector to be used for `optimizeDeps.include` for SSR (#18506) --- packages/vite/src/node/optimizer/index.ts | 4 +- packages/vite/src/node/optimizer/resolve.ts | 2 +- packages/vite/src/node/plugins/resolve.ts | 77 ++++----------------- 3 files changed, 17 insertions(+), 66 deletions(-) diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index eb855065e449dd..9460c3da844fcf 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -842,9 +842,7 @@ export async function addManuallyIncludedOptimizeDeps( const entry = await resolve(id) if (entry) { if (isOptimizable(entry, optimizeDeps)) { - if (!entry.endsWith('?__vite_skip_optimization')) { - deps[normalizedId] = entry - } + deps[normalizedId] = entry } else { unableToOptimize(id, 'Cannot optimize dependency') } diff --git a/packages/vite/src/node/optimizer/resolve.ts b/packages/vite/src/node/optimizer/resolve.ts index f1dbc0d199e60d..6da566b3c6e011 100644 --- a/packages/vite/src/node/optimizer/resolve.ts +++ b/packages/vite/src/node/optimizer/resolve.ts @@ -15,9 +15,9 @@ export function createOptimizeDepsIncludeResolver( const resolve = createBackCompatIdResolver(topLevelConfig, { asSrc: false, scan: true, - ssrOptimizeCheck: environment.config.consumer === 'server', packageCache: new Map(), }) + return async (id: string) => { const lastArrowIndex = id.lastIndexOf('>') if (lastArrowIndex === -1) { diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index e998fc0a1c800c..bb3084eebb3833 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -11,7 +11,6 @@ import { DEP_VERSION_RE, ENV_ENTRY, FS_PREFIX, - OPTIMIZABLE_ENTRY_RE, SPECIAL_QUERY_RE, } from '../constants' import { @@ -36,7 +35,7 @@ import { import type { ResolvedEnvironmentOptions } from '../config' import { optimizedDepInfoFromFile, optimizedDepInfoFromId } from '../optimizer' import type { DepsOptimizer } from '../optimizer' -import type { DepOptimizationOptions, SSROptions } from '..' +import type { SSROptions } from '..' import type { PackageCache, PackageData } from '../packages' import { shouldExternalize } from '../external' import { @@ -129,8 +128,6 @@ interface ResolvePluginOptions { isFromTsImporter?: boolean // True when resolving during the scan phase to discover dependencies scan?: boolean - // Appends ?__vite_skip_optimization to the resolved id if shouldn't be optimized - ssrOptimizeCheck?: boolean /** * Optimize deps during dev, defaults to false // TODO: Review default @@ -248,8 +245,6 @@ export function resolvePlugin( scan: resolveOpts?.scan ?? resolveOptions.scan, } - const depsOptimizerOptions = this.environment.config.optimizeDeps - const resolvedImports = resolveSubpathImports(id, importer, options) if (resolvedImports) { id = resolvedImports @@ -337,14 +332,7 @@ export function resolvePlugin( if ( options.webCompatible && options.mainFields.includes('browser') && - (res = tryResolveBrowserMapping( - fsPath, - importer, - options, - true, - undefined, - depsOptimizerOptions, - )) + (res = tryResolveBrowserMapping(fsPath, importer, options, true)) ) { return res } @@ -436,7 +424,6 @@ export function resolvePlugin( options, false, external, - depsOptimizerOptions, )) ) { return res @@ -450,7 +437,6 @@ export function resolvePlugin( depsOptimizer, external, undefined, - depsOptimizerOptions, )) ) { return res @@ -742,7 +728,6 @@ export function tryNodeResolve( depsOptimizer?: DepsOptimizer, externalize?: boolean, allowLinkedExternal: boolean = true, - depsOptimizerOptions?: DepOptimizationOptions, ): PartialResolvedId | undefined { const { root, dedupe, isBuild, preserveSymlinks, packageCache } = options @@ -846,10 +831,7 @@ export function tryNodeResolve( return { ...resolved, id: resolvedId, external: true } } - if ( - !options.idOnly && - ((!options.scan && isBuild && !depsOptimizer) || externalize) - ) { + if (!options.idOnly && ((!options.scan && isBuild) || externalize)) { // Resolve package side effects for build so that rollup can better // perform tree-shaking return processResult({ @@ -859,70 +841,43 @@ export function tryNodeResolve( } if ( - !options.ssrOptimizeCheck && - (!isInNodeModules(resolved) || // linked - !depsOptimizer || // resolving before listening to the server - options.scan) // initial esbuild scan phase + !isInNodeModules(resolved) || // linked + !depsOptimizer || // resolving before listening to the server + options.scan // initial esbuild scan phase ) { return { id: resolved } } // if we reach here, it's a valid dep import that hasn't been optimized. - const isJsType = depsOptimizer - ? isOptimizable(resolved, depsOptimizer.options) - : OPTIMIZABLE_ENTRY_RE.test(resolved) - - let exclude = depsOptimizer?.options.exclude - if (options.ssrOptimizeCheck) { - // we don't have the depsOptimizer - exclude = depsOptimizerOptions?.exclude - } + const isJsType = isOptimizable(resolved, depsOptimizer.options) + const exclude = depsOptimizer.options.exclude const skipOptimization = - (!options.ssrOptimizeCheck && depsOptimizer?.options.noDiscovery) || + depsOptimizer.options.noDiscovery || !isJsType || (importer && isInNodeModules(importer)) || exclude?.includes(pkgId) || exclude?.includes(id) || SPECIAL_QUERY_RE.test(resolved) - if (options.ssrOptimizeCheck) { - return { - id: skipOptimization - ? injectQuery(resolved, `__vite_skip_optimization`) - : resolved, - } - } - if (skipOptimization) { // excluded from optimization // Inject a version query to npm deps so that the browser // can cache it without re-validation, but only do so for known js types. // otherwise we may introduce duplicated modules for externalized files // from pre-bundled deps. - if (!isBuild) { - const versionHash = depsOptimizer!.metadata.browserHash - if (versionHash && isJsType) { - resolved = injectQuery(resolved, `v=${versionHash}`) - } + const versionHash = depsOptimizer.metadata.browserHash + if (versionHash && isJsType) { + resolved = injectQuery(resolved, `v=${versionHash}`) } } else { // this is a missing import, queue optimize-deps re-run and // get a resolved its optimized info - const optimizedInfo = depsOptimizer!.registerMissingImport(id, resolved) - resolved = depsOptimizer!.getOptimizedDepId(optimizedInfo) + const optimizedInfo = depsOptimizer.registerMissingImport(id, resolved) + resolved = depsOptimizer.getOptimizedDepId(optimizedInfo) } - if (!options.idOnly && !options.scan && isBuild) { - // Resolve package side effects for build so that rollup can better - // perform tree-shaking - return { - id: resolved, - moduleSideEffects: pkg.hasSideEffects(resolved), - } - } else { - return { id: resolved! } - } + return { id: resolved } } export async function tryOptimizedResolve( @@ -1186,7 +1141,6 @@ function tryResolveBrowserMapping( options: InternalResolveOptions, isFilePath: boolean, externalize?: boolean, - depsOptimizerOptions?: DepOptimizationOptions, ) { let res: string | undefined const pkg = @@ -1205,7 +1159,6 @@ function tryResolveBrowserMapping( undefined, undefined, undefined, - depsOptimizerOptions, )?.id : tryFsResolve(path.join(pkg.dir, browserMappedPath), options)) ) { From 8a7af50b5ddf72f21098406e9668bc609b323899 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 31 Oct 2024 15:57:16 +0800 Subject: [PATCH 068/156] feat(html)!: support more asset sources (#11138) --- docs/guide/features.md | 22 ++- .../src/node/__tests__/assetSource.spec.ts | 97 ++++++++++ packages/vite/src/node/assetSource.ts | 151 +++++++++++++++ packages/vite/src/node/plugins/html.ts | 178 +++++++----------- .../src/node/server/middlewares/indexHtml.ts | 46 ++--- playground/assets/__tests__/assets.spec.ts | 12 ++ playground/assets/index.html | 5 + 7 files changed, 369 insertions(+), 142 deletions(-) create mode 100644 packages/vite/src/node/__tests__/assetSource.spec.ts create mode 100644 packages/vite/src/node/assetSource.ts diff --git a/docs/guide/features.md b/docs/guide/features.md index ea5855ab9ba368..30b0ad7456ce8a 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -170,9 +170,24 @@ Any HTML files in your project root can be directly accessed by its respective d - `/about.html` -> `http://localhost:5173/about.html` - `/blog/index.html` -> `http://localhost:5173/blog/index.html` -HTML elements such as ` diff --git a/packages/vite/src/node/__tests__/assetSource.spec.ts b/packages/vite/src/node/__tests__/assetSource.spec.ts new file mode 100644 index 00000000000000..2c05d90e57b265 --- /dev/null +++ b/packages/vite/src/node/__tests__/assetSource.spec.ts @@ -0,0 +1,97 @@ +import { describe, expect, test } from 'vitest' +import { type DefaultTreeAdapterMap, parseFragment } from 'parse5' +import { getNodeAssetAttributes } from '../assetSource' + +describe('getNodeAssetAttributes', () => { + const getNode = (html: string) => { + const ast = parseFragment(html, { sourceCodeLocationInfo: true }) + return ast.childNodes[0] as DefaultTreeAdapterMap['element'] + } + + test('handles img src', () => { + const node = getNode('') + const attrs = getNodeAssetAttributes(node) + expect(attrs).toHaveLength(1) + expect(attrs[0]).toHaveProperty('type', 'src') + expect(attrs[0]).toHaveProperty('key', 'src') + expect(attrs[0]).toHaveProperty('value', 'foo.jpg') + expect(attrs[0].attributes).toEqual({ src: 'foo.jpg' }) + expect(attrs[0].location).toHaveProperty('startOffset', 5) + expect(attrs[0].location).toHaveProperty('endOffset', 18) + }) + + test('handles source srcset', () => { + const node = getNode('') + const attrs = getNodeAssetAttributes(node) + expect(attrs).toHaveLength(1) + expect(attrs[0]).toHaveProperty('type', 'srcset') + expect(attrs[0]).toHaveProperty('key', 'srcset') + expect(attrs[0]).toHaveProperty('value', 'foo.jpg 1x, bar.jpg 2x') + expect(attrs[0].attributes).toEqual({ srcset: 'foo.jpg 1x, bar.jpg 2x' }) + }) + + test('handles video src and poster', () => { + const node = getNode('
) diff --git a/playground/rolldown-dev-react/vite.config.ts b/playground/rolldown-dev-react/vite.config.ts index 80021f10be9e4a..d9dd3a2db2eacd 100644 --- a/playground/rolldown-dev-react/vite.config.ts +++ b/playground/rolldown-dev-react/vite.config.ts @@ -8,4 +8,38 @@ export default defineConfig({ reactRefresh: true, }, }, + plugins: [ + { + name: 'test', + options() { + console.log('[debug:options]', this.environment?.name) + }, + buildStart() { + console.log('[debug:buildStart]', this.environment?.name) + }, + buildEnd() { + console.log('[debug:buildEnd]', this.environment?.name) + }, + resolveId: { + handler(source, importer, _options) { + if (source === 'virtual:test') { + console.log('[debug:resolveId]', [ + this.environment?.name, + source, + importer, + ]) + return `\0virtual:test` + } + }, + }, + load: { + handler(id, _options) { + if (id === '\0virtual:test') { + console.log('[debug:load]', this.environment?.name) + return `export default "virtual-ok"` + } + }, + }, + }, + ], }) From ff80fa1bb86a39e51720acb4214db148d99e1e6f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 17:50:13 +0900 Subject: [PATCH 145/156] chore: cleanup --- packages/vite/src/node/server/environments/rolldown.ts | 1 - packages/vite/src/node/server/pluginContainer.ts | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index cd19bf3de7b714..c1aee76ac67771 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -199,7 +199,6 @@ class RolldownEnvironment extends DevEnvironment { await super.init() // patch out plugin container hooks assert(this._pluginContainer) - this._pluginContainer.resolveRollupOptions = async () => undefined! this._pluginContainer.buildStart = async () => {} this._pluginContainer.close = async () => {} await this.build() diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index 04d0b53a4d5609..a85d17509eb0d4 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -141,6 +141,9 @@ export async function createEnvironmentPluginContainer( plugins, watcher, ) + if (environment.config.experimental.rolldownDev) { + return container + } await container.resolveRollupOptions() return container } @@ -257,9 +260,6 @@ class EnvironmentPluginContainer { } async resolveRollupOptions(): Promise { - if (this.environment.config.experimental.rolldownDev) { - return undefined! - } if (!this._resolvedRollupOptions) { let options = this.environment.config.build.rollupOptions for (const optionsHook of this.getSortedPluginHooks('options')) { @@ -310,9 +310,6 @@ class EnvironmentPluginContainer { } async buildStart(_options?: InputOptions): Promise { - if (this.environment.config.experimental.rolldownDev) { - return - } if (this._started) { if (this._buildStartPromise) { await this._buildStartPromise From a40461680772f4b4c5c7176b4d8bad8bcaba3095 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 18:07:00 +0900 Subject: [PATCH 146/156] feat: use injectEnvironmentToHooks --- .../src/node/server/environments/rolldown.ts | 6 ++-- playground/rolldown-dev-react/vite.config.ts | 2 +- playground/rolldown-dev-ssr/src/app.tsx | 3 ++ playground/rolldown-dev-ssr/vite.config.ts | 32 +++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index c1aee76ac67771..becd6664e0938b 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -18,6 +18,7 @@ import type { ViteDevServer, } from '../..' import { CLIENT_ENTRY } from '../../constants' +import { injectEnvironmentToHooks } from '../../build' const require = createRequire(import.meta.url) @@ -221,11 +222,12 @@ class RolldownEnvironment extends DevEnvironment { } // all plugins are shared like Vite 6 `sharedConfigBuild`. - // TODO: setup PluginContext.environment - const plugins = this._plugins!.filter( + let plugins = this._plugins! + plugins = plugins.filter( // TODO: reuse core plugins (p) => !(p.name?.startsWith('vite:') || p.name === 'alias'), ) + plugins = plugins.map((p) => injectEnvironmentToHooks(this as any, p)) console.time(`[rolldown:${this.name}:build]`) const inputOptions: rolldown.InputOptions = { diff --git a/playground/rolldown-dev-react/vite.config.ts b/playground/rolldown-dev-react/vite.config.ts index d9dd3a2db2eacd..2e1177de4a86ed 100644 --- a/playground/rolldown-dev-react/vite.config.ts +++ b/playground/rolldown-dev-react/vite.config.ts @@ -36,7 +36,7 @@ export default defineConfig({ handler(id, _options) { if (id === '\0virtual:test') { console.log('[debug:load]', this.environment?.name) - return `export default "virtual-ok"` + return `export default "virtual-ok, environment.name: ${this.environment.name}"` } }, }, diff --git a/playground/rolldown-dev-ssr/src/app.tsx b/playground/rolldown-dev-ssr/src/app.tsx index 627093629d6052..2b0900cb8c7d17 100644 --- a/playground/rolldown-dev-ssr/src/app.tsx +++ b/playground/rolldown-dev-ssr/src/app.tsx @@ -1,4 +1,6 @@ import React from 'react' +// @ts-expect-error no type +import virtualTest from 'virtual:test' export function App() { const [count, setCount] = React.useState(0) @@ -7,6 +9,7 @@ export function App() {

Rolldown SSR

+
[virtual:test] {virtualTest}
) } diff --git a/playground/rolldown-dev-ssr/vite.config.ts b/playground/rolldown-dev-ssr/vite.config.ts index e4cd8536b6f090..bbf5c614d1aff3 100644 --- a/playground/rolldown-dev-ssr/vite.config.ts +++ b/playground/rolldown-dev-ssr/vite.config.ts @@ -42,5 +42,37 @@ export default defineConfig({ } }, }, + { + name: 'test', + options() { + console.log('[debug:options]', this.environment?.name) + }, + buildStart() { + console.log('[debug:buildStart]', this.environment?.name) + }, + buildEnd() { + console.log('[debug:buildEnd]', this.environment?.name) + }, + resolveId: { + handler(source, importer, _options) { + if (source === 'virtual:test') { + console.log('[debug:resolveId]', [ + this.environment?.name, + source, + importer, + ]) + return `\0virtual:test` + } + }, + }, + load: { + handler(id, _options) { + if (id === '\0virtual:test') { + console.log('[debug:load]', this.environment?.name) + return `export default "virtual-ok"` + } + }, + }, + }, ], }) From e453c2f0e71abbab9527ee05e1f1b8c444d2d166 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 18:09:34 +0900 Subject: [PATCH 147/156] chore: cleanup --- packages/vite/src/node/server/pluginContainer.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index a85d17509eb0d4..b9ec86179f4a74 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -141,10 +141,9 @@ export async function createEnvironmentPluginContainer( plugins, watcher, ) - if (environment.config.experimental.rolldownDev) { - return container + if (!environment.config.experimental.rolldownDev) { + await container.resolveRollupOptions() } - await container.resolveRollupOptions() return container } From 6f30df47d7bb9af94422bf864ba6fc92b78afdf3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 18:23:36 +0900 Subject: [PATCH 148/156] wip: integrate core plugin (vite:define) --- packages/vite/src/node/config.ts | 1 + .../src/node/server/environments/rolldown.ts | 16 ++++++++++------ playground/rolldown-dev-ssr/vite.config.ts | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index e3cf8da89841cb..5451009f739b14 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -1007,6 +1007,7 @@ export async function resolveConfig( process.env.NODE_ENV = defaultNodeEnv } + // TODO: should we make `command == "build"` on rolldown dev? const configEnv: ConfigEnv = { mode, command, diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index becd6664e0938b..11b7966f339a7b 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -39,9 +39,8 @@ export function rolldownDevHandleConfig(config: UserConfig): UserConfig { noDiscovery: true, include: [], }, - define: { - // TODO: use vite:define plugin - 'process.env.NODE_ENV': "'development'", + experimental: { + enableNativePlugin: true, }, environments: { client: { @@ -224,8 +223,14 @@ class RolldownEnvironment extends DevEnvironment { // all plugins are shared like Vite 6 `sharedConfigBuild`. let plugins = this._plugins! plugins = plugins.filter( - // TODO: reuse core plugins - (p) => !(p.name?.startsWith('vite:') || p.name === 'alias'), + (p) => + !( + typeof p.name === 'number' || + p.name?.startsWith('vite:') || + p.name === 'alias' + ) || + // enable some core plugins + p.name === 'vite:define', ) plugins = plugins.map((p) => injectEnvironmentToHooks(this as any, p)) @@ -240,7 +245,6 @@ class RolldownEnvironment extends DevEnvironment { mainFields: this.config.resolve.mainFields, symlinks: !this.config.resolve.preserveSymlinks, }, - define: this.config.define, plugins: [ viterollEntryPlugin(this.config, this.rolldownDevOptions), // TODO: how to use jsx-dev-runtime? diff --git a/playground/rolldown-dev-ssr/vite.config.ts b/playground/rolldown-dev-ssr/vite.config.ts index bbf5c614d1aff3..0505f6df344d65 100644 --- a/playground/rolldown-dev-ssr/vite.config.ts +++ b/playground/rolldown-dev-ssr/vite.config.ts @@ -25,6 +25,7 @@ export default defineConfig({ }, experimental: { rolldownDev: { hmr: true, reactRefresh: true }, + enableNativePlugin: true, }, plugins: [ { From 5d45256aa406173a50743d630d40c94407b97c3a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 18:33:13 +0900 Subject: [PATCH 149/156] chore: cleanup --- packages/vite/src/node/config.ts | 1 - packages/vite/src/node/server/environments/rolldown.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 5451009f739b14..e3cf8da89841cb 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -1007,7 +1007,6 @@ export async function resolveConfig( process.env.NODE_ENV = defaultNodeEnv } - // TODO: should we make `command == "build"` on rolldown dev? const configEnv: ConfigEnv = { mode, command, diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index 11b7966f339a7b..ed863e451486d3 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -222,6 +222,7 @@ class RolldownEnvironment extends DevEnvironment { // all plugins are shared like Vite 6 `sharedConfigBuild`. let plugins = this._plugins! + // TODO: adopt more core plugins (should we filter inside `resolvePlugins`?) plugins = plugins.filter( (p) => !( From 658f0dacd49ad4c05d25989039b7d0c577a22bca Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 18:49:25 +0900 Subject: [PATCH 150/156] wip: integrate core alias plugin --- .../src/node/server/environments/rolldown.ts | 16 +++++----------- playground/rolldown-dev-react/src/app.tsx | 5 ++++- .../rolldown-dev-react/src/test-alias-dest.tsx | 1 + playground/rolldown-dev-react/vite.config.ts | 8 +++++++- 4 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 playground/rolldown-dev-react/src/test-alias-dest.tsx diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index ed863e451486d3..889c1c01ad5a31 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -222,16 +222,13 @@ class RolldownEnvironment extends DevEnvironment { // all plugins are shared like Vite 6 `sharedConfigBuild`. let plugins = this._plugins! - // TODO: adopt more core plugins (should we filter inside `resolvePlugins`?) + // enable some core plugins + // TODO: adopt more (should we filter inside `resolvePlugins`?) plugins = plugins.filter( (p) => - !( - typeof p.name === 'number' || - p.name?.startsWith('vite:') || - p.name === 'alias' - ) || - // enable some core plugins - p.name === 'vite:define', + !(typeof p.name === 'number' || p.name?.startsWith('vite:')) || + ['vite:define'].includes(p.name) || + ['AliasPlugin'].includes(p.constructor.name), ) plugins = plugins.map((p) => injectEnvironmentToHooks(this as any, p)) @@ -253,9 +250,6 @@ class RolldownEnvironment extends DevEnvironment { reactRefresh: this.rolldownDevOptions?.reactRefresh, }), reactRefreshPlugin(this.rolldownDevOptions), - rolldownExperimental.aliasPlugin({ - entries: this.config.resolve.alias, - }), ...plugins, ], } diff --git a/playground/rolldown-dev-react/src/app.tsx b/playground/rolldown-dev-react/src/app.tsx index 8420e99f0e1448..13fb7edcd6aaae 100644 --- a/playground/rolldown-dev-react/src/app.tsx +++ b/playground/rolldown-dev-react/src/app.tsx @@ -1,6 +1,8 @@ import { useState } from 'react' // @ts-expect-error no type import virtualTest from 'virtual:test' +// @ts-expect-error no type +import testAlias from 'test-alias' export function App() { const [count, setCount] = useState(0) @@ -12,7 +14,8 @@ export function App() { -
[virtual:test] {virtualTest}
+
[virtual] {virtualTest}
+
[alias] {testAlias}
) diff --git a/playground/rolldown-dev-react/src/test-alias-dest.tsx b/playground/rolldown-dev-react/src/test-alias-dest.tsx new file mode 100644 index 00000000000000..d11d8b5b18649a --- /dev/null +++ b/playground/rolldown-dev-react/src/test-alias-dest.tsx @@ -0,0 +1 @@ +export default 'test-alias-dest:ok' diff --git a/playground/rolldown-dev-react/vite.config.ts b/playground/rolldown-dev-react/vite.config.ts index 2e1177de4a86ed..40b8fa4d07859d 100644 --- a/playground/rolldown-dev-react/vite.config.ts +++ b/playground/rolldown-dev-react/vite.config.ts @@ -1,3 +1,4 @@ +import { join } from 'node:path' import { defineConfig } from 'vite' export default defineConfig({ @@ -8,6 +9,11 @@ export default defineConfig({ reactRefresh: true, }, }, + resolve: { + alias: { + 'test-alias': join(import.meta.dirname, './src/test-alias-dest.tsx'), + }, + }, plugins: [ { name: 'test', @@ -36,7 +42,7 @@ export default defineConfig({ handler(id, _options) { if (id === '\0virtual:test') { console.log('[debug:load]', this.environment?.name) - return `export default "virtual-ok, environment.name: ${this.environment.name}"` + return `export default "test:virtual:ok, environment.name: ${this.environment.name}"` } }, }, From 01dba7dc565ae011f7e319bbab396d83ce4e08ad Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 19:04:26 +0900 Subject: [PATCH 151/156] wip: integrate core transform plugin --- packages/vite/src/node/plugins/index.ts | 6 +++++- packages/vite/src/node/server/environments/rolldown.ts | 9 ++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 7d3f7ef014ee83..2e741c6f764484 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -57,6 +57,7 @@ export async function resolvePlugins( (environment) => !isDepOptimizationDisabled(environment.optimizeDeps), ) const enableNativePlugin = config.experimental.enableNativePlugin + const rolldownDev = config.experimental.rolldownDev return [ depOptimizationEnabled ? optimizedDepsPlugin() : null, @@ -125,7 +126,10 @@ export async function resolvePlugins( cssPlugin(config), config.oxc !== false ? enableNativePlugin - ? nativeTransformPlugin() + ? nativeTransformPlugin({ + // TODO: how to jsx dev? + reactRefresh: rolldownDev?.reactRefresh, + }) : oxcPlugin(config) : null, enableNativePlugin diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index 889c1c01ad5a31..cb25fcc40d5e35 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -6,7 +6,6 @@ import path from 'node:path' import { pathToFileURL } from 'node:url' import MagicString from 'magic-string' import * as rolldown from 'rolldown' -import * as rolldownExperimental from 'rolldown/experimental' import sirv from 'sirv' import { createLogger } from '../../publicUtils' import { DevEnvironment } from '../environment' @@ -228,7 +227,7 @@ class RolldownEnvironment extends DevEnvironment { (p) => !(typeof p.name === 'number' || p.name?.startsWith('vite:')) || ['vite:define'].includes(p.name) || - ['AliasPlugin'].includes(p.constructor.name), + ['AliasPlugin', 'TransformPlugin'].includes(p.constructor.name), ) plugins = plugins.map((p) => injectEnvironmentToHooks(this as any, p)) @@ -244,13 +243,9 @@ class RolldownEnvironment extends DevEnvironment { symlinks: !this.config.resolve.preserveSymlinks, }, plugins: [ + ...plugins, viterollEntryPlugin(this.config, this.rolldownDevOptions), - // TODO: how to use jsx-dev-runtime? - rolldownExperimental.transformPlugin({ - reactRefresh: this.rolldownDevOptions?.reactRefresh, - }), reactRefreshPlugin(this.rolldownDevOptions), - ...plugins, ], } this.instance = await rolldown.rolldown(inputOptions) From 75bca1771016fde7015d0f984a0efc0b1fd340bd Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 19:11:23 +0900 Subject: [PATCH 152/156] chore: cleanup --- playground/rolldown-dev-react/src/main.tsx | 2 +- playground/rolldown-dev-react/tsconfig.json | 15 +++------------ playground/rolldown-dev-ssr/tsconfig.json | 15 +++------------ 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/playground/rolldown-dev-react/src/main.tsx b/playground/rolldown-dev-react/src/main.tsx index ade9e415f8f7c7..954d0f3158a328 100644 --- a/playground/rolldown-dev-react/src/main.tsx +++ b/playground/rolldown-dev-react/src/main.tsx @@ -1,6 +1,6 @@ import React from 'react' import ReactDOMClient from 'react-dom/client' -import { App } from './app.tsx' +import { App } from './app' ReactDOMClient.createRoot(document.getElementById('root')!).render( diff --git a/playground/rolldown-dev-react/tsconfig.json b/playground/rolldown-dev-react/tsconfig.json index e12edac8af75da..bde6a8a19bbb41 100644 --- a/playground/rolldown-dev-react/tsconfig.json +++ b/playground/rolldown-dev-react/tsconfig.json @@ -1,16 +1,7 @@ { - "include": ["src", "*.ts"], + "extends": "../tsconfig.json", + "include": ["src", ".ts"], "compilerOptions": { - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "verbatimModuleSyntax": true, - "allowImportingTsExtensions": true, - "moduleResolution": "Bundler", - "module": "ESNext", - "target": "ESNext", - "lib": ["ESNext", "DOM"], - "jsx": "react-jsx", - "noEmit": true + "jsx": "react-jsx" } } diff --git a/playground/rolldown-dev-ssr/tsconfig.json b/playground/rolldown-dev-ssr/tsconfig.json index e12edac8af75da..bde6a8a19bbb41 100644 --- a/playground/rolldown-dev-ssr/tsconfig.json +++ b/playground/rolldown-dev-ssr/tsconfig.json @@ -1,16 +1,7 @@ { - "include": ["src", "*.ts"], + "extends": "../tsconfig.json", + "include": ["src", ".ts"], "compilerOptions": { - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "verbatimModuleSyntax": true, - "allowImportingTsExtensions": true, - "moduleResolution": "Bundler", - "module": "ESNext", - "target": "ESNext", - "lib": ["ESNext", "DOM"], - "jsx": "react-jsx", - "noEmit": true + "jsx": "react-jsx" } } From dfd18c59511b0deee2335b292b93925df07bdf3f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 19:12:40 +0900 Subject: [PATCH 153/156] fix: use createBuiltinPluginWithEnvironmentSupport for transformPlugin --- packages/vite/src/node/plugins/index.ts | 18 ++++++++++++------ .../src/node/server/environments/rolldown.ts | 3 +-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 2e741c6f764484..0975d52cfe07df 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -125,12 +125,18 @@ export async function resolvePlugins( htmlInlineProxyPlugin(config), cssPlugin(config), config.oxc !== false - ? enableNativePlugin - ? nativeTransformPlugin({ - // TODO: how to jsx dev? - reactRefresh: rolldownDev?.reactRefresh, - }) - : oxcPlugin(config) + ? rolldownDev + ? createBuiltinPluginWithEnvironmentSupport( + 'native:transform', + (environment) => + nativeTransformPlugin({ + reactRefresh: + environment.name === 'client' && rolldownDev?.reactRefresh, + }), + ) + : enableNativePlugin + ? nativeTransformPlugin() + : oxcPlugin(config) : null, enableNativePlugin ? nativeJsonPlugin({ diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index cb25fcc40d5e35..1846fdc5e5a90b 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -221,8 +221,7 @@ class RolldownEnvironment extends DevEnvironment { // all plugins are shared like Vite 6 `sharedConfigBuild`. let plugins = this._plugins! - // enable some core plugins - // TODO: adopt more (should we filter inside `resolvePlugins`?) + // TODO: enable more core plugins plugins = plugins.filter( (p) => !(typeof p.name === 'number' || p.name?.startsWith('vite:')) || From ff382371ddafa7be9de868c422849db568514d13 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 19:26:15 +0900 Subject: [PATCH 154/156] chore: lockfile --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5adbd12402fa2a..f0d72f93f16a91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1343,7 +1343,7 @@ importers: devDependencies: '@types/react': specifier: ^18.3.11 - version: 18.3.11 + version: 18.3.12 '@types/react-dom': specifier: ^18.3.1 version: 18.3.1 From d31313cb5721637ad9bec8c6d7a71663faa11dc1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 19:28:54 +0900 Subject: [PATCH 155/156] chore: type error --- packages/vite/src/node/build.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 5f103e3e032dbf..ed2e5e5285e4b3 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -1348,6 +1348,7 @@ const relativeUrlMechanisms: Record< `(typeof document === 'undefined' && typeof location === 'undefined' ? ${getFileUrlFromRelativePath( relativePath, )} : ${getRelativeUrlFromDocument(relativePath, true)})`, + app: () => 'todo', } /* end of copy */ From 6a2ea61874fa70b0d1eb383afc507888d37cb8f8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 11 Nov 2024 19:30:41 +0900 Subject: [PATCH 156/156] chore: unused --- .../src/node/server/environments/rolldown.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/vite/src/node/server/environments/rolldown.ts b/packages/vite/src/node/server/environments/rolldown.ts index 1846fdc5e5a90b..bc459a555e278b 100644 --- a/packages/vite/src/node/server/environments/rolldown.ts +++ b/packages/vite/src/node/server/environments/rolldown.ts @@ -112,23 +112,6 @@ export function rolldownDevConfigureServer(server: ViteDevServer): void { await environments.client.build() server.ws.send({ type: 'full-reload' }) }) - - // disable automatic html reload - // https://github.com/vitejs/vite/blob/01cf7e14ca63988c05627907e72b57002ffcb8d5/packages/vite/src/node/server/hmr.ts#L590-L595 - const oldSend = server.ws.send - server.ws.send = function (...args: any) { - const arg = args[0] - if ( - arg && - typeof arg === 'object' && - arg.type === 'full-reload' && - typeof arg.path === 'string' && - arg.path.endsWith('.html') - ) { - return - } - oldSend.apply(this, args) - } } export async function rolldownDevHandleHotUpdate(