diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml new file mode 100644 index 00000000000000..113ede518e8962 --- /dev/null +++ b/.github/workflows/preview-release.yml @@ -0,0 +1,44 @@ +name: Preview release + +env: + # install playwright binary manually (because pnpm only runs install script once) + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + +permissions: + pull-requests: write + +on: + push: + branches: + - main + pull_request: + types: [labeled] + +jobs: + preview: + if: > + github.repository == 'vitejs/vite' && + (github.event_name == 'push' || + (github.event.issue.pull_request && contains(github.event.pull_request.labels.*.name, 'trigger: preview'))) + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4.0.0 + + - name: Install dependencies + run: pnpm install + + - name: Build + working-directory: ./packages/vite + run: pnpm build + + - run: pnpm dlx pkg-pr-new@0.0 publish --compact --pnpm ./packages/vite + + - if: github.event_name != 'push' + run: | + gh issue edit ${{ github.event.issue.number }} --remove-label "trigger: preview" --repo ${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-commit.yml b/.github/workflows/publish-commit.yml deleted file mode 100644 index c409979c033157..00000000000000 --- a/.github/workflows/publish-commit.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Publish Any Commit - -env: - # install playwright binary manually (because pnpm only runs install script once) - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" - -on: - push: - branches: - - main - issue_comment: - types: [created] - -jobs: - build: - if: github.repository == 'vitejs/vite' && (github.event_name == 'push' || github.event.issue.pull_request && startsWith(github.event.comment.body, '/pkg-pr-new')) - runs-on: ubuntu-latest - - steps: - - if: github.event.issue.pull_request - uses: actions/github-script@v7 - with: - script: | - const user = context.payload.sender.login - console.log(`Validate user: ${user}`) - - let hasTriagePermission = false - try { - const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: context.repo.owner, - repo: context.repo.repo, - username: user, - }); - hasTriagePermission = data.user.permissions.triage - } catch (e) { - console.warn(e) - } - - if (hasTriagePermission) { - console.log('Allowed') - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: '+1', - }) - } else { - console.log('Not allowed') - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: '-1', - }) - throw new Error('not allowed') - } - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install pnpm - uses: pnpm/action-setup@v4.0.0 - - - name: Install dependencies - run: pnpm install - - - name: Build - working-directory: ./packages/vite - run: pnpm build - - - run: pnpm dlx pkg-pr-new@0.0 publish --compact --pnpm ./packages/vite diff --git a/.npmrc b/.npmrc index ff6f1665b13102..3c758b7ba41b57 100644 --- a/.npmrc +++ b/.npmrc @@ -1,4 +1,3 @@ -hoist-pattern[]=ts-node # package/vite: postcss-load-config hoist-pattern[]=postcss # package/vite hoist-pattern[]=pug # playground/tailwind: @vue/compiler-sfc shell-emulator=true diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 8c706a92aa725a..f9e4f632c45113 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -456,19 +456,13 @@ export default defineConfig({ }, }), ], - environments: { - client: { - dev: { - optimizeDeps: { - include: [ - '@shikijs/vitepress-twoslash/client', - 'gsap', - 'gsap/dist/ScrollTrigger', - 'gsap/dist/MotionPathPlugin', - ], - }, - }, - }, + optimizeDeps: { + include: [ + '@shikijs/vitepress-twoslash/client', + 'gsap', + 'gsap/dist/ScrollTrigger', + 'gsap/dist/MotionPathPlugin', + ], }, }, buildEnd, diff --git a/docs/guide/api-environment.md b/docs/guide/api-environment.md index d6d33b2e39352a..b2589b9e39121e 100644 --- a/docs/guide/api-environment.md +++ b/docs/guide/api-environment.md @@ -38,9 +38,7 @@ export default { }, }, ssr: { - dev: { - optimizeDeps: {}, // configure the SSR environment - }, + optimizeDeps: {}, // configure the SSR environment }, rsc: { resolve: { @@ -61,7 +59,7 @@ export default { } ``` -The `EnvironmentOptions` interface exposes all the per-environment options. There are `SharedEnvironmentOptions` that apply to both `build` and `dev`, like `resolve`. And there are `DevEnvironmentOptions` and `BuildEnvironmentOptions` for dev and build specific options (like `dev.optimizeDeps` or `build.outDir`). +The `EnvironmentOptions` interface exposes all the per-environment options. There are `SharedEnvironmentOptions` that apply to both `build` and `dev`, like `resolve`. And there are `DevEnvironmentOptions` and `BuildEnvironmentOptions` for dev and build specific options (like `optimizeDeps` or `build.outDir`). ```ts interface EnvironmentOptions extends SharedEnvironmentOptions { 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/docs/guide/migration.md b/docs/guide/migration.md index aab7b16c390d31..a48b9faccb8db4 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -20,6 +20,10 @@ 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`. +### postcss-load-config + +[`postcss-load-config`](https://npmjs.com/package/postcss-load-config) has been updated to v6 from v4. [`tsx`](https://www.npmjs.com/package/tsx) or [`jiti`](https://www.npmjs.com/package/jiti) is now required to load TypeScript postcss config files instead of [`ts-node`](https://www.npmjs.com/package/ts-node). Also [`yaml`](https://www.npmjs.com/package/yaml) is now required to load YAML postcss config files. + ### 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. @@ -36,6 +40,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. diff --git a/eslint.config.js b/eslint.config.js index 0c936aa7986ff1..968a7a89b5e1a4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -85,7 +85,15 @@ export default tseslint.config( 'n/no-extraneous-import': [ 'error', { - allowModules: ['vite', 'less', 'sass', 'vitest', 'unbuild'], + allowModules: [ + 'vite', + 'less', + 'sass', + 'sass-embedded', + 'lightningcss', + 'vitest', + 'unbuild', + ], }, ], 'n/no-extraneous-require': [ @@ -196,7 +204,6 @@ export default tseslint.config( 'playground/**/*dep*/**', 'playground/resolve/browser-module-field2/index.web.js', 'playground/resolve/browser-field/**', - 'playground/tailwind/**', // blocked by https://github.com/postcss/postcss-load-config/issues/239 ], rules: { 'import-x/no-commonjs': 'error', 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/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": [ diff --git a/packages/vite/LICENSE.md b/packages/vite/LICENSE.md index cccba70e5d37d9..247fa945327298 100644 --- a/packages/vite/LICENSE.md +++ b/packages/vite/LICENSE.md @@ -2518,24 +2518,3 @@ Repository: git+https://github.com/websockets/ws.git > 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. - ---------------------------------------- - -## yaml -License: ISC -By: Eemeli Aro -Repository: github:eemeli/yaml - -> Copyright Eemeli Aro -> -> Permission to use, copy, modify, and/or distribute this software for any purpose -> with or without fee is hereby granted, provided that the above copyright notice -> and this permission notice appear in all copies. -> -> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -> FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -> OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -> TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -> THIS SOFTWARE. diff --git a/packages/vite/package.json b/packages/vite/package.json index 0b4d6d8958ee38..0e399e473380a6 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": { @@ -135,7 +136,7 @@ "picocolors": "^1.1.1", "picomatch": "^4.0.2", "postcss-import": "^16.1.0", - "postcss-load-config": "^4.0.2", + "postcss-load-config": "^6.0.1", "postcss-modules": "^6.0.0", "resolve.exports": "^2.0.2", "rollup-plugin-dts": "^6.1.1", @@ -147,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", @@ -156,18 +157,24 @@ }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.16.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "sass": { "optional": true }, @@ -188,6 +195,12 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } } diff --git a/packages/vite/rollup.config.ts b/packages/vite/rollup.config.ts index fd5c406e70644f..e72752a82194f5 100644 --- a/packages/vite/rollup.config.ts +++ b/packages/vite/rollup.config.ts @@ -99,6 +99,10 @@ const nodeConfig = defineConfig({ 'fsevents', 'lightningcss', 'rollup/parseAst', + // postcss-load-config + 'yaml', + 'jiti', + /^tsx(\/|$)/, ...Object.keys(pkg.dependencies), ], plugins: [ @@ -107,35 +111,50 @@ const nodeConfig = defineConfig({ // Shim them with eval() so rollup can skip these calls. shimDepsPlugin({ // chokidar -> fsevents - 'fsevents-handler.js': { - src: `require('fsevents')`, - replacement: `__require('fsevents')`, - }, + 'fsevents-handler.js': [ + { + src: `require('fsevents')`, + replacement: `__require('fsevents')`, + }, + ], // postcss-import -> sugarss - 'process-content.js': { - src: 'require("sugarss")', - replacement: `__require('sugarss')`, - }, - 'lilconfig/src/index.js': { - pattern: /: require;/g, - replacement: `: __require;`, - }, - // postcss-load-config calls require after register ts-node - 'postcss-load-config/src/index.js': { - pattern: /require(?=\((configFile|'ts-node')\))/g, - replacement: `__require`, - }, + 'process-content.js': [ + { + src: 'require("sugarss")', + replacement: `__require('sugarss')`, + }, + ], + 'lilconfig/src/index.js': [ + { + pattern: /: require;/g, + replacement: ': __require;', + }, + ], + 'postcss-load-config/src/req.js': [ + { + src: "const { pathToFileURL } = require('node:url')", + replacement: `const { fileURLToPath, pathToFileURL } = require('node:url')`, + }, + { + src: '__filename', + replacement: 'fileURLToPath(import.meta.url)', + }, + ], // postcss-import uses the `resolve` dep if the `resolve` option is not passed. // However, we always pass the `resolve` option. Remove this import to avoid // bundling the `resolve` dep. - 'postcss-import/index.js': { - src: 'const resolveId = require("./lib/resolve-id")', - replacement: 'const resolveId = (id) => id', - }, - 'postcss-import/lib/parse-styles.js': { - src: 'const resolveId = require("./resolve-id")', - replacement: 'const resolveId = (id) => id', - }, + 'postcss-import/index.js': [ + { + src: 'const resolveId = require("./lib/resolve-id")', + replacement: 'const resolveId = (id) => id', + }, + ], + 'postcss-import/lib/parse-styles.js': [ + { + src: 'const resolveId = require("./resolve-id")', + replacement: 'const resolveId = (id) => id', + }, + ], }), ...createSharedNodePlugins({}), licensePlugin( @@ -199,7 +218,7 @@ interface ShimOptions { pattern?: RegExp } -function shimDepsPlugin(deps: Record): Plugin { +function shimDepsPlugin(deps: Record): Plugin { const transformed: Record = {} return { @@ -207,38 +226,45 @@ function shimDepsPlugin(deps: Record): Plugin { transform(code, id) { for (const file in deps) { if (id.replace(/\\/g, '/').endsWith(file)) { - const { src, replacement, pattern } = deps[file] + for (const { src, replacement, pattern } of deps[file]) { + const magicString = new MagicString(code) - const magicString = new MagicString(code) - if (src) { - const pos = code.indexOf(src) - if (pos < 0) { - this.error( - `Could not find expected src "${src}" in file "${file}"`, - ) - } - transformed[file] = true - magicString.overwrite(pos, pos + src.length, replacement) - console.log(`shimmed: ${file}`) - } - - if (pattern) { - let match - while ((match = pattern.exec(code))) { + if (src) { + const pos = code.indexOf(src) + if (pos < 0) { + this.error( + `Could not find expected src "${src}" in file "${file}"`, + ) + } transformed[file] = true - const start = match.index - const end = start + match[0].length - magicString.overwrite(start, end, replacement) + magicString.overwrite(pos, pos + src.length, replacement) } - if (!transformed[file]) { - this.error( - `Could not find expected pattern "${pattern}" in file "${file}"`, - ) + + if (pattern) { + let match + while ((match = pattern.exec(code))) { + transformed[file] = true + const start = match.index + const end = start + match[0].length + let _replacement = replacement + for (let i = 1; i <= match.length; i++) { + _replacement = _replacement.replace(`$${i}`, match[i] || '') + } + magicString.overwrite(start, end, _replacement) + } + if (!transformed[file]) { + this.error( + `Could not find expected pattern "${pattern}" in file "${file}"`, + ) + } } - console.log(`shimmed: ${file}`) + + code = magicString.toString() } - return magicString.toString() + console.log(`shimmed: ${file}`) + + return code } } }, diff --git a/packages/vite/src/node/baseEnvironment.ts b/packages/vite/src/node/baseEnvironment.ts index 730eb26e9e6d7b..b30067cb400685 100644 --- a/packages/vite/src/node/baseEnvironment.ts +++ b/packages/vite/src/node/baseEnvironment.ts @@ -18,6 +18,7 @@ export function getDefaultResolvedEnvironmentOptions( resolve: config.resolve, consumer: 'server', webCompatible: false, + optimizeDeps: config.optimizeDeps, dev: config.dev, build: config.build, } diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 146f0110678f19..db540cbb5af2fa 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -170,11 +170,6 @@ export interface DevEnvironmentOptions { | false | ((sourcePath: string, sourcemapPath: string) => boolean) - /** - * Optimize deps config - */ - optimizeDeps?: DepOptimizationOptions - /** * create the Dev Environment instance */ @@ -251,6 +246,10 @@ export interface SharedEnvironmentOptions { * Temporal options, we should remove these in favor of fine-grained control */ webCompatible?: boolean // was ssr.target === 'webworker' + /** + * Optimize deps config + */ + optimizeDeps?: DepOptimizationOptions } export interface EnvironmentOptions extends SharedEnvironmentOptions { @@ -271,6 +270,7 @@ export type ResolvedEnvironmentOptions = { resolve: ResolvedResolveOptions consumer: 'client' | 'server' webCompatible: boolean + optimizeDeps: DepOptimizationOptions dev: ResolvedDevEnvironmentOptions build: ResolvedBuildEnvironmentOptions } @@ -589,7 +589,6 @@ export type ResolvedConfig = Readonly< export function resolveDevEnvironmentOptions( dev: DevEnvironmentOptions | undefined, - preserverSymlinks: boolean, environmentName: string | undefined, consumer: 'client' | 'server' | undefined, // Backward compatibility @@ -603,11 +602,6 @@ export function resolveDevEnvironmentOptions( : dev?.sourcemapIgnoreList || isInNodeModules, preTransformRequests: dev?.preTransformRequests ?? consumer === 'client', warmup: dev?.warmup ?? [], - optimizeDeps: resolveDepOptimizationOptions( - dev?.optimizeDeps, - preserverSymlinks, - consumer, - ), createEnvironment: dev?.createEnvironment ?? (environmentName === 'client' @@ -646,9 +640,13 @@ function resolveEnvironmentOptions( resolve, consumer, webCompatible: options.webCompatible ?? consumer === 'client', + optimizeDeps: resolveDepOptimizationOptions( + options.optimizeDeps, + resolve.preserveSymlinks, + consumer, + ), dev: resolveDevEnvironmentOptions( options.dev, - resolve.preserveSymlinks, environmentName, consumer, skipSsrTransform, @@ -911,13 +909,8 @@ export async function resolveConfig( checkBadCharactersInPath(resolvedRoot, logger) - // Backward compatibility: merge optimizeDeps into environments.client.dev.optimizeDeps as defaults const configEnvironmentsClient = config.environments!.client! configEnvironmentsClient.dev ??= {} - configEnvironmentsClient.dev.optimizeDeps = mergeConfig( - config.optimizeDeps ?? {}, - configEnvironmentsClient.dev.optimizeDeps ?? {}, - ) const deprecatedSsrOptimizeDepsConfig = config.ssr?.optimizeDeps ?? {} let configEnvironmentsSsr = config.environments!.ssr @@ -935,10 +928,9 @@ export async function resolveConfig( // Backward compatibility: merge ssr into environments.ssr.config as defaults if (configEnvironmentsSsr) { - configEnvironmentsSsr.dev ??= {} - configEnvironmentsSsr.dev.optimizeDeps = mergeConfig( + configEnvironmentsSsr.optimizeDeps = mergeConfig( deprecatedSsrOptimizeDepsConfig, - configEnvironmentsSsr.dev.optimizeDeps ?? {}, + configEnvironmentsSsr.optimizeDeps ?? {}, ) configEnvironmentsSsr.resolve ??= {} @@ -972,9 +964,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 = { + ...defaultEnvironmentOptions, + optimizeDeps: config.optimizeDeps, + } for (const name of Object.keys(config.environments)) { config.environments[name] = mergeConfig( - defaultEnvironmentOptions, + name === 'client' + ? defaultClientEnvironmentOptions + : defaultEnvironmentOptions, config.environments[name], ) } @@ -995,16 +994,15 @@ export async function resolveConfig( ) } - // Backward compatibility: merge environments.client.dev.optimizeDeps back into optimizeDeps + // Backward compatibility: merge environments.client.optimizeDeps back into optimizeDeps // The same object is assigned back for backward compatibility. The ecosystem is modifying // optimizeDeps in the ResolvedConfig hook, so these changes will be reflected on the // client environment. const backwardCompatibleOptimizeDeps = - resolvedEnvironments.client.dev.optimizeDeps + resolvedEnvironments.client.optimizeDeps const resolvedDevEnvironmentOptions = resolveDevEnvironmentOptions( config.dev, - resolvedDefaultResolve.preserveSymlinks, // default environment options undefined, undefined, @@ -1022,7 +1020,7 @@ export async function resolveConfig( ...config.ssr, external: resolvedEnvironments.ssr?.resolve.external, noExternal: resolvedEnvironments.ssr?.resolve.noExternal, - optimizeDeps: resolvedEnvironments.ssr?.dev?.optimizeDeps, + optimizeDeps: resolvedEnvironments.ssr?.optimizeDeps, resolve: { ...config.ssr?.resolve, conditions: resolvedEnvironments.ssr?.resolve.conditions, @@ -1230,10 +1228,9 @@ export async function resolveConfig( }, future: config.future, - // Backward compatibility, users should use environment.config.dev.optimizeDeps - optimizeDeps: backwardCompatibleOptimizeDeps, ssr, + optimizeDeps: backwardCompatibleOptimizeDeps, resolve: resolvedDefaultResolve, dev: resolvedDevEnvironmentOptions, build: resolvedBuildOptions, @@ -1740,7 +1737,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) } } diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 5aaff7552740ba..481ea0952b579d 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' @@ -216,7 +219,7 @@ export type { Terser } from 'dep-types/terser' export type { RollupCommonJSOptions } from 'dep-types/commonjs' export type { RollupDynamicImportVarsOptions } from 'dep-types/dynamicImportVars' export type { Matcher, AnymatchPattern, AnymatchFn } from 'dep-types/anymatch' -export type { LightningCSSOptions } from 'dep-types/lightningcss' +export type { LightningCSSOptions } from 'types/internal/lightningcssOptions' // Backward compatibility export type { ModuleGraph, ModuleNode } from './server/mixedModuleGraph' diff --git a/packages/vite/src/node/logger.ts b/packages/vite/src/node/logger.ts index a9491c477f054a..8bfa027c61fcd2 100644 --- a/packages/vite/src/node/logger.ts +++ b/packages/vite/src/node/logger.ts @@ -50,6 +50,7 @@ export interface LoggerOptions { prefix?: string allowClearScreen?: boolean customLogger?: Logger + console?: Console } // Only initialize the timeFormatter when the timestamp option is used, and @@ -73,7 +74,11 @@ export function createLogger( } const loggedErrors = new WeakSet() - const { prefix = '[vite]', allowClearScreen = true } = options + const { + prefix = '[vite]', + allowClearScreen = true, + console = globalThis.console, + } = options const thresh = LogLevels[level] const canClearScreen = allowClearScreen && process.stdout.isTTY && !process.env.CI diff --git a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts index 088cb35d770bab..c22a52b57ce6a1 100644 --- a/packages/vite/src/node/optimizer/esbuildDepPlugin.ts +++ b/packages/vite/src/node/optimizer/esbuildDepPlugin.ts @@ -53,7 +53,7 @@ export function esbuildDepPlugin( external: string[], ): Plugin { const { isProduction } = environment.config - const { extensions } = environment.config.dev.optimizeDeps + const { extensions } = environment.config.optimizeDeps // remove optimizable extensions from `externalTypes` list const allExternalTypes = extensions diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index 3de6a04367b3ab..f70a71f6ed8125 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -288,7 +288,7 @@ export async function optimizeExplicitEnvironmentDeps( ): Promise { const cachedMetadata = await loadCachedDepOptimizationMetadata( environment, - environment.config.dev.optimizeDeps.force ?? false, + environment.config.optimizeDeps.force ?? false, false, ) if (cachedMetadata) { @@ -729,7 +729,7 @@ async function prepareEsbuildOptimizerRun( const flatIdDeps: Record = {} const idToExports: Record = {} - const { optimizeDeps } = environment.config.dev + const { optimizeDeps } = environment.config const { plugins: pluginsFromConfig = [], ...esbuildOptions } = optimizeDeps?.esbuildOptions ?? {} @@ -812,7 +812,7 @@ export async function addManuallyIncludedOptimizeDeps( deps: Record, ): Promise { const { logger } = environment - const { optimizeDeps } = environment.config.dev + const { optimizeDeps } = environment.config const optimizeDepsInclude = optimizeDeps?.include ?? [] if (optimizeDepsInclude.length) { const unableToOptimize = (id: string, msg: string) => { @@ -1059,7 +1059,7 @@ export async function extractExportsData( ): Promise { await init - const { optimizeDeps } = environment.config.dev + const { optimizeDeps } = environment.config const esbuildOptions = optimizeDeps?.esbuildOptions ?? {} if (optimizeDeps.extensions?.some((ext) => filePath.endsWith(ext))) { @@ -1112,7 +1112,7 @@ function needsInterop( exportsData: ExportsData, output?: { exports: string[] }, ): boolean { - if (environment.config.dev.optimizeDeps?.needsInterop?.includes(id)) { + if (environment.config.optimizeDeps?.needsInterop?.includes(id)) { return true } const { hasModuleSyntax, exports } = exportsData @@ -1156,7 +1156,7 @@ function getConfigHash(environment: Environment): string { // Take config into account // only a subset of config options that can affect dep optimization const { config } = environment - const { optimizeDeps } = config.dev + const { optimizeDeps } = config const content = JSON.stringify( { mode: process.env.NODE_ENV || config.mode, diff --git a/packages/vite/src/node/optimizer/optimizer.ts b/packages/vite/src/node/optimizer/optimizer.ts index 585892d7adeb11..54fffc4ca9e387 100644 --- a/packages/vite/src/node/optimizer/optimizer.ts +++ b/packages/vite/src/node/optimizer/optimizer.ts @@ -44,7 +44,7 @@ export function createDepsOptimizer( let closed = false - const options = environment.config.dev.optimizeDeps + const options = environment.config.optimizeDeps const { noDiscovery, holdUntilCrawlEnd } = options @@ -748,7 +748,7 @@ export function createExplicitDepsOptimizer( run: () => {}, close: async () => {}, - options: environment.config.dev.optimizeDeps, + options: environment.config.optimizeDeps, } let inited = false diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index c8a3020d2b34cd..771222f8e5b9f7 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -147,7 +147,7 @@ export function scanImports(environment: ScanEnvironment): { entries = computedEntries if (!entries.length) { - if (!config.optimizeDeps.entries && !config.dev.optimizeDeps.include) { + if (!config.optimizeDeps.entries && !config.optimizeDeps.include) { environment.logger.warn( colors.yellow( '(!) Could not auto-determine entry point from rollupOptions or html files ' + @@ -247,7 +247,7 @@ export function scanImports(environment: ScanEnvironment): { async function computeEntries(environment: ScanEnvironment) { let entries: string[] = [] - const explicitEntryPatterns = environment.config.dev.optimizeDeps.entries + const explicitEntryPatterns = environment.config.optimizeDeps.entries const buildInput = environment.config.build.rollupOptions?.input if (explicitEntryPatterns) { @@ -283,7 +283,7 @@ async function computeEntries(environment: ScanEnvironment) { // dependencies. entries = entries.filter( (entry) => - isScannable(entry, environment.config.dev.optimizeDeps.extensions) && + isScannable(entry, environment.config.optimizeDeps.extensions) && fs.existsSync(entry), ) @@ -302,7 +302,7 @@ async function prepareEsbuildScanner( const plugin = esbuildScanPlugin(environment, deps, missing, entries) const { plugins = [], ...esbuildOptions } = - environment.config.dev.optimizeDeps.esbuildOptions ?? {} + environment.config.optimizeDeps.esbuildOptions ?? {} // The plugin pipeline automatically loads the closest tsconfig.json. // But esbuild doesn't support reading tsconfig.json if the plugin has resolved the path (https://github.com/evanw/esbuild/issues/2265). @@ -356,7 +356,7 @@ function globEntries(pattern: string | string[], environment: ScanEnvironment) { '**/node_modules/**', `**/${environment.config.build.outDir}/**`, // if there aren't explicit entries, also ignore other common folders - ...(environment.config.dev.optimizeDeps.entries + ...(environment.config.optimizeDeps.entries ? [] : [`**/__tests__/**`, `**/coverage/**`]), ], @@ -409,7 +409,7 @@ function esbuildScanPlugin( return res } - const optimizeDepsOptions = environment.config.dev.optimizeDeps + const optimizeDepsOptions = environment.config.optimizeDeps const include = optimizeDepsOptions.include const exclude = [ ...(optimizeDepsOptions.exclude ?? []), diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 1acab05082d114..ef59cf26a3f59c 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -21,7 +21,7 @@ import type Sass from 'sass' import type Stylus from 'stylus' import type Less from 'less' import type { Alias } from 'dep-types/alias' -import type { LightningCSSOptions } from 'dep-types/lightningcss' +import type { LightningCSSOptions } from 'types/internal/lightningcssOptions' import type { TransformOptions } from 'esbuild' import { formatMessages, transform } from 'esbuild' import type { RawSourceMap } from '@ampproject/remapping' @@ -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 { @@ -181,9 +181,7 @@ export interface CSSModulesOptions { } export type ResolvedCSSOptions = Omit & { - lightningcss?: LightningCSSOptions & { - targets: LightningCSSOptions['targets'] - } + lightningcss?: LightningCSSOptions } export function resolveCSSOptions( @@ -762,7 +760,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') @@ -867,8 +865,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 +883,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 +898,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 @@ -1976,18 +1974,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/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 + }), + ) //