refactor(core): code cleanup (#7084)

This commit is contained in:
Joshua Chen 2022-04-01 13:41:39 +08:00 committed by GitHub
parent ff96606865
commit 898611d4ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 114 additions and 179 deletions

View file

@ -227,7 +227,6 @@ export type LoadContext = {
*/ */
baseUrl: string; baseUrl: string;
i18n: I18n; i18n: I18n;
ssrTemplate: string;
codeTranslations: CodeTranslations; codeTranslations: CodeTranslations;
}; };

View file

@ -12,7 +12,7 @@ import renderRoutes from './exports/renderRoutes';
import {BrowserContextProvider} from './browserContext'; import {BrowserContextProvider} from './browserContext';
import {DocusaurusContextProvider} from './docusaurusContext'; import {DocusaurusContextProvider} from './docusaurusContext';
import PendingNavigation from './PendingNavigation'; import PendingNavigation from './PendingNavigation';
import BaseUrlIssueBanner from './baseUrlIssueBanner/BaseUrlIssueBanner'; import BaseUrlIssueBanner from './BaseUrlIssueBanner';
import SiteMetadataDefaults from './SiteMetadataDefaults'; import SiteMetadataDefaults from './SiteMetadataDefaults';
import Root from '@theme/Root'; import Root from '@theme/Root';
import SiteMetadata from '@theme/SiteMetadata'; import SiteMetadata from '@theme/SiteMetadata';

View file

@ -74,11 +74,11 @@ function insertBanner() {
declare global { declare global {
interface Window { interface Window {
__DOCUSAURUS_INSERT_BASEURL_BANNER: boolean; [InsertBannerWindowAttribute]: boolean;
} }
} }
function BaseUrlIssueBannerEnabled() { function BaseUrlIssueBanner() {
const { const {
siteConfig: {baseUrl}, siteConfig: {baseUrl},
} = useDocusaurusContext(); } = useDocusaurusContext();
@ -92,6 +92,8 @@ function BaseUrlIssueBannerEnabled() {
return ( return (
<> <>
{!ExecutionEnvironment.canUseDOM && ( {!ExecutionEnvironment.canUseDOM && (
// Safe to use `ExecutionEnvironment`, because `Head` is purely
// side-effect and doesn't affect hydration
<Head> <Head>
<script>{createInlineScript(baseUrl)}</script> <script>{createInlineScript(baseUrl)}</script>
</Head> </Head>
@ -103,22 +105,22 @@ function BaseUrlIssueBannerEnabled() {
/** /**
* We want to help the users with a bad baseUrl configuration (very common * 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: 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 * @see https://github.com/facebook/docusaurus/pull/3621
*/ */
export default function BaseUrlIssueBanner(): JSX.Element | null { export default function MaybeBaseUrlIssueBanner(): JSX.Element | null {
const { const {
siteConfig: {baseUrl, baseUrlIssueBanner}, siteConfig: {baseUrl, baseUrlIssueBanner},
} = useDocusaurusContext(); } = useDocusaurusContext();
const {pathname} = useLocation(); const {pathname} = useLocation();
// returns true for the homepage during SSR
// returns true for the homepage during SRR
const isHomePage = pathname === baseUrl; const isHomePage = pathname === baseUrl;
const enabled = baseUrlIssueBanner && isHomePage; const enabled = baseUrlIssueBanner && isHomePage;
return enabled ? <BaseUrlIssueBanner /> : null;
return enabled ? <BaseUrlIssueBannerEnabled /> : null;
} }

View file

@ -7,16 +7,14 @@
import React, {isValidElement} from 'react'; import React, {isValidElement} from 'react';
import useIsBrowser from '@docusaurus/useIsBrowser'; import useIsBrowser from '@docusaurus/useIsBrowser';
import type {Props} from '@docusaurus/BrowserOnly';
// Similar comp to the one described here: // Similar comp to the one described here:
// https://www.joshwcomeau.com/react/the-perils-of-rehydration/#abstractions // https://www.joshwcomeau.com/react/the-perils-of-rehydration/#abstractions
export default function BrowserOnly({ export default function BrowserOnly({
children, children,
fallback, fallback,
}: { }: Props): JSX.Element | null {
children: () => JSX.Element;
fallback?: JSX.Element;
}): JSX.Element | null {
const isBrowser = useIsBrowser(); const isBrowser = useIsBrowser();
if (isBrowser) { if (isBrowser) {
@ -27,7 +25,7 @@ export default function BrowserOnly({
throw new Error(`Docusaurus error: The children of <BrowserOnly> must be a "render function", e.g. <BrowserOnly>{() => <span>{window.location.href}</span>}</BrowserOnly>. throw new Error(`Docusaurus error: The children of <BrowserOnly> must be a "render function", e.g. <BrowserOnly>{() => <span>{window.location.href}</span>}</BrowserOnly>.
Current type: ${isValidElement(children) ? 'React element' : typeof children}`); Current type: ${isValidElement(children) ? 'React element' : typeof children}`);
} }
return <>{children()}</>; return <>{children?.()}</>;
} }
return fallback ?? null; return fallback ?? null;

View file

@ -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 More details here: https://github.com/facebook/docusaurus/pull/4295
*/ */
const ValueRegexp = /\{\w+\}/g;
const ValueFoundMarker = '{}'; // does not care much const ValueFoundMarker = '{}'; // does not care much
// If all the values are plain strings, then interpolate returns a simple string // If all the values are plain strings, then interpolate returns a simple string
@ -39,51 +38,44 @@ export function interpolate<Str extends string, Value extends ReactNode>(
): ReactNode { ): ReactNode {
const elements: (Value | string)[] = []; const elements: (Value | string)[] = [];
const processedText = text.replace(ValueRegexp, (match: string) => { const processedText = text.replace(
// remove {{ and }} around the placeholder // eslint-disable-next-line prefer-named-capture-group
const key = match.substring( /\{(\w+)\}/g,
1, (match, key: ExtractInterpolatePlaceholders<Str>) => {
match.length - 1, const value = values?.[key];
) as ExtractInterpolatePlaceholders<Str>;
const value = values?.[key]; if (typeof value !== 'undefined') {
const element = isValidElement(value)
if (typeof value !== 'undefined') { ? value
const element = isValidElement(value) : // For non-React elements: basic primitive->string conversion
? value String(value);
: // For non-React elements: basic primitive->string conversion elements.push(element);
String(value); return ValueFoundMarker;
elements.push(element); }
return ValueFoundMarker; return match; // no match? add warning?
} },
return match; // no match? add warning? );
});
// No interpolation to be done: just return the text // No interpolation to be done: just return the text
if (elements.length === 0) { if (elements.length === 0) {
return text; return text;
} }
// Basic string interpolation: returns interpolated string // 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 return processedText
.split(ValueFoundMarker) .split(ValueFoundMarker)
.reduce<string>( .reduce<string>(
(str, value, index) => (str, value, index) => str.concat(value).concat(elements[index] ?? ''),
str.concat(value).concat((elements[index] as string) ?? ''),
'', '',
); );
} }
// JSX interpolation: returns ReactNode // JSX interpolation: returns ReactNode
return processedText.split(ValueFoundMarker).reduce<ReactNode[]>( return processedText.split(ValueFoundMarker).map((value, index) => (
(array, value, index) => [ <React.Fragment key={index}>
...array, {value}
<React.Fragment key={index}> {elements[index]}
{value} </React.Fragment>
{elements[index]} ));
</React.Fragment>,
],
[],
);
} }
export default function Interpolate<Str extends string>({ export default function Interpolate<Str extends string>({

View file

@ -103,35 +103,29 @@ function Link(
const IOSupported = ExecutionEnvironment.canUseIntersectionObserver; const IOSupported = ExecutionEnvironment.canUseIntersectionObserver;
const ioRef = useRef<IntersectionObserver>(); const ioRef = useRef<IntersectionObserver>();
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. const handleRef = (el: HTMLAnchorElement | null) => {
ioRef.current!.observe(el); innerRef.current = el;
};
const handleRef = (ref: HTMLAnchorElement | null) => { if (IOSupported && el && isInternal) {
innerRef.current = ref;
if (IOSupported && ref && isInternal) {
// If IO supported and element reference found, set up Observer. // If IO supported and element reference found, set up Observer.
handleIntersection(ref, () => { ioRef.current = new window.IntersectionObserver((entries) => {
if (targetLink != null) { entries.forEach((entry) => {
window.docusaurus.prefetch(targetLink); 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 isAnchorLink = targetLink?.startsWith('#') ?? false;
const isRegularHtmlLink = !targetLink || !isInternal || isAnchorLink; const isRegularHtmlLink = !targetLink || !isInternal || isAnchorLink;
if (targetLink && isInternal && !isAnchorLink && !noBrokenLinkCheck) { if (!isRegularHtmlLink && !noBrokenLinkCheck) {
linksCollector.collectLink(targetLink); linksCollector.collectLink(targetLink!);
} }
return isRegularHtmlLink ? ( return isRegularHtmlLink ? (

View file

@ -91,7 +91,7 @@ async function createTestSite() {
const siteThemePathPosix = posixPath(siteThemePath); const siteThemePathPosix = posixPath(siteThemePath);
expect(tree(siteThemePathPosix)).toMatchSnapshot('theme dir tree'); expect(tree(siteThemePathPosix)).toMatchSnapshot('theme dir tree');
const files = Globby.sync(siteThemePathPosix) const files = (await Globby(siteThemePathPosix))
.map((file) => path.posix.relative(siteThemePathPosix, file)) .map((file) => path.posix.relative(siteThemePathPosix, file))
.sort(); .sort();

View file

@ -12,22 +12,10 @@ This could lead to non-deterministic routing behavior."
exports[`loadRoutes loads flat route config 1`] = ` exports[`loadRoutes loads flat route config 1`] = `
{ {
"registry": { "registry": {
"__comp---theme-blog-list-pagea-6-a-7ba": { "__comp---theme-blog-list-pagea-6-a-7ba": "@theme/BlogListPage",
"loader": "() => import(/* webpackChunkName: '__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",
"modulePath": "@theme/BlogListPage", "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",
"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",
},
}, },
"routesChunkNames": { "routesChunkNames": {
"/blog-599": { "/blog-599": {
@ -71,34 +59,13 @@ export default [
exports[`loadRoutes loads nested route config 1`] = ` exports[`loadRoutes loads nested route config 1`] = `
{ {
"registry": { "registry": {
"__comp---theme-doc-item-178-a40": { "__comp---theme-doc-item-178-a40": "@theme/DocItem",
"loader": "() => import(/* webpackChunkName: '__comp---theme-doc-item-178-a40' */ '@theme/DocItem')", "__comp---theme-doc-page-1-be-9be": "@theme/DocPage",
"modulePath": "@theme/DocItem", "content---docs-foo-baz-8-ce-61e": "docs/foo/baz.md",
}, "content---docs-helloaff-811": "docs/hello.md",
"__comp---theme-doc-page-1-be-9be": { "docsMetadata---docs-routef-34-881": "docs-b5f.json",
"loader": "() => import(/* webpackChunkName: '__comp---theme-doc-page-1-be-9be' */ '@theme/DocPage')", "metadata---docs-foo-baz-2-cf-fa7": "docs-foo-baz-dd9.json",
"modulePath": "@theme/DocPage", "metadata---docs-hello-956-741": "docs-hello-da2.json",
},
"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",
},
}, },
"routesChunkNames": { "routesChunkNames": {
"/docs/hello-44b": { "/docs/hello-44b": {
@ -159,10 +126,7 @@ export default [
exports[`loadRoutes loads route config with empty (but valid) path string 1`] = ` exports[`loadRoutes loads route config with empty (but valid) path string 1`] = `
{ {
"registry": { "registry": {
"__comp---hello-world-jse-0-f-b6c": { "__comp---hello-world-jse-0-f-b6c": "hello/world.js",
"loader": "() => import(/* webpackChunkName: '__comp---hello-world-jse-0-f-b6c' */ 'hello/world.js')",
"modulePath": "hello/world.js",
},
}, },
"routesChunkNames": { "routesChunkNames": {
"-b2a": { "-b2a": {

View file

@ -101,7 +101,7 @@ describe('handleDuplicateRoutes', () => {
}); });
describe('loadRoutes', () => { describe('loadRoutes', () => {
it('loads nested route config', async () => { it('loads nested route config', () => {
const nestedRouteConfig: RouteConfig = { const nestedRouteConfig: RouteConfig = {
component: '@theme/DocPage', component: '@theme/DocPage',
path: '/docs:route', path: '/docs:route',
@ -135,12 +135,10 @@ describe('loadRoutes', () => {
}, },
], ],
}; };
await expect( expect(loadRoutes([nestedRouteConfig], '/', 'ignore')).toMatchSnapshot();
loadRoutes([nestedRouteConfig], '/', 'ignore'),
).resolves.toMatchSnapshot();
}); });
it('loads flat route config', async () => { it('loads flat route config', () => {
const flatRouteConfig: RouteConfig = { const flatRouteConfig: RouteConfig = {
path: '/blog', path: '/blog',
component: '@theme/BlogListPage', component: '@theme/BlogListPage',
@ -169,17 +167,15 @@ describe('loadRoutes', () => {
], ],
}, },
}; };
await expect( expect(loadRoutes([flatRouteConfig], '/', 'ignore')).toMatchSnapshot();
loadRoutes([flatRouteConfig], '/', 'ignore'),
).resolves.toMatchSnapshot();
}); });
it('rejects invalid route config', async () => { it('rejects invalid route config', () => {
const routeConfigWithoutPath = { const routeConfigWithoutPath = {
component: 'hello/world.js', component: 'hello/world.js',
} as RouteConfig; } as RouteConfig;
await expect(loadRoutes([routeConfigWithoutPath], '/', 'ignore')).rejects expect(() => loadRoutes([routeConfigWithoutPath], '/', 'ignore'))
.toThrowErrorMatchingInlineSnapshot(` .toThrowErrorMatchingInlineSnapshot(`
"Invalid route config: path must be a string and component is required. "Invalid route config: path must be a string and component is required.
{\\"component\\":\\"hello/world.js\\"}" {\\"component\\":\\"hello/world.js\\"}"
@ -189,21 +185,19 @@ describe('loadRoutes', () => {
path: '/hello/world', path: '/hello/world',
} as RouteConfig; } as RouteConfig;
await expect(loadRoutes([routeConfigWithoutComponent], '/', 'ignore')) expect(() => loadRoutes([routeConfigWithoutComponent], '/', 'ignore'))
.rejects.toThrowErrorMatchingInlineSnapshot(` .toThrowErrorMatchingInlineSnapshot(`
"Invalid route config: path must be a string and component is required. "Invalid route config: path must be a string and component is required.
{\\"path\\":\\"/hello/world\\"}" {\\"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 = { const routeConfig = {
path: '', path: '',
component: 'hello/world.js', component: 'hello/world.js',
} as RouteConfig; } as RouteConfig;
await expect( expect(loadRoutes([routeConfig], '/', 'ignore')).toMatchSnapshot();
loadRoutes([routeConfig], '/', 'ignore'),
).resolves.toMatchSnapshot();
}); });
}); });

View file

@ -53,7 +53,7 @@ function getPageBrokenLinks({
// component, but we load route components with string paths. // component, but we load route components with string paths.
// We don't actually access component here, so it's fine. // We don't actually access component here, so it's fine.
.map((l) => matchRoutes(routes, l)) .map((l) => matchRoutes(routes, l))
.reduce((prev, cur) => prev.concat(cur)); .flat();
return matchedRoutes.length === 0; return matchedRoutes.length === 0;
} }

View file

@ -51,9 +51,8 @@ Note: Docusaurus only support running one locale at a time.`;
}; };
} }
const localeConfigs = locales.reduce( const localeConfigs = Object.fromEntries(
(acc, locale) => ({...acc, [locale]: getLocaleConfig(locale)}), locales.map((locale) => [locale, getLocaleConfig(locale)]),
{},
); );
return { return {

View file

@ -16,7 +16,6 @@ import {
import _ from 'lodash'; import _ from 'lodash';
import path from 'path'; import path from 'path';
import {loadSiteConfig} from './config'; import {loadSiteConfig} from './config';
import ssrDefaultTemplate from '../webpack/templates/ssr.html.template';
import {loadClientModules} from './clientModules'; import {loadClientModules} from './clientModules';
import {loadPlugins} from './plugins'; import {loadPlugins} from './plugins';
import {loadRoutes} from './routes'; import {loadRoutes} from './routes';
@ -101,7 +100,6 @@ export async function loadContext(
outDir, outDir,
baseUrl, baseUrl,
i18n, i18n,
ssrTemplate: siteConfig.ssrTemplate ?? ssrDefaultTemplate,
codeTranslations, codeTranslations,
}; };
} }
@ -122,18 +120,16 @@ export async function load(options: LoadContextOptions): Promise<Props> {
outDir, outDir,
baseUrl, baseUrl,
i18n, i18n,
ssrTemplate,
codeTranslations: siteCodeTranslations, codeTranslations: siteCodeTranslations,
} = context; } = context;
const {plugins, pluginsRouteConfigs, globalData} = await loadPlugins(context); const {plugins, pluginsRouteConfigs, globalData} = await loadPlugins(context);
const clientModules = loadClientModules(plugins); const clientModules = loadClientModules(plugins);
const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins); const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins);
const {registry, routesChunkNames, routesConfig, routesPaths} = const {registry, routesChunkNames, routesConfig, routesPaths} = loadRoutes(
await loadRoutes( pluginsRouteConfigs,
pluginsRouteConfigs, baseUrl,
baseUrl, siteConfig.onDuplicateRoutes,
siteConfig.onDuplicateRoutes, );
);
const codeTranslations = { const codeTranslations = {
...(await getPluginsDefaultCodeTranslationMessages(plugins)), ...(await getPluginsDefaultCodeTranslationMessages(plugins)),
...siteCodeTranslations, ...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( const genSiteConfig = generate(
generatedFilesDir, generatedFilesDir,
DEFAULT_CONFIG_FILE_NAME, DEFAULT_CONFIG_FILE_NAME,
@ -174,7 +168,7 @@ export default ${JSON.stringify(siteConfig, null, 2)};
${clientModules ${clientModules
// import() is async so we use require() because client modules can have // import() is async so we use require() because client modules can have
// CSS and the order matters for loading CSS. // CSS and the order matters for loading CSS.
.map((module) => ` require('${escapePath(module)}'),`) .map((clientModule) => ` require('${escapePath(clientModule)}'),`)
.join('\n')} .join('\n')}
]; ];
`, `,
@ -187,10 +181,8 @@ ${clientModules
${Object.entries(registry) ${Object.entries(registry)
.sort((a, b) => a[0].localeCompare(b[0])) .sort((a, b) => a[0].localeCompare(b[0]))
.map( .map(
([key, chunk]) => ([chunkName, modulePath]) =>
` '${key}': [${chunk.loader}, '${escapePath( ` '${chunkName}': [() => import(/* webpackChunkName: '${chunkName}' */ '${modulePath}'), '${modulePath}', require.resolveWeak('${modulePath}')],`,
chunk.modulePath,
)}', require.resolveWeak('${escapePath(chunk.modulePath)}')],`,
) )
.join('\n')}}; .join('\n')}};
`, `,
@ -256,7 +248,6 @@ ${Object.entries(registry)
headTags, headTags,
preBodyTags, preBodyTags,
postBodyTags, postBodyTags,
ssrTemplate,
codeTranslations, codeTranslations,
}; };
} }

View file

@ -29,9 +29,12 @@ type LoadedRoutes = {
routesConfig: string; routesConfig: string;
/** @see {ChunkNames} */ /** @see {ChunkNames} */
routesChunkNames: RouteChunkNames; 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: { registry: {
[chunkName: string]: {loader: string; modulePath: string}; [chunkName: string]: string;
}; };
/** /**
* Collect all page paths for injecting it later in the plugin lifecycle. * 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 // This is a leaf node, no need to recurse
const modulePath = getModulePath(routeModule); const modulePath = getModulePath(routeModule);
const chunkName = genChunkName(modulePath, prefix, name); const chunkName = genChunkName(modulePath, prefix, name);
res.registry[chunkName] = { res.registry[chunkName] = escapePath(modulePath);
loader: `() => import(/* webpackChunkName: '${chunkName}' */ '${escapePath(
modulePath,
)}')`,
modulePath,
};
return chunkName; return chunkName;
} }
if (Array.isArray(routeModule)) { if (Array.isArray(routeModule)) {
@ -294,11 +292,11 @@ ${JSON.stringify(routeConfig)}`,
* chunk names. * chunk names.
* - `registry`, a mapping from chunk names to options for react-loadable. * - `registry`, a mapping from chunk names to options for react-loadable.
*/ */
export async function loadRoutes( export function loadRoutes(
routeConfigs: RouteConfig[], routeConfigs: RouteConfig[],
baseUrl: string, baseUrl: string,
onDuplicateRoutes: ReportingSeverity, onDuplicateRoutes: ReportingSeverity,
): Promise<LoadedRoutes> { ): LoadedRoutes {
handleDuplicateRoutes(routeConfigs, onDuplicateRoutes); handleDuplicateRoutes(routeConfigs, onDuplicateRoutes);
const res: LoadedRoutes = { const res: LoadedRoutes = {
// To be written by `genRouteCode` // To be written by `genRouteCode`
@ -308,11 +306,16 @@ export async function loadRoutes(
routesPaths: [normalizeUrl([baseUrl, '404.html'])], 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'; res.routesConfig = `import React from 'react';
import ComponentCreator from '@docusaurus/ComponentCreator'; import ComponentCreator from '@docusaurus/ComponentCreator';
export default [ export default [
${indent(`${routeConfigs.map((r) => genRouteCode(r, res)).join(',\n')},`)} ${indent(routeConfigSerialized)},
{ {
path: '*', path: '*',
component: ComponentCreator('*'), component: ComponentCreator('*'),

View file

@ -81,8 +81,7 @@ function checkDocusaurusPackagesVersion(siteMetadata: SiteMetadata) {
versionInfo.version && versionInfo.version &&
versionInfo.version !== docusaurusVersion versionInfo.version !== docusaurusVersion
) { ) {
// should we throw instead? // Should we throw instead? It still could work with different versions
// It still could work with different versions
logger.error`Invalid name=${plugin} version number=${versionInfo.version}. logger.error`Invalid name=${plugin} version number=${versionInfo.version}.
All official @docusaurus/* packages should have the exact same version as @docusaurus/core (number=${docusaurusVersion}). 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?`; Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`;

View file

@ -14,6 +14,7 @@ import {createBaseConfig} from './base';
import WaitPlugin from './plugins/WaitPlugin'; import WaitPlugin from './plugins/WaitPlugin';
import LogPlugin from './plugins/LogPlugin'; import LogPlugin from './plugins/LogPlugin';
import {NODE_MAJOR_VERSION, NODE_MINOR_VERSION} from '@docusaurus/utils'; 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 // Forked for Docusaurus: https://github.com/slorber/static-site-generator-webpack-plugin
import StaticSiteGeneratorPlugin from '@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, headTags,
preBodyTags, preBodyTags,
postBodyTags, postBodyTags,
ssrTemplate, siteConfig: {noIndex, trailingSlash, ssrTemplate},
siteConfig: {noIndex, trailingSlash},
} = props; } = props;
const config = await createBaseConfig(props, true); const config = await createBaseConfig(props, true);
@ -73,7 +73,7 @@ export default async function createServerConfig({
preBodyTags, preBodyTags,
postBodyTags, postBodyTags,
onLinksCollected, onLinksCollected,
ssrTemplate, ssrTemplate: ssrTemplate ?? ssrDefaultTemplate,
noIndex, noIndex,
}, },
paths: ssgPaths, paths: ssgPaths,