From d0fda583df94b6045fd34b6f3333b57d5f3bfa5e Mon Sep 17 00:00:00 2001 From: patak-dev Date: Mon, 17 Jan 2022 09:35:14 +0100 Subject: [PATCH 1/9] refactor: avoid splitting vendor chunk by default --- packages/playground/vue/vite.config.ts | 3 +- packages/vite/src/node/build.ts | 59 ------------ packages/vite/src/node/index.ts | 5 + .../vite/src/node/plugins/splitVendorChunk.ts | 95 +++++++++++++++++++ 4 files changed, 102 insertions(+), 60 deletions(-) create mode 100644 packages/vite/src/node/plugins/splitVendorChunk.ts diff --git a/packages/playground/vue/vite.config.ts b/packages/playground/vue/vite.config.ts index 82efdac5e9f876..a9ca552cbccda2 100644 --- a/packages/playground/vue/vite.config.ts +++ b/packages/playground/vue/vite.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vite' +import { defineConfig, splitVendorChunkPlugin } from 'vite' import vuePlugin from '@vitejs/plugin-vue' import { vueI18nPlugin } from './CustomBlockPlugin' @@ -12,6 +12,7 @@ export default defineConfig({ vuePlugin({ reactivityTransform: true }), + splitVendorChunkPlugin(), vueI18nPlugin ], build: { diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index 84ffb93b432f15..bb724f80415113 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -12,8 +12,6 @@ import type { OutputOptions, RollupOutput, ExternalOption, - GetManualChunk, - GetModuleInfo, WatcherOptions, RollupWatcher, RollupError, @@ -37,7 +35,6 @@ import { dataURIPlugin } from './plugins/dataUri' import { buildImportAnalysisPlugin } from './plugins/importAnalysisBuild' import { resolveSSRExternal, shouldExternalizeForSSR } from './ssr/ssrExternal' import { ssrManifestPlugin } from './ssr/ssrManifestPlugin' -import { isCSSRequest } from './plugins/css' import type { DepOptimizationMetadata } from './optimizer' import { scanImports } from './optimizer/scan' import { assetImportMetaUrlPlugin } from './plugins/assetImportMetaUrl' @@ -509,13 +506,6 @@ async function doBuild( // #1048 add `Symbol.toStringTag` for module default export namespaceToStringTag: true, inlineDynamicImports: ssr && typeof input === 'string', - manualChunks: - !ssr && - !libOptions && - output?.format !== 'umd' && - output?.format !== 'iife' - ? createMoveToVendorChunkFn(config) - : undefined, ...output } } @@ -642,55 +632,6 @@ function getPkgName(root: string) { return name?.startsWith('@') ? name.split('/')[1] : name } -function createMoveToVendorChunkFn(config: ResolvedConfig): GetManualChunk { - const cache = new Map() - return (id, { getModuleInfo }) => { - if ( - id.includes('node_modules') && - !isCSSRequest(id) && - staticImportedByEntry(id, getModuleInfo, cache) - ) { - return 'vendor' - } - } -} - -function staticImportedByEntry( - id: string, - getModuleInfo: GetModuleInfo, - cache: Map, - importStack: string[] = [] -): boolean { - if (cache.has(id)) { - return cache.get(id) as boolean - } - if (importStack.includes(id)) { - // circular deps! - cache.set(id, false) - return false - } - const mod = getModuleInfo(id) - if (!mod) { - cache.set(id, false) - return false - } - - if (mod.isEntry) { - cache.set(id, true) - return true - } - const someImporterIs = mod.importers.some((importer) => - staticImportedByEntry( - importer, - getModuleInfo, - cache, - importStack.concat(id) - ) - ) - cache.set(id, someImporterIs) - return someImporterIs -} - export function resolveLibFilename( libOptions: LibraryOptions, format: ModuleFormat, diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 158368872877ac..147c834d3ba87f 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -7,6 +7,10 @@ export { send } from './server/send' export { createLogger, printHttpServerUrls } from './logger' export { transformWithEsbuild } from './plugins/esbuild' export { resolvePackageEntry } from './plugins/resolve' +export { + splitVendorChunkPlugin, + splitVendorChunk +} from './plugins/splitVendorChunk' export { resolvePackageData } from './packages' export { normalizePath } from './utils' @@ -91,3 +95,4 @@ export type { Terser } from 'types/terser' export type { RollupCommonJSOptions } from 'types/commonjs' export type { RollupDynamicImportVarsOptions } from 'types/dynamicImportVars' export type { Matcher, AnymatchPattern, AnymatchFn } from 'types/anymatch' +export type { SplitVendorChunkCache } from './plugins/splitVendorChunk' diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts new file mode 100644 index 00000000000000..9e8958f4620372 --- /dev/null +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -0,0 +1,95 @@ +import type { Plugin } from '../plugin' +import type { OutputOptions, GetManualChunk, GetModuleInfo } from 'rollup' +import { isCSSRequest } from './css' + +// Use splitVendorChunkPlugin() to get the same manualChunks strategy as Vite 2.7 +// We don't recomment using this strategy as a general solution moving forward + +// splitVendorChunk is a simple index/vendor strategy that was used in Vite +// until v2.8. It is exposed to let people continue to use it in case it was +// working well for their setups. +// The cache needs to be reset on buildStart for watch mode to work correctly +// Don't use this manualChunks strategy for ssr, lib mode, and 'umd' or 'iife' + +export class SplitVendorChunkCache { + cache: Map + constructor() { + this.cache = new Map() + } + reset() { + this.cache = new Map() + } +} + +export function splitVendorChunk({ + cache = new SplitVendorChunkCache() +}): GetManualChunk { + return (id, { getModuleInfo }) => { + if ( + id.includes('node_modules') && + !isCSSRequest(id) && + staticImportedByEntry(id, getModuleInfo, cache.cache) + ) { + return 'vendor' + } + } +} + +function staticImportedByEntry( + id: string, + getModuleInfo: GetModuleInfo, + cache: Map, + importStack: string[] = [] +): boolean { + if (cache.has(id)) { + return cache.get(id) as boolean + } + if (importStack.includes(id)) { + // circular deps! + cache.set(id, false) + return false + } + const mod = getModuleInfo(id) + if (!mod) { + cache.set(id, false) + return false + } + + if (mod.isEntry) { + cache.set(id, true) + return true + } + const someImporterIs = mod.importers.some((importer) => + staticImportedByEntry( + importer, + getModuleInfo, + cache, + importStack.concat(id) + ) + ) + cache.set(id, someImporterIs) + return someImporterIs +} + +export function splitVendorChunkPlugin(): Plugin { + const cache = new SplitVendorChunkCache() + return { + name: 'vite:split-vendor-chunk', + config(config) { + const build = config.build ?? {} + const format = (build.rollupOptions?.output as OutputOptions)?.format + if (!build.ssr && !build.lib && format !== 'umd' && format !== 'iife') { + return { + build: { + rollupOptions: { + manualChunks: splitVendorChunk({ cache }) + } + } + } + } + }, + buildStart() { + cache.reset() + } + } +} From 641e725ac7e8dbfdca96ec06a0985e0893066e86 Mon Sep 17 00:00:00 2001 From: patak Date: Mon, 17 Jan 2022 15:16:59 +0100 Subject: [PATCH 2/9] chore: fix typo Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/vite/src/node/plugins/splitVendorChunk.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts index 9e8958f4620372..b4125717737efa 100644 --- a/packages/vite/src/node/plugins/splitVendorChunk.ts +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -3,7 +3,7 @@ import type { OutputOptions, GetManualChunk, GetModuleInfo } from 'rollup' import { isCSSRequest } from './css' // Use splitVendorChunkPlugin() to get the same manualChunks strategy as Vite 2.7 -// We don't recomment using this strategy as a general solution moving forward +// We don't recommend using this strategy as a general solution moving forward // splitVendorChunk is a simple index/vendor strategy that was used in Vite // until v2.8. It is exposed to let people continue to use it in case it was From 0a06f137c11a58d83f2371ec14a78dfe87c0408a Mon Sep 17 00:00:00 2001 From: patak-dev Date: Mon, 17 Jan 2022 16:10:38 +0100 Subject: [PATCH 3/9] docs: add section to build guide --- docs/guide/build.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/guide/build.md b/docs/guide/build.md index aac86a237b6819..53faf5189d502c 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -43,6 +43,20 @@ module.exports = defineConfig({ For example, you can specify multiple Rollup outputs with plugins that are only applied during build. +## Chunking Strategy + +You can configure how chunks are split using `build.rollupOptions.manualChunks` (see [Rollup docs](https://rollupjs.org/guide/en/#outputmanualchunks)). Until Vite 2.7, the default chunking strategy divided the chunks into `index` and `vendor`. It is a good strategy for some SPAs, but it is hard to provide a general solution for every Vite target use case. From Vite 2.8, `manualChunks` is no longer modified by default. You can continue to use the Split Vendor Chunk strategy by adding the `splitVendorChunkPlugin` in your config file: + +```js +// vite.config.js +import { splitVendorChunkPlugin } from 'vite' +module.exports = defineConfig({ + plugins: [splitVendorChunkPlugin()] +}) +``` + +This strategy is also provided as a `splitVendorChunk({ cache: SplitVendorChunkCache )` factory, in case composition with custom logic is needed. `cache.reset()` needs to be called at `buildStart` for build watch mode to work correctly in this case. + ## Rebuild on files changes You can enable rollup watcher with `vite build --watch`. Or, you can directly adjust the underlying [`WatcherOptions`](https://rollupjs.org/guide/en/#watch-options) via `build.watch`: From 2306dad255bcfc932f9c97b1495a19bf9dc1ae71 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Mon, 17 Jan 2022 21:44:33 +0100 Subject: [PATCH 4/9] fix: account for rollupOptions.output array --- .../vite/src/node/plugins/splitVendorChunk.ts | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts index b4125717737efa..14c088bb546da2 100644 --- a/packages/vite/src/node/plugins/splitVendorChunk.ts +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -1,3 +1,4 @@ +import type { UserConfig } from '../../node' import type { Plugin } from '../plugin' import type { OutputOptions, GetManualChunk, GetModuleInfo } from 'rollup' import { isCSSRequest } from './css' @@ -72,24 +73,37 @@ function staticImportedByEntry( } export function splitVendorChunkPlugin(): Plugin { - const cache = new SplitVendorChunkCache() + const caches: SplitVendorChunkCache[] = [] + function createSplitVendorChunk(output: OutputOptions, config: UserConfig) { + const cache = new SplitVendorChunkCache() + caches.push(cache) + const build = config.build ?? {} + const format = output?.format + if (!build.ssr && !build.lib && format !== 'umd' && format !== 'iife') { + return splitVendorChunk({ cache }) + } + } return { name: 'vite:split-vendor-chunk', config(config) { - const build = config.build ?? {} - const format = (build.rollupOptions?.output as OutputOptions)?.format - if (!build.ssr && !build.lib && format !== 'umd' && format !== 'iife') { + let outputs = config?.build?.rollupOptions?.output + if (outputs) { + outputs = Array.isArray(outputs) ? outputs : [outputs] + for (const output of outputs) { + output.manualChunks = createSplitVendorChunk(output, config) + } + } else { return { build: { rollupOptions: { - manualChunks: splitVendorChunk({ cache }) + manualChunks: createSplitVendorChunk({}, config) } } } } }, buildStart() { - cache.reset() + caches.forEach((cache) => cache.reset()) } } } From 41421c4cee6bd70ada50f955ea05da89fc3e4f15 Mon Sep 17 00:00:00 2001 From: patak Date: Fri, 28 Jan 2022 07:34:46 +0100 Subject: [PATCH 5/9] chore: fix type Co-authored-by: ygj6 <7699524+ygj6@users.noreply.github.com> --- docs/guide/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/build.md b/docs/guide/build.md index 53faf5189d502c..1f1f149a07f7c9 100644 --- a/docs/guide/build.md +++ b/docs/guide/build.md @@ -55,7 +55,7 @@ module.exports = defineConfig({ }) ``` -This strategy is also provided as a `splitVendorChunk({ cache: SplitVendorChunkCache )` factory, in case composition with custom logic is needed. `cache.reset()` needs to be called at `buildStart` for build watch mode to work correctly in this case. +This strategy is also provided as a `splitVendorChunk({ cache: SplitVendorChunkCache })` factory, in case composition with custom logic is needed. `cache.reset()` needs to be called at `buildStart` for build watch mode to work correctly in this case. ## Rebuild on files changes From 9e07062947bbf0ff1d27e6c9fe9e2b6fed40031a Mon Sep 17 00:00:00 2001 From: patak Date: Fri, 28 Jan 2022 07:36:16 +0100 Subject: [PATCH 6/9] chore: fix default value --- packages/vite/src/node/plugins/splitVendorChunk.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts index 14c088bb546da2..7973cb499679ca 100644 --- a/packages/vite/src/node/plugins/splitVendorChunk.ts +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -24,7 +24,7 @@ export class SplitVendorChunkCache { export function splitVendorChunk({ cache = new SplitVendorChunkCache() -}): GetManualChunk { +} = {}): GetManualChunk { return (id, { getModuleInfo }) => { if ( id.includes('node_modules') && From a4d75bc0ad97cd019a101ff62ea12c808bdecc21 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Fri, 28 Jan 2022 07:44:16 +0100 Subject: [PATCH 7/9] chore: update --- packages/vite/src/node/plugins/splitVendorChunk.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts index 7973cb499679ca..09246e14e35234 100644 --- a/packages/vite/src/node/plugins/splitVendorChunk.ts +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -22,9 +22,10 @@ export class SplitVendorChunkCache { } } -export function splitVendorChunk({ - cache = new SplitVendorChunkCache() -} = {}): GetManualChunk { +export function splitVendorChunk( + options: { cache?: SplitVendorChunkCache } = {} +): GetManualChunk { + const cache = options.cache ?? new SplitVendorChunkCache() return (id, { getModuleInfo }) => { if ( id.includes('node_modules') && From 23440e9564fb794d904a905e82eb1a475e22d4be Mon Sep 17 00:00:00 2001 From: patak-dev Date: Tue, 1 Mar 2022 12:00:07 +0100 Subject: [PATCH 8/9] feat: support user manualChunks composition --- packages/playground/vue/vite.config.ts | 12 ++++++++- .../vite/src/node/plugins/splitVendorChunk.ts | 27 ++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/playground/vue/vite.config.ts b/packages/playground/vue/vite.config.ts index a9ca552cbccda2..f99a68ce8b6b10 100644 --- a/packages/playground/vue/vite.config.ts +++ b/packages/playground/vue/vite.config.ts @@ -17,7 +17,17 @@ export default defineConfig({ ], build: { // to make tests faster - minify: false + minify: false, + rollupOptions: { + output: { + // Test splitVendorChunkPlugin composition + manualChunks(id) { + if (id.includes('src-import')) { + return 'src-import' + } + } + } + } }, css: { modules: { diff --git a/packages/vite/src/node/plugins/splitVendorChunk.ts b/packages/vite/src/node/plugins/splitVendorChunk.ts index 09246e14e35234..3f7c16067a5f8e 100644 --- a/packages/vite/src/node/plugins/splitVendorChunk.ts +++ b/packages/vite/src/node/plugins/splitVendorChunk.ts @@ -1,6 +1,11 @@ import type { UserConfig } from '../../node' import type { Plugin } from '../plugin' -import type { OutputOptions, GetManualChunk, GetModuleInfo } from 'rollup' +import type { + OutputOptions, + GetManualChunk, + GetManualChunkApi, + GetModuleInfo +} from 'rollup' import { isCSSRequest } from './css' // Use splitVendorChunkPlugin() to get the same manualChunks strategy as Vite 2.7 @@ -91,13 +96,29 @@ export function splitVendorChunkPlugin(): Plugin { if (outputs) { outputs = Array.isArray(outputs) ? outputs : [outputs] for (const output of outputs) { - output.manualChunks = createSplitVendorChunk(output, config) + const viteManualChunks = createSplitVendorChunk(output, config) + if (viteManualChunks) { + if (output.manualChunks) { + if (typeof output.manualChunks === 'function') { + const userManualChunks = output.manualChunks + output.manualChunks = (id: string, api: GetManualChunkApi) => { + return userManualChunks(id, api) ?? viteManualChunks(id, api) + } + } + // else, leave the object form of manualChunks untouched, as + // we can't safely replicate rollup handling. + } else { + output.manualChunks = viteManualChunks + } + } } } else { return { build: { rollupOptions: { - manualChunks: createSplitVendorChunk({}, config) + output: { + manualChunks: createSplitVendorChunk({}, config) + } } } } From df912c0f4d845d44ca5ecfb1d43f2126a1759d70 Mon Sep 17 00:00:00 2001 From: patak-dev Date: Thu, 3 Mar 2022 14:25:55 +0100 Subject: [PATCH 9/9] chore: format --- packages/vite/src/node/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 8eb1ab7143588d..f6a8180cb53d55 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -108,4 +108,4 @@ declare module 'rollup' { export interface RenderedChunk { viteMetadata: ChunkMetadata } -} \ No newline at end of file +}