docs: add advanced guides (#6296)

* docs: add advanced guides

* complete

* fix link

* Create architecture

* use png

* explanations

* more text

* process.env.NODE_ENV

* typo

* Add image zoom

* nit

* nit

* reorganize

* fix links

* fix links

* reorganize

* elaborate on presets

* fix

* routing docs

* ssr section

* more content

* complete SSR guide

* pathname://

* improvements

* document executionenvironment

* reformat

* final tweaks!

* avoid slug

* oops
This commit is contained in:
Joshua Chen 2022-01-25 11:29:18 +08:00 committed by GitHub
parent e3be77081c
commit 59022c5eea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1341 additions and 535 deletions

View file

@ -13,6 +13,7 @@ architecting
astro
atrule
autogen
autogenerating
backport
backticks
bartosz
@ -39,6 +40,7 @@ clsx
codesandbox
contravariance
corejs
crawlable
creativecommons
csvg
customizability
@ -47,6 +49,7 @@ datagit
datas
dedup
deduplicated
déja
devto
dmitry
docgen
@ -259,6 +262,7 @@ unprefixed
unswizzle
unversioned
upvotes
userland
vannicatte
vercel
vetter

View file

@ -60,6 +60,7 @@ const dogfoodingPluginInstances = [
/** @type {import('@docusaurus/types').Plugin} */
function clientModuleTestPlugin() {
return {
name: 'client-module-test-plugin',
getClientModules() {
return [
require.resolve('./clientModuleExample.ts'),

View file

@ -0,0 +1,28 @@
---
description: How Docusaurus works to build your app
---
# Architecture
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Zoom from '@site/src/components/Zoom';
```
<Zoom>
![Architecture overview](/img/architecture.png)
</Zoom>
This diagram shows how Docusaurus works to build your app. Plugins each collect their content and emit JSON data; themes provide layout components which receive the JSON data as route modules. The bundler bundles all the components and emits a server bundle and a client bundle.
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.
- 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).
During bundling, the config file itself is serialized and bundled, allowing the theme to access config options like `themeConfig` or `baseUrl` through [`useDocusaurusContext()`](../docusaurus-core.md#useDocusaurusContext). However, the `siteConfig` object only contains **serializable values** (values that are preserved after `JSON.stringify()`). Functions, regexes, etc. would be lost on the client side. The `themeConfig` is designed to be entirely serializable.

View file

@ -0,0 +1,12 @@
# Advanced Tutorials
This section is not going to be very structured, but we will cover the following topics:
```mdx-code-block
import DocCardList from '@theme/DocCardList';
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocCardList items={useCurrentSidebarCategory().items}/>
```
We will assume that you have finished the guides, and know the basics like how to configure plugins, how to write React components, etc. These sections will have plugin authors and code contributors in mind, so we may occasionally refer to [plugin APIs](../api/plugin-methods/README.md) or other architecture details. Don't panic if you don't understand everything😉

View file

@ -0,0 +1,129 @@
# Plugins
Plugins are the building blocks of features in a Docusaurus 2 site. Each plugin handles its own individual feature. Plugins may work and be distributed as part of a bundle via presets.
## Creating plugins {#creating-plugins}
A plugin is a function that takes two parameters: `context` and `options`. It returns a plugin instance object (or a promise). You can create plugins as functions or modules. For more information, refer to the [plugin method references section](./api/plugin-methods/README.md).
### Function definition {#function-definition}
You can use a plugin as a function directly included in the Docusaurus config file:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
// highlight-start
async function myPlugin(context, options) {
// ...
return {
name: 'my-plugin',
async loadContent() {
// ...
},
async contentLoaded({content, actions}) {
// ...
},
/* other lifecycle API */
};
},
// highlight-end
],
};
```
### Module definition {#module-definition}
You can use a plugin as a module path referencing a separate file or NPM package:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
// without options:
'./my-plugin',
// or with options:
['./my-plugin', options],
],
};
```
Then in the folder `my-plugin`, you can create an `index.js` such as this:
```js title="my-plugin.js"
module.exports = async function myPlugin(context, options) {
// ...
return {
name: 'my-plugin',
async loadContent() {
/* ... */
},
async contentLoaded({content, actions}) {
/* ... */
},
/* other lifecycle API */
};
};
```
---
You can view all plugins installed in your site using the [debug plugin's metadata panel](/__docusaurus/debug/metadata).
Plugins come as several types:
- `package`: an external package you installed
- `project`: a plugin you created in your project, given to Docusaurus as a local file path
- `local`: a plugin created using the function definition
- `synthetic`: a "fake plugin" Docusaurus created internally, so we take advantage of our modular architecture and don't let the core do much special work. You won't see this in the metadata because it's an implementation detail.
You can access them on the client side with `useDocusaurusContext().siteMetadata.pluginVersions`.
## Plugin design
Docusaurus' implementation of the plugins system provides us with a convenient way to hook into the website's lifecycle to modify what goes on during development/build, which involves (but is not limited to) extending the webpack config, modifying the data loaded, and creating new components to be used in a page.
### Theme design
When plugins have loaded their content, the data is made available to the client side through actions like [`createData` + `addRoute`](../api/plugin-methods/lifecycle-apis.md#addRoute) or [`setGlobalData`](../api/plugin-methods/lifecycle-apis.md#setGlobalData). This data has to be _serialized_ to plain strings, because [plugins and themes run in different environments](./architecture.md). Once the data arrives on the client side, the rest becomes familiar to React developers: data is passed along components, components are bundled with Webpack, and rendered to the window through `ReactDOM.render`...
**Themes provide the set of UI components to render the content.** Most content plugins need to be paired with a theme in order to be actually useful. The UI is a separate layer from the data schema, which makes swapping designs easy.
For example, a Docusaurus blog may consist of a blog plugin and a blog theme.
:::note
This is a contrived example: in practice, `@docusaurus/theme-classic` provides the theme for docs, blog, and layouts.
:::
```js title="docusaurus.config.js"
module.exports = {
// highlight-next-line
themes: ['theme-blog'],
plugins: ['plugin-content-blog'],
};
```
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"
module.exports = {
// highlight-next-line
themes: ['theme-blog-bootstrap'],
plugins: ['plugin-content-blog'],
};
```
Now, although the theme receives the same data from the plugin, how the theme chooses to _render_ the data as UI can be drastically different.
While themes share the exact same lifecycle methods with plugins, themes' implementations can look very different from those of plugins based on themes' designed objectives.
Themes are designed to complete the build of your Docusaurus site and supply the components used by your site, plugins, and the themes themselves. A theme still acts like a plugin and exposes some lifecycle methods, but most likely they would not use [`loadContent`](../api/plugin-methods/lifecycle-apis.md#loadContent), since they only receive data from plugins, but don't generate data themselves; themes are typically also accompanied by an `src/theme` directory full of components, which are made known to the core through the [`getThemePath`](../api/plugin-methods/extend-infrastructure.md#getThemePath) lifecycle.
To summarize:
- Themes share the same lifecycle methods with Plugins
- Themes are run after all existing Plugins
- Themes add component aliases by providing `getThemePath`.

View file

@ -0,0 +1,277 @@
---
description: "Docusaurus' routing system follows single-page application conventions: one route, one component."
---
# Routing
```mdx-code-block
import Link from '@docusaurus/Link';
import {useLatestVersion, useActiveDocContext} from '@docusaurus/plugin-content-docs/client';
import {useLocation} from '@docusaurus/router';
import BrowserWindow from '@site/src/components/BrowserWindow';
```
Docusaurus' routing system follows single-page application conventions: one route, one component. In this section, we will begin by talking about routing within the three content plugins (docs, blog, and pages), and then go beyond to talk about the underlying routing system.
## Routing in content plugins
Every content plugin provides a `routeBasePath` option. It defines where the plugins append their routes to. By default, the docs plugin puts its routes under `/docs`; the blog plugin, `/blog`; and the pages plugin, `/`. You can think about the route structure like this:
![plugin routes model](/img/routes.png#gh-light-mode-only)![plugin routes model](/img/routes-dark.png#gh-dark-mode-only)
Any route will be matched against this nested route config until a good match is found. For example, when given a route `/docs/configuration`, Docusaurus first enters the `/docs` branch, and then searches among the subroutes created by the docs plugin.
Changing `routeBasePath` can effectively alter your site's route structure. For example, in [Docs-only mode](../guides/docs/docs-introduction.md#docs-only-mode), we mentioned that configuring `routeBasePath: '/'` for docs means that all routes that the docs plugin create would not have the `/docs` prefix, yet it doesn't prevent you from having more subroutes like `/blog` created by other plugins.
Next, let's look at how the three plugins structure their own "boxes of subroutes".
### Pages routing
Pages routing are straightforward: the file paths directly map to URLs, without any other way to customize. See the [pages docs](../guides/creating-pages.md#routing) for more information.
The component used for Markdown pages is `@theme/MDXPage`. React pages are directly used as the route's component.
### Blog routing
The blog creates the following routes:
- **Posts list pages**: `/`, `/page/2`, `/page/3`...
- The component is `@theme/BlogListPage`.
- **Post pages**: `/2021/11/21/algolia-docsearch-migration`, `/2021/05/12/announcing-docusaurus-two-beta`...
- Generated from each Markdown post.
- The routes are fully customizable through the `slug` front matter.
- The component is `@theme/BlogPostPage`.
- **Tags list page**: `/tags`
- The route is customizable through the `tagsBasePath` option.
- The component is `@theme/BlogTagsListPage`.
- **Tag pages**: `/tags/adoption`, `/tags/beta`...
- Generated through the tags defined in each post's front matter.
- The routes always have base defined in `tagsBasePath`, but the subroutes are customizable through the tag's `permalink` field.
- The component is `@theme/BlogTagsPostsPage`.
- **Archive page**: `/archive`
- The route is customizable through the `archiveBasePath` option.
- The component is `@theme/BlogArchivePage`.
### Docs routing
The docs is the only plugin that creates **nested routes**. At the top, it registers [**version paths**](../guides/docs/versioning.md): `/`, `/next`, `/2.0.0-beta.13`... which provide the version context, including the layout and sidebar. This ensures that when switching between individual docs, the sidebar's state is preserved, and that you can switch between versions through the navbar dropdown while staying on the same doc. The component used is `@theme/DocPage`.
```mdx-code-block
export const URLPath = () => <code>{useLocation().pathname}</code>;
export const FilePath = () => {
const currentVersion = useActiveDocContext('default').activeVersion.name;
return <code>{currentVersion === 'current' ? './docs/' : `./versioned_docs/version-${currentVersion}/`}advanced/routing.md</code>;
}
```
The individual docs are rendered in the remaining space after the navbar, footer, sidebar, etc. have all been provided by the `DocPage` component. For example, this page, <URLPath />, is generated from the file at <FilePath />. The component used is `@theme/DocItem`.
The doc's `slug` front matter customizes the last part of the route, but the base route is always defined by the plugin's `routeBasePath` and the version's `path`.
### File paths and URL paths
Throughout the documentation, we always try to be unambiguous about whether we are talking about file paths or URL paths. Content plugins usually map file paths directly to URL paths, for example, `./docs/advanced/routing.md` will become `/docs/advanced/routing`. However, with `slug`, you can make URLs totally decoupled from the file structure.
When writing links in Markdown, you could either mean a _file path_, or a _URL path_, which Docusaurus would use several heuristics to determine.
- If the path doesn't have an extension, it is a URL path. For example, a link `[page](../plugins)` on a page with URL `/docs/advanced/routing` will link to `/docs/plugins`. Docusaurus will only detect broken links when building your site (when it knows the full route structure), but will make no assumptions about the existence of a file. It is exactly equivalent to writing `<a href="../plugins">page</a>` in a JSX file.
- If the path has an `.md(x)` extension, Docusaurus would try to resolve that Markdown file to a URL, and replace the file path with a URL path.
- If the path has any other extension, Docusaurus would treat it as [an asset](../guides/markdown-features/markdown-features-assets.mdx) and bundle it.
The following directory structure may help you visualize this file -> URL mapping. Assume that there's no slug customization in any page.
<details>
<summary>A sample site structure</summary>
```bash
.
├── blog # blog plugin has routeBasePath: '/blog'
│ ├── 2019-05-28-first-blog-post.md # -> /blog/2019/05/28/first-blog-post
│ ├── 2019-05-29-long-blog-post.md # -> /blog/2019/05/29/long-blog-post
│ ├── 2021-08-01-mdx-blog-post.mdx # -> /blog/2021/08/01/mdx-blog-post
│ └── 2021-08-26-welcome
│ ├── docusaurus-plushie-banner.jpeg
│ └── index.md # -> /blog/2021/08/26/welcome
├── docs # docs plugin has routeBasePath: '/docs'; current version has base path '/'
│ ├── intro.md # -> /docs/intro
│ ├── tutorial-basics
│ │ ├── _category_.json
│ │ ├── congratulations.md # -> /docs/tutorial-basics/congratulations
│ │ └── markdown-features.mdx # -> /docs/tutorial-basics/congratulations
│ └── tutorial-extras
│ ├── _category_.json
│ ├── manage-docs-versions.md # -> /docs/tutorial-extras/manage-docs-versions
│ └── translate-your-site.md # -> /docs/tutorial-extras/translate-your-site
├── src
│ └── pages # pages plugin has routeBasePath: '/'
│ ├── index.module.css
│ ├── index.tsx # -> /
│ └── markdown-page.md # -> /markdown-page
└── versioned_docs
└── version-1.0.0 # version has base path '/1.0.0'
├── intro.md # -> /docs/1.0.0/intro
├── tutorial-basics
│ ├── _category_.json
│ ├── congratulations.md # -> /docs/1.0.0/tutorial-basics/congratulations
│ └── markdown-features.mdx # -> /docs/1.0.0/tutorial-basics/congratulations
└── tutorial-extras
├── _category_.json
├── manage-docs-versions.md # -> /docs/1.0.0/tutorial-extras/manage-docs-versions
└── translate-your-site.md # -> /docs/1.0.0/tutorial-extras/translate-your-site
```
</details>
So much about content plugins. Let's take one step back and talk about how routing works in a Docusaurus app in general.
## Routes become HTML files
Because Docusaurus is a server-side rendering framework, all routes generated will be server-side rendered into static HTML files. If you are familiar with the behavior of HTTP servers like [Apache2](https://httpd.apache.org/docs/trunk/getting-started.html), you will understand how this is done: when the browser sends a request to the route `/docs/advanced/routing`, the server interprets that as request for the HTML file `/docs/advanced/routing/index.html`, and returns that.
The `/docs/advanced/routing` route can correspond to either `/docs/advanced/routing/index.html` or `/docs/advanced/routing.html`. Some hosting providers differentiate between them using the presence of a trailing slash, and may or may not tolerate the other. Read more in the [trailing slash guide](https://github.com/slorber/trailing-slash-guide).
For example, the build output of the directory above is (ignoring other assets and JS bundle):
<details>
<summary>Output of the above workspace</summary>
```bash
build
├── 404.html # /404/
├── blog
│ ├── archive
│ │ └── index.html # /blog/archive/
│ ├── first-blog-post
│ │ └── index.html # /blog/first-blog-post/
│ ├── index.html # /blog/
│ ├── long-blog-post
│ │ └── index.html # /blog/long-blog-post/
│ ├── mdx-blog-post
│ │ └── index.html # /blog/mdx-blog-post/
│ ├── tags
│ │ ├── docusaurus
│ │ │ └── index.html # /blog/tags/docusaurus/
│ │ ├── hola
│ │ │ └── index.html # /blog/tags/hola/
│ │ └── index.html # /blog/tags/
│ └── welcome
│ └── index.html # /blog/welcome/
├── docs
│ ├── 1.0.0
│ │ ├── intro
│ │ │ └── index.html # /docs/1.0.0/intro/
│ │ ├── tutorial-basics
│ │ │ ├── congratulations
│ │ │ │ └── index.html # /docs/1.0.0/tutorial-basics/congratulations/
│ │ │ └── markdown-features
│ │ │ └── index.html # /docs/1.0.0/tutorial-basics/markdown-features/
│ │ └── tutorial-extras
│ │ ├── manage-docs-versions
│ │ │ └── index.html # /docs/1.0.0/tutorial-extras/manage-docs-versions/
│ │ └── translate-your-site
│ │ └── index.html # /docs/1.0.0/tutorial-extras/translate-your-site/
│ ├── intro
│ │ └── index.html # /docs/1.0.0/intro/
│ ├── tutorial-basics
│ │ ├── congratulations
│ │ │ └── index.html # /docs/tutorial-basics/congratulations/
│ │ └── markdown-features
│ │ └── index.html # /docs/tutorial-basics/markdown-features/
│ └── tutorial-extras
│ ├── manage-docs-versions
│ │ └── index.html # /docs/tutorial-extras/manage-docs-versions/
│ └── translate-your-site
│ └── index.html # /docs/tutorial-extras/translate-your-site/
├── index.html # /
└── markdown-page
└── index.html # /markdown-page/
```
</details>
If `trailingSlash` is set to `false`, the build would emit `intro.html` instead of `intro/index.html`.
All HTML files will reference its JS assets using absolute URLs, so in order for the correct assets to be located, you have to configure the `baseUrl` field. Note that `baseUrl` doesn't affect the emitted bundle's file structure: the base URL is one level above the Docusaurus routing system. You can see the aggregate of `url` and `baseUrl` as the actual location of your Docusaurus site.
For example, the emitted HTML would contain links like `<link rel="preload" href="/assets/js/runtime~main.7ed5108a.js" as="script">`. Because absolute URLs are resolved from the host, if the bundle placed under the path `https://example.com/base/`, the link will point to `https://example.com/assets/js/runtime~main.7ed5108a.js`, which is, well, non-existent. By specifying `/base/` as base URL, the link will correctly point to `/base/assets/js/runtime~main.7ed5108a.js`.
Localized sites have the locale as part of the base URL as well. For example, `https://docusaurus.io/zh-CN/docs/advanced/routing/` has base URL `/zh-CN/`.
## Generating and accessing routes
The `addRoute` lifecycle action is used to generate routes. It registers a piece of route config to the route tree, giving a route, a component, and props that the component needs. The props and the component are both provided as paths for the bundler to `require`, because as explained in the [architecture overview](architecture.md), server and client only communicate through temp files.
All routes are aggregated in `.docusaurus/routes.js`, which you can view with the debug plugin's [routes panel](/__docusaurus/debug/routes).
On the client side, we offer `@docusaurus/router` to access the page's route. `@docusaurus/router` is a re-export of the [`react-router-dom`](https://www.npmjs.com/package/react-router-dom/v/5.3.0) package. For example, you can use `useLocation` to get the current page's [location](https://developer.mozilla.org/en-US/docs/Web/API/Location), and `useHistory` to access the [history object](https://developer.mozilla.org/en-US/docs/Web/API/History). (They are not the same as the browser API, although similar in functionality. Refer to the React Router documentation for specific APIs.)
This API is **SSR safe**, as opposed to the browser-only `window.location`.
```jsx title="myComponent.js"
import React from 'react';
import {useLocation} from '@docusaurus/router';
export function PageRoute() {
// React router provides the current component's route, even in SSR
const location = useLocation();
return (
<span>
We are currently on <code>{location.pathname}</code>
</span>
);
}
```
```mdx-code-block
export function PageRoute() {
const location = useLocation();
return (
<span>
We are currently on <code>{location.pathname}</code>
</span>
);
}
<BrowserWindow>
<PageRoute />
</BrowserWindow>
```
## Escaping from SPA redirects
Docusaurus builds a [single-page application](https://developer.mozilla.org/en-US/docs/Glossary/SPA), where route transitions are done through the `history.push()` method of React router. This operation is done on the client side. However, the prerequisite for a route transition to happen this way is that the target URL is known to our router. Otherwise, the router catches this path and displays a 404 page instead.
If you put some HTML pages under the `static` folder, they will be copied to the build output and therefore become accessible as part of your website, yet it's not part of the Docusaurus route system. We provide a `pathname://` protocol that allows you to redirect to another part of your domain in a non-SPA fashion, as if this route is an external link. Try the following two links:
```md
- [/pure-html](/pure-html)
- [pathname:///pure-html](pathname:///pure-html)
```
<BrowserWindow>
- <Link data-noBrokenLinkCheck="true" to="/pure-html">/pure-html</Link>
- [pathname:///pure-html](pathname:///pure-html)
</BrowserWindow>
:::tip
The first link will trigger a "broken links detected" check during the production build.
:::
The `pathname://` protocol is useful for referencing any content in the static folder. For example, Docusaurus would convert [all Markdown static assets to require() calls](../guides/markdown-features/markdown-features-assets.mdx#static-assets). You can use `pathname://` to keep it a regular link instead of being hashed by Webpack.
```md title="my-doc.md"
![An image from the static](pathname:///img/docusaurus.png)
[An asset from the static](pathname:///files/asset.pdf)
```
Docusaurus will only strip the `pathname://` prefix without processing the content.

View file

@ -0,0 +1,206 @@
---
sidebar_label: Static site generation
description: Docusaurus statically renders your React code into HTML, allowing faster load speed and better SEO.
---
# Static site generation (SSG)
In [architecture](architecture.md), we mentioned that the theme is run in Webpack. But beware: that doesn't mean it always has access to browser globals! The theme is built twice:
- During **server-side rendering**, the theme is compiled in a sandbox called [React DOM Server](https://reactjs.org/docs/react-dom-server.html). You can see this as a "headless browser", where there is no `window` or `document`, only React. SSR produces static HTML pages.
- During **client-side rendering**, the theme is compiled with standard React DOM, and has access to browser variables. CSR produces dynamic JavaScript.
:::info SSR or SSG?
_Server-side rendering_ and _static site generation_ can be different concepts, but we use them interchangeably.
:::
Therefore, while you probably know not to access Node globals like `process` ([or can we?](#node-env)) or the `'fs'` module, you can't freely access browser globals either.
```jsx
import React from 'react';
export default function WhereAmI() {
return <span>{window.location.href}</span>;
}
```
This looks like idiomatic React, but if you run `docusaurus build`, you will get an error:
```
ReferenceError: window is not defined
```
This is because during server-side rendering, the Docusaurus app isn't actually run in browser, and it doesn't know what `window` is.
<details id="node-env">
<summary>What about <code>process.env.NODE_ENV</code>?</summary>
One exception to the "no Node globals" rule is `process.env.NODE_ENV`. In fact, you can use it in React, because Webpack injects this variable as a global:
```jsx
import React from 'react';
export default function expensiveComp() {
if (process.env.NODE_ENV === 'development') {
return <>This component is not shown in development</>;
}
const res = someExpensiveOperationThatLastsALongTime();
return <>{res}</>;
}
```
During Webpack build, the `process.env.NODE_ENV` will be replaced with the value, either `'development'` or `'production'`. You will then get different build results after dead code elimination:
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
<Tabs>
<TabItem value="Development">
```diff
import React from 'react';
export default function expensiveComp() {
// highlight-next-line
if ('development' === 'development') {
+ return <>This component is not shown in development</>;
}
- const res = someExpensiveOperationThatLastsALongTime();
- return <>{res}</>;
}
```
</TabItem>
<TabItem value="Production">
```diff
import React from 'react';
export default function expensiveComp() {
// highlight-next-line
- if ('production' === 'development') {
- return <>This component is not shown in development</>;
- }
+ const res = someExpensiveOperationThatLastsALongTime();
+ return <>{res}</>;
}
```
</TabItem>
</Tabs>
</details>
## Understanding SSR
React is not just a dynamic UI runtime—it's also a templating engine. Because Docusaurus sites mostly contain static contents, it should be able to work without any JavaScript (which React runs in), but only plain HTML/CSS. And that's what server-side rendering offers: statically rendering your React code into HTML, without any dynamic content. An HTML file has no concept of client state (it's purely markup), hence it shouldn't rely on browser APIs.
These HTML files are the first to arrive at the user's browser screen when a URL is visited (see [routing](routing.md)). Afterwards, the browser fetches and runs other JS code to provide the "dynamic" parts of your site—anything implemented with JavaScript. However, before that, the main content of your page is already visible, allowing faster loading.
In CSR-only apps, all DOM elements are generated on client side with React, and the HTML file only ever contains one root element for React to mount DOM to; in SSR, React is already facing a fully built HTML page, and it only needs to correlate the DOM elements with the virtual DOM in its model. This step is called "hydration". After React has hydrated the static markup, the app starts to work as any normal React app.
## Escape hatches
If you want to render any dynamic content on your screen that relies on the browser API to be functional at all, for example:
- Our [live codeblock](../guides/markdown-features/markdown-features-code-blocks.mdx#interactive-code-editor), which runs in the browser's JS runtime
- Our [themed image](../guides/markdown-features/markdown-features-assets.mdx#themed-images) that detects the user's color scheme to display different images
- The JSON viewer of our debug panel which uses the `window` global for styling
You may need to escape from SSR since static HTML can't display anything useful without knowing the client state.
:::caution
It is important for the first client-side render to produce the exact same DOM structure as server-side rendering, otherwise, React will correlate virtual DOM with the wrong DOM elements.
Therefore, the naïve attempt of `if (typeof window !== 'undefined) {/* render something */}` won't work appropriately as a browser vs. server detection, because the first client render would instantly render different markup from the server-generated one.
You can read more about this pitfall in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/).
:::
We provide several more reliable ways to escape SSR.
### `<BrowserOnly>`
If you need to render some component in browser only (for example, because the component relies on browser specifics to be functional at all), one common approach is to wrap your component with [`<BrowserOnly>`](../docusaurus-core.md#browseronly) to make sure it's invisible during SSR and only rendered in CSR.
```jsx
import BrowserOnly from '@docusaurus/BrowserOnly';
function MyComponent(props) {
return (
// highlight-start
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
const LibComponent =
require('some-lib-that-accesses-window').LibComponent;
return <LibComponent {...props} />;
}}
</BrowserOnly>
// highlight-end
);
}
```
It's important to realize that the children of `<BrowserOnly>` is not a JSX element, but a function that _returns_ an element. This is a design decision. Consider this code:
```jsx
import BrowserOnly from '@docusaurus/BrowserOnly';
function MyComponent() {
return (
<BrowserOnly>
{/* highlight-start */}
{/* DON'T DO THIS - doesn't actually work */}
<span>page url = {window.location.href}</span>
{/* highlight-end */}
</BrowserOnly>
);
}
```
While you may expect that `BrowserOnly` hides away the children during server-side rendering, it actually can't. When the React renderer tries to render this JSX tree, it does see the `{window.location.href}` variable as a node of this tree and tries to render it, although it's actually not used! Using a function ensures that we only let the renderer see the browser-only component when it's needed.
### `useIsBrowser`
You can also use the `useIsBrowser()` hook to test if the component is currently in a browser environment. It returns `false` in SSR and `true` is CSR, after first client render. Use this hook if you only need to perform certain conditional operations on client-side, but not render an entirely different UI.
```jsx
import useIsBrowser from '@docusaurus/useIsBrowser';
function MyComponent() {
const isBrowser = useIsBrowser();
const location = isBrowser ? window.location.href : 'fetching location...';
return <span>{location}</span>;
}
```
### `useEffect`
Lastly, you can put your logic in `useEffect()` to delay its execution until after first CSR. This is most appropriate if you are only performing side-effects but don't _get_ data from the client state.
```jsx
function MyComponent() {
useEffect(() => {
// Only logged in the browser console; nothing is logged during server-side rendering
console.log("I'm now in the browser");
}, []);
return <span>Some content...</span>;
}
```
### `ExecutionEnvironment`
The [`ExecutionEnvironment`](../docusaurus-core.md#executionenvironment) namespace contains several values, and `canUseDOM` is an effective way to detect browser environment.
Beware that it essentially checked `typeof window !== 'undefined'` under the hood, so you should not use it for rendering-related logic, but only imperative code, like reacting to user input by sending web requests, or dynamically importing libraries, where DOM isn't updated at all.
```js title="a-client-module.js"
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
if (ExecutionEnvironment.canUseDOM) {
document.title = "I'm loaded!";
}
```

View file

@ -0,0 +1,195 @@
---
description: Customize your site's appearance through creating your own theme components
---
# Swizzling
In this section, we will introduce how customization of layout is done in Docusaurus.
> Déja vu...?
This section is similar to [Styling and Layout](../styling-layout.md), but this time, we are going to write more code and go deeper into the internals instead of playing with stylesheets. We will talk about a central concept in Docusaurus customization: **swizzling**, from how to swizzle, to how it works under the hood.
We know you are busy, so we will start with the "how" before going into the "why".
## Swizzling
```mdx-code-block
import SwizzleWarning from "../_partials/swizzleWarning.mdx"
<SwizzleWarning/>
```
Docusaurus Themes' components are designed to be replaceable. The replacing is called "swizzle". In Objective C, method swizzling is the process of changing the implementation of an existing selector (method). **In the context of a website, component swizzling means providing an alternative component that takes precedence over the component provided by the theme.** (To gain a deeper understanding of this, you have to understand [how theme components are resolved](#theme-aliases)). To help you get started, we created a command called `docusaurus swizzle`.
### Ejecting theme components
To eject a component provided by the theme, run the following command in your doc site:
```bash npm2yarn
npm run swizzle [theme name] [component name]
```
As an example, to swizzle the `<Footer />` component in `@docusaurus/theme-classic` for your site, run:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic Footer
```
This will copy the current `<Footer />` component used by Docusaurus to an `src/theme/Footer` directory under the root of your site, which is where Docusaurus will look for swizzled components. Docusaurus will then use the swizzled component in place of the original one from the theme.
:::note
You need to restart your webpack dev server in order for Docusaurus to know about the new component.
:::
If you run `swizzle` without `component name` or `theme name`, the command will give you a list to choose from. To only list available components, run with the `--list` option:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic --list
```
"Swizzle" is a central concept in Docusaurus, and is a natural product of our [layered theme architecture](#theme-aliases). Note that the command `docusaurus swizzle` is only an automated way to help you swizzle the component: you can still do it manually by creating the `src/theme/Footer.js` file, and Docusaurus will pick that one up when resolving theme components. There's no internal magic behind this command!
### Wrapping theme components {#wrapping-theme-components}
Ejecting a component is risky. It means you have to maintain an almost duplicate copy of the original theme component. Also, it's likely that we will change internal implementations in future versions and break your component, even if you never touched that part of the code.
Very often, you don't need to re-implement a component from scratch, but only to render additional items before or after it, or conditionally call some other logic. In this case, you are still going to swizzle the component—but not making a self-sustained one. Instead, you can delegate most of the logic and layout to the original theme component. The `@theme-original` alias allows you to import the original theme component and wrap it as a higher-order component.
Here is an example to display some text just above the footer, with minimal code duplication.
```js title="src/theme/Footer.js"
import OriginalFooter from '@theme-original/Footer';
import React from 'react';
export default function Footer(props) {
return (
<>
<div>Before footer</div>
<OriginalFooter {...props} />
</>
);
}
```
Should you be wondering why we have to use `'@theme-original/Footer'` instead of `'@theme/Footer'`, a short explanation is that once you have the swizzled component, the `'@theme/Footer'` alias will now point to your swizzled component, and thus cause a self-import. For a more in-depth explanation, see [theme aliases](#theme-aliases).
## Which component should I swizzle?
Currently, `theme-classic` has about 100 components[^source]! If you want to customize a part of your site's layout, which component should you choose?
[^source]: https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-classic/src/theme
You can follow the following steps to locate the component to swizzle:
1. **Search.** Our components are semantically named, so you should be able to infer its function from the name. The swizzle CLI allows you to enter part of a component name to narrow down the available choices. For example, if you run `yarn swizzle @docusaurus/theme-classic`, and enter `Doc`, only the docs-related components will be listed.
2. **Start with a higher-level component.** Components form a tree with some components importing others. Every route will be associated with one top-level component that the route will render (most of them listed in [Routing in content plugins](routing.md#routing-in-content-plugins)). For example, all blog post pages have `@theme/BlogPostPage` as the topmost component. You can start with swizzling this component, and then go down the component tree to locate the component that renders just what you are targeting. Don't forget to unswizzle the rest by deleting the files after you've found the correct one, so you don't maintain too many components.
3. **Read the source code and use search wisely.** Topmost components are registered by the plugin with `addRoute`, so you can search for `addRoute` and see which component the plugin references. Afterwards, read the code of all components that this component references.
4. **Ask.** If you still have no idea which component to swizzle to achieve the desired effect, you can reach out for help in one of our [support channels](/community/support).
### Wrapping your site with `<Root>` {#wrapper-your-site-with-root}
The `<Root>` component is one that you probably won't spot. Every component provided by `theme-classic` is ultimately only rendered on certain routes, and will be unmounted during route transition; however, the `<Root>` theme component is rendered at the very top of the Docusaurus SPA, above the router and the theme `<Layout>`, and will **never unmount**, allowing you to wrap your site with additional logic like global state. You can swizzle it by creating a file at `src/theme/Root.js`:
```js title="website/src/theme/Root.js"
import React from 'react';
// Default implementation, that you can customize
function Root({children}) {
return <>{children}</>;
}
export default Root;
```
:::tip
Use this component to render React Context providers and global stateful logic.
:::
## Do I need to swizzle?
Swizzling ultimately means you have to maintain part of the code directly used to build your site, and you have to interact with Docusaurus internal APIs. If you can, think about the following alternatives when customizing your site:
1. **Use CSS.** CSS rules and selectors can often help you achieve a decent degree of customization. Refer to [styling and layout](../styling-layout.md) for more details.
2. **Use translations.** It may sound surprising, but translations are ultimately just a way to customize the text labels. For example, if your site's default language is `en`, you can still run `yarn write-translations -l en` and edit the `code.json` emitted. Refer to [i18n tutorial](../i18n/i18n-tutorial.md) for more details.
3. **The smaller, the better.** If swizzling is inevitable, prefer to swizzle only the relevant part and maintain as little code on your own as possible. Swizzling a small component often means less risk of breaking during upgrade. [Wrapping](#wrapping-theme-components) is also a far safer alternative to [ejecting](#ejecting-theme-components).
## Theme aliases
A theme works by exporting a set of components, e.g. `Navbar`, `Layout`, `Footer`, to render the data passed down from plugins. Docusaurus and users use these components by importing them using the `@theme` webpack alias:
```js
import Navbar from '@theme/Navbar';
```
The alias `@theme` can refer to a few directories, in the following priority:
1. A user's `website/src/theme` directory, which is a special directory that has the higher precedence.
2. A Docusaurus theme package's `theme` directory.
3. Fallback components provided by Docusaurus core (usually not needed).
This is called a _layered architecture_: a higher-priority layer providing the component would shadow a lower-priority layer, making swizzling possible. Given the following structure:
```
website
├── node_modules
│ └── @docusaurus/theme-classic
│ └── theme
│ └── Navbar.js
└── src
└── theme
└── Navbar.js
```
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target `@theme/Navbar` is pointing to!
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](#wrapping-theme-components) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
```js
import InitialCodeBlock from '@theme-init/CodeBlock';
import React from 'react';
export default function CodeBlock(props) {
return props.live ? (
<ReactLivePlayground {...props} />
) : (
<InitialCodeBlock {...props} />
);
}
```
Check the code of `@docusaurus/theme-live-codeblock` for details.
:::caution
Unless you want to publish a re-usable "theme enhancer" (like `@docusaurus/theme-live-codeblock`), you likely don't need `@theme-init`.
:::
It can be quite hard to wrap your mind around these aliases. Let's imagine the following case with a super convoluted setup with three themes/plugins and the site itself all trying to define the same component. Internally, Docusaurus loads these themes as a "stack".
```text
+-------------------------------------------------+
| `website/src/theme/CodeBlock.js` | <-- `@theme/CodeBlock` always points to the top
+-------------------------------------------------+
| `theme-live-codeblock/theme/CodeBlock/index.js` | <-- `@theme-original/CodeBlock` points to the topmost non-swizzled component
+-------------------------------------------------+
| `plugin-awesome-codeblock/theme/CodeBlock.js` |
+-------------------------------------------------+
| `theme-classic/theme/CodeBlock/index.js` | <-- `@theme-init/CodeBlock` always points to the bottom
+-------------------------------------------------+
```
The components in this "stack" are pushed in the order of `preset plugins > preset themes > plugins > themes > site`, so the swizzled component in `website/src/theme` always comes out on top because it's loaded last.
`@theme/*` always points to the topmost component—when `CodeBlock` is swizzled, all other components requesting `@theme/CodeBlock` receive the swizzled version.
`@theme-original/*` always points to the topmost non-swizzled component. That's why you can import `@theme-original/CodeBlock` in the swizzled component—it points to the next one in the "component stack", a theme-provided one. Plugin authors should not try to use this because your component could be the topmost component and cause a self-import.
`@theme-init/*` always points to the bottommost component—usually, this comes from the theme or plugin that first provides this component. Individual plugins / themes trying to enhance code block can safely use `@theme-init/CodeBlock` to get its basic version. Site creators should generally not use this because you likely want to enhance the _topmost_ instead of the _bottommost_ component. It's also possible that the `@theme-init/CodeBlock` alias does not exist at all—Docusaurus only creates it when it points to a different one from `@theme-original/CodeBlock`, i.e. when it's provided by more than one theme. We don't waste aliases!

View file

@ -70,7 +70,7 @@ Most Docusaurus users configure this plugin through the preset options.
<Tabs>
<TabItem value="Preset Options">
If you use a preset, configure this plugin through the [preset options](presets.md#docusauruspreset-classic):
If you use a preset, configure this plugin through the [preset options](../../using-plugins.md#docusauruspreset-classic):
```js title="docusaurus.config.js"
module.exports = {

View file

@ -35,7 +35,7 @@ It is recommended to check the [deployment docs](deployment.mdx) for more inform
### Theme, plugin, and preset configurations {#theme-plugin-and-preset-configurations}
List the [theme](using-themes.md), [plugins](using-plugins.md), and [presets](presets.md) for your site in the `themes`, `plugins`, and `presets` fields, respectively. These are typically npm packages:
List the [themes](./using-plugins.md#using-themes), [plugins](./using-plugins.md), and [presets](./using-plugins.md#using-presets) for your site in the `themes`, `plugins`, and `presets` fields, respectively. These are typically npm packages:
```js title="docusaurus.config.js"
module.exports = {
@ -50,7 +50,7 @@ module.exports = {
:::tip
Docusaurus supports **module shorthands**, allowing you to simplify the above configuration as:
Docusaurus supports [**module shorthands**](./using-plugins.md#module-shorthands), allowing you to simplify the above configuration as:
```js title="docusaurus.config.js"
module.exports = {
@ -60,51 +60,6 @@ module.exports = {
};
```
<details>
<summary>How are shorthands resolved?</summary>
When it sees a plugin / theme / preset name, it tries to load one of the following, in that order:
- `[name]`
- `@docusaurus/[moduleType]-[name]`
- `docusaurus-[moduleType]-[name]`,
where `moduleType` is one of `'preset'`, `'theme'`, `'plugin'`, depending on which field the module name is declared in. The first module name that's successfully found is loaded.
If the name is scoped (beginning with `@`), the name is first split into scope and package name by the first slash:
```
@scope
^----^
scope (no name!)
@scope/awesome
^----^ ^-----^
scope name
@scope/awesome/main
^----^ ^----------^
scope name
```
If the name is not specified, `{scope}/docusaurus-{type}` is loaded. Otherwise, the following are attempted:
- `{scope}/{name}`
- `{scope}/docusaurus-{type}-{name}`
Below are some examples, for a plugin registered in the `plugins` field. Note that unlike [ESLint](https://eslint.org/docs/user-guide/configuring/plugins#configuring-plugins) or [Babel](https://babeljs.io/docs/en/options#name-normalization) where a consistent naming convention for plugins is mandated, Docusaurus permits greater naming freedom, so the resolutions are not certain, but follows the priority defined above.
| Declaration | May be resolved as |
| --- | --- |
| `awesome` | `docusaurus-plugin-awesome` |
| `sitemap` | [`@docusaurus/plugin-sitemap`](./api/plugins/plugin-sitemap.md) |
| `@my-company` | `@my-company/docusaurus-plugin` (the only possible resolution!) |
| `@my-company/awesome` | `@my-company/docusaurus-plugin-awesome` |
| `@my-company/awesome/web` | `@my-company/docusaurus-plugin-awesome/web` |
</details>
:::
They can also be loaded from local directories:
@ -165,7 +120,7 @@ The `presets: [['classic', {...}]]` shorthand works as well.
:::
For further help configuring themes, plugins, and presets, see [Using Themes](using-themes.md), [Using Plugins](using-plugins.md), and [Using Presets](presets.md).
For further help configuring themes, plugins, and presets, see [Using Plugins](./using-plugins.md).
### Custom configurations {#custom-configurations}

View file

@ -228,29 +228,6 @@ const MyComponent = (props) => {
};
```
:::info Why do we use a "render function"?
It's important to realize that the children of `<BrowserOnly>` is not a JSX element, but a function that _returns_ an element. This is a design decision. Consider this code:
```jsx
import BrowserOnly from '@docusaurus/BrowserOnly';
const MyComponent = () => {
return (
<BrowserOnly>
{/* highlight-start */}
{/* DON'T DO THIS - doesn't actually work */}
<span>page url = {window.location.href}</span>
{/* highlight-end */}
</BrowserOnly>
);
};
```
While you may expect that `BrowserOnly` hides away the children during server-side rendering, it actually can't. When the React renderer tries to render this JSX tree, it does see the `{window.location.href}` variable as a node of this tree and tries to render it, although it's actually not used! Using a function ensures that we only let the renderer see the browser-only component when it's needed.
:::
### `<Interpolate/>` {#interpolate}
A simple interpolation component for text containing dynamic placeholders.

View file

@ -125,7 +125,10 @@ my-website
│ └── pages
│ ├── styles.module.css
│ ├── index.js
│ ├──_ignored.js
│ ├── _ignored.js
│ ├── _ignored-folder
│ │ ├── Component1.js
│ │ └── Component2.js
│ └── support
│ ├── index.js
│ └── styles.module.css

View file

@ -0,0 +1,21 @@
# What's next?
Congratulations! You have understood most core features of Docusaurus now. You have:
- [Used the pages plugin](./creating-pages.md) to create a standalone React / Markdown page
- [Used the docs plugin](./docs/docs-introduction.md) to create documentation pages. This includes [configuring the sidebar](./docs/sidebar/index.md), and even [versioning](./docs/versioning.md)
- [Used the blog plugin](../blog.mdx) to create a fully featured blog
- Tried your hands on [a range of Markdown features](./markdown-features/markdown-features-intro.mdx), which are useful for all content plugins
- [Used stylesheets](../styling-layout.md) to customize your site's appearance
- [Put images and other assets](../static-assets.md) in your pages
- [Added search](../search.md) to your site
- Understood how [browser support](../browser-support.md) and [SEO](../seo.md) are done through standard Docusaurus APIs
- Learned about how [individual plugins](../using-plugins.md) are installed and configured
- [Deployed](../deployment.mdx) your site to a content host
- [Internationalized](../i18n/i18n-tutorial.md) your site to include multiple languages
At this point, you probably have a big `docusaurus.config.js` already😄 However, you haven't written much code yet! Most of the features are implemented through calling encapsulated Docusaurus APIs. As you continue your journey, you can take three paths:
- Learn more advanced Docusaurus concepts. This will help you gain a deeper understand of what the APIs do.
- Read about [all existing APIs](../docusaurus-core.md). Many of them have not been covered in the Guides!
- Learn to [develop a plugin](../api/plugin-methods/README.md) to extend the functionality of your site.

View file

@ -455,7 +455,7 @@ If you had `cleanUrl: false` in v1, it's possible that people published links to
For SEO reasons, and avoiding breaking links, you should configure server-side redirect rules on your hosting provider.
As an escape hatch, you could use [@docusaurus/plugin-client-redirects](./using-plugins.md#docusaurusplugin-client-redirects) to create client-side redirects from `/installation.html` to `/installation`.
As an escape hatch, you could use [@docusaurus/plugin-client-redirects](../api/plugins/plugin-client-redirects.md) to create client-side redirects from `/installation.html` to `/installation`.
```js
module.exports = {
@ -492,7 +492,7 @@ You'll have to migrate your sidebar if it contains category type. Rename `subcat
### Footer {#footer}
`website/core/Footer.js` is no longer needed. If you want to modify the default footer provided by Docusaurus, [swizzle](using-themes.md#swizzling-theme-components) it:
`website/core/Footer.js` is no longer needed. If you want to modify the default footer provided by Docusaurus, [swizzle](../advanced/swizzling.md#swizzling) it:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic Footer

View file

@ -1,143 +0,0 @@
---
id: presets
title: Presets
---
Presets are collections of plugins and themes.
## Using presets {#using-presets}
A preset is usually an npm package, so you install them like other npm packages using npm.
```bash npm2yarn
npm install --save @docusaurus/preset-classic
```
Then, add it in your site's `docusaurus.config.js`'s `presets` option:
```js title="docusaurus.config.js"
module.exports = {
// ...
// highlight-next-line
presets: ['@docusaurus/preset-classic'],
};
```
To load presets from your local directory, specify how to resolve them:
```js title="docusaurus.config.js"
const path = require('path');
module.exports = {
// ...
// highlight-next-line
presets: [path.resolve(__dirname, '/path/to/docusaurus-local-presets')],
};
```
## Presets -> themes and plugins {#presets---themes-and-plugins}
Presets are a shorthand function to add plugins and themes to your Docusaurus config. For example, you can specify a preset that includes the following themes and plugins,
```js
module.exports = function preset(context, opts = {}) {
return {
themes: ['@docusaurus/theme-cool', opts.cool],
plugins: ['@docusaurus/plugin-blog', opts.blog],
};
};
```
then in your Docusaurus config, you may configure the preset instead:
```js title="docusaurus.config.js"
module.exports = {
presets: [
// highlight-start
[
'@docusaurus/preset-my-own',
{cool: {hello: 'world'}, blog: {path: '/blog'}},
],
// highlight-end
],
};
```
This is equivalent of doing:
```js title="docusaurus.config.js"
module.exports = {
themes: ['@docusaurus/themes-cool', {hello: 'world'}],
plugins: ['@docusaurus/plugin-blog', {path: '/blog'}],
};
```
This is especially useful when some plugins and themes are intended to be used together.
## Official presets {#official-presets}
### `@docusaurus/preset-classic` {#docusauruspreset-classic}
The classic preset that is usually shipped by default to new Docusaurus website. It is a set of plugins and themes.
| Themes | Plugins |
| --- | --- |
| [`@docusaurus/theme-classic`](./api/themes/theme-configuration.md) | [`@docusaurus/plugin-content-docs`](./api/plugins/plugin-content-docs.md) |
| [`@docusaurus/theme-search-algolia`](./api/themes/theme-search-algolia.md) | [`@docusaurus/plugin-content-blog`](./api/plugins/plugin-content-blog.md) |
| | [`@docusaurus/plugin-content-pages`](./api/plugins/plugin-content-pages.md) |
| | [`@docusaurus/plugin-debug`](./api/plugins/plugin-debug.md) |
| | [`@docusaurus/plugin-google-analytics`](./api/plugins/plugin-google-analytics.md) |
| | [`@docusaurus/plugin-google-gtag`](./api/plugins/plugin-google-gtag.md) |
| | [`@docusaurus/plugin-sitemap`](./api/plugins/plugin-sitemap.md) |
To specify plugin options individually, you can provide the necessary fields to certain plugins, i.e. `customCss` for `@docusaurus/theme-classic`, pass them in the preset field, like this:
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
// Debug defaults to true in dev, false in prod
debug: undefined,
// Will be passed to @docusaurus/theme-classic.
theme: {
customCss: [require.resolve('./src/css/custom.css')],
},
// Will be passed to @docusaurus/plugin-content-docs (false to disable)
docs: {},
// Will be passed to @docusaurus/plugin-content-blog (false to disable)
blog: {},
// Will be passed to @docusaurus/plugin-content-pages (false to disable)
pages: {},
// Will be passed to @docusaurus/plugin-content-sitemap (false to disable)
sitemap: {},
// Will be passed to @docusaurus/plugin-google-gtag (only enabled when explicitly specified)
gtag: {},
// Will be passed to @docusaurus/plugin-google-analytics (only enabled when explicitly specified)
googleAnalytics: {},
},
],
],
};
```
In addition to these plugins and themes, `@docusaurus/theme-classic` adds [`remark-admonitions`](https://github.com/elviswolcott/remark-admonitions) as a remark plugin to `@docusaurus/plugin-content-blog` and `@docusaurus/plugin-content-docs`.
The `admonitions` key will be passed as the [options](https://github.com/elviswolcott/remark-admonitions#options) to `remark-admonitions`. Passing `false` will prevent the plugin from being added to MDX.
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// options for remark-admonitions
admonitions: {},
},
},
],
],
};
```

View file

@ -8,7 +8,7 @@ import ColorGenerator from '@site/src/components/ColorGenerator';
:::tip
This section is focused on styling through stylesheets. If you find yourself needing to update the DOM structure, you can refer to [swizzling](./using-themes.md#swizzling-theme-components).
This section is focused on styling through stylesheets. If you find yourself needing to update the DOM structure, you can refer to [swizzling](./advanced/swizzling.md#swizzling).
:::

View file

@ -1,13 +1,12 @@
---
id: using-plugins
title: Plugins
---
# Using Plugins
Plugins are the building blocks of features in a Docusaurus 2 site. Each plugin handles its own individual feature. Plugins may work and be distributed as part of a bundle via [presets](presets.md).
**The Docusaurus core doesn't provide any feature of its own.** All features are delegated to individual plugins: the [docs](./guides/docs/docs-introduction.md) feature provided by the [docs plugin](./api/plugins/plugin-content-docs.md); the [blog](./blog.mdx) feature provided by the [blog plugin](./api/plugins/plugin-content-blog.md); or individual [pages](./guides/creating-pages.md) provided by the [pages plugin](./api/plugins/plugin-content-pages.md). If there are no plugins installed, the site won't contain any routes.
## Available plugins {#available-plugins}
You may not need to install common plugins one-by-one though: they can be distributed as a bundle in a [preset](#using-presets). For most users, plugins are configured through the preset configuration.
We maintain a [list of official plugins](./api/plugins/overview.md), but the community has also created some [unofficial plugins](/community/resources#community-plugins).
We maintain a [list of official plugins](./api/plugins/overview.md), but the community has also created some [unofficial plugins](/community/resources#community-plugins). If you want to add any feature: autogenerating doc pages, executing custom scripts, integrating other services... be sure to check out the list: someone may have implemented it for you!
If you are feeling energetic, you can also read [the plugin guide](./advanced/plugins.md) or [plugin method references](./api/plugin-methods/README.md) for how to make a plugin yourself.
## Installing a plugin {#installing-a-plugin}
@ -27,7 +26,7 @@ module.exports = {
};
```
Docusaurus can also load plugins from your local directory, you can do something like the following:
Docusaurus can also load plugins from your local directory, with something like the following:
```js title="docusaurus.config.js"
const path = require('path');
@ -43,7 +42,7 @@ module.exports = {
For the most basic usage of plugins, you can provide just the plugin name or the absolute path to the plugin.
However, plugins can have options specified by wrapping the name and an options object in an array 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"
module.exports = {
@ -67,7 +66,7 @@ Example:
module.exports = {
plugins: [
// Basic usage.
'@docusaurus/plugin-google-analytics',
'@docusaurus/plugin-debug',
// With options object (babel style)
[
@ -82,24 +81,24 @@ module.exports = {
## Multi-instance plugins and plugin ids {#multi-instance-plugins-and-plugin-ids}
All Docusaurus content plugins can support multiple plugin instances. The Docs plugin has [additional multi-instance documentation](./guides/docs/docs-multi-instance.mdx). 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"
module.exports = {
plugins: [
[
'@docusaurus/plugin-xxx',
'@docusaurus/plugin-content-docs',
{
// highlight-next-line
id: 'plugin-xxx-1',
id: 'docs-1',
// other options
},
],
[
'@docusaurus/plugin-xxx',
'@docusaurus/plugin-content-docs',
{
// highlight-next-line
id: 'plugin-xxx-2',
id: 'docs-2',
// other options
},
],
@ -113,71 +112,209 @@ At most one plugin instance can be the "default plugin instance", by omitting th
:::
## Plugins design {#plugins-design}
## Using themes
Docusaurus' implementation of the plugins system provides us with a convenient way to hook into the website's lifecycle to modify what goes on during development/build, which involves (but is not limited to) extending the webpack config, modifying the data loaded, and creating new components to be used in a page.
Themes are loaded in the exact same way as plugins—the line between them is blurry. From the consumer perspective, the `themes` and `plugins` entries are interchangeable when installing and configuring a plugin. The only nuance is that themes are loaded after plugins, and it's possible for [a theme to override a plugin's default theme components](./advanced/swizzling.md#theme-aliases).
## Creating plugins {#creating-plugins}
:::tip
A plugin is a function that takes two parameters: `context` and `options`. It returns a plugin instance object (or a promise). You can create plugins as functions or modules. For more information, refer to the [plugin method references section](./api/plugin-methods/README.md).
The `themes` and `plugins` options lead to different [shorthand resolutions](#module-shorthands), so if you want to take advantage of shorthands, be sure to use the right entry!
### Functional definition {#functional-definition}
You can use a plugin as a function directly included in the Docusaurus config file:
:::
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
// highlight-start
async function myPlugin(context, options) {
// highlight-next-line
themes: ['@docusaurus/theme-classic', '@docusaurus/theme-live-codeblock'],
};
```
## Using presets {#using-presets}
A preset is usually an npm package, so you install them like other npm packages using npm.
```bash npm2yarn
npm install --save @docusaurus/preset-classic
```
Then, add it in your site's `docusaurus.config.js`'s `presets` option:
```js title="docusaurus.config.js"
module.exports = {
// ...
// highlight-next-line
presets: ['@docusaurus/preset-classic'],
};
```
To load presets from your local directory, specify how to resolve them:
```js title="docusaurus.config.js"
const path = require('path');
module.exports = {
// ...
// highlight-next-line
presets: [path.resolve(__dirname, '/path/to/docusaurus-local-preset')],
};
```
Presets are a shorthand function to add plugins and themes to your Docusaurus config. For example, you can specify a preset that includes the following themes and plugins:
```js title="/path/to/docusaurus-local-preset"
module.exports = function preset(context, opts = {}) {
return {
name: 'my-plugin',
async loadContent() {
// ...
},
async contentLoaded({content, actions}) {
// ...
},
/* other lifecycle API */
themes: [['docusaurus-theme-awesome', opts.theme]],
plugins: [
// Using three docs plugins at the same time!
// Assigning a unique ID for each without asking the user to do it
['@docusaurus/plugin-content-docs', {...opts.docs1, id: 'docs1'}],
['@docusaurus/plugin-content-docs', {...opts.docs2, id: 'docs2'}],
['@docusaurus/plugin-content-docs', {...opts.docs3, id: 'docs3'}],
],
};
};
```
then in your Docusaurus config, you may configure the preset instead:
```js title="docusaurus.config.js"
module.exports = {
presets: [
// highlight-start
[
path.resolve(__dirname, '/path/to/docusaurus-local-preset'),
{
theme: {hello: 'world'},
docs1: {path: '/docs'},
docs2: {path: '/community'},
docs3: {path: '/api'},
},
],
// highlight-end
],
};
```
### Module definition {#module-definition}
You can use a plugin as a module path referencing a separate file or NPM package:
This is equivalent of doing:
```js title="docusaurus.config.js"
module.exports = {
// ...
themes: [['docusaurus-theme-awesome', {hello: 'world'}]],
plugins: [
// without options:
'./my-plugin',
// or with options:
['./my-plugin', options],
['@docusaurus/plugin-content-docs', {id: 'docs1', path: '/docs'}],
['@docusaurus/plugin-content-docs', {id: 'docs2', path: '/community'}],
['@docusaurus/plugin-content-docs', {id: 'docs3', path: '/api'}],
],
};
```
Then in the folder `my-plugin`, you can create an `index.js` such as this:
This is especially useful when some plugins and themes are intended to be used together. You can even link their options together, e.g. pass one option to multiple plugins.
```js title="my-plugin.js"
module.exports = async function myPlugin(context, options) {
// ...
return {
name: 'my-plugin',
async loadContent() {
/* ... */
### `@docusaurus/preset-classic` {#docusauruspreset-classic}
The classic preset is shipped by default to new Docusaurus website created with [`create-docusaurus`](./installation.md#scaffold-project-website). It is a set of plugins and themes.
| Themes | Plugins |
| --- | --- |
| [`@docusaurus/theme-classic`](./api/themes/theme-configuration.md) | [`@docusaurus/plugin-content-docs`](./api/plugins/plugin-content-docs.md) |
| [`@docusaurus/theme-search-algolia`](./api/themes/theme-search-algolia.md) | [`@docusaurus/plugin-content-blog`](./api/plugins/plugin-content-blog.md) |
| | [`@docusaurus/plugin-content-pages`](./api/plugins/plugin-content-pages.md) |
| | [`@docusaurus/plugin-debug`](./api/plugins/plugin-debug.md) |
| | [`@docusaurus/plugin-google-analytics`](./api/plugins/plugin-google-analytics.md) |
| | [`@docusaurus/plugin-google-gtag`](./api/plugins/plugin-google-gtag.md) |
| | [`@docusaurus/plugin-sitemap`](./api/plugins/plugin-sitemap.md) |
To specify plugin options individually, you can provide the necessary fields to certain plugins, i.e. `customCss` for `@docusaurus/theme-classic`, pass them in the preset field, like this:
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
// Debug defaults to true in dev, false in prod
debug: undefined,
// Will be passed to @docusaurus/theme-classic.
theme: {
customCss: [require.resolve('./src/css/custom.css')],
},
async contentLoaded({content, actions}) {
/* ... */
// Will be passed to @docusaurus/plugin-content-docs (false to disable)
docs: {},
// Will be passed to @docusaurus/plugin-content-blog (false to disable)
blog: {},
// Will be passed to @docusaurus/plugin-content-pages (false to disable)
pages: {},
// Will be passed to @docusaurus/plugin-content-sitemap (false to disable)
sitemap: {},
// Will be passed to @docusaurus/plugin-google-gtag (only enabled when explicitly specified)
gtag: {},
// Will be passed to @docusaurus/plugin-google-analytics (only enabled when explicitly specified)
googleAnalytics: {},
},
/* other lifecycle API */
};
],
],
};
```
In addition to these plugins and themes, `@docusaurus/theme-classic` adds [`remark-admonitions`](https://github.com/elviswolcott/remark-admonitions) as a remark plugin to `@docusaurus/plugin-content-blog` and `@docusaurus/plugin-content-docs`.
The `admonitions` key will be passed as the [options](https://github.com/elviswolcott/remark-admonitions#options) to `remark-admonitions`. Passing `false` will prevent the plugin from being added to MDX.
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// options for remark-admonitions
admonitions: {},
},
},
],
],
};
```
## Module shorthands
Docusaurus supports shorthands for plugins, themes, and presets. When it sees a plugin / theme / preset name, it tries to load one of the following, in that order:
- `[name]`
- `@docusaurus/[moduleType]-[name]`
- `docusaurus-[moduleType]-[name]`,
where `moduleType` is one of `'preset'`, `'theme'`, `'plugin'`, depending on which field the module name is declared in. The first module name that's successfully found is loaded.
If the name is scoped (beginning with `@`), the name is first split into scope and package name by the first slash:
```
@scope
^----^
scope (no name!)
@scope/awesome
^----^ ^-----^
scope name
@scope/awesome/main
^----^ ^----------^
scope name
```
If the name is not specified, `{scope}/docusaurus-{type}` is loaded. Otherwise, the following are attempted:
- `[scope]/[name]`
- `[scope]/docusaurus-[moduleType]-[name]`
Below are some examples, for a plugin registered in the `plugins` field. Note that unlike [ESLint](https://eslint.org/docs/user-guide/configuring/plugins#configuring-plugins) or [Babel](https://babeljs.io/docs/en/options#name-normalization) where a consistent naming convention for plugins is mandated, Docusaurus permits greater naming freedom, so the resolutions are not certain, but follows the priority defined above.
| Declaration | May be resolved as |
| --- | --- |
| `awesome` | `docusaurus-plugin-awesome` |
| `sitemap` | [`@docusaurus/plugin-sitemap`](./api/plugins/plugin-sitemap.md) |
| `@my-company` | `@my-company/docusaurus-plugin` (the only possible resolution!) |
| `@my-company/awesome` | `@my-company/docusaurus-plugin-awesome` |
| `@my-company/awesome/web` | `@my-company/docusaurus-plugin-awesome/web` |

View file

@ -1,252 +0,0 @@
---
id: using-themes
title: Themes
---
Like plugins, themes are designed to add functionality to your Docusaurus site. As a good rule of thumb, themes are mostly focused on the client-side, whereas plugins are more focused on server-side functionalities. Themes are also designed to be replaceable with other themes.
**Themes are for providing UI components to present the content.** Most content plugins need to be paired with a theme in order to be actually useful. The UI is a separate layer from the data schema, which makes swapping designs easy.
For example, a Docusaurus blog consists of a blog plugin and a blog theme.
:::note
This is a contrived example: in practice, `@docusaurus/theme-classic` provides the theme for docs, blog, and layouts.
:::
```js title="docusaurus.config.js"
module.exports = {
themes: ['theme-blog'],
plugins: ['plugin-content-blog'],
};
```
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"
module.exports = {
themes: ['theme-blog-bootstrap'],
plugins: ['plugin-content-blog'],
};
```
## Available themes {#available-themes}
We maintain a [list of official themes](./api/themes/overview.md).
## Using themes {#using-themes}
To use themes, specify the themes in your `docusaurus.config.js`. You may use multiple themes:
```js title="docusaurus.config.js"
module.exports = {
// ...
// highlight-next-line
themes: ['@docusaurus/theme-classic', '@docusaurus/theme-live-codeblock'],
};
```
## Theme components {#theme-components}
Most of the time, theme is used to provide a set of React components, e.g. `Navbar`, `Layout`, `Footer`.
Users can use these components in their code by importing them using the `@theme` webpack alias:
```js
import Navbar from '@theme/Navbar';
```
The alias `@theme` can refer to a few directories, in the following priority:
1. A user's `website/src/theme` directory, which is a special directory that has the higher precedence.
2. A Docusaurus theme package's `theme` directory.
3. Fallback components provided by Docusaurus core (usually not needed).
## Swizzling theme components {#swizzling-theme-components}
```mdx-code-block
import SwizzleWarning from "./_partials/swizzleWarning.mdx"
<SwizzleWarning/>
```
Docusaurus Themes' components are designed to be replaceable. To make it easier for you, we created a command for you to replace theme components called `swizzle`. Given the following structure:
```
website
├── node_modules
│ └── docusaurus-theme
│ └── theme
│ └── Navbar.js
└── src
└── theme
└── Navbar.js
```
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. In iOS, method swizzling is the process of changing the implementation of an existing selector (method). In the context of a website, component swizzling means providing an alternative component that takes precedence over the component provided by the theme.
To swizzle a component for a theme, run the following command in your doc site:
```bash npm2yarn
npm run swizzle <theme name> [component name]
```
As an example, to swizzle the `<Footer />` component in `@docusaurus/theme-classic` for your site, run:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic Footer
```
This will copy the current `<Footer />` component used by the theme to an `src/theme/Footer` directory under the root of your site, which is where Docusaurus will look for swizzled components. Docusaurus will then use the swizzled component in place of the original one from the theme.
Although we highly discourage swizzling of all components, if you wish to do that, run:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic
```
**Note**: You need to restart your webpack dev server in order for Docusaurus to know about the new component.
## Wrapping theme components {#wrapping-theme-components}
Sometimes, you just want to wrap an existing theme component with additional logic, and it can be a pain to have to maintain an almost duplicate copy of the original theme component.
In this case, you should swizzle the component you want to wrap, but import the original theme component in your customized version to wrap it.
### For site owners {#for-site-owners}
The `@theme-original` alias allows you to import the original theme component.
Here is an example to display some text just above the footer, with minimal code duplication.
```js title="src/theme/Footer.js"
// Note: importing from "@theme/Footer" would fail due to the file importing itself
import OriginalFooter from '@theme-original/Footer';
import React from 'react';
export default function Footer(props) {
return (
<>
<div>Before footer</div>
<OriginalFooter {...props} />
</>
);
}
```
### For plugin authors {#for-plugin-authors}
One theme can wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
```js
import InitialCodeBlock from '@theme-init/CodeBlock';
import React from 'react';
export default function CodeBlock(props) {
return props.live ? (
<ReactLivePlayground {...props} />
) : (
<InitialCodeBlock {...props} />
);
}
```
Check the code of `@docusaurus/theme-live-codeblock` for details.
:::caution
Unless you want to publish a re-usable "theme enhancer" (like `@docusaurus/theme-live-codeblock`), you likely don't need `@theme-init`.
:::
<details>
<summary>How are theme aliases resolved?</summary>
It can be quite hard to wrap your mind around these aliases. Let's imagine the following case with a super convoluted setup with three themes/plugins and the site itself all trying to define the same component. Internally, Docusaurus loads these themes as a "stack".
```text
+-------------------------------------------------+
| `website/src/theme/CodeBlock.js` | <-- `@theme/CodeBlock` always points to the top
+-------------------------------------------------+
| `theme-live-codeblock/theme/CodeBlock/index.js` | <-- `@theme-original/CodeBlock` points to the topmost non-swizzled component
+-------------------------------------------------+
| `plugin-awesome-codeblock/theme/CodeBlock.js` |
+-------------------------------------------------+
| `theme-classic/theme/CodeBlock/index.js` | <-- `@theme-init/CodeBlock` always points to the bottom
+-------------------------------------------------+
```
The components in this "stack" are pushed in the order of `preset plugins > preset themes > plugins > themes > site`, so the swizzled component in `website/src/theme` always comes out on top because it's loaded last.
`@theme/*` always points to the topmost component—when `CodeBlock` is swizzled, all other components requesting `@theme/CodeBlock` receive the swizzled version.
`@theme-original/*` always points to the topmost non-swizzled component. That's why you can import `@theme-original/CodeBlock` in the swizzled component—it points to the next one in the "component stack", a theme-provided one. Plugin authors should not try to use this because your component could be the topmost component and cause a self-import.
`@theme-init/*` always points to the bottommost component—usually, this comes from the theme or plugin that first provides this component. Individual plugins / themes trying to enhance code block can safely use `@theme-init/CodeBlock` to get its basic version. Site creators should generally not use this because you likely want to enhance the _topmost_ instead of the _bottommost_ component. It's also possible that the `@theme-init/CodeBlock` alias does not exist at all—Docusaurus only creates it when it points to a different one from `@theme-original/CodeBlock`, i.e. when it's provided by more than one theme. We don't waste aliases!
</details>
## Wrapping your site with `<Root>` {#wrapper-your-site-with-root}
A `<Root>` theme component is rendered at the very top of your Docusaurus site. It allows you to wrap your site with additional logic, by creating a file at `src/theme/Root.js`:
```js title="website/src/theme/Root.js"
import React from 'react';
// Default implementation, that you can customize
function Root({children}) {
return <>{children}</>;
}
export default Root;
```
This component is applied above the router and the theme `<Layout>`, and will **never unmount**.
:::tip
Use this component to render React Context providers and global stateful logic.
:::
## Themes design {#themes-design}
While themes share the exact same lifecycle methods with plugins, their implementations can look very different from those of plugins based on themes' designed objectives.
Themes are designed to complete the build of your Docusaurus site and supply the components used by your site, plugins, and the themes themselves. So a typical theme implementation would look like a `src/index.js` file that hooks it up to the lifecycle methods. Most likely they would not use `loadContent`, which plugins would use. And it is typically accompanied by an `src/theme` directory full of components.
To summarize:
- Themes share the same lifecycle methods with Plugins
- Themes are run after all existing Plugins
- Themes exist to add component aliases by extending the webpack config
## Writing customized Docusaurus themes {#writing-customized-docusaurus-themes}
A Docusaurus theme normally includes an `index.js` file where you hook up to the lifecycle methods, alongside a `theme/` directory of components. A typical Docusaurus `theme` folder looks like this:
```bash
website
├── package.json
└── src
├── index.js
# highlight-start
└── theme
├── MyThemeComponent
└── AnotherThemeComponent.js
# highlight-end
```
There are two lifecycle methods that are essential to theme implementation:
- [`getThemePath()`](./api/plugin-methods/extend-infrastructure.md#getThemePath)
- [`getClientModules()`](./api/plugin-methods/lifecycle-apis.md#getClientModules)
These lifecycle methods are not essential but recommended:
- [`validateThemeConfig({themeConfig, validate})`](./api/plugin-methods/static-methods.md#validateThemeConfig)
- [`validateOptions({options, validate})`](./api/plugin-methods/static-methods.md#validateOptions)

View file

@ -50,6 +50,7 @@
"npm-to-yarn": "^1.0.0-2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-medium-image-zoom": "^4.3.5",
"react-popper": "^2.2.5",
"rehype-katex": "^6.0.2",
"remark-math": "^3.0.1",

View file

@ -91,6 +91,7 @@ const sidebars = {
'search',
'browser-support',
'seo',
'using-plugins',
'deployment',
{
type: 'category',
@ -114,12 +115,20 @@ const sidebars = {
},
],
},
'guides/whats-next',
],
},
{
type: 'category',
label: 'Advanced Guides',
items: ['using-plugins', 'using-themes', 'presets'],
link: {type: 'doc', id: 'advanced/index'},
items: [
'advanced/architecture',
'advanced/plugins',
'advanced/routing',
'advanced/swizzling',
'advanced/ssg',
],
},
{
type: 'category',

View file

@ -0,0 +1,27 @@
/**
* 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 React from 'react';
import {useColorMode} from '@docusaurus/theme-common';
import BasicZoom from 'react-medium-image-zoom';
import 'react-medium-image-zoom/dist/styles.css';
export default function Zoom({
children,
}: {
children: React.ReactNode;
}): JSX.Element {
const {isDarkTheme} = useColorMode();
return (
<BasicZoom
overlayBgColorEnd={
isDarkTheme ? 'rgba(0, 0, 0, 0.95)' : 'rgba(255, 255, 255, 0.95)'
}>
{children}
</BasicZoom>
);
}

View file

@ -72,7 +72,7 @@ export default function plugin() {
type: 'link',
title: null,
// TODO make this version-aware; maybe we need a useVersionedLink() hook
url: '/docs/presets#docusauruspreset-classic',
url: '/docs/using-plugins#docusauruspreset-classic',
children: [
{
type: 'text',

View file

@ -6,6 +6,8 @@
/docs/next/docusaurus.config.js /docs/next/api/docusaurus-config
/docs/lifecycle-apis /docs/api/plugin-methods/
/docs/next/lifecycle-apis /docs/next/api/plugin-methods/
/docs/using-themes /docs/advanced/swizzling
/docs/next/using-themes /docs/next/advanced/swizzling
# v2.docusaurus.io domain redirect after we put v2 on docusaurus.io

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Purely HTML page | Docusaurus</title>
<meta charset="utf-8" />
<meta name="robots" content="noindex, nofollow" />
</head>
<body>
<h1>Not part of the Docusaurus app</h1>
<div>
This page is purely HTML, placed in the <code>static</code> folder
</div>
</body>
</html>

View file

@ -0,0 +1,17 @@
# Routing
This page is only accessible through version-switching. It shows how a versioned doc file becomes a webpage.
```mdx-code-block
import {useLatestVersion, useActiveDocContext} from '@docusaurus/plugin-content-docs/client';
import {useLocation} from '@docusaurus/router';
export const URLPath = () => <code>{useLocation().pathname}</code>;
export const FilePath = () => {
const currentVersion = useActiveDocContext('default').activeVersion.name;
return <code>{currentVersion === 'current' ? './docs/' : `./versioned_docs/version-${currentVersion}/`}advanced/routing.md</code>;
}
```
This page, <URLPath />, is generated from the file at <FilePath />. The component used is `@theme/DocItem`.

View file

@ -0,0 +1,17 @@
# Routing
This page is only accessible through version-switching. It shows how a versioned doc file becomes a webpage.
```mdx-code-block
import {useLatestVersion, useActiveDocContext} from '@docusaurus/plugin-content-docs/client';
import {useLocation} from '@docusaurus/router';
export const URLPath = () => <code>{useLocation().pathname}</code>;
export const FilePath = () => {
const currentVersion = useActiveDocContext('default').activeVersion.name;
return <code>{currentVersion === 'current' ? './docs/' : `./versioned_docs/version-${currentVersion}/`}advanced/routing.md</code>;
}
```
This page, <URLPath />, is generated from the file at <FilePath />. The component used is `@theme/DocItem`.

177
yarn.lock
View file

@ -3924,6 +3924,11 @@
jest-diff "^26.0.0"
pretty-format "^26.0.0"
"@types/js-cookie@^2.2.6":
version "2.2.7"
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3"
integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==
"@types/js-yaml@^4.0.0":
version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
@ -4619,6 +4624,11 @@
"@webassemblyjs/ast" "1.11.1"
"@xtuc/long" "4.2.2"
"@xobotyi/scrollbar-width@^1.9.5":
version "1.9.5"
resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -6876,6 +6886,13 @@ copy-text-to-clipboard@^3.0.1:
resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c"
integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==
copy-to-clipboard@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==
dependencies:
toggle-selection "^1.0.6"
copy-webpack-plugin@^10.2.0:
version "10.2.0"
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-10.2.0.tgz#24c2d256953a55400a1ec66be4e0eccd1c4ae958"
@ -7132,6 +7149,14 @@ css-declaration-sorter@^6.0.3:
dependencies:
timsort "^0.3.0"
css-in-js-utils@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99"
integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==
dependencies:
hyphenate-style-name "^1.0.2"
isobject "^3.0.1"
css-loader@^6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.5.1.tgz#0c43d4fbe0d97f699c91e9818cb585759091d1b1"
@ -7287,7 +7312,7 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
csstype@^3.0.2:
csstype@^3.0.2, csstype@^3.0.6:
version "3.0.10"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
@ -8115,7 +8140,7 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
error-stack-parser@^2.0.2, error-stack-parser@^2.0.3:
error-stack-parser@^2.0.2, error-stack-parser@^2.0.3, error-stack-parser@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8"
integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==
@ -8895,6 +8920,11 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
fast-shallow-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b"
integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==
fast-url-parser@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d"
@ -8907,6 +8937,11 @@ fastest-levenshtein@^1.0.12:
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
fastest-stable-stringify@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76"
integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==
fastq@^1.6.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
@ -9224,6 +9259,11 @@ fn.name@1.x.x:
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
focus-options-polyfill@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/focus-options-polyfill/-/focus-options-polyfill-1.2.0.tgz#9800ffb230bc9db63eea6d27694a62ac2e355946"
integrity sha512-4sgXxV/zU4WHM2IHWpjUmEWazbF6ie+M93/uo8ipfAbQ1GHopn+/V+Ca+PR0ndxswNQbisCNrjbnvWEq14MkTA==
folder-walker@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/folder-walker/-/folder-walker-3.2.0.tgz#98e00e59773f43416a6dcf0926d4c9436f65121d"
@ -10462,6 +10502,11 @@ husky@^7.0.4:
resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535"
integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
hyphenate-style-name@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -10637,6 +10682,13 @@ inline-style-parser@0.1.1:
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
inline-style-prefixer@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae"
integrity sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ==
dependencies:
css-in-js-utils "^2.0.0"
inquirer-autocomplete-prompt@^1.0.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-1.4.0.tgz#e767592f747e3d5bb6336fe71fb4094352e4c317"
@ -11743,6 +11795,11 @@ jpeg-js@0.4.2:
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.2.tgz#8b345b1ae4abde64c2da2fe67ea216a114ac279d"
integrity sha512-+az2gi/hvex7eLTMTlbRLOhH6P6WFdk2ITI8HJsaH2VqYO0I594zXSYEP+tf4FW+8Cy68ScDXoAsQdyQanv3sw==
js-cookie@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
js-string-escape@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
@ -13325,6 +13382,20 @@ mz@^2.4.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nano-css@^5.3.1:
version "5.3.4"
resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.4.tgz#40af6a83a76f84204f346e8ccaa9169cdae9167b"
integrity sha512-wfcviJB6NOxDIDfr7RFn/GlaN7I/Bhe4d39ZRCJ3xvZX60LVe2qZ+rDqM49nm4YT81gAjzS+ZklhKP/Gnfnubg==
dependencies:
css-tree "^1.1.2"
csstype "^3.0.6"
fastest-stable-stringify "^2.0.2"
inline-style-prefixer "^6.0.0"
rtl-css-js "^1.14.0"
sourcemap-codec "^1.4.8"
stacktrace-js "^2.0.2"
stylis "^4.0.6"
nanoid@^3.1.30:
version "3.1.32"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.32.tgz#8f96069e6239cc0a9ae8c0d3b41a3b4933a88c0a"
@ -15737,6 +15808,15 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies:
"@babel/runtime" "^7.10.3"
react-medium-image-zoom@^4.3.5:
version "4.3.5"
resolved "https://registry.yarnpkg.com/react-medium-image-zoom/-/react-medium-image-zoom-4.3.5.tgz#9ed82faf73a68ba4ce76d8f94c022fc555eec4e4"
integrity sha512-feYHG+8McStfQ+mvFJTlmD1GQaYWiVlMf/Rv1MSRcd6UBBj+X8yj5OySw/Xau5PYFPCms7FbpCkt6437iRW47w==
dependencies:
focus-options-polyfill "1.2.0"
react-use "^17.2.1"
tslib "^2.1.0"
react-popper@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.5.tgz#1214ef3cec86330a171671a4fbcbeeb65ee58e96"
@ -15818,6 +15898,31 @@ react-textarea-autosize@^8.3.2:
use-composed-ref "^1.0.0"
use-latest "^1.0.0"
react-universal-interface@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==
react-use@^17.2.1:
version "17.3.2"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.3.2.tgz#448abf515f47c41c32455024db28167cb6e53be8"
integrity sha512-bj7OD0/1wL03KyWmzFXAFe425zziuTf7q8olwCYBfOeFHY1qfO1FAMjROQLsLZYwG4Rx63xAfb7XAbBrJsZmEw==
dependencies:
"@types/js-cookie" "^2.2.6"
"@xobotyi/scrollbar-width" "^1.9.5"
copy-to-clipboard "^3.3.1"
fast-deep-equal "^3.1.3"
fast-shallow-equal "^1.0.0"
js-cookie "^2.2.1"
nano-css "^5.3.1"
react-universal-interface "^0.6.2"
resize-observer-polyfill "^1.5.1"
screenfull "^5.1.0"
set-harmonic-interval "^1.0.1"
throttle-debounce "^3.0.1"
ts-easing "^0.2.0"
tslib "^2.1.0"
react-waypoint@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/react-waypoint/-/react-waypoint-10.1.0.tgz#6ab522a61bd52946260e4a78b3182759a97b40ec"
@ -16386,6 +16491,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@ -16571,6 +16681,13 @@ rsvp@^4.8.4:
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
rtl-css-js@^1.14.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.15.0.tgz#680ed816e570a9ebccba9e1cd0f202c6a8bb2dc0"
integrity sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew==
dependencies:
"@babel/runtime" "^7.1.2"
rtl-detect@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6"
@ -16723,6 +16840,11 @@ schema-utils@^4.0.0:
ajv-formats "^2.1.1"
ajv-keywords "^5.0.0"
screenfull@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
section-matter@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167"
@ -16869,6 +16991,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-harmonic-interval@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249"
integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==
set-value@^2.0.0, set-value@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
@ -17221,6 +17348,11 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
source-map@0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
integrity sha1-dc449SvwczxafwwRjYEzSiu19BI=
source-map@^0.5.0, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@ -17243,7 +17375,7 @@ source-map@^0.8.0-beta.0:
dependencies:
whatwg-url "^7.0.0"
sourcemap-codec@^1.4.4:
sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8:
version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
@ -17390,7 +17522,7 @@ stable@^0.1.8:
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
stack-generator@^2.0.3:
stack-generator@^2.0.3, stack-generator@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36"
integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==
@ -17414,6 +17546,23 @@ stackframe@^1.1.1:
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303"
integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==
stacktrace-gps@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a"
integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==
dependencies:
source-map "0.5.6"
stackframe "^1.1.1"
stacktrace-js@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b"
integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==
dependencies:
error-stack-parser "^2.0.6"
stack-generator "^2.0.5"
stacktrace-gps "^3.0.4"
state-toggle@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe"
@ -17840,6 +17989,11 @@ stylelint@^14.2.0:
v8-compile-cache "^2.3.0"
write-file-atomic "^3.0.3"
stylis@^4.0.6:
version "4.0.13"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
sugarss@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d"
@ -18148,6 +18302,11 @@ throat@^5.0.0:
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
throttle-debounce@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
through2-filter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254"
@ -18310,6 +18469,11 @@ to-vfile@^6.0.0:
is-buffer "^2.0.0"
vfile "^4.0.0"
toggle-selection@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
@ -18415,6 +18579,11 @@ trough@^2.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-2.0.2.tgz#94a3aa9d5ce379fc561f6244905b3f36b7458d96"
integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==
ts-easing@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec"
integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==
ts-node@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7"