diff --git a/src/client/theme-default/without-fonts.ts b/src/client/theme-default/without-fonts.ts index f53fb679..35532c38 100644 --- a/src/client/theme-default/without-fonts.ts +++ b/src/client/theme-default/without-fonts.ts @@ -11,9 +11,6 @@ import type { Theme } from 'vitepress' import VPBadge from './components/VPBadge.vue' import Layout from './Layout.vue' -// Note: if we add more optional components here, i.e. components that are not -// used in the theme by default unless the user imports them, make sure to update -// the `lazyDefaultThemeComponentsRE` regex in src/node/build/bundle.ts. export { default as VPImage } from './components/VPImage.vue' export { default as VPButton } from './components/VPButton.vue' export { default as VPHomeHero } from './components/VPHomeHero.vue' diff --git a/src/node/build/bundle.ts b/src/node/build/bundle.ts index 159bb43c..76e6dfef 100644 --- a/src/node/build/bundle.ts +++ b/src/node/build/bundle.ts @@ -15,14 +15,24 @@ import { sanitizeFileName, slash } from '../shared' import { task } from '../utils/task' import { buildMPAClient } from './buildMPAClient' -// A list of default theme components that should only be loaded on demand. -const lazyDefaultThemeComponentsRE = - /VP(HomeSponsors|DocAsideSponsors|TeamPage|TeamMembers|LocalSearchBox|AlgoliaSearchBox|CarbonAds|DocAsideCarbonAds|Sponsors)/ +const CSS_LANGS_RE = + /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/ const clientDir = normalizePath( path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../client') ) +// to avoid circular dependency issues +// these deps are also being used in the client code (outside of the theme) +const excludedModules = [ + '/@siteData', + 'node_modules/@vueuse/core/', + 'node_modules/@vueuse/shared/', + 'node_modules/vue/', + 'node_modules/vue-demi/', + clientDir +] + // bundles the VitePress app for both client AND server. export async function bundle( config: SiteConfig, @@ -47,6 +57,12 @@ export async function bundle( input[slash(alias).replace(/\//g, '_')] = path.resolve(config.srcDir, file) }) + const themeEntryRE = new RegExp( + `^${escapeRegExp( + path.resolve(config.themeDir, 'index.js').replace(/\\/g, '/') + ).slice(0, -2)}m?(j|t)s` + ) + // resolve options to pass to vite const { rollupOptions } = options @@ -109,12 +125,6 @@ export async function bundle( : `${config.assetsDir}/chunks/[name].[hash].js` }, manualChunks(id, ctx) { - if (lazyDefaultThemeComponentsRE.test(id)) { - return - } - if (id.startsWith(`${clientDir}/theme-default`)) { - return 'theme' - } // move known framework code into a stable chunk so that // custom theme changes do not invalidate hash for all pages if (id.startsWith('\0vite')) { @@ -135,6 +145,19 @@ export async function bundle( ) { return 'framework' } + + if ( + (id.startsWith(`${clientDir}/theme-default`) || + !excludedModules.some((i) => id.includes(i))) && + staticImportedByEntry( + id, + ctx.getModuleInfo, + cacheTheme, + themeEntryRE + ) + ) { + return 'theme' + } } }) } @@ -182,6 +205,7 @@ export async function bundle( } const cache = new Map() +const cacheTheme = new Map() /** * Check if a module is statically imported by at least one entry. @@ -189,7 +213,7 @@ const cache = new Map() function isEagerChunk(id: string, getModuleInfo: Rollup.GetModuleInfo) { if ( id.includes('node_modules') && - !/\.css($|\\?)/.test(id) && + !CSS_LANGS_RE.test(id) && staticImportedByEntry(id, getModuleInfo, cache) ) { return true @@ -200,6 +224,7 @@ function staticImportedByEntry( id: string, getModuleInfo: Rollup.GetModuleInfo, cache: Map, + entryRE: RegExp | null = null, importStack: string[] = [] ): boolean { if (cache.has(id)) { @@ -216,7 +241,7 @@ function staticImportedByEntry( return false } - if (mod.isEntry) { + if (entryRE ? entryRE.test(id) : mod.isEntry) { cache.set(id, true) return true } @@ -225,9 +250,14 @@ function staticImportedByEntry( importer, getModuleInfo, cache, + entryRE, importStack.concat(id) ) ) cache.set(id, someImporterIs) return someImporterIs } + +function escapeRegExp(str: string) { + return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d') +}