Compare commits

..

1 Commits

Author SHA1 Message Date
ozakione 369abbbe11 docs: remove old github action description 2024-03-25 19:23:35 +01:00
197 changed files with 2932 additions and 24014 deletions

View File

@ -15,4 +15,4 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Dependency Review
uses: actions/dependency-review-action@733dd5d4a5203f238c33806593ec0f5fc5343d8c # 4.2.4
uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # 4.1.3

View File

@ -1,136 +1,5 @@
# Docusaurus 2 Changelog
## 3.2.0 (2024-03-29)
#### :rocket: New Feature
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-plugin-sitemap`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#9954](https://github.com/facebook/docusaurus/pull/9954) feat(sitemap): add support for "lastmod" ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils-validation`, `docusaurus-utils`
- [#9912](https://github.com/facebook/docusaurus/pull/9912) feat(blog): add LastUpdateAuthor & LastUpdateTime ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-plugin-debug`, `docusaurus-types`, `docusaurus`
- [#9931](https://github.com/facebook/docusaurus/pull/9931) feat(core): add new plugin allContentLoaded lifecycle ([@slorber](https://github.com/slorber))
- `docusaurus-theme-translations`
- [#9928](https://github.com/facebook/docusaurus/pull/9928) feat(theme-translations) Icelandic (is) ([@Hallinn](https://github.com/Hallinn))
- `docusaurus-plugin-content-blog`
- [#9886](https://github.com/facebook/docusaurus/pull/9886) feat(blog): allow processing blog posts through a processBlogPosts function ([@OzakIOne](https://github.com/OzakIOne))
- [#9838](https://github.com/facebook/docusaurus/pull/9838) feat(blog): add blog pageBasePath plugin option ([@ilg-ul](https://github.com/ilg-ul))
- `docusaurus`
- [#9681](https://github.com/facebook/docusaurus/pull/9681) feat(swizzle): ask user preferred language if no language CLI option provided ([@yixiaojiu](https://github.com/yixiaojiu))
- `create-docusaurus`, `docusaurus-utils`
- [#9442](https://github.com/facebook/docusaurus/pull/9442) feat(create-docusaurus): ask user for preferred language when no language CLI option provided ([@Rafael-Martins](https://github.com/Rafael-Martins))
- `docusaurus-plugin-vercel-analytics`
- [#9687](https://github.com/facebook/docusaurus/pull/9687) feat(plugin-vercel-analytics): add new vercel analytics plugin ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-mdx-loader`
- [#9684](https://github.com/facebook/docusaurus/pull/9684) feat(mdx-loader): the table-of-contents should display toc/headings of imported MDX partials ([@anatolykopyl](https://github.com/anatolykopyl))
#### :bug: Bug Fix
- `docusaurus-mdx-loader`
- [#9999](https://github.com/facebook/docusaurus/pull/9999) fix(mdx-loader): Ignore contentTitle coming after Markdown thematicBreak ([@slorber](https://github.com/slorber))
- `docusaurus-theme-search-algolia`
- [#9945](https://github.com/facebook/docusaurus/pull/9945) fix(a11y): move focus algolia-search focus back to search input on Escape ([@mxschmitt](https://github.com/mxschmitt))
- `docusaurus-plugin-content-blog`
- [#9920](https://github.com/facebook/docusaurus/pull/9920) fix(blog): apply trailing slash to blog feed ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-theme-classic`
- [#9944](https://github.com/facebook/docusaurus/pull/9944) fix(theme): improve a11y of DocSidebarItemCategory expand/collapsed button ([@mxschmitt](https://github.com/mxschmitt))
- `docusaurus-theme-translations`
- [#9915](https://github.com/facebook/docusaurus/pull/9915) fix(theme-translations): complete and modify Japanese translations ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
- [#9910](https://github.com/facebook/docusaurus/pull/9910) fix(theme-translations): add Japanese translations ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
- [#9872](https://github.com/facebook/docusaurus/pull/9872) fix(theme-translations): complete and improve Spanish theme translations ([@4troDev](https://github.com/4troDev))
- [#9812](https://github.com/facebook/docusaurus/pull/9812) fix(i18n): add missing theme translations for fa locale ([@VahidNaderi](https://github.com/VahidNaderi))
- `docusaurus-utils`
- [#9897](https://github.com/facebook/docusaurus/pull/9897) fix(mdx-loader): mdx-code-block should support CRLF ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#9878](https://github.com/facebook/docusaurus/pull/9878) fix(core): fix default i18n calendar used, infer it from locale if possible ([@slorber](https://github.com/slorber))
- [#9852](https://github.com/facebook/docusaurus/pull/9852) fix(core): ensure core error boundary is able to render theme layout ([@slorber](https://github.com/slorber))
- `docusaurus-remark-plugin-npm2yarn`
- [#9861](https://github.com/facebook/docusaurus/pull/9861) fix(remark-npm2yarn): update npm-to-yarn from 2.0.0 to 2.2.1, fix pnpm extra args syntax ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-theme-classic`, `docusaurus-theme-translations`
- [#9851](https://github.com/facebook/docusaurus/pull/9851) fix(theme-classic): should use plurals for category items description ([@baradusov](https://github.com/baradusov))
#### :running_woman: Performance
- `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#9975](https://github.com/facebook/docusaurus/pull/9975) refactor(core): improve dev perf, fine-grained site reloads - part 3 ([@slorber](https://github.com/slorber))
- `docusaurus-types`, `docusaurus`
- [#9968](https://github.com/facebook/docusaurus/pull/9968) refactor(core): improve dev perf, fine-grained site reloads - part2 ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-types`, `docusaurus`
- [#9903](https://github.com/facebook/docusaurus/pull/9903) refactor(core): improve dev perf, fine-grained site reloads - part1 ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-utils`
- [#9890](https://github.com/facebook/docusaurus/pull/9890) perf: optimize getFileCommitDate, make it async ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#9798](https://github.com/facebook/docusaurus/pull/9798) refactor(core): internalize, simplify and optimize the SSG logic ([@slorber](https://github.com/slorber))
#### :nail_care: Polish
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#9868](https://github.com/facebook/docusaurus/pull/9868) refactor(theme): dates should be formatted on the client-side instead of in nodejs code ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-plugin-content-blog`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-types`
- [#9669](https://github.com/facebook/docusaurus/pull/9669) refactor(theme): use JSON-LD instead of microdata for blog structured data ([@johnnyreilly](https://github.com/johnnyreilly))
- `docusaurus-plugin-content-docs`
- [#9839](https://github.com/facebook/docusaurus/pull/9839) refactor(blog): improve doc global data hook error message + add doc warning to blogOnly mode ([@OzakIOne](https://github.com/OzakIOne))
#### :memo: Documentation
- [#9937](https://github.com/facebook/docusaurus/pull/9937) docs: use official GitHub Action to deploy to GitHub Pages ([@vlad-nestorov](https://github.com/vlad-nestorov))
- [#9971](https://github.com/facebook/docusaurus/pull/9971) docs: replace VuePress by VitePress on tool comparison section ([@sunkanmii](https://github.com/sunkanmii))
- [#9914](https://github.com/facebook/docusaurus/pull/9914) docs: update legacy MDX v1 links to markdown links ([@OzakIOne](https://github.com/OzakIOne))
- [#9913](https://github.com/facebook/docusaurus/pull/9913) docs: update legacy MDX v1 links to markdown links ([@OzakIOne](https://github.com/OzakIOne))
- [#9906](https://github.com/facebook/docusaurus/pull/9906) docs: emphasize "index slug" convention ([@Josh-Cena](https://github.com/Josh-Cena))
- [#9877](https://github.com/facebook/docusaurus/pull/9877) docs: fix typos in deployment.mdx ([@Oreoxmt](https://github.com/Oreoxmt))
- [#9845](https://github.com/facebook/docusaurus/pull/9845) docs: typo ([@OzakIOne](https://github.com/OzakIOne))
- [#9816](https://github.com/facebook/docusaurus/pull/9816) docs: Add docs for Mermaid Component ([@Its-Just-Nans](https://github.com/Its-Just-Nans))
#### :robot: Dependencies
- [#9981](https://github.com/facebook/docusaurus/pull/9981) chore(deps): bump actions/dependency-review-action from 4.1.3 to 4.2.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9982](https://github.com/facebook/docusaurus/pull/9982) chore(deps): bump katex from 0.16.8 to 0.16.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9983](https://github.com/facebook/docusaurus/pull/9983) chore(deps): bump express from 4.18.2 to 4.19.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9977](https://github.com/facebook/docusaurus/pull/9977) chore(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9958](https://github.com/facebook/docusaurus/pull/9958) chore(deps): bump follow-redirects from 1.15.4 to 1.15.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9892](https://github.com/facebook/docusaurus/pull/9892) chore(deps): bump actions/dependency-review-action from 4.1.2 to 4.1.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9869](https://github.com/facebook/docusaurus/pull/9869) chore(deps): bump actions/dependency-review-action from 4.0.0 to 4.1.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9874](https://github.com/facebook/docusaurus/pull/9874) chore(deps): bump ip from 2.0.0 to 2.0.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9843](https://github.com/facebook/docusaurus/pull/9843) chore(deps): bump actions/setup-node from 4.0.1 to 4.0.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9824](https://github.com/facebook/docusaurus/pull/9824) chore(deps): bump treosh/lighthouse-ci-action from 10.1.0 to 11.4.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9823](https://github.com/facebook/docusaurus/pull/9823) chore(deps): bump marocchino/sticky-pull-request-comment from 2.8.0 to 2.9.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### :wrench: Maintenance
- `docusaurus-plugin-client-redirects`, `docusaurus-plugin-content-docs`, `docusaurus-utils-common`, `docusaurus-utils-validation`, `docusaurus-utils`, `docusaurus`
- [#9972](https://github.com/facebook/docusaurus/pull/9972) refactor(utils): remove duplicated function ([@OzakIOne](https://github.com/OzakIOne))
- Other
- [#9965](https://github.com/facebook/docusaurus/pull/9965) refactor(website): organise blog posts by year ([@GingerGeek](https://github.com/GingerGeek))
- [#9865](https://github.com/facebook/docusaurus/pull/9865) chore(website): update @crowdin/crowdin-api-client ([@chris-bateman](https://github.com/chris-bateman))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils`
- [#9963](https://github.com/facebook/docusaurus/pull/9963) refactor(docs,blog): last update timestamp should be in milliseconds instead of seconds ([@slorber](https://github.com/slorber))
#### Committers: 22
- Aolin ([@Oreoxmt](https://github.com/Oreoxmt))
- Anatoly Kopyl ([@anatolykopyl](https://github.com/anatolykopyl))
- Chris Bateman ([@chris-bateman](https://github.com/chris-bateman))
- Fafowora Sunkanmi ([@sunkanmii](https://github.com/sunkanmii))
- Hallbjörn Magnússon ([@Hallinn](https://github.com/Hallinn))
- John Reilly ([@johnnyreilly](https://github.com/johnnyreilly))
- Joshua Chen ([@Josh-Cena](https://github.com/Josh-Cena))
- Josue [4tro] A ([@4troDev](https://github.com/4troDev))
- Liviu Ionescu ([@ilg-ul](https://github.com/ilg-ul))
- Max Schmitt ([@mxschmitt](https://github.com/mxschmitt))
- Rafael Martins ([@Rafael-Martins](https://github.com/Rafael-Martins))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- Vahid Naderi ([@VahidNaderi](https://github.com/VahidNaderi))
- Vlad Nestorov ([@vlad-nestorov](https://github.com/vlad-nestorov))
- Zed Spencer-Milnes ([@GingerGeek](https://github.com/GingerGeek))
- axel7083 ([@axel7083](https://github.com/axel7083))
- krinza.eth ([@kaymomin](https://github.com/kaymomin))
- n4n5 ([@Its-Just-Nans](https://github.com/Its-Just-Nans))
- ozaki ([@OzakIOne](https://github.com/OzakIOne))
- suenryu ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
- Нуриль Барадусов ([@baradusov](https://github.com/baradusov))
- 翊小久 ([@yixiaojiu](https://github.com/yixiaojiu))
## 3.1.1 (2024-01-26)
#### :bug: Bug Fix

View File

@ -11,9 +11,9 @@ const CookieName = 'DocusaurusPlaygroundName';
const PlaygroundConfigs = {
codesandbox:
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic?file=%2FREADME.md&privacy=public',
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic?file=%2FREADME.md',
'codesandbox-ts':
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic-typescript?file=%2FREADME.md&privacy=public',
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic-typescript?file=%2FREADME.md',
// Slow to load
// stackblitz: 'https://stackblitz.com/github/facebook/docusaurus/tree/main/examples/classic',

View File

@ -1,6 +1,6 @@
{
"name": "new.docusaurus.io",
"version": "3.2.0",
"version": "3.0.0",
"private": true,
"scripts": {
"start": "npx --package netlify-cli netlify dev"

View File

@ -1,6 +1,6 @@
{
"name": "argos",
"version": "3.2.0",
"version": "3.0.0",
"description": "Argos visual diff tests",
"license": "MIT",
"private": true,

View File

@ -16,8 +16,8 @@
"dev": "docusaurus start"
},
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/preset-classic": "3.2.0",
"@docusaurus/core": "3.1.1",
"@docusaurus/preset-classic": "3.1.1",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -25,9 +25,9 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/tsconfig": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/module-type-aliases": "3.1.1",
"@docusaurus/tsconfig": "3.1.1",
"@docusaurus/types": "3.1.1",
"typescript": "~5.2.2"
},
"browserslist": {

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,8 @@
"dev": "docusaurus start"
},
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/preset-classic": "3.2.0",
"@docusaurus/core": "3.1.1",
"@docusaurus/preset-classic": "3.1.1",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -24,8 +24,8 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/types": "3.2.0"
"@docusaurus/module-type-aliases": "3.1.1",
"@docusaurus/types": "3.1.1"
},
"browserslist": {
"production": [

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{
"version": "3.2.0",
"version": "3.0.0",
"npmClient": "yarn",
"useWorkspaces": true,
"useNx": false,

View File

@ -1,6 +1,6 @@
{
"name": "create-docusaurus",
"version": "3.2.0",
"version": "3.0.0",
"description": "Create Docusaurus apps easily.",
"type": "module",
"repository": {
@ -22,8 +22,8 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/utils": "3.0.0",
"commander": "^5.1.0",
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",

View File

@ -1,6 +1,6 @@
{
"name": "docusaurus-2-classic-typescript-template",
"version": "3.2.0",
"version": "3.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -15,8 +15,8 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/preset-classic": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/preset-classic": "3.0.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -24,9 +24,9 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/tsconfig": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/tsconfig": "3.0.0",
"@docusaurus/types": "3.0.0",
"typescript": "~5.2.2"
},
"browserslist": {

View File

@ -1,6 +1,6 @@
{
"name": "docusaurus-2-classic-template",
"version": "3.2.0",
"version": "3.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -14,8 +14,8 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/preset-classic": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/preset-classic": "3.0.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -23,8 +23,8 @@
"react-dom": "^18.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/types": "3.2.0"
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/types": "3.0.0"
},
"browserslist": {
"production": [

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/cssnano-preset",
"version": "3.2.0",
"version": "3.0.0",
"description": "Advanced cssnano preset for maximum optimization.",
"main": "lib/index.js",
"license": "MIT",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/logger",
"version": "3.2.0",
"version": "3.0.0",
"description": "An encapsulated logger for semantically formatting console messages.",
"main": "./lib/index.js",
"repository": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/mdx-loader",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus Loader for MDX",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,9 +18,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@mdx-js/mdx": "^3.0.0",
"@slorber/remark-comment": "^1.0.0",
"escape-html": "^1.0.3",
@ -44,7 +44,7 @@
"webpack": "^5.88.1"
},
"devDependencies": {
"@docusaurus/types": "3.2.0",
"@docusaurus/types": "3.0.0",
"@types/escape-html": "^1.0.2",
"@types/mdast": "^4.0.2",
"@types/stringify-object": "^3.3.1",

View File

@ -71,21 +71,6 @@ some **markdown** *content*
expect(result.data.contentTitle).toBeUndefined();
});
it('ignore contentTitle if after thematic break', async () => {
const result = await process(`
Hey
---
# contentTitle 1
some **markdown** *content*
`);
expect(result.data.contentTitle).toBeUndefined();
});
it('is able to decently serialize Markdown syntax', async () => {
const result = await process(`
# some **markdown** \`content\` _italic_

View File

@ -34,24 +34,17 @@ const plugin: Plugin = function plugin(
const {toString} = await import('mdast-util-to-string');
const {visit, EXIT} = await import('unist-util-visit');
visit(root, ['heading', 'thematicBreak'], (node, index, parent) => {
if (node.type === 'heading') {
const headingNode = node as Heading;
if (headingNode.depth === 1) {
vfile.data.contentTitle = toString(headingNode);
if (removeContentTitle) {
// @ts-expect-error: TODO how to fix?
parent!.children.splice(index, 1);
}
return EXIT; // We only handle the very first heading
}
// We only handle contentTitle if it's the very first heading found
if (headingNode.depth >= 1) {
return EXIT;
visit(root, 'heading', (headingNode: Heading, index, parent) => {
if (headingNode.depth === 1) {
vfile.data.contentTitle = toString(headingNode);
if (removeContentTitle) {
// @ts-expect-error: TODO how to fix?
parent!.children.splice(index, 1);
}
return EXIT; // We only handle the very first heading
}
// We only handle contentTitle when it's above the first thematic break
if (node.type === 'thematicBreak') {
// We only handle contentTitle if it's the very first heading found
if (headingNode.depth >= 1) {
return EXIT;
}
return undefined;

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/module-type-aliases",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus module type aliases.",
"types": "./src/index.d.ts",
"publishConfig": {
@ -13,7 +13,7 @@
},
"dependencies": {
"@docusaurus/react-loadable": "5.5.2",
"@docusaurus/types": "3.2.0",
"@docusaurus/types": "3.0.0",
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router-config": "*",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-client-redirects",
"version": "3.2.0",
"version": "3.0.0",
"description": "Client redirects plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,18 +18,18 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"eta": "^2.2.0",
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",
"tslib": "^2.6.0"
},
"devDependencies": {
"@docusaurus/types": "3.2.0"
"@docusaurus/types": "3.0.0"
},
"peerDependencies": {
"react": "^18.0.0",

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {removeTrailingSlash} from '@docusaurus/utils-common';
import {removeTrailingSlash} from '@docusaurus/utils';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import collectRedirects from '../collectRedirects';
import {validateOptions} from '../options';

View File

@ -7,11 +7,8 @@
import _ from 'lodash';
import logger from '@docusaurus/logger';
import {
applyTrailingSlash,
addTrailingSlash,
removeTrailingSlash,
} from '@docusaurus/utils-common';
import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils';
import {applyTrailingSlash} from '@docusaurus/utils-common';
import {
createFromExtensionsRedirects,
createToExtensionsRedirects,

View File

@ -9,7 +9,7 @@ import {
addTrailingSlash,
removeSuffix,
removeTrailingSlash,
} from '@docusaurus/utils-common';
} from '@docusaurus/utils';
import type {RedirectItem} from './types';
const ExtensionAdditionalMessage =

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {addLeadingSlash, removePrefix} from '@docusaurus/utils-common';
import {removePrefix, addLeadingSlash} from '@docusaurus/utils';
import collectRedirects from './collectRedirects';
import writeRedirectFiles, {
toRedirectFiles,

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-content-blog",
"version": "3.2.0",
"version": "3.0.0",
"description": "Blog plugin for Docusaurus.",
"main": "lib/index.js",
"types": "src/plugin-content-blog.d.ts",
@ -31,13 +31,13 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/mdx-loader": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"cheerio": "^1.0.0-rc.12",
"feed": "^4.2.2",
"fs-extra": "^11.1.1",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-content-docs",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docs plugin for Docusaurus.",
"main": "lib/index.js",
"sideEffects": false,
@ -35,14 +35,13 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/mdx-loader": "3.0.0",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@types/react-router-config": "^5.0.7",
"combine-promises": "^1.1.0",
"fs-extra": "^11.1.1",

View File

@ -8,7 +8,7 @@
import path from 'path';
import _ from 'lodash';
import logger from '@docusaurus/logger';
import {addTrailingSlash} from '@docusaurus/utils-common';
import {addTrailingSlash} from '@docusaurus/utils';
import {createDocsByIdIndex, toCategoryIndexMatcherParam} from '../docs';
import type {
SidebarItemDoc,

View File

@ -5,8 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
import {isValidPathname, resolvePathname} from '@docusaurus/utils';
import {addLeadingSlash, addTrailingSlash} from '@docusaurus/utils-common';
import {
addLeadingSlash,
addTrailingSlash,
isValidPathname,
resolvePathname,
} from '@docusaurus/utils';
import {
DefaultNumberPrefixParser,
stripPathNumberPrefixes,

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-content-pages",
"version": "3.2.0",
"version": "3.0.0",
"description": "Pages plugin for Docusaurus.",
"main": "lib/index.js",
"types": "src/plugin-content-pages.d.ts",
@ -18,11 +18,11 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/mdx-loader": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"fs-extra": "^11.1.1",
"tslib": "^2.6.0",
"webpack": "^5.88.1"

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-debug",
"version": "3.2.0",
"version": "3.0.0",
"description": "Debug plugin for Docusaurus.",
"main": "lib/index.js",
"types": "src/plugin-debug.d.ts",
@ -20,9 +20,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"fs-extra": "^11.1.1",
"react-json-view-lite": "^1.2.0",
"tslib": "^2.6.0"

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-google-analytics",
"version": "3.2.0",
"version": "3.0.0",
"description": "Global analytics (analytics.js) plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,9 +18,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"tslib": "^2.6.0"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-google-gtag",
"version": "3.2.0",
"version": "3.0.0",
"description": "Global Site Tag (gtag.js) plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,9 +18,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@types/gtag.js": "^0.0.12",
"tslib": "^2.6.0"
},

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-google-tag-manager",
"version": "3.2.0",
"version": "3.0.0",
"description": "Google Tag Manager (gtm.js) plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,9 +18,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"tslib": "^2.6.0"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-ideal-image",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).",
"main": "lib/index.js",
"types": "src/plugin-ideal-image.d.ts",
@ -20,12 +20,12 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/lqip-loader": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/lqip-loader": "3.0.0",
"@docusaurus/responsive-loader": "^1.7.0",
"@docusaurus/theme-translations": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/theme-translations": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@slorber/react-ideal-image": "^0.0.12",
"react-waypoint": "^10.3.0",
"sharp": "^0.32.3",
@ -33,7 +33,7 @@
"webpack": "^5.88.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/module-type-aliases": "3.0.0",
"fs-extra": "^11.1.0"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-pwa",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus Plugin to add PWA support.",
"main": "lib/index.js",
"types": "src/plugin-pwa.d.ts",
@ -22,12 +22,12 @@
"dependencies": {
"@babel/core": "^7.23.3",
"@babel/preset-env": "^7.23.3",
"@docusaurus/core": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/theme-translations": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/theme-translations": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"babel-loader": "^9.1.3",
"clsx": "^2.0.0",
"core-js": "^3.31.1",
@ -41,7 +41,7 @@
"workbox-window": "^7.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/module-type-aliases": "3.0.0",
"fs-extra": "^11.1.0"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-sitemap",
"version": "3.2.0",
"version": "3.0.0",
"description": "Simple sitemap generation plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,12 +18,12 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"fs-extra": "^11.1.1",
"sitemap": "^7.1.1",
"tslib": "^2.6.0"

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-vercel-analytics",
"version": "3.2.0",
"version": "3.0.0",
"description": "Global vercel analytics plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,11 +18,11 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@vercel/analytics": "^1.1.1",
"tslib": "^2.6.0"
},

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/preset-classic",
"version": "3.2.0",
"version": "3.0.0",
"description": "Classic preset for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,19 +18,19 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/plugin-content-blog": "3.2.0",
"@docusaurus/plugin-content-docs": "3.2.0",
"@docusaurus/plugin-content-pages": "3.2.0",
"@docusaurus/plugin-debug": "3.2.0",
"@docusaurus/plugin-google-analytics": "3.2.0",
"@docusaurus/plugin-google-gtag": "3.2.0",
"@docusaurus/plugin-google-tag-manager": "3.2.0",
"@docusaurus/plugin-sitemap": "3.2.0",
"@docusaurus/theme-classic": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/theme-search-algolia": "3.2.0",
"@docusaurus/types": "3.2.0"
"@docusaurus/core": "3.0.0",
"@docusaurus/plugin-content-blog": "3.0.0",
"@docusaurus/plugin-content-docs": "3.0.0",
"@docusaurus/plugin-content-pages": "3.0.0",
"@docusaurus/plugin-debug": "3.0.0",
"@docusaurus/plugin-google-analytics": "3.0.0",
"@docusaurus/plugin-google-gtag": "3.0.0",
"@docusaurus/plugin-google-tag-manager": "3.0.0",
"@docusaurus/plugin-sitemap": "3.0.0",
"@docusaurus/theme-classic": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/theme-search-algolia": "3.0.0",
"@docusaurus/types": "3.0.0"
},
"peerDependencies": {
"react": "^18.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/remark-plugin-npm2yarn",
"version": "3.2.0",
"version": "3.0.0",
"description": "Remark plugin for converting npm commands to Yarn commands as tabs.",
"main": "lib/index.js",
"publishConfig": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/theme-classic",
"version": "3.2.0",
"version": "3.0.0",
"description": "Classic theme for Docusaurus",
"main": "lib/index.js",
"types": "src/theme-classic.d.ts",
@ -20,18 +20,18 @@
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
},
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/plugin-content-blog": "3.2.0",
"@docusaurus/plugin-content-docs": "3.2.0",
"@docusaurus/plugin-content-pages": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/theme-translations": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/mdx-loader": "3.0.0",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/plugin-content-blog": "3.0.0",
"@docusaurus/plugin-content-docs": "3.0.0",
"@docusaurus/plugin-content-pages": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/theme-translations": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"copy-text-to-clipboard": "^3.2.0",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/theme-common",
"version": "3.2.0",
"version": "3.0.0",
"description": "Common code for Docusaurus themes.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -30,13 +30,13 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/plugin-content-blog": "3.2.0",
"@docusaurus/plugin-content-docs": "3.2.0",
"@docusaurus/plugin-content-pages": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/mdx-loader": "3.0.0",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/plugin-content-blog": "3.0.0",
"@docusaurus/plugin-content-docs": "3.0.0",
"@docusaurus/plugin-content-pages": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router-config": "*",
@ -47,8 +47,8 @@
"utility-types": "^3.10.0"
},
"devDependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/types": "3.0.0",
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",
"schema-dts": "^1.1.2"

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/theme-live-codeblock",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus live code block component.",
"main": "lib/index.js",
"types": "src/theme-live-codeblock.d.ts",
@ -23,10 +23,10 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/theme-translations": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/theme-translations": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@philpl/buble": "^0.19.7",
"clsx": "^2.0.0",
"fs-extra": "^11.1.1",
@ -34,7 +34,7 @@
"tslib": "^2.6.0"
},
"devDependencies": {
"@docusaurus/types": "3.2.0",
"@docusaurus/types": "3.0.0",
"@types/buble": "^0.20.1"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/theme-mermaid",
"version": "3.2.0",
"version": "3.0.0",
"description": "Mermaid components for Docusaurus.",
"main": "lib/index.js",
"types": "src/theme-mermaid.d.ts",
@ -33,11 +33,11 @@
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
},
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/types": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"mermaid": "^10.4.0",
"tslib": "^2.6.0"
},

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/theme-search-algolia",
"version": "3.2.0",
"version": "3.0.0",
"description": "Algolia search component for Docusaurus.",
"main": "lib/index.js",
"sideEffects": [
@ -34,13 +34,13 @@
},
"dependencies": {
"@docsearch/react": "^3.5.2",
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/plugin-content-docs": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/theme-translations": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/plugin-content-docs": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/theme-translations": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"algoliasearch": "^4.18.0",
"algoliasearch-helper": "^3.13.3",
"clsx": "^2.0.0",
@ -51,7 +51,7 @@
"utility-types": "^3.10.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0"
"@docusaurus/module-type-aliases": "3.0.0"
},
"peerDependencies": {
"react": "^18.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/theme-translations",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus theme translations.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -23,8 +23,8 @@
"tslib": "^2.6.0"
},
"devDependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"lodash": "^4.17.21"
},
"engines": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/tsconfig",
"version": "3.2.0",
"version": "3.0.0",
"description": "Docusaurus base TypeScript configuration.",
"main": "tsconfig.json",
"publishConfig": {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/types",
"version": "3.2.0",
"version": "3.0.0",
"description": "Common types for Docusaurus packages.",
"types": "./src/index.d.ts",
"publishConfig": {

View File

@ -31,7 +31,6 @@ export type GlobalData = {[pluginName: string]: {[pluginId: string]: unknown}};
export type LoadContext = {
siteDir: string;
siteVersion: string | undefined;
generatedFilesDir: string;
siteConfig: DocusaurusConfig;
siteConfigPath: string;

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import type {CodeTranslations, TranslationFile} from './i18n';
import type {TranslationFile} from './i18n';
import type {RuleSetRule, Configuration as WebpackConfiguration} from 'webpack';
import type {CustomizeRuleString} from 'webpack-merge/dist/types';
import type {CommanderStatic} from 'commander';
@ -185,7 +185,6 @@ export type LoadedPlugin = InitializedPlugin & {
readonly content: unknown;
readonly globalData: unknown;
readonly routes: RouteConfig[];
readonly defaultCodeTranslations: CodeTranslations;
};
export type PluginModule<Content = unknown> = {

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/utils-common",
"version": "3.2.0",
"version": "3.0.0",
"description": "Common (Node/Browser) utility functions for Docusaurus packages.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",

View File

@ -6,10 +6,7 @@
*/
import applyTrailingSlash, {
addTrailingSlash,
type ApplyTrailingSlashParams,
addLeadingSlash,
removeTrailingSlash,
} from '../applyTrailingSlash';
function params(
@ -179,30 +176,3 @@ describe('applyTrailingSlash', () => {
).toBe('https://xyz.com/abc/?search#anchor');
});
});
describe('addTrailingSlash', () => {
it('is no-op for path with trailing slash', () => {
expect(addTrailingSlash('/abcd/')).toBe('/abcd/');
});
it('adds / for path without trailing slash', () => {
expect(addTrailingSlash('/abcd')).toBe('/abcd/');
});
});
describe('addLeadingSlash', () => {
it('is no-op for path with leading slash', () => {
expect(addLeadingSlash('/abc')).toBe('/abc');
});
it('adds / for path without leading slash', () => {
expect(addLeadingSlash('abc')).toBe('/abc');
});
});
describe('removeTrailingSlash', () => {
it('is no-op for path without trailing slash', () => {
expect(removeTrailingSlash('/abcd')).toBe('/abcd');
});
it('removes / for path with trailing slash', () => {
expect(removeTrailingSlash('/abcd/')).toBe('/abcd');
});
});

View File

@ -1,55 +0,0 @@
/**
* 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 {addPrefix, addSuffix, removePrefix, removeSuffix} from '../stringUtils';
describe('removePrefix', () => {
it("is no-op when prefix doesn't exist", () => {
expect(removePrefix('abcdef', 'ijk')).toBe('abcdef');
expect(removePrefix('abcdef', 'def')).toBe('abcdef');
expect(removePrefix('abcdef', '')).toBe('abcdef');
});
it('removes prefix', () => {
expect(removePrefix('prefix', 'pre')).toBe('fix');
});
});
describe('removeSuffix', () => {
it("is no-op when suffix doesn't exist", () => {
expect(removeSuffix('abcdef', 'ijk')).toBe('abcdef');
expect(removeSuffix('abcdef', 'abc')).toBe('abcdef');
expect(removeSuffix('abcdef', '')).toBe('abcdef');
});
it('removes suffix', () => {
expect(removeSuffix('abcdef', 'ef')).toBe('abcd');
});
it('removes empty suffix', () => {
expect(removeSuffix('abcdef', '')).toBe('abcdef');
});
});
describe('addPrefix', () => {
it('is no-op when prefix already exists', () => {
expect(addPrefix('abcdef', 'abc')).toBe('abcdef');
expect(addPrefix('abc', '')).toBe('abc');
expect(addPrefix('', '')).toBe('');
});
it('adds prefix', () => {
expect(addPrefix('def', 'abc')).toBe('abcdef');
});
});
describe('addSuffix', () => {
it('is no-op when suffix already exists', () => {
expect(addSuffix('abcdef', 'def')).toBe('abcdef');
expect(addSuffix('abc', '')).toBe('abc');
expect(addSuffix('', '')).toBe('');
});
it('adds suffix', () => {
expect(addSuffix('abc', 'def')).toBe('abcdef');
});
});

View File

@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
import {addPrefix, removeSuffix} from './stringUtils';
import type {DocusaurusConfig} from '@docusaurus/types';
export type ApplyTrailingSlashParams = Pick<
@ -13,10 +12,6 @@ export type ApplyTrailingSlashParams = Pick<
'trailingSlash' | 'baseUrl'
>;
export function addTrailingSlash(str: string): string {
return str.endsWith('/') ? str : `${str}/`;
}
// Trailing slash handling depends in some site configuration options
export default function applyTrailingSlash(
path: string,
@ -29,6 +24,13 @@ export default function applyTrailingSlash(
return path;
}
// TODO deduplicate: also present in @docusaurus/utils
function addTrailingSlash(str: string): string {
return str.endsWith('/') ? str : `${str}/`;
}
function removeTrailingSlash(str: string): string {
return str.endsWith('/') ? str.slice(0, -1) : str;
}
function handleTrailingSlash(str: string, trailing: boolean): string {
return trailing ? addTrailingSlash(str) : removeTrailingSlash(str);
}
@ -53,13 +55,3 @@ export default function applyTrailingSlash(
return path.replace(pathname, newPathname);
}
/** Appends a leading slash to `str`, if one doesn't exist. */
export function addLeadingSlash(str: string): string {
return addPrefix(str, '/');
}
/** Removes the trailing slash from `str`. */
export function removeTrailingSlash(str: string): string {
return removeSuffix(str, '/');
}

View File

@ -11,10 +11,6 @@ export const blogPostContainerID = '__blog-post-container';
export {
default as applyTrailingSlash,
addTrailingSlash,
addLeadingSlash,
removeTrailingSlash,
type ApplyTrailingSlashParams,
} from './applyTrailingSlash';
export {addPrefix, removeSuffix, addSuffix, removePrefix} from './stringUtils';
export {getErrorCausalChain} from './errorUtils';

View File

@ -1,30 +0,0 @@
/**
* 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.
*/
/** Adds a given string prefix to `str`. */
export function addPrefix(str: string, prefix: string): string {
return str.startsWith(prefix) ? str : `${prefix}${str}`;
}
/** Removes a given string suffix from `str`. */
export function removeSuffix(str: string, suffix: string): string {
if (suffix === '') {
// str.slice(0, 0) is ""
return str;
}
return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str;
}
/** Adds a given string suffix to `str`. */
export function addSuffix(str: string, suffix: string): string {
return str.endsWith(suffix) ? str : `${str}${suffix}`;
}
/** Removes a given string prefix from `str`. */
export function removePrefix(str: string, prefix: string): string {
return str.startsWith(prefix) ? str.slice(prefix.length) : str;
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/utils-validation",
"version": "3.2.0",
"version": "3.0.0",
"description": "Node validation utility functions for Docusaurus packages.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -18,9 +18,8 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/utils": "3.0.0",
"joi": "^17.9.2",
"js-yaml": "^4.1.0",
"tslib": "^2.6.0"

View File

@ -5,8 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
import {isValidPathname, DEFAULT_PLUGIN_ID, type Tag} from '@docusaurus/utils';
import {addLeadingSlash} from '@docusaurus/utils-common';
import {
isValidPathname,
DEFAULT_PLUGIN_ID,
type Tag,
addLeadingSlash,
} from '@docusaurus/utils';
import Joi from './Joi';
import {JoiFrontMatter} from './JoiFrontMatter';

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/utils",
"version": "3.2.0",
"version": "3.0.0",
"description": "Node utility functions for Docusaurus packages.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -18,8 +18,7 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/logger": "3.0.0",
"@svgr/webpack": "^6.5.1",
"escape-string-regexp": "^4.0.0",
"file-loader": "^6.2.0",
@ -42,7 +41,7 @@
"node": ">=18.0"
},
"devDependencies": {
"@docusaurus/types": "3.2.0",
"@docusaurus/types": "3.0.0",
"@types/dedent": "^0.7.0",
"@types/github-slugger": "^1.3.0",
"@types/micromatch": "^4.0.2",

View File

@ -7,7 +7,34 @@
import {jest} from '@jest/globals';
import _ from 'lodash';
import {mapAsyncSequential, findAsyncSequential} from '../jsUtils';
import {
removeSuffix,
removePrefix,
mapAsyncSequential,
findAsyncSequential,
} from '../jsUtils';
describe('removeSuffix', () => {
it("is no-op when suffix doesn't exist", () => {
expect(removeSuffix('abcdef', 'ijk')).toBe('abcdef');
expect(removeSuffix('abcdef', 'abc')).toBe('abcdef');
expect(removeSuffix('abcdef', '')).toBe('abcdef');
});
it('removes suffix', () => {
expect(removeSuffix('abcdef', 'ef')).toBe('abcd');
});
});
describe('removePrefix', () => {
it("is no-op when prefix doesn't exist", () => {
expect(removePrefix('abcdef', 'ijk')).toBe('abcdef');
expect(removePrefix('abcdef', 'def')).toBe('abcdef');
expect(removePrefix('abcdef', '')).toBe('abcdef');
});
it('removes prefix', () => {
expect(removePrefix('prefix', 'pre')).toBe('fix');
});
});
describe('mapAsyncSequential', () => {
function sleep(timeout: number): Promise<void> {

View File

@ -10,6 +10,9 @@ import {
getEditUrl,
fileToPath,
isValidPathname,
addTrailingSlash,
addLeadingSlash,
removeTrailingSlash,
resolvePathname,
encodePath,
buildSshUrl,
@ -204,6 +207,33 @@ describe('isValidPathname', () => {
});
});
describe('addTrailingSlash', () => {
it('is no-op for path with trailing slash', () => {
expect(addTrailingSlash('/abcd/')).toBe('/abcd/');
});
it('adds / for path without trailing slash', () => {
expect(addTrailingSlash('/abcd')).toBe('/abcd/');
});
});
describe('addLeadingSlash', () => {
it('is no-op for path with leading slash', () => {
expect(addLeadingSlash('/abc')).toBe('/abc');
});
it('adds / for path without leading slash', () => {
expect(addLeadingSlash('abc')).toBe('/abc');
});
});
describe('removeTrailingSlash', () => {
it('is no-op for path without trailing slash', () => {
expect(removeTrailingSlash('/abcd')).toBe('/abcd');
});
it('removes / for path with trailing slash', () => {
expect(removeTrailingSlash('/abcd/')).toBe('/abcd');
});
});
describe('parseURLPath', () => {
it('parse and resolve pathname', () => {
expect(parseURLPath('')).toEqual({

View File

@ -12,10 +12,6 @@ import {findAsyncSequential} from './jsUtils';
const fileHash = new Map<string, string>();
const hashContent = (content: string): string => {
return createHash('md5').update(content).digest('hex');
};
/**
* Outputs a file to the generated files directory. Only writes files if content
* differs from cache (for hot reload performance).
@ -42,7 +38,7 @@ export async function generate(
// first "A" remains in cache. But if the file never existed in cache, no
// need to register it.
if (fileHash.get(filepath)) {
fileHash.set(filepath, hashContent(content));
fileHash.set(filepath, createHash('md5').update(content).digest('hex'));
}
return;
}
@ -54,11 +50,11 @@ export async function generate(
// overwriting and we can reuse old file.
if (!lastHash && (await fs.pathExists(filepath))) {
const lastContent = await fs.readFile(filepath, 'utf8');
lastHash = hashContent(lastContent);
lastHash = createHash('md5').update(lastContent).digest('hex');
fileHash.set(filepath, lastHash);
}
const currentHash = hashContent(content);
const currentHash = createHash('md5').update(content).digest('hex');
if (lastHash !== currentHash) {
await fs.outputFile(filepath, content);

View File

@ -6,16 +6,7 @@
*/
import path from 'path';
import fs from 'fs-extra';
import _ from 'lodash';
import shell from 'shelljs'; // TODO replace with async-first version
const realHasGitFn = () => !!shell.which('git');
// The hasGit call is synchronous IO so we memoize it
// The user won't install Git in the middle of a build anyway...
const hasGit =
process.env.NODE_ENV === 'test' ? realHasGitFn : _.memoize(realHasGitFn);
import shell from 'shelljs';
/** Custom error thrown when git is not found in `PATH`. */
export class GitNotFoundError extends Error {}
@ -95,13 +86,13 @@ export async function getFileCommitDate(
timestamp: number;
author?: string;
}> {
if (!hasGit()) {
if (!shell.which('git')) {
throw new GitNotFoundError(
`Failed to retrieve git history for "${file}" because git is not installed.`,
);
}
if (!(await fs.pathExists(file))) {
if (!shell.test('-f', file)) {
throw new Error(
`Failed to retrieve git history for "${file}" because the file does not exist.`,
);

View File

@ -9,7 +9,8 @@
import path from 'path';
import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby
import {addSuffix} from '@docusaurus/utils-common';
import {addSuffix} from './jsUtils';
/** A re-export of the globby instance. */
export {default as Globby} from 'globby';

View File

@ -35,7 +35,12 @@ export {
getPluginI18nPath,
localizePath,
} from './i18nUtils';
export {mapAsyncSequential, findAsyncSequential} from './jsUtils';
export {
removeSuffix,
removePrefix,
mapAsyncSequential,
findAsyncSequential,
} from './jsUtils';
export {
normalizeUrl,
getEditUrl,
@ -45,6 +50,9 @@ export {
resolvePathname,
parseURLPath,
serializeURLPath,
addLeadingSlash,
addTrailingSlash,
removeTrailingSlash,
hasSSHProtocol,
buildHttpsUrl,
buildSshUrl,

View File

@ -5,6 +5,30 @@
* LICENSE file in the root directory of this source tree.
*/
/** Adds a given string prefix to `str`. */
export function addPrefix(str: string, prefix: string): string {
return str.startsWith(prefix) ? str : `${prefix}${str}`;
}
/** Adds a given string suffix to `str`. */
export function addSuffix(str: string, suffix: string): string {
return str.endsWith(suffix) ? str : `${str}${suffix}`;
}
/** Removes a given string suffix from `str`. */
export function removeSuffix(str: string, suffix: string): string {
if (suffix === '') {
// str.slice(0, 0) is ""
return str;
}
return str.endsWith(suffix) ? str.slice(0, -suffix.length) : str;
}
/** Removes a given string prefix from `str`. */
export function removePrefix(str: string, prefix: string): string {
return str.startsWith(prefix) ? str.slice(prefix.length) : str;
}
/**
* `Array#map` for async operations where order matters.
* @param array The array to traverse.

View File

@ -6,6 +6,7 @@
*/
import resolvePathnameUnsafe from 'resolve-pathname';
import {addPrefix, addSuffix, removeSuffix} from './jsUtils';
/**
* Much like `path.join`, but much better. Takes an array of URL segments, and
@ -231,6 +232,22 @@ export function resolvePathname(to: string, from?: string): string {
return resolvePathnameUnsafe(to, from);
}
/** Appends a leading slash to `str`, if one doesn't exist. */
export function addLeadingSlash(str: string): string {
return addPrefix(str, '/');
}
// TODO deduplicate: also present in @docusaurus/utils-common
/** Appends a trailing slash to `str`, if one doesn't exist. */
export function addTrailingSlash(str: string): string {
return addSuffix(str, '/');
}
/** Removes the trailing slash from `str`. */
export function removeTrailingSlash(str: string): string {
return removeSuffix(str, '/');
}
/** Constructs an SSH URL that can be used to push to GitHub. */
export function buildSshUrl(
githubHost: string,

View File

@ -1,7 +1,7 @@
{
"name": "@docusaurus/core",
"description": "Easy to Maintain Open Source Documentation Websites",
"version": "3.2.0",
"version": "3.0.0",
"license": "MIT",
"publishConfig": {
"access": "public"
@ -43,13 +43,13 @@
"@babel/runtime": "^7.22.6",
"@babel/runtime-corejs3": "^7.22.6",
"@babel/traverse": "^7.22.8",
"@docusaurus/cssnano-preset": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/cssnano-preset": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/mdx-loader": "3.0.0",
"@docusaurus/react-loadable": "5.5.2",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@docusaurus/utils-validation": "3.0.0",
"@svgr/webpack": "^6.5.1",
"autoprefixer": "^10.4.14",
"babel-loader": "^9.1.3",
@ -69,8 +69,8 @@
"del": "^6.1.1",
"detect-port": "^1.5.1",
"escape-html": "^1.0.3",
"eta": "^2.2.0",
"eval": "^0.1.8",
"eta": "^2.2.0",
"file-loader": "^6.2.0",
"fs-extra": "^11.1.1",
"html-minifier-terser": "^7.2.0",
@ -105,8 +105,8 @@
"webpackbar": "^5.0.2"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/types": "3.0.0",
"@total-typescript/shoehorn": "^0.1.2",
"@types/detect-port": "^1.3.3",
"@types/react-dom": "^18.2.7",

View File

@ -64,15 +64,23 @@ export async function build(
process.on(sig, () => process.exit());
});
async function tryToBuildLocale({locale}: {locale: string}) {
async function tryToBuildLocale({
locale,
isLastLocale,
}: {
locale: string;
isLastLocale: boolean;
}) {
try {
await PerfLogger.async(`${logger.name(locale)}`, () =>
buildLocale({
siteDir,
locale,
cliOptions,
}),
);
PerfLogger.start(`Building site for locale ${locale}`);
await buildLocale({
siteDir,
locale,
cliOptions,
forceTerminate,
isLastLocale,
});
PerfLogger.end(`Building site for locale ${locale}`);
} catch (err) {
throw new Error(
logger.interpolate`Unable to build website for locale name=${locale}.`,
@ -83,28 +91,20 @@ export async function build(
}
}
const locales = await PerfLogger.async('Get locales to build', () =>
getLocalesToBuild({siteDir, cliOptions}),
);
PerfLogger.start(`Get locales to build`);
const locales = await getLocalesToBuild({siteDir, cliOptions});
PerfLogger.end(`Get locales to build`);
if (locales.length > 1) {
logger.info`Website will be built for all these locales: ${locales}`;
}
await PerfLogger.async(`Build`, () =>
mapAsyncSequential(locales, async (locale) => {
const isLastLocale = locales.indexOf(locale) === locales.length - 1;
await tryToBuildLocale({locale});
if (isLastLocale) {
logger.info`Use code=${'npm run serve'} command to test your build locally.`;
}
// TODO do we really need this historical forceTerminate exit???
if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
process.exit(0);
}
}),
);
PerfLogger.start(`Building ${locales.length} locales`);
await mapAsyncSequential(locales, (locale) => {
const isLastLocale = locales.indexOf(locale) === locales.length - 1;
return tryToBuildLocale({locale, isLastLocale});
});
PerfLogger.end(`Building ${locales.length} locales`);
}
async function getLocalesToBuild({
@ -144,10 +144,14 @@ async function buildLocale({
siteDir,
locale,
cliOptions,
forceTerminate,
isLastLocale,
}: {
siteDir: string;
locale: string;
cliOptions: Partial<BuildCLIOptions>;
forceTerminate: boolean;
isLastLocale: boolean;
}): Promise<string> {
// Temporary workaround to unlock the ability to translate the site config
// We'll remove it if a better official API can be designed
@ -156,66 +160,81 @@ async function buildLocale({
logger.info`name=${`[${locale}]`} Creating an optimized production build...`;
const site = await PerfLogger.async('Load site', () =>
loadSite({
siteDir,
outDir: cliOptions.outDir,
config: cliOptions.config,
locale,
localizePath: cliOptions.locale ? false : undefined,
}),
);
PerfLogger.start('Loading site');
const site = await loadSite({
siteDir,
outDir: cliOptions.outDir,
config: cliOptions.config,
locale,
localizePath: cliOptions.locale ? false : undefined,
});
PerfLogger.end('Loading site');
const {props} = site;
const {outDir, plugins} = props;
// We can build the 2 configs in parallel
PerfLogger.start('Creating webpack configs');
const [{clientConfig, clientManifestPath}, {serverConfig, serverBundlePath}] =
await PerfLogger.async('Creating webpack configs', () =>
Promise.all([
getBuildClientConfig({
props,
cliOptions,
}),
getBuildServerConfig({
props,
}),
]),
);
await Promise.all([
getBuildClientConfig({
props,
cliOptions,
}),
getBuildServerConfig({
props,
}),
]);
PerfLogger.end('Creating webpack configs');
// Make sure generated client-manifest is cleaned first, so we don't reuse
// the one from previous builds.
// TODO do we really need this? .docusaurus folder is cleaned between builds
PerfLogger.start('Deleting previous client manifest');
await ensureUnlink(clientManifestPath);
PerfLogger.end('Deleting previous client manifest');
// Run webpack to build JS bundle (client) and static html files (server).
await PerfLogger.async('Bundling with Webpack', () =>
compile([clientConfig, serverConfig]),
);
PerfLogger.start('Bundling');
await compile([clientConfig, serverConfig]);
PerfLogger.end('Bundling');
const {collectedData} = await PerfLogger.async('SSG', () =>
executeSSG({
props,
serverBundlePath,
clientManifestPath,
}),
);
PerfLogger.start('Executing static site generation');
const {collectedData} = await executeSSG({
props,
serverBundlePath,
clientManifestPath,
});
PerfLogger.end('Executing static site generation');
// Remove server.bundle.js because it is not needed.
await PerfLogger.async('Deleting server bundle', () =>
ensureUnlink(serverBundlePath),
);
PerfLogger.start('Deleting server bundle');
await ensureUnlink(serverBundlePath);
PerfLogger.end('Deleting server bundle');
// Plugin Lifecycle - postBuild.
await PerfLogger.async('postBuild()', () =>
executePluginsPostBuild({plugins, props, collectedData}),
);
PerfLogger.start('Executing postBuild()');
await executePluginsPostBuild({plugins, props, collectedData});
PerfLogger.end('Executing postBuild()');
// TODO execute this in parallel to postBuild?
await PerfLogger.async('Broken links checker', () =>
executeBrokenLinksCheck({props, collectedData}),
);
PerfLogger.start('Executing broken links checker');
await executeBrokenLinksCheck({props, collectedData});
PerfLogger.end('Executing broken links checker');
logger.success`Generated static files in path=${path.relative(
process.cwd(),
outDir,
)}.`;
if (isLastLocale) {
logger.info`Use code=${'npm run serve'} command to test your build locally.`;
}
if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
process.exit(0);
}
return outDir;
}
@ -228,39 +247,40 @@ async function executeSSG({
serverBundlePath: string;
clientManifestPath: string;
}) {
const manifest: Manifest = await PerfLogger.async(
'Read client manifest',
() => fs.readJSON(clientManifestPath, 'utf-8'),
);
PerfLogger.start('Reading client manifest');
const manifest: Manifest = await fs.readJSON(clientManifestPath, 'utf-8');
PerfLogger.end('Reading client manifest');
const ssrTemplate = await PerfLogger.async('Compile SSR template', () =>
compileSSRTemplate(props.siteConfig.ssrTemplate ?? defaultSSRTemplate),
PerfLogger.start('Compiling SSR template');
const ssrTemplate = await compileSSRTemplate(
props.siteConfig.ssrTemplate ?? defaultSSRTemplate,
);
PerfLogger.end('Compiling SSR template');
const renderer = await PerfLogger.async('Load App renderer', () =>
loadAppRenderer({
serverBundlePath,
}),
);
PerfLogger.start('Loading App renderer');
const renderer = await loadAppRenderer({
serverBundlePath,
});
PerfLogger.end('Loading App renderer');
const ssgResult = await PerfLogger.async('Generate static files', () =>
generateStaticFiles({
pathnames: props.routesPaths,
renderer,
params: {
trailingSlash: props.siteConfig.trailingSlash,
outDir: props.outDir,
baseUrl: props.baseUrl,
manifest,
headTags: props.headTags,
preBodyTags: props.preBodyTags,
postBodyTags: props.postBodyTags,
ssrTemplate,
noIndex: props.siteConfig.noIndex,
DOCUSAURUS_VERSION,
},
}),
);
PerfLogger.start('Generate static files');
const ssgResult = await generateStaticFiles({
pathnames: props.routesPaths,
renderer,
params: {
trailingSlash: props.siteConfig.trailingSlash,
outDir: props.outDir,
baseUrl: props.baseUrl,
manifest,
headTags: props.headTags,
preBodyTags: props.preBodyTags,
postBodyTags: props.postBodyTags,
ssrTemplate,
noIndex: props.siteConfig.noIndex,
DOCUSAURUS_VERSION,
},
});
PerfLogger.end('Generate static files');
return ssgResult;
}

View File

@ -18,7 +18,6 @@ import {
reloadSite,
reloadSitePlugin,
} from '../../server/site';
import {formatPluginName} from '../../server/plugins/pluginsUtils';
import type {StartCLIOptions} from './start';
import type {LoadedPlugin} from '@docusaurus/types';
@ -70,13 +69,10 @@ async function createLoadSiteParams({
export async function createReloadableSite(startParams: StartParams) {
const openUrlContext = await createOpenUrlContext(startParams);
const loadSiteParams = await PerfLogger.async('createLoadSiteParams', () =>
createLoadSiteParams(startParams),
);
let site = await PerfLogger.async('Load site', () =>
loadSite(loadSiteParams),
);
let site = await PerfLogger.async('Loading site', async () => {
const params = await createLoadSiteParams(startParams);
return loadSite(params);
});
const get = () => site;
@ -93,7 +89,7 @@ export async function createReloadableSite(startParams: StartParams) {
const reloadBase = async () => {
try {
const oldSite = site;
site = await PerfLogger.async('Reload site', () => reloadSite(site));
site = await PerfLogger.async('Reloading site', () => reloadSite(site));
if (oldSite.props.baseUrl !== site.props.baseUrl) {
printOpenUrlMessage();
}
@ -112,7 +108,7 @@ export async function createReloadableSite(startParams: StartParams) {
const reloadPlugin = async (plugin: LoadedPlugin) => {
try {
site = await PerfLogger.async(
`Reload site plugin ${formatPluginName(plugin)}`,
`Reloading site plugin ${plugin.name}@${plugin.options.id}`,
() => {
const pluginIdentifier = {name: plugin.name, id: plugin.options.id};
return reloadSitePlugin(site, pluginIdentifier);
@ -120,7 +116,7 @@ export async function createReloadableSite(startParams: StartParams) {
);
} catch (e) {
logger.error(
`Site plugin reload failure - Plugin ${formatPluginName(plugin)}`,
`Site plugin reload failure - Plugin ${plugin.name}@${plugin.options.id}`,
);
console.error(e);
}

View File

@ -13,7 +13,7 @@ import {
writePluginTranslations,
writeCodeTranslations,
type WriteTranslationsOptions,
loadPluginsDefaultCodeTranslationMessages,
getPluginsDefaultCodeTranslationMessages,
applyDefaultCodeTranslations,
} from '../server/translations/translations';
import {
@ -114,7 +114,7 @@ Available locales are: ${context.i18n.locales.join(',')}.`,
await getExtraSourceCodeFilePaths(),
);
const defaultCodeMessages = await loadPluginsDefaultCodeTranslationMessages(
const defaultCodeMessages = await getPluginsDefaultCodeTranslationMessages(
plugins,
);

View File

@ -36,7 +36,6 @@ exports[`load loads props for site with custom i18n path 1`] = `
"plugins": [
{
"content": undefined,
"defaultCodeTranslations": {},
"getClientModules": [Function],
"globalData": undefined,
"injectHtmlTags": [Function],
@ -53,7 +52,6 @@ exports[`load loads props for site with custom i18n path 1`] = `
{
"configureWebpack": [Function],
"content": undefined,
"defaultCodeTranslations": {},
"globalData": undefined,
"name": "docusaurus-mdx-fallback-plugin",
"options": {
@ -134,6 +132,5 @@ exports[`load loads props for site with custom i18n path 1`] = `
"pluginVersions": {},
"siteVersion": undefined,
},
"siteVersion": undefined,
}
`;

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {getAllClientModules} from '../clientModules';
import {loadClientModules} from '../clientModules';
import type {LoadedPlugin} from '@docusaurus/types';
const pluginEmpty = {
@ -33,14 +33,14 @@ const pluginHelloWorld = {
},
} as unknown as LoadedPlugin;
describe('getAllClientModules', () => {
describe('loadClientModules', () => {
it('loads an empty plugin', () => {
const clientModules = getAllClientModules([pluginEmpty]);
const clientModules = loadClientModules([pluginEmpty]);
expect(clientModules).toMatchInlineSnapshot(`[]`);
});
it('loads a non-empty plugin', () => {
const clientModules = getAllClientModules([pluginFooBar]);
const clientModules = loadClientModules([pluginFooBar]);
expect(clientModules).toMatchInlineSnapshot(`
[
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
@ -50,7 +50,7 @@ describe('getAllClientModules', () => {
});
it('loads multiple non-empty plugins', () => {
const clientModules = getAllClientModules([pluginFooBar, pluginHelloWorld]);
const clientModules = loadClientModules([pluginFooBar, pluginHelloWorld]);
expect(clientModules).toMatchInlineSnapshot(`
[
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
@ -62,7 +62,7 @@ describe('getAllClientModules', () => {
});
it('loads multiple non-empty plugins in different order', () => {
const clientModules = getAllClientModules([pluginHelloWorld, pluginFooBar]);
const clientModules = loadClientModules([pluginHelloWorld, pluginFooBar]);
expect(clientModules).toMatchInlineSnapshot(`
[
"/hello",
@ -74,7 +74,7 @@ describe('getAllClientModules', () => {
});
it('loads both empty and non-empty plugins', () => {
const clientModules = getAllClientModules([
const clientModules = loadClientModules([
pluginHelloWorld,
pluginEmpty,
pluginFooBar,
@ -90,7 +90,7 @@ describe('getAllClientModules', () => {
});
it('loads empty and non-empty in a different order', () => {
const clientModules = getAllClientModules([
const clientModules = loadClientModules([
pluginHelloWorld,
pluginFooBar,
pluginEmpty,

View File

@ -7,13 +7,13 @@
import path from 'path';
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
import {loadPluginVersion, createSiteMetadata} from '../siteMetadata';
import {getPluginVersion, loadSiteMetadata} from '../siteMetadata';
import type {LoadedPlugin} from '@docusaurus/types';
describe('loadPluginVersion', () => {
describe('getPluginVersion', () => {
it('detects external packages plugins versions', async () => {
await expect(
loadPluginVersion(
getPluginVersion(
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
// Make the plugin appear external.
path.join(__dirname, '..', '..', '..', '..', '..', '..', 'website'),
@ -23,7 +23,7 @@ describe('loadPluginVersion', () => {
it('detects project plugins versions', async () => {
await expect(
loadPluginVersion(
getPluginVersion(
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
// Make the plugin appear project local.
path.join(__dirname, '__fixtures__/siteMetadata'),
@ -32,14 +32,14 @@ describe('loadPluginVersion', () => {
});
it('detects local packages versions', async () => {
await expect(loadPluginVersion('/', '/')).resolves.toEqual({type: 'local'});
await expect(getPluginVersion('/', '/')).resolves.toEqual({type: 'local'});
});
});
describe('createSiteMetadata', () => {
it('throws if plugin versions mismatch', () => {
expect(() =>
createSiteMetadata({
describe('loadSiteMetadata', () => {
it('throws if plugin versions mismatch', async () => {
await expect(
loadSiteMetadata({
plugins: [
{
name: 'docusaurus-plugin-content-docs',
@ -50,9 +50,10 @@ describe('createSiteMetadata', () => {
},
},
] as LoadedPlugin[],
siteVersion: 'some-random-version',
siteDir: path.join(__dirname, '__fixtures__/siteMetadata'),
}),
).toThrow(`Invalid name=docusaurus-plugin-content-docs version number=1.0.0.
).rejects
.toThrow(`Invalid name=docusaurus-plugin-content-docs version number=1.0.0.
All official @docusaurus/* packages should have the exact same version as @docusaurus/core (number=${DOCUSAURUS_VERSION}).
Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?`);
});

View File

@ -9,12 +9,13 @@ import _ from 'lodash';
import logger from '@docusaurus/logger';
import {matchRoutes as reactRouterMatchRoutes} from 'react-router-config';
import {
addTrailingSlash,
parseURLPath,
removeTrailingSlash,
serializeURLPath,
flattenRoutes,
type URLPath,
} from '@docusaurus/utils';
import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils-common';
import type {RouteConfig, ReportingSeverity} from '@docusaurus/types';
function matchRoutes(routeConfig: RouteConfig[], pathname: string) {

View File

@ -12,7 +12,7 @@ import type {LoadedPlugin} from '@docusaurus/types';
* Runs the `getClientModules` lifecycle. The returned file paths are all
* absolute.
*/
export function getAllClientModules(plugins: LoadedPlugin[]): string[] {
export function loadClientModules(plugins: LoadedPlugin[]): string[] {
return plugins.flatMap(
(plugin) =>
plugin.getClientModules?.().map((p) => path.resolve(plugin.path, p)) ??

View File

@ -9,13 +9,11 @@ import {
DEFAULT_PARSE_FRONT_MATTER,
DEFAULT_STATIC_DIR_NAME,
DEFAULT_I18N_DIR_NAME,
addLeadingSlash,
addTrailingSlash,
removeTrailingSlash,
} from '@docusaurus/utils';
import {Joi, printWarning} from '@docusaurus/utils-validation';
import {
addTrailingSlash,
addLeadingSlash,
removeTrailingSlash,
} from '@docusaurus/utils-common';
import type {
DocusaurusConfig,
I18nConfig,

View File

@ -48,7 +48,6 @@ export async function createPluginActionsUtils({
dataDir,
`${docuHash('pluginRouteContextModule')}.json`,
);
// TODO not ideal place to generate that file
await generate(
'/',
pluginRouteContextModulePath,

View File

@ -12,7 +12,7 @@ import {
normalizePluginOptions,
normalizeThemeConfig,
} from '@docusaurus/utils-validation';
import {loadPluginVersion} from '../siteMetadata';
import {getPluginVersion} from '../siteMetadata';
import {ensureUniquePluginInstanceIds} from './pluginIds';
import {loadPluginConfigs, type NormalizedPluginConfig} from './configs';
import type {
@ -61,14 +61,14 @@ export async function initPlugins(
const pluginRequire = createRequire(context.siteConfigPath);
const pluginConfigs = await loadPluginConfigs(context);
async function doLoadPluginVersion(
async function doGetPluginVersion(
normalizedPluginConfig: NormalizedPluginConfig,
): Promise<PluginVersionInformation> {
if (normalizedPluginConfig.pluginModule?.path) {
const pluginPath = pluginRequire.resolve(
normalizedPluginConfig.pluginModule.path,
);
return loadPluginVersion(pluginPath, context.siteDir);
return getPluginVersion(pluginPath, context.siteDir);
}
return {type: 'local'};
}
@ -109,7 +109,7 @@ export async function initPlugins(
async function initializePlugin(
normalizedPluginConfig: NormalizedPluginConfig,
): Promise<InitializedPlugin> {
const pluginVersion: PluginVersionInformation = await doLoadPluginVersion(
const pluginVersion: PluginVersionInformation = await doGetPluginVersion(
normalizedPluginConfig,
);
const pluginOptions = doValidatePluginOptions(normalizedPluginConfig);

View File

@ -15,7 +15,6 @@ import {
aggregateAllContent,
aggregateGlobalData,
aggregateRoutes,
formatPluginName,
getPluginByIdentifier,
mergeGlobalData,
} from './pluginsUtils';
@ -74,57 +73,46 @@ async function executePluginContentLoading({
plugin: InitializedPlugin;
context: LoadContext;
}): Promise<LoadedPlugin> {
return PerfLogger.async(`Load ${formatPluginName(plugin)}`, async () => {
let content = await PerfLogger.async('loadContent()', () =>
plugin.loadContent?.(),
);
return PerfLogger.async(
`Plugins - single plugin content loading - ${plugin.name}@${plugin.options.id}`,
async () => {
let content = await plugin.loadContent?.();
content = await PerfLogger.async('translatePluginContent()', () =>
translatePluginContent({
content = await translatePluginContent({
plugin,
content,
context,
}),
);
});
const defaultCodeTranslations =
(await PerfLogger.async('getDefaultCodeTranslationMessages()', () =>
plugin.getDefaultCodeTranslationMessages?.(),
)) ?? {};
if (!plugin.contentLoaded) {
return {
...plugin,
content,
routes: [],
globalData: undefined,
};
}
const pluginActionsUtils = await createPluginActionsUtils({
plugin,
generatedFilesDir: context.generatedFilesDir,
baseUrl: context.siteConfig.baseUrl,
trailingSlash: context.siteConfig.trailingSlash,
});
await plugin.contentLoaded({
content,
actions: pluginActionsUtils.getActions(),
});
if (!plugin.contentLoaded) {
return {
...plugin,
content,
defaultCodeTranslations,
routes: [],
globalData: undefined,
routes: pluginActionsUtils.getRoutes(),
globalData: pluginActionsUtils.getGlobalData(),
};
}
const pluginActionsUtils = await createPluginActionsUtils({
plugin,
generatedFilesDir: context.generatedFilesDir,
baseUrl: context.siteConfig.baseUrl,
trailingSlash: context.siteConfig.trailingSlash,
});
await PerfLogger.async('contentLoaded()', () =>
// @ts-expect-error: should autofix with TS 5.4
plugin.contentLoaded({
content,
actions: pluginActionsUtils.getActions(),
}),
);
return {
...plugin,
content,
defaultCodeTranslations,
routes: pluginActionsUtils.getRoutes(),
globalData: pluginActionsUtils.getGlobalData(),
};
});
},
);
}
async function executeAllPluginsContentLoading({
@ -134,7 +122,7 @@ async function executeAllPluginsContentLoading({
plugins: InitializedPlugin[];
context: LoadContext;
}): Promise<LoadedPlugin[]> {
return PerfLogger.async(`Load plugins content`, () => {
return PerfLogger.async(`Plugins - all plugins content loading`, () => {
return Promise.all(
plugins.map((plugin) => executePluginContentLoading({plugin, context})),
);
@ -151,7 +139,7 @@ async function executePluginAllContentLoaded({
allContent: AllContent;
}): Promise<{routes: RouteConfig[]; globalData: unknown}> {
return PerfLogger.async(
`allContentLoaded() - ${formatPluginName(plugin)}`,
`Plugins - allContentLoaded - ${plugin.name}@${plugin.options.id}`,
async () => {
if (!plugin.allContentLoaded) {
return {routes: [], globalData: undefined};
@ -183,7 +171,7 @@ async function executeAllPluginsAllContentLoaded({
plugins: LoadedPlugin[];
context: LoadContext;
}): Promise<AllContentLoadedResult> {
return PerfLogger.async(`allContentLoaded()`, async () => {
return PerfLogger.async(`Plugins - allContentLoaded`, async () => {
const allContent = aggregateAllContent(plugins);
const routes: RouteConfig[] = [];
@ -211,9 +199,6 @@ async function executeAllPluginsAllContentLoaded({
});
}
// This merges plugins routes and global data created from both lifecycles:
// - contentLoaded()
// - allContentLoaded()
function mergeResults({
plugins,
allContentLoadedResult,
@ -247,9 +232,9 @@ export type LoadPluginsResult = {
export async function loadPlugins(
context: LoadContext,
): Promise<LoadPluginsResult> {
return PerfLogger.async('Load plugins', async () => {
return PerfLogger.async('Plugins - loadPlugins', async () => {
const initializedPlugins: InitializedPlugin[] = await PerfLogger.async(
'Init plugins',
'Plugins - initPlugins',
() => initPlugins(context),
);
@ -287,39 +272,36 @@ export async function reloadPlugin({
plugins: LoadedPlugin[];
context: LoadContext;
}): Promise<LoadPluginsResult> {
return PerfLogger.async(
`Reload plugin ${formatPluginName(pluginIdentifier)}`,
async () => {
const previousPlugin = getPluginByIdentifier({
plugins: previousPlugins,
pluginIdentifier,
});
const plugin = await executePluginContentLoading({
plugin: previousPlugin,
context,
});
return PerfLogger.async('Plugins - reloadPlugin', async () => {
const previousPlugin = getPluginByIdentifier({
plugins: previousPlugins,
pluginIdentifier,
});
const plugin = await executePluginContentLoading({
plugin: previousPlugin,
context,
});
/*
/*
// TODO Docusaurus v4 - upgrade to Node 20, use array.with()
const plugins = previousPlugins.with(
previousPlugins.indexOf(previousPlugin),
plugin,
);
*/
const plugins = [...previousPlugins];
plugins[previousPlugins.indexOf(previousPlugin)] = plugin;
const plugins = [...previousPlugins];
plugins[previousPlugins.indexOf(previousPlugin)] = plugin;
const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
plugins,
context,
});
const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
plugins,
context,
});
const {routes, globalData} = mergeResults({
plugins,
allContentLoadedResult,
});
const {routes, globalData} = mergeResults({
plugins,
allContentLoadedResult,
});
return {plugins, routes, globalData};
},
);
return {plugins, routes, globalData};
});
}

View File

@ -29,9 +29,7 @@ export function getPluginByIdentifier<P extends InitializedPlugin>({
);
if (!plugin) {
throw new Error(
logger.interpolate`Plugin not found for identifier ${formatPluginName(
pluginIdentifier,
)}`,
logger.interpolate`Plugin not found for identifier ${pluginIdentifier.name}@${pluginIdentifier.id}`,
);
}
return plugin;
@ -87,22 +85,3 @@ export function mergeGlobalData(...globalDataList: GlobalData[]): GlobalData {
return result;
}
// This is primarily useful for colored logging purpose
// Do not rely on this for logic
export function formatPluginName(
plugin: InitializedPlugin | PluginIdentifier,
): string {
let formattedName = plugin.name;
// Hacky way to reduce string size for logging purpose
formattedName = formattedName.replace('docusaurus-plugin-content-', '');
formattedName = formattedName.replace('docusaurus-plugin-', '');
formattedName = formattedName.replace('docusaurus-theme-', '');
formattedName = formattedName.replace('-plugin', '');
formattedName = logger.name(formattedName);
const id = 'id' in plugin ? plugin.id : plugin.options.id;
const formattedId = logger.subdue(id);
return `${formattedName}@${formattedId}`;
}

View File

@ -13,14 +13,14 @@ import {
} from '@docusaurus/utils';
import combinePromises from 'combine-promises';
import {loadSiteConfig} from './config';
import {getAllClientModules} from './clientModules';
import {loadClientModules} from './clientModules';
import {loadPlugins, reloadPlugin} from './plugins/plugins';
import {loadHtmlTags} from './htmlTags';
import {createSiteMetadata, loadSiteVersion} from './siteMetadata';
import {loadSiteMetadata} from './siteMetadata';
import {loadI18n} from './i18n';
import {
loadSiteCodeTranslations,
getPluginsDefaultCodeTranslations,
getPluginsDefaultCodeTranslationMessages,
} from './translations/translations';
import {PerfLogger} from '../utils';
import {generateSiteFiles} from './codegen/codegen';
@ -76,15 +76,9 @@ export async function loadContext(
} = params;
const generatedFilesDir = path.resolve(siteDir, GENERATED_FILES_DIR_NAME);
const {
siteVersion,
loadSiteConfig: {siteConfig: initialSiteConfig, siteConfigPath},
} = await combinePromises({
siteVersion: loadSiteVersion(siteDir),
loadSiteConfig: loadSiteConfig({
siteDir,
customConfigFilePath,
}),
const {siteConfig: initialSiteConfig, siteConfigPath} = await loadSiteConfig({
siteDir,
customConfigFilePath,
});
const i18n = await loadI18n(initialSiteConfig, {locale});
@ -113,7 +107,6 @@ export async function loadContext(
return {
siteDir,
siteVersion,
generatedFilesDir,
localizationDir,
siteConfig,
@ -125,14 +118,13 @@ export async function loadContext(
};
}
function createSiteProps(
async function createSiteProps(
params: LoadPluginsResult & {context: LoadContext},
): Props {
): Promise<Props> {
const {plugins, routes, context} = params;
const {
generatedFilesDir,
siteDir,
siteVersion,
siteConfig,
siteConfigPath,
outDir,
@ -144,12 +136,19 @@ function createSiteProps(
const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins);
const siteMetadata = createSiteMetadata({plugins, siteVersion});
const codeTranslations = {
...getPluginsDefaultCodeTranslations({plugins}),
...siteCodeTranslations,
};
const {codeTranslations, siteMetadata} = await combinePromises({
// TODO code translations should be loaded as part of LoadedPlugin?
codeTranslations: PerfLogger.async(
'Load - loadCodeTranslations',
async () => ({
...(await getPluginsDefaultCodeTranslationMessages(plugins)),
...siteCodeTranslations,
}),
),
siteMetadata: PerfLogger.async('Load - loadSiteMetadata', () =>
loadSiteMetadata({plugins, siteDir}),
),
});
handleDuplicateRoutes(routes, siteConfig.onDuplicateRoutes);
const routesPaths = getRoutesPaths(routes, baseUrl);
@ -158,7 +157,6 @@ function createSiteProps(
siteConfig,
siteConfigPath,
siteMetadata,
siteVersion,
siteDir,
outDir,
baseUrl,
@ -183,7 +181,7 @@ async function createSiteFiles({
site: Site;
globalData: GlobalData;
}) {
return PerfLogger.async('Create site files', async () => {
return PerfLogger.async('Load - createSiteFiles', async () => {
const {
props: {
plugins,
@ -196,7 +194,7 @@ async function createSiteFiles({
baseUrl,
},
} = site;
const clientModules = getAllClientModules(plugins);
const clientModules = loadClientModules(plugins);
await generateSiteFiles({
generatedFilesDir,
clientModules,
@ -218,11 +216,13 @@ async function createSiteFiles({
* it generates temp files in the `.docusaurus` folder for the bundler.
*/
export async function loadSite(params: LoadContextParams): Promise<Site> {
const context = await PerfLogger.async('Load context', () =>
loadContext(params),
);
PerfLogger.start('Load - loadContext');
const context = await loadContext(params);
PerfLogger.end('Load - loadContext');
PerfLogger.start('Load - loadPlugins');
const {plugins, routes, globalData} = await loadPlugins(context);
PerfLogger.end('Load - loadPlugins');
const props = await createSiteProps({plugins, routes, globalData, context});

View File

@ -14,7 +14,7 @@ import type {
SiteMetadata,
} from '@docusaurus/types';
async function loadPackageJsonVersion(
async function getPackageJsonVersion(
packageJsonPath: string,
): Promise<string | undefined> {
if (await fs.pathExists(packageJsonPath)) {
@ -24,20 +24,14 @@ async function loadPackageJsonVersion(
return undefined;
}
async function loadPackageJsonName(
async function getPackageJsonName(
packageJsonPath: string,
): Promise<string | undefined> {
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
return (require(packageJsonPath) as {name?: string}).name;
}
export async function loadSiteVersion(
siteDir: string,
): Promise<string | undefined> {
return loadPackageJsonVersion(path.join(siteDir, 'package.json'));
}
export async function loadPluginVersion(
export async function getPluginVersion(
pluginPath: string,
siteDir: string,
): Promise<PluginVersionInformation> {
@ -58,8 +52,8 @@ export async function loadPluginVersion(
}
return {
type: 'package',
name: await loadPackageJsonName(packageJsonPath),
version: await loadPackageJsonVersion(packageJsonPath),
name: await getPackageJsonName(packageJsonPath),
version: await getPackageJsonVersion(packageJsonPath),
};
}
potentialPluginPackageJsonDirectory = path.dirname(
@ -95,16 +89,18 @@ Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?
);
}
export function createSiteMetadata({
siteVersion,
export async function loadSiteMetadata({
plugins,
siteDir,
}: {
siteVersion: string | undefined;
plugins: LoadedPlugin[];
}): SiteMetadata {
siteDir: string;
}): Promise<SiteMetadata> {
const siteMetadata: SiteMetadata = {
docusaurusVersion: DOCUSAURUS_VERSION,
siteVersion,
siteVersion: await getPackageJsonVersion(
path.join(siteDir, 'package.json'),
),
pluginVersions: Object.fromEntries(
plugins
.filter(({version: {type}}) => type !== 'synthetic')

View File

@ -15,7 +15,7 @@ import {
readCodeTranslationFileContent,
type WriteTranslationsOptions,
localizePluginTranslationFile,
loadPluginsDefaultCodeTranslationMessages,
getPluginsDefaultCodeTranslationMessages,
applyDefaultCodeTranslations,
} from '../translations';
import type {
@ -537,7 +537,7 @@ describe('readCodeTranslationFileContent', () => {
});
});
describe('loadPluginsDefaultCodeTranslationMessages', () => {
describe('getPluginsDefaultCodeTranslationMessages', () => {
function createTestPlugin(
fn: InitializedPlugin['getDefaultCodeTranslationMessages'],
): InitializedPlugin {
@ -547,14 +547,14 @@ describe('loadPluginsDefaultCodeTranslationMessages', () => {
it('works for empty plugins', async () => {
const plugins: InitializedPlugin[] = [];
await expect(
loadPluginsDefaultCodeTranslationMessages(plugins),
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({});
});
it('works for 1 plugin without lifecycle', async () => {
const plugins: InitializedPlugin[] = [createTestPlugin(undefined)];
await expect(
loadPluginsDefaultCodeTranslationMessages(plugins),
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({});
});
@ -566,7 +566,7 @@ describe('loadPluginsDefaultCodeTranslationMessages', () => {
})),
];
await expect(
loadPluginsDefaultCodeTranslationMessages(plugins),
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({
a: '1',
b: '2',
@ -585,7 +585,7 @@ describe('loadPluginsDefaultCodeTranslationMessages', () => {
})),
];
await expect(
loadPluginsDefaultCodeTranslationMessages(plugins),
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({
a: '1',
b: '2',
@ -613,7 +613,7 @@ describe('loadPluginsDefaultCodeTranslationMessages', () => {
createTestPlugin(undefined),
];
await expect(
loadPluginsDefaultCodeTranslationMessages(plugins),
getPluginsDefaultCodeTranslationMessages(plugins),
).resolves.toEqual({
// merge, last plugin wins
b: '2',

View File

@ -20,7 +20,6 @@ import type {
TranslationFile,
CodeTranslations,
InitializedPlugin,
LoadedPlugin,
} from '@docusaurus/types';
export type WriteTranslationsOptions = {
@ -243,33 +242,17 @@ export async function localizePluginTranslationFile({
return translationFile;
}
export function mergeCodeTranslations(
codeTranslations: CodeTranslations[],
): CodeTranslations {
return codeTranslations.reduce(
(allCodeTranslations, current) => ({
...allCodeTranslations,
...current,
}),
{},
);
}
export async function loadPluginsDefaultCodeTranslationMessages(
export async function getPluginsDefaultCodeTranslationMessages(
plugins: InitializedPlugin[],
): Promise<CodeTranslations> {
const pluginsMessages = await Promise.all(
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
);
return mergeCodeTranslations(pluginsMessages);
}
export function getPluginsDefaultCodeTranslations({
plugins,
}: {
plugins: LoadedPlugin[];
}): CodeTranslations {
return mergeCodeTranslations(plugins.map((p) => p.defaultCodeTranslations));
return pluginsMessages.reduce(
(allMessages, pluginMessages) => ({...allMessages, ...pluginMessages}),
{},
);
}
export function applyDefaultCodeTranslations({

View File

@ -47,11 +47,12 @@ export async function loadAppRenderer({
}: {
serverBundlePath: string;
}): Promise<AppRenderer> {
const source = await PerfLogger.async(`Load server bundle`, () =>
fs.readFile(serverBundlePath),
);
console.log(`SSG - Load server bundle`);
PerfLogger.start(`SSG - Load server bundle`);
const source = await fs.readFile(serverBundlePath);
PerfLogger.end(`SSG - Load server bundle`);
PerfLogger.log(
`Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`,
`SSG - Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`,
);
const filename = path.basename(serverBundlePath);
@ -68,16 +69,14 @@ export async function loadAppRenderer({
require: createRequire(serverBundlePath),
};
const serverEntry = await PerfLogger.async(
`Evaluate server bundle`,
() =>
evaluate(
source,
/* filename: */ filename,
/* scope: */ globals,
/* includeGlobals: */ true,
) as {default?: AppRenderer},
);
PerfLogger.start(`SSG - Evaluate server bundle`);
const serverEntry = evaluate(
source,
/* filename: */ filename,
/* scope: */ globals,
/* includeGlobals: */ true,
) as {default?: AppRenderer};
PerfLogger.end(`SSG - Evaluate server bundle`);
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
throw new Error(

View File

@ -4,7 +4,6 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {AsyncLocalStorage} from 'async_hooks';
import logger from '@docusaurus/logger';
// For now this is a private env variable we use internally
@ -18,16 +17,6 @@ const Thresholds = {
red: 1000,
};
const PerfPrefix = logger.yellow(`[PERF] `);
// This is what enables to "see the parent stack" for each log
// Parent1 > Parent2 > Parent3 > child trace
const ParentPrefix = new AsyncLocalStorage<string>();
function applyParentPrefix(label: string) {
const parentPrefix = ParentPrefix.getStore();
return parentPrefix ? `${parentPrefix} > ${label}` : label;
}
type PerfLoggerAPI = {
start: (label: string) => void;
end: (label: string) => void;
@ -49,6 +38,8 @@ function createPerfLogger(): PerfLoggerAPI {
};
}
const prefix = logger.yellow(`[PERF] `);
const formatDuration = (duration: number): string => {
if (duration > Thresholds.red) {
return logger.red(`${(duration / 1000).toFixed(2)} seconds!`);
@ -63,7 +54,7 @@ function createPerfLogger(): PerfLoggerAPI {
if (duration < Thresholds.min) {
return;
}
console.log(`${PerfPrefix + label} - ${formatDuration(duration)}`);
console.log(`${prefix + label} - ${formatDuration(duration)}`);
};
const start: PerfLoggerAPI['start'] = (label) => performance.mark(label);
@ -71,18 +62,18 @@ function createPerfLogger(): PerfLoggerAPI {
const end: PerfLoggerAPI['end'] = (label) => {
const {duration} = performance.measure(label);
performance.clearMarks(label);
logDuration(applyParentPrefix(label), duration);
logDuration(label, duration);
};
const log: PerfLoggerAPI['log'] = (label: string) =>
console.log(PerfPrefix + applyParentPrefix(label));
console.log(prefix + label);
const async: PerfLoggerAPI['async'] = async (label, asyncFn) => {
const finalLabel = applyParentPrefix(label);
start(label);
const before = performance.now();
const result = await ParentPrefix.run(finalLabel, () => asyncFn());
const result = await asyncFn();
const duration = performance.now() - before;
logDuration(finalLabel, duration);
logDuration(label, duration);
return result;
};

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/eslint-plugin",
"version": "3.2.0",
"version": "3.0.0",
"description": "ESLint plugin to enforce best Docusaurus practices.",
"main": "lib/index.js",
"keywords": [

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/lqip-loader",
"version": "3.2.0",
"version": "3.0.0",
"description": "Low Quality Image Placeholders (LQIP) loader for webpack.",
"main": "lib/index.js",
"publishConfig": {
@ -17,7 +17,7 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.2.0",
"@docusaurus/logger": "3.0.0",
"file-loader": "^6.2.0",
"lodash": "^4.17.21",
"sharp": "^0.32.3",

View File

@ -1,6 +1,6 @@
{
"name": "stylelint-copyright",
"version": "3.2.0",
"version": "3.0.0",
"description": "Stylelint plugin to check CSS files for a copyright header.",
"main": "lib/index.js",
"license": "MIT",

View File

@ -16,7 +16,6 @@ architecting
Astro
atrule
Autoconverted
autofix
Autogen
autogen
autogenerating

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

View File

@ -1,112 +0,0 @@
---
title: Docusaurus 3.2
authors: [slorber]
tags: [release]
image: ./img/social-card.png
date: 2024-03-29
---
We are happy to announce **Docusaurus 3.2**.
The upgrade should be easy: as explained in our [release process documentation](/community/release-process), minor versions respect [Semantic Versioning](https://semver.org/).
![Docusaurus blog post social card](./img/social-card.png)
<!--truncate-->
import BrowserWindow from '@site/src/components/BrowserWindow';
import IframeWindow from '@site/src/components/BrowserWindow/IframeWindow';
import ErrorBoundaryTestButton from '@site/src/components/ErrorBoundaryTestButton';
## Highlights
### Faster builds
We worked hard to reduce the time it takes to build a Docusaurus site in production mode.
Between v3.1.0 and v3.2.0, several changes have been made, leading to significantly faster production builds for many sites.
Let's take an example. Our benchmark on the [React Native website upgrading to v3.2](https://github.com/facebook/react-native-website/pull/4072) reports the following results:
- 🔥 Cold builds: 95s ➡️ 66s (~30% faster)
- 🔥 Incremental builds: 55s ➡️ 22s (~60% faster)
The results will vary depending on your site's topology and the options you turned on, but we expect the largest sites will see the most significant improvements.
Note that this is only the beginning, and Docusaurus performance can still be significantly improved, notably the bundling time and the memory consumption. Track our [performance issue](https://github.com/facebook/docusaurus/issues/4765) for upcoming improvements.
<details>
<summary>What is the difference between a cold build and an incremental build?</summary>
A cold build is when the Docusaurus caches are empty, generally after running `docusaurus clear`.
An incremental build happens when you run another time the `docusaurus build` command. Docusaurus automatically tries to "re-use" computations from former builds to make subsequent builds faster. In practice it's based on [Webpack persistent caching](https://webpack.js.org/guides/build-performance/#persistent-cache). To enable incremental builds on your CI server, you can persist the `node_modules/.cache` folder across builds.
</details>
### Faster Dev Server
We also worked on improving the performance of the dev server, so that you can get a faster feedback when editing Markdown/MDX files.
The way we initially implemented content reloading wasn't great. For example, editing a blog post file would also trigger a useless reload of the unrelated docs plugin. From now on, when editing a plugin's content, only that plugin will reload. It's hard to measure precisely the impact of this change, but I estimate edits should appear in your browser at least 50% faster 🔥.
We plan to keep improving the speed of our dev server, with even more granular hot reloads, ensuring we don't run useless computations that would always keep giving the same result.
### MDX partials table-of-contents
With [#9684](https://github.com/facebook/docusaurus/pull/9684), Docusaurus is now able to render headings coming from an imported partial into the table-of-contents.
Docusaurus and MDX allows you to [import one Markdown file into another](/docs/markdown-features/react#importing-markdown). We usually call the imported Markdown file a "partial", and use the `_` prefix so that this file does not lead to the creation of a new page.
```md title="myDoc.mdx"
# My Doc
## Doc heading
Content is imported from another MDX file:
import ImportedDoc from './\_importedDoc.mdx';
<ImportedDoc />
```
```md title="_myPartial.mdx"
## Partial heading
Some paragraph
```
Previously, the heading `Partial heading` did not appear in the table-of-contents, but now it will!
### Blog improvements
We improved the blog plugin with several new options to make it even more powerful and flexible:
- [#9912](https://github.com/facebook/docusaurus/pull/9912): you can now display the last update time and author of a blog post, a feature the docs plugin already had.
- [#9886](https://github.com/facebook/docusaurus/pull/9886): a new `processBlogPosts` option allow you to filter/transform/sort blog posts.
- [#9838](https://github.com/facebook/docusaurus/pull/9838): a new `pageBasePath` option allows you to customize the blog pagination URL segment (`/blog/page/2`)
### Sitemap lastmod
With [#9954](https://github.com/facebook/docusaurus/pull/9954), the sitemap plugin has a new `lastmod` option that can now emit a `<lastmod>` tag on the XML. The value is read from the Git history by default, but can be overridden with docs and blog `last_update` front matter.
We also made it possible to opt-out of emitting `<priority>` and `<frequency>` tags, which are generally ignored by crawlers (notably [Google](https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping)).
We recommend using the following sitemap plugin config, that will become the default in Docusaurus V4:
```js
{
lastmod: 'date',
priority: null,
changefreq: null,
}
```
## Other changes
- [#9687](https://github.com/facebook/docusaurus/pull/9687): new Vercel Analytics plugin
- [#9681](https://github.com/facebook/docusaurus/pull/9681) and [#9442](https://github.com/facebook/docusaurus/pull/9442): `docusaurus swizzle` and `create-docusaurus` CLIs now ask users if they prefer to use TypeScript
- [#9928](https://github.com/facebook/docusaurus/pull/9928): new Icelandic translation
- [#9928](https://github.com/facebook/docusaurus/pull/9931): new `allContentLoaded` plugin lifecycle (experimental)
Check the **[3.2.0 changelog entry](/changelog/3.2.0)** for an exhaustive list of changes.

View File

@ -355,11 +355,6 @@ Our goal is that:
1. When a new pull request is made to `main`, there's an action that ensures the site builds successfully, without actually deploying. This job will be called `test-deploy`.
2. When a pull request is merged to the `main` branch or someone pushes to the `main` branch directly, it will be built and deployed to GitHub Pages. This job will be called `deploy`.
Here are two approaches to deploying your docs with GitHub Actions. Based on the location of your deployment branch (`gh-pages`), choose the relevant tab below:
- Source repo and deployment repo are the **same** repository.
- The deployment repo is a **remote** repository, different from the source. Instructions for this scenario assume [publishing source](https://help.github.com/en/github/working-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site) is the `gh-pages` branch.
```mdx-code-block
<Tabs>
<TabItem value="same" label="Same">

View File

@ -504,9 +504,9 @@ export default async function createConfigAsync() {
respectPrefersColorScheme: true,
},
announcementBar: {
id: 'announcementBar-v3.2', // Increment on change
id: 'announcementBar-3', // Increment on change
// content: `⭐️ If you like Docusaurus, give it a star on <a target="_blank" rel="noopener noreferrer" href="https://github.com/facebook/docusaurus">GitHub</a> and follow us on <a target="_blank" rel="noopener noreferrer" href="https://twitter.com/docusaurus">Twitter ${TwitterSvg}</a>`,
content: `🎉️ <b><a target="_blank" href="https://docusaurus.io/blog/releases/3.2">Docusaurus v3.2</a> is out!</b> 🥳️`,
content: `🎉️ <b><a target="_blank" href="https://docusaurus.io/blog/releases/3.0">Docusaurus v3.0</a> is now out!</b> 🥳️`,
},
prism: {
additionalLanguages: [

View File

@ -1,6 +1,6 @@
{
"name": "website",
"version": "3.2.0",
"version": "3.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -36,19 +36,19 @@
"dependencies": {
"@crowdin/cli": "^3.13.0",
"@crowdin/crowdin-api-client": "^1.29.5",
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/plugin-client-redirects": "3.2.0",
"@docusaurus/plugin-ideal-image": "3.2.0",
"@docusaurus/plugin-pwa": "3.2.0",
"@docusaurus/preset-classic": "3.2.0",
"@docusaurus/remark-plugin-npm2yarn": "3.2.0",
"@docusaurus/theme-classic": "3.2.0",
"@docusaurus/theme-common": "3.2.0",
"@docusaurus/theme-live-codeblock": "3.2.0",
"@docusaurus/theme-mermaid": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/core": "3.0.0",
"@docusaurus/logger": "3.0.0",
"@docusaurus/plugin-client-redirects": "3.0.0",
"@docusaurus/plugin-ideal-image": "3.0.0",
"@docusaurus/plugin-pwa": "3.0.0",
"@docusaurus/preset-classic": "3.0.0",
"@docusaurus/remark-plugin-npm2yarn": "3.0.0",
"@docusaurus/theme-classic": "3.0.0",
"@docusaurus/theme-common": "3.0.0",
"@docusaurus/theme-live-codeblock": "3.0.0",
"@docusaurus/theme-mermaid": "3.0.0",
"@docusaurus/utils": "3.0.0",
"@docusaurus/utils-common": "3.0.0",
"@popperjs/core": "^2.11.8",
"@swc/core": "1.2.197",
"clsx": "^2.0.0",
@ -83,8 +83,8 @@
]
},
"devDependencies": {
"@docusaurus/eslint-plugin": "3.2.0",
"@docusaurus/tsconfig": "3.2.0",
"@docusaurus/eslint-plugin": "3.0.0",
"@docusaurus/tsconfig": "3.0.0",
"@types/color": "^3.0.4",
"@types/jest": "^29.5.3",
"cross-env": "^7.0.3",

View File

@ -222,11 +222,9 @@ function TopBanner() {
<div className={styles.topBanner}>
<div className={styles.topBannerTitle}>
{'🎉\xa0'}
<Link to="/blog/releases/3.2" className={styles.topBannerTitleText}>
<Translate
id="homepage.banner.launch.newVersion"
values={{newVersion: '3.2'}}>
{'Docusaurus\xa0{newVersion} is\xa0out!'}
<Link to="/blog/releases/3.0" className={styles.topBannerTitleText}>
<Translate id="homepage.banner.launch.3.0">
{'Docusaurus\xa03.0 is\xa0out!'}
</Translate>
</Link>
{'\xa0🥳'}

View File

@ -1,28 +0,0 @@
---
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 'react-medium-image-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 ES modules that can be imported, or CommonJS that can be `require`'d.
- The theme code is built with Webpack. They can be provided as ESM—following React conventions.
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.mdx#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

@ -1,184 +0,0 @@
---
description: How the Docusaurus client is structured
---
# Client architecture
## Theme aliases {#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`](../swizzling.mdx#wrapping) 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.
:::warning
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!
## Client modules {#client-modules}
Client modules are part of your site's bundle, just like theme components. However, they are usually side-effect-ful. Client modules are anything that can be `import`ed by Webpack—CSS, JS, etc. JS scripts usually work on the global context, like registering event listeners, creating global variables...
These modules are imported globally before React even renders the initial UI.
```js title="@docusaurus/core/App.tsx"
// How it works under the hood
import '@generated/client-modules';
```
Plugins and sites can both declare client modules, through [`getClientModules`](../api/plugin-methods/lifecycle-apis.mdx#getClientModules) and [`siteConfig.clientModules`](../api/docusaurus.config.js.mdx#clientModules), respectively.
Client modules are called during server-side rendering as well, so remember to check the [execution environment](./ssg.mdx#escape-hatches) before accessing client-side globals.
```js title="mySiteGlobalJs.js"
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
if (ExecutionEnvironment.canUseDOM) {
// As soon as the site loads in the browser, register a global event listener
window.addEventListener('keydown', (e) => {
if (e.code === 'Period') {
location.assign(location.href.replace('.com', '.dev'));
}
});
}
```
CSS stylesheets imported as client modules are [global](../styling-layout.mdx#global-styles).
```css title="mySiteGlobalCss.css"
/* This stylesheet is global. */
.globalSelector {
color: red;
}
```
### Client module lifecycles {#client-module-lifecycles}
Besides introducing side-effects, client modules can optionally export two lifecycle functions: `onRouteUpdate` and `onRouteDidUpdate`.
Because Docusaurus builds a single-page application, `script` tags will only be executed the first time the page loads, but will not re-execute on page transitions. These lifecycles are useful if you have some imperative JS logic that should execute every time a new page has loaded, e.g., to manipulate DOM elements, to send analytics data, etc.
For every route transition, there will be several important timings:
1. The user clicks a link, which causes the router to change its current location.
2. Docusaurus preloads the next route's assets, while keeping displaying the current page's content.
3. The next route's assets have loaded.
4. The new location's route component gets rendered to DOM.
`onRouteUpdate` will be called at event (2), and `onRouteDidUpdate` will be called at (4). They both receive the current location and the previous location (which can be `null`, if this is the first screen).
`onRouteUpdate` can optionally return a "cleanup" callback, which will be called at (3). For example, if you want to display a progress bar, you can start a timeout in `onRouteUpdate`, and clear the timeout in the callback. (The classic theme already provides an `nprogress` integration this way.)
Note that the new page's DOM is only available during event (4). If you need to manipulate the new page's DOM, you'll likely want to use `onRouteDidUpdate`, which will be fired as soon as the DOM on the new page has mounted.
```js title="myClientModule.js"
export function onRouteDidUpdate({location, previousLocation}) {
// Don't execute if we are still on the same page; the lifecycle may be fired
// because the hash changes (e.g. when navigating between headings)
if (location.pathname !== previousLocation?.pathname) {
const title = document.getElementsByTagName('h1')[0];
if (title) {
title.innerText += '❤️';
}
}
}
export function onRouteUpdate({location, previousLocation}) {
if (location.pathname !== previousLocation?.pathname) {
const progressBarTimeout = window.setTimeout(() => {
nprogress.start();
}, delay);
return () => window.clearTimeout(progressBarTimeout);
}
return undefined;
}
```
Or, if you are using TypeScript and you want to leverage contextual typing:
```ts title="myClientModule.ts"
import type {ClientModule} from '@docusaurus/types';
const module: ClientModule = {
onRouteUpdate({location, previousLocation}) {
// ...
},
onRouteDidUpdate({location, previousLocation}) {
// ...
},
};
export default module;
```
Both lifecycles will fire on first render, but they will not fire on server-side, so you can safely access browser globals in them.
:::tip Prefer using React
Client module lifecycles are purely imperative, and you can't use React hooks or access React contexts within them. If your operations are state-driven or involve complicated DOM manipulations, you should consider [swizzling components](../swizzling.mdx) instead.
:::

View File

@ -1,11 +0,0 @@
# 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';
<DocCardList />
```
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.mdx) or other architecture details. Don't panic if you don't understand everything😉

View File

@ -1,129 +0,0 @@
# Plugins
Plugins are the building blocks of features in a Docusaurus 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.mdx).
### Function definition {#function-definition}
You can use a plugin as a function directly included in the Docusaurus config file:
```js title="docusaurus.config.js"
export default {
// ...
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"
export default {
// ...
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/index.js"
export default 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 {#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 {#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.mdx#addRoute) or [`setGlobalData`](../api/plugin-methods/lifecycle-apis.mdx#setGlobalData). This data has to be _serialized_ to plain strings, because [plugins and themes run in different environments](./architecture.mdx). 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"
export default {
// 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"
export default {
// 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.mdx#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.mdx#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`.

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