From 898611d4adcfc82f7acb55a7812d770d89cd0a6d Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Fri, 1 Apr 2022 13:41:39 +0800 Subject: [PATCH] refactor(core): code cleanup (#7084) --- packages/docusaurus-types/src/index.d.ts | 1 - packages/docusaurus/src/client/App.tsx | 2 +- .../index.tsx} | 24 ++++---- .../styles.module.css | 0 .../src/client/exports/BrowserOnly.tsx | 8 +-- .../src/client/exports/Interpolate.tsx | 56 ++++++++--------- .../docusaurus/src/client/exports/Link.tsx | 48 +++++++-------- .../commands/swizzle/__tests__/index.test.ts | 2 +- .../__snapshots__/routes.test.ts.snap | 60 ++++--------------- .../src/server/__tests__/routes.test.ts | 26 ++++---- packages/docusaurus/src/server/brokenLinks.ts | 2 +- packages/docusaurus/src/server/i18n.ts | 5 +- packages/docusaurus/src/server/index.ts | 25 +++----- packages/docusaurus/src/server/routes.ts | 25 ++++---- .../docusaurus/src/server/siteMetadata.ts | 3 +- packages/docusaurus/src/webpack/server.ts | 6 +- 16 files changed, 114 insertions(+), 179 deletions(-) rename packages/docusaurus/src/client/{baseUrlIssueBanner/BaseUrlIssueBanner.tsx => BaseUrlIssueBanner/index.tsx} (87%) rename packages/docusaurus/src/client/{baseUrlIssueBanner => BaseUrlIssueBanner}/styles.module.css (100%) diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index 7bc1f119a2..7d24b70a47 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -227,7 +227,6 @@ export type LoadContext = { */ baseUrl: string; i18n: I18n; - ssrTemplate: string; codeTranslations: CodeTranslations; }; diff --git a/packages/docusaurus/src/client/App.tsx b/packages/docusaurus/src/client/App.tsx index 01d74bb1cd..206dfb27bd 100644 --- a/packages/docusaurus/src/client/App.tsx +++ b/packages/docusaurus/src/client/App.tsx @@ -12,7 +12,7 @@ import renderRoutes from './exports/renderRoutes'; import {BrowserContextProvider} from './browserContext'; import {DocusaurusContextProvider} from './docusaurusContext'; import PendingNavigation from './PendingNavigation'; -import BaseUrlIssueBanner from './baseUrlIssueBanner/BaseUrlIssueBanner'; +import BaseUrlIssueBanner from './BaseUrlIssueBanner'; import SiteMetadataDefaults from './SiteMetadataDefaults'; import Root from '@theme/Root'; import SiteMetadata from '@theme/SiteMetadata'; diff --git a/packages/docusaurus/src/client/baseUrlIssueBanner/BaseUrlIssueBanner.tsx b/packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx similarity index 87% rename from packages/docusaurus/src/client/baseUrlIssueBanner/BaseUrlIssueBanner.tsx rename to packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx index 59f693931a..3ddfaa5ed9 100644 --- a/packages/docusaurus/src/client/baseUrlIssueBanner/BaseUrlIssueBanner.tsx +++ b/packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx @@ -74,11 +74,11 @@ function insertBanner() { declare global { interface Window { - __DOCUSAURUS_INSERT_BASEURL_BANNER: boolean; + [InsertBannerWindowAttribute]: boolean; } } -function BaseUrlIssueBannerEnabled() { +function BaseUrlIssueBanner() { const { siteConfig: {baseUrl}, } = useDocusaurusContext(); @@ -92,6 +92,8 @@ function BaseUrlIssueBannerEnabled() { return ( <> {!ExecutionEnvironment.canUseDOM && ( + // Safe to use `ExecutionEnvironment`, because `Head` is purely + // side-effect and doesn't affect hydration @@ -103,22 +105,22 @@ function BaseUrlIssueBannerEnabled() { /** * We want to help the users with a bad baseUrl configuration (very common - * error) Help message is inlined, and hidden if JS or CSS is able to load + * error). Help message is inlined, and hidden if JS or CSS is able to load. + * + * This component only inserts the base URL banner for the homepage, to avoid + * polluting every statically rendered page. + * * Note: it might create false positives (ie network failures): not a big deal - * Note: we only inline this for the homepage to avoid polluting all the site's - * pages + * * @see https://github.com/facebook/docusaurus/pull/3621 */ -export default function BaseUrlIssueBanner(): JSX.Element | null { +export default function MaybeBaseUrlIssueBanner(): JSX.Element | null { const { siteConfig: {baseUrl, baseUrlIssueBanner}, } = useDocusaurusContext(); const {pathname} = useLocation(); - - // returns true for the homepage during SRR + // returns true for the homepage during SSR const isHomePage = pathname === baseUrl; - const enabled = baseUrlIssueBanner && isHomePage; - - return enabled ? : null; + return enabled ? : null; } diff --git a/packages/docusaurus/src/client/baseUrlIssueBanner/styles.module.css b/packages/docusaurus/src/client/BaseUrlIssueBanner/styles.module.css similarity index 100% rename from packages/docusaurus/src/client/baseUrlIssueBanner/styles.module.css rename to packages/docusaurus/src/client/BaseUrlIssueBanner/styles.module.css diff --git a/packages/docusaurus/src/client/exports/BrowserOnly.tsx b/packages/docusaurus/src/client/exports/BrowserOnly.tsx index 025049b782..16e64ef06d 100644 --- a/packages/docusaurus/src/client/exports/BrowserOnly.tsx +++ b/packages/docusaurus/src/client/exports/BrowserOnly.tsx @@ -7,16 +7,14 @@ import React, {isValidElement} from 'react'; import useIsBrowser from '@docusaurus/useIsBrowser'; +import type {Props} from '@docusaurus/BrowserOnly'; // Similar comp to the one described here: // https://www.joshwcomeau.com/react/the-perils-of-rehydration/#abstractions export default function BrowserOnly({ children, fallback, -}: { - children: () => JSX.Element; - fallback?: JSX.Element; -}): JSX.Element | null { +}: Props): JSX.Element | null { const isBrowser = useIsBrowser(); if (isBrowser) { @@ -27,7 +25,7 @@ export default function BrowserOnly({ throw new Error(`Docusaurus error: The children of must be a "render function", e.g. {() => {window.location.href}}. Current type: ${isValidElement(children) ? 'React element' : typeof children}`); } - return <>{children()}; + return <>{children?.()}; } return fallback ?? null; diff --git a/packages/docusaurus/src/client/exports/Interpolate.tsx b/packages/docusaurus/src/client/exports/Interpolate.tsx index 076c7b1abc..99f0b76e1f 100644 --- a/packages/docusaurus/src/client/exports/Interpolate.tsx +++ b/packages/docusaurus/src/client/exports/Interpolate.tsx @@ -18,7 +18,6 @@ We don't ship a markdown parser nor a feature-complete i18n library on purpose. More details here: https://github.com/facebook/docusaurus/pull/4295 */ -const ValueRegexp = /\{\w+\}/g; const ValueFoundMarker = '{}'; // does not care much // If all the values are plain strings, then interpolate returns a simple string @@ -39,51 +38,44 @@ export function interpolate( ): ReactNode { const elements: (Value | string)[] = []; - const processedText = text.replace(ValueRegexp, (match: string) => { - // remove {{ and }} around the placeholder - const key = match.substring( - 1, - match.length - 1, - ) as ExtractInterpolatePlaceholders; + const processedText = text.replace( + // eslint-disable-next-line prefer-named-capture-group + /\{(\w+)\}/g, + (match, key: ExtractInterpolatePlaceholders) => { + const value = values?.[key]; - const value = values?.[key]; - - if (typeof value !== 'undefined') { - const element = isValidElement(value) - ? value - : // For non-React elements: basic primitive->string conversion - String(value); - elements.push(element); - return ValueFoundMarker; - } - return match; // no match? add warning? - }); + if (typeof value !== 'undefined') { + const element = isValidElement(value) + ? value + : // For non-React elements: basic primitive->string conversion + String(value); + elements.push(element); + return ValueFoundMarker; + } + return match; // no match? add warning? + }, + ); // No interpolation to be done: just return the text if (elements.length === 0) { return text; } // Basic string interpolation: returns interpolated string - if (elements.every((el) => typeof el === 'string')) { + if (elements.every((el): el is string => typeof el === 'string')) { return processedText .split(ValueFoundMarker) .reduce( - (str, value, index) => - str.concat(value).concat((elements[index] as string) ?? ''), + (str, value, index) => str.concat(value).concat(elements[index] ?? ''), '', ); } // JSX interpolation: returns ReactNode - return processedText.split(ValueFoundMarker).reduce( - (array, value, index) => [ - ...array, - - {value} - {elements[index]} - , - ], - [], - ); + return processedText.split(ValueFoundMarker).map((value, index) => ( + + {value} + {elements[index]} + + )); } export default function Interpolate({ diff --git a/packages/docusaurus/src/client/exports/Link.tsx b/packages/docusaurus/src/client/exports/Link.tsx index 6850f23761..53da88a791 100644 --- a/packages/docusaurus/src/client/exports/Link.tsx +++ b/packages/docusaurus/src/client/exports/Link.tsx @@ -103,35 +103,29 @@ function Link( const IOSupported = ExecutionEnvironment.canUseIntersectionObserver; const ioRef = useRef(); - const handleIntersection = (el: HTMLAnchorElement, cb: () => void) => { - ioRef.current = new window.IntersectionObserver((entries) => { - entries.forEach((entry) => { - if (el === entry.target) { - // If element is in viewport, stop observing and run callback. - // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API - if (entry.isIntersecting || entry.intersectionRatio > 0) { - ioRef.current!.unobserve(el); - ioRef.current!.disconnect(); - cb(); - } - } - }); - }); - // Add element to the observer. - ioRef.current!.observe(el); - }; + const handleRef = (el: HTMLAnchorElement | null) => { + innerRef.current = el; - const handleRef = (ref: HTMLAnchorElement | null) => { - innerRef.current = ref; - - if (IOSupported && ref && isInternal) { + if (IOSupported && el && isInternal) { // If IO supported and element reference found, set up Observer. - handleIntersection(ref, () => { - if (targetLink != null) { - window.docusaurus.prefetch(targetLink); - } + ioRef.current = new window.IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (el === entry.target) { + // If element is in viewport, stop observing and run callback. + // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API + if (entry.isIntersecting || entry.intersectionRatio > 0) { + ioRef.current!.unobserve(el); + ioRef.current!.disconnect(); + if (targetLink != null) { + window.docusaurus.prefetch(targetLink); + } + } + } + }); }); + // Add element to the observer. + ioRef.current.observe(el); } }; @@ -161,8 +155,8 @@ function Link( const isAnchorLink = targetLink?.startsWith('#') ?? false; const isRegularHtmlLink = !targetLink || !isInternal || isAnchorLink; - if (targetLink && isInternal && !isAnchorLink && !noBrokenLinkCheck) { - linksCollector.collectLink(targetLink); + if (!isRegularHtmlLink && !noBrokenLinkCheck) { + linksCollector.collectLink(targetLink!); } return isRegularHtmlLink ? ( diff --git a/packages/docusaurus/src/commands/swizzle/__tests__/index.test.ts b/packages/docusaurus/src/commands/swizzle/__tests__/index.test.ts index 7faa5c74c9..0f55edac83 100644 --- a/packages/docusaurus/src/commands/swizzle/__tests__/index.test.ts +++ b/packages/docusaurus/src/commands/swizzle/__tests__/index.test.ts @@ -91,7 +91,7 @@ async function createTestSite() { const siteThemePathPosix = posixPath(siteThemePath); expect(tree(siteThemePathPosix)).toMatchSnapshot('theme dir tree'); - const files = Globby.sync(siteThemePathPosix) + const files = (await Globby(siteThemePathPosix)) .map((file) => path.posix.relative(siteThemePathPosix, file)) .sort(); diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap index b895467529..80de4e006f 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap @@ -12,22 +12,10 @@ This could lead to non-deterministic routing behavior." exports[`loadRoutes loads flat route config 1`] = ` { "registry": { - "__comp---theme-blog-list-pagea-6-a-7ba": { - "loader": "() => import(/* webpackChunkName: '__comp---theme-blog-list-pagea-6-a-7ba' */ '@theme/BlogListPage')", - "modulePath": "@theme/BlogListPage", - }, - "content---blog-0-b-4-09e": { - "loader": "() => import(/* webpackChunkName: 'content---blog-0-b-4-09e' */ 'blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true')", - "modulePath": "blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true", - }, - "content---blog-7-b-8-fd9": { - "loader": "() => import(/* webpackChunkName: 'content---blog-7-b-8-fd9' */ 'blog/2018-12-14-Happy-First-Birthday-Slash.md')", - "modulePath": "blog/2018-12-14-Happy-First-Birthday-Slash.md", - }, - "metadata---blog-0-b-6-74c": { - "loader": "() => import(/* webpackChunkName: 'metadata---blog-0-b-6-74c' */ 'blog-2018-12-14-happy-first-birthday-slash-d2c.json')", - "modulePath": "blog-2018-12-14-happy-first-birthday-slash-d2c.json", - }, + "__comp---theme-blog-list-pagea-6-a-7ba": "@theme/BlogListPage", + "content---blog-0-b-4-09e": "blog/2018-12-14-Happy-First-Birthday-Slash.md?truncated=true", + "content---blog-7-b-8-fd9": "blog/2018-12-14-Happy-First-Birthday-Slash.md", + "metadata---blog-0-b-6-74c": "blog-2018-12-14-happy-first-birthday-slash-d2c.json", }, "routesChunkNames": { "/blog-599": { @@ -71,34 +59,13 @@ export default [ exports[`loadRoutes loads nested route config 1`] = ` { "registry": { - "__comp---theme-doc-item-178-a40": { - "loader": "() => import(/* webpackChunkName: '__comp---theme-doc-item-178-a40' */ '@theme/DocItem')", - "modulePath": "@theme/DocItem", - }, - "__comp---theme-doc-page-1-be-9be": { - "loader": "() => import(/* webpackChunkName: '__comp---theme-doc-page-1-be-9be' */ '@theme/DocPage')", - "modulePath": "@theme/DocPage", - }, - "content---docs-foo-baz-8-ce-61e": { - "loader": "() => import(/* webpackChunkName: 'content---docs-foo-baz-8-ce-61e' */ 'docs/foo/baz.md')", - "modulePath": "docs/foo/baz.md", - }, - "content---docs-helloaff-811": { - "loader": "() => import(/* webpackChunkName: 'content---docs-helloaff-811' */ 'docs/hello.md')", - "modulePath": "docs/hello.md", - }, - "docsMetadata---docs-routef-34-881": { - "loader": "() => import(/* webpackChunkName: 'docsMetadata---docs-routef-34-881' */ 'docs-b5f.json')", - "modulePath": "docs-b5f.json", - }, - "metadata---docs-foo-baz-2-cf-fa7": { - "loader": "() => import(/* webpackChunkName: 'metadata---docs-foo-baz-2-cf-fa7' */ 'docs-foo-baz-dd9.json')", - "modulePath": "docs-foo-baz-dd9.json", - }, - "metadata---docs-hello-956-741": { - "loader": "() => import(/* webpackChunkName: 'metadata---docs-hello-956-741' */ 'docs-hello-da2.json')", - "modulePath": "docs-hello-da2.json", - }, + "__comp---theme-doc-item-178-a40": "@theme/DocItem", + "__comp---theme-doc-page-1-be-9be": "@theme/DocPage", + "content---docs-foo-baz-8-ce-61e": "docs/foo/baz.md", + "content---docs-helloaff-811": "docs/hello.md", + "docsMetadata---docs-routef-34-881": "docs-b5f.json", + "metadata---docs-foo-baz-2-cf-fa7": "docs-foo-baz-dd9.json", + "metadata---docs-hello-956-741": "docs-hello-da2.json", }, "routesChunkNames": { "/docs/hello-44b": { @@ -159,10 +126,7 @@ export default [ exports[`loadRoutes loads route config with empty (but valid) path string 1`] = ` { "registry": { - "__comp---hello-world-jse-0-f-b6c": { - "loader": "() => import(/* webpackChunkName: '__comp---hello-world-jse-0-f-b6c' */ 'hello/world.js')", - "modulePath": "hello/world.js", - }, + "__comp---hello-world-jse-0-f-b6c": "hello/world.js", }, "routesChunkNames": { "-b2a": { diff --git a/packages/docusaurus/src/server/__tests__/routes.test.ts b/packages/docusaurus/src/server/__tests__/routes.test.ts index faf37563fd..1e8062a696 100644 --- a/packages/docusaurus/src/server/__tests__/routes.test.ts +++ b/packages/docusaurus/src/server/__tests__/routes.test.ts @@ -101,7 +101,7 @@ describe('handleDuplicateRoutes', () => { }); describe('loadRoutes', () => { - it('loads nested route config', async () => { + it('loads nested route config', () => { const nestedRouteConfig: RouteConfig = { component: '@theme/DocPage', path: '/docs:route', @@ -135,12 +135,10 @@ describe('loadRoutes', () => { }, ], }; - await expect( - loadRoutes([nestedRouteConfig], '/', 'ignore'), - ).resolves.toMatchSnapshot(); + expect(loadRoutes([nestedRouteConfig], '/', 'ignore')).toMatchSnapshot(); }); - it('loads flat route config', async () => { + it('loads flat route config', () => { const flatRouteConfig: RouteConfig = { path: '/blog', component: '@theme/BlogListPage', @@ -169,17 +167,15 @@ describe('loadRoutes', () => { ], }, }; - await expect( - loadRoutes([flatRouteConfig], '/', 'ignore'), - ).resolves.toMatchSnapshot(); + expect(loadRoutes([flatRouteConfig], '/', 'ignore')).toMatchSnapshot(); }); - it('rejects invalid route config', async () => { + it('rejects invalid route config', () => { const routeConfigWithoutPath = { component: 'hello/world.js', } as RouteConfig; - await expect(loadRoutes([routeConfigWithoutPath], '/', 'ignore')).rejects + expect(() => loadRoutes([routeConfigWithoutPath], '/', 'ignore')) .toThrowErrorMatchingInlineSnapshot(` "Invalid route config: path must be a string and component is required. {\\"component\\":\\"hello/world.js\\"}" @@ -189,21 +185,19 @@ describe('loadRoutes', () => { path: '/hello/world', } as RouteConfig; - await expect(loadRoutes([routeConfigWithoutComponent], '/', 'ignore')) - .rejects.toThrowErrorMatchingInlineSnapshot(` + expect(() => loadRoutes([routeConfigWithoutComponent], '/', 'ignore')) + .toThrowErrorMatchingInlineSnapshot(` "Invalid route config: path must be a string and component is required. {\\"path\\":\\"/hello/world\\"}" `); }); - it('loads route config with empty (but valid) path string', async () => { + it('loads route config with empty (but valid) path string', () => { const routeConfig = { path: '', component: 'hello/world.js', } as RouteConfig; - await expect( - loadRoutes([routeConfig], '/', 'ignore'), - ).resolves.toMatchSnapshot(); + expect(loadRoutes([routeConfig], '/', 'ignore')).toMatchSnapshot(); }); }); diff --git a/packages/docusaurus/src/server/brokenLinks.ts b/packages/docusaurus/src/server/brokenLinks.ts index c213ca19ef..c8806fa842 100644 --- a/packages/docusaurus/src/server/brokenLinks.ts +++ b/packages/docusaurus/src/server/brokenLinks.ts @@ -53,7 +53,7 @@ function getPageBrokenLinks({ // component, but we load route components with string paths. // We don't actually access component here, so it's fine. .map((l) => matchRoutes(routes, l)) - .reduce((prev, cur) => prev.concat(cur)); + .flat(); return matchedRoutes.length === 0; } diff --git a/packages/docusaurus/src/server/i18n.ts b/packages/docusaurus/src/server/i18n.ts index e171b21f78..b2d5dfe570 100644 --- a/packages/docusaurus/src/server/i18n.ts +++ b/packages/docusaurus/src/server/i18n.ts @@ -51,9 +51,8 @@ Note: Docusaurus only support running one locale at a time.`; }; } - const localeConfigs = locales.reduce( - (acc, locale) => ({...acc, [locale]: getLocaleConfig(locale)}), - {}, + const localeConfigs = Object.fromEntries( + locales.map((locale) => [locale, getLocaleConfig(locale)]), ); return { diff --git a/packages/docusaurus/src/server/index.ts b/packages/docusaurus/src/server/index.ts index fecb98e311..e971d13263 100644 --- a/packages/docusaurus/src/server/index.ts +++ b/packages/docusaurus/src/server/index.ts @@ -16,7 +16,6 @@ import { import _ from 'lodash'; import path from 'path'; import {loadSiteConfig} from './config'; -import ssrDefaultTemplate from '../webpack/templates/ssr.html.template'; import {loadClientModules} from './clientModules'; import {loadPlugins} from './plugins'; import {loadRoutes} from './routes'; @@ -101,7 +100,6 @@ export async function loadContext( outDir, baseUrl, i18n, - ssrTemplate: siteConfig.ssrTemplate ?? ssrDefaultTemplate, codeTranslations, }; } @@ -122,18 +120,16 @@ export async function load(options: LoadContextOptions): Promise { outDir, baseUrl, i18n, - ssrTemplate, codeTranslations: siteCodeTranslations, } = context; const {plugins, pluginsRouteConfigs, globalData} = await loadPlugins(context); const clientModules = loadClientModules(plugins); const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins); - const {registry, routesChunkNames, routesConfig, routesPaths} = - await loadRoutes( - pluginsRouteConfigs, - baseUrl, - siteConfig.onDuplicateRoutes, - ); + const {registry, routesChunkNames, routesConfig, routesPaths} = loadRoutes( + pluginsRouteConfigs, + baseUrl, + siteConfig.onDuplicateRoutes, + ); const codeTranslations = { ...(await getPluginsDefaultCodeTranslationMessages(plugins)), ...siteCodeTranslations, @@ -153,8 +149,6 @@ next build. You can clear all build artifacts (including this folder) with the `, ); - // Site config must be generated after plugins - // We want the generated config to have been normalized by the plugins! const genSiteConfig = generate( generatedFilesDir, DEFAULT_CONFIG_FILE_NAME, @@ -174,7 +168,7 @@ export default ${JSON.stringify(siteConfig, null, 2)}; ${clientModules // import() is async so we use require() because client modules can have // CSS and the order matters for loading CSS. - .map((module) => ` require('${escapePath(module)}'),`) + .map((clientModule) => ` require('${escapePath(clientModule)}'),`) .join('\n')} ]; `, @@ -187,10 +181,8 @@ ${clientModules ${Object.entries(registry) .sort((a, b) => a[0].localeCompare(b[0])) .map( - ([key, chunk]) => - ` '${key}': [${chunk.loader}, '${escapePath( - chunk.modulePath, - )}', require.resolveWeak('${escapePath(chunk.modulePath)}')],`, + ([chunkName, modulePath]) => + ` '${chunkName}': [() => import(/* webpackChunkName: '${chunkName}' */ '${modulePath}'), '${modulePath}', require.resolveWeak('${modulePath}')],`, ) .join('\n')}}; `, @@ -256,7 +248,6 @@ ${Object.entries(registry) headTags, preBodyTags, postBodyTags, - ssrTemplate, codeTranslations, }; } diff --git a/packages/docusaurus/src/server/routes.ts b/packages/docusaurus/src/server/routes.ts index 22fb44bbd7..6350e21660 100644 --- a/packages/docusaurus/src/server/routes.ts +++ b/packages/docusaurus/src/server/routes.ts @@ -29,9 +29,12 @@ type LoadedRoutes = { routesConfig: string; /** @see {ChunkNames} */ routesChunkNames: RouteChunkNames; - /** A map from chunk name to module loaders. */ + /** + * A map from chunk name to module paths. Module paths would have backslash + * escaped already, so they can be directly printed. + */ registry: { - [chunkName: string]: {loader: string; modulePath: string}; + [chunkName: string]: string; }; /** * Collect all page paths for injecting it later in the plugin lifecycle. @@ -195,12 +198,7 @@ function genChunkNames( // This is a leaf node, no need to recurse const modulePath = getModulePath(routeModule); const chunkName = genChunkName(modulePath, prefix, name); - res.registry[chunkName] = { - loader: `() => import(/* webpackChunkName: '${chunkName}' */ '${escapePath( - modulePath, - )}')`, - modulePath, - }; + res.registry[chunkName] = escapePath(modulePath); return chunkName; } if (Array.isArray(routeModule)) { @@ -294,11 +292,11 @@ ${JSON.stringify(routeConfig)}`, * chunk names. * - `registry`, a mapping from chunk names to options for react-loadable. */ -export async function loadRoutes( +export function loadRoutes( routeConfigs: RouteConfig[], baseUrl: string, onDuplicateRoutes: ReportingSeverity, -): Promise { +): LoadedRoutes { handleDuplicateRoutes(routeConfigs, onDuplicateRoutes); const res: LoadedRoutes = { // To be written by `genRouteCode` @@ -308,11 +306,16 @@ export async function loadRoutes( routesPaths: [normalizeUrl([baseUrl, '404.html'])], }; + // `genRouteCode` would mutate `res` + const routeConfigSerialized = routeConfigs + .map((r) => genRouteCode(r, res)) + .join(',\n'); + res.routesConfig = `import React from 'react'; import ComponentCreator from '@docusaurus/ComponentCreator'; export default [ -${indent(`${routeConfigs.map((r) => genRouteCode(r, res)).join(',\n')},`)} +${indent(routeConfigSerialized)}, { path: '*', component: ComponentCreator('*'), diff --git a/packages/docusaurus/src/server/siteMetadata.ts b/packages/docusaurus/src/server/siteMetadata.ts index 199dbc034a..ec855f94d5 100644 --- a/packages/docusaurus/src/server/siteMetadata.ts +++ b/packages/docusaurus/src/server/siteMetadata.ts @@ -81,8 +81,7 @@ function checkDocusaurusPackagesVersion(siteMetadata: SiteMetadata) { versionInfo.version && versionInfo.version !== docusaurusVersion ) { - // should we throw instead? - // It still could work with different versions + // Should we throw instead? It still could work with different versions logger.error`Invalid name=${plugin} version number=${versionInfo.version}. All official @docusaurus/* packages should have the exact same version as @docusaurus/core (number=${docusaurusVersion}). Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`; diff --git a/packages/docusaurus/src/webpack/server.ts b/packages/docusaurus/src/webpack/server.ts index 3adc362a41..93cb258c34 100644 --- a/packages/docusaurus/src/webpack/server.ts +++ b/packages/docusaurus/src/webpack/server.ts @@ -14,6 +14,7 @@ import {createBaseConfig} from './base'; import WaitPlugin from './plugins/WaitPlugin'; import LogPlugin from './plugins/LogPlugin'; import {NODE_MAJOR_VERSION, NODE_MINOR_VERSION} from '@docusaurus/utils'; +import ssrDefaultTemplate from './templates/ssr.html.template'; // Forked for Docusaurus: https://github.com/slorber/static-site-generator-webpack-plugin import StaticSiteGeneratorPlugin from '@slorber/static-site-generator-webpack-plugin'; @@ -32,8 +33,7 @@ export default async function createServerConfig({ headTags, preBodyTags, postBodyTags, - ssrTemplate, - siteConfig: {noIndex, trailingSlash}, + siteConfig: {noIndex, trailingSlash, ssrTemplate}, } = props; const config = await createBaseConfig(props, true); @@ -73,7 +73,7 @@ export default async function createServerConfig({ preBodyTags, postBodyTags, onLinksCollected, - ssrTemplate, + ssrTemplate: ssrTemplate ?? ssrDefaultTemplate, noIndex, }, paths: ssgPaths,