Skip to content

Commit

Permalink
Move eta, html-minifier, lodash and other dependencies outside the se…
Browse files Browse the repository at this point in the history
…rver bundle
  • Loading branch information
slorber committed Feb 2, 2024
1 parent 5e7692d commit 7cab720
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 89 deletions.
90 changes: 5 additions & 85 deletions packages/docusaurus/src/client/serverEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@
*/

import React from 'react';
// eslint-disable-next-line no-restricted-imports
import _ from 'lodash';
import * as eta from 'eta';
import {StaticRouter} from 'react-router-dom';
import {HelmetProvider, type FilledContext} from 'react-helmet-async';
import {getBundles} from 'react-loadable-ssr-addon-v5-slorber';
import Loadable from 'react-loadable';
import {minify} from 'html-minifier-terser';
import {renderStaticApp} from './serverRenderer';
import preload from './preload';
import App from './App';
Expand All @@ -23,17 +18,6 @@ import {
} from './BrokenLinksContext';
import type {PageCollectedData, ServerEntryRenderer} from '../types';

const getCompiledSSRTemplate = _.memoize((template: string) =>
eta.compile(template.trim(), {
rmWhitespace: true,
}),
);

function renderSSRTemplate(ssrTemplate: string, data: object) {
const compiled = getCompiledSSRTemplate(ssrTemplate);
return compiled(data, eta.defaultConfig);
}

function buildSSRErrorMessage({
error,
pathname,
Expand All @@ -58,23 +42,12 @@ It might also require to wrap your client code in \`useEffect\` hook and/or impo
return parts.join('\n');
}

const doRender: ServerEntryRenderer = async ({pathname, serverEntryParams}) => {
const {
headTags,
preBodyTags,
postBodyTags,
baseUrl,
ssrTemplate,
noIndex,
DOCUSAURUS_VERSION,
manifest,
} = serverEntryParams;

const doRender: ServerEntryRenderer = async ({pathname}) => {
await preload(pathname);

const modules = new Set<string>();
const routerContext = {};
const helmetContext = {};

const statefulBrokenLinks = createStatefulBrokenLinks();

const app = (
Expand All @@ -90,50 +63,17 @@ const doRender: ServerEntryRenderer = async ({pathname, serverEntryParams}) => {
</Loadable.Capture>
);

const appHtml = await renderStaticApp(app);
const html = await renderStaticApp(app);
const {helmet} = helmetContext as FilledContext;

const collectedData: PageCollectedData = {
anchors: statefulBrokenLinks.getCollectedAnchors(),
links: statefulBrokenLinks.getCollectedLinks(),
headTags: helmet,
modules: Array.from(modules),
};

const htmlAttributes = helmet.htmlAttributes.toString();
const bodyAttributes = helmet.bodyAttributes.toString();
const metaStrings = [
helmet.title.toString(),
helmet.meta.toString(),
helmet.link.toString(),
helmet.script.toString(),
];
const metaAttributes = metaStrings.filter(Boolean);

// Get all required assets for this particular page based on client
// manifest information.
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
const bundles = getBundles(manifest, modulesToBeLoaded);
const stylesheets = (bundles.css ?? []).map((b) => b.file);
const scripts = (bundles.js ?? []).map((b) => b.file);

const renderedHtml = renderSSRTemplate(ssrTemplate, {
appHtml,
baseUrl,
htmlAttributes,
bodyAttributes,
headTags,
preBodyTags,
postBodyTags,
metaAttributes,
scripts,
stylesheets,
noIndex,
version: DOCUSAURUS_VERSION,
});

const minifiedHtml = await minifyHtml(renderedHtml);

return {html: minifiedHtml, collectedData};
return {html, collectedData};
};

const render: ServerEntryRenderer = async (params) => {
Expand All @@ -147,23 +87,3 @@ const render: ServerEntryRenderer = async (params) => {
};

export default render;

async function minifyHtml(html: string): Promise<string> {
try {
if (process.env.SKIP_HTML_MINIFICATION === 'true') {
return html;
}
// Minify html with https://github.com/DanielRuf/html-minifier-terser
return await minify(html, {
removeComments: false,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyJS: true,
});
} catch (err) {
throw new Error('HTML minification failed', {cause: err as Error});
}
}
112 changes: 109 additions & 3 deletions packages/docusaurus/src/ssg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import path from 'path';
import _ from 'lodash';
import evaluate from 'eval';
import pMap from 'p-map';
import eta from 'eta';
import {minify} from 'html-minifier-terser';
import {getBundles} from 'react-loadable-ssr-addon-v5-slorber';
import {PerfLogger} from './utils';
import type {Manifest} from 'react-loadable-ssr-addon-v5-slorber';
import type {
ServerEntryRenderer,
ServerEntryResult,
Expand Down Expand Up @@ -161,13 +165,20 @@ async function generateStaticFile({
serverEntryParams: ServerEntryParams;
}) {
try {
const result = await renderer({pathname, serverEntryParams});
// This only renders the app HTML
const serverEntryResult = await renderer({pathname, serverEntryParams});
// This renders the full page HTML, including head tags...
const fullPageHtml = renderSSRTemplate({
serverEntryParams,
serverEntryResult,
});
const content = await minifyHtml(fullPageHtml);
await writeStaticFile({
pathname,
content: result.html,
content,
serverEntryParams,
});
return result;
return serverEntryResult;
} catch (errorUnknown) {
throw new Error(`Can't render static file for pathname=${pathname}`, {
cause: errorUnknown as Error,
Expand Down Expand Up @@ -198,3 +209,98 @@ function removeBaseUrl(pathname: string, baseUrl: string): string {
? pathname
: pathname.replace(new RegExp(`^${baseUrl}`), '/');
}

function getScriptsAndStylesheets({
modules,
manifest,
}: {
modules: string[];
manifest: Manifest;
}) {
// Get all required assets for this particular page based on client
// manifest information.
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
const bundles = getBundles(manifest, modulesToBeLoaded);
const stylesheets = (bundles.css ?? []).map((b) => b.file);
const scripts = (bundles.js ?? []).map((b) => b.file);
return {scripts, stylesheets};
}

const getCompiledSSRTemplate = _.memoize((template: string) =>
eta.compile(template.trim(), {
rmWhitespace: true,
}),
);

function renderSSRTemplate({
serverEntryParams,
serverEntryResult,
}: {
serverEntryParams: ServerEntryParams;
serverEntryResult: ServerEntryResult;
}) {
const {
ssrTemplate,
baseUrl,
headTags,
preBodyTags,
postBodyTags,
manifest,
noIndex,
DOCUSAURUS_VERSION,
} = serverEntryParams;
const {
html: appHtml,
collectedData: {modules, headTags: helmet},
} = serverEntryResult;

const {scripts, stylesheets} = getScriptsAndStylesheets({manifest, modules});

const htmlAttributes = helmet.htmlAttributes.toString();
const bodyAttributes = helmet.bodyAttributes.toString();
const metaStrings = [
helmet.title.toString(),
helmet.meta.toString(),
helmet.link.toString(),
helmet.script.toString(),
];
const metaAttributes = metaStrings.filter(Boolean);

const templateData = {
appHtml,
baseUrl,
htmlAttributes,
bodyAttributes,
headTags,
preBodyTags,
postBodyTags,
metaAttributes,
scripts,
stylesheets,
noIndex,
version: DOCUSAURUS_VERSION,
};

const compiled = getCompiledSSRTemplate(ssrTemplate);
return compiled(templateData, eta.defaultConfig);
}

async function minifyHtml(html: string): Promise<string> {
try {
if (process.env.SKIP_HTML_MINIFICATION === 'true') {
return html;
}
// Minify html with https://github.com/DanielRuf/html-minifier-terser
return await minify(html, {
removeComments: false,
removeRedundantAttributes: true,
removeEmptyAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyJS: true,
});
} catch (err) {
throw new Error('HTML minification failed', {cause: err as Error});
}
}
3 changes: 2 additions & 1 deletion packages/docusaurus/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ export type ServerEntryRenderer = (params: {
}) => Promise<ServerEntryResult>;

export type PageCollectedData = {
headTags: HelmetServerState;
links: string[];
anchors: string[];
headTags: HelmetServerState;
modules: string[];
};

export type SiteCollectedData = {
Expand Down

0 comments on commit 7cab720

Please sign in to comment.