Skip to content

Commit a6cd891

Browse files
authored
fix: fix theme chunking logic causing out-of-order styles (#3403)
1 parent 1407baf commit a6cd891

File tree

4 files changed

+48
-22
lines changed

4 files changed

+48
-22
lines changed

Diff for: src/client/theme-default/components/VPLocalSearchBox.vue

+4-7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from 'vue'
2929
import type { ModalTranslations } from '../../../../types/local-search'
3030
import { pathToFile } from '../../app/utils'
31+
import { escapeRegExp } from '../../shared'
3132
import { useData } from '../composables/data'
3233
import { LRUCache } from '../support/lru'
3334
import { createSearchTranslate } from '../support/translation'
@@ -146,8 +147,8 @@ const cache = new LRUCache<string, Map<string, string>>(16) // 16 files
146147
debouncedWatch(
147148
() => [searchIndex.value, filterText.value, showDetailedList.value] as const,
148149
async ([index, filterTextValue, showDetailedListValue], old, onCleanup) => {
149-
150-
if (old?.[0] !== index) { // in case of hmr
150+
if (old?.[0] !== index) {
151+
// in case of hmr
151152
cache.clear()
152153
}
153154
@@ -396,11 +397,7 @@ function formMarkRegex(terms: Set<string>) {
396397
return new RegExp(
397398
[...terms]
398399
.sort((a, b) => b.length - a.length)
399-
.map((term) => {
400-
return `(${term
401-
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
402-
.replace(/-/g, '\\x2d')})`
403-
})
400+
.map((term) => `(${escapeRegExp(term)})`)
404401
.join('|'),
405402
'gi'
406403
)

Diff for: src/client/theme-default/without-fonts.ts

-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ import type { Theme } from 'vitepress'
1111
import VPBadge from './components/VPBadge.vue'
1212
import Layout from './Layout.vue'
1313

14-
// Note: if we add more optional components here, i.e. components that are not
15-
// used in the theme by default unless the user imports them, make sure to update
16-
// the `lazyDefaultThemeComponentsRE` regex in src/node/build/bundle.ts.
1714
export { default as VPImage } from './components/VPImage.vue'
1815
export { default as VPButton } from './components/VPButton.vue'
1916
export { default as VPHomeHero } from './components/VPHomeHero.vue'

Diff for: src/node/build/bundle.ts

+39-12
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,29 @@ import {
1111
import { APP_PATH } from '../alias'
1212
import type { SiteConfig } from '../config'
1313
import { createVitePressPlugin } from '../plugin'
14-
import { sanitizeFileName, slash } from '../shared'
14+
import { escapeRegExp, sanitizeFileName, slash } from '../shared'
1515
import { task } from '../utils/task'
1616
import { buildMPAClient } from './buildMPAClient'
1717

18-
// A list of default theme components that should only be loaded on demand.
19-
const lazyDefaultThemeComponentsRE =
20-
/VP(HomeSponsors|DocAsideSponsors|TeamPage|TeamMembers|LocalSearchBox|AlgoliaSearchBox|CarbonAds|DocAsideCarbonAds|Sponsors)/
18+
// https://github.com/vitejs/vite/blob/d2aa0969ee316000d3b957d7e879f001e85e369e/packages/vite/src/node/plugins/splitVendorChunk.ts#L14
19+
const CSS_LANGS_RE =
20+
/\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/
2121

2222
const clientDir = normalizePath(
2323
path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../client')
2424
)
2525

26+
// these deps are also being used in the client code (outside of the theme)
27+
// exclude them from the theme chunk so there is no circular dependency
28+
const excludedModules = [
29+
'/@siteData',
30+
'node_modules/@vueuse/core/',
31+
'node_modules/@vueuse/shared/',
32+
'node_modules/vue/',
33+
'node_modules/vue-demi/',
34+
clientDir
35+
]
36+
2637
// bundles the VitePress app for both client AND server.
2738
export async function bundle(
2839
config: SiteConfig,
@@ -47,6 +58,12 @@ export async function bundle(
4758
input[slash(alias).replace(/\//g, '_')] = path.resolve(config.srcDir, file)
4859
})
4960

61+
const themeEntryRE = new RegExp(
62+
`^${escapeRegExp(
63+
path.resolve(config.themeDir, 'index.js').replace(/\\/g, '/')
64+
).slice(0, -2)}m?(j|t)s`
65+
)
66+
5067
// resolve options to pass to vite
5168
const { rollupOptions } = options
5269

@@ -109,12 +126,6 @@ export async function bundle(
109126
: `${config.assetsDir}/chunks/[name].[hash].js`
110127
},
111128
manualChunks(id, ctx) {
112-
if (lazyDefaultThemeComponentsRE.test(id)) {
113-
return
114-
}
115-
if (id.startsWith(`${clientDir}/theme-default`)) {
116-
return 'theme'
117-
}
118129
// move known framework code into a stable chunk so that
119130
// custom theme changes do not invalidate hash for all pages
120131
if (id.startsWith('\0vite')) {
@@ -135,6 +146,19 @@ export async function bundle(
135146
) {
136147
return 'framework'
137148
}
149+
150+
if (
151+
(id.startsWith(`${clientDir}/theme-default`) ||
152+
!excludedModules.some((i) => id.includes(i))) &&
153+
staticImportedByEntry(
154+
id,
155+
ctx.getModuleInfo,
156+
cacheTheme,
157+
themeEntryRE
158+
)
159+
) {
160+
return 'theme'
161+
}
138162
}
139163
})
140164
}
@@ -182,14 +206,15 @@ export async function bundle(
182206
}
183207

184208
const cache = new Map<string, boolean>()
209+
const cacheTheme = new Map<string, boolean>()
185210

186211
/**
187212
* Check if a module is statically imported by at least one entry.
188213
*/
189214
function isEagerChunk(id: string, getModuleInfo: Rollup.GetModuleInfo) {
190215
if (
191216
id.includes('node_modules') &&
192-
!/\.css($|\\?)/.test(id) &&
217+
!CSS_LANGS_RE.test(id) &&
193218
staticImportedByEntry(id, getModuleInfo, cache)
194219
) {
195220
return true
@@ -200,6 +225,7 @@ function staticImportedByEntry(
200225
id: string,
201226
getModuleInfo: Rollup.GetModuleInfo,
202227
cache: Map<string, boolean>,
228+
entryRE: RegExp | null = null,
203229
importStack: string[] = []
204230
): boolean {
205231
if (cache.has(id)) {
@@ -216,7 +242,7 @@ function staticImportedByEntry(
216242
return false
217243
}
218244

219-
if (mod.isEntry) {
245+
if (entryRE ? entryRE.test(id) : mod.isEntry) {
220246
cache.set(id, true)
221247
return true
222248
}
@@ -225,6 +251,7 @@ function staticImportedByEntry(
225251
importer,
226252
getModuleInfo,
227253
cache,
254+
entryRE,
228255
importStack.concat(id)
229256
)
230257
)

Diff for: src/shared/shared.ts

+5
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,8 @@ export function treatAsHtml(filename: string): boolean {
195195

196196
return ext == null || !KNOWN_EXTENSIONS.has(ext.toLowerCase())
197197
}
198+
199+
// https://github.com/sindresorhus/escape-string-regexp/blob/ba9a4473850cb367936417e97f1f2191b7cc67dd/index.js
200+
export function escapeRegExp(str: string) {
201+
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d')
202+
}

0 commit comments

Comments
 (0)