mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-04 11:52:39 +02:00
docs: refactor & refine lifecycle API docs (#5918)
* docs: refactor & refine lifecycle API docs * Fix links * More writeup * Rewording * Rename path * Use README * Fix links * Add redirects * Do the same for latest version as well * Move folder * Fix broken link
This commit is contained in:
parent
dc1ccd2fbb
commit
9078fd9fb2
24 changed files with 1822 additions and 1716 deletions
|
@ -414,7 +414,7 @@ module.exports = {
|
|||
};
|
||||
```
|
||||
|
||||
See also: [`getClientModules()`](lifecycle-apis.md#getclientmodules).
|
||||
See also: [`getClientModules()`](./plugin-methods/lifecycle-apis.md#getClientModules).
|
||||
|
||||
### `ssrTemplate` {#ssrtemplate}
|
||||
|
||||
|
|
|
@ -1,832 +0,0 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
id: lifecycle-apis
|
||||
title: Lifecycle APIs
|
||||
slug: /lifecycle-apis
|
||||
toc_max_heading_level: 4
|
||||
---
|
||||
|
||||
:::caution
|
||||
|
||||
This section is a work in progress.
|
||||
|
||||
:::
|
||||
|
||||
Lifecycle APIs are shared by Themes and Plugins.
|
||||
|
||||
## `validateOptions({options, validate})` {#validateoptionsoptions-validate}
|
||||
|
||||
Return validated and normalized options for the plugin. This method is called before the plugin is initialized.You must return options since the returned options will be passed to plugin during initialization.
|
||||
|
||||
### `options` {#options}
|
||||
|
||||
`validateOptions` is called with `options` passed to plugin for validation and normalization.
|
||||
|
||||
### `validate` {#validate}
|
||||
|
||||
`validateOptions` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and options as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of options.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options and return options in case of success.
|
||||
|
||||
```js {8-11} title="my-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.validateOptions = ({options, validate}) => {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
};
|
||||
```
|
||||
|
||||
You can also use ES modules style exports.
|
||||
|
||||
```ts {8-11} title="my-plugin/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateOptions({options, validate}) {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
}
|
||||
```
|
||||
|
||||
## `validateThemeConfig({themeConfig, validate})` {#validatethemeconfigthemeconfig-validate}
|
||||
|
||||
Return validated and normalized configuration for the theme.
|
||||
|
||||
### `themeConfig` {#themeconfig}
|
||||
|
||||
`validateThemeConfig` is called with `themeConfig` provided in `docusaurus.config.js` for validation and normalization.
|
||||
|
||||
### `validate` {#validate-1}
|
||||
|
||||
`validateThemeConfig` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and `themeConfig` as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of theme config.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options.
|
||||
|
||||
```js {8-11} title="my-theme/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.validateThemeConfig = ({themeConfig, validate}) => {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
};
|
||||
```
|
||||
|
||||
You can also use ES modules style exports.
|
||||
|
||||
```ts {8-11} title="my-theme/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateThemeConfig({themeConfig, validate}) {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
}
|
||||
```
|
||||
|
||||
## `getPathsToWatch()` {#getpathstowatch}
|
||||
|
||||
Specifies the paths to watch for plugins and themes. The paths are watched by the dev server so that the plugin lifecycles are reloaded when contents in the watched paths change. Note that the plugins and themes modules are initially called with `context` and `options` from Node, which you may use to find the necessary directory information about the site.
|
||||
|
||||
Example:
|
||||
|
||||
```js {5-7} title="docusaurus-plugin/src/index.js"
|
||||
const path = require('path');
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
getPathsToWatch() {
|
||||
const contentPath = path.resolve(context.siteDir, options.path);
|
||||
return [`${contentPath}/**/*.{ts,tsx}`];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async loadContent()` {#async-loadcontent}
|
||||
|
||||
Plugins should use this lifecycle to fetch from data sources (filesystem, remote API, headless CMS, etc) or doing some server processing.
|
||||
|
||||
For example, this plugin below return a random integer between 1 to 10 as content;
|
||||
|
||||
```js {5-6} title="docusaurus-plugin/src/index.js"
|
||||
const path = require('path');
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async loadContent() {
|
||||
return 1 + Math.floor(Math.random() * 10);
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async contentLoaded({content, actions})` {#async-contentloadedcontent-actions}
|
||||
|
||||
Plugins should use the data loaded in `loadContent` and construct the pages/routes that consume the loaded data (optional).
|
||||
|
||||
### `content` {#content}
|
||||
|
||||
`contentLoaded` will be called _after_ `loadContent` is done, the return value of `loadContent()` will be passed to `contentLoaded` as `content`.
|
||||
|
||||
### `actions` {#actions}
|
||||
|
||||
`actions` contain three functions:
|
||||
|
||||
#### `addRoute(config: RouteConfig): void`
|
||||
|
||||
Create a route to add to the website.
|
||||
|
||||
```typescript
|
||||
interface RouteConfig {
|
||||
path: string;
|
||||
component: string;
|
||||
modules?: RouteModule;
|
||||
routes?: RouteConfig[];
|
||||
exact?: boolean;
|
||||
priority?: number;
|
||||
}
|
||||
interface RouteModule {
|
||||
[module: string]: Module | RouteModule | RouteModule[];
|
||||
}
|
||||
type Module =
|
||||
| {
|
||||
path: string;
|
||||
__import?: boolean;
|
||||
query?: ParsedUrlQueryInput;
|
||||
}
|
||||
| string;
|
||||
```
|
||||
|
||||
#### `createData(name: string, data: any): Promise<string>`
|
||||
|
||||
A function to help you create static data (generally json or string), that you can provide to your routes as props.
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
|
||||
export default function FriendsComponent({friends}) {
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-23} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {createData, addRoute} = actions;
|
||||
// Create friends.json
|
||||
const friends = ['Yangshun', 'Sebastien'];
|
||||
const friendsJsonPath = await createData(
|
||||
'friends.json',
|
||||
JSON.stringify(friends),
|
||||
);
|
||||
|
||||
// Add the '/friends' routes, and ensure it receives the friends props
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
modules: {
|
||||
// propName -> JSON file path
|
||||
friends: friendsJsonPath,
|
||||
},
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### `setGlobalData(data: any): void`
|
||||
|
||||
This function permits to create some global plugin data, that can be read from any page, including the pages created by other plugins, and your theme layout.
|
||||
|
||||
This data become accessible to your client-side/theme code, through the [`useGlobalData`](./docusaurus-core.md#useglobaldata) and [`usePluginData`](./docusaurus-core.md#useplugindatapluginname-string-pluginid-string).
|
||||
|
||||
One this data is created, you can access it with the global data hooks APIs.
|
||||
|
||||
:::caution
|
||||
|
||||
Global data is... global: its size affects the loading time of all pages of your site, so try to keep it small.
|
||||
|
||||
Prefer `createData` and page-specific data whenever possible.
|
||||
|
||||
:::
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
import {usePluginData} from '@docusaurus/useGlobalData';
|
||||
|
||||
export default function FriendsComponent() {
|
||||
const {friends} = usePluginData('my-friends-plugin');
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-14} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {setGlobalData, addRoute} = actions;
|
||||
// Create friends global data
|
||||
setGlobalData({friends: ['Yangshun', 'Sebastien']});
|
||||
|
||||
// Add the '/friends' routes
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## `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.
|
||||
|
||||
### `isServer` {#isserver}
|
||||
|
||||
`configureWebpack` will be called both in server build and in client build. The server build receives `true` and the client build receives `false` as `isServer`.
|
||||
|
||||
### `utils` {#utils}
|
||||
|
||||
`configureWebpack` also receives an util object:
|
||||
|
||||
- `getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]`
|
||||
- `getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
|
||||
|
||||
You may use them to return your webpack configures conditionally.
|
||||
|
||||
For example, this plugin below modify the webpack config to transpile `.foo` file.
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
// highlight-start
|
||||
configureWebpack(config, isServer, utils) {
|
||||
const {getCacheLoader} = utils;
|
||||
return {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.foo$/,
|
||||
use: [getCacheLoader(isServer), 'my-custom-webpack-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `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).
|
||||
|
||||
It is possible to specify the merge strategy. For example, if you want a webpack rule to be prepended instead of appended:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
configureWebpack(config, isServer, utils) {
|
||||
return {
|
||||
// highlight-start
|
||||
mergeStrategy: {'module.rules': 'prepend'},
|
||||
module: {rules: [myRuleToPrepend]},
|
||||
// highlight-end
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.
|
||||
|
||||
## `configurePostCss(options)` {#configurepostcssoptions}
|
||||
|
||||
Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during the generation of the client bundle.
|
||||
|
||||
Should return the mutated `postcssOptions`.
|
||||
|
||||
By default, `postcssOptions` looks like this:
|
||||
|
||||
```js
|
||||
const postcssOptions = {
|
||||
ident: 'postcss',
|
||||
plugins: [require('autoprefixer')],
|
||||
};
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// highlight-start
|
||||
configurePostCss(postcssOptions) {
|
||||
// Appends new PostCSS plugin.
|
||||
postcssOptions.plugins.push(require('postcss-import'));
|
||||
return postcssOptions;
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `postBuild(props)` {#postbuildprops}
|
||||
|
||||
Called when a (production) build finishes.
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
headTags: string;
|
||||
preBodyTags: string;
|
||||
postBodyTags: string;
|
||||
routesPaths: string[];
|
||||
plugins: Plugin<any>[];
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
|
||||
// Print out to console all the rendered routes.
|
||||
routesPaths.map((route) => {
|
||||
console.log(route);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `extendCli(cli)` {#extendclicli}
|
||||
|
||||
Register an extra command to enhance the CLI of Docusaurus. `cli` is [commander](https://www.npmjs.com/package/commander) object.
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
extendCli(cli) {
|
||||
cli
|
||||
.command('roll')
|
||||
.description('Roll a random number between 1 and 1000')
|
||||
.action(() => {
|
||||
console.log(Math.floor(Math.random() * 1000 + 1));
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `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;
|
||||
preBodyTags?: HtmlTags;
|
||||
postBodyTags?: HtmlTags;
|
||||
};
|
||||
|
||||
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
|
||||
|
||||
interface HtmlTagObject {
|
||||
/**
|
||||
* Attributes of the HTML tag
|
||||
* E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
|
||||
*/
|
||||
attributes?: {
|
||||
[attributeName: string]: string | boolean;
|
||||
};
|
||||
/**
|
||||
* The tag name e.g. `div`, `script`, `link`, `meta`
|
||||
*/
|
||||
tagName: string;
|
||||
/**
|
||||
* The inner HTML
|
||||
*/
|
||||
innerHTML?: string;
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
loadContent: async () => {
|
||||
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
|
||||
},
|
||||
// highlight-start
|
||||
injectHtmlTags({content}) {
|
||||
return {
|
||||
headTags: [
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
rel: 'preconnect',
|
||||
href: 'https://www.github.com',
|
||||
},
|
||||
},
|
||||
...content.remoteHeadTags,
|
||||
],
|
||||
preBodyTags: [
|
||||
{
|
||||
tagName: 'script',
|
||||
attributes: {
|
||||
charset: 'utf-8',
|
||||
src: '/noflash.js',
|
||||
},
|
||||
},
|
||||
],
|
||||
postBodyTags: [`<div> This is post body </div>`],
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getThemePath()` {#getthemepath}
|
||||
|
||||
Returns the path to the directory where the theme components can be found. When your users calls `swizzle`, `getThemePath` is called and its returned path is used to find your theme components.
|
||||
|
||||
If you use the folder directory above, your `getThemePath` can be:
|
||||
|
||||
```js {6-8} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getThemePath() {
|
||||
return path.resolve(__dirname, './theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getTypeScriptThemePath()` {#gettypescriptthemepath}
|
||||
|
||||
Similar to `getThemePath()`, it should return the path to the directory where the source code of TypeScript theme components can be found. Theme components under this path will **not** be resolved by Webpack. Therefore, it is not a replacement of `getThemePath()`. Instead, this path is purely for swizzling TypeScript theme components.
|
||||
|
||||
If you want to support TypeScript component swizzling for your theme, you can make the path returned by `getTypeScriptThemePath()` be your source directory, and make path returned by `getThemePath()` be the compiled JavaScript output.
|
||||
|
||||
Example:
|
||||
|
||||
```js {6-13} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getThemePath() {
|
||||
// Where compiled JavaScript output lives
|
||||
return path.join(__dirname, '..', 'lib', 'theme');
|
||||
},
|
||||
getTypeScriptThemePath() {
|
||||
// Where TypeScript source code lives
|
||||
return path.resolve(__dirname, './theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getSwizzleComponentList()` {#getswizzlecomponentlist}
|
||||
|
||||
Return a list of stable component that are considered as safe for swizzling. These components will be listed in swizzle component without `--danger`. All the components are considers unstable by default. If an empty array is returned then all components are considered unstable, if `undefined` is returned then all component are considered stable.
|
||||
|
||||
```js {0-12} title="my-theme/src/index.js"
|
||||
const swizzleAllowedComponents = [
|
||||
'CodeBlock',
|
||||
'DocSidebar',
|
||||
'Footer',
|
||||
'NotFound',
|
||||
'SearchBar',
|
||||
'hooks/useTheme',
|
||||
'prism-include-languages',
|
||||
];
|
||||
|
||||
module.exports.getSwizzleComponentList = () => swizzleAllowedComponents;
|
||||
```
|
||||
|
||||
## `getClientModules()` {#getclientmodules}
|
||||
|
||||
Returns an array of paths to the modules that are to be imported in the client bundle. These modules are imported globally before React even renders the initial UI.
|
||||
|
||||
As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user:
|
||||
|
||||
```js {7-9} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
const {customCss, customJs} = options || {};
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getClientModules() {
|
||||
return [customCss, customJs];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
<!--
|
||||
For example, the in docusaurus-plugin-content-docs:
|
||||
|
||||
In loadContent, it loads the doc Markdown files based on the specified directory in options (defaulting to docs).
|
||||
In contentLoaded, for each doc Markdown file, a route is created: /doc/installation, /doc/getting-started, etc.
|
||||
-->
|
||||
|
||||
## i18n lifecycles {#i18n-lifecycles}
|
||||
|
||||
### `getTranslationFiles({content})` {#get-translation-files}
|
||||
|
||||
Plugins declare the JSON translation files they want to use.
|
||||
|
||||
Returns translation files `{path: string, content: ChromeI18nJSON}`:
|
||||
|
||||
- Path: relative to the plugin localized folder `i18n/<locale>/pluginName`. Extension `.json` is not necessary.
|
||||
- Content: using the Chrome i18n JSON format
|
||||
|
||||
These files will be written by the [`write-translations` CLI](./cli.md#docusaurus-write-translations-sitedir) to the plugin i18n subfolder, and will be read in the appropriate locale before calling [`translateContent()`](#translate-content) and [`translateThemeConfig()`](#translate-theme-config)
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
async getTranslationFiles({content}) {
|
||||
return [
|
||||
{
|
||||
path: 'sidebar-labels',
|
||||
content: {
|
||||
someSidebarLabel: {
|
||||
message: 'Some Sidebar Label',
|
||||
description: 'A label used in my plugin in the sidebar',
|
||||
},
|
||||
someLabelFromContent: content.myLabel,
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `translateContent({content,translationFiles})` {#translate-content}
|
||||
|
||||
Translate the plugin content, using the localized translation files.
|
||||
|
||||
Returns the localized plugin content.
|
||||
|
||||
The `contentLoaded()` lifecycle will be called with the localized plugin content returned by `translateContent()`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
translateContent({content, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...content,
|
||||
someContentLabel: myTranslationFile.someContentLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `translateThemeConfig({themeConfig,translationFiles})` {#translate-theme-config}
|
||||
|
||||
Translate the site `themeConfig` labels, using the localized translation files.
|
||||
|
||||
Returns the localized `themeConfig`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...themeConfig,
|
||||
someThemeConfigLabel: myTranslationFile.someThemeConfigLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `async getDefaultCodeTranslationMessages()` {#get-default-code-translation-messages}
|
||||
|
||||
Themes using the `<Translate>` API can provide default code translation messages.
|
||||
|
||||
It should return messages in `Record<string,string`>, where keys are translation ids and values are messages (without the description) localized using the site current locale.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
return readJsonFile(`${context.i18n.currentLocale}.json`);
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Example {#example}
|
||||
|
||||
Here's a mind model for a presumptuous plugin implementation.
|
||||
|
||||
```jsx
|
||||
const DEFAULT_OPTIONS = {
|
||||
// Some defaults.
|
||||
};
|
||||
|
||||
// A JavaScript function that returns an object.
|
||||
// `context` is provided by Docusaurus. Example: siteConfig can be accessed from context.
|
||||
// `opts` is the user-defined options.
|
||||
module.exports = function (context, opts) {
|
||||
// Merge defaults with user-defined options.
|
||||
const options = {...DEFAULT_OPTIONS, ...options};
|
||||
|
||||
return {
|
||||
// A compulsory field used as the namespace for directories to cache
|
||||
// the intermediate data for each plugin.
|
||||
// If you're writing your own local plugin, you will want it to
|
||||
// be unique in order not to potentially conflict with imported plugins.
|
||||
// A good way will be to add your own project name within.
|
||||
name: 'docusaurus-my-project-cool-plugin',
|
||||
|
||||
async loadContent() {
|
||||
// The loadContent hook is executed after siteConfig and env has been loaded.
|
||||
// You can return a JavaScript object that will be passed to contentLoaded hook.
|
||||
},
|
||||
|
||||
async contentLoaded({content, actions}) {
|
||||
// The contentLoaded hook is done after loadContent hook is done.
|
||||
// `actions` are set of functional API provided by Docusaurus (e.g. addRoute)
|
||||
},
|
||||
|
||||
async postBuild(props) {
|
||||
// After docusaurus <build> finish.
|
||||
},
|
||||
|
||||
// TODO
|
||||
async postStart(props) {
|
||||
// docusaurus <start> finish
|
||||
},
|
||||
|
||||
// TODO
|
||||
afterDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverbefore
|
||||
},
|
||||
|
||||
// TODO
|
||||
beforeDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverafter
|
||||
},
|
||||
|
||||
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.
|
||||
},
|
||||
|
||||
getPathsToWatch() {
|
||||
// Paths to watch.
|
||||
},
|
||||
|
||||
getThemePath() {
|
||||
// Returns the path to the directory where the theme components can
|
||||
// be found.
|
||||
},
|
||||
|
||||
getClientModules() {
|
||||
// Return an array of paths to the modules that are to be imported
|
||||
// in the client bundle. These modules are imported globally before
|
||||
// React even renders the initial UI.
|
||||
},
|
||||
|
||||
extendCli(cli) {
|
||||
// Register an extra command to enhance the CLI of Docusaurus
|
||||
},
|
||||
|
||||
injectHtmlTags({content}) {
|
||||
// Inject head and/or body HTML tags.
|
||||
},
|
||||
|
||||
async getTranslationFiles({content}) {
|
||||
// Return translation files
|
||||
},
|
||||
|
||||
translateContent({content, translationFiles}) {
|
||||
// translate the plugin content here
|
||||
},
|
||||
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
// translate the site themeConfig here
|
||||
},
|
||||
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
// return default theme translations here
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
146
website/docs/api/plugin-methods/README.md
Normal file
146
website/docs/api/plugin-methods/README.md
Normal file
|
@ -0,0 +1,146 @@
|
|||
# Plugin Method References
|
||||
|
||||
:::caution
|
||||
|
||||
This section is a work in progress. Anchor links or even URLs are not guaranteed to be stable.
|
||||
|
||||
:::
|
||||
|
||||
Plugin APIs are shared by themes and plugins—themes are loaded just like plugins.
|
||||
|
||||
## Plugin module
|
||||
|
||||
Every plugin is imported as a module. The module is expected to have the following members:
|
||||
|
||||
- A **default export**: the constructor function for the plugin.
|
||||
- **Named exports**: the [static methods](./static-methods.md) called before plugins are initialized.
|
||||
|
||||
## Plugin constructor
|
||||
|
||||
The plugin module's default export is a constructor function with the signature `(context: LoadContext, options: PluginOptions) => Plugin`.
|
||||
|
||||
### `context` {#context}
|
||||
|
||||
`context` is plugin-agnostic, and the same object will be passed into all plugins used for a Docusaurus website. The `context` object contains the following fields:
|
||||
|
||||
```ts
|
||||
interface LoadContext {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `options` {#options}
|
||||
|
||||
`options` are the [second optional parameter when the plugins are used](../../using-plugins.md#configuring-plugins). `options` are plugin-specific and are specified by users when they use them in `docusaurus.config.js`. If there's a [`validateOptions`](./static-methods.md#validateOptions) function exported, the `options` will be validated and normalized beforehand.
|
||||
|
||||
Alternatively, if preset contains the plugin, the preset will then be in charge of passing the correct options into the plugin. It is up to individual plugin to define what options it takes.
|
||||
|
||||
## Example {#example}
|
||||
|
||||
Here's a mind model for a presumptuous plugin implementation.
|
||||
|
||||
```js
|
||||
// A JavaScript function that returns an object.
|
||||
// `context` is provided by Docusaurus. Example: siteConfig can be accessed from context.
|
||||
// `opts` is the user-defined options.
|
||||
function myPlugin(context, opts) {
|
||||
return {
|
||||
// A compulsory field used as the namespace for directories to cache
|
||||
// the intermediate data for each plugin.
|
||||
// If you're writing your own local plugin, you will want it to
|
||||
// be unique in order not to potentially conflict with imported plugins.
|
||||
// A good way will be to add your own project name within.
|
||||
name: 'docusaurus-my-project-cool-plugin',
|
||||
|
||||
async loadContent() {
|
||||
// The loadContent hook is executed after siteConfig and env has been loaded.
|
||||
// You can return a JavaScript object that will be passed to contentLoaded hook.
|
||||
},
|
||||
|
||||
async contentLoaded({content, actions}) {
|
||||
// The contentLoaded hook is done after loadContent hook is done.
|
||||
// `actions` are set of functional API provided by Docusaurus (e.g. addRoute)
|
||||
},
|
||||
|
||||
async postBuild(props) {
|
||||
// After docusaurus <build> finish.
|
||||
},
|
||||
|
||||
// TODO
|
||||
async postStart(props) {
|
||||
// docusaurus <start> finish
|
||||
},
|
||||
|
||||
// TODO
|
||||
afterDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverbefore
|
||||
},
|
||||
|
||||
// TODO
|
||||
beforeDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverafter
|
||||
},
|
||||
|
||||
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.
|
||||
},
|
||||
|
||||
getPathsToWatch() {
|
||||
// Paths to watch.
|
||||
},
|
||||
|
||||
getThemePath() {
|
||||
// Returns the path to the directory where the theme components can
|
||||
// be found.
|
||||
},
|
||||
|
||||
getClientModules() {
|
||||
// Return an array of paths to the modules that are to be imported
|
||||
// in the client bundle. These modules are imported globally before
|
||||
// React even renders the initial UI.
|
||||
},
|
||||
|
||||
extendCli(cli) {
|
||||
// Register an extra command to enhance the CLI of Docusaurus
|
||||
},
|
||||
|
||||
injectHtmlTags({content}) {
|
||||
// Inject head and/or body HTML tags.
|
||||
},
|
||||
|
||||
async getTranslationFiles({content}) {
|
||||
// Return translation files
|
||||
},
|
||||
|
||||
translateContent({content, translationFiles}) {
|
||||
// translate the plugin content here
|
||||
},
|
||||
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
// translate the site themeConfig here
|
||||
},
|
||||
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
// return default theme translations here
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
myPlugin.validateOptions = ({options, validate}) => {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
};
|
||||
|
||||
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
};
|
||||
|
||||
module.exports = myPlugin;
|
||||
```
|
2
website/docs/api/plugin-methods/_category_.yml
Normal file
2
website/docs/api/plugin-methods/_category_.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
label: Plugin method references
|
||||
position: 1
|
125
website/docs/api/plugin-methods/extend-infrastructure.md
Normal file
125
website/docs/api/plugin-methods/extend-infrastructure.md
Normal file
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Extending infrastructure
|
||||
|
||||
Docusaurus has some infrastructure like hot reloading, CLI, and swizzling, that can be extended by external plugins.
|
||||
|
||||
## `getPathsToWatch()` {#getPathsToWatch}
|
||||
|
||||
Specifies the paths to watch for plugins and themes. The paths are watched by the dev server so that the plugin lifecycles are reloaded when contents in the watched paths change. Note that the plugins and themes modules are initially called with `context` and `options` from Node, which you may use to find the necessary directory information about the site. Use this for files that are consumed server-side, because theme files are automatically watched by Webpack dev server.
|
||||
|
||||
Example:
|
||||
|
||||
```js {5-7} title="docusaurus-plugin/src/index.js"
|
||||
const path = require('path');
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
getPathsToWatch() {
|
||||
const contentPath = path.resolve(context.siteDir, options.path);
|
||||
return [`${contentPath}/**/*.{ts,tsx}`];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `extendCli(cli)` {#extendCli}
|
||||
|
||||
Register an extra command to enhance the CLI of Docusaurus. `cli` is [commander](https://www.npmjs.com/package/commander/v/5.1.0) object.
|
||||
|
||||
:::caution
|
||||
|
||||
The commander version matters! We use commander v5, and make sure you are referring to the right version documentation for available APIs.
|
||||
|
||||
:::
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
extendCli(cli) {
|
||||
cli
|
||||
.command('roll')
|
||||
.description('Roll a random number between 1 and 1000')
|
||||
.action(() => {
|
||||
console.log(Math.floor(Math.random() * 1000 + 1));
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getThemePath()` {#getThemePath}
|
||||
|
||||
Returns the path to the directory where the theme components can be found. When your users calls `swizzle`, `getThemePath` is called and its returned path is used to find your theme components.
|
||||
|
||||
For example, your `getThemePath` can be:
|
||||
|
||||
```js {6-8} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
getThemePath() {
|
||||
return path.resolve(__dirname, './theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getTypeScriptThemePath()` {#getTypeScriptThemePath}
|
||||
|
||||
Similar to `getThemePath()`, it should return the path to the directory where the source code of TypeScript theme components can be found. This path is purely for swizzling TypeScript theme components, and theme components under this path will **not** be resolved by Webpack. Therefore, it is not a replacement of `getThemePath()`. Typically, you can make the path returned by `getTypeScriptThemePath()` be your source directory, and make path returned by `getThemePath()` be the compiled JavaScript output.
|
||||
|
||||
:::tip
|
||||
|
||||
For TypeScript theme authors: you are strongly advised to make your compiled output as human-readable as possible. Only strip type annotations and don't transpile any syntaxes, because they will be handled by Webpack's Babel loader based on the targeted browser versions.
|
||||
|
||||
You should also format these files with Prettier. Remember—JS files can and will be directly consumed by your users.
|
||||
|
||||
:::
|
||||
|
||||
Example:
|
||||
|
||||
```js {6-13} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
getThemePath() {
|
||||
// Where compiled JavaScript output lives
|
||||
return path.join(__dirname, '../lib/theme');
|
||||
},
|
||||
getTypeScriptThemePath() {
|
||||
// Where TypeScript source code lives
|
||||
return path.resolve(__dirname, '../src/theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getSwizzleComponentList()` {#getSwizzleComponentList}
|
||||
|
||||
**This is a static method, not attached to any plugin instance.**
|
||||
|
||||
Returns a list of stable component that are considered as safe for swizzling. These components will be listed in swizzle component without `--danger`. All the components are considers unstable by default. If an empty array is returned, all components are considered unstable. If `undefined` is returned, all component are considered stable.
|
||||
|
||||
```js {0-12} title="my-theme/src/index.js"
|
||||
const swizzleAllowedComponents = [
|
||||
'CodeBlock',
|
||||
'DocSidebar',
|
||||
'Footer',
|
||||
'NotFound',
|
||||
'SearchBar',
|
||||
'hooks/useTheme',
|
||||
'prism-include-languages',
|
||||
];
|
||||
|
||||
myTheme.getSwizzleComponentList = () => swizzleAllowedComponents;
|
||||
```
|
121
website/docs/api/plugin-methods/i18n-lifecycles.md
Normal file
121
website/docs/api/plugin-methods/i18n-lifecycles.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# I18n lifecycles
|
||||
|
||||
Plugins use these lifecycles to load i18n-related data.
|
||||
|
||||
## `getTranslationFiles({content})` {#getTranslationFiles}
|
||||
|
||||
Plugins declare the JSON translation files they want to use.
|
||||
|
||||
Returns translation files `{path: string, content: ChromeI18nJSON}`:
|
||||
|
||||
- `path`: relative to the plugin localized folder `i18n/<locale>/pluginName`. Extension `.json` should be omitted to remain generic.
|
||||
- `content`: using the Chrome i18n JSON format.
|
||||
|
||||
These files will be written by the [`write-translations` CLI](../../cli.md#docusaurus-write-translations-sitedir) to the plugin i18n subfolder, and will be read in the appropriate locale before calling [`translateContent()`](#translateContent) and [`translateThemeConfig()`](#translateThemeConfig)
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
async getTranslationFiles({content}) {
|
||||
return [
|
||||
{
|
||||
path: 'sidebar-labels',
|
||||
content: {
|
||||
someSidebarLabel: {
|
||||
message: 'Some Sidebar Label',
|
||||
description: 'A label used in my plugin in the sidebar',
|
||||
},
|
||||
someLabelFromContent: content.myLabel,
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `translateContent({content,translationFiles})` {#translateContent}
|
||||
|
||||
Translate the plugin content, using the localized translation files.
|
||||
|
||||
Returns the localized plugin content.
|
||||
|
||||
The `contentLoaded()` lifecycle will be called with the localized plugin content returned by `translateContent()`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
translateContent({content, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...content,
|
||||
someContentLabel: myTranslationFile.someContentLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `translateThemeConfig({themeConfig,translationFiles})` {#translateThemeConfig}
|
||||
|
||||
Translate the site `themeConfig` labels, using the localized translation files.
|
||||
|
||||
Returns the localized `themeConfig`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...themeConfig,
|
||||
someThemeConfigLabel: myTranslationFile.someThemeConfigLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async getDefaultCodeTranslationMessages()` {#getDefaultCodeTranslationMessages}
|
||||
|
||||
Themes using the `<Translate>` API can provide default code translation messages.
|
||||
|
||||
It should return messages in `Record<string, string>`, where keys are translation ids and values are messages (without the description) localized using the site current locale.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
return readJsonFile(`${context.i18n.currentLocale}.json`);
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
388
website/docs/api/plugin-methods/lifecycle-apis.md
Normal file
388
website/docs/api/plugin-methods/lifecycle-apis.md
Normal file
|
@ -0,0 +1,388 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
toc_max_heading_level: 4
|
||||
---
|
||||
|
||||
# Lifecycle APIs
|
||||
|
||||
During build, plugins are loaded in parallel to fetch their own contents and render them to routes. Plugins may also configure webpack or post-process the generated files.
|
||||
|
||||
## `async loadContent()` {#loadContent}
|
||||
|
||||
Plugins should use this lifecycle to fetch from data sources (filesystem, remote API, headless CMS, etc.) or do some server processing. The return value is the content it needs.
|
||||
|
||||
For example, this plugin below return a random integer between 1 to 10 as content.
|
||||
|
||||
```js {5-6} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async loadContent() {
|
||||
return 1 + Math.floor(Math.random() * 10);
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async contentLoaded({content, actions})` {#contentLoaded}
|
||||
|
||||
The data that was loaded in `loadContent` will be consumed in `contentLoaded`. It can be rendered to routes, registered as global data, etc.
|
||||
|
||||
### `content` {#content}
|
||||
|
||||
`contentLoaded` will be called _after_ `loadContent` is done. The return value of `loadContent()` will be passed to `contentLoaded` as `content`.
|
||||
|
||||
### `actions` {#actions}
|
||||
|
||||
`actions` contain three functions:
|
||||
|
||||
#### `addRoute(config: RouteConfig): void` {#addRoute}
|
||||
|
||||
Create a route to add to the website.
|
||||
|
||||
```ts
|
||||
interface RouteConfig {
|
||||
path: string;
|
||||
component: string;
|
||||
modules?: RouteModule;
|
||||
routes?: RouteConfig[];
|
||||
exact?: boolean;
|
||||
priority?: number;
|
||||
}
|
||||
interface RouteModule {
|
||||
[module: string]: Module | RouteModule | RouteModule[];
|
||||
}
|
||||
type Module =
|
||||
| {
|
||||
path: string;
|
||||
__import?: boolean;
|
||||
query?: ParsedUrlQueryInput;
|
||||
}
|
||||
| string;
|
||||
```
|
||||
|
||||
#### `createData(name: string, data: any): Promise<string>` {#createData}
|
||||
|
||||
A declarative callback to create static data (generally json or string) which can later be provided to your routes as props. Takes the file name and data to be stored, and returns the actual data file's path.
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
|
||||
export default function FriendsComponent({friends}) {
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-23} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {createData, addRoute} = actions;
|
||||
// Create friends.json
|
||||
const friends = ['Yangshun', 'Sebastien'];
|
||||
const friendsJsonPath = await createData(
|
||||
'friends.json',
|
||||
JSON.stringify(friends),
|
||||
);
|
||||
|
||||
// Add the '/friends' routes, and ensure it receives the friends props
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
modules: {
|
||||
// propName -> JSON file path
|
||||
friends: friendsJsonPath,
|
||||
},
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### `setGlobalData(data: any): void`
|
||||
|
||||
This function permits to create some global plugin data that can be read from any page, including the pages created by other plugins, and your theme layout.
|
||||
|
||||
This data becomes accessible to your client-side/theme code through the [`useGlobalData`](../../docusaurus-core.md#useGlobalData) and [`usePluginData`](../../docusaurus-core.md#usePluginData) hooks.
|
||||
|
||||
:::caution
|
||||
|
||||
Global data is... global: its size affects the loading time of all pages of your site, so try to keep it small. Prefer `createData` and page-specific data whenever possible.
|
||||
|
||||
:::
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
import {usePluginData} from '@docusaurus/useGlobalData';
|
||||
|
||||
export default function FriendsComponent() {
|
||||
const {friends} = usePluginData('my-friends-plugin');
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-14} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {setGlobalData, addRoute} = actions;
|
||||
// Create friends global data
|
||||
setGlobalData({friends: ['Yangshun', 'Sebastien']});
|
||||
|
||||
// Add the '/friends' routes
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## `configureWebpack(config, isServer, utils, content)` {#configureWebpack}
|
||||
|
||||
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 second 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.
|
||||
|
||||
### `isServer` {#isServer}
|
||||
|
||||
`configureWebpack` will be called both in server build and in client build. The server build receives `true` and the client build receives `false` as `isServer`.
|
||||
|
||||
### `utils` {#utils}
|
||||
|
||||
`configureWebpack` also receives an util object:
|
||||
|
||||
- `getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]`
|
||||
- `getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
|
||||
|
||||
You may use them to return your webpack configures conditionally.
|
||||
|
||||
For example, this plugin below modify the webpack config to transpile `.foo` file.
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
// highlight-start
|
||||
configureWebpack(config, isServer, utils) {
|
||||
const {getCacheLoader} = utils;
|
||||
return {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.foo$/,
|
||||
use: [getCacheLoader(isServer), 'my-custom-webpack-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `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).
|
||||
|
||||
It is possible to specify the merge strategy. For example, if you want a webpack rule to be prepended instead of appended:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
configureWebpack(config, isServer, utils) {
|
||||
return {
|
||||
// highlight-start
|
||||
mergeStrategy: {'module.rules': 'prepend'},
|
||||
module: {rules: [myRuleToPrepend]},
|
||||
// highlight-end
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.
|
||||
|
||||
## `configurePostCss(options)` {#configurePostCss}
|
||||
|
||||
Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during the generation of the client bundle.
|
||||
|
||||
Should return the mutated `postcssOptions`.
|
||||
|
||||
By default, `postcssOptions` looks like this:
|
||||
|
||||
```js
|
||||
const postcssOptions = {
|
||||
ident: 'postcss',
|
||||
plugins: [require('autoprefixer')],
|
||||
};
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// highlight-start
|
||||
configurePostCss(postcssOptions) {
|
||||
// Appends new PostCSS plugin.
|
||||
postcssOptions.plugins.push(require('postcss-import'));
|
||||
return postcssOptions;
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `postBuild(props)` {#postBuild}
|
||||
|
||||
Called when a (production) build finishes.
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
headTags: string;
|
||||
preBodyTags: string;
|
||||
postBodyTags: string;
|
||||
routesPaths: string[];
|
||||
plugins: Plugin<any>[];
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
|
||||
// Print out to console all the rendered routes.
|
||||
routesPaths.map((route) => {
|
||||
console.log(route);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `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;
|
||||
preBodyTags?: HtmlTags;
|
||||
postBodyTags?: HtmlTags;
|
||||
};
|
||||
|
||||
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
|
||||
|
||||
interface HtmlTagObject {
|
||||
/**
|
||||
* Attributes of the HTML tag
|
||||
* E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
|
||||
*/
|
||||
attributes?: {
|
||||
[attributeName: string]: string | boolean;
|
||||
};
|
||||
/**
|
||||
* The tag name e.g. `div`, `script`, `link`, `meta`
|
||||
*/
|
||||
tagName: string;
|
||||
/**
|
||||
* The inner HTML
|
||||
*/
|
||||
innerHTML?: string;
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
loadContent: async () => {
|
||||
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
|
||||
},
|
||||
// highlight-start
|
||||
injectHtmlTags({content}) {
|
||||
return {
|
||||
headTags: [
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
rel: 'preconnect',
|
||||
href: 'https://www.github.com',
|
||||
},
|
||||
},
|
||||
...content.remoteHeadTags,
|
||||
],
|
||||
preBodyTags: [
|
||||
{
|
||||
tagName: 'script',
|
||||
attributes: {
|
||||
charset: 'utf-8',
|
||||
src: '/noflash.js',
|
||||
},
|
||||
},
|
||||
],
|
||||
postBodyTags: [`<div> This is post body </div>`],
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getClientModules()` {#getClientModules}
|
||||
|
||||
Returns an array of paths to the modules that are to be imported in the client bundle. These modules are imported globally before React even renders the initial UI.
|
||||
|
||||
As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user:
|
||||
|
||||
```js {7-9} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
const {customCss, customJs} = options || {};
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getClientModules() {
|
||||
return [customCss, customJs];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
115
website/docs/api/plugin-methods/static-methods.md
Normal file
115
website/docs/api/plugin-methods/static-methods.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Static methods
|
||||
|
||||
Static methods are not part of the plugin instance—they are attached to the constructor function. These methods are used to validate and normalize the plugin options and theme config, which are then used as constructor parameters to initialize the plugin instance.
|
||||
|
||||
## `validateOptions({options, validate})` {#validateOptions}
|
||||
|
||||
Return validated and normalized options for the plugin. This method is called before the plugin is initialized.You must return options since the returned options will be passed to plugin during initialization.
|
||||
|
||||
### `options` {#options}
|
||||
|
||||
`validateOptions` is called with `options` passed to plugin for validation and normalization.
|
||||
|
||||
### `validate` {#validate}
|
||||
|
||||
`validateOptions` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and options as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of options.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options and return options in case of success.
|
||||
|
||||
```js {8-11} title="my-plugin/src/index.js"
|
||||
function myPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
myPlugin.validateOptions = ({options, validate}) => {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
};
|
||||
|
||||
module.exports = myPlugin;
|
||||
```
|
||||
|
||||
In TypeScript, you can also choose to export this as a separate named export.
|
||||
|
||||
```ts {8-11} title="my-plugin/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateOptions({options, validate}) {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
}
|
||||
```
|
||||
|
||||
## `validateThemeConfig({themeConfig, validate})` {#validateThemeConfig}
|
||||
|
||||
Return validated and normalized configuration for the theme.
|
||||
|
||||
### `themeConfig` {#themeConfig}
|
||||
|
||||
`validateThemeConfig` is called with `themeConfig` provided in `docusaurus.config.js` for validation and normalization.
|
||||
|
||||
### `validate` {#validate-1}
|
||||
|
||||
`validateThemeConfig` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and `themeConfig` as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of theme config.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options.
|
||||
|
||||
```js {8-11} title="my-theme/src/index.js"
|
||||
function myPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
};
|
||||
|
||||
module.exports = validateThemeConfig;
|
||||
```
|
||||
|
||||
In TypeScript, you can also choose to export this as a separate named export.
|
||||
|
||||
```ts {8-11} title="my-theme/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateThemeConfig({themeConfig, validate}) {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
}
|
||||
```
|
|
@ -312,7 +312,7 @@ You can even omit a children prop and specify a translation string in your `code
|
|||
|
||||
## Hooks {#hooks}
|
||||
|
||||
### `useDocusaurusContext` {#usedocusauruscontext}
|
||||
### `useDocusaurusContext` {#useDocusaurusContext}
|
||||
|
||||
React hook to access Docusaurus Context. Context contains `siteConfig` object from [docusaurus.config.js](api/docusaurus.config.js.md), and some additional site metadata.
|
||||
|
||||
|
@ -396,7 +396,7 @@ const MyComponent = () => {
|
|||
};
|
||||
```
|
||||
|
||||
### `useBaseUrl` {#usebaseurl}
|
||||
### `useBaseUrl` {#useBaseUrl}
|
||||
|
||||
React hook to prepend your site `baseUrl` to a string.
|
||||
|
||||
|
@ -446,7 +446,7 @@ Prefer a `require()` call for [assets](./guides/markdown-features/markdown-featu
|
|||
|
||||
:::
|
||||
|
||||
### `useBaseUrlUtils` {#usebaseurlutils}
|
||||
### `useBaseUrlUtils` {#useBaseUrlUtils}
|
||||
|
||||
Sometimes `useBaseUrl` is not good enough. This hook return additional utils related to your site's base url.
|
||||
|
||||
|
@ -466,7 +466,7 @@ const Component = () => {
|
|||
};
|
||||
```
|
||||
|
||||
### `useGlobalData` {#useglobaldata}
|
||||
### `useGlobalData` {#useGlobalData}
|
||||
|
||||
React hook to access Docusaurus global data created by all the plugins.
|
||||
|
||||
|
@ -507,7 +507,7 @@ Inspect your site's global data at `./docusaurus/globalData.json`
|
|||
|
||||
:::
|
||||
|
||||
### `usePluginData` {#useplugindata}
|
||||
### `usePluginData` {#usePluginData}
|
||||
|
||||
Access global data created by a specific plugin instance.
|
||||
|
||||
|
@ -531,7 +531,7 @@ const MyComponent = () => {
|
|||
};
|
||||
```
|
||||
|
||||
### `useAllPluginInstancesData` {#useallplugininstancesdata}
|
||||
### `useAllPluginInstancesData` {#useAllPluginInstancesData}
|
||||
|
||||
Access global data created by a specific plugin. Given a plugin name, it returns the data of all the plugins instances of that name, by plugin id.
|
||||
|
||||
|
|
|
@ -134,4 +134,4 @@ For themes that supports TypeScript theme components, you can add the `--typescr
|
|||
npm run swizzle @docusaurus/theme-classic Footer -- --typescript
|
||||
```
|
||||
|
||||
At this moment, the only official Docusaurus theme that supports TypeScript theme components is `@docusaurus/theme-classic`. If you are a Docusaurus theme package author who wants to add TypeScript support, see the [Lifecycle APIs docs](./api/lifecycle-apis.md#gettypescriptthemepath).
|
||||
At this moment, the only official Docusaurus theme that supports TypeScript theme components is `@docusaurus/theme-classic`. If you are a Docusaurus theme package author who wants to add TypeScript support, see the [Lifecycle APIs docs](./api/plugin-methods/extend-infrastructure.md#getTypeScriptThemePath).
|
||||
|
|
|
@ -119,15 +119,11 @@ Docusaurus' implementation of the plugins system provides us with a convenient w
|
|||
|
||||
## Creating plugins {#creating-plugins}
|
||||
|
||||
A plugin is a function that takes two parameters: `context` and `options`.
|
||||
|
||||
It returns a plugin instance object, containing plugin [lifecycle APIs](./api/lifecycle-apis.md).
|
||||
|
||||
It can be defined as a function or a module.
|
||||
A plugin is a function that takes two parameters: `context` and `options`. It returns a plugin instance object. You can create plugins as functions or modules. For more information, refer to the [plugin method references section](./api/plugin-methods/README.md).
|
||||
|
||||
### Functional definition {#functional-definition}
|
||||
|
||||
You can use a plugin as a function, directly in the Docusaurus config file:
|
||||
You can use a plugin as a function directly included in the Docusaurus config file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
module.exports = {
|
||||
|
@ -154,7 +150,7 @@ module.exports = {
|
|||
|
||||
### Module definition {#module-definition}
|
||||
|
||||
You can use a plugin as a module, loading it from a separate file or NPM package:
|
||||
You can use a plugin as a module path referencing a separate file or NPM package:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
module.exports = {
|
||||
|
@ -168,7 +164,7 @@ module.exports = {
|
|||
};
|
||||
```
|
||||
|
||||
Then in the folder `my-plugin` you can create an index.js such as this
|
||||
Then in the folder `my-plugin` you can create an index.js such as this:
|
||||
|
||||
```js title="my-plugin.js"
|
||||
module.exports = function myPlugin(context, options) {
|
||||
|
@ -185,25 +181,3 @@ module.exports = function myPlugin(context, options) {
|
|||
};
|
||||
};
|
||||
```
|
||||
|
||||
#### `context` {#context}
|
||||
|
||||
`context` is plugin-agnostic, and the same object will be passed into all plugins used for a Docusaurus website. The `context` object contains the following fields:
|
||||
|
||||
```ts
|
||||
interface LoadContext {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
}
|
||||
```
|
||||
|
||||
#### `options` {#options}
|
||||
|
||||
`options` are the [second optional parameter when the plugins are used](using-plugins.md#configuring-plugins). `options` are plugin-specific and are specified by users when they use them in `docusaurus.config.js`. Alternatively, if preset contains the plugin, the preset will then be in charge of passing the correct options into the plugin. It is up to individual plugin to define what options it takes.
|
||||
|
||||
#### Return value {#return-value}
|
||||
|
||||
The returned object value should implement the [lifecycle APIs](./api/lifecycle-apis.md).
|
||||
|
|
|
@ -240,13 +240,13 @@ website
|
|||
|
||||
There are two lifecycle methods that are essential to theme implementation:
|
||||
|
||||
- [`getThemePath()`](./api/lifecycle-apis.md#getthemepath)
|
||||
- [`getClientModules()`](./api/lifecycle-apis.md#getclientmodules)
|
||||
- [`getThemePath()`](./api/plugin-methods/extend-infrastructure.md#getThemePath)
|
||||
- [`getClientModules()`](./api/plugin-methods/lifecycle-apis.md#getClientModules)
|
||||
|
||||
These lifecycle methods are not essential but recommended:
|
||||
|
||||
- [`validateThemeConfig({themeConfig, validate})`](./api/lifecycle-apis.md#validatethemeconfigthemeconfig-validate)
|
||||
- [`validateOptions({options, validate})`](./api/lifecycle-apis.md#validateoptionsoptions-validate)
|
||||
- [`validateThemeConfig({themeConfig, validate})`](./api/plugin-methods/static-methods.md#validateThemeConfig)
|
||||
- [`validateOptions({options, validate})`](./api/plugin-methods/static-methods.md#validateOptions)
|
||||
|
||||
<!--
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
# Renamed doc routes
|
||||
/docs/docusaurus.config.js /docs/api/docusaurus-config
|
||||
/docs/next/docusaurus.config.js /docs/next/api/docusaurus-config
|
||||
/docs/lifecycle-apis /docs/api/plugin-methods/
|
||||
/docs/next/lifecycle-apis /docs/next/api/plugin-methods/
|
||||
|
||||
|
||||
# v2.docusaurus.io domain redirect after we put v2 on docusaurus.io
|
||||
|
|
|
@ -414,7 +414,7 @@ module.exports = {
|
|||
};
|
||||
```
|
||||
|
||||
See also: [`getClientModules()`](lifecycle-apis.md#getclientmodules).
|
||||
See also: [`getClientModules()`](./plugin-methods/lifecycle-apis.md#getClientModules).
|
||||
|
||||
### `ssrTemplate` {#ssrtemplate}
|
||||
|
||||
|
|
|
@ -1,832 +0,0 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
id: lifecycle-apis
|
||||
title: Lifecycle APIs
|
||||
slug: /lifecycle-apis
|
||||
toc_max_heading_level: 4
|
||||
---
|
||||
|
||||
:::caution
|
||||
|
||||
This section is a work in progress.
|
||||
|
||||
:::
|
||||
|
||||
Lifecycle APIs are shared by Themes and Plugins.
|
||||
|
||||
## `validateOptions({options, validate})` {#validateoptionsoptions-validate}
|
||||
|
||||
Return validated and normalized options for the plugin. This method is called before the plugin is initialized.You must return options since the returned options will be passed to plugin during initialization.
|
||||
|
||||
### `options` {#options}
|
||||
|
||||
`validateOptions` is called with `options` passed to plugin for validation and normalization.
|
||||
|
||||
### `validate` {#validate}
|
||||
|
||||
`validateOptions` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and options as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of options.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options and return options in case of success.
|
||||
|
||||
```js {8-11} title="my-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.validateOptions = ({options, validate}) => {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
};
|
||||
```
|
||||
|
||||
You can also use ES modules style exports.
|
||||
|
||||
```ts {8-11} title="my-plugin/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateOptions({options, validate}) {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
}
|
||||
```
|
||||
|
||||
## `validateThemeConfig({themeConfig, validate})` {#validatethemeconfigthemeconfig-validate}
|
||||
|
||||
Return validated and normalized configuration for the theme.
|
||||
|
||||
### `themeConfig` {#themeconfig}
|
||||
|
||||
`validateThemeConfig` is called with `themeConfig` provided in `docusaurus.config.js` for validation and normalization.
|
||||
|
||||
### `validate` {#validate-1}
|
||||
|
||||
`validateThemeConfig` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and `themeConfig` as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of theme config.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options.
|
||||
|
||||
```js {8-11} title="my-theme/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.validateThemeConfig = ({themeConfig, validate}) => {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
};
|
||||
```
|
||||
|
||||
You can also use ES modules style exports.
|
||||
|
||||
```ts {8-11} title="my-theme/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateThemeConfig({themeConfig, validate}) {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
}
|
||||
```
|
||||
|
||||
## `getPathsToWatch()` {#getpathstowatch}
|
||||
|
||||
Specifies the paths to watch for plugins and themes. The paths are watched by the dev server so that the plugin lifecycles are reloaded when contents in the watched paths change. Note that the plugins and themes modules are initially called with `context` and `options` from Node, which you may use to find the necessary directory information about the site.
|
||||
|
||||
Example:
|
||||
|
||||
```js {5-7} title="docusaurus-plugin/src/index.js"
|
||||
const path = require('path');
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
getPathsToWatch() {
|
||||
const contentPath = path.resolve(context.siteDir, options.path);
|
||||
return [`${contentPath}/**/*.{ts,tsx}`];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async loadContent()` {#async-loadcontent}
|
||||
|
||||
Plugins should use this lifecycle to fetch from data sources (filesystem, remote API, headless CMS, etc) or doing some server processing.
|
||||
|
||||
For example, this plugin below return a random integer between 1 to 10 as content;
|
||||
|
||||
```js {5-6} title="docusaurus-plugin/src/index.js"
|
||||
const path = require('path');
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async loadContent() {
|
||||
return 1 + Math.floor(Math.random() * 10);
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async contentLoaded({content, actions})` {#async-contentloadedcontent-actions}
|
||||
|
||||
Plugins should use the data loaded in `loadContent` and construct the pages/routes that consume the loaded data (optional).
|
||||
|
||||
### `content` {#content}
|
||||
|
||||
`contentLoaded` will be called _after_ `loadContent` is done, the return value of `loadContent()` will be passed to `contentLoaded` as `content`.
|
||||
|
||||
### `actions` {#actions}
|
||||
|
||||
`actions` contain three functions:
|
||||
|
||||
#### `addRoute(config: RouteConfig): void`
|
||||
|
||||
Create a route to add to the website.
|
||||
|
||||
```typescript
|
||||
interface RouteConfig {
|
||||
path: string;
|
||||
component: string;
|
||||
modules?: RouteModule;
|
||||
routes?: RouteConfig[];
|
||||
exact?: boolean;
|
||||
priority?: number;
|
||||
}
|
||||
interface RouteModule {
|
||||
[module: string]: Module | RouteModule | RouteModule[];
|
||||
}
|
||||
type Module =
|
||||
| {
|
||||
path: string;
|
||||
__import?: boolean;
|
||||
query?: ParsedUrlQueryInput;
|
||||
}
|
||||
| string;
|
||||
```
|
||||
|
||||
#### `createData(name: string, data: any): Promise<string>`
|
||||
|
||||
A function to help you create static data (generally json or string), that you can provide to your routes as props.
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
|
||||
export default function FriendsComponent({friends}) {
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-23} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {createData, addRoute} = actions;
|
||||
// Create friends.json
|
||||
const friends = ['Yangshun', 'Sebastien'];
|
||||
const friendsJsonPath = await createData(
|
||||
'friends.json',
|
||||
JSON.stringify(friends),
|
||||
);
|
||||
|
||||
// Add the '/friends' routes, and ensure it receives the friends props
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
modules: {
|
||||
// propName -> JSON file path
|
||||
friends: friendsJsonPath,
|
||||
},
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### `setGlobalData(data: any): void`
|
||||
|
||||
This function permits to create some global plugin data, that can be read from any page, including the pages created by other plugins, and your theme layout.
|
||||
|
||||
This data become accessible to your client-side/theme code, through the [`useGlobalData`](./docusaurus-core.md#useglobaldata) and [`usePluginData`](./docusaurus-core.md#useplugindatapluginname-string-pluginid-string).
|
||||
|
||||
One this data is created, you can access it with the global data hooks APIs.
|
||||
|
||||
:::caution
|
||||
|
||||
Global data is... global: its size affects the loading time of all pages of your site, so try to keep it small.
|
||||
|
||||
Prefer `createData` and page-specific data whenever possible.
|
||||
|
||||
:::
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
import {usePluginData} from '@docusaurus/useGlobalData';
|
||||
|
||||
export default function FriendsComponent() {
|
||||
const {friends} = usePluginData('my-friends-plugin');
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-14} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {setGlobalData, addRoute} = actions;
|
||||
// Create friends global data
|
||||
setGlobalData({friends: ['Yangshun', 'Sebastien']});
|
||||
|
||||
// Add the '/friends' routes
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## `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.
|
||||
|
||||
### `isServer` {#isserver}
|
||||
|
||||
`configureWebpack` will be called both in server build and in client build. The server build receives `true` and the client build receives `false` as `isServer`.
|
||||
|
||||
### `utils` {#utils}
|
||||
|
||||
`configureWebpack` also receives an util object:
|
||||
|
||||
- `getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]`
|
||||
- `getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
|
||||
|
||||
You may use them to return your webpack configures conditionally.
|
||||
|
||||
For example, this plugin below modify the webpack config to transpile `.foo` file.
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
// highlight-start
|
||||
configureWebpack(config, isServer, utils) {
|
||||
const {getCacheLoader} = utils;
|
||||
return {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.foo$/,
|
||||
use: [getCacheLoader(isServer), 'my-custom-webpack-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `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).
|
||||
|
||||
It is possible to specify the merge strategy. For example, if you want a webpack rule to be prepended instead of appended:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
configureWebpack(config, isServer, utils) {
|
||||
return {
|
||||
// highlight-start
|
||||
mergeStrategy: {'module.rules': 'prepend'},
|
||||
module: {rules: [myRuleToPrepend]},
|
||||
// highlight-end
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.
|
||||
|
||||
## `configurePostCss(options)` {#configurepostcssoptions}
|
||||
|
||||
Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during the generation of the client bundle.
|
||||
|
||||
Should return the mutated `postcssOptions`.
|
||||
|
||||
By default, `postcssOptions` looks like this:
|
||||
|
||||
```js
|
||||
const postcssOptions = {
|
||||
ident: 'postcss',
|
||||
plugins: [require('autoprefixer')],
|
||||
};
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// highlight-start
|
||||
configurePostCss(postcssOptions) {
|
||||
// Appends new PostCSS plugin.
|
||||
postcssOptions.plugins.push(require('postcss-import'));
|
||||
return postcssOptions;
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `postBuild(props)` {#postbuildprops}
|
||||
|
||||
Called when a (production) build finishes.
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
headTags: string;
|
||||
preBodyTags: string;
|
||||
postBodyTags: string;
|
||||
routesPaths: string[];
|
||||
plugins: Plugin<any>[];
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
|
||||
// Print out to console all the rendered routes.
|
||||
routesPaths.map((route) => {
|
||||
console.log(route);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `extendCli(cli)` {#extendclicli}
|
||||
|
||||
Register an extra command to enhance the CLI of Docusaurus. `cli` is [commander](https://www.npmjs.com/package/commander) object.
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
extendCli(cli) {
|
||||
cli
|
||||
.command('roll')
|
||||
.description('Roll a random number between 1 and 1000')
|
||||
.action(() => {
|
||||
console.log(Math.floor(Math.random() * 1000 + 1));
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `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;
|
||||
preBodyTags?: HtmlTags;
|
||||
postBodyTags?: HtmlTags;
|
||||
};
|
||||
|
||||
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
|
||||
|
||||
interface HtmlTagObject {
|
||||
/**
|
||||
* Attributes of the HTML tag
|
||||
* E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
|
||||
*/
|
||||
attributes?: {
|
||||
[attributeName: string]: string | boolean;
|
||||
};
|
||||
/**
|
||||
* The tag name e.g. `div`, `script`, `link`, `meta`
|
||||
*/
|
||||
tagName: string;
|
||||
/**
|
||||
* The inner HTML
|
||||
*/
|
||||
innerHTML?: string;
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
loadContent: async () => {
|
||||
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
|
||||
},
|
||||
// highlight-start
|
||||
injectHtmlTags({content}) {
|
||||
return {
|
||||
headTags: [
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
rel: 'preconnect',
|
||||
href: 'https://www.github.com',
|
||||
},
|
||||
},
|
||||
...content.remoteHeadTags,
|
||||
],
|
||||
preBodyTags: [
|
||||
{
|
||||
tagName: 'script',
|
||||
attributes: {
|
||||
charset: 'utf-8',
|
||||
src: '/noflash.js',
|
||||
},
|
||||
},
|
||||
],
|
||||
postBodyTags: [`<div> This is post body </div>`],
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getThemePath()` {#getthemepath}
|
||||
|
||||
Returns the path to the directory where the theme components can be found. When your users calls `swizzle`, `getThemePath` is called and its returned path is used to find your theme components.
|
||||
|
||||
If you use the folder directory above, your `getThemePath` can be:
|
||||
|
||||
```js {6-8} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getThemePath() {
|
||||
return path.resolve(__dirname, './theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getTypeScriptThemePath()` {#gettypescriptthemepath}
|
||||
|
||||
Similar to `getThemePath()`, it should return the path to the directory where the source code of TypeScript theme components can be found. Theme components under this path will **not** be resolved by Webpack. Therefore, it is not a replacement of `getThemePath()`. Instead, this path is purely for swizzling TypeScript theme components.
|
||||
|
||||
If you want to support TypeScript component swizzling for your theme, you can make the path returned by `getTypeScriptThemePath()` be your source directory, and make path returned by `getThemePath()` be the compiled JavaScript output.
|
||||
|
||||
Example:
|
||||
|
||||
```js {6-13} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getThemePath() {
|
||||
// Where compiled JavaScript output lives
|
||||
return path.join(__dirname, '..', 'lib', 'theme');
|
||||
},
|
||||
getTypeScriptThemePath() {
|
||||
// Where TypeScript source code lives
|
||||
return path.resolve(__dirname, './theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getSwizzleComponentList()` {#getswizzlecomponentlist}
|
||||
|
||||
Return a list of stable component that are considered as safe for swizzling. These components will be listed in swizzle component without `--danger`. All the components are considers unstable by default. If an empty array is returned then all components are considered unstable, if `undefined` is returned then all component are considered stable.
|
||||
|
||||
```js {0-12} title="my-theme/src/index.js"
|
||||
const swizzleAllowedComponents = [
|
||||
'CodeBlock',
|
||||
'DocSidebar',
|
||||
'Footer',
|
||||
'NotFound',
|
||||
'SearchBar',
|
||||
'hooks/useTheme',
|
||||
'prism-include-languages',
|
||||
];
|
||||
|
||||
module.exports.getSwizzleComponentList = () => swizzleAllowedComponents;
|
||||
```
|
||||
|
||||
## `getClientModules()` {#getclientmodules}
|
||||
|
||||
Returns an array of paths to the modules that are to be imported in the client bundle. These modules are imported globally before React even renders the initial UI.
|
||||
|
||||
As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user:
|
||||
|
||||
```js {7-9} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
const {customCss, customJs} = options || {};
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getClientModules() {
|
||||
return [customCss, customJs];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
<!--
|
||||
For example, the in docusaurus-plugin-content-docs:
|
||||
|
||||
In loadContent, it loads the doc Markdown files based on the specified directory in options (defaulting to docs).
|
||||
In contentLoaded, for each doc Markdown file, a route is created: /doc/installation, /doc/getting-started, etc.
|
||||
-->
|
||||
|
||||
## i18n lifecycles {#i18n-lifecycles}
|
||||
|
||||
### `getTranslationFiles({content})` {#get-translation-files}
|
||||
|
||||
Plugins declare the JSON translation files they want to use.
|
||||
|
||||
Returns translation files `{path: string, content: ChromeI18nJSON}`:
|
||||
|
||||
- Path: relative to the plugin localized folder `i18n/<locale>/pluginName`. Extension `.json` is not necessary.
|
||||
- Content: using the Chrome i18n JSON format
|
||||
|
||||
These files will be written by the [`write-translations` CLI](./cli.md#docusaurus-write-translations-sitedir) to the plugin i18n subfolder, and will be read in the appropriate locale before calling [`translateContent()`](#translate-content) and [`translateThemeConfig()`](#translate-theme-config)
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
async getTranslationFiles({content}) {
|
||||
return [
|
||||
{
|
||||
path: 'sidebar-labels',
|
||||
content: {
|
||||
someSidebarLabel: {
|
||||
message: 'Some Sidebar Label',
|
||||
description: 'A label used in my plugin in the sidebar',
|
||||
},
|
||||
someLabelFromContent: content.myLabel,
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `translateContent({content,translationFiles})` {#translate-content}
|
||||
|
||||
Translate the plugin content, using the localized translation files.
|
||||
|
||||
Returns the localized plugin content.
|
||||
|
||||
The `contentLoaded()` lifecycle will be called with the localized plugin content returned by `translateContent()`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
translateContent({content, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...content,
|
||||
someContentLabel: myTranslationFile.someContentLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `translateThemeConfig({themeConfig,translationFiles})` {#translate-theme-config}
|
||||
|
||||
Translate the site `themeConfig` labels, using the localized translation files.
|
||||
|
||||
Returns the localized `themeConfig`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...themeConfig,
|
||||
someThemeConfigLabel: myTranslationFile.someThemeConfigLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `async getDefaultCodeTranslationMessages()` {#get-default-code-translation-messages}
|
||||
|
||||
Themes using the `<Translate>` API can provide default code translation messages.
|
||||
|
||||
It should return messages in `Record<string,string`>, where keys are translation ids and values are messages (without the description) localized using the site current locale.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
return readJsonFile(`${context.i18n.currentLocale}.json`);
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Example {#example}
|
||||
|
||||
Here's a mind model for a presumptuous plugin implementation.
|
||||
|
||||
```jsx
|
||||
const DEFAULT_OPTIONS = {
|
||||
// Some defaults.
|
||||
};
|
||||
|
||||
// A JavaScript function that returns an object.
|
||||
// `context` is provided by Docusaurus. Example: siteConfig can be accessed from context.
|
||||
// `opts` is the user-defined options.
|
||||
module.exports = function (context, opts) {
|
||||
// Merge defaults with user-defined options.
|
||||
const options = {...DEFAULT_OPTIONS, ...options};
|
||||
|
||||
return {
|
||||
// A compulsory field used as the namespace for directories to cache
|
||||
// the intermediate data for each plugin.
|
||||
// If you're writing your own local plugin, you will want it to
|
||||
// be unique in order not to potentially conflict with imported plugins.
|
||||
// A good way will be to add your own project name within.
|
||||
name: 'docusaurus-my-project-cool-plugin',
|
||||
|
||||
async loadContent() {
|
||||
// The loadContent hook is executed after siteConfig and env has been loaded.
|
||||
// You can return a JavaScript object that will be passed to contentLoaded hook.
|
||||
},
|
||||
|
||||
async contentLoaded({content, actions}) {
|
||||
// The contentLoaded hook is done after loadContent hook is done.
|
||||
// `actions` are set of functional API provided by Docusaurus (e.g. addRoute)
|
||||
},
|
||||
|
||||
async postBuild(props) {
|
||||
// After docusaurus <build> finish.
|
||||
},
|
||||
|
||||
// TODO
|
||||
async postStart(props) {
|
||||
// docusaurus <start> finish
|
||||
},
|
||||
|
||||
// TODO
|
||||
afterDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverbefore
|
||||
},
|
||||
|
||||
// TODO
|
||||
beforeDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverafter
|
||||
},
|
||||
|
||||
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.
|
||||
},
|
||||
|
||||
getPathsToWatch() {
|
||||
// Paths to watch.
|
||||
},
|
||||
|
||||
getThemePath() {
|
||||
// Returns the path to the directory where the theme components can
|
||||
// be found.
|
||||
},
|
||||
|
||||
getClientModules() {
|
||||
// Return an array of paths to the modules that are to be imported
|
||||
// in the client bundle. These modules are imported globally before
|
||||
// React even renders the initial UI.
|
||||
},
|
||||
|
||||
extendCli(cli) {
|
||||
// Register an extra command to enhance the CLI of Docusaurus
|
||||
},
|
||||
|
||||
injectHtmlTags({content}) {
|
||||
// Inject head and/or body HTML tags.
|
||||
},
|
||||
|
||||
async getTranslationFiles({content}) {
|
||||
// Return translation files
|
||||
},
|
||||
|
||||
translateContent({content, translationFiles}) {
|
||||
// translate the plugin content here
|
||||
},
|
||||
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
// translate the site themeConfig here
|
||||
},
|
||||
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
// return default theme translations here
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
|
@ -0,0 +1,146 @@
|
|||
# Plugin Method References
|
||||
|
||||
:::caution
|
||||
|
||||
This section is a work in progress. Anchor links or even URLs are not guaranteed to be stable.
|
||||
|
||||
:::
|
||||
|
||||
Plugin APIs are shared by themes and plugins—themes are loaded just like plugins.
|
||||
|
||||
## Plugin module
|
||||
|
||||
Every plugin is imported as a module. The module is expected to have the following members:
|
||||
|
||||
- A **default export**: the constructor function for the plugin.
|
||||
- **Named exports**: the [static methods](./static-methods.md) called before plugins are initialized.
|
||||
|
||||
## Plugin constructor
|
||||
|
||||
The plugin module's default export is a constructor function with the signature `(context: LoadContext, options: PluginOptions) => Plugin`.
|
||||
|
||||
### `context` {#context}
|
||||
|
||||
`context` is plugin-agnostic, and the same object will be passed into all plugins used for a Docusaurus website. The `context` object contains the following fields:
|
||||
|
||||
```ts
|
||||
interface LoadContext {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `options` {#options}
|
||||
|
||||
`options` are the [second optional parameter when the plugins are used](../../using-plugins.md#configuring-plugins). `options` are plugin-specific and are specified by users when they use them in `docusaurus.config.js`. If there's a [`validateOptions`](./static-methods.md#validateOptions) function exported, the `options` will be validated and normalized beforehand.
|
||||
|
||||
Alternatively, if preset contains the plugin, the preset will then be in charge of passing the correct options into the plugin. It is up to individual plugin to define what options it takes.
|
||||
|
||||
## Example {#example}
|
||||
|
||||
Here's a mind model for a presumptuous plugin implementation.
|
||||
|
||||
```js
|
||||
// A JavaScript function that returns an object.
|
||||
// `context` is provided by Docusaurus. Example: siteConfig can be accessed from context.
|
||||
// `opts` is the user-defined options.
|
||||
function myPlugin(context, opts) {
|
||||
return {
|
||||
// A compulsory field used as the namespace for directories to cache
|
||||
// the intermediate data for each plugin.
|
||||
// If you're writing your own local plugin, you will want it to
|
||||
// be unique in order not to potentially conflict with imported plugins.
|
||||
// A good way will be to add your own project name within.
|
||||
name: 'docusaurus-my-project-cool-plugin',
|
||||
|
||||
async loadContent() {
|
||||
// The loadContent hook is executed after siteConfig and env has been loaded.
|
||||
// You can return a JavaScript object that will be passed to contentLoaded hook.
|
||||
},
|
||||
|
||||
async contentLoaded({content, actions}) {
|
||||
// The contentLoaded hook is done after loadContent hook is done.
|
||||
// `actions` are set of functional API provided by Docusaurus (e.g. addRoute)
|
||||
},
|
||||
|
||||
async postBuild(props) {
|
||||
// After docusaurus <build> finish.
|
||||
},
|
||||
|
||||
// TODO
|
||||
async postStart(props) {
|
||||
// docusaurus <start> finish
|
||||
},
|
||||
|
||||
// TODO
|
||||
afterDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverbefore
|
||||
},
|
||||
|
||||
// TODO
|
||||
beforeDevServer(app, server) {
|
||||
// https://webpack.js.org/configuration/dev-server/#devserverafter
|
||||
},
|
||||
|
||||
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.
|
||||
},
|
||||
|
||||
getPathsToWatch() {
|
||||
// Paths to watch.
|
||||
},
|
||||
|
||||
getThemePath() {
|
||||
// Returns the path to the directory where the theme components can
|
||||
// be found.
|
||||
},
|
||||
|
||||
getClientModules() {
|
||||
// Return an array of paths to the modules that are to be imported
|
||||
// in the client bundle. These modules are imported globally before
|
||||
// React even renders the initial UI.
|
||||
},
|
||||
|
||||
extendCli(cli) {
|
||||
// Register an extra command to enhance the CLI of Docusaurus
|
||||
},
|
||||
|
||||
injectHtmlTags({content}) {
|
||||
// Inject head and/or body HTML tags.
|
||||
},
|
||||
|
||||
async getTranslationFiles({content}) {
|
||||
// Return translation files
|
||||
},
|
||||
|
||||
translateContent({content, translationFiles}) {
|
||||
// translate the plugin content here
|
||||
},
|
||||
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
// translate the site themeConfig here
|
||||
},
|
||||
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
// return default theme translations here
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
myPlugin.validateOptions = ({options, validate}) => {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
};
|
||||
|
||||
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
};
|
||||
|
||||
module.exports = myPlugin;
|
||||
```
|
|
@ -0,0 +1,2 @@
|
|||
label: Plugin method references
|
||||
position: 1
|
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Extending infrastructure
|
||||
|
||||
Docusaurus has some infrastructure like hot reloading, CLI, and swizzling, that can be extended by external plugins.
|
||||
|
||||
## `getPathsToWatch()` {#getPathsToWatch}
|
||||
|
||||
Specifies the paths to watch for plugins and themes. The paths are watched by the dev server so that the plugin lifecycles are reloaded when contents in the watched paths change. Note that the plugins and themes modules are initially called with `context` and `options` from Node, which you may use to find the necessary directory information about the site. Use this for files that are consumed server-side, because theme files are automatically watched by Webpack dev server.
|
||||
|
||||
Example:
|
||||
|
||||
```js {5-7} title="docusaurus-plugin/src/index.js"
|
||||
const path = require('path');
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
getPathsToWatch() {
|
||||
const contentPath = path.resolve(context.siteDir, options.path);
|
||||
return [`${contentPath}/**/*.{ts,tsx}`];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `extendCli(cli)` {#extendCli}
|
||||
|
||||
Register an extra command to enhance the CLI of Docusaurus. `cli` is [commander](https://www.npmjs.com/package/commander/v/5.1.0) object.
|
||||
|
||||
:::caution
|
||||
|
||||
The commander version matters! We use commander v5, and make sure you are referring to the right version documentation for available APIs.
|
||||
|
||||
:::
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
extendCli(cli) {
|
||||
cli
|
||||
.command('roll')
|
||||
.description('Roll a random number between 1 and 1000')
|
||||
.action(() => {
|
||||
console.log(Math.floor(Math.random() * 1000 + 1));
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getThemePath()` {#getThemePath}
|
||||
|
||||
Returns the path to the directory where the theme components can be found. When your users calls `swizzle`, `getThemePath` is called and its returned path is used to find your theme components.
|
||||
|
||||
For example, your `getThemePath` can be:
|
||||
|
||||
```js {6-8} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
getThemePath() {
|
||||
return path.resolve(__dirname, './theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getTypeScriptThemePath()` {#getTypeScriptThemePath}
|
||||
|
||||
Similar to `getThemePath()`, it should return the path to the directory where the source code of TypeScript theme components can be found. This path is purely for swizzling TypeScript theme components, and theme components under this path will **not** be resolved by Webpack. Therefore, it is not a replacement of `getThemePath()`. Typically, you can make the path returned by `getTypeScriptThemePath()` be your source directory, and make path returned by `getThemePath()` be the compiled JavaScript output.
|
||||
|
||||
:::tip
|
||||
|
||||
For TypeScript theme authors: you are strongly advised to make your compiled output as human-readable as possible. Only strip type annotations and don't transpile any syntaxes, because they will be handled by Webpack's Babel loader based on the targeted browser versions.
|
||||
|
||||
You should also format these files with Prettier. Remember—JS files can and will be directly consumed by your users.
|
||||
|
||||
:::
|
||||
|
||||
Example:
|
||||
|
||||
```js {6-13} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
getThemePath() {
|
||||
// Where compiled JavaScript output lives
|
||||
return path.join(__dirname, '../lib/theme');
|
||||
},
|
||||
getTypeScriptThemePath() {
|
||||
// Where TypeScript source code lives
|
||||
return path.resolve(__dirname, '../src/theme');
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getSwizzleComponentList()` {#getSwizzleComponentList}
|
||||
|
||||
**This is a static method, not attached to any plugin instance.**
|
||||
|
||||
Returns a list of stable component that are considered as safe for swizzling. These components will be listed in swizzle component without `--danger`. All the components are considers unstable by default. If an empty array is returned, all components are considered unstable. If `undefined` is returned, all component are considered stable.
|
||||
|
||||
```js {0-12} title="my-theme/src/index.js"
|
||||
const swizzleAllowedComponents = [
|
||||
'CodeBlock',
|
||||
'DocSidebar',
|
||||
'Footer',
|
||||
'NotFound',
|
||||
'SearchBar',
|
||||
'hooks/useTheme',
|
||||
'prism-include-languages',
|
||||
];
|
||||
|
||||
myTheme.getSwizzleComponentList = () => swizzleAllowedComponents;
|
||||
```
|
|
@ -0,0 +1,121 @@
|
|||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# I18n lifecycles
|
||||
|
||||
Plugins use these lifecycles to load i18n-related data.
|
||||
|
||||
## `getTranslationFiles({content})` {#getTranslationFiles}
|
||||
|
||||
Plugins declare the JSON translation files they want to use.
|
||||
|
||||
Returns translation files `{path: string, content: ChromeI18nJSON}`:
|
||||
|
||||
- `path`: relative to the plugin localized folder `i18n/<locale>/pluginName`. Extension `.json` should be omitted to remain generic.
|
||||
- `content`: using the Chrome i18n JSON format.
|
||||
|
||||
These files will be written by the [`write-translations` CLI](../../cli.md#docusaurus-write-translations-sitedir) to the plugin i18n subfolder, and will be read in the appropriate locale before calling [`translateContent()`](#translateContent) and [`translateThemeConfig()`](#translateThemeConfig)
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
async getTranslationFiles({content}) {
|
||||
return [
|
||||
{
|
||||
path: 'sidebar-labels',
|
||||
content: {
|
||||
someSidebarLabel: {
|
||||
message: 'Some Sidebar Label',
|
||||
description: 'A label used in my plugin in the sidebar',
|
||||
},
|
||||
someLabelFromContent: content.myLabel,
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `translateContent({content,translationFiles})` {#translateContent}
|
||||
|
||||
Translate the plugin content, using the localized translation files.
|
||||
|
||||
Returns the localized plugin content.
|
||||
|
||||
The `contentLoaded()` lifecycle will be called with the localized plugin content returned by `translateContent()`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-plugin',
|
||||
// highlight-start
|
||||
translateContent({content, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...content,
|
||||
someContentLabel: myTranslationFile.someContentLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `translateThemeConfig({themeConfig,translationFiles})` {#translateThemeConfig}
|
||||
|
||||
Translate the site `themeConfig` labels, using the localized translation files.
|
||||
|
||||
Returns the localized `themeConfig`.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
translateThemeConfig({themeConfig, translationFiles}) {
|
||||
const myTranslationFile = translationFiles.find(
|
||||
(f) => f.path === 'myTranslationFile',
|
||||
);
|
||||
return {
|
||||
...themeConfig,
|
||||
someThemeConfigLabel: myTranslationFile.someThemeConfigLabel.message,
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async getDefaultCodeTranslationMessages()` {#getDefaultCodeTranslationMessages}
|
||||
|
||||
Themes using the `<Translate>` API can provide default code translation messages.
|
||||
|
||||
It should return messages in `Record<string, string>`, where keys are translation ids and values are messages (without the description) localized using the site current locale.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'my-theme',
|
||||
// highlight-start
|
||||
async getDefaultCodeTranslationMessages() {
|
||||
return readJsonFile(`${context.i18n.currentLocale}.json`);
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
|
@ -0,0 +1,388 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
toc_max_heading_level: 4
|
||||
---
|
||||
|
||||
# Lifecycle APIs
|
||||
|
||||
During build, plugins are loaded in parallel to fetch their own contents and render them to routes. Plugins may also configure webpack or post-process the generated files.
|
||||
|
||||
## `async loadContent()` {#loadContent}
|
||||
|
||||
Plugins should use this lifecycle to fetch from data sources (filesystem, remote API, headless CMS, etc.) or do some server processing. The return value is the content it needs.
|
||||
|
||||
For example, this plugin below return a random integer between 1 to 10 as content.
|
||||
|
||||
```js {5-6} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async loadContent() {
|
||||
return 1 + Math.floor(Math.random() * 10);
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `async contentLoaded({content, actions})` {#contentLoaded}
|
||||
|
||||
The data that was loaded in `loadContent` will be consumed in `contentLoaded`. It can be rendered to routes, registered as global data, etc.
|
||||
|
||||
### `content` {#content}
|
||||
|
||||
`contentLoaded` will be called _after_ `loadContent` is done. The return value of `loadContent()` will be passed to `contentLoaded` as `content`.
|
||||
|
||||
### `actions` {#actions}
|
||||
|
||||
`actions` contain three functions:
|
||||
|
||||
#### `addRoute(config: RouteConfig): void` {#addRoute}
|
||||
|
||||
Create a route to add to the website.
|
||||
|
||||
```ts
|
||||
interface RouteConfig {
|
||||
path: string;
|
||||
component: string;
|
||||
modules?: RouteModule;
|
||||
routes?: RouteConfig[];
|
||||
exact?: boolean;
|
||||
priority?: number;
|
||||
}
|
||||
interface RouteModule {
|
||||
[module: string]: Module | RouteModule | RouteModule[];
|
||||
}
|
||||
type Module =
|
||||
| {
|
||||
path: string;
|
||||
__import?: boolean;
|
||||
query?: ParsedUrlQueryInput;
|
||||
}
|
||||
| string;
|
||||
```
|
||||
|
||||
#### `createData(name: string, data: any): Promise<string>` {#createData}
|
||||
|
||||
A declarative callback to create static data (generally json or string) which can later be provided to your routes as props. Takes the file name and data to be stored, and returns the actual data file's path.
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
|
||||
export default function FriendsComponent({friends}) {
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-23} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {createData, addRoute} = actions;
|
||||
// Create friends.json
|
||||
const friends = ['Yangshun', 'Sebastien'];
|
||||
const friendsJsonPath = await createData(
|
||||
'friends.json',
|
||||
JSON.stringify(friends),
|
||||
);
|
||||
|
||||
// Add the '/friends' routes, and ensure it receives the friends props
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
modules: {
|
||||
// propName -> JSON file path
|
||||
friends: friendsJsonPath,
|
||||
},
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### `setGlobalData(data: any): void`
|
||||
|
||||
This function permits to create some global plugin data that can be read from any page, including the pages created by other plugins, and your theme layout.
|
||||
|
||||
This data becomes accessible to your client-side/theme code through the [`useGlobalData`](../../docusaurus-core.md#useGlobalData) and [`usePluginData`](../../docusaurus-core.md#usePluginData) hooks.
|
||||
|
||||
:::caution
|
||||
|
||||
Global data is... global: its size affects the loading time of all pages of your site, so try to keep it small. Prefer `createData` and page-specific data whenever possible.
|
||||
|
||||
:::
|
||||
|
||||
For example, this plugin below create a `/friends` page which display `Your friends are: Yangshun, Sebastien`:
|
||||
|
||||
```jsx title="website/src/components/Friends.js"
|
||||
import React from 'react';
|
||||
import {usePluginData} from '@docusaurus/useGlobalData';
|
||||
|
||||
export default function FriendsComponent() {
|
||||
const {friends} = usePluginData('my-friends-plugin');
|
||||
return <div>Your friends are {friends.join(',')}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```js {4-14} title="docusaurus-friends-plugin/src/index.js"
|
||||
export default function friendsPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-friends-plugin',
|
||||
async contentLoaded({content, actions}) {
|
||||
const {setGlobalData, addRoute} = actions;
|
||||
// Create friends global data
|
||||
setGlobalData({friends: ['Yangshun', 'Sebastien']});
|
||||
|
||||
// Add the '/friends' routes
|
||||
addRoute({
|
||||
path: '/friends',
|
||||
component: '@site/src/components/Friends.js',
|
||||
exact: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## `configureWebpack(config, isServer, utils, content)` {#configureWebpack}
|
||||
|
||||
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 second 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.
|
||||
|
||||
### `isServer` {#isServer}
|
||||
|
||||
`configureWebpack` will be called both in server build and in client build. The server build receives `true` and the client build receives `false` as `isServer`.
|
||||
|
||||
### `utils` {#utils}
|
||||
|
||||
`configureWebpack` also receives an util object:
|
||||
|
||||
- `getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]`
|
||||
- `getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
|
||||
|
||||
You may use them to return your webpack configures conditionally.
|
||||
|
||||
For example, this plugin below modify the webpack config to transpile `.foo` file.
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
// highlight-start
|
||||
configureWebpack(config, isServer, utils) {
|
||||
const {getCacheLoader} = utils;
|
||||
return {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.foo$/,
|
||||
use: [getCacheLoader(isServer), 'my-custom-webpack-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### `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).
|
||||
|
||||
It is possible to specify the merge strategy. For example, if you want a webpack rule to be prepended instead of appended:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'custom-docusaurus-plugin',
|
||||
configureWebpack(config, isServer, utils) {
|
||||
return {
|
||||
// highlight-start
|
||||
mergeStrategy: {'module.rules': 'prepend'},
|
||||
module: {rules: [myRuleToPrepend]},
|
||||
// highlight-end
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.
|
||||
|
||||
## `configurePostCss(options)` {#configurePostCss}
|
||||
|
||||
Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during the generation of the client bundle.
|
||||
|
||||
Should return the mutated `postcssOptions`.
|
||||
|
||||
By default, `postcssOptions` looks like this:
|
||||
|
||||
```js
|
||||
const postcssOptions = {
|
||||
ident: 'postcss',
|
||||
plugins: [require('autoprefixer')],
|
||||
};
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// highlight-start
|
||||
configurePostCss(postcssOptions) {
|
||||
// Appends new PostCSS plugin.
|
||||
postcssOptions.plugins.push(require('postcss-import'));
|
||||
return postcssOptions;
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `postBuild(props)` {#postBuild}
|
||||
|
||||
Called when a (production) build finishes.
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
siteDir: string;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
outDir: string;
|
||||
baseUrl: string;
|
||||
headTags: string;
|
||||
preBodyTags: string;
|
||||
postBodyTags: string;
|
||||
routesPaths: string[];
|
||||
plugins: Plugin<any>[];
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js {4-11} title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
|
||||
// Print out to console all the rendered routes.
|
||||
routesPaths.map((route) => {
|
||||
console.log(route);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `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;
|
||||
preBodyTags?: HtmlTags;
|
||||
postBodyTags?: HtmlTags;
|
||||
};
|
||||
|
||||
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
|
||||
|
||||
interface HtmlTagObject {
|
||||
/**
|
||||
* Attributes of the HTML tag
|
||||
* E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
|
||||
*/
|
||||
attributes?: {
|
||||
[attributeName: string]: string | boolean;
|
||||
};
|
||||
/**
|
||||
* The tag name e.g. `div`, `script`, `link`, `meta`
|
||||
*/
|
||||
tagName: string;
|
||||
/**
|
||||
* The inner HTML
|
||||
*/
|
||||
innerHTML?: string;
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```js title="docusaurus-plugin/src/index.js"
|
||||
module.exports = function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
loadContent: async () => {
|
||||
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
|
||||
},
|
||||
// highlight-start
|
||||
injectHtmlTags({content}) {
|
||||
return {
|
||||
headTags: [
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
rel: 'preconnect',
|
||||
href: 'https://www.github.com',
|
||||
},
|
||||
},
|
||||
...content.remoteHeadTags,
|
||||
],
|
||||
preBodyTags: [
|
||||
{
|
||||
tagName: 'script',
|
||||
attributes: {
|
||||
charset: 'utf-8',
|
||||
src: '/noflash.js',
|
||||
},
|
||||
},
|
||||
],
|
||||
postBodyTags: [`<div> This is post body </div>`],
|
||||
};
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## `getClientModules()` {#getClientModules}
|
||||
|
||||
Returns an array of paths to the modules that are to be imported in the client bundle. These modules are imported globally before React even renders the initial UI.
|
||||
|
||||
As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user:
|
||||
|
||||
```js {7-9} title="my-theme/src/index.js"
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (context, options) {
|
||||
const {customCss, customJs} = options || {};
|
||||
return {
|
||||
name: 'name-of-my-theme',
|
||||
getClientModules() {
|
||||
return [customCss, customJs];
|
||||
},
|
||||
};
|
||||
};
|
||||
```
|
|
@ -0,0 +1,115 @@
|
|||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Static methods
|
||||
|
||||
Static methods are not part of the plugin instance—they are attached to the constructor function. These methods are used to validate and normalize the plugin options and theme config, which are then used as constructor parameters to initialize the plugin instance.
|
||||
|
||||
## `validateOptions({options, validate})` {#validateOptions}
|
||||
|
||||
Return validated and normalized options for the plugin. This method is called before the plugin is initialized.You must return options since the returned options will be passed to plugin during initialization.
|
||||
|
||||
### `options` {#options}
|
||||
|
||||
`validateOptions` is called with `options` passed to plugin for validation and normalization.
|
||||
|
||||
### `validate` {#validate}
|
||||
|
||||
`validateOptions` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and options as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of options.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options and return options in case of success.
|
||||
|
||||
```js {8-11} title="my-plugin/src/index.js"
|
||||
function myPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
myPlugin.validateOptions = ({options, validate}) => {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
};
|
||||
|
||||
module.exports = myPlugin;
|
||||
```
|
||||
|
||||
In TypeScript, you can also choose to export this as a separate named export.
|
||||
|
||||
```ts {8-11} title="my-plugin/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateOptions({options, validate}) {
|
||||
const validatedOptions = validate(myValidationSchema, options);
|
||||
return validationOptions;
|
||||
}
|
||||
```
|
||||
|
||||
## `validateThemeConfig({themeConfig, validate})` {#validateThemeConfig}
|
||||
|
||||
Return validated and normalized configuration for the theme.
|
||||
|
||||
### `themeConfig` {#themeConfig}
|
||||
|
||||
`validateThemeConfig` is called with `themeConfig` provided in `docusaurus.config.js` for validation and normalization.
|
||||
|
||||
### `validate` {#validate-1}
|
||||
|
||||
`validateThemeConfig` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and `themeConfig` as argument, returns validated and normalized options. `validate` will automatically handle error and validation config.
|
||||
|
||||
:::tip
|
||||
|
||||
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of theme config.
|
||||
|
||||
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
|
||||
|
||||
:::
|
||||
|
||||
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options.
|
||||
|
||||
```js {8-11} title="my-theme/src/index.js"
|
||||
function myPlugin(context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
};
|
||||
|
||||
module.exports = validateThemeConfig;
|
||||
```
|
||||
|
||||
In TypeScript, you can also choose to export this as a separate named export.
|
||||
|
||||
```ts {8-11} title="my-theme/src/index.ts"
|
||||
export default function (context, options) {
|
||||
return {
|
||||
name: 'docusaurus-plugin',
|
||||
// rest of methods
|
||||
};
|
||||
}
|
||||
|
||||
export function validateThemeConfig({themeConfig, validate}) {
|
||||
const validatedThemeConfig = validate(myValidationSchema, options);
|
||||
return validatedThemeConfig;
|
||||
}
|
||||
```
|
|
@ -134,4 +134,4 @@ For themes that supports TypeScript theme components, you can add the `--typescr
|
|||
npm run swizzle @docusaurus/theme-classic Footer -- --typescript
|
||||
```
|
||||
|
||||
At this moment, the only official Docusaurus theme that supports TypeScript theme components is `@docusaurus/theme-classic`. If you are a Docusaurus theme package author who wants to add TypeScript support, see the [Lifecycle APIs docs](./api/lifecycle-apis.md#gettypescriptthemepath).
|
||||
At this moment, the only official Docusaurus theme that supports TypeScript theme components is `@docusaurus/theme-classic`. If you are a Docusaurus theme package author who wants to add TypeScript support, see the [Lifecycle APIs docs](./api/plugin-methods/extend-infrastructure.md#getTypeScriptThemePath).
|
||||
|
|
|
@ -121,7 +121,7 @@ Docusaurus' implementation of the plugins system provides us with a convenient w
|
|||
|
||||
A plugin is a function that takes two parameters: `context` and `options`.
|
||||
|
||||
It returns a plugin instance object, containing plugin [lifecycle APIs](./api/lifecycle-apis.md).
|
||||
It returns a plugin instance object, containing plugin [lifecycle APIs](./api/plugin-methods/README.md).
|
||||
|
||||
It can be defined as a function or a module.
|
||||
|
||||
|
@ -206,4 +206,4 @@ interface LoadContext {
|
|||
|
||||
#### Return value {#return-value}
|
||||
|
||||
The returned object value should implement the [lifecycle APIs](./api/lifecycle-apis.md).
|
||||
The returned object value should implement the [lifecycle APIs](./api/plugin-methods/README.md).
|
||||
|
|
|
@ -238,13 +238,13 @@ website
|
|||
|
||||
There are two lifecycle methods that are essential to theme implementation:
|
||||
|
||||
- [`getThemePath()`](./api/lifecycle-apis.md#getthemepath)
|
||||
- [`getClientModules()`](./api/lifecycle-apis.md#getclientmodules)
|
||||
- [`getThemePath()`](./api/plugin-methods/extend-infrastructure.md#getThemePath)
|
||||
- [`getClientModules()`](./api/plugin-methods/lifecycle-apis.md#getClientModules)
|
||||
|
||||
These lifecycle methods are not essential but recommended:
|
||||
|
||||
- [`validateThemeConfig({themeConfig, validate})`](./api/lifecycle-apis.md#validatethemeconfigthemeconfig-validate)
|
||||
- [`validateOptions({options, validate})`](./api/lifecycle-apis.md#validateoptionsoptions-validate)
|
||||
- [`validateThemeConfig({themeConfig, validate})`](./api/plugin-methods/static-methods.md#validateThemeConfig)
|
||||
- [`validateOptions({options, validate})`](./api/plugin-methods/static-methods.md#validateOptions)
|
||||
|
||||
<!--
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue