mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-07 05:12:31 +02:00
Merge branch 'main' into ozaki/showcase
This commit is contained in:
commit
c89f8bed6e
288 changed files with 24854 additions and 3432 deletions
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
|
@ -15,4 +15,4 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # 4.1.3
|
||||
uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # 4.2.5
|
||||
|
|
153
CHANGELOG.md
153
CHANGELOG.md
|
@ -1,5 +1,158 @@
|
|||
# Docusaurus 2 Changelog
|
||||
|
||||
## 3.2.1 (2024-04-04)
|
||||
|
||||
#### :bug: Bug Fix
|
||||
|
||||
- `docusaurus`
|
||||
- [#10012](https://github.com/facebook/docusaurus/pull/10012) fix(core): fix configurePostCss v3.2 regression ([@slorber](https://github.com/slorber))
|
||||
|
||||
#### :memo: Documentation
|
||||
|
||||
- [#9980](https://github.com/facebook/docusaurus/pull/9980) docs: remove old github action description ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- [#10014](https://github.com/facebook/docusaurus/pull/10014) docs(website): fix SEO docs headTags example ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- [#10004](https://github.com/facebook/docusaurus/pull/10004) docs(website): Announce v3.2 on website/homepage ([@slorber](https://github.com/slorber))
|
||||
|
||||
#### :robot: Dependencies
|
||||
|
||||
- [#10006](https://github.com/facebook/docusaurus/pull/10006) chore(deps): bump actions/dependency-review-action from 4.2.4 to 4.2.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
|
||||
#### Committers: 2
|
||||
|
||||
- Sébastien Lorber ([@slorber](https://github.com/slorber))
|
||||
- ozaki ([@OzakIOne](https://github.com/OzakIOne))
|
||||
|
||||
## 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
|
||||
|
|
|
@ -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',
|
||||
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic?file=%2FREADME.md&privacy=public',
|
||||
'codesandbox-ts':
|
||||
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic-typescript?file=%2FREADME.md',
|
||||
'https://codesandbox.io/p/sandbox/github/facebook/docusaurus/tree/main/examples/classic-typescript?file=%2FREADME.md&privacy=public',
|
||||
|
||||
// Slow to load
|
||||
// stackblitz: 'https://stackblitz.com/github/facebook/docusaurus/tree/main/examples/classic',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "new.docusaurus.io",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npx --package netlify-cli netlify dev"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "argos",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Argos visual diff tests",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
|
10
examples/classic-typescript/package.json
generated
10
examples/classic-typescript/package.json
generated
|
@ -16,8 +16,8 @@
|
|||
"dev": "docusaurus start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/preset-classic": "3.1.1",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/preset-classic": "3.2.0",
|
||||
"@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.1.1",
|
||||
"@docusaurus/tsconfig": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/tsconfig": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"typescript": "~5.2.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
|
2286
examples/classic-typescript/yarn.lock
generated
2286
examples/classic-typescript/yarn.lock
generated
File diff suppressed because it is too large
Load diff
8
examples/classic/package.json
generated
8
examples/classic/package.json
generated
|
@ -15,8 +15,8 @@
|
|||
"dev": "docusaurus start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/preset-classic": "3.1.1",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/preset-classic": "3.2.0",
|
||||
"@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.1.1",
|
||||
"@docusaurus/types": "3.1.1"
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
2278
examples/classic/yarn.lock
generated
2278
examples/classic/yarn.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"useNx": false,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "create-docusaurus",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Create Docusaurus apps easily.",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
|
@ -22,8 +22,8 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"commander": "^5.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docusaurus-2-classic-typescript-template",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
@ -15,8 +15,8 @@
|
|||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/preset-classic": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/preset-classic": "3.2.1",
|
||||
"@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.0.0",
|
||||
"@docusaurus/tsconfig": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/tsconfig": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"typescript": "~5.2.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docusaurus-2-classic-template",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
@ -14,8 +14,8 @@
|
|||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/preset-classic": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/preset-classic": "3.2.1",
|
||||
"@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.0.0",
|
||||
"@docusaurus/types": "3.0.0"
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/cssnano-preset",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Advanced cssnano preset for maximum optimization.",
|
||||
"main": "lib/index.js",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/logger",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "An encapsulated logger for semantically formatting console messages.",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/mdx-loader",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Docusaurus Loader for MDX",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@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.0.0",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@types/escape-html": "^1.0.2",
|
||||
"@types/mdast": "^4.0.2",
|
||||
"@types/stringify-object": "^3.3.1",
|
||||
|
|
|
@ -65,6 +65,21 @@ some **markdown** *content*
|
|||
|
||||
# contentTitle 1
|
||||
|
||||
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*
|
||||
`);
|
||||
|
||||
|
|
|
@ -34,17 +34,24 @@ const plugin: Plugin = function plugin(
|
|||
const {toString} = await import('mdast-util-to-string');
|
||||
const {visit, EXIT} = await import('unist-util-visit');
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
// We only handle contentTitle when it's above the first thematic break
|
||||
if (node.type === 'thematicBreak') {
|
||||
return EXIT;
|
||||
}
|
||||
return undefined;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/module-type-aliases",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router-config": "*",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-client-redirects",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/utils-common": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"eta": "^2.2.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.0.0"
|
||||
"@docusaurus/types": "3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {removeTrailingSlash} from '@docusaurus/utils';
|
||||
import {removeTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import collectRedirects from '../collectRedirects';
|
||||
import {validateOptions} from '../options';
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils';
|
||||
import {applyTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {
|
||||
applyTrailingSlash,
|
||||
addTrailingSlash,
|
||||
removeTrailingSlash,
|
||||
} from '@docusaurus/utils-common';
|
||||
import {
|
||||
createFromExtensionsRedirects,
|
||||
createToExtensionsRedirects,
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
addTrailingSlash,
|
||||
removeSuffix,
|
||||
removeTrailingSlash,
|
||||
} from '@docusaurus/utils';
|
||||
} from '@docusaurus/utils-common';
|
||||
import type {RedirectItem} from './types';
|
||||
|
||||
const ExtensionAdditionalMessage =
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {removePrefix, addLeadingSlash} from '@docusaurus/utils';
|
||||
import {addLeadingSlash, removePrefix} from '@docusaurus/utils-common';
|
||||
import collectRedirects from './collectRedirects';
|
||||
import writeRedirectFiles, {
|
||||
toRedirectFiles,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-blog",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/mdx-loader": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Docs plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": false,
|
||||
|
@ -35,13 +35,14 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/mdx-loader": "3.2.1",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@types/react-router-config": "^5.0.7",
|
||||
"combine-promises": "^1.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {addTrailingSlash} from '@docusaurus/utils';
|
||||
import {addTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {createDocsByIdIndex, toCategoryIndexMatcherParam} from '../docs';
|
||||
import type {
|
||||
SidebarItemDoc,
|
||||
|
|
|
@ -5,12 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
addLeadingSlash,
|
||||
addTrailingSlash,
|
||||
isValidPathname,
|
||||
resolvePathname,
|
||||
} from '@docusaurus/utils';
|
||||
import {isValidPathname, resolvePathname} from '@docusaurus/utils';
|
||||
import {addLeadingSlash, addTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {
|
||||
DefaultNumberPrefixParser,
|
||||
stripPathNumberPrefixes,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-pages",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/mdx-loader": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/mdx-loader": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"tslib": "^2.6.0",
|
||||
"webpack": "^5.88.1"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-debug",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"react-json-view-lite": "^1.2.0",
|
||||
"tslib": "^2.6.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-analytics",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-gtag",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-tag-manager",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-ideal-image",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/lqip-loader": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/lqip-loader": "3.2.1",
|
||||
"@docusaurus/responsive-loader": "^1.7.0",
|
||||
"@docusaurus/theme-translations": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/theme-translations": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@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.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"fs-extra": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-pwa",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/theme-common": "3.2.1",
|
||||
"@docusaurus/theme-translations": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"fs-extra": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-sitemap",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"sitemap": "^7.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-vercel-analytics",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@vercel/analytics": "^1.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/preset-classic",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Classic preset for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,19 +18,19 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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"
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/plugin-content-blog": "3.2.1",
|
||||
"@docusaurus/plugin-content-docs": "3.2.1",
|
||||
"@docusaurus/plugin-content-pages": "3.2.1",
|
||||
"@docusaurus/plugin-debug": "3.2.1",
|
||||
"@docusaurus/plugin-google-analytics": "3.2.1",
|
||||
"@docusaurus/plugin-google-gtag": "3.2.1",
|
||||
"@docusaurus/plugin-google-tag-manager": "3.2.1",
|
||||
"@docusaurus/plugin-sitemap": "3.2.1",
|
||||
"@docusaurus/theme-classic": "3.2.1",
|
||||
"@docusaurus/theme-common": "3.2.1",
|
||||
"@docusaurus/theme-search-algolia": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/remark-plugin-npm2yarn",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Remark plugin for converting npm commands to Yarn commands as tabs.",
|
||||
"main": "lib/index.js",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-classic",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Classic theme for Docusaurus",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-classic.d.ts",
|
||||
|
@ -20,19 +20,19 @@
|
|||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@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/plugin-content-showcase": "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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/mdx-loader": "3.2.1",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/plugin-content-blog": "3.2.1",
|
||||
"@docusaurus/plugin-content-docs": "3.2.1",
|
||||
"@docusaurus/plugin-content-pages": "3.2.1",
|
||||
"@docusaurus/plugin-content-showcase": "3.2.1",
|
||||
"@docusaurus/theme-common": "3.2.1",
|
||||
"@docusaurus/theme-translations": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"copy-text-to-clipboard": "^3.2.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-common",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.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",
|
||||
"@docusaurus/mdx-loader": "3.2.1",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/plugin-content-blog": "3.2.1",
|
||||
"@docusaurus/plugin-content-docs": "3.2.1",
|
||||
"@docusaurus/plugin-content-pages": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router-config": "*",
|
||||
|
@ -47,8 +47,8 @@
|
|||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"schema-dts": "^1.1.2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-live-codeblock",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/theme-common": "3.0.0",
|
||||
"@docusaurus/theme-translations": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/theme-common": "3.2.1",
|
||||
"@docusaurus/theme-translations": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@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.0.0",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@types/buble": "^0.20.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-mermaid",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/theme-common": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"mermaid": "^10.4.0",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-search-algolia",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Algolia search component for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": [
|
||||
|
@ -34,13 +34,13 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.5.2",
|
||||
"@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",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/plugin-content-docs": "3.2.1",
|
||||
"@docusaurus/theme-common": "3.2.1",
|
||||
"@docusaurus/theme-translations": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"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.0.0"
|
||||
"@docusaurus/module-type-aliases": "3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-translations",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/core": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/tsconfig",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Docusaurus base TypeScript configuration.",
|
||||
"main": "tsconfig.json",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/types",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Common types for Docusaurus packages.",
|
||||
"types": "./src/index.d.ts",
|
||||
"publishConfig": {
|
||||
|
|
1
packages/docusaurus-types/src/context.d.ts
vendored
1
packages/docusaurus-types/src/context.d.ts
vendored
|
@ -31,6 +31,7 @@ export type GlobalData = {[pluginName: string]: {[pluginId: string]: unknown}};
|
|||
|
||||
export type LoadContext = {
|
||||
siteDir: string;
|
||||
siteVersion: string | undefined;
|
||||
generatedFilesDir: string;
|
||||
siteConfig: DocusaurusConfig;
|
||||
siteConfigPath: string;
|
||||
|
|
5
packages/docusaurus-types/src/plugin.d.ts
vendored
5
packages/docusaurus-types/src/plugin.d.ts
vendored
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {TranslationFile} from './i18n';
|
||||
import type {CodeTranslations, TranslationFile} from './i18n';
|
||||
import type {RuleSetRule, Configuration as WebpackConfiguration} from 'webpack';
|
||||
import type {CustomizeRuleString} from 'webpack-merge/dist/types';
|
||||
import type {CommanderStatic} from 'commander';
|
||||
|
@ -183,6 +183,9 @@ export type InitializedPlugin = Plugin & {
|
|||
|
||||
export type LoadedPlugin = InitializedPlugin & {
|
||||
readonly content: unknown;
|
||||
readonly globalData: unknown;
|
||||
readonly routes: RouteConfig[];
|
||||
readonly defaultCodeTranslations: CodeTranslations;
|
||||
};
|
||||
|
||||
export type PluginModule<Content = unknown> = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils-common",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Common (Node/Browser) utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
|
||||
import applyTrailingSlash, {
|
||||
addTrailingSlash,
|
||||
type ApplyTrailingSlashParams,
|
||||
addLeadingSlash,
|
||||
removeTrailingSlash,
|
||||
} from '../applyTrailingSlash';
|
||||
|
||||
function params(
|
||||
|
@ -176,3 +179,30 @@ 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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* 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');
|
||||
});
|
||||
});
|
|
@ -5,6 +5,7 @@
|
|||
* 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<
|
||||
|
@ -12,6 +13,10 @@ 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,
|
||||
|
@ -24,13 +29,6 @@ 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);
|
||||
}
|
||||
|
@ -55,3 +53,13 @@ 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, '/');
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@ 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';
|
||||
|
|
30
packages/docusaurus-utils-common/src/stringUtils.ts
Normal file
30
packages/docusaurus-utils-common/src/stringUtils.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils-validation",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Node validation utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
@ -18,8 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"joi": "^17.9.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"tslib": "^2.6.0"
|
||||
|
|
|
@ -5,12 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
isValidPathname,
|
||||
DEFAULT_PLUGIN_ID,
|
||||
type Tag,
|
||||
addLeadingSlash,
|
||||
} from '@docusaurus/utils';
|
||||
import {isValidPathname, DEFAULT_PLUGIN_ID, type Tag} from '@docusaurus/utils';
|
||||
import {addLeadingSlash} from '@docusaurus/utils-common';
|
||||
import Joi from './Joi';
|
||||
import {JoiFrontMatter} from './JoiFrontMatter';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Node utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
@ -18,7 +18,8 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
|
@ -41,7 +42,7 @@
|
|||
"node": ">=18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/micromatch": "^4.0.2",
|
||||
|
|
|
@ -7,34 +7,7 @@
|
|||
|
||||
import {jest} from '@jest/globals';
|
||||
import _ from 'lodash';
|
||||
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');
|
||||
});
|
||||
});
|
||||
import {mapAsyncSequential, findAsyncSequential} from '../jsUtils';
|
||||
|
||||
describe('mapAsyncSequential', () => {
|
||||
function sleep(timeout: number): Promise<void> {
|
||||
|
|
|
@ -10,9 +10,6 @@ import {
|
|||
getEditUrl,
|
||||
fileToPath,
|
||||
isValidPathname,
|
||||
addTrailingSlash,
|
||||
addLeadingSlash,
|
||||
removeTrailingSlash,
|
||||
resolvePathname,
|
||||
encodePath,
|
||||
buildSshUrl,
|
||||
|
@ -207,33 +204,6 @@ 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({
|
||||
|
|
|
@ -12,6 +12,10 @@ 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).
|
||||
|
@ -38,7 +42,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, createHash('md5').update(content).digest('hex'));
|
||||
fileHash.set(filepath, hashContent(content));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -50,11 +54,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 = createHash('md5').update(lastContent).digest('hex');
|
||||
lastHash = hashContent(lastContent);
|
||||
fileHash.set(filepath, lastHash);
|
||||
}
|
||||
|
||||
const currentHash = createHash('md5').update(content).digest('hex');
|
||||
const currentHash = hashContent(content);
|
||||
|
||||
if (lastHash !== currentHash) {
|
||||
await fs.outputFile(filepath, content);
|
||||
|
|
|
@ -6,7 +6,16 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
import shell from 'shelljs';
|
||||
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);
|
||||
|
||||
/** Custom error thrown when git is not found in `PATH`. */
|
||||
export class GitNotFoundError extends Error {}
|
||||
|
@ -86,33 +95,41 @@ export async function getFileCommitDate(
|
|||
timestamp: number;
|
||||
author?: string;
|
||||
}> {
|
||||
if (!shell.which('git')) {
|
||||
if (!hasGit()) {
|
||||
throw new GitNotFoundError(
|
||||
`Failed to retrieve git history for "${file}" because git is not installed.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!shell.test('-f', file)) {
|
||||
if (!(await fs.pathExists(file))) {
|
||||
throw new Error(
|
||||
`Failed to retrieve git history for "${file}" because the file does not exist.`,
|
||||
);
|
||||
}
|
||||
|
||||
// We add a "RESULT:" prefix to make parsing easier
|
||||
// See why: https://github.com/facebook/docusaurus/pull/10022
|
||||
const resultFormat = includeAuthor ? 'RESULT:%ct,%an' : 'RESULT:%ct';
|
||||
|
||||
const args = [
|
||||
`--format=%ct${includeAuthor ? ',%an' : ''}`,
|
||||
`--format=${resultFormat}`,
|
||||
'--max-count=1',
|
||||
age === 'oldest' ? '--follow --diff-filter=A' : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
const command = `git -c log.showSignature=false log ${args} -- "${path.basename(
|
||||
file,
|
||||
)}"`;
|
||||
|
||||
const result = await new Promise<{
|
||||
code: number;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
}>((resolve) => {
|
||||
shell.exec(
|
||||
`git log ${args} -- "${path.basename(file)}"`,
|
||||
command,
|
||||
{
|
||||
// Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
|
||||
cwd: path.dirname(file),
|
||||
|
@ -129,10 +146,12 @@ export async function getFileCommitDate(
|
|||
`Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
|
||||
);
|
||||
}
|
||||
let regex = /^(?<timestamp>\d+)$/;
|
||||
if (includeAuthor) {
|
||||
regex = /^(?<timestamp>\d+),(?<author>.+)$/;
|
||||
}
|
||||
|
||||
// We only parse the output line starting with our "RESULT:" prefix
|
||||
// See why https://github.com/facebook/docusaurus/pull/10022
|
||||
const regex = includeAuthor
|
||||
? /(?:^|\n)RESULT:(?<timestamp>\d+),(?<author>.+)(?:$|\n)/
|
||||
: /(?:^|\n)RESULT:(?<timestamp>\d+)(?:$|\n)/;
|
||||
|
||||
const output = result.stdout.trim();
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
|
||||
import path from 'path';
|
||||
import Micromatch from 'micromatch'; // Note: Micromatch is used by Globby
|
||||
import {addSuffix} from './jsUtils';
|
||||
|
||||
import {addSuffix} from '@docusaurus/utils-common';
|
||||
/** A re-export of the globby instance. */
|
||||
export {default as Globby} from 'globby';
|
||||
|
||||
|
|
|
@ -35,12 +35,7 @@ export {
|
|||
getPluginI18nPath,
|
||||
localizePath,
|
||||
} from './i18nUtils';
|
||||
export {
|
||||
removeSuffix,
|
||||
removePrefix,
|
||||
mapAsyncSequential,
|
||||
findAsyncSequential,
|
||||
} from './jsUtils';
|
||||
export {mapAsyncSequential, findAsyncSequential} from './jsUtils';
|
||||
export {
|
||||
normalizeUrl,
|
||||
getEditUrl,
|
||||
|
@ -50,9 +45,6 @@ export {
|
|||
resolvePathname,
|
||||
parseURLPath,
|
||||
serializeURLPath,
|
||||
addLeadingSlash,
|
||||
addTrailingSlash,
|
||||
removeTrailingSlash,
|
||||
hasSSHProtocol,
|
||||
buildHttpsUrl,
|
||||
buildSshUrl,
|
||||
|
|
|
@ -5,30 +5,6 @@
|
|||
* 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.
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
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
|
||||
|
@ -232,22 +231,6 @@ 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,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@docusaurus/core",
|
||||
"description": "Easy to Maintain Open Source Documentation Websites",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"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.0.0",
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/mdx-loader": "3.0.0",
|
||||
"@docusaurus/cssnano-preset": "3.2.1",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"@docusaurus/mdx-loader": "3.2.1",
|
||||
"@docusaurus/react-loadable": "5.5.2",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/utils-common": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/utils": "3.2.1",
|
||||
"@docusaurus/utils-common": "3.2.1",
|
||||
"@docusaurus/utils-validation": "3.2.1",
|
||||
"@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",
|
||||
"eval": "^0.1.8",
|
||||
"eta": "^2.2.0",
|
||||
"eval": "^0.1.8",
|
||||
"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.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.1",
|
||||
"@docusaurus/types": "3.2.1",
|
||||
"@total-typescript/shoehorn": "^0.1.2",
|
||||
"@types/detect-port": "^1.3.3",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
|
|
|
@ -64,23 +64,15 @@ export async function build(
|
|||
process.on(sig, () => process.exit());
|
||||
});
|
||||
|
||||
async function tryToBuildLocale({
|
||||
locale,
|
||||
isLastLocale,
|
||||
}: {
|
||||
locale: string;
|
||||
isLastLocale: boolean;
|
||||
}) {
|
||||
async function tryToBuildLocale({locale}: {locale: string}) {
|
||||
try {
|
||||
PerfLogger.start(`Building site for locale ${locale}`);
|
||||
await buildLocale({
|
||||
siteDir,
|
||||
locale,
|
||||
cliOptions,
|
||||
forceTerminate,
|
||||
isLastLocale,
|
||||
});
|
||||
PerfLogger.end(`Building site for locale ${locale}`);
|
||||
await PerfLogger.async(`${logger.name(locale)}`, () =>
|
||||
buildLocale({
|
||||
siteDir,
|
||||
locale,
|
||||
cliOptions,
|
||||
}),
|
||||
);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
logger.interpolate`Unable to build website for locale name=${locale}.`,
|
||||
|
@ -91,20 +83,28 @@ export async function build(
|
|||
}
|
||||
}
|
||||
|
||||
PerfLogger.start(`Get locales to build`);
|
||||
const locales = await getLocalesToBuild({siteDir, cliOptions});
|
||||
PerfLogger.end(`Get locales to build`);
|
||||
const locales = await PerfLogger.async('Get locales to build', () =>
|
||||
getLocalesToBuild({siteDir, cliOptions}),
|
||||
);
|
||||
|
||||
if (locales.length > 1) {
|
||||
logger.info`Website will be built for all these locales: ${locales}`;
|
||||
}
|
||||
|
||||
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`);
|
||||
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);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function getLocalesToBuild({
|
||||
|
@ -144,14 +144,10 @@ 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
|
||||
|
@ -160,81 +156,66 @@ async function buildLocale({
|
|||
|
||||
logger.info`name=${`[${locale}]`} Creating an optimized production build...`;
|
||||
|
||||
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 site = await PerfLogger.async('Load site', () =>
|
||||
loadSite({
|
||||
siteDir,
|
||||
outDir: cliOptions.outDir,
|
||||
config: cliOptions.config,
|
||||
locale,
|
||||
localizePath: cliOptions.locale ? false : undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
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 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');
|
||||
await PerfLogger.async('Creating webpack configs', () =>
|
||||
Promise.all([
|
||||
getBuildClientConfig({
|
||||
props,
|
||||
cliOptions,
|
||||
}),
|
||||
getBuildServerConfig({
|
||||
props,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// Run webpack to build JS bundle (client) and static html files (server).
|
||||
PerfLogger.start('Bundling');
|
||||
await compile([clientConfig, serverConfig]);
|
||||
PerfLogger.end('Bundling');
|
||||
await PerfLogger.async('Bundling with Webpack', () =>
|
||||
compile([clientConfig, serverConfig]),
|
||||
);
|
||||
|
||||
PerfLogger.start('Executing static site generation');
|
||||
const {collectedData} = await executeSSG({
|
||||
props,
|
||||
serverBundlePath,
|
||||
clientManifestPath,
|
||||
});
|
||||
PerfLogger.end('Executing static site generation');
|
||||
const {collectedData} = await PerfLogger.async('SSG', () =>
|
||||
executeSSG({
|
||||
props,
|
||||
serverBundlePath,
|
||||
clientManifestPath,
|
||||
}),
|
||||
);
|
||||
|
||||
// Remove server.bundle.js because it is not needed.
|
||||
PerfLogger.start('Deleting server bundle');
|
||||
await ensureUnlink(serverBundlePath);
|
||||
PerfLogger.end('Deleting server bundle');
|
||||
await PerfLogger.async('Deleting server bundle', () =>
|
||||
ensureUnlink(serverBundlePath),
|
||||
);
|
||||
|
||||
// Plugin Lifecycle - postBuild.
|
||||
PerfLogger.start('Executing postBuild()');
|
||||
await executePluginsPostBuild({plugins, props, collectedData});
|
||||
PerfLogger.end('Executing postBuild()');
|
||||
await PerfLogger.async('postBuild()', () =>
|
||||
executePluginsPostBuild({plugins, props, collectedData}),
|
||||
);
|
||||
|
||||
// TODO execute this in parallel to postBuild?
|
||||
PerfLogger.start('Executing broken links checker');
|
||||
await executeBrokenLinksCheck({props, collectedData});
|
||||
PerfLogger.end('Executing broken links checker');
|
||||
await PerfLogger.async('Broken links checker', () =>
|
||||
executeBrokenLinksCheck({props, collectedData}),
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -247,40 +228,39 @@ async function executeSSG({
|
|||
serverBundlePath: string;
|
||||
clientManifestPath: string;
|
||||
}) {
|
||||
PerfLogger.start('Reading client manifest');
|
||||
const manifest: Manifest = await fs.readJSON(clientManifestPath, 'utf-8');
|
||||
PerfLogger.end('Reading client manifest');
|
||||
|
||||
PerfLogger.start('Compiling SSR template');
|
||||
const ssrTemplate = await compileSSRTemplate(
|
||||
props.siteConfig.ssrTemplate ?? defaultSSRTemplate,
|
||||
const manifest: Manifest = await PerfLogger.async(
|
||||
'Read client manifest',
|
||||
() => fs.readJSON(clientManifestPath, 'utf-8'),
|
||||
);
|
||||
PerfLogger.end('Compiling SSR template');
|
||||
|
||||
PerfLogger.start('Loading App renderer');
|
||||
const renderer = await loadAppRenderer({
|
||||
serverBundlePath,
|
||||
});
|
||||
PerfLogger.end('Loading App renderer');
|
||||
const ssrTemplate = await PerfLogger.async('Compile SSR template', () =>
|
||||
compileSSRTemplate(props.siteConfig.ssrTemplate ?? defaultSSRTemplate),
|
||||
);
|
||||
|
||||
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');
|
||||
const renderer = await PerfLogger.async('Load App renderer', () =>
|
||||
loadAppRenderer({
|
||||
serverBundlePath,
|
||||
}),
|
||||
);
|
||||
|
||||
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,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return ssgResult;
|
||||
}
|
||||
|
@ -345,6 +325,10 @@ async function getBuildClientConfig({
|
|||
bundleAnalyzer: cliOptions.bundleAnalyzer ?? false,
|
||||
});
|
||||
let {config} = result;
|
||||
config = executePluginsConfigurePostCss({
|
||||
plugins,
|
||||
config,
|
||||
});
|
||||
config = executePluginsConfigureWebpack({
|
||||
plugins,
|
||||
config,
|
||||
|
@ -360,10 +344,6 @@ async function getBuildServerConfig({props}: {props: Props}) {
|
|||
props,
|
||||
});
|
||||
let {config} = result;
|
||||
config = executePluginsConfigurePostCss({
|
||||
plugins,
|
||||
config,
|
||||
});
|
||||
config = executePluginsConfigureWebpack({
|
||||
plugins,
|
||||
config,
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
reloadSite,
|
||||
reloadSitePlugin,
|
||||
} from '../../server/site';
|
||||
import {formatPluginName} from '../../server/plugins/pluginsUtils';
|
||||
import type {StartCLIOptions} from './start';
|
||||
import type {LoadedPlugin} from '@docusaurus/types';
|
||||
|
||||
|
@ -69,10 +70,13 @@ async function createLoadSiteParams({
|
|||
export async function createReloadableSite(startParams: StartParams) {
|
||||
const openUrlContext = await createOpenUrlContext(startParams);
|
||||
|
||||
let site = await PerfLogger.async('Loading site', async () => {
|
||||
const params = await createLoadSiteParams(startParams);
|
||||
return loadSite(params);
|
||||
});
|
||||
const loadSiteParams = await PerfLogger.async('createLoadSiteParams', () =>
|
||||
createLoadSiteParams(startParams),
|
||||
);
|
||||
|
||||
let site = await PerfLogger.async('Load site', () =>
|
||||
loadSite(loadSiteParams),
|
||||
);
|
||||
|
||||
const get = () => site;
|
||||
|
||||
|
@ -89,7 +93,7 @@ export async function createReloadableSite(startParams: StartParams) {
|
|||
const reloadBase = async () => {
|
||||
try {
|
||||
const oldSite = site;
|
||||
site = await PerfLogger.async('Reloading site', () => reloadSite(site));
|
||||
site = await PerfLogger.async('Reload site', () => reloadSite(site));
|
||||
if (oldSite.props.baseUrl !== site.props.baseUrl) {
|
||||
printOpenUrlMessage();
|
||||
}
|
||||
|
@ -108,7 +112,7 @@ export async function createReloadableSite(startParams: StartParams) {
|
|||
const reloadPlugin = async (plugin: LoadedPlugin) => {
|
||||
try {
|
||||
site = await PerfLogger.async(
|
||||
`Reloading site plugin ${plugin.name}@${plugin.options.id}`,
|
||||
`Reload site plugin ${formatPluginName(plugin)}`,
|
||||
() => {
|
||||
const pluginIdentifier = {name: plugin.name, id: plugin.options.id};
|
||||
return reloadSitePlugin(site, pluginIdentifier);
|
||||
|
@ -116,7 +120,7 @@ export async function createReloadableSite(startParams: StartParams) {
|
|||
);
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Site plugin reload failure - Plugin ${plugin.name}@${plugin.options.id}`,
|
||||
`Site plugin reload failure - Plugin ${formatPluginName(plugin)}`,
|
||||
);
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
writePluginTranslations,
|
||||
writeCodeTranslations,
|
||||
type WriteTranslationsOptions,
|
||||
getPluginsDefaultCodeTranslationMessages,
|
||||
loadPluginsDefaultCodeTranslationMessages,
|
||||
applyDefaultCodeTranslations,
|
||||
} from '../server/translations/translations';
|
||||
import {
|
||||
|
@ -114,7 +114,7 @@ Available locales are: ${context.i18n.locales.join(',')}.`,
|
|||
await getExtraSourceCodeFilePaths(),
|
||||
);
|
||||
|
||||
const defaultCodeMessages = await getPluginsDefaultCodeTranslationMessages(
|
||||
const defaultCodeMessages = await loadPluginsDefaultCodeTranslationMessages(
|
||||
plugins,
|
||||
);
|
||||
|
||||
|
|
|
@ -36,13 +36,16 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
|||
"plugins": [
|
||||
{
|
||||
"content": undefined,
|
||||
"defaultCodeTranslations": {},
|
||||
"getClientModules": [Function],
|
||||
"globalData": undefined,
|
||||
"injectHtmlTags": [Function],
|
||||
"name": "docusaurus-bootstrap-plugin",
|
||||
"options": {
|
||||
"id": "default",
|
||||
},
|
||||
"path": "<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/__fixtures__/custom-i18n-site",
|
||||
"routes": [],
|
||||
"version": {
|
||||
"type": "synthetic",
|
||||
},
|
||||
|
@ -50,11 +53,14 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
|||
{
|
||||
"configureWebpack": [Function],
|
||||
"content": undefined,
|
||||
"defaultCodeTranslations": {},
|
||||
"globalData": undefined,
|
||||
"name": "docusaurus-mdx-fallback-plugin",
|
||||
"options": {
|
||||
"id": "default",
|
||||
},
|
||||
"path": ".",
|
||||
"routes": [],
|
||||
"version": {
|
||||
"type": "synthetic",
|
||||
},
|
||||
|
@ -128,5 +134,6 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
|||
"pluginVersions": {},
|
||||
"siteVersion": undefined,
|
||||
},
|
||||
"siteVersion": undefined,
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {loadClientModules} from '../clientModules';
|
||||
import {getAllClientModules} from '../clientModules';
|
||||
import type {LoadedPlugin} from '@docusaurus/types';
|
||||
|
||||
const pluginEmpty = {
|
||||
|
@ -33,14 +33,14 @@ const pluginHelloWorld = {
|
|||
},
|
||||
} as unknown as LoadedPlugin;
|
||||
|
||||
describe('loadClientModules', () => {
|
||||
describe('getAllClientModules', () => {
|
||||
it('loads an empty plugin', () => {
|
||||
const clientModules = loadClientModules([pluginEmpty]);
|
||||
const clientModules = getAllClientModules([pluginEmpty]);
|
||||
expect(clientModules).toMatchInlineSnapshot(`[]`);
|
||||
});
|
||||
|
||||
it('loads a non-empty plugin', () => {
|
||||
const clientModules = loadClientModules([pluginFooBar]);
|
||||
const clientModules = getAllClientModules([pluginFooBar]);
|
||||
expect(clientModules).toMatchInlineSnapshot(`
|
||||
[
|
||||
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
|
||||
|
@ -50,7 +50,7 @@ describe('loadClientModules', () => {
|
|||
});
|
||||
|
||||
it('loads multiple non-empty plugins', () => {
|
||||
const clientModules = loadClientModules([pluginFooBar, pluginHelloWorld]);
|
||||
const clientModules = getAllClientModules([pluginFooBar, pluginHelloWorld]);
|
||||
expect(clientModules).toMatchInlineSnapshot(`
|
||||
[
|
||||
"<PROJECT_ROOT>/packages/docusaurus/src/server/__tests__/foo",
|
||||
|
@ -62,7 +62,7 @@ describe('loadClientModules', () => {
|
|||
});
|
||||
|
||||
it('loads multiple non-empty plugins in different order', () => {
|
||||
const clientModules = loadClientModules([pluginHelloWorld, pluginFooBar]);
|
||||
const clientModules = getAllClientModules([pluginHelloWorld, pluginFooBar]);
|
||||
expect(clientModules).toMatchInlineSnapshot(`
|
||||
[
|
||||
"/hello",
|
||||
|
@ -74,7 +74,7 @@ describe('loadClientModules', () => {
|
|||
});
|
||||
|
||||
it('loads both empty and non-empty plugins', () => {
|
||||
const clientModules = loadClientModules([
|
||||
const clientModules = getAllClientModules([
|
||||
pluginHelloWorld,
|
||||
pluginEmpty,
|
||||
pluginFooBar,
|
||||
|
@ -90,7 +90,7 @@ describe('loadClientModules', () => {
|
|||
});
|
||||
|
||||
it('loads empty and non-empty in a different order', () => {
|
||||
const clientModules = loadClientModules([
|
||||
const clientModules = getAllClientModules([
|
||||
pluginHelloWorld,
|
||||
pluginFooBar,
|
||||
pluginEmpty,
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import path from 'path';
|
||||
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
|
||||
import {getPluginVersion, loadSiteMetadata} from '../siteMetadata';
|
||||
import {loadPluginVersion, createSiteMetadata} from '../siteMetadata';
|
||||
import type {LoadedPlugin} from '@docusaurus/types';
|
||||
|
||||
describe('getPluginVersion', () => {
|
||||
describe('loadPluginVersion', () => {
|
||||
it('detects external packages plugins versions', async () => {
|
||||
await expect(
|
||||
getPluginVersion(
|
||||
loadPluginVersion(
|
||||
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
|
||||
// Make the plugin appear external.
|
||||
path.join(__dirname, '..', '..', '..', '..', '..', '..', 'website'),
|
||||
|
@ -23,7 +23,7 @@ describe('getPluginVersion', () => {
|
|||
|
||||
it('detects project plugins versions', async () => {
|
||||
await expect(
|
||||
getPluginVersion(
|
||||
loadPluginVersion(
|
||||
path.join(__dirname, '__fixtures__/siteMetadata/dummy-plugin.js'),
|
||||
// Make the plugin appear project local.
|
||||
path.join(__dirname, '__fixtures__/siteMetadata'),
|
||||
|
@ -32,14 +32,14 @@ describe('getPluginVersion', () => {
|
|||
});
|
||||
|
||||
it('detects local packages versions', async () => {
|
||||
await expect(getPluginVersion('/', '/')).resolves.toEqual({type: 'local'});
|
||||
await expect(loadPluginVersion('/', '/')).resolves.toEqual({type: 'local'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadSiteMetadata', () => {
|
||||
it('throws if plugin versions mismatch', async () => {
|
||||
await expect(
|
||||
loadSiteMetadata({
|
||||
describe('createSiteMetadata', () => {
|
||||
it('throws if plugin versions mismatch', () => {
|
||||
expect(() =>
|
||||
createSiteMetadata({
|
||||
plugins: [
|
||||
{
|
||||
name: 'docusaurus-plugin-content-docs',
|
||||
|
@ -50,10 +50,9 @@ describe('loadSiteMetadata', () => {
|
|||
},
|
||||
},
|
||||
] as LoadedPlugin[],
|
||||
siteDir: path.join(__dirname, '__fixtures__/siteMetadata'),
|
||||
siteVersion: 'some-random-version',
|
||||
}),
|
||||
).rejects
|
||||
.toThrow(`Invalid name=docusaurus-plugin-content-docs version number=1.0.0.
|
||||
).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?`);
|
||||
});
|
||||
|
|
|
@ -9,13 +9,12 @@ 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) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {LoadedPlugin} from '@docusaurus/types';
|
|||
* Runs the `getClientModules` lifecycle. The returned file paths are all
|
||||
* absolute.
|
||||
*/
|
||||
export function loadClientModules(plugins: LoadedPlugin[]): string[] {
|
||||
export function getAllClientModules(plugins: LoadedPlugin[]): string[] {
|
||||
return plugins.flatMap(
|
||||
(plugin) =>
|
||||
plugin.getClientModules?.().map((p) => path.resolve(plugin.path, p)) ??
|
||||
|
|
|
@ -9,11 +9,13 @@ 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,
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
import path from 'path';
|
||||
import {fromPartial} from '@total-typescript/shoehorn';
|
||||
import {loadPlugins, mergeGlobalData} from '../plugins';
|
||||
import type {
|
||||
GlobalData,
|
||||
LoadContext,
|
||||
Plugin,
|
||||
PluginConfig,
|
||||
} from '@docusaurus/types';
|
||||
import {loadPlugins, reloadPlugin} from '../plugins';
|
||||
import type {LoadContext, Plugin, PluginConfig} from '@docusaurus/types';
|
||||
|
||||
function testLoad({
|
||||
async function testLoad({
|
||||
plugins,
|
||||
themes,
|
||||
}: {
|
||||
|
@ -39,7 +34,9 @@ function testLoad({
|
|||
},
|
||||
});
|
||||
|
||||
return loadPlugins(context);
|
||||
const result = await loadPlugins(context);
|
||||
|
||||
return {context, ...result};
|
||||
}
|
||||
|
||||
const SyntheticPluginNames = [
|
||||
|
@ -50,7 +47,7 @@ const SyntheticPluginNames = [
|
|||
async function testPlugin<Content = unknown>(
|
||||
pluginConfig: PluginConfig<Content>,
|
||||
) {
|
||||
const {plugins, routes, globalData} = await testLoad({
|
||||
const {context, plugins, routes, globalData} = await testLoad({
|
||||
plugins: [pluginConfig],
|
||||
themes: [],
|
||||
});
|
||||
|
@ -62,204 +59,9 @@ async function testPlugin<Content = unknown>(
|
|||
const plugin = nonSyntheticPlugins[0]!;
|
||||
expect(plugin).toBeDefined();
|
||||
|
||||
return {plugin, routes, globalData};
|
||||
return {context, plugin, routes, globalData};
|
||||
}
|
||||
|
||||
describe('mergeGlobalData', () => {
|
||||
it('no global data', () => {
|
||||
expect(mergeGlobalData()).toEqual({});
|
||||
});
|
||||
|
||||
it('1 global data', () => {
|
||||
const globalData: GlobalData = {
|
||||
plugin: {
|
||||
default: {someData: 'val'},
|
||||
},
|
||||
};
|
||||
expect(mergeGlobalData(globalData)).toEqual(globalData);
|
||||
});
|
||||
|
||||
it('1 global data - primitive value', () => {
|
||||
// For retro-compatibility we allow primitive values to be kept as is
|
||||
// Not sure anyone is using primitive global data though...
|
||||
const globalData: GlobalData = {
|
||||
plugin: {
|
||||
default: 42,
|
||||
},
|
||||
};
|
||||
expect(mergeGlobalData(globalData)).toEqual(globalData);
|
||||
});
|
||||
|
||||
it('3 distinct plugins global data', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin1: {
|
||||
default: {someData1: 'val1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin2: {
|
||||
default: {someData2: 'val2'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin3: {
|
||||
default: {someData3: 'val3'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin1: {
|
||||
default: {someData1: 'val1'},
|
||||
},
|
||||
plugin2: {
|
||||
default: {someData2: 'val2'},
|
||||
},
|
||||
plugin3: {
|
||||
default: {someData3: 'val3'},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('3 plugin instances of same plugin', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin: {
|
||||
id1: {someData1: 'val1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin: {
|
||||
id2: {someData2: 'val2'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin: {
|
||||
id3: {someData3: 'val3'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin: {
|
||||
id1: {someData1: 'val1'},
|
||||
id2: {someData2: 'val2'},
|
||||
id3: {someData3: 'val3'},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('3 times the same plugin', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin: {
|
||||
id: {someData1: 'val1', shared: 'shared1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin: {
|
||||
id: {someData2: 'val2', shared: 'shared2'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin: {
|
||||
id: {someData3: 'val3', shared: 'shared3'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin: {
|
||||
id: {
|
||||
someData1: 'val1',
|
||||
someData2: 'val2',
|
||||
someData3: 'val3',
|
||||
shared: 'shared3',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('3 times same plugin - including primitive values', () => {
|
||||
// Very unlikely to happen, but we can't merge primitive values together
|
||||
// Since we use Object.assign(), the primitive values are simply ignored
|
||||
const globalData1: GlobalData = {
|
||||
plugin: {
|
||||
default: 42,
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin: {
|
||||
default: {hey: 'val'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin: {
|
||||
default: 84,
|
||||
},
|
||||
};
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin: {
|
||||
default: {hey: 'val'},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('real world case', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin1: {
|
||||
id1: {someData1: 'val1', shared: 'globalData1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin1: {
|
||||
id1: {someData2: 'val2', shared: 'globalData2'},
|
||||
},
|
||||
};
|
||||
|
||||
const globalData3: GlobalData = {
|
||||
plugin1: {
|
||||
id2: {someData3: 'val3', shared: 'globalData3'},
|
||||
},
|
||||
};
|
||||
|
||||
const globalData4: GlobalData = {
|
||||
plugin2: {
|
||||
id1: {someData1: 'val1', shared: 'globalData4'},
|
||||
},
|
||||
};
|
||||
const globalData5: GlobalData = {
|
||||
plugin2: {
|
||||
id2: {someData1: 'val1', shared: 'globalData5'},
|
||||
},
|
||||
};
|
||||
|
||||
const globalData6: GlobalData = {
|
||||
plugin3: {
|
||||
id1: {someData1: 'val1', shared: 'globalData6'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
mergeGlobalData(
|
||||
globalData1,
|
||||
globalData2,
|
||||
globalData3,
|
||||
globalData4,
|
||||
globalData5,
|
||||
globalData6,
|
||||
),
|
||||
).toEqual({
|
||||
plugin1: {
|
||||
id1: {someData1: 'val1', someData2: 'val2', shared: 'globalData2'},
|
||||
id2: {someData3: 'val3', shared: 'globalData3'},
|
||||
},
|
||||
plugin2: {
|
||||
id1: {someData1: 'val1', shared: 'globalData4'},
|
||||
id2: {someData1: 'val1', shared: 'globalData5'},
|
||||
},
|
||||
plugin3: {
|
||||
id1: {someData1: 'val1', shared: 'globalData6'},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadPlugins', () => {
|
||||
it('registers default synthetic plugins', async () => {
|
||||
const {plugins, routes, globalData} = await testLoad({
|
||||
|
@ -526,3 +328,272 @@ describe('loadPlugins', () => {
|
|||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reloadPlugin', () => {
|
||||
it('can reload a single complex plugin with same content', async () => {
|
||||
const plugin: PluginConfig = () => ({
|
||||
name: 'plugin-name',
|
||||
contentLoaded({actions}) {
|
||||
actions.addRoute({
|
||||
path: '/contentLoadedRouteParent',
|
||||
component: 'Comp',
|
||||
routes: [
|
||||
{path: '/contentLoadedRouteParent/child', component: 'Comp'},
|
||||
],
|
||||
});
|
||||
actions.addRoute({
|
||||
path: '/contentLoadedRouteSingle',
|
||||
component: 'Comp',
|
||||
});
|
||||
actions.setGlobalData({
|
||||
globalContentLoaded: 'val1',
|
||||
globalOverridden: 'initial-value',
|
||||
});
|
||||
},
|
||||
allContentLoaded({actions}) {
|
||||
actions.addRoute({
|
||||
path: '/allContentLoadedRouteParent',
|
||||
component: 'Comp',
|
||||
routes: [
|
||||
{path: '/allContentLoadedRouteParent/child', component: 'Comp'},
|
||||
],
|
||||
});
|
||||
actions.addRoute({
|
||||
path: '/allContentLoadedRouteSingle',
|
||||
component: 'Comp',
|
||||
});
|
||||
actions.setGlobalData({
|
||||
globalAllContentLoaded: 'val2',
|
||||
globalOverridden: 'override-value',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const loadResult = await testLoad({
|
||||
plugins: [plugin],
|
||||
themes: [],
|
||||
});
|
||||
const reloadResult = await reloadPlugin({
|
||||
context: loadResult.context,
|
||||
plugins: loadResult.plugins,
|
||||
pluginIdentifier: {name: 'plugin-name', id: 'default'},
|
||||
});
|
||||
|
||||
expect(loadResult.routes).toEqual(reloadResult.routes);
|
||||
expect(loadResult.globalData).toEqual(reloadResult.globalData);
|
||||
expect(reloadResult.routes).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/allContentLoadedRouteSingle/",
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/contentLoadedRouteSingle/",
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/allContentLoadedRouteParent/",
|
||||
"routes": [
|
||||
{
|
||||
"component": "Comp",
|
||||
"path": "/allContentLoadedRouteParent/child/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/contentLoadedRouteParent/",
|
||||
"routes": [
|
||||
{
|
||||
"component": "Comp",
|
||||
"path": "/contentLoadedRouteParent/child/",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(reloadResult.globalData).toMatchInlineSnapshot(`
|
||||
{
|
||||
"plugin-name": {
|
||||
"default": {
|
||||
"globalAllContentLoaded": "val2",
|
||||
"globalContentLoaded": "val1",
|
||||
"globalOverridden": "override-value",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('can reload plugins in real-world setup', async () => {
|
||||
let isPlugin1Reload = false;
|
||||
|
||||
const plugin1: PluginConfig = () => ({
|
||||
name: 'plugin-name-1',
|
||||
contentLoaded({actions}) {
|
||||
actions.addRoute({
|
||||
path: isPlugin1Reload
|
||||
? '/contentLoaded-route-reload'
|
||||
: '/contentLoaded-route-initial',
|
||||
component: 'Comp',
|
||||
});
|
||||
actions.setGlobalData({
|
||||
contentLoadedVal: isPlugin1Reload
|
||||
? 'contentLoaded-val-reload'
|
||||
: 'contentLoaded-val-initial',
|
||||
});
|
||||
},
|
||||
allContentLoaded({actions}) {
|
||||
actions.addRoute({
|
||||
path: isPlugin1Reload
|
||||
? '/allContentLoaded-route-reload'
|
||||
: '/allContentLoaded-route-initial',
|
||||
component: 'Comp',
|
||||
});
|
||||
actions.setGlobalData({
|
||||
allContentLoadedVal: isPlugin1Reload
|
||||
? 'allContentLoaded-val-reload'
|
||||
: 'allContentLoaded-val-initial',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const plugin2: PluginConfig = () => ({
|
||||
name: 'plugin-name-2',
|
||||
contentLoaded({actions}) {
|
||||
actions.addRoute({
|
||||
path: '/plugin-2-route',
|
||||
component: 'Comp',
|
||||
});
|
||||
actions.setGlobalData({plugin2Val: 'val'});
|
||||
},
|
||||
});
|
||||
|
||||
const loadResult = await testLoad({
|
||||
plugins: [plugin1, plugin2],
|
||||
themes: [],
|
||||
});
|
||||
|
||||
isPlugin1Reload = true;
|
||||
|
||||
const reloadResult = await reloadPlugin({
|
||||
context: loadResult.context,
|
||||
plugins: loadResult.plugins,
|
||||
pluginIdentifier: {name: 'plugin-name-1', id: 'default'},
|
||||
});
|
||||
|
||||
expect(loadResult.routes).not.toEqual(reloadResult.routes);
|
||||
expect(loadResult.globalData).not.toEqual(reloadResult.globalData);
|
||||
expect(loadResult.routes).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name-1/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/allContentLoaded-route-initial/",
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name-1/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/contentLoaded-route-initial/",
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name-2/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/plugin-2-route/",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(loadResult.globalData).toMatchInlineSnapshot(`
|
||||
{
|
||||
"plugin-name-1": {
|
||||
"default": {
|
||||
"allContentLoadedVal": "allContentLoaded-val-initial",
|
||||
"contentLoadedVal": "contentLoaded-val-initial",
|
||||
},
|
||||
},
|
||||
"plugin-name-2": {
|
||||
"default": {
|
||||
"plugin2Val": "val",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(reloadResult.routes).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name-1/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/allContentLoaded-route-reload/",
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name-1/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/contentLoaded-route-reload/",
|
||||
},
|
||||
{
|
||||
"component": "Comp",
|
||||
"context": {
|
||||
"plugin": "<PROJECT_ROOT>/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/site-with-plugin/.docusaurus/plugin-name-2/default/plugin-route-context-module-100.json",
|
||||
},
|
||||
"path": "/plugin-2-route/",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(reloadResult.globalData).toMatchInlineSnapshot(`
|
||||
{
|
||||
"plugin-name-1": {
|
||||
"default": {
|
||||
"allContentLoadedVal": "allContentLoaded-val-reload",
|
||||
"contentLoadedVal": "contentLoaded-val-reload",
|
||||
},
|
||||
},
|
||||
"plugin-name-2": {
|
||||
"default": {
|
||||
"plugin2Val": "val",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// Trying to reload again one plugin or the other should give
|
||||
// the same result because the plugin content doesn't change
|
||||
const reloadResult2 = await reloadPlugin({
|
||||
context: loadResult.context,
|
||||
plugins: reloadResult.plugins,
|
||||
pluginIdentifier: {name: 'plugin-name-1', id: 'default'},
|
||||
});
|
||||
expect(reloadResult2.routes).toEqual(reloadResult.routes);
|
||||
expect(reloadResult2.globalData).toEqual(reloadResult.globalData);
|
||||
|
||||
const reloadResult3 = await reloadPlugin({
|
||||
context: loadResult.context,
|
||||
plugins: reloadResult2.plugins,
|
||||
pluginIdentifier: {name: 'plugin-name-2', id: 'default'},
|
||||
});
|
||||
expect(reloadResult3.routes).toEqual(reloadResult.routes);
|
||||
expect(reloadResult3.globalData).toEqual(reloadResult.globalData);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/**
|
||||
* 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 {mergeGlobalData} from '../pluginsUtils';
|
||||
import type {GlobalData} from '@docusaurus/types';
|
||||
|
||||
describe('mergeGlobalData', () => {
|
||||
it('no global data', () => {
|
||||
expect(mergeGlobalData()).toEqual({});
|
||||
});
|
||||
|
||||
it('1 global data', () => {
|
||||
const globalData: GlobalData = {
|
||||
plugin: {
|
||||
default: {someData: 'val'},
|
||||
},
|
||||
};
|
||||
expect(mergeGlobalData(globalData)).toEqual(globalData);
|
||||
});
|
||||
|
||||
it('1 global data - primitive value', () => {
|
||||
// For retro-compatibility we allow primitive values to be kept as is
|
||||
// Not sure anyone is using primitive global data though...
|
||||
const globalData: GlobalData = {
|
||||
plugin: {
|
||||
default: 42,
|
||||
},
|
||||
};
|
||||
expect(mergeGlobalData(globalData)).toEqual(globalData);
|
||||
});
|
||||
|
||||
it('3 distinct plugins global data', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin1: {
|
||||
default: {someData1: 'val1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin2: {
|
||||
default: {someData2: 'val2'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin3: {
|
||||
default: {someData3: 'val3'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin1: {
|
||||
default: {someData1: 'val1'},
|
||||
},
|
||||
plugin2: {
|
||||
default: {someData2: 'val2'},
|
||||
},
|
||||
plugin3: {
|
||||
default: {someData3: 'val3'},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('3 plugin instances of same plugin', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin: {
|
||||
id1: {someData1: 'val1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin: {
|
||||
id2: {someData2: 'val2'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin: {
|
||||
id3: {someData3: 'val3'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin: {
|
||||
id1: {someData1: 'val1'},
|
||||
id2: {someData2: 'val2'},
|
||||
id3: {someData3: 'val3'},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('3 times the same plugin', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin: {
|
||||
id: {someData1: 'val1', shared: 'shared1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin: {
|
||||
id: {someData2: 'val2', shared: 'shared2'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin: {
|
||||
id: {someData3: 'val3', shared: 'shared3'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin: {
|
||||
id: {
|
||||
someData1: 'val1',
|
||||
someData2: 'val2',
|
||||
someData3: 'val3',
|
||||
shared: 'shared3',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('3 times same plugin - including primitive values', () => {
|
||||
// Very unlikely to happen, but we can't merge primitive values together
|
||||
// Since we use Object.assign(), the primitive values are simply ignored
|
||||
const globalData1: GlobalData = {
|
||||
plugin: {
|
||||
default: 42,
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin: {
|
||||
default: {hey: 'val'},
|
||||
},
|
||||
};
|
||||
const globalData3: GlobalData = {
|
||||
plugin: {
|
||||
default: 84,
|
||||
},
|
||||
};
|
||||
expect(mergeGlobalData(globalData1, globalData2, globalData3)).toEqual({
|
||||
plugin: {
|
||||
default: {hey: 'val'},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('real world case', () => {
|
||||
const globalData1: GlobalData = {
|
||||
plugin1: {
|
||||
id1: {someData1: 'val1', shared: 'globalData1'},
|
||||
},
|
||||
};
|
||||
const globalData2: GlobalData = {
|
||||
plugin1: {
|
||||
id1: {someData2: 'val2', shared: 'globalData2'},
|
||||
},
|
||||
};
|
||||
|
||||
const globalData3: GlobalData = {
|
||||
plugin1: {
|
||||
id2: {someData3: 'val3', shared: 'globalData3'},
|
||||
},
|
||||
};
|
||||
|
||||
const globalData4: GlobalData = {
|
||||
plugin2: {
|
||||
id1: {someData1: 'val1', shared: 'globalData4'},
|
||||
},
|
||||
};
|
||||
const globalData5: GlobalData = {
|
||||
plugin2: {
|
||||
id2: {someData1: 'val1', shared: 'globalData5'},
|
||||
},
|
||||
};
|
||||
|
||||
const globalData6: GlobalData = {
|
||||
plugin3: {
|
||||
id1: {someData1: 'val1', shared: 'globalData6'},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
mergeGlobalData(
|
||||
globalData1,
|
||||
globalData2,
|
||||
globalData3,
|
||||
globalData4,
|
||||
globalData5,
|
||||
globalData6,
|
||||
),
|
||||
).toEqual({
|
||||
plugin1: {
|
||||
id1: {someData1: 'val1', someData2: 'val2', shared: 'globalData2'},
|
||||
id2: {someData3: 'val3', shared: 'globalData3'},
|
||||
},
|
||||
plugin2: {
|
||||
id1: {someData1: 'val1', shared: 'globalData4'},
|
||||
id2: {someData1: 'val1', shared: 'globalData5'},
|
||||
},
|
||||
plugin3: {
|
||||
id1: {someData1: 'val1', shared: 'globalData6'},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,7 +9,7 @@ import path from 'path';
|
|||
import {docuHash, generate} from '@docusaurus/utils';
|
||||
import {applyRouteTrailingSlash} from './routeConfig';
|
||||
import type {
|
||||
LoadedPlugin,
|
||||
InitializedPlugin,
|
||||
PluginContentLoadedActions,
|
||||
PluginRouteContext,
|
||||
RouteConfig,
|
||||
|
@ -31,7 +31,7 @@ export async function createPluginActionsUtils({
|
|||
baseUrl,
|
||||
trailingSlash,
|
||||
}: {
|
||||
plugin: LoadedPlugin;
|
||||
plugin: InitializedPlugin;
|
||||
generatedFilesDir: string;
|
||||
baseUrl: string;
|
||||
trailingSlash: boolean | undefined;
|
||||
|
@ -48,6 +48,7 @@ export async function createPluginActionsUtils({
|
|||
dataDir,
|
||||
`${docuHash('pluginRouteContextModule')}.json`,
|
||||
);
|
||||
// TODO not ideal place to generate that file
|
||||
await generate(
|
||||
'/',
|
||||
pluginRouteContextModulePath,
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
normalizePluginOptions,
|
||||
normalizeThemeConfig,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import {getPluginVersion} from '../siteMetadata';
|
||||
import {loadPluginVersion} 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 doGetPluginVersion(
|
||||
async function doLoadPluginVersion(
|
||||
normalizedPluginConfig: NormalizedPluginConfig,
|
||||
): Promise<PluginVersionInformation> {
|
||||
if (normalizedPluginConfig.pluginModule?.path) {
|
||||
const pluginPath = pluginRequire.resolve(
|
||||
normalizedPluginConfig.pluginModule.path,
|
||||
);
|
||||
return getPluginVersion(pluginPath, context.siteDir);
|
||||
return loadPluginVersion(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 doGetPluginVersion(
|
||||
const pluginVersion: PluginVersionInformation = await doLoadPluginVersion(
|
||||
normalizedPluginConfig,
|
||||
);
|
||||
const pluginOptions = doValidatePluginOptions(normalizedPluginConfig);
|
||||
|
|
|
@ -5,14 +5,20 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {initPlugins} from './init';
|
||||
import {createBootstrapPlugin, createMDXFallbackPlugin} from './synthetic';
|
||||
import {localizePluginTranslationFile} from '../translations/translations';
|
||||
import {sortRoutes} from './routeConfig';
|
||||
import {PerfLogger} from '../../utils';
|
||||
import {createPluginActionsUtils} from './actions';
|
||||
import {
|
||||
aggregateAllContent,
|
||||
aggregateGlobalData,
|
||||
aggregateRoutes,
|
||||
formatPluginName,
|
||||
getPluginByIdentifier,
|
||||
mergeGlobalData,
|
||||
} from './pluginsUtils';
|
||||
import type {
|
||||
LoadContext,
|
||||
RouteConfig,
|
||||
|
@ -23,17 +29,17 @@ import type {
|
|||
InitializedPlugin,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
async function translatePlugin({
|
||||
async function translatePluginContent({
|
||||
plugin,
|
||||
content,
|
||||
context,
|
||||
}: {
|
||||
plugin: LoadedPlugin;
|
||||
plugin: InitializedPlugin;
|
||||
content: unknown;
|
||||
context: LoadContext;
|
||||
}): Promise<LoadedPlugin> {
|
||||
const {content} = plugin;
|
||||
|
||||
}): Promise<unknown> {
|
||||
const rawTranslationFiles =
|
||||
(await plugin.getTranslationFiles?.({content: plugin.content})) ?? [];
|
||||
(await plugin.getTranslationFiles?.({content})) ?? [];
|
||||
|
||||
const translationFiles = await Promise.all(
|
||||
rawTranslationFiles.map((translationFile) =>
|
||||
|
@ -58,81 +64,81 @@ async function translatePlugin({
|
|||
// translate its own slice of theme config and should make no assumptions
|
||||
// about other plugins' keys, so this is safe to run in parallel.
|
||||
Object.assign(context.siteConfig.themeConfig, translatedThemeConfigSlice);
|
||||
return {...plugin, content: translatedContent};
|
||||
return translatedContent;
|
||||
}
|
||||
|
||||
async function executePluginLoadContent({
|
||||
async function executePluginContentLoading({
|
||||
plugin,
|
||||
context,
|
||||
}: {
|
||||
plugin: InitializedPlugin;
|
||||
context: LoadContext;
|
||||
}): Promise<LoadedPlugin> {
|
||||
return PerfLogger.async(
|
||||
`Plugin - loadContent - ${plugin.name}@${plugin.options.id}`,
|
||||
async () => {
|
||||
const content = await plugin.loadContent?.();
|
||||
const loadedPlugin: LoadedPlugin = {...plugin, content};
|
||||
return translatePlugin({plugin: loadedPlugin, context});
|
||||
},
|
||||
);
|
||||
return PerfLogger.async(`Load ${formatPluginName(plugin)}`, async () => {
|
||||
let content = await PerfLogger.async('loadContent()', () =>
|
||||
plugin.loadContent?.(),
|
||||
);
|
||||
|
||||
content = await PerfLogger.async('translatePluginContent()', () =>
|
||||
translatePluginContent({
|
||||
plugin,
|
||||
content,
|
||||
context,
|
||||
}),
|
||||
);
|
||||
|
||||
const defaultCodeTranslations =
|
||||
(await PerfLogger.async('getDefaultCodeTranslationMessages()', () =>
|
||||
plugin.getDefaultCodeTranslationMessages?.(),
|
||||
)) ?? {};
|
||||
|
||||
if (!plugin.contentLoaded) {
|
||||
return {
|
||||
...plugin,
|
||||
content,
|
||||
defaultCodeTranslations,
|
||||
routes: [],
|
||||
globalData: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
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 executePluginsLoadContent({
|
||||
async function executeAllPluginsContentLoading({
|
||||
plugins,
|
||||
context,
|
||||
}: {
|
||||
plugins: InitializedPlugin[];
|
||||
context: LoadContext;
|
||||
}) {
|
||||
return PerfLogger.async(`Plugins - loadContent`, () =>
|
||||
Promise.all(
|
||||
plugins.map((plugin) => executePluginLoadContent({plugin, context})),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function aggregateAllContent(loadedPlugins: LoadedPlugin[]): AllContent {
|
||||
return _.chain(loadedPlugins)
|
||||
.groupBy((item) => item.name)
|
||||
.mapValues((nameItems) =>
|
||||
_.chain(nameItems)
|
||||
.groupBy((item) => item.options.id)
|
||||
.mapValues((idItems) => idItems[0]!.content)
|
||||
.value(),
|
||||
)
|
||||
.value();
|
||||
}
|
||||
|
||||
async function executePluginContentLoaded({
|
||||
plugin,
|
||||
context,
|
||||
}: {
|
||||
plugin: LoadedPlugin;
|
||||
context: LoadContext;
|
||||
}): Promise<{routes: RouteConfig[]; globalData: unknown}> {
|
||||
return PerfLogger.async(
|
||||
`Plugins - contentLoaded - ${plugin.name}@${plugin.options.id}`,
|
||||
async () => {
|
||||
if (!plugin.contentLoaded) {
|
||||
return {routes: [], globalData: undefined};
|
||||
}
|
||||
const pluginActionsUtils = await createPluginActionsUtils({
|
||||
plugin,
|
||||
generatedFilesDir: context.generatedFilesDir,
|
||||
baseUrl: context.siteConfig.baseUrl,
|
||||
trailingSlash: context.siteConfig.trailingSlash,
|
||||
});
|
||||
await plugin.contentLoaded({
|
||||
content: plugin.content,
|
||||
actions: pluginActionsUtils.getActions(),
|
||||
});
|
||||
return {
|
||||
routes: pluginActionsUtils.getRoutes(),
|
||||
globalData: pluginActionsUtils.getGlobalData(),
|
||||
};
|
||||
},
|
||||
);
|
||||
}): Promise<LoadedPlugin[]> {
|
||||
return PerfLogger.async(`Load plugins content`, () => {
|
||||
return Promise.all(
|
||||
plugins.map((plugin) => executePluginContentLoading({plugin, context})),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function executePluginAllContentLoaded({
|
||||
|
@ -145,7 +151,7 @@ async function executePluginAllContentLoaded({
|
|||
allContent: AllContent;
|
||||
}): Promise<{routes: RouteConfig[]; globalData: unknown}> {
|
||||
return PerfLogger.async(
|
||||
`Plugins - allContentLoaded - ${plugin.name}@${plugin.options.id}`,
|
||||
`allContentLoaded() - ${formatPluginName(plugin)}`,
|
||||
async () => {
|
||||
if (!plugin.allContentLoaded) {
|
||||
return {routes: [], globalData: undefined};
|
||||
|
@ -168,50 +174,16 @@ async function executePluginAllContentLoaded({
|
|||
);
|
||||
}
|
||||
|
||||
async function executePluginsContentLoaded({
|
||||
type AllContentLoadedResult = {routes: RouteConfig[]; globalData: GlobalData};
|
||||
|
||||
async function executeAllPluginsAllContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
}: {
|
||||
plugins: LoadedPlugin[];
|
||||
context: LoadContext;
|
||||
}): Promise<{routes: RouteConfig[]; globalData: GlobalData}> {
|
||||
return PerfLogger.async(`Plugins - contentLoaded`, async () => {
|
||||
const routes: RouteConfig[] = [];
|
||||
const globalData: GlobalData = {};
|
||||
|
||||
await Promise.all(
|
||||
plugins.map(async (plugin) => {
|
||||
const {routes: pluginRoutes, globalData: pluginGlobalData} =
|
||||
await executePluginContentLoaded({
|
||||
plugin,
|
||||
context,
|
||||
});
|
||||
|
||||
routes.push(...pluginRoutes);
|
||||
|
||||
if (pluginGlobalData !== undefined) {
|
||||
globalData[plugin.name] ??= {};
|
||||
globalData[plugin.name]![plugin.options.id] = pluginGlobalData;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// Sort the route config.
|
||||
// This ensures that route with sub routes are always placed last.
|
||||
sortRoutes(routes, context.siteConfig.baseUrl);
|
||||
|
||||
return {routes, globalData};
|
||||
});
|
||||
}
|
||||
|
||||
async function executePluginsAllContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
}: {
|
||||
plugins: LoadedPlugin[];
|
||||
context: LoadContext;
|
||||
}): Promise<{routes: RouteConfig[]; globalData: GlobalData}> {
|
||||
return PerfLogger.async(`Plugins - allContentLoaded`, async () => {
|
||||
}): Promise<AllContentLoadedResult> {
|
||||
return PerfLogger.async(`allContentLoaded()`, async () => {
|
||||
const allContent = aggregateAllContent(plugins);
|
||||
|
||||
const routes: RouteConfig[] = [];
|
||||
|
@ -235,100 +207,70 @@ async function executePluginsAllContentLoaded({
|
|||
}),
|
||||
);
|
||||
|
||||
// Sort the route config.
|
||||
// This ensures that route with sub routes are always placed last.
|
||||
sortRoutes(routes, context.siteConfig.baseUrl);
|
||||
|
||||
return {routes, globalData};
|
||||
});
|
||||
}
|
||||
|
||||
// This merges plugins routes and global data created from both lifecycles:
|
||||
// - contentLoaded()
|
||||
// - allContentLoaded()
|
||||
function mergeResults({
|
||||
plugins,
|
||||
allContentLoadedResult,
|
||||
}: {
|
||||
plugins: LoadedPlugin[];
|
||||
allContentLoadedResult: AllContentLoadedResult;
|
||||
}) {
|
||||
const routes: RouteConfig[] = [
|
||||
...aggregateRoutes(plugins),
|
||||
...allContentLoadedResult.routes,
|
||||
];
|
||||
sortRoutes(routes);
|
||||
|
||||
const globalData: GlobalData = mergeGlobalData(
|
||||
aggregateGlobalData(plugins),
|
||||
allContentLoadedResult.globalData,
|
||||
);
|
||||
|
||||
return {routes, globalData};
|
||||
}
|
||||
|
||||
export type LoadPluginsResult = {
|
||||
plugins: LoadedPlugin[];
|
||||
routes: RouteConfig[];
|
||||
globalData: GlobalData;
|
||||
};
|
||||
|
||||
type ContentLoadedResult = {routes: RouteConfig[]; globalData: GlobalData};
|
||||
|
||||
export function mergeGlobalData(...globalDataList: GlobalData[]): GlobalData {
|
||||
const result: GlobalData = {};
|
||||
|
||||
const allPluginIdentifiers: PluginIdentifier[] = globalDataList.flatMap(
|
||||
(gd) =>
|
||||
Object.keys(gd).flatMap((name) =>
|
||||
Object.keys(gd[name]!).map((id) => ({name, id})),
|
||||
),
|
||||
);
|
||||
|
||||
allPluginIdentifiers.forEach(({name, id}) => {
|
||||
const allData = globalDataList
|
||||
.map((gd) => gd?.[name]?.[id])
|
||||
.filter((d) => typeof d !== 'undefined');
|
||||
const mergedData =
|
||||
allData.length === 1 ? allData[0] : Object.assign({}, ...allData);
|
||||
result[name] ??= {};
|
||||
result[name]![id] = mergedData;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function mergeResults({
|
||||
contentLoadedResult,
|
||||
allContentLoadedResult,
|
||||
}: {
|
||||
contentLoadedResult: ContentLoadedResult;
|
||||
allContentLoadedResult: ContentLoadedResult;
|
||||
}): ContentLoadedResult {
|
||||
const routes = [
|
||||
...contentLoadedResult.routes,
|
||||
...allContentLoadedResult.routes,
|
||||
];
|
||||
sortRoutes(routes);
|
||||
|
||||
const globalData = mergeGlobalData(
|
||||
contentLoadedResult.globalData,
|
||||
allContentLoadedResult.globalData,
|
||||
);
|
||||
|
||||
return {routes, globalData};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugins and run their lifecycle functions.
|
||||
*/
|
||||
export async function loadPlugins(
|
||||
context: LoadContext,
|
||||
): Promise<LoadPluginsResult> {
|
||||
return PerfLogger.async('Plugins - loadPlugins', async () => {
|
||||
return PerfLogger.async('Load plugins', async () => {
|
||||
const initializedPlugins: InitializedPlugin[] = await PerfLogger.async(
|
||||
'Plugins - initPlugins',
|
||||
'Init plugins',
|
||||
() => initPlugins(context),
|
||||
);
|
||||
|
||||
// TODO probably not the ideal place to hardcode those plugins
|
||||
initializedPlugins.push(
|
||||
createBootstrapPlugin(context),
|
||||
createMDXFallbackPlugin(context),
|
||||
);
|
||||
|
||||
const plugins = await executePluginsLoadContent({
|
||||
const plugins = await executeAllPluginsContentLoading({
|
||||
plugins: initializedPlugins,
|
||||
context,
|
||||
});
|
||||
|
||||
const contentLoadedResult = await executePluginsContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
});
|
||||
|
||||
const allContentLoadedResult = await executePluginsAllContentLoaded({
|
||||
const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
});
|
||||
|
||||
const {routes, globalData} = mergeResults({
|
||||
contentLoadedResult,
|
||||
plugins,
|
||||
allContentLoadedResult,
|
||||
});
|
||||
|
||||
|
@ -336,25 +278,6 @@ export async function loadPlugins(
|
|||
});
|
||||
}
|
||||
|
||||
export function getPluginByIdentifier({
|
||||
plugins,
|
||||
pluginIdentifier,
|
||||
}: {
|
||||
pluginIdentifier: PluginIdentifier;
|
||||
plugins: LoadedPlugin[];
|
||||
}): LoadedPlugin {
|
||||
const plugin = plugins.find(
|
||||
(p) =>
|
||||
p.name === pluginIdentifier.name && p.options.id === pluginIdentifier.id,
|
||||
);
|
||||
if (!plugin) {
|
||||
throw new Error(
|
||||
logger.interpolate`Plugin not found for identifier ${pluginIdentifier.name}@${pluginIdentifier.id}`,
|
||||
);
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
|
||||
export async function reloadPlugin({
|
||||
pluginIdentifier,
|
||||
plugins: previousPlugins,
|
||||
|
@ -364,34 +287,39 @@ export async function reloadPlugin({
|
|||
plugins: LoadedPlugin[];
|
||||
context: LoadContext;
|
||||
}): Promise<LoadPluginsResult> {
|
||||
return PerfLogger.async('Plugins - reloadPlugin', async () => {
|
||||
const plugin = getPluginByIdentifier({
|
||||
plugins: previousPlugins,
|
||||
pluginIdentifier,
|
||||
});
|
||||
return PerfLogger.async(
|
||||
`Reload plugin ${formatPluginName(pluginIdentifier)}`,
|
||||
async () => {
|
||||
const previousPlugin = getPluginByIdentifier({
|
||||
plugins: previousPlugins,
|
||||
pluginIdentifier,
|
||||
});
|
||||
const plugin = await executePluginContentLoading({
|
||||
plugin: previousPlugin,
|
||||
context,
|
||||
});
|
||||
|
||||
const reloadedPlugin = await executePluginLoadContent({plugin, context});
|
||||
/*
|
||||
// TODO Docusaurus v4 - upgrade to Node 20, use array.with()
|
||||
const plugins = previousPlugins.with(
|
||||
previousPlugins.indexOf(plugin),
|
||||
reloadedPlugin,
|
||||
previousPlugins.indexOf(previousPlugin),
|
||||
plugin,
|
||||
);
|
||||
*/
|
||||
const plugins = [...previousPlugins];
|
||||
plugins[previousPlugins.indexOf(previousPlugin)] = plugin;
|
||||
|
||||
// TODO optimize this, we shouldn't need to re-run this lifecycle
|
||||
const contentLoadedResult = await executePluginsContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
});
|
||||
const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
});
|
||||
|
||||
const allContentLoadedResult = await executePluginsAllContentLoaded({
|
||||
plugins,
|
||||
context,
|
||||
});
|
||||
const {routes, globalData} = mergeResults({
|
||||
plugins,
|
||||
allContentLoadedResult,
|
||||
});
|
||||
|
||||
const {routes, globalData} = mergeResults({
|
||||
contentLoadedResult,
|
||||
allContentLoadedResult,
|
||||
});
|
||||
|
||||
return {plugins, routes, globalData};
|
||||
});
|
||||
return {plugins, routes, globalData};
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
108
packages/docusaurus/src/server/plugins/pluginsUtils.ts
Normal file
108
packages/docusaurus/src/server/plugins/pluginsUtils.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* 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 _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import type {
|
||||
AllContent,
|
||||
GlobalData,
|
||||
InitializedPlugin,
|
||||
LoadedPlugin,
|
||||
PluginIdentifier,
|
||||
RouteConfig,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
export function getPluginByIdentifier<P extends InitializedPlugin>({
|
||||
plugins,
|
||||
pluginIdentifier,
|
||||
}: {
|
||||
pluginIdentifier: PluginIdentifier;
|
||||
plugins: P[];
|
||||
}): P {
|
||||
const plugin = plugins.find(
|
||||
(p) =>
|
||||
p.name === pluginIdentifier.name && p.options.id === pluginIdentifier.id,
|
||||
);
|
||||
if (!plugin) {
|
||||
throw new Error(
|
||||
logger.interpolate`Plugin not found for identifier ${formatPluginName(
|
||||
pluginIdentifier,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
|
||||
export function aggregateAllContent(loadedPlugins: LoadedPlugin[]): AllContent {
|
||||
return _.chain(loadedPlugins)
|
||||
.groupBy((item) => item.name)
|
||||
.mapValues((nameItems) =>
|
||||
_.chain(nameItems)
|
||||
.groupBy((item) => item.options.id)
|
||||
.mapValues((idItems) => idItems[0]!.content)
|
||||
.value(),
|
||||
)
|
||||
.value();
|
||||
}
|
||||
|
||||
export function aggregateRoutes(loadedPlugins: LoadedPlugin[]): RouteConfig[] {
|
||||
return loadedPlugins.flatMap((p) => p.routes);
|
||||
}
|
||||
|
||||
export function aggregateGlobalData(loadedPlugins: LoadedPlugin[]): GlobalData {
|
||||
const globalData: GlobalData = {};
|
||||
loadedPlugins.forEach((plugin) => {
|
||||
if (plugin.globalData !== undefined) {
|
||||
globalData[plugin.name] ??= {};
|
||||
globalData[plugin.name]![plugin.options.id] = plugin.globalData;
|
||||
}
|
||||
});
|
||||
|
||||
return globalData;
|
||||
}
|
||||
|
||||
export function mergeGlobalData(...globalDataList: GlobalData[]): GlobalData {
|
||||
const result: GlobalData = {};
|
||||
|
||||
const allPluginIdentifiers: PluginIdentifier[] = globalDataList.flatMap(
|
||||
(gd) =>
|
||||
Object.keys(gd).flatMap((name) =>
|
||||
Object.keys(gd[name]!).map((id) => ({name, id})),
|
||||
),
|
||||
);
|
||||
|
||||
allPluginIdentifiers.forEach(({name, id}) => {
|
||||
const allData = globalDataList
|
||||
.map((gd) => gd?.[name]?.[id])
|
||||
.filter((d) => typeof d !== 'undefined');
|
||||
const mergedData =
|
||||
allData.length === 1 ? allData[0] : Object.assign({}, ...allData);
|
||||
result[name] ??= {};
|
||||
result[name]![id] = mergedData;
|
||||
});
|
||||
|
||||
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}`;
|
||||
}
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
import path from 'path';
|
||||
import type {RuleSetRule} from 'webpack';
|
||||
import type {HtmlTagObject, LoadedPlugin, LoadContext} from '@docusaurus/types';
|
||||
import type {
|
||||
HtmlTagObject,
|
||||
LoadContext,
|
||||
InitializedPlugin,
|
||||
} from '@docusaurus/types';
|
||||
import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader';
|
||||
|
||||
/**
|
||||
|
@ -18,7 +22,7 @@ import type {Options as MDXLoaderOptions} from '@docusaurus/mdx-loader';
|
|||
export function createBootstrapPlugin({
|
||||
siteDir,
|
||||
siteConfig,
|
||||
}: LoadContext): LoadedPlugin {
|
||||
}: LoadContext): InitializedPlugin {
|
||||
const {
|
||||
stylesheets,
|
||||
scripts,
|
||||
|
@ -27,7 +31,6 @@ export function createBootstrapPlugin({
|
|||
} = siteConfig;
|
||||
return {
|
||||
name: 'docusaurus-bootstrap-plugin',
|
||||
content: null,
|
||||
options: {
|
||||
id: 'default',
|
||||
},
|
||||
|
@ -75,10 +78,9 @@ export function createBootstrapPlugin({
|
|||
export function createMDXFallbackPlugin({
|
||||
siteDir,
|
||||
siteConfig,
|
||||
}: LoadContext): LoadedPlugin {
|
||||
}: LoadContext): InitializedPlugin {
|
||||
return {
|
||||
name: 'docusaurus-mdx-fallback-plugin',
|
||||
content: null,
|
||||
options: {
|
||||
id: 'default',
|
||||
},
|
||||
|
|
|
@ -13,14 +13,14 @@ import {
|
|||
} from '@docusaurus/utils';
|
||||
import combinePromises from 'combine-promises';
|
||||
import {loadSiteConfig} from './config';
|
||||
import {loadClientModules} from './clientModules';
|
||||
import {getAllClientModules} from './clientModules';
|
||||
import {loadPlugins, reloadPlugin} from './plugins/plugins';
|
||||
import {loadHtmlTags} from './htmlTags';
|
||||
import {loadSiteMetadata} from './siteMetadata';
|
||||
import {createSiteMetadata, loadSiteVersion} from './siteMetadata';
|
||||
import {loadI18n} from './i18n';
|
||||
import {
|
||||
loadSiteCodeTranslations,
|
||||
getPluginsDefaultCodeTranslationMessages,
|
||||
getPluginsDefaultCodeTranslations,
|
||||
} from './translations/translations';
|
||||
import {PerfLogger} from '../utils';
|
||||
import {generateSiteFiles} from './codegen/codegen';
|
||||
|
@ -76,9 +76,15 @@ export async function loadContext(
|
|||
} = params;
|
||||
const generatedFilesDir = path.resolve(siteDir, GENERATED_FILES_DIR_NAME);
|
||||
|
||||
const {siteConfig: initialSiteConfig, siteConfigPath} = await loadSiteConfig({
|
||||
siteDir,
|
||||
customConfigFilePath,
|
||||
const {
|
||||
siteVersion,
|
||||
loadSiteConfig: {siteConfig: initialSiteConfig, siteConfigPath},
|
||||
} = await combinePromises({
|
||||
siteVersion: loadSiteVersion(siteDir),
|
||||
loadSiteConfig: loadSiteConfig({
|
||||
siteDir,
|
||||
customConfigFilePath,
|
||||
}),
|
||||
});
|
||||
|
||||
const i18n = await loadI18n(initialSiteConfig, {locale});
|
||||
|
@ -107,6 +113,7 @@ export async function loadContext(
|
|||
|
||||
return {
|
||||
siteDir,
|
||||
siteVersion,
|
||||
generatedFilesDir,
|
||||
localizationDir,
|
||||
siteConfig,
|
||||
|
@ -118,13 +125,14 @@ export async function loadContext(
|
|||
};
|
||||
}
|
||||
|
||||
async function createSiteProps(
|
||||
function createSiteProps(
|
||||
params: LoadPluginsResult & {context: LoadContext},
|
||||
): Promise<Props> {
|
||||
): Props {
|
||||
const {plugins, routes, context} = params;
|
||||
const {
|
||||
generatedFilesDir,
|
||||
siteDir,
|
||||
siteVersion,
|
||||
siteConfig,
|
||||
siteConfigPath,
|
||||
outDir,
|
||||
|
@ -136,19 +144,12 @@ async function createSiteProps(
|
|||
|
||||
const {headTags, preBodyTags, postBodyTags} = loadHtmlTags(plugins);
|
||||
|
||||
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}),
|
||||
),
|
||||
});
|
||||
const siteMetadata = createSiteMetadata({plugins, siteVersion});
|
||||
|
||||
const codeTranslations = {
|
||||
...getPluginsDefaultCodeTranslations({plugins}),
|
||||
...siteCodeTranslations,
|
||||
};
|
||||
|
||||
handleDuplicateRoutes(routes, siteConfig.onDuplicateRoutes);
|
||||
const routesPaths = getRoutesPaths(routes, baseUrl);
|
||||
|
@ -157,6 +158,7 @@ async function createSiteProps(
|
|||
siteConfig,
|
||||
siteConfigPath,
|
||||
siteMetadata,
|
||||
siteVersion,
|
||||
siteDir,
|
||||
outDir,
|
||||
baseUrl,
|
||||
|
@ -181,7 +183,7 @@ async function createSiteFiles({
|
|||
site: Site;
|
||||
globalData: GlobalData;
|
||||
}) {
|
||||
return PerfLogger.async('Load - createSiteFiles', async () => {
|
||||
return PerfLogger.async('Create site files', async () => {
|
||||
const {
|
||||
props: {
|
||||
plugins,
|
||||
|
@ -194,7 +196,7 @@ async function createSiteFiles({
|
|||
baseUrl,
|
||||
},
|
||||
} = site;
|
||||
const clientModules = loadClientModules(plugins);
|
||||
const clientModules = getAllClientModules(plugins);
|
||||
await generateSiteFiles({
|
||||
generatedFilesDir,
|
||||
clientModules,
|
||||
|
@ -216,13 +218,11 @@ async function createSiteFiles({
|
|||
* it generates temp files in the `.docusaurus` folder for the bundler.
|
||||
*/
|
||||
export async function loadSite(params: LoadContextParams): Promise<Site> {
|
||||
PerfLogger.start('Load - loadContext');
|
||||
const context = await loadContext(params);
|
||||
PerfLogger.end('Load - loadContext');
|
||||
const context = await PerfLogger.async('Load context', () =>
|
||||
loadContext(params),
|
||||
);
|
||||
|
||||
PerfLogger.start('Load - loadPlugins');
|
||||
const {plugins, routes, globalData} = await loadPlugins(context);
|
||||
PerfLogger.end('Load - loadPlugins');
|
||||
|
||||
const props = await createSiteProps({plugins, routes, globalData, context});
|
||||
|
||||
|
@ -247,10 +247,6 @@ export async function reloadSitePlugin(
|
|||
site: Site,
|
||||
pluginIdentifier: PluginIdentifier,
|
||||
): Promise<Site> {
|
||||
console.log(
|
||||
`reloadSitePlugin ${pluginIdentifier.name}@${pluginIdentifier.id}`,
|
||||
);
|
||||
|
||||
const {plugins, routes, globalData} = await reloadPlugin({
|
||||
pluginIdentifier,
|
||||
plugins: site.props.plugins,
|
||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
|||
SiteMetadata,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
async function getPackageJsonVersion(
|
||||
async function loadPackageJsonVersion(
|
||||
packageJsonPath: string,
|
||||
): Promise<string | undefined> {
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
|
@ -24,14 +24,20 @@ async function getPackageJsonVersion(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
async function getPackageJsonName(
|
||||
async function loadPackageJsonName(
|
||||
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 getPluginVersion(
|
||||
export async function loadSiteVersion(
|
||||
siteDir: string,
|
||||
): Promise<string | undefined> {
|
||||
return loadPackageJsonVersion(path.join(siteDir, 'package.json'));
|
||||
}
|
||||
|
||||
export async function loadPluginVersion(
|
||||
pluginPath: string,
|
||||
siteDir: string,
|
||||
): Promise<PluginVersionInformation> {
|
||||
|
@ -52,8 +58,8 @@ export async function getPluginVersion(
|
|||
}
|
||||
return {
|
||||
type: 'package',
|
||||
name: await getPackageJsonName(packageJsonPath),
|
||||
version: await getPackageJsonVersion(packageJsonPath),
|
||||
name: await loadPackageJsonName(packageJsonPath),
|
||||
version: await loadPackageJsonVersion(packageJsonPath),
|
||||
};
|
||||
}
|
||||
potentialPluginPackageJsonDirectory = path.dirname(
|
||||
|
@ -89,18 +95,16 @@ Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?
|
|||
);
|
||||
}
|
||||
|
||||
export async function loadSiteMetadata({
|
||||
export function createSiteMetadata({
|
||||
siteVersion,
|
||||
plugins,
|
||||
siteDir,
|
||||
}: {
|
||||
siteVersion: string | undefined;
|
||||
plugins: LoadedPlugin[];
|
||||
siteDir: string;
|
||||
}): Promise<SiteMetadata> {
|
||||
}): SiteMetadata {
|
||||
const siteMetadata: SiteMetadata = {
|
||||
docusaurusVersion: DOCUSAURUS_VERSION,
|
||||
siteVersion: await getPackageJsonVersion(
|
||||
path.join(siteDir, 'package.json'),
|
||||
),
|
||||
siteVersion,
|
||||
pluginVersions: Object.fromEntries(
|
||||
plugins
|
||||
.filter(({version: {type}}) => type !== 'synthetic')
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
readCodeTranslationFileContent,
|
||||
type WriteTranslationsOptions,
|
||||
localizePluginTranslationFile,
|
||||
getPluginsDefaultCodeTranslationMessages,
|
||||
loadPluginsDefaultCodeTranslationMessages,
|
||||
applyDefaultCodeTranslations,
|
||||
} from '../translations';
|
||||
import type {
|
||||
|
@ -537,7 +537,7 @@ describe('readCodeTranslationFileContent', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getPluginsDefaultCodeTranslationMessages', () => {
|
||||
describe('loadPluginsDefaultCodeTranslationMessages', () => {
|
||||
function createTestPlugin(
|
||||
fn: InitializedPlugin['getDefaultCodeTranslationMessages'],
|
||||
): InitializedPlugin {
|
||||
|
@ -547,14 +547,14 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
|||
it('works for empty plugins', async () => {
|
||||
const plugins: InitializedPlugin[] = [];
|
||||
await expect(
|
||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||
).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('works for 1 plugin without lifecycle', async () => {
|
||||
const plugins: InitializedPlugin[] = [createTestPlugin(undefined)];
|
||||
await expect(
|
||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||
).resolves.toEqual({});
|
||||
});
|
||||
|
||||
|
@ -566,7 +566,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
|||
})),
|
||||
];
|
||||
await expect(
|
||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||
).resolves.toEqual({
|
||||
a: '1',
|
||||
b: '2',
|
||||
|
@ -585,7 +585,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
|||
})),
|
||||
];
|
||||
await expect(
|
||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||
).resolves.toEqual({
|
||||
a: '1',
|
||||
b: '2',
|
||||
|
@ -613,7 +613,7 @@ describe('getPluginsDefaultCodeTranslationMessages', () => {
|
|||
createTestPlugin(undefined),
|
||||
];
|
||||
await expect(
|
||||
getPluginsDefaultCodeTranslationMessages(plugins),
|
||||
loadPluginsDefaultCodeTranslationMessages(plugins),
|
||||
).resolves.toEqual({
|
||||
// merge, last plugin wins
|
||||
b: '2',
|
||||
|
|
|
@ -20,6 +20,7 @@ import type {
|
|||
TranslationFile,
|
||||
CodeTranslations,
|
||||
InitializedPlugin,
|
||||
LoadedPlugin,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
export type WriteTranslationsOptions = {
|
||||
|
@ -242,17 +243,33 @@ export async function localizePluginTranslationFile({
|
|||
return translationFile;
|
||||
}
|
||||
|
||||
export async function getPluginsDefaultCodeTranslationMessages(
|
||||
export function mergeCodeTranslations(
|
||||
codeTranslations: CodeTranslations[],
|
||||
): CodeTranslations {
|
||||
return codeTranslations.reduce(
|
||||
(allCodeTranslations, current) => ({
|
||||
...allCodeTranslations,
|
||||
...current,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
export async function loadPluginsDefaultCodeTranslationMessages(
|
||||
plugins: InitializedPlugin[],
|
||||
): Promise<CodeTranslations> {
|
||||
const pluginsMessages = await Promise.all(
|
||||
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
|
||||
);
|
||||
return mergeCodeTranslations(pluginsMessages);
|
||||
}
|
||||
|
||||
return pluginsMessages.reduce(
|
||||
(allMessages, pluginMessages) => ({...allMessages, ...pluginMessages}),
|
||||
{},
|
||||
);
|
||||
export function getPluginsDefaultCodeTranslations({
|
||||
plugins,
|
||||
}: {
|
||||
plugins: LoadedPlugin[];
|
||||
}): CodeTranslations {
|
||||
return mergeCodeTranslations(plugins.map((p) => p.defaultCodeTranslations));
|
||||
}
|
||||
|
||||
export function applyDefaultCodeTranslations({
|
||||
|
|
|
@ -47,12 +47,11 @@ export async function loadAppRenderer({
|
|||
}: {
|
||||
serverBundlePath: string;
|
||||
}): Promise<AppRenderer> {
|
||||
console.log(`SSG - Load server bundle`);
|
||||
PerfLogger.start(`SSG - Load server bundle`);
|
||||
const source = await fs.readFile(serverBundlePath);
|
||||
PerfLogger.end(`SSG - Load server bundle`);
|
||||
const source = await PerfLogger.async(`Load server bundle`, () =>
|
||||
fs.readFile(serverBundlePath),
|
||||
);
|
||||
PerfLogger.log(
|
||||
`SSG - Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`,
|
||||
`Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`,
|
||||
);
|
||||
|
||||
const filename = path.basename(serverBundlePath);
|
||||
|
@ -69,14 +68,16 @@ export async function loadAppRenderer({
|
|||
require: createRequire(serverBundlePath),
|
||||
};
|
||||
|
||||
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`);
|
||||
const serverEntry = await PerfLogger.async(
|
||||
`Evaluate server bundle`,
|
||||
() =>
|
||||
evaluate(
|
||||
source,
|
||||
/* filename: */ filename,
|
||||
/* scope: */ globals,
|
||||
/* includeGlobals: */ true,
|
||||
) as {default?: AppRenderer},
|
||||
);
|
||||
|
||||
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
|
||||
throw new Error(
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 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
|
||||
|
@ -11,6 +12,22 @@ import logger from '@docusaurus/logger';
|
|||
export const PerfDebuggingEnabled: boolean =
|
||||
!!process.env.DOCUSAURUS_PERF_LOGGER;
|
||||
|
||||
const Thresholds = {
|
||||
min: 5,
|
||||
yellow: 100,
|
||||
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;
|
||||
|
@ -32,19 +49,40 @@ 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!`);
|
||||
} else if (duration > Thresholds.yellow) {
|
||||
return logger.yellow(`${duration.toFixed(2)} ms`);
|
||||
} else {
|
||||
return logger.green(`${duration.toFixed(2)} ms`);
|
||||
}
|
||||
};
|
||||
|
||||
const start: PerfLoggerAPI['start'] = (label) => console.time(prefix + label);
|
||||
const logDuration = (label: string, duration: number) => {
|
||||
if (duration < Thresholds.min) {
|
||||
return;
|
||||
}
|
||||
console.log(`${PerfPrefix + label} - ${formatDuration(duration)}`);
|
||||
};
|
||||
|
||||
const end: PerfLoggerAPI['end'] = (label) => console.timeEnd(prefix + label);
|
||||
const start: PerfLoggerAPI['start'] = (label) => performance.mark(label);
|
||||
|
||||
const end: PerfLoggerAPI['end'] = (label) => {
|
||||
const {duration} = performance.measure(label);
|
||||
performance.clearMarks(label);
|
||||
logDuration(applyParentPrefix(label), duration);
|
||||
};
|
||||
|
||||
const log: PerfLoggerAPI['log'] = (label: string) =>
|
||||
console.log(prefix + label);
|
||||
console.log(PerfPrefix + applyParentPrefix(label));
|
||||
|
||||
const async: PerfLoggerAPI['async'] = async (label, asyncFn) => {
|
||||
start(label);
|
||||
const result = await asyncFn();
|
||||
end(label);
|
||||
const finalLabel = applyParentPrefix(label);
|
||||
const before = performance.now();
|
||||
const result = await ParentPrefix.run(finalLabel, () => asyncFn());
|
||||
const duration = performance.now() - before;
|
||||
logDuration(finalLabel, duration);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/eslint-plugin",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "ESLint plugin to enforce best Docusaurus practices.",
|
||||
"main": "lib/index.js",
|
||||
"keywords": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/lqip-loader",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Low Quality Image Placeholders (LQIP) loader for webpack.",
|
||||
"main": "lib/index.js",
|
||||
"publishConfig": {
|
||||
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"sharp": "^0.32.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "stylelint-copyright",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.1",
|
||||
"description": "Stylelint plugin to check CSS files for a copyright header.",
|
||||
"main": "lib/index.js",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -16,6 +16,7 @@ architecting
|
|||
Astro
|
||||
atrule
|
||||
Autoconverted
|
||||
autofix
|
||||
Autogen
|
||||
autogen
|
||||
autogenerating
|
||||
|
@ -243,7 +244,6 @@ Paraiso
|
|||
paraiso
|
||||
pathinfo
|
||||
paularmstrong
|
||||
peaceiris
|
||||
philpl
|
||||
Photoshop
|
||||
photoshop
|
||||
|
@ -328,6 +328,7 @@ solana
|
|||
spâce
|
||||
stackblitz
|
||||
stackblitzrc
|
||||
Stormkit
|
||||
Strikethrough
|
||||
strikethroughs
|
||||
stylelint
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue