feat(v2): allow users to specify a custom ssr HTML template (#3387)

* feat(v2): allow users to specify a custom ssr HTML template

* docs(v2): add ssrTemplate property to docusaurus.config.js page
This commit is contained in:
Méril 2020-09-02 19:29:04 +02:00 committed by GitHub
parent 9857f7b2b5
commit 3ace60043b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 8 deletions

View file

@ -41,6 +41,7 @@ export interface DocusaurusConfig {
[key: string]: unknown; [key: string]: unknown;
} }
)[]; )[];
ssrTemplate?: string;
stylesheets?: ( stylesheets?: (
| string | string
| { | {
@ -110,6 +111,7 @@ export interface LoadContext {
siteConfig: DocusaurusConfig; siteConfig: DocusaurusConfig;
outDir: string; outDir: string;
baseUrl: string; baseUrl: string;
ssrTemplate?: string;
} }
export interface InjectedHtmlTags { export interface InjectedHtmlTags {

View file

@ -26,19 +26,18 @@ import {
createStatefulLinksCollector, createStatefulLinksCollector,
ProvideLinksCollector, ProvideLinksCollector,
} from './LinksCollector'; } from './LinksCollector';
import ssrTemplate from './templates/ssr.html.template';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import {memoize} from 'lodash'; import {memoize} from 'lodash';
const getCompiledSSRTemplate = memoize(() => { const getCompiledSSRTemplate = memoize((template) => {
return eta.compile(ssrTemplate.trim(), { return eta.compile(template.trim(), {
rmWhitespace: true, rmWhitespace: true,
}); });
}); });
function renderSSRTemplate(data) { function renderSSRTemplate(ssrTemplate, data) {
const compiled = getCompiledSSRTemplate(); const compiled = getCompiledSSRTemplate(ssrTemplate);
return compiled(data, eta.defaultConfig); return compiled(data, eta.defaultConfig);
} }
@ -51,6 +50,7 @@ export default async function render(locals) {
postBodyTags, postBodyTags,
onLinksCollected, onLinksCollected,
baseUrl, baseUrl,
ssrTemplate,
} = locals; } = locals;
const location = routesLocation[locals.path]; const location = routesLocation[locals.path];
await preload(routes, location); await preload(routes, location);
@ -90,7 +90,7 @@ export default async function render(locals) {
const stylesheets = (bundles.css || []).map((b) => b.file); const stylesheets = (bundles.css || []).map((b) => b.file);
const scripts = (bundles.js || []).map((b) => b.file); const scripts = (bundles.js || []).map((b) => b.file);
const renderedHtml = renderSSRTemplate({ const renderedHtml = renderSSRTemplate(ssrTemplate, {
appHtml, appHtml,
baseUrl, baseUrl,
htmlAttributes: htmlAttributes || '', htmlAttributes: htmlAttributes || '',

View file

@ -81,6 +81,7 @@ const ConfigSchema = Joi.object({
// See https://github.com/facebook/docusaurus/issues/3378 // See https://github.com/facebook/docusaurus/issues/3378
.unknown(), .unknown(),
), ),
ssrTemplate: Joi.string(),
stylesheets: Joi.array().items( stylesheets: Joi.array().items(
Joi.string(), Joi.string(),
Joi.object({ Joi.object({

View file

@ -7,6 +7,7 @@
import {generate} from '@docusaurus/utils'; import {generate} from '@docusaurus/utils';
import path, {join} from 'path'; import path, {join} from 'path';
import ssrDefaultTemplate from '../client/templates/ssr.html.template';
import { import {
BUILD_DIR_NAME, BUILD_DIR_NAME,
CONFIG_FILE_NAME, CONFIG_FILE_NAME,
@ -43,7 +44,7 @@ export function loadContext(
const outDir = customOutDir const outDir = customOutDir
? path.resolve(customOutDir) ? path.resolve(customOutDir)
: path.resolve(siteDir, BUILD_DIR_NAME); : path.resolve(siteDir, BUILD_DIR_NAME);
const {baseUrl} = siteConfig; const {baseUrl, ssrTemplate} = siteConfig;
return { return {
siteDir, siteDir,
@ -51,6 +52,7 @@ export function loadContext(
siteConfig, siteConfig,
outDir, outDir,
baseUrl, baseUrl,
ssrTemplate,
}; };
} }
@ -72,7 +74,7 @@ export async function load(
): Promise<Props> { ): Promise<Props> {
// Context. // Context.
const context: LoadContext = loadContext(siteDir, customOutDir); const context: LoadContext = loadContext(siteDir, customOutDir);
const {generatedFilesDir, siteConfig, outDir, baseUrl} = context; const {generatedFilesDir, siteConfig, outDir, baseUrl, ssrTemplate} = context;
// Plugins. // Plugins.
const pluginConfigs: PluginConfig[] = loadPluginConfigs(context); const pluginConfigs: PluginConfig[] = loadPluginConfigs(context);
@ -237,6 +239,7 @@ ${Object.keys(registry)
headTags, headTags,
preBodyTags, preBodyTags,
postBodyTags, postBodyTags,
ssrTemplate: ssrTemplate || ssrDefaultTemplate,
}; };
return props; return props;

View file

@ -31,6 +31,7 @@ export default function createServerConfig({
headTags, headTags,
preBodyTags, preBodyTags,
postBodyTags, postBodyTags,
ssrTemplate,
} = props; } = props;
const config = createBaseConfig(props, true, minify); const config = createBaseConfig(props, true, minify);
@ -70,6 +71,7 @@ export default function createServerConfig({
preBodyTags, preBodyTags,
postBodyTags, postBodyTags,
onLinksCollected, onLinksCollected,
ssrTemplate,
}, },
paths: ssgPaths, paths: ssgPaths,
}), }),

View file

@ -306,6 +306,50 @@ module.exports = {
}; };
``` ```
### `ssrTemplate`
An HTML template that will be used to render your application. This can be used to set custom attributes on the `body` tags, additional `meta` tags, customize the `viewport`, etc. Please note that Docusaurus will rely on the template to be correctly structured in order to function properly, once you do customize it, you will have to make sure that your template is compliant with the requirements from `upstream`.
- Type: `string`
Example:
```js title="docusaurus.config.js"
module.exports = {
ssrTemplate: `<!DOCTYPE html>
<html <%~ it.htmlAttributes %>>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=3.0, minimum-scale=0.86">
<meta name="generator" content="Docusaurus v<%= it.version %>">
<%~ it.headTags %>
<% it.metaAttributes.forEach((metaAttribute) => { %>
<%~ metaAttribute %>
<% }); %>
<% it.stylesheets.forEach((stylesheet) => { %>
<link rel="stylesheet" type="text/css" href="<%= it.baseUrl %><%= stylesheet %>" />
<% }); %>
<% it.scripts.forEach((script) => { %>
<link rel="preload" href="<%= it.baseUrl %><%= script %>" as="script">
<% }); %>
</head>
<body <%~ it.bodyAttributes %> itemscope="" itemtype="http://schema.org/Organization">
<%~ it.preBodyTags %>
<div id="__docusaurus">
<%~ it.appHtml %>
</div>
<div id="outside-docusaurus">
<span>Custom markup</span>
</div>
<% it.scripts.forEach((script) => { %>
<script type="text/javascript" src="<%= it.baseUrl %><%= script %>"></script>
<% }); %>
<%~ it.postBodyTags %>
</body>
</html>
};
```
### `stylesheets` ### `stylesheets`
An array of CSS sources to load. The values can be either strings or plain objects of attribute-value maps. The `<link>` tags will be inserted in the HTML `<head>`. An array of CSS sources to load. The values can be either strings or plain objects of attribute-value maps. The `<link>` tags will be inserted in the HTML `<head>`.