mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-21 04:57:05 +02:00
feat(v2): plugins injectHtmlTags + configureWebpack should receive content loaded (#5037)
* more lifecycles should receive plugin loaded content * refactor docs/blog plugins to use newly injected loaded plugin content instead of a mutable variable * update lifecycle docs * update lifecycle docs * fix failing tests
This commit is contained in:
parent
4e88ea0a1a
commit
119c6d143e
12 changed files with 112 additions and 62 deletions
|
@ -55,7 +55,7 @@ import {
|
|||
export default function pluginContentBlog(
|
||||
context: LoadContext,
|
||||
options: PluginOptions,
|
||||
): Plugin<BlogContent | null> {
|
||||
): Plugin<BlogContent> {
|
||||
if (options.admonitions) {
|
||||
options.remarkPlugins = options.remarkPlugins.concat([
|
||||
[admonitions, options.admonitions],
|
||||
|
@ -88,8 +88,6 @@ export default function pluginContentBlog(
|
|||
const aliasedSource = (source: string) =>
|
||||
`~blog/${posixPath(path.relative(pluginDataDirRoot, source))}`;
|
||||
|
||||
let blogPosts: BlogPost[] = [];
|
||||
|
||||
return {
|
||||
name: 'docusaurus-plugin-content-blog',
|
||||
|
||||
|
@ -116,10 +114,19 @@ export default function pluginContentBlog(
|
|||
async loadContent() {
|
||||
const {postsPerPage, routeBasePath} = options;
|
||||
|
||||
blogPosts = await generateBlogPosts(contentPaths, context, options);
|
||||
const blogPosts: BlogPost[] = await generateBlogPosts(
|
||||
contentPaths,
|
||||
context,
|
||||
options,
|
||||
);
|
||||
|
||||
if (!blogPosts.length) {
|
||||
return null;
|
||||
return {
|
||||
blogPosts: [],
|
||||
blogListPaginated: [],
|
||||
blogTags: {},
|
||||
blogTagsListPath: null,
|
||||
};
|
||||
}
|
||||
|
||||
// Colocate next and prev metadata.
|
||||
|
@ -242,7 +249,7 @@ export default function pluginContentBlog(
|
|||
|
||||
const {addRoute, createData} = actions;
|
||||
const {
|
||||
blogPosts: loadedBlogPosts,
|
||||
blogPosts,
|
||||
blogListPaginated,
|
||||
blogTags,
|
||||
blogTagsListPath,
|
||||
|
@ -275,7 +282,7 @@ export default function pluginContentBlog(
|
|||
|
||||
// Create routes for blog entries.
|
||||
await Promise.all(
|
||||
loadedBlogPosts.map(async (blogPost) => {
|
||||
blogPosts.map(async (blogPost) => {
|
||||
const {id, metadata} = blogPost;
|
||||
await createData(
|
||||
// Note that this created data path must be in sync with
|
||||
|
@ -403,6 +410,7 @@ export default function pluginContentBlog(
|
|||
_config: Configuration,
|
||||
isServer: boolean,
|
||||
{getJSLoader}: ConfigureWebpackUtils,
|
||||
content,
|
||||
) {
|
||||
const {
|
||||
rehypePlugins,
|
||||
|
@ -416,7 +424,7 @@ export default function pluginContentBlog(
|
|||
siteDir,
|
||||
contentPaths,
|
||||
truncateMarker,
|
||||
sourceToPermalink: getSourceToPermalink(blogPosts),
|
||||
sourceToPermalink: getSourceToPermalink(content.blogPosts),
|
||||
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
||||
if (onBrokenMarkdownLinks === 'ignore') {
|
||||
return;
|
||||
|
@ -506,8 +514,8 @@ export default function pluginContentBlog(
|
|||
);
|
||||
},
|
||||
|
||||
injectHtmlTags() {
|
||||
if (!blogPosts.length) {
|
||||
injectHtmlTags({content}) {
|
||||
if (!content.blogPosts.length) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -309,6 +309,8 @@ describe('simple website', () => {
|
|||
test('configureWebpack', async () => {
|
||||
const {plugin} = await loadSite();
|
||||
|
||||
const content = await plugin.loadContent?.();
|
||||
|
||||
const config = applyConfigureWebpack(
|
||||
plugin.configureWebpack,
|
||||
{
|
||||
|
@ -319,6 +321,8 @@ describe('simple website', () => {
|
|||
},
|
||||
},
|
||||
false,
|
||||
undefined,
|
||||
content,
|
||||
);
|
||||
const errors = validate(config);
|
||||
expect(errors).toBeUndefined();
|
||||
|
|
|
@ -41,7 +41,7 @@ import {PermalinkToSidebar} from '@docusaurus/plugin-content-docs-types';
|
|||
import {RuleSetRule} from 'webpack';
|
||||
import {cliDocsVersionCommand} from './cli';
|
||||
import {VERSIONS_JSON_FILE} from './constants';
|
||||
import {flatten, keyBy, compact} from 'lodash';
|
||||
import {flatten, keyBy, compact, mapValues} from 'lodash';
|
||||
import {toGlobalDataVersion} from './globalData';
|
||||
import {toVersionMetadataProp} from './props';
|
||||
import {
|
||||
|
@ -59,7 +59,6 @@ export default function pluginContentDocs(
|
|||
|
||||
const versionsMetadata = readVersionsMetadata({context, options});
|
||||
|
||||
const sourceToPermalink: SourceToPermalink = {};
|
||||
const pluginId = options.id ?? DEFAULT_PLUGIN_ID;
|
||||
|
||||
const pluginDataDirRoot = path.join(
|
||||
|
@ -225,12 +224,6 @@ export default function pluginContentDocs(
|
|||
// sort to ensure consistent output for tests
|
||||
docs.sort((a, b) => a.id.localeCompare(b.id));
|
||||
|
||||
// TODO annoying side effect!
|
||||
Object.values(docs).forEach((loadedDoc) => {
|
||||
const {source, permalink} = loadedDoc;
|
||||
sourceToPermalink[source] = permalink;
|
||||
});
|
||||
|
||||
// TODO really useful? replace with global state logic?
|
||||
const permalinkToSidebar: PermalinkToSidebar = {};
|
||||
Object.values(docs).forEach((doc) => {
|
||||
|
@ -369,7 +362,7 @@ export default function pluginContentDocs(
|
|||
});
|
||||
},
|
||||
|
||||
configureWebpack(_config, isServer, utils) {
|
||||
configureWebpack(_config, isServer, utils, content) {
|
||||
const {getJSLoader} = utils;
|
||||
const {
|
||||
rehypePlugins,
|
||||
|
@ -378,9 +371,17 @@ export default function pluginContentDocs(
|
|||
beforeDefaultRemarkPlugins,
|
||||
} = options;
|
||||
|
||||
function getSourceToPermalink(): SourceToPermalink {
|
||||
const allDocs = flatten(content.loadedVersions.map((v) => v.docs));
|
||||
return mapValues(
|
||||
keyBy(allDocs, (d) => d.source),
|
||||
(d) => d.permalink,
|
||||
);
|
||||
}
|
||||
|
||||
const docsMarkdownOptions: DocsMarkdownOption = {
|
||||
siteDir,
|
||||
sourceToPermalink,
|
||||
sourceToPermalink: getSourceToPermalink(),
|
||||
versionsMetadata,
|
||||
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
||||
if (siteConfig.onBrokenMarkdownLinks === 'ignore') {
|
||||
|
|
8
packages/docusaurus-types/src/index.d.ts
vendored
8
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -198,7 +198,7 @@ export interface Props extends LoadContext, InjectedHtmlTags {
|
|||
siteMetadata: DocusaurusSiteMetadata;
|
||||
routes: RouteConfig[];
|
||||
routesPaths: string[];
|
||||
plugins: Plugin<unknown>[];
|
||||
plugins: LoadedPlugin<unknown>[];
|
||||
}
|
||||
|
||||
export interface PluginContentLoadedActions {
|
||||
|
@ -233,10 +233,12 @@ export interface Plugin<Content> {
|
|||
routesLoaded?(routes: RouteConfig[]): void; // TODO remove soon, deprecated (alpha-60)
|
||||
postBuild?(props: Props): void;
|
||||
postStart?(props: Props): void;
|
||||
// TODO refactor the configureWebpack API surface: use an object instead of multiple params (requires breaking change)
|
||||
configureWebpack?(
|
||||
config: Configuration,
|
||||
isServer: boolean,
|
||||
utils: ConfigureWebpackUtils,
|
||||
content: Content,
|
||||
): Configuration & {mergeStrategy?: ConfigureWebpackFnMergeStrategy};
|
||||
configurePostCss?(options: PostCssOptions): PostCssOptions;
|
||||
getThemePath?(): string;
|
||||
|
@ -244,7 +246,9 @@ export interface Plugin<Content> {
|
|||
getPathsToWatch?(): string[];
|
||||
getClientModules?(): string[];
|
||||
extendCli?(cli: Command): void;
|
||||
injectHtmlTags?(): {
|
||||
injectHtmlTags?({
|
||||
content: Content,
|
||||
}): {
|
||||
headTags?: HtmlTags;
|
||||
preBodyTags?: HtmlTags;
|
||||
postBodyTags?: HtmlTags;
|
||||
|
|
|
@ -184,17 +184,19 @@ async function buildLocale({
|
|||
|
||||
if (configureWebpack) {
|
||||
clientConfig = applyConfigureWebpack(
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`. // TODO remove this implicit api: inject in callback instead
|
||||
clientConfig,
|
||||
false,
|
||||
props.siteConfig.webpack?.jsLoader,
|
||||
plugin.content,
|
||||
);
|
||||
|
||||
serverConfig = applyConfigureWebpack(
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`. // TODO remove this implicit api: inject in callback instead
|
||||
serverConfig,
|
||||
true,
|
||||
props.siteConfig.webpack?.jsLoader,
|
||||
plugin.content,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -156,10 +156,11 @@ export default async function start(
|
|||
|
||||
if (configureWebpack) {
|
||||
config = applyConfigureWebpack(
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`. // TODO remove this implicit api: inject in callback instead
|
||||
config,
|
||||
false,
|
||||
props.siteConfig.webpack?.jsLoader,
|
||||
plugin.content,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -6,12 +6,8 @@
|
|||
*/
|
||||
|
||||
import htmlTagObjectToString from './htmlTags';
|
||||
import {
|
||||
Plugin,
|
||||
InjectedHtmlTags,
|
||||
HtmlTagObject,
|
||||
HtmlTags,
|
||||
} from '@docusaurus/types';
|
||||
import {InjectedHtmlTags, HtmlTagObject, HtmlTags} from '@docusaurus/types';
|
||||
import {LoadedPlugin} from '../plugins';
|
||||
|
||||
function toString(val: string | HtmlTagObject): string {
|
||||
return typeof val === 'string' ? val : htmlTagObjectToString(val);
|
||||
|
@ -21,14 +17,14 @@ export function createHtmlTagsString(tags: HtmlTags): string {
|
|||
return Array.isArray(tags) ? tags.map(toString).join('\n') : toString(tags);
|
||||
}
|
||||
|
||||
export function loadHtmlTags(plugins: Plugin<unknown>[]): InjectedHtmlTags {
|
||||
export function loadHtmlTags(plugins: LoadedPlugin[]): InjectedHtmlTags {
|
||||
const htmlTags = plugins.reduce(
|
||||
(acc, plugin) => {
|
||||
if (!plugin.injectHtmlTags) {
|
||||
return acc;
|
||||
}
|
||||
const {headTags, preBodyTags, postBodyTags} =
|
||||
plugin.injectHtmlTags() || {};
|
||||
plugin.injectHtmlTags({content: plugin.content}) || {};
|
||||
return {
|
||||
headTags: headTags
|
||||
? `${acc.headTags}\n${createHtmlTagsString(headTags)}`
|
||||
|
|
|
@ -196,6 +196,7 @@ export async function load(
|
|||
} = siteConfig;
|
||||
plugins.push({
|
||||
name: 'docusaurus-bootstrap-plugin',
|
||||
content: null,
|
||||
options: {},
|
||||
version: {type: 'synthetic'},
|
||||
getClientModules() {
|
||||
|
|
|
@ -53,6 +53,8 @@ export function sortConfig(routeConfigs: RouteConfig[]): void {
|
|||
});
|
||||
}
|
||||
|
||||
export type LoadedPlugin = InitPlugin & {content: unknown};
|
||||
|
||||
export async function loadPlugins({
|
||||
pluginConfigs,
|
||||
context,
|
||||
|
@ -60,7 +62,7 @@ export async function loadPlugins({
|
|||
pluginConfigs: PluginConfig[];
|
||||
context: LoadContext;
|
||||
}): Promise<{
|
||||
plugins: InitPlugin[];
|
||||
plugins: LoadedPlugin[];
|
||||
pluginsRouteConfigs: RouteConfig[];
|
||||
globalData: unknown;
|
||||
themeConfigTranslated: ThemeConfig;
|
||||
|
@ -75,21 +77,20 @@ export async function loadPlugins({
|
|||
// Currently plugins run lifecycle methods in parallel and are not order-dependent.
|
||||
// We could change this in future if there are plugins which need to
|
||||
// run in certain order or depend on others for data.
|
||||
type ContentLoadedPlugin = {plugin: InitPlugin; content: unknown};
|
||||
const contentLoadedPlugins: ContentLoadedPlugin[] = await Promise.all(
|
||||
const loadedPlugins: LoadedPlugin[] = await Promise.all(
|
||||
plugins.map(async (plugin) => {
|
||||
const content = plugin.loadContent ? await plugin.loadContent() : null;
|
||||
return {plugin, content};
|
||||
return {...plugin, content};
|
||||
}),
|
||||
);
|
||||
|
||||
type ContentLoadedTranslatedPlugin = ContentLoadedPlugin & {
|
||||
type ContentLoadedTranslatedPlugin = LoadedPlugin & {
|
||||
translationFiles: TranslationFiles;
|
||||
};
|
||||
const contentLoadedTranslatedPlugins: ContentLoadedTranslatedPlugin[] = await Promise.all(
|
||||
contentLoadedPlugins.map(async (contentLoadedPlugin) => {
|
||||
loadedPlugins.map(async (contentLoadedPlugin) => {
|
||||
const translationFiles =
|
||||
(await contentLoadedPlugin.plugin?.getTranslationFiles?.({
|
||||
(await contentLoadedPlugin?.getTranslationFiles?.({
|
||||
content: contentLoadedPlugin.content,
|
||||
})) ?? [];
|
||||
const localizedTranslationFiles = await Promise.all(
|
||||
|
@ -98,7 +99,7 @@ export async function loadPlugins({
|
|||
locale: context.i18n.currentLocale,
|
||||
siteDir: context.siteDir,
|
||||
translationFile,
|
||||
plugin: contentLoadedPlugin.plugin,
|
||||
plugin: contentLoadedPlugin,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
@ -109,11 +110,11 @@ export async function loadPlugins({
|
|||
}),
|
||||
);
|
||||
|
||||
const allContent: AllContent = chain(contentLoadedPlugins)
|
||||
.groupBy((item) => item.plugin.name)
|
||||
const allContent: AllContent = chain(loadedPlugins)
|
||||
.groupBy((item) => item.name)
|
||||
.mapValues((nameItems) => {
|
||||
return chain(nameItems)
|
||||
.groupBy((item) => item.plugin.options.id ?? DEFAULT_PLUGIN_ID)
|
||||
.groupBy((item) => item.options.id ?? DEFAULT_PLUGIN_ID)
|
||||
.mapValues((idItems) => idItems[0].content)
|
||||
.value();
|
||||
})
|
||||
|
@ -126,7 +127,7 @@ export async function loadPlugins({
|
|||
|
||||
await Promise.all(
|
||||
contentLoadedTranslatedPlugins.map(
|
||||
async ({plugin, content, translationFiles}) => {
|
||||
async ({content, translationFiles, ...plugin}) => {
|
||||
if (!plugin.contentLoaded) {
|
||||
return;
|
||||
}
|
||||
|
@ -191,7 +192,7 @@ export async function loadPlugins({
|
|||
// We could change this in future if there are plugins which need to
|
||||
// run in certain order or depend on others for data.
|
||||
await Promise.all(
|
||||
contentLoadedTranslatedPlugins.map(async ({plugin}) => {
|
||||
contentLoadedTranslatedPlugins.map(async (plugin) => {
|
||||
if (!plugin.routesLoaded) {
|
||||
return null;
|
||||
}
|
||||
|
@ -218,10 +219,10 @@ export async function loadPlugins({
|
|||
untranslatedThemeConfig: ThemeConfig,
|
||||
): ThemeConfig {
|
||||
return contentLoadedTranslatedPlugins.reduce(
|
||||
(currentThemeConfig, {plugin, translationFiles}) => {
|
||||
(currentThemeConfig, plugin) => {
|
||||
const translatedThemeConfigSlice = plugin.translateThemeConfig?.({
|
||||
themeConfig: currentThemeConfig,
|
||||
translationFiles,
|
||||
translationFiles: plugin.translationFiles,
|
||||
});
|
||||
return {
|
||||
...currentThemeConfig,
|
||||
|
@ -233,7 +234,7 @@ export async function loadPlugins({
|
|||
}
|
||||
|
||||
return {
|
||||
plugins,
|
||||
plugins: loadedPlugins,
|
||||
pluginsRouteConfigs,
|
||||
globalData,
|
||||
themeConfigTranslated: translateThemeConfig(context.siteConfig.themeConfig),
|
||||
|
|
|
@ -77,7 +77,9 @@ describe('extending generated webpack config', () => {
|
|||
return {};
|
||||
};
|
||||
|
||||
config = applyConfigureWebpack(configureWebpack, config, false);
|
||||
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
||||
content: 42,
|
||||
});
|
||||
expect(config).toEqual({
|
||||
entry: 'entry.js',
|
||||
output: {
|
||||
|
@ -105,7 +107,9 @@ describe('extending generated webpack config', () => {
|
|||
},
|
||||
});
|
||||
|
||||
config = applyConfigureWebpack(configureWebpack, config, false);
|
||||
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
||||
content: 42,
|
||||
});
|
||||
expect(config).toEqual({
|
||||
entry: 'entry.js',
|
||||
output: {
|
||||
|
@ -137,6 +141,8 @@ describe('extending generated webpack config', () => {
|
|||
createConfigureWebpack(),
|
||||
config,
|
||||
false,
|
||||
undefined,
|
||||
{content: 42},
|
||||
);
|
||||
expect(defaultStrategyMergeConfig).toEqual({
|
||||
module: {
|
||||
|
@ -148,6 +154,8 @@ describe('extending generated webpack config', () => {
|
|||
createConfigureWebpack({'module.rules': 'prepend'}),
|
||||
config,
|
||||
false,
|
||||
undefined,
|
||||
{content: 42},
|
||||
);
|
||||
expect(prependRulesStrategyConfig).toEqual({
|
||||
module: {
|
||||
|
@ -159,6 +167,8 @@ describe('extending generated webpack config', () => {
|
|||
createConfigureWebpack({uselessAttributeName: 'append'}),
|
||||
config,
|
||||
false,
|
||||
undefined,
|
||||
{content: 42},
|
||||
);
|
||||
expect(uselessMergeStrategyConfig).toEqual({
|
||||
module: {
|
||||
|
|
|
@ -198,13 +198,15 @@ function getCacheLoaderDeprecated() {
|
|||
* @param config initial webpack config
|
||||
* @param isServer indicates if this is a server webpack configuration
|
||||
* @param jsLoader custom js loader config
|
||||
* @param content content loaded by the plugin
|
||||
* @returns final/ modified webpack config
|
||||
*/
|
||||
export function applyConfigureWebpack(
|
||||
configureWebpack: ConfigureWebpackFn,
|
||||
config: Configuration,
|
||||
isServer: boolean,
|
||||
jsLoader?: 'babel' | ((isServer: boolean) => RuleSetRule),
|
||||
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined,
|
||||
content: unknown,
|
||||
): Configuration {
|
||||
// Export some utility functions
|
||||
const utils: ConfigureWebpackUtils = {
|
||||
|
@ -214,7 +216,12 @@ export function applyConfigureWebpack(
|
|||
getCacheLoader: getCacheLoaderDeprecated,
|
||||
};
|
||||
if (typeof configureWebpack === 'function') {
|
||||
const {mergeStrategy, ...res} = configureWebpack(config, isServer, utils);
|
||||
const {mergeStrategy, ...res} = configureWebpack(
|
||||
config,
|
||||
isServer,
|
||||
utils,
|
||||
content,
|
||||
);
|
||||
if (res && typeof res === 'object') {
|
||||
// @ts-expect-error: annoying error due to enums: https://github.com/survivejs/webpack-merge/issues/179
|
||||
const customizeRules: Record<string, CustomizeRule> = mergeStrategy ?? {};
|
||||
|
|
|
@ -279,10 +279,16 @@ export default function friendsPlugin(context, options) {
|
|||
}
|
||||
```
|
||||
|
||||
## `configureWebpack(config, isServer, utils)` {#configurewebpackconfig-isserver-utils}
|
||||
## `configureWebpack(config, isServer, utils, content)` {#configurewebpackconfig-isserver-utils}
|
||||
|
||||
Modifies the internal webpack config. If the return value is a JavaScript object, it will be merged into the final config using [`webpack-merge`](https://github.com/survivejs/webpack-merge). If it is a function, it will be called and receive `config` as the first argument and an `isServer` flag as the argument argument.
|
||||
|
||||
:::caution
|
||||
|
||||
The API of `configureWebpack` will be modified in the future to accept an object (`configureWebpack({config, isServer, utils, content})`)
|
||||
|
||||
:::
|
||||
|
||||
### `config` {#config}
|
||||
|
||||
`configureWebpack` is called with `config` generated according to client/server build. You may treat this as the base config to be merged with.
|
||||
|
@ -293,11 +299,10 @@ Modifies the internal webpack config. If the return value is a JavaScript object
|
|||
|
||||
### `utils` {#utils}
|
||||
|
||||
The initial call to `configureWebpack` also receives a util object consists of three functions:
|
||||
`configureWebpack` also receives an util object:
|
||||
|
||||
- `getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]`
|
||||
- `getCacheLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
|
||||
- `getBabelLoader(isServer: boolean, babelOptions?: {}): Loader`
|
||||
- `getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
|
||||
|
||||
You may use them to return your webpack configures conditionally.
|
||||
|
||||
|
@ -326,6 +331,10 @@ module.exports = function (context, options) {
|
|||
};
|
||||
```
|
||||
|
||||
### `content` {#content}
|
||||
|
||||
`configureWebpack` will be called both with the content loaded by the plugin.
|
||||
|
||||
### Merge strategy {#merge-strategy}
|
||||
|
||||
We merge the Webpack configuration parts of plugins into the global Webpack config using [webpack-merge](https://github.com/survivejs/webpack-merge).
|
||||
|
@ -439,10 +448,12 @@ module.exports = function (context, options) {
|
|||
};
|
||||
```
|
||||
|
||||
## `injectHtmlTags()` {#injecthtmltags}
|
||||
## `injectHtmlTags({content})` {#injecthtmltags}
|
||||
|
||||
Inject head and/or body HTML tags to Docusaurus generated HTML.
|
||||
|
||||
`injectHtmlTags` will be called both with the content loaded by the plugin.
|
||||
|
||||
```typescript
|
||||
function injectHtmlTags(): {
|
||||
headTags?: HtmlTags;
|
||||
|
@ -477,8 +488,11 @@ Example:
|
|||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
loadContent: async () => {
|
||||
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
|
||||
},
|
||||
// highlight-start
|
||||
injectHtmlTags() {
|
||||
injectHtmlTags({content}) {
|
||||
return {
|
||||
headTags: [
|
||||
{
|
||||
|
@ -488,6 +502,7 @@ module.exports = function (context, options) {
|
|||
href: 'https://www.github.com',
|
||||
},
|
||||
},
|
||||
...content.remoteHeadTags,
|
||||
],
|
||||
preBodyTags: [
|
||||
{
|
||||
|
@ -765,7 +780,7 @@ module.exports = function (context, opts) {
|
|||
// https://webpack.js.org/configuration/dev-server/#devserverafter
|
||||
},
|
||||
|
||||
configureWebpack(config, isServer) {
|
||||
configureWebpack(config, isServer, utils, content) {
|
||||
// Modify internal webpack config. If returned value is an Object, it
|
||||
// will be merged into the final config using webpack-merge;
|
||||
// If the returned value is a function, it will receive the config as the 1st argument and an isServer flag as the 2nd argument.
|
||||
|
@ -790,11 +805,11 @@ module.exports = function (context, opts) {
|
|||
// Register an extra command to enhance the CLI of Docusaurus
|
||||
},
|
||||
|
||||
injectHtmlTags() {
|
||||
injectHtmlTags({content}) {
|
||||
// Inject head and/or body HTML tags.
|
||||
},
|
||||
|
||||
async getTranslationFiles() {
|
||||
async getTranslationFiles({content}) {
|
||||
// Return translation files
|
||||
},
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue