feat(core): support TypeScript + ESM configuration (#9317)

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
This commit is contained in:
Chongyi Zheng 2023-10-13 20:46:03 -04:00 committed by GitHub
parent 336a44f3ea
commit 45f1a669b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
126 changed files with 2054 additions and 914 deletions

View file

@ -157,17 +157,6 @@ async function copyTemplate(
): Promise<void> { ): Promise<void> {
await fs.copy(path.join(templatesDir, 'shared'), dest); await fs.copy(path.join(templatesDir, 'shared'), dest);
// TypeScript variants will copy duplicate resources like CSS & config from
// base template
if (typescript) {
await fs.copy(template.path, dest, {
filter: async (filePath) =>
(await fs.stat(filePath)).isDirectory() ||
path.extname(filePath) === '.css' ||
path.basename(filePath) === 'docusaurus.config.js',
});
}
await fs.copy(typescript ? template.tsVariantPath! : template.path, dest, { await fs.copy(typescript ? template.tsVariantPath! : template.path, dest, {
// Symlinks don't exist in published npm packages anymore, so this is only // Symlinks don't exist in published npm packages anymore, so this is only
// to prevent errors during local testing // to prevent errors during local testing

View file

@ -0,0 +1,133 @@
import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
const config: Config = {
title: 'My Site',
tagline: 'Dinosaurs are cool',
favicon: 'img/favicon.ico',
// Set the production url of your site here
url: 'https://your-docusaurus-site.example.com',
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: '/',
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: 'facebook', // Usually your GitHub org/user name.
projectName: 'docusaurus', // Usually your repo name.
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
// may want to replace "en" with "zh-Hans".
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
presets: [
[
'classic',
{
docs: {
sidebarPath: './sidebars.ts',
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl:
'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
},
blog: {
showReadingTime: true,
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl:
'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
},
theme: {
customCss: './src/css/custom.css',
},
} satisfies Preset.Options,
],
],
themeConfig: {
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
navbar: {
title: 'My Site',
logo: {
alt: 'My Site Logo',
src: 'img/logo.svg',
},
items: [
{
type: 'docSidebar',
sidebarId: 'tutorialSidebar',
position: 'left',
label: 'Tutorial',
},
{to: '/blog', label: 'Blog', position: 'left'},
{
href: 'https://github.com/facebook/docusaurus',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
title: 'Docs',
items: [
{
label: 'Tutorial',
to: '/docs/intro',
},
],
},
{
title: 'Community',
items: [
{
label: 'Stack Overflow',
href: 'https://stackoverflow.com/questions/tagged/docusaurus',
},
{
label: 'Discord',
href: 'https://discordapp.com/invite/docusaurus',
},
{
label: 'Twitter',
href: 'https://twitter.com/docusaurus',
},
],
},
{
title: 'More',
items: [
{
label: 'Blog',
to: '/blog',
},
{
label: 'GitHub',
href: 'https://github.com/facebook/docusaurus',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
},
} satisfies Preset.ThemeConfig,
};
export default config;

View file

@ -26,6 +26,7 @@
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "3.0.0-beta.0", "@docusaurus/module-type-aliases": "3.0.0-beta.0",
"@docusaurus/tsconfig": "3.0.0-beta.0", "@docusaurus/tsconfig": "3.0.0-beta.0",
"@docusaurus/types": "3.0.0-beta.0",
"typescript": "~5.2.2" "typescript": "~5.2.2"
}, },
"browserslist": { "browserslist": {

View file

@ -0,0 +1,31 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
// But you can create a sidebar manually
/*
tutorialSidebar: [
'intro',
'hello',
{
type: 'category',
label: 'Tutorial',
items: ['tutorial-basics/create-a-document'],
},
],
*/
};
export default sidebars;

View file

@ -1 +0,0 @@
../../../../classic/src/components/HomepageFeatures/styles.module.css

View file

@ -1 +0,0 @@
../../../classic/src/pages/index.module.css

View file

@ -4,10 +4,7 @@
// There are various equivalent ways to declare your Docusaurus config. // There are various equivalent ways to declare your Docusaurus config.
// See: https://docusaurus.io/docs/api/docusaurus-config // See: https://docusaurus.io/docs/api/docusaurus-config
const {themes} = require('prism-react-renderer'); import {themes as prismThemes} from 'prism-react-renderer';
const lightCodeTheme = themes.github;
const darkCodeTheme = themes.dracula;
/** @type {import('@docusaurus/types').Config} */ /** @type {import('@docusaurus/types').Config} */
const config = { const config = {
@ -43,7 +40,7 @@ const config = {
/** @type {import('@docusaurus/preset-classic').Options} */ /** @type {import('@docusaurus/preset-classic').Options} */
({ ({
docs: { docs: {
sidebarPath: require.resolve('./sidebars.js'), sidebarPath: './sidebars.js',
// Please change this to your repo. // Please change this to your repo.
// Remove this to remove the "edit this page" links. // Remove this to remove the "edit this page" links.
editUrl: editUrl:
@ -57,7 +54,7 @@ const config = {
'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/', 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
}, },
theme: { theme: {
customCss: require.resolve('./src/css/custom.css'), customCss: './src/css/custom.css',
}, },
}), }),
], ],
@ -135,10 +132,10 @@ const config = {
copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,
}, },
prism: { prism: {
theme: lightCodeTheme, theme: prismThemes.github,
darkTheme: darkCodeTheme, darkTheme: prismThemes.dracula,
}, },
}), }),
}; };
module.exports = config; export default config;

View file

@ -23,7 +23,8 @@
"react-dom": "^18.0.0" "react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "3.0.0-beta.0" "@docusaurus/module-type-aliases": "3.0.0-beta.0",
"@docusaurus/types": "3.0.0-beta.0"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

View file

@ -30,4 +30,4 @@ const sidebars = {
*/ */
}; };
module.exports = sidebars; export default sidebars;

View file

@ -42,7 +42,7 @@ This is my **first Docusaurus document**!
It is also possible to create your sidebar explicitly in `sidebars.js`: It is also possible to create your sidebar explicitly in `sidebars.js`:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorialSidebar: [ tutorialSidebar: [
'intro', 'intro',
// highlight-next-line // highlight-next-line

View file

@ -28,7 +28,7 @@ To navigate seamlessly across versions, add a version dropdown.
Modify the `docusaurus.config.js` file: Modify the `docusaurus.config.js` file:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [

View file

@ -11,7 +11,7 @@ Let's translate `docs/intro.md` to French.
Modify `docusaurus.config.js` to add support for the `fr` locale: Modify `docusaurus.config.js` to add support for the `fr` locale:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en', 'fr'], locales: ['en', 'fr'],
@ -54,7 +54,7 @@ To navigate seamlessly across languages, add a locale dropdown.
Modify the `docusaurus.config.js` file: Modify the `docusaurus.config.js` file:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [

View file

@ -45,7 +45,6 @@
"@types/react-router-config": "^5.0.7", "@types/react-router-config": "^5.0.7",
"combine-promises": "^1.1.0", "combine-promises": "^1.1.0",
"fs-extra": "^11.1.1", "fs-extra": "^11.1.1",
"import-fresh": "^3.3.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"tslib": "^2.6.0", "tslib": "^2.6.0",

View file

@ -17,7 +17,7 @@ import {
readVersionsFile, readVersionsFile,
} from './versions/files'; } from './versions/files';
import {validateVersionName} from './versions/validation'; import {validateVersionName} from './versions/validation';
import {loadSidebarsFileUnsafe} from './sidebars'; import {loadSidebarsFile} from './sidebars';
import {CURRENT_VERSION_NAME} from './constants'; import {CURRENT_VERSION_NAME} from './constants';
import type {PluginOptions} from '@docusaurus/plugin-content-docs'; import type {PluginOptions} from '@docusaurus/plugin-content-docs';
import type {LoadContext} from '@docusaurus/types'; import type {LoadContext} from '@docusaurus/types';
@ -37,7 +37,7 @@ async function createVersionedSidebarFile({
// Note: we don't need the sidebars file to be normalized: it's ok to let // Note: we don't need the sidebars file to be normalized: it's ok to let
// plugin option changes to impact older, versioned sidebars // plugin option changes to impact older, versioned sidebars
// We don't validate here, assuming the user has already built the version // We don't validate here, assuming the user has already built the version
const sidebars = await loadSidebarsFileUnsafe(sidebarPath); const sidebars = await loadSidebarsFile(sidebarPath);
// Do not create a useless versioned sidebars file if sidebars file is empty // Do not create a useless versioned sidebars file if sidebars file is empty
// or sidebars are disabled/false) // or sidebars are disabled/false)

View file

@ -9,10 +9,9 @@ import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import _ from 'lodash'; import _ from 'lodash';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import {Globby} from '@docusaurus/utils'; import {loadFreshModule, Globby} from '@docusaurus/utils';
import Yaml from 'js-yaml'; import Yaml from 'js-yaml';
import combinePromises from 'combine-promises'; import combinePromises from 'combine-promises';
import importFresh from 'import-fresh';
import {validateSidebars, validateCategoryMetadataFile} from './validation'; import {validateSidebars, validateCategoryMetadataFile} from './validation';
import {normalizeSidebars} from './normalization'; import {normalizeSidebars} from './normalization';
import {processSidebars} from './processor'; import {processSidebars} from './processor';
@ -68,7 +67,7 @@ async function readCategoriesMetadata(contentPath: string) {
); );
} }
export async function loadSidebarsFileUnsafe( async function loadSidebarsFileUnsafe(
sidebarFilePath: string | false | undefined, sidebarFilePath: string | false | undefined,
): Promise<SidebarsConfig> { ): Promise<SidebarsConfig> {
// false => no sidebars // false => no sidebars
@ -89,7 +88,19 @@ export async function loadSidebarsFileUnsafe(
} }
// We don't want sidebars to be cached because of hot reloading. // We don't want sidebars to be cached because of hot reloading.
return importFresh(sidebarFilePath); const module = await loadFreshModule(sidebarFilePath);
// TODO unsafe, need to refactor and improve validation
return module as SidebarsConfig;
}
export async function loadSidebarsFile(
sidebarFilePath: string | false | undefined,
): Promise<SidebarsConfig> {
const sidebars = await loadSidebarsFileUnsafe(sidebarFilePath);
// TODO unsafe, need to refactor and improve validation
return sidebars as SidebarsConfig;
} }
export async function loadSidebars( export async function loadSidebars(

View file

@ -47,6 +47,8 @@ declare module '@docusaurus/plugin-ideal-image' {
*/ */
disableInDev?: boolean; disableInDev?: boolean;
}; };
export type Options = Partial<PluginOptions>;
} }
declare module '@theme/IdealImage' { declare module '@theme/IdealImage' {

View file

@ -52,6 +52,7 @@ export {
PluginVersionInformation, PluginVersionInformation,
Preset, Preset,
PresetConfig, PresetConfig,
PresetConfigDefined,
PresetModule, PresetModule,
OptionValidationContext, OptionValidationContext,
ThemeConfigValidationContext, ThemeConfigValidationContext,

View file

@ -26,11 +26,9 @@ export type PluginConfig =
| false | false
| null; | null;
export type PresetConfig = export type PresetConfigDefined = string | [string, {[key: string]: unknown}];
| string
| [string, {[key: string]: unknown}] export type PresetConfig = PresetConfigDefined | false | null;
| false
| null;
/** /**
* - `type: 'package'`, plugin is in a different package. * - `type: 'package'`, plugin is in a different package.

View file

@ -26,6 +26,7 @@
"github-slugger": "^1.5.0", "github-slugger": "^1.5.0",
"globby": "^11.1.0", "globby": "^11.1.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"jiti": "^1.20.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
@ -44,7 +45,8 @@
"@types/github-slugger": "^1.3.0", "@types/github-slugger": "^1.3.0",
"@types/micromatch": "^4.0.2", "@types/micromatch": "^4.0.2",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.2.7",
"dedent": "^0.7.0" "dedent": "^0.7.0",
"tmp-promise": "^3.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"@docusaurus/types": "*" "@docusaurus/types": "*"

View file

@ -0,0 +1,7 @@
exports.someNamedExport = 42;
module.exports = {
firstName: 'Sebastien',
lastName: 'Lorber',
birthYear: 1986,
};

View file

@ -0,0 +1,7 @@
exports.someNamedExport = 42;
module.exports = {
firstName: 'Sebastien',
lastName: 'Lorber',
birthYear: 1986,
};

View file

@ -0,0 +1,11 @@
exports.someNamedExport = 42 as number;
module.exports = {
firstName: 'Sebastien',
lastName: 'Lorber',
birthYear: 1986,
} satisfies {
firstName: string;
lastName: string;
birthYear: number;
};

View file

@ -0,0 +1,7 @@
export const someNamedExport = 42;
export default {
firstName: 'Sebastien',
lastName: 'Lorber',
birthYear: 1986,
};

View file

@ -0,0 +1,11 @@
export const someNamedExport: number = 42;
export default {
firstName: 'Sebastien',
lastName: 'Lorber',
birthYear: 1986,
} satisfies {
firstName: string;
lastName: string;
birthYear: number;
};

View file

@ -0,0 +1,7 @@
export const someNamedExport = 42;
export default {
firstName: 'Sebastien',
lastName: 'Lorber',
birthYear: 1986,
};

View file

@ -0,0 +1,289 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import fs from 'fs-extra';
import path from 'path';
import tmp from 'tmp-promise';
import dedent from 'dedent';
import {loadFreshModule} from '../moduleUtils';
async function createTmpDir() {
return (
await tmp.dir({
prefix: 'jest-tmp-moduleUtils-tests',
})
).path;
}
async function moduleGraphHelpers() {
const dir = await createTmpDir();
async function fileHelper(name: string, initialContent?: string) {
const filePath = path.resolve(dir, name);
if (initialContent) {
await fs.outputFile(filePath, initialContent);
}
return {
filePath,
write: (content: string) => fs.outputFile(filePath, content),
load: () => loadFreshModule(filePath),
};
}
return {fileHelper};
}
async function loadFixtureModule(fixtureName: string) {
return loadFreshModule(
path.resolve(__dirname, '__fixtures__/moduleUtils', fixtureName),
);
}
describe('loadFreshModule', () => {
describe('can load CJS user module', () => {
async function testUserFixture(fixtureName: string) {
const userFixturePath = `user/${fixtureName}`;
const userModule = await loadFixtureModule(userFixturePath);
expect(userModule).toEqual({
birthYear: 1986,
firstName: 'Sebastien',
lastName: 'Lorber',
});
}
it('for .cjs.js', async () => {
await testUserFixture('user.cjs.js');
});
it('for .cjs.ts', async () => {
await testUserFixture('user.cjs.ts');
});
it('for .cjs', async () => {
await testUserFixture('user.cjs');
});
});
describe('can load ESM user module', () => {
async function testUserFixture(fixtureName: string) {
const userFixturePath = `user/${fixtureName}`;
const userModule = await loadFixtureModule(userFixturePath);
expect(userModule).toEqual({
birthYear: 1986,
firstName: 'Sebastien',
lastName: 'Lorber',
someNamedExport: 42,
});
}
it('for .esm.js', async () => {
await testUserFixture('user.esm.js');
});
it('for .esm.ts', async () => {
await testUserFixture('user.esm.ts');
});
it('for .mjs', async () => {
await testUserFixture('user.mjs');
});
});
describe('module graph', () => {
it('can load and reload fresh module graph', async () => {
const {fileHelper} = await moduleGraphHelpers();
const dependency1 = await fileHelper(
'dependency1.js',
/* language=js */
dedent`
export const dep1Export = "dep1 val1";
export default {dep1Val: "dep1 val2"}
`,
);
const dependency2 = await fileHelper(
'dependency2.ts',
/* language=ts */
dedent`
export default {dep2Val: "dep2 val"} satisfies {dep2Val: string}
`,
);
const entryFile = await fileHelper(
'entry.js',
/* language=js */
dedent`
import dependency1 from "./dependency1";
import dependency2 from "./dependency2";
export default {
someEntryValue: "entryVal",
dependency1,
dependency2
};
`,
);
// Should be able to read the initial module graph
await expect(entryFile.load()).resolves.toEqual({
someEntryValue: 'entryVal',
dependency1: {
dep1Export: 'dep1 val1',
dep1Val: 'dep1 val2',
},
dependency2: {
dep2Val: 'dep2 val',
},
});
await expect(dependency1.load()).resolves.toEqual({
dep1Export: 'dep1 val1',
dep1Val: 'dep1 val2',
});
await expect(dependency2.load()).resolves.toEqual({
dep2Val: 'dep2 val',
});
// Should be able to read the module graph again after updates
await dependency1.write(
/* language=js */
dedent`
export const dep1Export = "dep1 val1 updated";
export default {dep1Val: "dep1 val2 updated"}
`,
);
await expect(entryFile.load()).resolves.toEqual({
someEntryValue: 'entryVal',
dependency1: {
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
},
dependency2: {
dep2Val: 'dep2 val',
},
});
await expect(dependency1.load()).resolves.toEqual({
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
});
await expect(dependency2.load()).resolves.toEqual({
dep2Val: 'dep2 val',
});
// Should be able to read the module graph again after updates
await dependency2.write(
/* language=ts */
dedent`
export default {dep2Val: "dep2 val updated"} satisfies {dep2Val: string}
`,
);
await expect(entryFile.load()).resolves.toEqual({
someEntryValue: 'entryVal',
dependency1: {
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
},
dependency2: {
dep2Val: 'dep2 val updated',
},
});
await expect(dependency1.load()).resolves.toEqual({
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
});
await expect(dependency2.load()).resolves.toEqual({
dep2Val: 'dep2 val updated',
});
// Should be able to read the module graph again after updates
await entryFile.write(
/* language=js */
dedent`
import dependency1 from "./dependency1";
import dependency2 from "./dependency2";
export default {
someEntryValue: "entryVal updated",
dependency1,
dependency2,
newAttribute: "is there"
}
`,
);
await expect(entryFile.load()).resolves.toEqual({
someEntryValue: 'entryVal updated',
newAttribute: 'is there',
dependency1: {
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
},
dependency2: {
dep2Val: 'dep2 val updated',
},
});
await expect(dependency1.load()).resolves.toEqual({
dep1Export: 'dep1 val1 updated',
dep1Val: 'dep1 val2 updated',
});
await expect(dependency2.load()).resolves.toEqual({
dep2Val: 'dep2 val updated',
});
});
});
describe('invalid module path param', () => {
it('throws if module path does not exist', async () => {
await expect(() => loadFreshModule('/some/unknown/module/path.js'))
.rejects.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "/some/unknown/module/path.js"
Cause: Cannot find module '/some/unknown/module/path.js' from 'packages/docusaurus-utils/src/moduleUtils.ts'"
`);
});
it('throws if module path is undefined', async () => {
await expect(() =>
// @ts-expect-error: undefined is invalid
loadFreshModule(undefined),
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "undefined"
Cause: Invalid module path of type undefined"
`);
});
it('throws if module path is null', async () => {
await expect(() =>
// @ts-expect-error: null is invalid
loadFreshModule(null),
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "null"
Cause: Invalid module path of type null"
`);
});
it('throws if module path is number', async () => {
await expect(() =>
// @ts-expect-error: number is invalid
loadFreshModule(42),
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "42"
Cause: Invalid module path of type 42"
`);
});
it('throws if module path is object', async () => {
await expect(() =>
// @ts-expect-error: object is invalid
loadFreshModule({}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Docusaurus could not load module at path "[object Object]"
Cause: Invalid module path of type [object Object]"
`);
});
});
});

View file

@ -100,6 +100,7 @@ export {
} from './globUtils'; } from './globUtils';
export {getFileLoaderUtils} from './webpackUtils'; export {getFileLoaderUtils} from './webpackUtils';
export {escapeShellArg} from './shellUtils'; export {escapeShellArg} from './shellUtils';
export {loadFreshModule} from './moduleUtils';
export { export {
getDataFilePath, getDataFilePath,
getDataFileData, getDataFileData,

View file

@ -0,0 +1,43 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import jiti from 'jiti';
import logger from '@docusaurus/logger';
/*
jiti is able to load ESM, CJS, JSON, TS modules
*/
export async function loadFreshModule(modulePath: string): Promise<unknown> {
try {
if (typeof modulePath !== 'string') {
throw new Error(
logger.interpolate`Invalid module path of type name=${modulePath}`,
);
}
const load = jiti(__filename, {
// Transpilation cache, can be safely enabled
cache: true,
// Bypass Node.js runtime require cache
// Same as "import-fresh" package we used previously
requireCache: false,
// Only take into consideration the default export
// For now we don't need named exports
// This also helps normalize return value for both CJS/ESM/TS modules
interopDefault: true,
// debug: true,
});
return load(modulePath);
} catch (error) {
throw new Error(
logger.interpolate`Docusaurus could not load module at path path=${modulePath}\nCause: ${
(error as Error).message
}`,
{cause: error},
);
}
}

View file

@ -76,7 +76,6 @@
"html-minifier-terser": "^7.2.0", "html-minifier-terser": "^7.2.0",
"html-tags": "^3.3.1", "html-tags": "^3.3.1",
"html-webpack-plugin": "^5.5.3", "html-webpack-plugin": "^5.5.3",
"import-fresh": "^3.3.0",
"leven": "^3.1.0", "leven": "^3.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.7.6", "mini-css-extract-plugin": "^2.7.6",

View file

@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports = {
title: 'title',
url: 'https://example.com',
baseUrl: '/',
}

View file

@ -0,0 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type {Config} from '@docusaurus/types';
module.exports = {
title: 'title',
url: 'https://example.com',
baseUrl: '/',
} satisfies Config;

View file

@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export default {
title: 'title',
url: 'https://example.com',
baseUrl: '/',
}

View file

@ -0,0 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type {Config} from '@docusaurus/types';
export default {
title: 'title',
url: 'https://example.com',
baseUrl: '/',
} satisfies Config;

View file

@ -0,0 +1,13 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export default {
title: 'JS title',
url: 'https://example.com',
baseUrl: '/',
}

View file

@ -0,0 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type {Config} from '@docusaurus/types';
export default {
title: 'TS title',
url: 'https://example.com',
baseUrl: '/',
} satisfies Config;

View file

@ -48,6 +48,246 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = `
} }
`; `;
exports[`loadSiteConfig website with ts + js config 1`] = `
{
"siteConfig": {
"baseUrl": "/",
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
"locales": [
"en",
],
"path": "i18n",
},
"markdown": {
"format": "mdx",
"mdx1Compat": {
"admonitions": true,
"comments": true,
"headingIds": true,
},
"mermaid": false,
"preprocessor": undefined,
},
"noIndex": false,
"onBrokenLinks": "throw",
"onBrokenMarkdownLinks": "warn",
"onDuplicateRoutes": "warn",
"plugins": [],
"presets": [],
"scripts": [],
"staticDirectories": [
"static",
],
"stylesheets": [],
"tagline": "",
"themeConfig": {},
"themes": [],
"title": "TS title",
"titleDelimiter": "|",
"url": "https://example.com",
},
"siteConfigPath": "<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/__fixtures__/config/sites/ts-and-js-site/docusaurus.config.ts",
}
`;
exports[`loadSiteConfig website with valid JS CJS config 1`] = `
{
"siteConfig": {
"baseUrl": "/",
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
"locales": [
"en",
],
"path": "i18n",
},
"markdown": {
"format": "mdx",
"mdx1Compat": {
"admonitions": true,
"comments": true,
"headingIds": true,
},
"mermaid": false,
"preprocessor": undefined,
},
"noIndex": false,
"onBrokenLinks": "throw",
"onBrokenMarkdownLinks": "warn",
"onDuplicateRoutes": "warn",
"plugins": [],
"presets": [],
"scripts": [],
"staticDirectories": [
"static",
],
"stylesheets": [],
"tagline": "",
"themeConfig": {},
"themes": [],
"title": "title",
"titleDelimiter": "|",
"url": "https://example.com",
},
"siteConfigPath": "<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/__fixtures__/config/configCJS.js",
}
`;
exports[`loadSiteConfig website with valid JS ESM config 1`] = `
{
"siteConfig": {
"baseUrl": "/",
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
"locales": [
"en",
],
"path": "i18n",
},
"markdown": {
"format": "mdx",
"mdx1Compat": {
"admonitions": true,
"comments": true,
"headingIds": true,
},
"mermaid": false,
"preprocessor": undefined,
},
"noIndex": false,
"onBrokenLinks": "throw",
"onBrokenMarkdownLinks": "warn",
"onDuplicateRoutes": "warn",
"plugins": [],
"presets": [],
"scripts": [],
"staticDirectories": [
"static",
],
"stylesheets": [],
"tagline": "",
"themeConfig": {},
"themes": [],
"title": "title",
"titleDelimiter": "|",
"url": "https://example.com",
},
"siteConfigPath": "<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/__fixtures__/config/configESM.js",
}
`;
exports[`loadSiteConfig website with valid TypeScript CJS config 1`] = `
{
"siteConfig": {
"baseUrl": "/",
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
"locales": [
"en",
],
"path": "i18n",
},
"markdown": {
"format": "mdx",
"mdx1Compat": {
"admonitions": true,
"comments": true,
"headingIds": true,
},
"mermaid": false,
"preprocessor": undefined,
},
"noIndex": false,
"onBrokenLinks": "throw",
"onBrokenMarkdownLinks": "warn",
"onDuplicateRoutes": "warn",
"plugins": [],
"presets": [],
"scripts": [],
"staticDirectories": [
"static",
],
"stylesheets": [],
"tagline": "",
"themeConfig": {},
"themes": [],
"title": "title",
"titleDelimiter": "|",
"url": "https://example.com",
},
"siteConfigPath": "<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/__fixtures__/config/configCJS.ts",
}
`;
exports[`loadSiteConfig website with valid TypeScript ESM config 1`] = `
{
"siteConfig": {
"baseUrl": "/",
"baseUrlIssueBanner": true,
"clientModules": [],
"customFields": {},
"headTags": [],
"i18n": {
"defaultLocale": "en",
"localeConfigs": {},
"locales": [
"en",
],
"path": "i18n",
},
"markdown": {
"format": "mdx",
"mdx1Compat": {
"admonitions": true,
"comments": true,
"headingIds": true,
},
"mermaid": false,
"preprocessor": undefined,
},
"noIndex": false,
"onBrokenLinks": "throw",
"onBrokenMarkdownLinks": "warn",
"onDuplicateRoutes": "warn",
"plugins": [],
"presets": [],
"scripts": [],
"staticDirectories": [
"static",
],
"stylesheets": [],
"tagline": "",
"themeConfig": {},
"themes": [],
"title": "title",
"titleDelimiter": "|",
"url": "https://example.com",
},
"siteConfigPath": "<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/__fixtures__/config/configESM.ts",
}
`;
exports[`loadSiteConfig website with valid async config 1`] = ` exports[`loadSiteConfig website with valid async config 1`] = `
{ {
"siteConfig": { "siteConfig": {

View file

@ -19,6 +19,20 @@ describe('loadSiteConfig', () => {
expect(config).not.toEqual({}); expect(config).not.toEqual({});
}); });
it('website with ts + js config', async () => {
const config = await loadSiteConfig({
siteDir: path.join(
__dirname,
'__fixtures__',
'config/sites/ts-and-js-site',
),
});
expect(config).toMatchSnapshot();
// Docusaurus uses in priority a TS config
expect(config.siteConfig.title).toBe('TS title');
expect(config).not.toEqual({});
});
it('website with .cjs siteConfig', async () => { it('website with .cjs siteConfig', async () => {
const config = await loadSiteConfig({siteDir}); const config = await loadSiteConfig({siteDir});
expect(config).toMatchSnapshot(); expect(config).toMatchSnapshot();
@ -52,6 +66,42 @@ describe('loadSiteConfig', () => {
expect(config).not.toEqual({}); expect(config).not.toEqual({});
}); });
it('website with valid JS CJS config', async () => {
const config = await loadSiteConfig({
siteDir,
customConfigFilePath: 'configCJS.js',
});
expect(config).toMatchSnapshot();
expect(config).not.toEqual({});
});
it('website with valid JS ESM config', async () => {
const config = await loadSiteConfig({
siteDir,
customConfigFilePath: 'configESM.js',
});
expect(config).toMatchSnapshot();
expect(config).not.toEqual({});
});
it('website with valid TypeScript CJS config', async () => {
const config = await loadSiteConfig({
siteDir,
customConfigFilePath: 'configCJS.ts',
});
expect(config).toMatchSnapshot();
expect(config).not.toEqual({});
});
it('website with valid TypeScript ESM config', async () => {
const config = await loadSiteConfig({
siteDir,
customConfigFilePath: 'configESM.ts',
});
expect(config).toMatchSnapshot();
expect(config).not.toEqual({});
});
it('website with incomplete siteConfig', async () => { it('website with incomplete siteConfig', async () => {
await expect( await expect(
loadSiteConfig({ loadSiteConfig({

View file

@ -7,15 +7,18 @@
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs-extra';
import importFresh from 'import-fresh';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import {DEFAULT_CONFIG_FILE_NAME, findAsyncSequential} from '@docusaurus/utils'; import {
DEFAULT_CONFIG_FILE_NAME,
findAsyncSequential,
loadFreshModule,
} from '@docusaurus/utils';
import {validateConfig} from './configValidation'; import {validateConfig} from './configValidation';
import type {LoadContext} from '@docusaurus/types'; import type {LoadContext} from '@docusaurus/types';
async function findConfig(siteDir: string) { async function findConfig(siteDir: string) {
// We could support .mjs, .ts, etc. in the future // We could support .mjs, .ts, etc. in the future
const candidates = ['.js', '.cjs'].map( const candidates = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs'].map(
(ext) => DEFAULT_CONFIG_FILE_NAME + ext, (ext) => DEFAULT_CONFIG_FILE_NAME + ext,
); );
const configPath = await findAsyncSequential( const configPath = await findAsyncSequential(
@ -46,7 +49,7 @@ export async function loadSiteConfig({
throw new Error(`Config file at "${siteConfigPath}" not found.`); throw new Error(`Config file at "${siteConfigPath}" not found.`);
} }
const importedConfig = importFresh(siteConfigPath); const importedConfig = await loadFreshModule(siteConfigPath);
const loadedConfig: unknown = const loadedConfig: unknown =
typeof importedConfig === 'function' typeof importedConfig === 'function'

View file

@ -0,0 +1,15 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
export default function preset(context, opts = {}) {
return {
plugins: [
['@docusaurus/plugin-content-docs', opts.docs],
['@docusaurus/plugin-content-blog', opts.blog],
],
};
}

View file

@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {LoadContext, Preset} from '@docusaurus/types';
export default function preset(
context: LoadContext,
opts: {theme: {}; test: {}},
) {
return {
themes: [['@docusaurus/theme-classic', opts.theme], null],
plugins: [['@docusaurus/plugin-test', opts.theme], false],
} satisfies Preset;
}

View file

@ -23,8 +23,8 @@ module.exports = {
], ],
'./plugin3.js', './plugin3.js',
['./plugin4.js', {}], ['./plugin4.js', {}],
'./pluginEsm',
'./pluginTypeScript',
], ],
presets: [ presets: ['./preset.js'],
'./preset.js',
],
}; };

View file

@ -0,0 +1,11 @@
import {Joi} from '@docusaurus/utils-validation';
export default function (context, options) {
return {
name: 'plugin-esm',
};
}
export function validateThemeConfig({validate, themeConfig}) {
return {esmPlugin: {joi: !!Joi}};
}

View file

@ -0,0 +1,14 @@
import {Joi} from '@docusaurus/utils-validation';
import {LoadContext, ThemeConfigValidationContext} from '@docusaurus/types';
export default function (context: LoadContext, options: unknown) {
return {
name: 'plugin-ts',
};
}
export function validateThemeConfig({
validate,
themeConfig,
}: ThemeConfigValidationContext<unknown, unknown>) {
return {tsPlugin: {joi: !!Joi}};
}

View file

@ -63,6 +63,38 @@ exports[`loadPresets array form with options 1`] = `
} }
`; `;
exports[`loadPresets cjs form 1`] = `
{
"plugins": [
[
"@docusaurus/plugin-content-docs",
undefined,
],
[
"@docusaurus/plugin-content-blog",
undefined,
],
],
"themes": [],
}
`;
exports[`loadPresets esm form 1`] = `
{
"plugins": [
[
"@docusaurus/plugin-content-docs",
undefined,
],
[
"@docusaurus/plugin-content-blog",
undefined,
],
],
"themes": [],
}
`;
exports[`loadPresets mixed form 1`] = ` exports[`loadPresets mixed form 1`] = `
{ {
"plugins": [ "plugins": [
@ -127,22 +159,6 @@ exports[`loadPresets mixed form with themes 1`] = `
} }
`; `;
exports[`loadPresets string form 1`] = `
{
"plugins": [
[
"@docusaurus/plugin-content-docs",
undefined,
],
[
"@docusaurus/plugin-content-blog",
undefined,
],
],
"themes": [],
}
`;
exports[`loadPresets string form composite 1`] = ` exports[`loadPresets string form composite 1`] = `
{ {
"plugins": [ "plugins": [
@ -167,3 +183,22 @@ exports[`loadPresets string form composite 1`] = `
], ],
} }
`; `;
exports[`loadPresets ts form 1`] = `
{
"plugins": [
[
"@docusaurus/plugin-test",
undefined,
],
false,
],
"themes": [
[
"@docusaurus/theme-classic",
undefined,
],
null,
],
}
`;

View file

@ -21,8 +21,8 @@ describe('initPlugins', () => {
it('parses plugins correctly and loads them in correct order', async () => { it('parses plugins correctly and loads them in correct order', async () => {
const {context, plugins} = await loadSite(); const {context, plugins} = await loadSite();
expect(context.siteConfig.plugins).toHaveLength(4); expect(context.siteConfig.plugins).toHaveLength(6);
expect(plugins).toHaveLength(8); expect(plugins).toHaveLength(10);
expect(plugins[0]!.name).toBe('preset-plugin1'); expect(plugins[0]!.name).toBe('preset-plugin1');
expect(plugins[1]!.name).toBe('preset-plugin2'); expect(plugins[1]!.name).toBe('preset-plugin2');
@ -32,7 +32,15 @@ describe('initPlugins', () => {
expect(plugins[5]!.name).toBe('second-plugin'); expect(plugins[5]!.name).toBe('second-plugin');
expect(plugins[6]!.name).toBe('third-plugin'); expect(plugins[6]!.name).toBe('third-plugin');
expect(plugins[7]!.name).toBe('fourth-plugin'); expect(plugins[7]!.name).toBe('fourth-plugin');
expect(context.siteConfig.themeConfig).toEqual({a: 1}); expect(context.siteConfig.themeConfig).toEqual({
a: 1,
esmPlugin: {
joi: true,
},
tsPlugin: {
joi: true,
},
});
}); });
it('throws user-friendly error message for plugins with bad values', async () => { it('throws user-friendly error message for plugins with bad values', async () => {

View file

@ -26,12 +26,38 @@ describe('loadPresets', () => {
`); `);
}); });
it('string form', async () => { it('cjs form', async () => {
const context = { const context = {
siteConfigPath: __dirname, siteConfigPath: __dirname,
siteConfig: { siteConfig: {
presets: [ presets: [
path.join(__dirname, '__fixtures__/presets/preset-plugins.js'), path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js'),
],
},
} as LoadContext;
const presets = await loadPresets(context);
expect(presets).toMatchSnapshot();
});
it('esm form', async () => {
const context = {
siteConfigPath: __dirname,
siteConfig: {
presets: [
path.join(__dirname, '__fixtures__/presets/preset-plugins.esm.js'),
],
},
} as LoadContext;
const presets = await loadPresets(context);
expect(presets).toMatchSnapshot();
});
it('ts form', async () => {
const context = {
siteConfigPath: __dirname,
siteConfig: {
presets: [
path.join(__dirname, '__fixtures__/presets/preset-plugins.ts'),
], ],
}, },
} as LoadContext; } as LoadContext;
@ -44,7 +70,7 @@ describe('loadPresets', () => {
siteConfigPath: __dirname, siteConfigPath: __dirname,
siteConfig: { siteConfig: {
presets: [ presets: [
path.join(__dirname, '__fixtures__/presets/preset-plugins.js'), path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js'),
path.join(__dirname, '__fixtures__/presets/preset-themes.js'), path.join(__dirname, '__fixtures__/presets/preset-themes.js'),
], ],
}, },
@ -58,7 +84,7 @@ describe('loadPresets', () => {
siteConfigPath: __dirname, siteConfigPath: __dirname,
siteConfig: { siteConfig: {
presets: [ presets: [
[path.join(__dirname, '__fixtures__/presets/preset-plugins.js')], [path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js')],
], ],
}, },
} as unknown as LoadContext; } as unknown as LoadContext;
@ -72,7 +98,7 @@ describe('loadPresets', () => {
siteConfig: { siteConfig: {
presets: [ presets: [
[ [
path.join(__dirname, '__fixtures__/presets/preset-plugins.js'), path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js'),
{docs: {path: '../'}}, {docs: {path: '../'}},
], ],
], ],
@ -88,7 +114,7 @@ describe('loadPresets', () => {
siteConfig: { siteConfig: {
presets: [ presets: [
[ [
path.join(__dirname, '__fixtures__/presets/preset-plugins.js'), path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js'),
{docs: {path: '../'}}, {docs: {path: '../'}},
], ],
[ [
@ -108,7 +134,7 @@ describe('loadPresets', () => {
siteConfig: { siteConfig: {
presets: [ presets: [
[ [
path.join(__dirname, '__fixtures__/presets/preset-plugins.js'), path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js'),
{docs: {path: '../'}}, {docs: {path: '../'}},
], ],
path.join(__dirname, '__fixtures__/presets/preset-themes.js'), path.join(__dirname, '__fixtures__/presets/preset-themes.js'),
@ -125,7 +151,7 @@ describe('loadPresets', () => {
siteConfig: { siteConfig: {
presets: [ presets: [
[ [
path.join(__dirname, '__fixtures__/presets/preset-plugins.js'), path.join(__dirname, '__fixtures__/presets/preset-plugins.cjs.js'),
{docs: {path: '../'}}, {docs: {path: '../'}},
], ],
false, false,

View file

@ -6,7 +6,7 @@
*/ */
import {createRequire} from 'module'; import {createRequire} from 'module';
import importFresh from 'import-fresh'; import {loadFreshModule} from '@docusaurus/utils';
import {loadPresets} from './presets'; import {loadPresets} from './presets';
import {resolveModuleName} from './moduleShorthand'; import {resolveModuleName} from './moduleShorthand';
import type { import type {
@ -61,7 +61,9 @@ async function normalizePluginConfig(
if (typeof pluginConfig === 'string') { if (typeof pluginConfig === 'string') {
const pluginModuleImport = pluginConfig; const pluginModuleImport = pluginConfig;
const pluginPath = pluginRequire.resolve(pluginModuleImport); const pluginPath = pluginRequire.resolve(pluginModuleImport);
const pluginModule = importFresh<ImportedPluginModule>(pluginPath); const pluginModule = (await loadFreshModule(
pluginPath,
)) as ImportedPluginModule;
return { return {
plugin: pluginModule.default ?? pluginModule, plugin: pluginModule.default ?? pluginModule,
options: {}, options: {},
@ -88,7 +90,9 @@ async function normalizePluginConfig(
if (typeof pluginConfig[0] === 'string') { if (typeof pluginConfig[0] === 'string') {
const pluginModuleImport = pluginConfig[0]; const pluginModuleImport = pluginConfig[0];
const pluginPath = pluginRequire.resolve(pluginModuleImport); const pluginPath = pluginRequire.resolve(pluginModuleImport);
const pluginModule = importFresh<ImportedPluginModule>(pluginPath); const pluginModule = (await loadFreshModule(
pluginPath,
)) as ImportedPluginModule;
return { return {
plugin: pluginModule.default ?? pluginModule, plugin: pluginModule.default ?? pluginModule,
options: pluginConfig[1], options: pluginConfig[1],

View file

@ -6,12 +6,13 @@
*/ */
import {createRequire} from 'module'; import {createRequire} from 'module';
import importFresh from 'import-fresh'; import {loadFreshModule} from '@docusaurus/utils';
import {resolveModuleName} from './moduleShorthand'; import {resolveModuleName} from './moduleShorthand';
import type { import type {
LoadContext, LoadContext,
PluginConfig, PresetConfigDefined,
PresetModule, PresetModule,
Preset,
DocusaurusConfig, DocusaurusConfig,
} from '@docusaurus/types'; } from '@docusaurus/types';
@ -28,16 +29,13 @@ export async function loadPresets(
// we are using `require.resolve` on those module names. // we are using `require.resolve` on those module names.
const presetRequire = createRequire(context.siteConfigPath); const presetRequire = createRequire(context.siteConfigPath);
const {presets} = context.siteConfig; const presets = context.siteConfig.presets.filter(
const plugins: PluginConfig[] = []; (p): p is PresetConfigDefined => !!p,
const themes: PluginConfig[] = []; );
presets.forEach((presetItem) => { async function loadPreset(presetItem: PresetConfigDefined): Promise<Preset> {
let presetModuleImport: string; let presetModuleImport: string;
let presetOptions = {}; let presetOptions = {};
if (!presetItem) {
return;
}
if (typeof presetItem === 'string') { if (typeof presetItem === 'string') {
presetModuleImport = presetItem; presetModuleImport = presetItem;
} else { } else {
@ -49,21 +47,20 @@ export async function loadPresets(
'preset', 'preset',
); );
const presetModule = importFresh<ImportedPresetModule>( const presetPath = presetRequire.resolve(presetName);
presetRequire.resolve(presetName), const presetModule = (await loadFreshModule(
); presetPath,
const preset = (presetModule.default ?? presetModule)( )) as ImportedPresetModule;
context,
presetOptions,
);
if (preset.plugins) { const presetFunction = presetModule.default ?? presetModule;
plugins.push(...preset.plugins);
return presetFunction(context, presetOptions);
} }
if (preset.themes) {
themes.push(...preset.themes); const loadedPresets = await Promise.all(presets.map(loadPreset));
}
}); const plugins = loadedPresets.flatMap((preset) => preset.plugins ?? []);
const themes = loadedPresets.flatMap((preset) => preset.themes ?? []);
return {plugins, themes}; return {plugins, themes};
} }

View file

@ -60,7 +60,6 @@ cssnano
csvg csvg
customizability customizability
dabit dabit
dabit
daishi daishi
datagit datagit
datas datas
@ -98,20 +97,22 @@ eslintcache
estree estree
evaluable evaluable
execa execa
execa
externalwaiting externalwaiting
failfast failfast
Fargate
fbid fbid
février février
fienny fienny
flac flac
flightcontrol
formik formik
fouc fouc
froms froms
funboxteam funboxteam
gantt
gabrielcsapo gabrielcsapo
gantt
getopts getopts
gifs
gitgraph gitgraph
gitpod gitpod
globbing globbing
@ -123,6 +124,7 @@ gtag
hahaha hahaha
hamel hamel
hardcoding hardcoding
hastscript
hasura hasura
heavener heavener
héctor héctor
@ -136,6 +138,7 @@ hoverable
husain husain
ianad ianad
idempotency idempotency
Iframes
immer immer
infima infima
inlines inlines
@ -148,6 +151,7 @@ jakepartusch
jamstack jamstack
janvier janvier
javadoc javadoc
jiti
jmarcey jmarcey
jodyheavener jodyheavener
joshcena joshcena
@ -183,6 +187,7 @@ mathjax
maxlynch maxlynch
maxresdefault maxresdefault
mdast mdast
mdwn
mdxa mdxa
mdxast mdxast
mdxhast mdxhast
@ -198,9 +203,8 @@ minifier
mkcert mkcert
mkdir mkdir
mkdirs mkdirs
mkdocs
mkdn mkdn
mdwn mkdocs
mkdown mkdown
moesif moesif
msapplication msapplication
@ -273,6 +277,7 @@ prerendered
prerendering prerendering
println println
prismjs prismjs
producthunt
profilo profilo
protobuf protobuf
protobuffet protobuffet
@ -319,7 +324,6 @@ serializers
setaf setaf
setext setext
shiki shiki
shiki
showinfo showinfo
sida sida
simen simen
@ -328,7 +332,6 @@ sluggified
sluggifies sluggifies
sluggify sluggify
solana solana
solana
spâce spâce
stackblitz stackblitz
stackblitzrc stackblitzrc
@ -373,6 +376,7 @@ typecheck
typechecks typechecks
typedoc typedoc
typesense typesense
unavatar
unflat unflat
unist unist
unlinkable unlinkable
@ -415,11 +419,3 @@ yangshunz
zhou zhou
zoomable zoomable
zpao zpao
hastscript
Flightcontrol
Fargate
Flightcontrol's
producthunt
Gifs
Iframes
Unavatar

View file

@ -5,24 +5,24 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
/** @type {import('@docusaurus/types').PluginConfig[]} */ import type {PluginConfig, Plugin} from '@docusaurus/types';
const dogfoodingThemeInstances = [ import type {Options as DocsOptions} from '@docusaurus/plugin-content-docs';
/** @type {import('@docusaurus/types').PluginModule} */ import type {Options as BlogOptions} from '@docusaurus/plugin-content-blog';
function swizzleThemeTests() { import type {Options as PageOptions} from '@docusaurus/plugin-content-pages';
export const dogfoodingThemeInstances: PluginConfig[] = [
function swizzleThemeTests(): Plugin {
return { return {
name: 'swizzle-theme-tests', name: 'swizzle-theme-tests',
getThemePath: () => './_swizzle_theme_tests/src/theme', getThemePath: () => './_swizzle_theme_tests/src/theme',
}; };
}, },
]; ];
exports.dogfoodingThemeInstances = dogfoodingThemeInstances;
/** @type {import('@docusaurus/types').PluginConfig[]} */ export const dogfoodingPluginInstances: PluginConfig[] = [
const dogfoodingPluginInstances = [
[ [
'content-docs', // Shorthand 'content-docs', // Shorthand
/** @type {import('@docusaurus/plugin-content-docs').Options} */ {
({
id: 'docs-tests', id: 'docs-tests',
routeBasePath: '/tests/docs', routeBasePath: '/tests/docs',
sidebarPath: '_dogfooding/docs-tests-sidebars.js', sidebarPath: '_dogfooding/docs-tests-sidebars.js',
@ -43,20 +43,20 @@ const dogfoodingPluginInstances = [
const eligibleDocIndexNames = [ const eligibleDocIndexNames = [
'index', 'index',
'readme', 'readme',
directories[0].toLowerCase(), directories[0]!.toLowerCase(),
'intro', 'intro',
]; ];
return eligibleDocIndexNames.includes(fileName.toLowerCase()); return eligibleDocIndexNames.includes(fileName.toLowerCase());
}, },
}); });
}, },
}), } satisfies DocsOptions,
], ],
[ [
'@docusaurus/plugin-content-blog', // Longhand '@docusaurus/plugin-content-blog', // Longhand
/** @type {import('@docusaurus/plugin-content-blog').Options} */ /** @type {import('@docusaurus/plugin-content-blog').Options} */
({ {
id: 'blog-tests', id: 'blog-tests',
path: '_dogfooding/_blog tests', path: '_dogfooding/_blog tests',
routeBasePath: '/tests/blog', routeBasePath: '/tests/blog',
@ -72,21 +72,19 @@ const dogfoodingPluginInstances = [
frontMatter.hide_reading_time frontMatter.hide_reading_time
? undefined ? undefined
: defaultReadingTime({content, options: {wordsPerMinute: 5}}), : defaultReadingTime({content, options: {wordsPerMinute: 5}}),
}), } satisfies BlogOptions,
], ],
[ [
require.resolve('@docusaurus/plugin-content-pages'), // Full path require.resolve('@docusaurus/plugin-content-pages'), // Full path
/** @type {import('@docusaurus/plugin-content-pages').Options} */ {
({
id: 'pages-tests', id: 'pages-tests',
path: '_dogfooding/_pages tests', path: '_dogfooding/_pages tests',
routeBasePath: '/tests/pages', routeBasePath: '/tests/pages',
}), } satisfies PageOptions,
], ],
/** @type {import('@docusaurus/types').Plugin} */ function clientModuleTestPlugin(): Plugin {
function clientModuleTestPlugin() {
return { return {
name: 'client-module-test-plugin', name: 'client-module-test-plugin',
getClientModules() { getClientModules() {
@ -99,9 +97,7 @@ const dogfoodingPluginInstances = [
}, },
]; ];
exports.dogfoodingPluginInstances = dogfoodingPluginInstances; export const dogfoodingRedirects: {from: string[]; to: string}[] = [
exports.dogfoodingRedirects = [
{ {
from: ['/home/'], from: ['/home/'],
to: '/', to: '/',

View file

@ -362,13 +362,22 @@ console.log('hello');
All the official packages (Unified, Remark, Rehype...) in the MDX ecosystem are now [**ES Modules only**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and do not support [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) anymore. All the official packages (Unified, Remark, Rehype...) in the MDX ecosystem are now [**ES Modules only**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and do not support [CommonJS](https://nodejs.org/api/modules.html#modules-commonjs-modules) anymore.
In practice this means that you can't do `require("remark-plugin")` anymore. Unfortunately the Docusaurus config file only supports CommonJS at the moment, and you can't use `import MyRemarkPlugin from "remark-plugin"`. In practice this means that you can't do `require("remark-plugin")` anymore.
:::tip How to prepare :::tip How to prepare
Using dynamic imports is a good workaround that enables you to import ES modules inside a CommonJS module. Fortunately, the [Docusaurus config supports the usage of an async function](/docs/configuration#syntax-to-declare-docusaurus-config) to let you do so. Docusaurus v3 now supports [**ES Modules**](https://flaviocopes.com/es-modules/) configuration files. We recommend that you migrate your config file to ES module, that enables you to import the Remark plugins easily:
You can start refactoring your config to use **async dynamic imports** today. Refer to the [MDX plugin installation documentation](/docs/3.0.0-beta.0/markdown-features/plugins#installing-plugins) for details. ```js title="docusaurus.config.js"
import remarkPlugin from 'remark-plugin';
export default {
title: 'Docusaurus',
/* site config using remark plugins here */
};
```
If you want to keep using CommonJS modules, you can use dynamic imports as a workaround that enables you to import ES modules inside a CommonJS module. Fortunately, the [Docusaurus config supports the usage of an async function](/docs/configuration#syntax-to-declare-docusaurus-config) to let you do so.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = async function () { module.exports = async function () {

View file

@ -20,7 +20,7 @@ This diagram shows how Docusaurus works to build your app. Plugins each collect
Although you (either plugin authors or site creators) are writing JavaScript all the time, bear in mind that the JS is actually run in different environments: Although you (either plugin authors or site creators) are writing JavaScript all the time, bear in mind that the JS is actually run in different environments:
- All plugin lifecycle methods are run in Node. Therefore, until we support ES Modules in our codebase, plugin source code must be provided as CommonJS that can be `require`'d. - All plugin lifecycle methods are run in Node. Therefore, until we support ES Modules in our codebase, plugin source code must be provided as ES modules that can be imported, or CommonJS that can be `require`'d.
- The theme code is built with Webpack. They can be provided as ESM—following React conventions. - The theme code is built with Webpack. They can be provided as ESM—following React conventions.
Plugin code and theme code never directly import each other: they only communicate through protocols (in our case, through JSON temp files and calls to `addRoute`). A useful mental model is to imagine that the plugins are not written in JavaScript, but in another language like Rust. The only way to interact with plugins for the user is through `docusaurus.config.js`, which itself is run in Node (hence you can use `require` and pass callbacks as plugin options). Plugin code and theme code never directly import each other: they only communicate through protocols (in our case, through JSON temp files and calls to `addRoute`). A useful mental model is to imagine that the plugins are not written in JavaScript, but in another language like Rust. The only way to interact with plugins for the user is through `docusaurus.config.js`, which itself is run in Node (hence you can use `require` and pass callbacks as plugin options).

View file

@ -11,7 +11,7 @@ A plugin is a function that takes two parameters: `context` and `options`. It re
You can use a plugin as a function directly included 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" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: [ plugins: [
// highlight-start // highlight-start
@ -38,7 +38,7 @@ module.exports = {
You can use a plugin as a module path referencing 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" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: [ plugins: [
// without options: // without options:
@ -52,7 +52,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/index.js" ```js title="my-plugin/index.js"
module.exports = async function myPlugin(context, options) { export default async function myPlugin(context, options) {
// ... // ...
return { return {
name: 'my-plugin', name: 'my-plugin',
@ -64,7 +64,7 @@ module.exports = async function myPlugin(context, options) {
}, },
/* other lifecycle API */ /* other lifecycle API */
}; };
}; }
``` ```
--- ---
@ -99,7 +99,7 @@ This is a contrived example: in practice, `@docusaurus/theme-classic` provides t
::: :::
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// highlight-next-line // highlight-next-line
themes: ['theme-blog'], themes: ['theme-blog'],
plugins: ['plugin-content-blog'], plugins: ['plugin-content-blog'],
@ -109,7 +109,7 @@ module.exports = {
And if you want to use Bootstrap styling, you can swap out the theme with `theme-blog-bootstrap` (another fictitious non-existing theme): And if you want to use Bootstrap styling, you can swap out the theme with `theme-blog-bootstrap` (another fictitious non-existing theme):
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// highlight-next-line // highlight-next-line
themes: ['theme-blog-bootstrap'], themes: ['theme-blog-bootstrap'],
plugins: ['plugin-content-blog'], plugins: ['plugin-content-blog'],

View file

@ -18,12 +18,20 @@ Refer to the Getting Started [**Configuration**](../configuration.mdx) for examp
`docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site. `docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site.
This file is run in Node.js using the [**CommonJS**](https://flaviocopes.com/commonjs/) module system, and should export a site configuration object, or a function that creates it. This file is run in Node.js and should export a site configuration object, or a function that creates it.
The `docusaurus.config.js` file supports:
- [**ES Modules**](https://flaviocopes.com/es-modules/)
- [**CommonJS**](https://flaviocopes.com/commonjs/)
- [**TypeScript**](../typescript-support.mdx#typing-config)
hey
Examples: Examples:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
title: 'Docusaurus', title: 'Docusaurus',
url: 'https://docusaurus.io', url: 'https://docusaurus.io',
// your site config ... // your site config ...
@ -31,13 +39,13 @@ module.exports = {
``` ```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = async function createConfigAsync() { export default async function createConfigAsync() {
return { return {
title: 'Docusaurus', title: 'Docusaurus',
url: 'https://docusaurus.io', url: 'https://docusaurus.io',
// your site config ... // your site config ...
}; };
}; }
``` ```
:::tip :::tip
@ -55,7 +63,7 @@ Refer to [**Syntax to declare `docusaurus.config.js`**](../configuration.mdx#syn
Title for your website. Will be used in metadata and as browser tab title. Title for your website. Will be used in metadata and as browser tab title.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
title: 'Docusaurus', title: 'Docusaurus',
}; };
``` ```
@ -67,7 +75,7 @@ module.exports = {
URL for your website. This can also be considered the top-level hostname. For example, `https://facebook.github.io` is the URL of https://facebook.github.io/metro/, and `https://docusaurus.io` is the URL for https://docusaurus.io. This field is related to the [`baseUrl`](#baseUrl) field. URL for your website. This can also be considered the top-level hostname. For example, `https://facebook.github.io` is the URL of https://facebook.github.io/metro/, and `https://docusaurus.io` is the URL for https://docusaurus.io. This field is related to the [`baseUrl`](#baseUrl) field.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
url: 'https://docusaurus.io', url: 'https://docusaurus.io',
}; };
``` ```
@ -79,7 +87,7 @@ module.exports = {
Base URL for your site. Can be considered as the path after the host. For example, `/metro/` is the base URL of https://facebook.github.io/metro/. For URLs that have no path, the baseUrl should be set to `/`. This field is related to the [`url`](#url) field. Always has both leading and trailing slash. Base URL for your site. Can be considered as the path after the host. For example, `/metro/` is the base URL of https://facebook.github.io/metro/. For URLs that have no path, the baseUrl should be set to `/`. This field is related to the [`url`](#url) field. Always has both leading and trailing slash.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
baseUrl: '/', baseUrl: '/',
}; };
``` ```
@ -93,7 +101,7 @@ module.exports = {
Path to your site favicon; must be a URL that can be used in link's href. For example, if your favicon is in `static/img/favicon.ico`: Path to your site favicon; must be a URL that can be used in link's href. For example, if your favicon is in `static/img/favicon.ico`:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
favicon: '/img/favicon.ico', favicon: '/img/favicon.ico',
}; };
``` ```
@ -127,7 +135,7 @@ Example:
{/* cSpell:ignore فارسی */} {/* cSpell:ignore فارسی */}
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en', 'fa'], locales: ['en', 'fa'],
@ -171,7 +179,7 @@ This option adds `<meta name="robots" content="noindex, nofollow">` to every pag
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
noIndex: true, // Defaults to `false` noIndex: true, // Defaults to `false`
}; };
``` ```
@ -213,7 +221,7 @@ By default, it displays a warning after you run `yarn start` or `yarn build`.
The tagline for your website. The tagline for your website.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
tagline: tagline:
'Docusaurus makes it easy to maintain Open Source documentation websites.', 'Docusaurus makes it easy to maintain Open Source documentation websites.',
}; };
@ -226,7 +234,7 @@ module.exports = {
The GitHub user or organization that owns the repository. You don't need this if you are not using the `docusaurus deploy` command. The GitHub user or organization that owns the repository. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// Docusaurus' organization is facebook // Docusaurus' organization is facebook
organizationName: 'facebook', organizationName: 'facebook',
}; };
@ -239,7 +247,7 @@ module.exports = {
The name of the GitHub repository. You don't need this if you are not using the `docusaurus deploy` command. The name of the GitHub repository. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
projectName: 'docusaurus', projectName: 'docusaurus',
}; };
``` ```
@ -251,7 +259,7 @@ module.exports = {
The name of the branch to deploy the static files to. You don't need this if you are not using the `docusaurus deploy` command. The name of the branch to deploy the static files to. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
deploymentBranch: 'gh-pages', deploymentBranch: 'gh-pages',
}; };
``` ```
@ -263,7 +271,7 @@ module.exports = {
The hostname of your server. Useful if you are using GitHub Enterprise. You don't need this if you are not using the `docusaurus deploy` command. The hostname of your server. Useful if you are using GitHub Enterprise. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
githubHost: 'github.com', githubHost: 'github.com',
}; };
``` ```
@ -275,7 +283,7 @@ module.exports = {
The port of your server. Useful if you are using GitHub Enterprise. You don't need this if you are not using the `docusaurus deploy` command. The port of your server. Useful if you are using GitHub Enterprise. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
githubPort: '22', githubPort: '22',
}; };
``` ```
@ -289,7 +297,7 @@ The [theme configuration](./themes/theme-configuration.mdx) object to customize
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
docs: { docs: {
sidebar: { sidebar: {
@ -358,7 +366,7 @@ type PluginConfig = string | [string, any] | PluginModule | [PluginModule, any];
See [plugin method references](./plugin-methods/README.mdx) for the shape of a `PluginModule`. See [plugin method references](./plugin-methods/README.mdx) for the shape of a `PluginModule`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
'docusaurus-plugin-awesome', 'docusaurus-plugin-awesome',
['docusuarus-plugin-confetti', {fancy: false}], ['docusuarus-plugin-confetti', {fancy: false}],
@ -376,7 +384,7 @@ module.exports = {
- Type: `PluginConfig[]` - Type: `PluginConfig[]`
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themes: ['@docusaurus/theme-classic'], themes: ['@docusaurus/theme-classic'],
}; };
``` ```
@ -390,7 +398,7 @@ type PresetConfig = string | [string, any];
``` ```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [], presets: [],
}; };
``` ```
@ -426,7 +434,7 @@ type MarkdownConfig = {
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
markdown: { markdown: {
format: 'mdx', format: 'mdx',
mermaid: true, mermaid: true,
@ -464,7 +472,7 @@ Docusaurus guards `docusaurus.config.js` from unknown fields. To add a custom fi
- Type: `Object` - Type: `Object`
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
customFields: { customFields: {
admin: 'endi', admin: 'endi',
superman: 'lol', superman: 'lol',
@ -487,7 +495,7 @@ An array of paths, relative to the site's directory or absolute. Files under the
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
staticDirectories: ['static'], staticDirectories: ['static'],
}; };
``` ```
@ -501,7 +509,7 @@ An array of tags that will be inserted in the HTML `<head>`. The values must be
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
headTags: [ headTags: [
{ {
tagName: 'link', tagName: 'link',
@ -527,7 +535,7 @@ Note that `<script>` added here are render-blocking, so you might want to add `a
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
scripts: [ scripts: [
// String format. // String format.
'https://docusaurus.io/script.js', 'https://docusaurus.io/script.js',
@ -549,7 +557,7 @@ An array of CSS sources to load. The values can be either strings or plain objec
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
stylesheets: [ stylesheets: [
// String format. // String format.
'https://docusaurus.io/style.css', 'https://docusaurus.io/style.css',
@ -574,11 +582,8 @@ An array of [client modules](../advanced/client.mdx#client-modules) to load glob
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
clientModules: [ clientModules: ['./mySiteGlobalJs.js', './mySiteGlobalCss.css'],
require.resolve('./mySiteGlobalJs.js'),
require.resolve('./mySiteGlobalCss.css'),
],
}; };
``` ```
@ -591,7 +596,7 @@ An HTML template written in [Eta's syntax](https://eta.js.org/docs/syntax#syntax
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
ssrTemplate: `<!DOCTYPE html> ssrTemplate: `<!DOCTYPE html>
<html <%~ it.htmlAttributes %>> <html <%~ it.htmlAttributes %>>
<head> <head>
@ -631,7 +636,7 @@ Will be used as title delimiter in the generated `<title>` tag.
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
titleDelimiter: '🦖', // Defaults to `|` titleDelimiter: '🦖', // Defaults to `|`
}; };
``` ```
@ -645,7 +650,7 @@ When enabled, will show a banner in case your site can't load its CSS or JavaScr
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
baseUrlIssueBanner: true, // Defaults to `true` baseUrlIssueBanner: true, // Defaults to `true`
}; };
``` ```

View file

@ -47,7 +47,7 @@ Here's a mental model for a presumptuous plugin implementation.
// A JavaScript function that returns an object. // A JavaScript function that returns an object.
// `context` is provided by Docusaurus. Example: siteConfig can be accessed from context. // `context` is provided by Docusaurus. Example: siteConfig can be accessed from context.
// `opts` is the user-defined options. // `opts` is the user-defined options.
async function myPlugin(context, opts) { export default async function myPlugin(context, opts) {
return { return {
// A compulsory field used as the namespace for directories to cache // A compulsory field used as the namespace for directories to cache
// the intermediate data for each plugin. // the intermediate data for each plugin.
@ -132,15 +132,13 @@ async function myPlugin(context, opts) {
}; };
} }
myPlugin.validateOptions = ({options, validate}) => { export function validateOptions({options, validate}) {
const validatedOptions = validate(myValidationSchema, options); const validatedOptions = validate(myValidationSchema, options);
return validatedOptions; return validatedOptions;
}; }
myPlugin.validateThemeConfig = ({themeConfig, validate}) => { export function validateThemeConfig({themeConfig, validate}) {
const validatedThemeConfig = validate(myValidationSchema, options); const validatedThemeConfig = validate(myValidationSchema, options);
return validatedThemeConfig; return validatedThemeConfig;
}; }
module.exports = myPlugin;
``` ```

View file

@ -15,8 +15,9 @@ Use this for files that are consumed server-side, because theme files are automa
Example: Example:
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
const path = require('path'); import path from 'path';
module.exports = function (context, options) {
export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// highlight-start // highlight-start
@ -26,7 +27,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `extendCli(cli)` {#extendCli} ## `extendCli(cli)` {#extendCli}
@ -42,7 +43,7 @@ The commander version matters! We use commander v5, and make sure you are referr
Example: Example:
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// highlight-start // highlight-start
@ -56,7 +57,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `getThemePath()` {#getThemePath} ## `getThemePath()` {#getThemePath}
@ -66,9 +67,7 @@ Returns the path to the directory where the theme components can be found. When
For example, your `getThemePath` can be: For example, your `getThemePath` can be:
```js title="my-theme/src/index.js" ```js title="my-theme/src/index.js"
const path = require('path'); export default function (context, options) {
module.exports = function (context, options) {
return { return {
name: 'my-theme', name: 'my-theme',
// highlight-start // highlight-start
@ -77,7 +76,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `getTypeScriptThemePath()` {#getTypeScriptThemePath} ## `getTypeScriptThemePath()` {#getTypeScriptThemePath}
@ -95,9 +94,7 @@ You should also format these files with Prettier. Remember—JS files can and wi
Example: Example:
```js title="my-theme/src/index.js" ```js title="my-theme/src/index.js"
const path = require('path'); export default function (context, options) {
module.exports = function (context, options) {
return { return {
name: 'my-theme', name: 'my-theme',
// highlight-start // highlight-start
@ -111,7 +108,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `getSwizzleComponentList()` {#getSwizzleComponentList} ## `getSwizzleComponentList()` {#getSwizzleComponentList}
@ -121,7 +118,8 @@ module.exports = function (context, options) {
Returns a list of stable components that are considered safe for swizzling. These components will be swizzlable without `--danger`. All components are considered unstable by default. If an empty array is returned, all components are considered unstable. If `undefined` is returned, all components are considered stable. Returns a list of stable components that are considered safe for swizzling. These components will be swizzlable without `--danger`. All components are considered unstable by default. If an empty array is returned, all components are considered unstable. If `undefined` is returned, all components are considered stable.
```js title="my-theme/src/index.js" ```js title="my-theme/src/index.js"
const swizzleAllowedComponents = [ export function getSwizzleComponentList() {
return [
'CodeBlock', 'CodeBlock',
'DocSidebar', 'DocSidebar',
'Footer', 'Footer',
@ -130,6 +128,5 @@ const swizzleAllowedComponents = [
'hooks/useTheme', 'hooks/useTheme',
'prism-include-languages', 'prism-include-languages',
]; ];
}
myTheme.getSwizzleComponentList = () => swizzleAllowedComponents;
``` ```

View file

@ -19,8 +19,8 @@ These files will be written by the [`write-translations` CLI](../../cli.mdx#docu
Example: Example:
```js ```js title="my-plugin.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'my-plugin', name: 'my-plugin',
// highlight-start // highlight-start
@ -40,7 +40,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `translateContent({content,translationFiles})` {#translateContent} ## `translateContent({content,translationFiles})` {#translateContent}
@ -53,8 +53,8 @@ The `contentLoaded()` lifecycle will be called with the localized plugin content
Example: Example:
```js ```js title="my-plugin.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'my-plugin', name: 'my-plugin',
// highlight-start // highlight-start
@ -69,7 +69,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `translateThemeConfig({themeConfig,translationFiles})` {#translateThemeConfig} ## `translateThemeConfig({themeConfig,translationFiles})` {#translateThemeConfig}
@ -80,8 +80,8 @@ Returns the localized `themeConfig`.
Example: Example:
```js ```js title="my-plugin.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'my-theme', name: 'my-theme',
// highlight-start // highlight-start
@ -96,7 +96,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `async getDefaultCodeTranslationMessages()` {#getDefaultCodeTranslationMessages} ## `async getDefaultCodeTranslationMessages()` {#getDefaultCodeTranslationMessages}
@ -107,8 +107,8 @@ It should return messages in `Record<string, string>`, where keys are translatio
Example: Example:
```js ```js title="my-plugin.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'my-theme', name: 'my-theme',
// highlight-start // highlight-start
@ -117,5 +117,5 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```

View file

@ -14,7 +14,7 @@ Plugins should use this lifecycle to fetch from data sources (filesystem, remote
For example, this plugin below returns a random integer between 1 and 10 as content. For example, this plugin below returns a random integer between 1 and 10 as content.
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// highlight-start // highlight-start
@ -23,7 +23,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `async contentLoaded({content, actions})` {#contentLoaded} ## `async contentLoaded({content, actions})` {#contentLoaded}
@ -183,7 +183,7 @@ You may use them to return your webpack configuration conditionally.
For example, this plugin below modify the webpack config to transpile `.foo` files. For example, this plugin below modify the webpack config to transpile `.foo` files.
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'custom-docusaurus-plugin', name: 'custom-docusaurus-plugin',
// highlight-start // highlight-start
@ -202,7 +202,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
### `content` {#content-1} ### `content` {#content-1}
@ -216,7 +216,7 @@ We merge the Webpack configuration parts of plugins into the global Webpack conf
It is possible to specify the merge strategy. For example, if you want a webpack rule to be prepended instead of appended: 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" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'custom-docusaurus-plugin', name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) { configureWebpack(config, isServer, utils) {
@ -228,7 +228,7 @@ module.exports = function (context, options) {
}; };
}, },
}; };
}; }
``` ```
Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details. Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.
@ -238,7 +238,7 @@ Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge
The dev server can be configured through returning a `devServer` field. The dev server can be configured through returning a `devServer` field.
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'custom-docusaurus-plugin', name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) { configureWebpack(config, isServer, utils) {
@ -251,7 +251,7 @@ module.exports = function (context, options) {
}; };
}, },
}; };
}; }
``` ```
## `configurePostCss(options)` {#configurePostCss} ## `configurePostCss(options)` {#configurePostCss}
@ -272,7 +272,7 @@ const postcssOptions = {
Example: Example:
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// highlight-start // highlight-start
@ -283,7 +283,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `postBuild(props)` {#postBuild} ## `postBuild(props)` {#postBuild}
@ -309,7 +309,7 @@ interface Props {
Example: Example:
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// highlight-start // highlight-start
@ -321,7 +321,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
## `injectHtmlTags({content})` {#injectHtmlTags} ## `injectHtmlTags({content})` {#injectHtmlTags}
@ -361,7 +361,7 @@ type HtmlTagObject = {
Example: Example:
```js title="docusaurus-plugin/src/index.js" ```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) { export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
loadContent: async () => { loadContent: async () => {
@ -394,7 +394,7 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```
Tags will be added as follows: Tags will be added as follows:
@ -410,9 +410,7 @@ Returns an array of paths to the [client modules](../../advanced/client.mdx#clie
As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user: As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user:
```js title="my-theme/src/index.js" ```js title="my-theme/src/index.js"
const path = require('path'); export default function (context, options) {
module.exports = function (context, options) {
const {customCss, customJs} = options || {}; const {customCss, customJs} = options || {};
return { return {
name: 'name-of-my-theme', name: 'name-of-my-theme',
@ -422,5 +420,5 @@ module.exports = function (context, options) {
}, },
// highlight-end // highlight-end
}; };
}; }
``` ```

View file

@ -22,34 +22,14 @@ Returns validated and normalized options for the plugin. This method is called b
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of options. [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")` To avoid mixing Joi versions, use `import {Joi} from '@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. 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 title="my-plugin/src/index.js" ```js title="my-plugin/src/index.js"
function myPlugin(context, options) { export default function myPlugin(context, options) {
return {
name: 'docusaurus-plugin',
// rest of methods
};
}
// highlight-start
myPlugin.validateOptions = ({options, validate}) => {
const validatedOptions = validate(myValidationSchema, options);
return validatedOptions;
};
// highlight-end
module.exports = myPlugin;
```
In TypeScript, you can also choose to export this as a separate named export.
```ts title="my-plugin/src/index.ts"
export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// rest of methods // rest of methods
@ -80,34 +60,14 @@ Return validated and normalized configuration for the theme.
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of theme config. [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")` To avoid mixing Joi versions, use `import {Joi} from '@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. 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 title="my-theme/src/index.js" ```js title="my-theme/src/index.js"
function myPlugin(context, options) { export default function myPlugin(context, options) {
return {
name: 'docusaurus-plugin',
// rest of methods
};
}
// highlight-start
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
const validatedThemeConfig = validate(myValidationSchema, options);
return validatedThemeConfig;
};
// highlight-end
module.exports = validateThemeConfig;
```
In TypeScript, you can also choose to export this as a separate named export.
```ts title="my-theme/src/index.ts"
export default function (context, options) {
return { return {
name: 'docusaurus-plugin', name: 'docusaurus-plugin',
// rest of methods // rest of methods

View file

@ -89,7 +89,7 @@ type CreateRedirectsFn = (path: string) => string[] | string | null | undefined;
Here's an example configuration: Here's an example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-client-redirects', '@docusaurus/plugin-client-redirects',

View file

@ -169,7 +169,7 @@ const config = {
blogPostComponent: '@theme/BlogPostPage', blogPostComponent: '@theme/BlogPostPage',
blogTagsListComponent: '@theme/BlogTagsListPage', blogTagsListComponent: '@theme/BlogTagsListPage',
blogTagsPostsComponent: '@theme/BlogTagsPostsPage', blogTagsPostsComponent: '@theme/BlogTagsPostsPage',
remarkPlugins: [require('remark-math')], remarkPlugins: [require('./my-remark-plugin')],
rehypePlugins: [], rehypePlugins: [],
beforeDefaultRemarkPlugins: [], beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [], beforeDefaultRehypePlugins: [],

View file

@ -234,7 +234,7 @@ const config = {
}, },
docLayoutComponent: '@theme/DocPage', docLayoutComponent: '@theme/DocPage',
docItemComponent: '@theme/DocItem', docItemComponent: '@theme/DocItem',
remarkPlugins: [require('remark-math')], remarkPlugins: [require('./my-remark-plugin')],
rehypePlugins: [], rehypePlugins: [],
beforeDefaultRemarkPlugins: [], beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [], beforeDefaultRehypePlugins: [],

View file

@ -72,7 +72,7 @@ const config = {
'**/__tests__/**', '**/__tests__/**',
], ],
mdxPageComponent: '@theme/MDXPage', mdxPageComponent: '@theme/MDXPage',
remarkPlugins: [require('remark-math')], remarkPlugins: [require('./my-remark-plugin')],
rehypePlugins: [], rehypePlugins: [],
beforeDefaultRemarkPlugins: [], beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [], beforeDefaultRehypePlugins: [],

View file

@ -21,7 +21,7 @@ If you use the plugin via the classic preset, the preset will **enable the plugi
If you use a standalone plugin, you may need to achieve the same effect by checking the environment: If you use a standalone plugin, you may need to achieve the same effect by checking the environment:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
// highlight-next-line // highlight-next-line
process.env.NODE_ENV === 'production' && '@docusaurus/plugin-debug', process.env.NODE_ENV === 'production' && '@docusaurus/plugin-debug',
@ -75,7 +75,7 @@ Most Docusaurus users configure this plugin through the preset options.
If you use a preset, configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic): If you use a preset, configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic):
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -96,7 +96,7 @@ module.exports = {
If you are using a standalone plugin, provide options directly to the plugin: If you are using a standalone plugin, provide options directly to the plugin:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// highlight-next-line // highlight-next-line
plugins: ['@docusaurus/plugin-debug'], plugins: ['@docusaurus/plugin-debug'],
}; };

View file

@ -73,7 +73,7 @@ Accepted fields:
Here's an example configuration: Here's an example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-ideal-image', '@docusaurus/plugin-ideal-image',

View file

@ -20,7 +20,7 @@ Create a [PWA manifest](https://web.dev/add-manifest/) at `./static/manifest.jso
Modify `docusaurus.config.js` with a minimal PWA config, like: Modify `docusaurus.config.js` with a minimal PWA config, like:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-pwa', '@docusaurus/plugin-pwa',
@ -148,7 +148,7 @@ The [`standalone` strategy](https://petelepage.com/blog/2019/07/is-my-pwa-instal
- Default: `{}` - Default: `{}`
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-pwa', '@docusaurus/plugin-pwa',
@ -179,7 +179,7 @@ module.exports = {
Array of objects containing `tagName` and key-value pairs for attributes to inject into the `<head>` tag. Technically you can inject any head tag through this, but it's ideally used for tags to make your site PWA compliant. Here's a list of tag to make your app fully compliant: Array of objects containing `tagName` and key-value pairs for attributes to inject into the `<head>` tag. Technically you can inject any head tag through this, but it's ideally used for tags to make your site PWA compliant. Here's a list of tag to make your app fully compliant:
```js ```js
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-pwa', '@docusaurus/plugin-pwa',
@ -288,7 +288,7 @@ The Docusaurus site manifest can serve as an inspiration:
import CodeBlock from '@theme/CodeBlock'; import CodeBlock from '@theme/CodeBlock';
<CodeBlock className="language-json"> <CodeBlock className="language-json">
{JSON.stringify(require("@site/static/manifest.json"),null,2)} {JSON.stringify(require('@site/static/manifest.json'),null,2)}
</CodeBlock> </CodeBlock>
``` ```

View file

@ -58,6 +58,6 @@ Most Docusaurus users configure this plugin through the preset options.
// Plugin Options: @docusaurus/theme-classic // Plugin Options: @docusaurus/theme-classic
const config = { const config = {
customCss: require.resolve('./src/css/custom.css'), customCss: './src/css/custom.css',
}; };
``` ```

View file

@ -38,7 +38,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-start // highlight-start
colorMode: { colorMode: {
@ -80,7 +80,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-next-line // highlight-next-line
image: 'img/docusaurus.png', image: 'img/docusaurus.png',
@ -109,7 +109,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-next-line // highlight-next-line
metadata: [{name: 'twitter:card', content: 'summary'}], metadata: [{name: 'twitter:card', content: 'summary'}],
@ -142,7 +142,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-start // highlight-start
announcementBar: { announcementBar: {
@ -209,7 +209,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
title: 'Site Title', title: 'Site Title',
@ -236,7 +236,7 @@ module.exports = {
You can add items to the navbar via `themeConfig.navbar.items`. You can add items to the navbar via `themeConfig.navbar.items`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
// highlight-start // highlight-start
@ -311,7 +311,7 @@ In addition to the fields above, you can specify other arbitrary attributes that
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -368,7 +368,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -422,7 +422,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -471,7 +471,7 @@ Use this navbar item type if your sidebar is updated often and the order is not
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -490,7 +490,7 @@ module.exports = {
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorial: [ tutorial: [
{ {
type: 'autogenerated', type: 'autogenerated',
@ -537,7 +537,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -580,7 +580,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -625,7 +625,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -668,7 +668,7 @@ However, with this special navbar item type, you can change the default location
``` ```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -704,7 +704,7 @@ You can also render your own HTML markup inside a navbar item using this navbar
``` ```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [
@ -726,7 +726,7 @@ module.exports = {
You can enable this cool UI feature that automatically hides the navbar when a user starts scrolling down the page, and show it again when the user scrolls up. You can enable this cool UI feature that automatically hides the navbar when a user starts scrolling down the page, and show it again when the user scrolls up.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
// highlight-next-line // highlight-next-line
@ -743,7 +743,7 @@ You can set the static Navbar style without disabling the theme switching abilit
Currently, there are two possible style options: `dark` and `primary` (based on the `--ifm-color-primary` color). You can see the styles preview in the [Infima documentation](https://infima.dev/docs/components/navbar/). Currently, there are two possible style options: `dark` and `primary` (based on the `--ifm-color-primary` color). You can see the styles preview in the [Infima documentation](https://infima.dev/docs/components/navbar/).
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
// highlight-next-line // highlight-next-line
@ -799,14 +799,14 @@ By default, we use [Palenight](https://github.com/FormidableLabs/prism-react-ren
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
const {themes} = require('prism-react-renderer'); import {themes as prismThemes} from 'prism-react-renderer';
module.exports = { export default {
themeConfig: { themeConfig: {
prism: { prism: {
// highlight-start // highlight-start
theme: themes.github, theme: prismThemes.github,
darkTheme: themes.dracula, darkTheme: prismThemes.dracula,
// highlight-end // highlight-end
}, },
}, },
@ -826,7 +826,7 @@ You can set a default language for code blocks if no language is added after the
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
prism: { prism: {
// highlight-next-line // highlight-next-line
@ -860,7 +860,7 @@ Accepted fields:
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-start // highlight-start
footer: { footer: {
@ -917,7 +917,7 @@ Accepted fields of each `FooterItem`:
Example multi-column configuration: Example multi-column configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
footer: { footer: {
// highlight-start // highlight-start
links: [ links: [
@ -969,7 +969,7 @@ A simple footer just has a list of `FooterItem`s displayed in a row.
Example simple configuration: Example simple configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
footer: { footer: {
// highlight-start // highlight-start
links: [ links: [
@ -1018,7 +1018,7 @@ You can adjust the default table of contents via `themeConfig.tableOfContents`.
Example configuration: Example configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-start // highlight-start
tableOfContents: { tableOfContents: {

View file

@ -14,7 +14,7 @@ npm install --save @docusaurus/theme-live-codeblock
### Configuration {#configuration} ### Configuration {#configuration}
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: ['@docusaurus/theme-live-codeblock'], plugins: ['@docusaurus/theme-live-codeblock'],
themeConfig: { themeConfig: {
liveCodeBlock: { liveCodeBlock: {

View file

@ -14,7 +14,7 @@ npm install --save @docusaurus/theme-mermaid
## Configuration {#configuration} ## Configuration {#configuration}
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themes: ['@docusaurus/theme-mermaid'], themes: ['@docusaurus/theme-mermaid'],
// In order for Mermaid code blocks in Markdown to work, // In order for Mermaid code blocks in Markdown to work,
// you also need to enable the Remark plugin with this option // you also need to enable the Remark plugin with this option

View file

@ -22,7 +22,7 @@ To set up your site's blog, start by creating a `blog` directory.
Then, add an item link to your blog within `docusaurus.config.js`: Then, add an item link to your blog within `docusaurus.config.js`:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// ... // ...
navbar: { navbar: {
@ -108,7 +108,7 @@ But anything from here on down will not be.
By default, 10 posts are shown on each blog list page, but you can control pagination with the `postsPerPage` option in the plugin configuration. If you set `postsPerPage: 'ALL'`, pagination will be disabled and all posts will be displayed on the first page. You can also add a meta description to the blog list page for better SEO: By default, 10 posts are shown on each blog list page, but you can control pagination with the `postsPerPage` option in the plugin configuration. If you set `postsPerPage: 'ALL'`, pagination will be disabled and all posts will be displayed on the first page. You can also add a meta description to the blog list page for better SEO:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [
@ -134,7 +134,7 @@ The blog sidebar displays recent blog posts. The default number of items shown i
You can also alter the sidebar heading text with the `blogSidebarTitle` option. For example, if you have set `blogSidebarCount: 'ALL'`, instead of the default "Recent posts", you may rather make it say "All posts": You can also alter the sidebar heading text with the `blogSidebarTitle` option. For example, if you have set `blogSidebarCount: 'ALL'`, instead of the default "Recent posts", you may rather make it say "All posts":
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -385,7 +385,7 @@ An author, either declared through front matter or through the authors map, need
Docusaurus generates a reading time estimation for each blog post based on word count. We provide an option to customize this. Docusaurus generates a reading time estimation for each blog post based on word count. We provide an option to customize this.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -419,7 +419,7 @@ Use the callback for all your customization needs:
**Disable reading time on one page:** **Disable reading time on one page:**
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -457,7 +457,7 @@ This page will no longer display the reading time stats!
**Pass options to the default reading time function:** **Pass options to the default reading time function:**
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -482,9 +482,9 @@ module.exports = {
**Use a custom implementation of reading time:** **Use a custom implementation of reading time:**
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
const myReadingTime = require('./myReadingTime'); import myReadingTime from './myReadingTime';
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -539,7 +539,7 @@ type BlogOptions = {
Example usage: Example usage:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [
@ -600,7 +600,7 @@ https://example.com/blog/feed.json
You can run your Docusaurus 2 site without a dedicated landing page and instead have your blog's post list page as the index page. Set the `routeBasePath` to be `'/'` to serve the blog through the root route `example.com/` instead of the subroute `example.com/blog/`. You can run your Docusaurus 2 site without a dedicated landing page and instead have your blog's post list page as the index page. Set the `routeBasePath` to be `'/'` to serve the blog through the root route `example.com/` instead of the subroute `example.com/blog/`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [
@ -640,7 +640,7 @@ Set the `routeBasePath` to the URL route that you want your second blog to be ac
As documented for [multi-instance plugins](./using-plugins.mdx#multi-instance-plugins-and-plugin-ids), you need to assign a unique ID to the plugins. As documented for [multi-instance plugins](./using-plugins.mdx#multi-instance-plugins-and-plugin-ids), you need to assign a unique ID to the plugins.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: [ plugins: [
[ [

View file

@ -25,15 +25,28 @@ The `docusaurus.config.js` file is run in Node.js and should export either:
:::info :::info
The `docusaurus.config.js` file only supports the [**CommonJS**](https://flaviocopes.com/commonjs/) module system: The `docusaurus.config.js` file supports:
- **Required:** use `module.exports = /* your config*/` to export your Docusaurus config - [**ES Modules**](https://flaviocopes.com/es-modules/)
- **Optional:** use `require("lib")` to import Node.js packages - [**CommonJS**](https://flaviocopes.com/commonjs/)
- **Optional:** use `await import("lib")` (dynamic import) in an async function to import ESM-Only Node.js packages - [**TypeScript**](./typescript-support.mdx#typing-config)
Constraints:
- **Required:** use `export default /* your config*/` (or `module.exports` to export your Docusaurus config
- **Optional:** use `import Lib from 'lib'` (or `require('lib')`) to import Node.js packages
::: :::
Node.js gives us the ability to declare our Docusaurus configuration in various **equivalent ways**, and all the following config examples lead to the exact same result: Docusaurus gives us the ability to declare its configuration in various **equivalent ways**, and all the following config examples lead to the exact same result:
```js title="docusaurus.config.js"
export default {
title: 'Docusaurus',
url: 'https://docusaurus.io',
// your site config ...
};
```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { module.exports = {
@ -43,6 +56,16 @@ module.exports = {
}; };
``` ```
```ts title="docusaurus.config.ts"
import type {Config} from '@docusaurus/types';
export default {
title: 'Docusaurus',
url: 'https://docusaurus.io',
// your site config ...
} satisfies Config;
```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
const config = { const config = {
title: 'Docusaurus', title: 'Docusaurus',
@ -50,27 +73,27 @@ const config = {
// your site config ... // your site config ...
}; };
module.exports = config; export default config;
``` ```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = function configCreator() { export default function configCreator() {
return { return {
title: 'Docusaurus', title: 'Docusaurus',
url: 'https://docusaurus.io', url: 'https://docusaurus.io',
// your site config ... // your site config ...
}; };
}; }
``` ```
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = async function createConfigAsync() { export default async function createConfigAsync() {
return { return {
title: 'Docusaurus', title: 'Docusaurus',
url: 'https://docusaurus.io', url: 'https://docusaurus.io',
// your site config ... // your site config ...
}; };
}; }
``` ```
:::tip Using ESM-only packages :::tip Using ESM-only packages
@ -78,7 +101,7 @@ module.exports = async function createConfigAsync() {
Using an async config creator can be useful to import ESM-only modules (notably most Remark plugins). It is possible to import such modules thanks to dynamic imports: Using an async config creator can be useful to import ESM-only modules (notably most Remark plugins). It is possible to import such modules thanks to dynamic imports:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = async function createConfigAsync() { export default async function createConfigAsync() {
// Use a dynamic import instead of require('esm-lib') // Use a dynamic import instead of require('esm-lib')
// highlight-next-line // highlight-next-line
const lib = await import('lib'); const lib = await import('lib');
@ -88,7 +111,7 @@ module.exports = async function createConfigAsync() {
url: 'https://docusaurus.io', url: 'https://docusaurus.io',
// rest of your site config... // rest of your site config...
}; };
}; }
``` ```
::: :::
@ -120,7 +143,7 @@ It is recommended to check the [deployment docs](deployment.mdx) for more inform
List the [themes](./using-plugins.mdx#using-themes), [plugins](./using-plugins.mdx), and [presets](./using-plugins.mdx#using-presets) for your site in the `themes`, `plugins`, and `presets` fields, respectively. These are typically npm packages: List the [themes](./using-plugins.mdx#using-themes), [plugins](./using-plugins.mdx), and [presets](./using-plugins.mdx#using-presets) for your site in the `themes`, `plugins`, and `presets` fields, respectively. These are typically npm packages:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: [ plugins: [
'@docusaurus/plugin-content-blog', '@docusaurus/plugin-content-blog',
@ -135,7 +158,7 @@ module.exports = {
Docusaurus supports [**module shorthands**](./using-plugins.mdx#module-shorthands), allowing you to simplify the above configuration as: Docusaurus supports [**module shorthands**](./using-plugins.mdx#module-shorthands), allowing you to simplify the above configuration as:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: ['content-blog', 'content-pages'], plugins: ['content-blog', 'content-pages'],
themes: ['classic'], themes: ['classic'],
@ -147,9 +170,9 @@ module.exports = {
They can also be loaded from local directories: They can also be loaded from local directories:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
const path = require('path'); import path from 'path';
module.exports = { export default {
// ... // ...
themes: [path.resolve(__dirname, '/path/to/docusaurus-local-theme')], themes: [path.resolve(__dirname, '/path/to/docusaurus-local-theme')],
}; };
@ -158,7 +181,7 @@ module.exports = {
To specify options for a plugin or theme, replace the name of the plugin or theme in the config file with an array containing the name and an options object: To specify options for a plugin or theme, replace the name of the plugin or theme in the config file with an array containing the name and an options object:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: [ plugins: [
[ [
@ -178,17 +201,17 @@ module.exports = {
To specify options for a plugin or theme that is bundled in a preset, pass the options through the `presets` field. In this example, `docs` refers to `@docusaurus/plugin-content-docs` and `theme` refers to `@docusaurus/theme-classic`. To specify options for a plugin or theme that is bundled in a preset, pass the options through the `presets` field. In this example, `docs` refers to `@docusaurus/plugin-content-docs` and `theme` refers to `@docusaurus/theme-classic`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
{ {
docs: { docs: {
sidebarPath: require.resolve('./sidebars.js'), sidebarPath: './sidebars.js',
}, },
theme: { theme: {
customCss: [require.resolve('./src/css/custom.css')], customCss: ['./src/css/custom.css'],
}, },
}, },
], ],
@ -211,7 +234,7 @@ Docusaurus guards `docusaurus.config.js` from unknown fields. To add custom fiel
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-start // highlight-start
customFields: { customFields: {
@ -255,8 +278,8 @@ If you just want to use those fields on the client side, you could create your o
For new Docusaurus projects, we automatically generated a `babel.config.js` in the project root. For new Docusaurus projects, we automatically generated a `babel.config.js` in the project root.
```js title="babel.config.js" ```js title="babel.config.js"
module.exports = { export default {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')], presets: ['@docusaurus/core/lib/babel/preset'],
}; };
``` ```

View file

@ -61,9 +61,9 @@ Putting potentially sensitive information in the environment is common practice.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
// If you are using dotenv (https://www.npmjs.com/package/dotenv) // If you are using dotenv (https://www.npmjs.com/package/dotenv)
require('dotenv').config(); import 'dotenv/config';
module.exports = { export default {
title: '...', title: '...',
url: process.env.URL, // You can use environment variables to control site specifics as well url: process.env.URL, // You can use environment variables to control site specifics as well
// highlight-start // highlight-start
@ -157,7 +157,7 @@ Because we can only provide this content on a best-effort basis only, we have st
To deploy your Docusaurus 2 sites to [Netlify](https://www.netlify.com/), first make sure the following options are properly configured: To deploy your Docusaurus 2 sites to [Netlify](https://www.netlify.com/), first make sure the following options are properly configured:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// highlight-start // highlight-start
url: 'https://docusaurus-2.netlify.app', // Url to your site with no trailing slash url: 'https://docusaurus-2.netlify.app', // Url to your site with no trailing slash
baseUrl: '/', // Base directory of your site relative to your repo baseUrl: '/', // Base directory of your site relative to your repo
@ -263,7 +263,7 @@ GitHub Pages adds a trailing slash to Docusaurus URLs by default. It is recommen
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
url: 'https://endiliey.github.io', // Your website URL url: 'https://endiliey.github.io', // Your website URL
baseUrl: '/', baseUrl: '/',

View file

@ -32,7 +32,7 @@ const SafeComponent = () => (
``` ```
```mdx-code-block ```mdx-code-block
import ErrorBoundaryTestButton from "@site/src/components/ErrorBoundaryTestButton" import ErrorBoundaryTestButton from '@site/src/components/ErrorBoundaryTestButton'
``` ```
:::tip :::tip

View file

@ -44,7 +44,7 @@ All docs will be served under the subroute `docs/`. But what if **your site only
Assume that you have the following in your configuration: Assume that you have the following in your configuration:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [
@ -66,7 +66,7 @@ module.exports = {
To enter docs-only mode, change it to like this: To enter docs-only mode, change it to like this:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [

View file

@ -64,7 +64,7 @@ In this case, you should use the same plugin twice in your site configuration.
When using the preset: When using the preset:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -75,7 +75,7 @@ module.exports = {
// highlight-end // highlight-end
path: 'product', path: 'product',
routeBasePath: 'product', routeBasePath: 'product',
sidebarPath: require.resolve('./sidebarsProduct.js'), sidebarPath: './sidebarsProduct.js',
// ... other options // ... other options
}, },
}, },
@ -90,7 +90,7 @@ module.exports = {
// highlight-end // highlight-end
path: 'community', path: 'community',
routeBasePath: 'community', routeBasePath: 'community',
sidebarPath: require.resolve('./sidebarsCommunity.js'), sidebarPath: './sidebarsCommunity.js',
// ... other options // ... other options
}, },
], ],
@ -101,7 +101,7 @@ module.exports = {
When not using the preset: When not using the preset:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-content-docs', '@docusaurus/plugin-content-docs',
@ -111,7 +111,7 @@ module.exports = {
// highlight-end // highlight-end
path: 'product', path: 'product',
routeBasePath: 'product', routeBasePath: 'product',
sidebarPath: require.resolve('./sidebarsProduct.js'), sidebarPath: './sidebarsProduct.js',
// ... other options // ... other options
}, },
], ],
@ -123,7 +123,7 @@ module.exports = {
// highlight-end // highlight-end
path: 'community', path: 'community',
routeBasePath: 'community', routeBasePath: 'community',
sidebarPath: require.resolve('./sidebarsCommunity.js'), sidebarPath: './sidebarsCommunity.js',
// ... other options // ... other options
}, },
], ],
@ -190,7 +190,7 @@ Each docs-related [theme navbar items](../../api/themes/theme-configuration.mdx#
For example, if you want to have one version dropdown for each mobile SDK (iOS and Android), you could do: For example, if you want to have one version dropdown for each mobile SDK (iOS and Android), you could do:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [

View file

@ -21,7 +21,7 @@ type SidebarItemAutogenerated = {
Docusaurus can generate a full sidebar from your docs folder: Docusaurus can generate a full sidebar from your docs folder:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
myAutogeneratedSidebar: [ myAutogeneratedSidebar: [
// highlight-start // highlight-start
{ {
@ -67,7 +67,7 @@ docs
And assume every doc's ID is just its file name. If you define an autogenerated sidebar like this: And assume every doc's ID is just its file name. If you define an autogenerated sidebar like this:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
'intro', 'intro',
{ {
@ -109,7 +109,7 @@ module.exports = {
It would be resolved as: It would be resolved as:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
'intro', 'intro',
{ {
@ -175,7 +175,7 @@ A category index document is a document following one of those filename conventi
This is equivalent to using a category with a [doc link](items.mdx#category-doc-link): This is equivalent to using a category with a [doc link](items.mdx#category-doc-link):
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: [ docs: [
// highlight-start // highlight-start
{ {
@ -215,7 +215,7 @@ some-doc
It is possible to opt out any of the category index conventions, or define even more conventions. You can inject your own `isCategoryIndex` matcher through the [`sidebarItemsGenerator`](#customize-the-sidebar-items-generator) callback. For example, you can also pick `intro` as another file name eligible for automatically becoming the category index. It is possible to opt out any of the category index conventions, or define even more conventions. You can inject your own `isCategoryIndex` matcher through the [`sidebarItemsGenerator`](#customize-the-sidebar-items-generator) callback. For example, you can also pick `intro` as another file name eligible for automatically becoming the category index.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-content-docs', '@docusaurus/plugin-content-docs',
@ -247,7 +247,7 @@ module.exports = {
Or choose to not have any category index convention. Or choose to not have any category index convention.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-content-docs', '@docusaurus/plugin-content-docs',
@ -426,7 +426,7 @@ Updating a number prefix can be annoying, as it can require **updating multiple
You can provide a custom `sidebarItemsGenerator` function in the docs plugin (or preset) config: You can provide a custom `sidebarItemsGenerator` function in the docs plugin (or preset) config:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-content-docs', '@docusaurus/plugin-content-docs',
@ -477,7 +477,7 @@ function reverseSidebarItems(items) {
} }
// highlight-end // highlight-end
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-content-docs', '@docusaurus/plugin-content-docs',

View file

@ -16,14 +16,14 @@ To use sidebars on your Docusaurus site:
2. Pass this object into the `@docusaurus/plugin-docs` plugin directly or via `@docusaurus/preset-classic`. 2. Pass this object into the `@docusaurus/plugin-docs` plugin directly or via `@docusaurus/preset-classic`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
{ {
docs: { docs: {
// highlight-next-line // highlight-next-line
sidebarPath: require.resolve('./sidebars.js'), sidebarPath: './sidebars.js',
}, },
}, },
], ],
@ -44,7 +44,7 @@ import DocCardList from '@theme/DocCardList';
If the `sidebarPath` is unspecified, Docusaurus [automatically generates a sidebar](autogenerated.mdx) for you, by using the filesystem structure of the `docs` folder: If the `sidebarPath` is unspecified, Docusaurus [automatically generates a sidebar](autogenerated.mdx) for you, by using the filesystem structure of the `docs` folder:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
{ {
type: 'autogenerated', type: 'autogenerated',
@ -71,7 +71,7 @@ type Sidebar =
For example: For example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
{ {
type: 'category', type: 'category',
@ -123,7 +123,7 @@ type SidebarsFile = {
By enabling the `themeConfig.docs.sidebar.hideable` option, you can make the entire sidebar hideable, allowing users to better focus on the content. This is especially useful when content is consumed on medium-sized screens (e.g. tablets). By enabling the `themeConfig.docs.sidebar.hideable` option, you can make the entire sidebar hideable, allowing users to better focus on the content. This is especially useful when content is consumed on medium-sized screens (e.g. tablets).
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-start // highlight-start
docs: { docs: {
@ -141,7 +141,7 @@ module.exports = {
The `themeConfig.docs.sidebar.autoCollapseCategories` option would collapse all sibling categories when expanding one category. This saves the user from having too many categories open and helps them focus on the selected section. The `themeConfig.docs.sidebar.autoCollapseCategories` option would collapse all sibling categories when expanding one category. This saves the user from having too many categories open and helps them focus on the selected section.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// highlight-start // highlight-start
docs: { docs: {
@ -178,7 +178,7 @@ By default, breadcrumbs are rendered at the top, using the "sidebar path" of the
This behavior can be disabled with plugin options: This behavior can be disabled with plugin options:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -201,7 +201,7 @@ A real-world example from the Docusaurus site:
import CodeBlock from '@theme/CodeBlock'; import CodeBlock from '@theme/CodeBlock';
<CodeBlock language="js" title="sidebars.js"> <CodeBlock language="js" title="sidebars.js">
{require('!!raw-loader!@site/sidebars.js') {require('!!raw-loader!@site/sidebars.ts')
.default .default
.split('\n') .split('\n')
// remove comments // remove comments

View file

@ -42,7 +42,7 @@ type SidebarItemDoc =
Example: Example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
// Normal syntax: // Normal syntax:
// highlight-start // highlight-start
@ -92,7 +92,7 @@ type SidebarItemLink = {
Example: Example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
myLinksSidebar: [ myLinksSidebar: [
// highlight-start // highlight-start
// External link // External link
@ -133,7 +133,7 @@ type SidebarItemHtml = {
Example: Example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
myHtmlSidebar: [ myHtmlSidebar: [
// highlight-start // highlight-start
{ {
@ -151,7 +151,7 @@ module.exports = {
The menu item is already wrapped in an `<li>` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it: The menu item is already wrapped in an `<li>` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
myHtmlSidebar: [ myHtmlSidebar: [
{ {
type: 'html', type: 'html',
@ -186,7 +186,7 @@ type SidebarItemCategory = {
Example: Example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: [ docs: [
{ {
type: 'category', type: 'category',
@ -211,7 +211,7 @@ module.exports = {
Use the [**shorthand syntax**](#category-shorthand) when you don't need customizations: Use the [**shorthand syntax**](#category-shorthand) when you don't need customizations:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: { docs: {
Guides: [ Guides: [
'creating-pages', 'creating-pages',
@ -242,7 +242,7 @@ Autogenerated categories can use the [`_category_.yml`](./autogenerated.mdx#cate
You can auto-generate an index page that displays all the direct children of this category. The `slug` allows you to customize the generated page's route, which defaults to `/category/[categoryName]`. You can auto-generate an index page that displays all the direct children of this category. The `slug` allows you to customize the generated page's route, which defaults to `/category/[categoryName]`.
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: [ docs: [
{ {
type: 'category', type: 'category',
@ -276,7 +276,7 @@ Use `generated-index` links as a quick way to get an introductory document.
A category can link to an existing document. A category can link to an existing document.
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: [ docs: [
{ {
type: 'category', type: 'category',
@ -317,7 +317,7 @@ import DocCardList from '@theme/DocCardList';
We support the option to expand/collapse categories. Categories are collapsible by default, but you can disable collapsing with `collapsible: false`. We support the option to expand/collapse categories. Categories are collapsible by default, but you can disable collapsing with `collapsible: false`.
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: [ docs: [
{ {
type: 'category', type: 'category',
@ -340,7 +340,7 @@ module.exports = {
To make all categories non-collapsible by default, set the `sidebarCollapsible` option in `plugin-content-docs` to `false`: To make all categories non-collapsible by default, set the `sidebarCollapsible` option in `plugin-content-docs` to `false`:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -366,7 +366,7 @@ The option in `sidebars.js` takes precedence over plugin configuration, so it is
Collapsible categories are collapsed by default. If you want them to be expanded on the first render, you can set `collapsed` to `false`: Collapsible categories are collapsed by default. If you want them to be expanded on the first render, you can set `collapsed` to `false`:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
docs: { docs: {
Guides: [ Guides: [
'creating-pages', 'creating-pages',
@ -385,7 +385,7 @@ module.exports = {
Similar to `collapsible`, you can also set the global configuration `options.sidebarCollapsed` to `false`. Individual `collapsed` options in `sidebars.js` will still take precedence over this configuration. Similar to `collapsible`, you can also set the global configuration `options.sidebarCollapsed` to `false`. Individual `collapsed` options in `sidebars.js` will still take precedence over this configuration.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -420,7 +420,7 @@ An item with type `doc` can be simply a string representing its ID:
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
sidebar: [ sidebar: [
// highlight-start // highlight-start
{ {
@ -438,7 +438,7 @@ module.exports = {
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
sidebar: [ sidebar: [
// highlight-start // highlight-start
'myDoc', 'myDoc',
@ -455,7 +455,7 @@ module.exports = {
So it's possible to simplify the example above to: So it's possible to simplify the example above to:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
{ {
type: 'category', type: 'category',
@ -494,7 +494,7 @@ A category item can be represented by an object whose key is its label, and the
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
sidebar: [ sidebar: [
// highlight-start // highlight-start
{ {
@ -513,7 +513,7 @@ module.exports = {
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
sidebar: [ sidebar: [
// highlight-start // highlight-start
{ {
@ -532,7 +532,7 @@ module.exports = {
This permits us to simplify that example to: This permits us to simplify that example to:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
// highlight-start // highlight-start
{ {
@ -554,7 +554,7 @@ module.exports = {
Each shorthand object after this transformation will contain exactly one entry. Now consider the further simplified example below: Each shorthand object after this transformation will contain exactly one entry. Now consider the further simplified example below:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
mySidebar: [ mySidebar: [
// highlight-start // highlight-start
{ {
@ -581,7 +581,7 @@ Wherever you have an array of items that is reduced to one category shorthand, y
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
sidebar: [ sidebar: [
{ {
'Getting started': ['doc1'], 'Getting started': ['doc1'],
@ -602,7 +602,7 @@ module.exports = {
``` ```
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
sidebar: { sidebar: {
'Getting started': ['doc1'], 'Getting started': ['doc1'],
Docusaurus: { Docusaurus: {

View file

@ -18,7 +18,7 @@ The Docusaurus site is a good example of using multiple sidebars:
Consider this example: Consider this example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorialSidebar: { tutorialSidebar: {
'Category A': ['doc1', 'doc2'], 'Category A': ['doc1', 'doc2'],
}, },
@ -33,7 +33,7 @@ When browsing `doc1` or `doc2`, the `tutorialSidebar` will be displayed; when br
Following the example above, if a `commonDoc` is included in both sidebars: Following the example above, if a `commonDoc` is included in both sidebars:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorialSidebar: { tutorialSidebar: {
'Category A': ['doc1', 'doc2', 'commonDoc'], 'Category A': ['doc1', 'doc2', 'commonDoc'],
}, },
@ -51,7 +51,7 @@ When you add doc Y to sidebar X, it creates a two-way binding: sidebar X contain
Front matter option `displayed_sidebar` will forcibly set the sidebar association. For the same example, you can still use doc shorthands without any special configuration: Front matter option `displayed_sidebar` will forcibly set the sidebar association. For the same example, you can still use doc shorthands without any special configuration:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorialSidebar: { tutorialSidebar: {
'Category A': ['doc1', 'doc2'], 'Category A': ['doc1', 'doc2'],
}, },
@ -88,7 +88,7 @@ If a sidebar is displayed by setting `displayed_sidebar` front matter, and this
You can customize pagination with front matter `pagination_next` and `pagination_prev`. Consider this sidebar: You can customize pagination with front matter `pagination_next` and `pagination_prev`. Consider this sidebar:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorial: [ tutorial: [
'introduction', 'introduction',
{ {
@ -123,7 +123,7 @@ It is particularly useful where you wish to link to the same document from multi
Consider this example: Consider this example:
```js title="sidebars.js" ```js title="sidebars.js"
module.exports = { export default {
tutorialSidebar: { tutorialSidebar: {
'Category A': [ 'Category A': [
'doc1', 'doc1',
@ -135,7 +135,6 @@ module.exports = {
}, },
apiSidebar: ['doc3', 'doc4', 'commonDoc'], apiSidebar: ['doc3', 'doc4', 'commonDoc'],
}; };
}
``` ```
You can think of the `ref` type as the equivalent to doing the following: You can think of the `ref` type as the equivalent to doing the following:

View file

@ -187,7 +187,7 @@ Docusaurus defaults work great for the first use case. We will label the current
**For the 2nd use case**: if you release v1 and don't plan to work on v2 anytime soon, instead of versioning v1 and having to maintain the docs in 2 folders (`./docs` + `./versioned_docs/version-1.0.0`), you may consider "pretending" that the current version is a cut version by giving it a path and a label: **For the 2nd use case**: if you release v1 and don't plan to work on v2 anytime soon, instead of versioning v1 and having to maintain the docs in 2 folders (`./docs` + `./versioned_docs/version-1.0.0`), you may consider "pretending" that the current version is a cut version by giving it a path and a label:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
docs: { docs: {

View file

@ -275,7 +275,7 @@ export default function AdmonitionWrapper(props) {
Admonitions are implemented with a [Remark plugin](./markdown-features-plugins.mdx). The plugin is designed to be configurable. To customize the Remark plugin for a specific content plugin (docs, blog, pages), pass the options through the `admonitions` key. Admonitions are implemented with a [Remark plugin](./markdown-features-plugins.mdx). The plugin is designed to be configurable. To customize the Remark plugin for a specific content plugin (docs, blog, pages), pass the options through the `admonitions` key.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -306,7 +306,7 @@ By default, the theme doesn't know what do to with custom admonition keywords su
If you registered a new admonition type `my-custom-admonition` via the following config: If you registered a new admonition type `my-custom-admonition` via the following config:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [

View file

@ -64,19 +64,19 @@ By default, the Prism [syntax highlighting theme](https://github.com/FormidableL
For example, if you prefer to use the `dracula` highlighting theme: For example, if you prefer to use the `dracula` highlighting theme:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
const {themes} = require('prism-react-renderer'); import {themes as prismThemes} from 'prism-react-renderer';
module.exports = { export default {
themeConfig: { themeConfig: {
prism: { prism: {
// highlight-next-line // highlight-next-line
theme: themes.dracula, theme: prismThemes.dracula,
}, },
}, },
}; };
``` ```
Because a Prism theme is just a JS object, you can also write your own theme if you are not satisfied with the default. Docusaurus enhances the `github` and `vsDark` themes to provide richer highlight, and you can check our implementations for the [light](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismLight.mjs) and [dark](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismDark.mjs) code block themes. Because a Prism theme is just a JS object, you can also write your own theme if you are not satisfied with the default. Docusaurus enhances the `github` and `vsDark` themes to provide richer highlight, and you can check our implementations for the [light](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismLight.ts) and [dark](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismDark.ts) code block themes.
### Supported Languages {#supported-languages} ### Supported Languages {#supported-languages}
@ -99,7 +99,7 @@ Each additional language has to be a valid Prism component name. For example, Pr
For example, if you want to add highlighting for the PowerShell language: For example, if you want to add highlighting for the PowerShell language:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
themeConfig: { themeConfig: {
prism: { prism: {
@ -301,7 +301,7 @@ You can declare custom magic comments through theme config. For example, you can
``` ```
```js ```js
module.exports = { export default {
themeConfig: { themeConfig: {
prism: { prism: {
magicComments: [ magicComments: [
@ -445,7 +445,7 @@ npm install --save @docusaurus/theme-live-codeblock
You will also need to add the plugin to your `docusaurus.config.js`. You will also need to add the plugin to your `docusaurus.config.js`.
```js {3} ```js {3}
module.exports = { export default {
// ... // ...
themes: ['@docusaurus/theme-live-codeblock'], themes: ['@docusaurus/theme-live-codeblock'],
// ... // ...
@ -758,7 +758,7 @@ npm install @docusaurus/remark-plugin-npm2yarn
Docusaurus provides such a utility out of the box, freeing you from using the `Tabs` component every time. To enable this feature, first install the `@docusaurus/remark-plugin-npm2yarn` package as above, and then in `docusaurus.config.js`, for the plugins where you need this feature (doc, blog, pages, etc.), register it in the `remarkPlugins` option. (See [Docs configuration](../../api/plugins/plugin-content-docs.mdx#ex-config) for more details on configuration format) Docusaurus provides such a utility out of the box, freeing you from using the `Tabs` component every time. To enable this feature, first install the `@docusaurus/remark-plugin-npm2yarn` package as above, and then in `docusaurus.config.js`, for the plugins where you need this feature (doc, blog, pages, etc.), register it in the `remarkPlugins` option. (See [Docs configuration](../../api/plugins/plugin-content-docs.mdx#ex-config) for more details on configuration format)
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [

View file

@ -18,7 +18,7 @@ npm install --save @docusaurus/theme-mermaid
Enable Mermaid functionality by adding plugin `@docusaurus/theme-mermaid` and setting `markdown.mermaid` to `true` in your `docusaurus.config.js`. Enable Mermaid functionality by adding plugin `@docusaurus/theme-mermaid` and setting `markdown.mermaid` to `true` in your `docusaurus.config.js`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
markdown: { markdown: {
mermaid: true, mermaid: true,
}, },
@ -55,7 +55,7 @@ See the [Mermaid syntax documentation](https://mermaid-js.github.io/mermaid/#/./
The diagram dark and light themes can be changed by setting `mermaid.theme` values in the `themeConfig` in your `docusaurus.config.js`. You can set themes for both light and dark mode. The diagram dark and light themes can be changed by setting `mermaid.theme` values in the `themeConfig` in your `docusaurus.config.js`. You can set themes for both light and dark mode.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
mermaid: { mermaid: {
theme: {light: 'neutral', dark: 'forest'}, theme: {light: 'neutral', dark: 'forest'},
@ -71,7 +71,7 @@ See the [Mermaid theme documentation](https://mermaid-js.github.io/mermaid/#/the
Options in `mermaid.options` will be passed directly to `mermaid.initialize`: Options in `mermaid.options` will be passed directly to `mermaid.initialize`:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
mermaid: { mermaid: {
options: { options: {

View file

@ -63,25 +63,63 @@ Make sure to use `remark-math >= 5` and `rehype-katex >= 6` for Docusaurus v3 (u
::: :::
Those 2 plugins are now only available as ESM packages, and you will need to import them dynamically. Those 2 plugins are now [**only available as ES Modules**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). To simplify usage, it is recommended to use an [**ES Modules**](https://flaviocopes.com/es-modules/) config file:
First, turn your site config into an async config creator function: ```js title="ES module docusaurus.config.js"
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
```js title="docusaurus.config.js" // highlight-start
module.exports = async function createConfigAsync() { export default {
return { presets: [
// your site config... [
}; '@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
// highlight-end
},
},
],
],
}; };
``` ```
It is now possible to import the plugins dynamically and add them to your content plugin or preset options (usually `@docusaurus/preset-classic` docs options): <details>
<summary>Using a [**CommonJS**](https://nodejs.org/api/modules.html#modules-commonjs-modules) config file?</summary>
```js If you decide to use a CommonJS config file, it is possible to load those ES module plugins thanks to dynamic imports and an async config creator function:
```js title="CommonJS module docusaurus.config.js"
// highlight-start
module.exports = async function createConfigAsync() {
// highlight-end
return {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [(await import('remark-math')).default], remarkPlugins: [(await import('remark-math')).default],
rehypePlugins: [(await import('rehype-katex')).default], rehypePlugins: [(await import('rehype-katex')).default],
// highlight-end
},
},
],
],
};
};
``` ```
</details>
Include the KaTeX CSS in your config under `stylesheets`: Include the KaTeX CSS in your config under `stylesheets`:
```js ```js
@ -99,8 +137,12 @@ stylesheets: [
Overall the changes look like: Overall the changes look like:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = async function createConfigAsync() { // highlight-start
return { import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
export default {
title: 'Docusaurus', title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content', tagline: 'Build optimized websites quickly, focus on your content',
presets: [ presets: [
@ -110,8 +152,8 @@ module.exports = async function createConfigAsync() {
docs: { docs: {
path: 'docs', path: 'docs',
// highlight-start // highlight-start
remarkPlugins: [(await import('remark-math')).default], remarkPlugins: [remarkMath],
rehypePlugins: [(await import('rehype-katex')).default], rehypePlugins: [rehypeKatex],
// highlight-end // highlight-end
}, },
}, },
@ -129,7 +171,6 @@ module.exports = async function createConfigAsync() {
], ],
// highlight-end // highlight-end
}; };
};
``` ```
## Self-hosting KaTeX assets {#self-hosting-katex-assets} ## Self-hosting KaTeX assets {#self-hosting-katex-assets}
@ -137,7 +178,7 @@ module.exports = async function createConfigAsync() {
Loading stylesheets, fonts, and JavaScript libraries from CDN sources is a good practice for popular libraries and assets, since it reduces the amount of assets you have to host. In case you prefer to self-host the `katex.min.css` (along with required KaTeX fonts), you can download the latest version from [KaTeX GitHub releases](https://github.com/KaTeX/KaTeX/releases), extract and copy `katex.min.css` and `fonts` directory (only `.woff2` font types should be enough) to your site's `static` directory, and in `docusaurus.config.js`, replace the stylesheet's `href` from the CDN URL to your local path (say, `/katex/katex.min.css`). Loading stylesheets, fonts, and JavaScript libraries from CDN sources is a good practice for popular libraries and assets, since it reduces the amount of assets you have to host. In case you prefer to self-host the `katex.min.css` (along with required KaTeX fonts), you can download the latest version from [KaTeX GitHub releases](https://github.com/KaTeX/KaTeX/releases), extract and copy `katex.min.css` and `fonts` directory (only `.woff2` font types should be enough) to your site's `static` directory, and in `docusaurus.config.js`, replace the stylesheet's `href` from the CDN URL to your local path (say, `/katex/katex.min.css`).
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
stylesheets: [ stylesheets: [
{ {
href: '/katex/katex.min.css', href: '/katex/katex.min.css',

View file

@ -59,11 +59,41 @@ Next, the `rehype-katex` operates on the Hypertext AST where everything has been
:::warning :::warning
Many official Remark/Rehype plugins are using ES Modules, a new JavaScript module system, which Docusaurus doesn't support yet. To work around this issue, we recommend to use dynamic `import()` inside an `async` config creation function. Many official Remark/Rehype plugins are [**ES Modules only**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c), a JavaScript module system, which Docusaurus supports. We recommend using an [**ES Modules**](https://flaviocopes.com/es-modules/) config file to make it easier to import such packages.
::: :::
Next, add them to the plugin options through plugin or preset config in `docusaurus.config.js`, using dynamic `import()`: Next, import your plugins and add them to the plugin options through plugin or preset config in `docusaurus.config.js`:
```js title="docusaurus.config.js"
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
// highlight-start
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
// highlight-end
},
},
],
],
};
```
<details>
<summary>Using a [**CommonJS**](https://nodejs.org/api/modules.html#modules-commonjs-modules) config file?</summary>
If you decide to use a CommonJS config file, it is possible to load those ES module plugins thanks to dynamic imports and an async config creator function:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
// highlight-start // highlight-start
@ -88,13 +118,16 @@ module.exports = async function createConfigAsync() {
}; };
``` ```
</details>
## Configuring plugins {#configuring-plugins} ## Configuring plugins {#configuring-plugins}
Some plugins can be configured and accept their own options. In that case, use the `[plugin, pluginOptions]` syntax, like this: Some plugins can be configured and accept their own options. In that case, use the `[plugin, pluginOptions]` syntax, like this:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = async function createConfigAsync() { import rehypeKatex from 'rehype-katex';
return {
export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -102,7 +135,7 @@ module.exports = async function createConfigAsync() {
docs: { docs: {
rehypePlugins: [ rehypePlugins: [
// highlight-start // highlight-start
[(await import('rehype-katex')).default, {strict: false}], [rehypeKatex, {strict: false}],
// highlight-end // highlight-end
], ],
}, },
@ -110,7 +143,6 @@ module.exports = async function createConfigAsync() {
], ],
], ],
}; };
};
``` ```
You should check your plugin's documentation for the options it supports. You should check your plugin's documentation for the options it supports.
@ -128,7 +160,7 @@ The writeup below is **not** meant to be a comprehensive guide to creating a plu
For example, let's make a plugin that visits every `h2` heading and adds a `Section X. ` prefix. First, create your plugin source file anywhere—you can even publish it as a separate npm package and install it like explained above. We would put ours at `src/remark/section-prefix.js`. A remark/rehype plugin is just a function that receives the `options` and returns a `transformer` that operates on the AST. For example, let's make a plugin that visits every `h2` heading and adds a `Section X. ` prefix. First, create your plugin source file anywhere—you can even publish it as a separate npm package and install it like explained above. We would put ours at `src/remark/section-prefix.js`. A remark/rehype plugin is just a function that receives the `options` and returns a `transformer` that operates on the AST.
```js "src/remark/section-prefix.js" ```js "src/remark/section-prefix.js"
const visit = require('unist-util-visit'); import visit from 'unist-util-visit';
const plugin = (options) => { const plugin = (options) => {
const transformer = async (ast) => { const transformer = async (ast) => {
@ -146,16 +178,16 @@ const plugin = (options) => {
return transformer; return transformer;
}; };
module.exports = plugin; export default plugin;
``` ```
You can now import your plugin in `docusaurus.config.js` and use it just like an installed plugin! You can now import your plugin in `docusaurus.config.js` and use it just like an installed plugin!
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
// highlight-next-line // highlight-next-line
const sectionPrefix = require('./src/remark/section-prefix'); import sectionPrefix from './src/remark/section-prefix';
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -195,7 +227,7 @@ Our `transformImage` plugin uses this parameter, for example, to transform relat
The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with `require()` calls already. For example, in the example above, the table of contents generated is still the same even when all `h2` headings are now prefixed by `Section X.`, because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the `beforeDefaultRemarkPlugins` and `beforeDefaultRehypePlugins`. The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with `require()` calls already. For example, in the example above, the table of contents generated is still the same even when all `h2` headings are now prefixed by `Section X.`, because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the `beforeDefaultRemarkPlugins` and `beforeDefaultRehypePlugins`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',

View file

@ -76,7 +76,7 @@ toc_max_heading_level: 5
To set the heading level for _all_ pages, use the [`themeConfig.tableOfContents`](../../api/themes/theme-configuration.mdx#table-of-contents) option. To set the heading level for _all_ pages, use the [`themeConfig.tableOfContents`](../../api/themes/theme-configuration.mdx#table-of-contents) option.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
tableOfContents: { tableOfContents: {
// highlight-start // highlight-start

View file

@ -59,7 +59,7 @@ npx create-docusaurus@latest website classic
Add the site configuration for the French language: Add the site configuration for the French language:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en', 'fr'], locales: ['en', 'fr'],
@ -460,7 +460,7 @@ You may prefer the edit button to link to the Crowdin interface instead by using
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
const DefaultLocale = 'en'; const DefaultLocale = 'en';
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',

View file

@ -46,7 +46,7 @@ npx create-docusaurus@latest website classic
Add the site configuration for the French language: Add the site configuration for the French language:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en', 'fr'], locales: ['en', 'fr'],

View file

@ -26,7 +26,7 @@ Modify `docusaurus.config.js` to add the i18n support for the French language.
Use the [site i18n configuration](./../api/docusaurus.config.js.mdx#i18n) to declare the i18n locales: Use the [site i18n configuration](./../api/docusaurus.config.js.mdx#i18n) to declare the i18n locales:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en', 'fr', 'fa'], locales: ['en', 'fr', 'fa'],
@ -52,7 +52,7 @@ Docusaurus uses the locale names to provide **sensible defaults**: the `<html la
Add a **navbar item** of type `localeDropdown` so that users can select the locale they want: Add a **navbar item** of type `localeDropdown` so that users can select the locale they want:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
navbar: { navbar: {
items: [ items: [

View file

@ -69,7 +69,7 @@ npm install --save @docusaurus/theme-search-algolia
2. Register the theme in `docusaurus.config.js`: 2. Register the theme in `docusaurus.config.js`:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
title: 'My site', title: 'My site',
// ... // ...
themes: ['@docusaurus/theme-search-algolia'], themes: ['@docusaurus/theme-search-algolia'],
@ -84,7 +84,7 @@ module.exports = {
Then, add an `algolia` field in your `themeConfig`. **[Apply for DocSearch](https://docsearch.algolia.com/apply/)** to get your Algolia index and API key. Then, add an `algolia` field in your `themeConfig`. **[Apply for DocSearch](https://docsearch.algolia.com/apply/)** to get your Algolia index and API key.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
themeConfig: { themeConfig: {
// ... // ...
@ -146,7 +146,7 @@ Contextual search is **enabled by default**.
It ensures that search results are **relevant to the current language and version**. It ensures that search results are **relevant to the current language and version**.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
themeConfig: { themeConfig: {
// ... // ...
@ -177,7 +177,7 @@ When using `contextualSearch: true` (default), the contextual facet filters will
For specific needs, you can disable `contextualSearch` and define your own `facetFilters`: For specific needs, you can disable `contextualSearch` and define your own `facetFilters`:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
themeConfig: { themeConfig: {
// ... // ...
@ -252,7 +252,7 @@ Still, you can reuse the [Infima CSS variables](styling-layout.mdx#styling-your-
Algolia DocSearch supports a [list of options](https://autocomplete-experimental.netlify.app/docs/DocSearchModal#reference) that you can pass to the `algolia` field in the `docusaurus.config.js` file. Algolia DocSearch supports a [list of options](https://autocomplete-experimental.netlify.app/docs/DocSearchModal#reference) that you can pass to the `algolia` field in the `docusaurus.config.js` file.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// ... // ...
algolia: { algolia: {

View file

@ -17,7 +17,7 @@ Docusaurus supports search engine optimization in a variety of ways.
Provide global meta attributes for the entire site through the [site configuration](./configuration.mdx#site-metadata). The metadata will all be rendered in the HTML `<head>` using the key-value pairs as the prop name and value. The `metadata` attribute is a convenient shortcut to declare `<meta>` tags, but it is also possible to inject arbitrary tags in `<head>` with the `headTags` attribute. Provide global meta attributes for the entire site through the [site configuration](./configuration.mdx#site-metadata). The metadata will all be rendered in the HTML `<head>` using the key-value pairs as the prop name and value. The `metadata` attribute is a convenient shortcut to declare `<meta>` tags, but it is also possible to inject arbitrary tags in `<head>` with the `headTags` attribute.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themeConfig: { themeConfig: {
// Declare some <meta> tags // Declare some <meta> tags
metadata: [ metadata: [

View file

@ -16,7 +16,7 @@ This means that:
You can customize the static directory sources in `docusaurus.config.js`. For example, we can add `public` as another possible path: You can customize the static directory sources in `docusaurus.config.js`. For example, we can add `public` as another possible path:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
title: 'My site', title: 'My site',
staticDirectories: ['public', 'static'], staticDirectories: ['public', 'static'],
// ... // ...

View file

@ -24,7 +24,7 @@ This is the most traditional way of styling that most developers (including non-
If you're using `@docusaurus/preset-classic`, you can create your own CSS files (e.g. `/src/css/custom.css`) and import them globally by passing them as an option of the classic theme. If you're using `@docusaurus/preset-classic`, you can create your own CSS files (e.g. `/src/css/custom.css`) and import them globally by passing them as an option of the classic theme.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
presets: [ presets: [
[ [
@ -32,7 +32,7 @@ module.exports = {
{ {
// highlight-start // highlight-start
theme: { theme: {
customCss: [require.resolve('./src/css/custom.css')], customCss: ['./src/css/custom.css'],
}, },
// highlight-end // highlight-end
}, },
@ -234,7 +234,7 @@ npm install --save docusaurus-plugin-sass sass
2. Include the plugin in your `docusaurus.config.js` file: 2. Include the plugin in your `docusaurus.config.js` file:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-next-line // highlight-next-line
plugins: ['docusaurus-plugin-sass'], plugins: ['docusaurus-plugin-sass'],
@ -249,7 +249,7 @@ module.exports = {
You can now set the `customCss` property of `@docusaurus/preset-classic` to point to your Sass/SCSS file: You can now set the `customCss` property of `@docusaurus/preset-classic` to point to your Sass/SCSS file:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -257,7 +257,7 @@ module.exports = {
// ... // ...
theme: { theme: {
// highlight-next-line // highlight-next-line
customCss: [require.resolve('./src/css/custom.scss')], customCss: ['./src/css/custom.scss'],
}, },
// ... // ...
}, },

View file

@ -6,6 +6,8 @@ description: Docusaurus is written in TypeScript and provides first-class TypeSc
Docusaurus is written in TypeScript and provides first-class TypeScript support. Docusaurus is written in TypeScript and provides first-class TypeScript support.
The minimum required version is **TypeScript 5.0**.
## Initialization {#initialization} ## Initialization {#initialization}
Docusaurus supports writing and using TypeScript theme components. If the init template provides a TypeScript variant, you can directly initialize a site with full TypeScript support by using the `--typescript` flag. Docusaurus supports writing and using TypeScript theme components. If the init template provides a TypeScript variant, you can directly initialize a site with full TypeScript support by using the `--typescript` flag.
@ -18,10 +20,10 @@ Below are some guides on how to migrate an existing project to TypeScript.
## Setup {#setup} ## Setup {#setup}
To start using TypeScript, add `@docusaurus/module-type-aliases` and the base TS config to your project: Add the following packages to your project:
```bash npm2yarn ```bash npm2yarn
npm install --save-dev typescript @docusaurus/module-type-aliases @docusaurus/tsconfig npm install --save-dev typescript @docusaurus/module-type-aliases @docusaurus/tsconfig @docusaurus/types
``` ```
Then add `tsconfig.json` to your project root with the following content: Then add `tsconfig.json` to your project root with the following content:
@ -41,69 +43,84 @@ Now you can start writing TypeScript theme components.
## Typing the config file {#typing-config} ## Typing the config file {#typing-config}
It is **not possible** to use a TypeScript config file in Docusaurus unless you compile it yourself to JavaScript. It is possible to use a TypeScript config file in Docusaurus.
We recommend using [JSDoc type annotations](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html): ```ts title="docusaurus.config.ts"
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
// highlight-next-line
const config: Config = {
title: 'My Site',
favicon: 'img/favicon.ico',
/* Your site config here */
presets: [
[
'classic',
{
/* Your preset config here */
// highlight-next-line
} satisfies Preset.Options,
],
],
themeConfig: {
/* Your theme config here */
// highlight-next-line
} satisfies Preset.ThemeConfig,
};
export default config;
```
<details>
<summary>It is also possible to use [JSDoc type annotations](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) within a `.js` file:</summary>
By default, the Docusaurus TypeScript config does not type-check JavaScript files.
The `// @ts-check` comment ensures the config file is properly type-checked when running `npx tsc`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
// highlight-next-line // highlight-next-line
// @ts-check // @ts-check
// highlight-next-line
/** @type {import('@docusaurus/types').Plugin} */
function MyPlugin(context, options) {
return {
name: 'my-plugin',
};
}
// highlight-next-line // highlight-next-line
/** @type {import('@docusaurus/types').Config} */ /** @type {import('@docusaurus/types').Config} */
const config = { const config = {
title: 'Docusaurus', tagline: 'Dinosaurs are cool',
tagline: 'Build optimized websites quickly, focus on your content', favicon: 'img/favicon.ico',
organizationName: 'facebook',
projectName: 'docusaurus', /* Your site config here */
plugins: [MyPlugin],
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
// highlight-next-line // highlight-next-line
/** @type {import('@docusaurus/preset-classic').Options} */ /** @type {import('@docusaurus/preset-classic').Options} */
({ (
docs: { {
path: 'docs', /* Your preset config here */
sidebarPath: 'sidebars.js', }
}, ),
blog: {
path: 'blog',
postsPerPage: 5,
},
}),
], ],
], ],
themeConfig: themeConfig:
// highlight-next-line // highlight-next-line
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */ /** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({ (
colorMode: { {
defaultMode: 'dark', /* Your theme config here */
}, }
navbar: { ),
hideOnScroll: true,
title: 'Docusaurus',
logo: {
alt: 'Docusaurus Logo',
src: 'img/docusaurus.svg',
srcDark: 'img/docusaurus_keytar.svg',
},
},
}),
}; };
module.exports = config; export default config;
``` ```
</details>
:::tip :::tip
Type annotations are very useful and help your IDE understand the type of config objects! Type annotations are very useful and help your IDE understand the type of config objects!
@ -112,14 +129,6 @@ The best IDEs (VS Code, WebStorm, IntelliJ...) will provide a nice auto-completi
::: :::
:::info
By default, the Docusaurus TypeScript config does not type-check JavaScript files.
The `// @ts-check` comment ensures the config file is properly type-checked when running `npx tsc`.
:::
## Swizzling TypeScript theme components {#swizzling-typescript-theme-components} ## Swizzling TypeScript theme components {#swizzling-typescript-theme-components}
For themes that support TypeScript theme components, you can add the `--typescript` flag to the end of the `swizzle` command to get TypeScript source code. For example, the following command will generate `index.tsx` and `styles.module.css` into `src/theme/Footer`. For themes that support TypeScript theme components, you can add the `--typescript` flag to the end of the `swizzle` command to get TypeScript source code. For example, the following command will generate `index.tsx` and `styles.module.css` into `src/theme/Footer`.

View file

@ -19,7 +19,7 @@ npm install --save docusaurus-plugin-name
Then you add it in your site's `docusaurus.config.js`'s `plugins` option: Then you add it in your site's `docusaurus.config.js`'s `plugins` option:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-next-line // highlight-next-line
plugins: ['@docusaurus/plugin-content-pages'], plugins: ['@docusaurus/plugin-content-pages'],
@ -29,7 +29,7 @@ module.exports = {
Docusaurus can also load plugins from your local directory, with something like the following: Docusaurus can also load plugins from your local directory, with something like the following:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-next-line // highlight-next-line
plugins: ['./src/plugins/docusaurus-local-plugin'], plugins: ['./src/plugins/docusaurus-local-plugin'],
@ -45,7 +45,7 @@ For the most basic usage of plugins, you can provide just the plugin name or the
However, plugins can have options specified by wrapping the name and an options object in a two-member tuple inside your config. This style is usually called "Babel Style". However, plugins can have options specified by wrapping the name and an options object in a two-member tuple inside your config. This style is usually called "Babel Style".
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
plugins: [ plugins: [
// highlight-start // highlight-start
@ -63,7 +63,7 @@ module.exports = {
Example: Example:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
// Basic usage. // Basic usage.
'@docusaurus/plugin-debug', '@docusaurus/plugin-debug',
@ -84,7 +84,7 @@ module.exports = {
All Docusaurus content plugins can support multiple plugin instances. For example, it may be useful to have [multiple docs plugin instances](./guides/docs/docs-multi-instance.mdx) or [multiple blogs](./blog.mdx#multiple-blogs). It is required to assign a unique ID to each plugin instance, and by default, the plugin ID is `default`. All Docusaurus content plugins can support multiple plugin instances. For example, it may be useful to have [multiple docs plugin instances](./guides/docs/docs-multi-instance.mdx) or [multiple blogs](./blog.mdx#multiple-blogs). It is required to assign a unique ID to each plugin instance, and by default, the plugin ID is `default`.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
plugins: [ plugins: [
[ [
'@docusaurus/plugin-content-docs', '@docusaurus/plugin-content-docs',
@ -123,7 +123,7 @@ The `themes` and `plugins` options lead to different [shorthand resolutions](#mo
::: :::
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-next-line // highlight-next-line
themes: ['@docusaurus/theme-classic', '@docusaurus/theme-live-codeblock'], themes: ['@docusaurus/theme-classic', '@docusaurus/theme-live-codeblock'],
@ -152,7 +152,7 @@ The classic preset is shipped by default to new Docusaurus websites created with
The classic preset will relay each option entry to the respective plugin/theme. The classic preset will relay each option entry to the respective plugin/theme.
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -161,7 +161,7 @@ module.exports = {
debug: undefined, debug: undefined,
// Will be passed to @docusaurus/theme-classic. // Will be passed to @docusaurus/theme-classic.
theme: { theme: {
customCss: [require.resolve('./src/css/custom.css')], customCss: ['./src/css/custom.css'],
}, },
// Will be passed to @docusaurus/plugin-content-docs (false to disable) // Will be passed to @docusaurus/plugin-content-docs (false to disable)
docs: {}, docs: {},
@ -194,7 +194,7 @@ npm install --save @docusaurus/preset-classic
Then, add it in your site's `docusaurus.config.js`'s `presets` option: Then, add it in your site's `docusaurus.config.js`'s `presets` option:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-next-line // highlight-next-line
presets: ['@docusaurus/preset-classic'], presets: ['@docusaurus/preset-classic'],
@ -204,7 +204,7 @@ module.exports = {
Preset paths can be relative to the config file: Preset paths can be relative to the config file:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
// ... // ...
// highlight-next-line // highlight-next-line
presets: ['./src/presets/docusaurus-local-preset'], presets: ['./src/presets/docusaurus-local-preset'],
@ -216,7 +216,7 @@ module.exports = {
A preset is a function with the same shape as the [plugin constructor](./api/plugin-methods/README.mdx#plugin-constructor). It should return an object of `{ plugins: PluginConfig[], themes: PluginConfig[] }`, in the same as how they are accepted in the site config. For example, you can specify a preset that includes the following themes and plugins: A preset is a function with the same shape as the [plugin constructor](./api/plugin-methods/README.mdx#plugin-constructor). It should return an object of `{ plugins: PluginConfig[], themes: PluginConfig[] }`, in the same as how they are accepted in the site config. For example, you can specify a preset that includes the following themes and plugins:
```js title="src/presets/docusaurus-preset-multi-docs.js" ```js title="src/presets/docusaurus-preset-multi-docs.js"
module.exports = function preset(context, opts = {}) { export default function preset(context, opts = {}) {
return { return {
themes: [['docusaurus-theme-awesome', opts.theme]], themes: [['docusaurus-theme-awesome', opts.theme]],
plugins: [ plugins: [
@ -227,13 +227,13 @@ module.exports = function preset(context, opts = {}) {
['@docusaurus/plugin-content-docs', {...opts.docs3, id: 'docs3'}], ['@docusaurus/plugin-content-docs', {...opts.docs3, id: 'docs3'}],
], ],
}; };
}; }
``` ```
Then in your Docusaurus config, you may configure the preset: Then in your Docusaurus config, you may configure the preset:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
presets: [ presets: [
// highlight-start // highlight-start
[ [
@ -253,7 +253,7 @@ module.exports = {
This is equivalent of doing: This is equivalent of doing:
```js title="docusaurus.config.js" ```js title="docusaurus.config.js"
module.exports = { export default {
themes: [['docusaurus-theme-awesome', {hello: 'world'}]], themes: [['docusaurus-theme-awesome', {hello: 'world'}]],
plugins: [ plugins: [
['@docusaurus/plugin-content-docs', {id: 'docs1', path: '/docs'}], ['@docusaurus/plugin-content-docs', {id: 'docs1', path: '/docs'}],

Some files were not shown because too many files have changed in this diff Show more