From 6582ea79d76bb75e40736d1df564b31d43ff59ad Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 23 Feb 2024 19:53:25 +0100 Subject: [PATCH] Rework full build workflow for hash router support Do not compile a server bundle that we won't use --- packages/docusaurus/src/commands/build.ts | 72 +++++++++++++------ packages/docusaurus/src/ssg.ts | 14 ++++ .../docusaurus/src/templates/templates.ts | 42 ++++++++++- packages/docusaurus/src/webpack/base.ts | 2 +- 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index c332a38b9af2..efbd77560977 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -23,12 +23,21 @@ import { import {PerfLogger} from '../utils'; import {loadI18n} from '../server/i18n'; -import {generateStaticFiles, loadAppRenderer} from '../ssg'; -import {compileSSRTemplate} from '../templates/templates'; +import { + generateHashRouterEntrypoint, + generateStaticFiles, + loadAppRenderer +} from '../ssg'; +import { + compileSSRTemplate, + renderHashRouterTemplate, +} from '../templates/templates'; import defaultSSRTemplate from '../templates/ssr.html.template'; +import type { + SSGParams} from '../ssg'; import type {Manifest} from 'react-loadable-ssr-addon-v5-slorber'; -import type {LoadedPlugin, Props} from '@docusaurus/types'; +import type {LoadedPlugin, Props, RouterType} from '@docusaurus/types'; import type {SiteCollectedData} from '../common'; export type BuildCLIOptions = Pick< @@ -171,7 +180,11 @@ async function buildLocale({ PerfLogger.end('Loading site'); // Apply user webpack config. - const {outDir, plugins} = props; + const { + outDir, + plugins, + siteConfig: {router}, + } = props; // We can build the 2 configs in parallel PerfLogger.start('Creating webpack configs'); @@ -196,7 +209,11 @@ async function buildLocale({ // Run webpack to build JS bundle (client) and static html files (server). PerfLogger.start('Bundling'); - await compile([clientConfig, serverConfig]); + if (router === 'hash') { + await compile([clientConfig]); + } else { + await compile([clientConfig, serverConfig]); + } PerfLogger.end('Bundling'); PerfLogger.start('Executing static site generation'); @@ -204,6 +221,7 @@ async function buildLocale({ props, serverBundlePath, clientManifestPath, + router, }); PerfLogger.end('Executing static site generation'); @@ -242,11 +260,13 @@ async function executeSSG({ props, serverBundlePath, clientManifestPath, + router, }: { props: Props; serverBundlePath: string; clientManifestPath: string; -}) { + router: RouterType; +}): Promise<{collectedData: SiteCollectedData}> { PerfLogger.start('Reading client manifest'); const manifest: Manifest = await fs.readJSON(clientManifestPath, 'utf-8'); PerfLogger.end('Reading client manifest'); @@ -257,32 +277,38 @@ async function executeSSG({ ); PerfLogger.end('Compiling SSR template'); + const params: SSGParams = { + trailingSlash: props.siteConfig.trailingSlash, + outDir: props.outDir, + baseUrl: props.baseUrl, + manifest, + headTags: props.headTags, + preBodyTags: props.preBodyTags, + postBodyTags: props.postBodyTags, + ssrTemplate, + noIndex: props.siteConfig.noIndex, + DOCUSAURUS_VERSION, + }; + + if (router === 'hash') { + PerfLogger.start('Generate Hash Router entry point'); + const content = renderHashRouterTemplate({params}); + await generateHashRouterEntrypoint({content, params}); + PerfLogger.end('Generate Hash Router entry point'); + return {collectedData: {}}; + } + PerfLogger.start('Loading App renderer'); const renderer = await loadAppRenderer({ serverBundlePath, }); PerfLogger.end('Loading App renderer'); - // TODO maybe there's a more elegant way than reusing our SSG pipeline? - const pathnames = - props.siteConfig.router === 'hash' ? ['/'] : props.routesPaths; - PerfLogger.start('Generate static files'); const ssgResult = await generateStaticFiles({ - pathnames, + pathnames: props.routesPaths, renderer, - params: { - trailingSlash: props.siteConfig.trailingSlash, - outDir: props.outDir, - baseUrl: props.baseUrl, - manifest, - headTags: props.headTags, - preBodyTags: props.preBodyTags, - postBodyTags: props.postBodyTags, - ssrTemplate, - noIndex: props.siteConfig.noIndex, - DOCUSAURUS_VERSION, - }, + params, }); PerfLogger.end('Generate static files'); diff --git a/packages/docusaurus/src/ssg.ts b/packages/docusaurus/src/ssg.ts index a1e6d4e9ce18..4c8ab0f5e93f 100644 --- a/packages/docusaurus/src/ssg.ts +++ b/packages/docusaurus/src/ssg.ts @@ -226,6 +226,20 @@ It might also require to wrap your client code in ${logger.code( return parts.join('\n'); } +export async function generateHashRouterEntrypoint({ + content, + params, +}: { + content: string; + params: SSGParams; +}): Promise { + await writeStaticFile({ + pathname: '/', + content, + params, + }); +} + async function writeStaticFile({ content, pathname, diff --git a/packages/docusaurus/src/templates/templates.ts b/packages/docusaurus/src/templates/templates.ts index d0a024f3addd..3484b3d8e5e8 100644 --- a/packages/docusaurus/src/templates/templates.ts +++ b/packages/docusaurus/src/templates/templates.ts @@ -70,7 +70,7 @@ export function renderSSRTemplate({ result: AppRenderResult; }): string { const { - // baseUrl, + baseUrl, headTags, preBodyTags, postBodyTags, @@ -98,7 +98,7 @@ export function renderSSRTemplate({ const data: SSRTemplateData = { appHtml, - baseUrl: './', + baseUrl, htmlAttributes, bodyAttributes, headTags, @@ -113,3 +113,41 @@ export function renderSSRTemplate({ return ssrTemplate(data); } + +export function renderHashRouterTemplate({ + params, +}: { + params: SSGParams; +}): string { + const { + // baseUrl, + headTags, + preBodyTags, + postBodyTags, + manifest, + DOCUSAURUS_VERSION, + ssrTemplate, + } = params; + + const {scripts, stylesheets} = getScriptsAndStylesheets({ + manifest, + modules: [], + }); + + const data: SSRTemplateData = { + appHtml: '', + baseUrl: './', + htmlAttributes: '', + bodyAttributes: '', + headTags, + preBodyTags, + postBodyTags, + metaAttributes: [], + scripts, + stylesheets, + noIndex: false, + version: DOCUSAURUS_VERSION, + }; + + return ssrTemplate(data); +} diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 98c98f457007..45f297c965fd 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -112,7 +112,7 @@ export async function createBaseConfig({ chunkFilename: isProd ? 'assets/js/[name].[contenthash:8].js' : '[name].js', - publicPath: isServer ? baseUrl : 'auto', + publicPath: siteConfig.router === 'hash' ? 'auto' : baseUrl, hashFunction: 'xxhash64', }, // Don't throw warning when asset created is over 250kb