diff --git a/__tests__/base/vitestGlobalSetup.ts b/__tests__/base/vitestGlobalSetup.ts index 0f7e5d0c..62953c6d 100644 --- a/__tests__/base/vitestGlobalSetup.ts +++ b/__tests__/base/vitestGlobalSetup.ts @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process' -import { readFile } from 'node:fs/promises' +import { readFile, rm } from 'node:fs/promises' import { createServer, type Server } from 'node:http' import type { AddressInfo } from 'node:net' import { extname, join, resolve } from 'node:path' @@ -65,6 +65,8 @@ export async function setup() { // one process per flavor: the markdown renderer is a module-level // singleton, so in-process builds would leak the first base into the rest for (const mode of ['plain', 'relative', 'cdn', 'mpa']) { + // mpa builds never empty outDir, so stale assets would survive reruns + await rm(dist(mode), { recursive: true, force: true }) const res = spawnSync(process.execPath, [bin, 'build', 'fixture'], { cwd: dir, env: { diff --git a/src/node/build/build.ts b/src/node/build/build.ts index a38384d8..e81df4a3 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -3,7 +3,6 @@ import fs from 'node:fs' import { mkdir, readFile, - readdir, rm, symlink, unlink, @@ -165,6 +164,9 @@ async function render( const clientOutput: (Rolldown.OutputChunk | Rolldown.OutputAsset)[] = clientResult?.output || [] + const resultOutput: (Rolldown.OutputChunk | Rolldown.OutputAsset)[] = + (siteConfig.mpa ? serverResult : clientResult)?.output || [] + const appChunk = clientOutput.find( (chunk): chunk is Rolldown.OutputChunk => chunk.type === 'chunk' && @@ -172,20 +174,12 @@ async function render( !!chunk.facadeModuleId?.endsWith('.js') ) - // MPA has no client bundle — detect the theme from the bundle that exists - const isDefaultTheme = ( - (siteConfig.mpa ? serverResult : clientResult)?.output || [] - ).some( + const isDefaultTheme = resultOutput.some( (chunk): chunk is Rolldown.OutputChunk => chunk.type === 'chunk' && chunk.moduleIds.some((id) => id.includes('client/theme-default')) ) - // ---- - - const resultOutput: (Rolldown.OutputChunk | Rolldown.OutputAsset)[] = - (siteConfig.mpa ? serverResult : clientResult)?.output || [] - const cssChunk = resultOutput.find( (chunk): chunk is Rolldown.OutputAsset => chunk.type === 'asset' && chunk.fileName.endsWith('.css') @@ -226,9 +220,7 @@ async function render( } } - // pre-seeded with icons SSR collection cannot see (client-only renders); - // the Array.isArray guard keeps an untyped config's bare string from - // spreading into characters + // pre-seeded with icons SSR collection cannot see (client-only renders) const include = siteConfig.icons?.include const usedIcons = new Set(Array.isArray(include) ? include : []) @@ -275,18 +267,9 @@ async function emitIconsCSS( config.logger.warn(c.yellow(`(icons) ${warning}`)) } - // MPA builds never empty outDir, so files from prior builds linger — both - // hashed sheets and the fixed-name file pre-rework versions emitted const assetsDir = path.join(config.outDir, config.assetsDir) - const existing = await readdir(assetsDir).catch(() => [] as string[]) - await Promise.all([ - unlink(path.join(config.outDir, 'vp-icons.css')).catch(() => {}), - ...existing - .filter((file) => /^vp-icons\.[0-9a-f]{8}\.css$/.test(file)) - .map((file) => unlink(path.join(assetsDir, file))) - ]) - const placeholder = vpIconsFileName(VP_ICONS_HASH_PLACEHOLDER) + let hashedName = '' if (css) { hashedName = vpIconsFileName( @@ -296,10 +279,6 @@ async function emitIconsCSS( await writeFile(path.join(assetsDir, hashedName), css) } - // pages linked the placeholder name before the hash could exist — point - // them at the emitted file, or drop the whole tag when there is none. - // Anchored on the placeholder, not the tag shape, so a transformHtml - // hook reformatting attributes doesn't defeat it const linkRE = new RegExp( `[ \\t]*]*${VP_ICONS_HASH_PLACEHOLDER}[^>]*>\\n?` ) diff --git a/src/node/build/bundle.ts b/src/node/build/bundle.ts index 9290c77f..f2d0ad08 100644 --- a/src/node/build/bundle.ts +++ b/src/node/build/bundle.ts @@ -155,6 +155,10 @@ export async function bundle( )) as Rolldown.RolldownOutput if (config.mpa) { + // FIXME: nothing ever empties outDir in MPA mode (no client build runs + // with emptyOutDir, and buildMPAClient sets emptyOutDir: false), so + // hashed assets of every kind accumulate across rebuilds into a dirty + // output directory // in MPA mode, we need to copy over the non-js asset files from the // server build since there is no client-side build. await pMap(