diff --git a/src/node/build/build.ts b/src/node/build/build.ts index 29fa53b20bf6..32f40225e142 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -6,14 +6,22 @@ import { renderPage } from './render' import { OutputChunk, OutputAsset } from 'rollup' import ora from 'ora' -export async function build(root: string, buildOptions: BuildOptions = {}) { +export async function build( + root: string, + buildOptions: BuildOptions & { mpa?: string } = {} +) { const start = Date.now() process.env.NODE_ENV = 'production' const siteConfig = await resolveConfig(root) + if (buildOptions.mpa) { + siteConfig.mpa = true + delete buildOptions.mpa + } + try { - const [clientResult, , pageToHashMap] = await bundle( + const [clientResult, serverResult, pageToHashMap] = await bundle( siteConfig, buildOptions ) @@ -22,11 +30,13 @@ export async function build(root: string, buildOptions: BuildOptions = {}) { spinner.start('rendering pages...') try { - const appChunk = clientResult.output.find( - (chunk) => chunk.type === 'chunk' && chunk.isEntry - ) as OutputChunk + const appChunk = + clientResult && + (clientResult.output.find( + (chunk) => chunk.type === 'chunk' && chunk.isEntry + ) as OutputChunk) - const cssChunk = clientResult.output.find( + const cssChunk = (clientResult || serverResult).output.find( (chunk) => chunk.type === 'asset' && chunk.fileName.endsWith('.css') ) as OutputAsset diff --git a/src/node/build/bundle.ts b/src/node/build/bundle.ts index 672e3bbe2c6e..18cdd40b7bf2 100644 --- a/src/node/build/bundle.ts +++ b/src/node/build/bundle.ts @@ -1,5 +1,6 @@ import ora from 'ora' import path from 'path' +import fs from 'fs-extra' import { slash } from '../utils/slash' import { APP_PATH } from '../alias' import { SiteConfig } from '../config' @@ -71,7 +72,8 @@ export async function bundle( }) } }, - minify: ssr ? false : !process.env.DEBUG + // minify with esbuild in MPA mode (for CSS) + minify: ssr ? (config.mpa ? 'esbuild' : false) : !process.env.DEBUG } }) @@ -82,7 +84,7 @@ export async function bundle( spinner.start('building client + server bundles...') try { ;[clientResult, serverResult] = await (Promise.all([ - build(resolveViteConfig(false)), + config.mpa ? null : build(resolveViteConfig(false)), build(resolveViteConfig(true)) ]) as Promise<[RollupOutput, RollupOutput]>) } catch (e) { @@ -95,6 +97,23 @@ export async function bundle( symbol: okMark }) + if (config.mpa) { + // in MPA mode, we need to copy over the non-js asset files from the + // server build since there is no client-side build. + for (const chunk of serverResult.output) { + if (!chunk.fileName.endsWith('.js')) { + const tempPath = path.resolve(config.tempDir, chunk.fileName) + const outPath = path.resolve(config.outDir, chunk.fileName) + await fs.copy(tempPath, outPath) + } + } + // also copy over public dir + const publicDir = path.resolve(config.srcDir, 'public') + if (fs.existsSync(publicDir)) { + await fs.copy(publicDir, config.outDir) + } + } + return [clientResult, serverResult, pageToHashMap] } diff --git a/src/node/build/render.ts b/src/node/build/render.ts index 72b080f01072..52aee1f0e091 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -39,18 +39,20 @@ export async function renderPage( )) const pageData = JSON.parse(__pageData) - const preloadLinks = [ - // resolve imports for index.js + page.md.js and inject script tags for - // them as well so we fetch everything as early as possible without having - // to wait for entry chunks to parse - ...resolvePageImports(config, page, result, appChunk), - pageClientJsFileName, - appChunk.fileName - ] - .map((file) => { - return `` - }) - .join('\n ') + const preloadLinks = config.mpa + ? '' + : [ + // resolve imports for index.js + page.md.js and inject script tags for + // them as well so we fetch everything as early as possible without having + // to wait for entry chunks to parse + ...resolvePageImports(config, page, result, appChunk), + pageClientJsFileName, + appChunk.fileName + ] + .map((file) => { + return `` + }) + .join('\n ') const stylesheetLink = cssChunk ? `` @@ -83,11 +85,12 @@ export async function renderPage(
${content}
- - - + ${ + config.mpa + ? `` + : `` + + `` + } `.trim() const htmlFileName = path.join(config.outDir, page.replace(/\.md$/, '.html')) await fs.ensureDir(path.dirname(htmlFileName)) diff --git a/src/node/config.ts b/src/node/config.ts index b1b3e4a8dbd3..20dac3686e32 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -22,6 +22,7 @@ export { resolveSiteDataByRoute } from './shared' const debug = require('debug')('vitepress:config') export interface UserConfig { + extends?: RawConfigExports lang?: string base?: string title?: string @@ -43,15 +44,9 @@ export interface UserConfig { srcExclude?: string[] /** - * @deprecated use `srcExclude` instead - */ - exclude?: string[] - /** - * @deprecated use `vue` instead + * Enable MPA / zero-JS mode */ - vueOptions?: VuePluginOptions - - extends?: RawConfigExports + mpa?: boolean } type RawConfigExports = @@ -72,6 +67,7 @@ export interface SiteConfig { markdown: MarkdownOptions | undefined vue: VuePluginOptions | undefined vite: ViteConfig | undefined + mpa: boolean } const resolve = (root: string, file: string) => @@ -81,22 +77,7 @@ export async function resolveConfig( root: string = process.cwd() ): Promise { const userConfig = await resolveUserConfig(root) - - if (userConfig.vueOptions) { - console.warn( - chalk.yellow(`[vitepress] "vueOptions" option has been renamed to "vue".`) - ) - } - if (userConfig.exclude) { - console.warn( - chalk.yellow( - `[vitepress] "exclude" option has been renamed to "ssrExclude".` - ) - ) - } - const site = await resolveSiteData(root, userConfig) - const srcDir = path.resolve(root, userConfig.srcDir || '.') // resolve theme path @@ -120,7 +101,8 @@ export async function resolveConfig( markdown: userConfig.markdown, alias: resolveAliases(themeDir), vue: userConfig.vue, - vite: userConfig.vite + vite: userConfig.vite, + mpa: !!userConfig.mpa } return config