Compare commits
24 Commits
ozaki/util
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
26e0bd928c | |
|
|
70415a4cef | |
|
|
a34e3f8f91 | |
|
|
7edfe0e2d1 | |
|
|
debfc87d34 | |
|
|
1a5fe5c412 | |
|
|
821247142e | |
|
|
efbe474e9c | |
|
|
06e70a4f9a | |
|
|
1430c85a82 | |
|
|
8024d9b858 | |
|
|
3877c1254f | |
|
|
6f17d54938 | |
|
|
93a09ea086 | |
|
|
91f93656d8 | |
|
|
9a47ec0581 | |
|
|
9017fb9b1d | |
|
|
465cf4d82c | |
|
|
6c724ed857 | |
|
|
c745021b01 | |
|
|
7938803747 | |
|
|
14bec09670 | |
|
|
032e3b8f4d | |
|
|
6151a41428 |
|
|
@ -85,6 +85,7 @@ module.exports = {
|
|||
ignorePattern: '(eslint-disable|@)',
|
||||
},
|
||||
],
|
||||
'arrow-body-style': OFF,
|
||||
'no-await-in-loop': OFF,
|
||||
'no-case-declarations': WARNING,
|
||||
'no-console': OFF,
|
||||
|
|
@ -347,10 +348,7 @@ module.exports = {
|
|||
ERROR,
|
||||
{'ts-expect-error': 'allow-with-description'},
|
||||
],
|
||||
'@typescript-eslint/consistent-indexed-object-style': [
|
||||
WARNING,
|
||||
'index-signature',
|
||||
],
|
||||
'@typescript-eslint/consistent-indexed-object-style': OFF,
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
WARNING,
|
||||
{disallowTypeAnnotations: false},
|
||||
|
|
|
|||
|
|
@ -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@733dd5d4a5203f238c33806593ec0f5fc5343d8c # 4.2.4
|
||||
|
|
|
|||
131
CHANGELOG.md
131
CHANGELOG.md
|
|
@ -1,5 +1,136 @@
|
|||
# Docusaurus 2 Changelog
|
||||
|
||||
## 3.2.0 (2024-03-29)
|
||||
|
||||
#### :rocket: New Feature
|
||||
|
||||
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-plugin-sitemap`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
|
||||
- [#9954](https://github.com/facebook/docusaurus/pull/9954) feat(sitemap): add support for "lastmod" ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils-validation`, `docusaurus-utils`
|
||||
- [#9912](https://github.com/facebook/docusaurus/pull/9912) feat(blog): add LastUpdateAuthor & LastUpdateTime ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- `docusaurus-plugin-debug`, `docusaurus-types`, `docusaurus`
|
||||
- [#9931](https://github.com/facebook/docusaurus/pull/9931) feat(core): add new plugin allContentLoaded lifecycle ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-theme-translations`
|
||||
- [#9928](https://github.com/facebook/docusaurus/pull/9928) feat(theme-translations) Icelandic (is) ([@Hallinn](https://github.com/Hallinn))
|
||||
- `docusaurus-plugin-content-blog`
|
||||
- [#9886](https://github.com/facebook/docusaurus/pull/9886) feat(blog): allow processing blog posts through a processBlogPosts function ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- [#9838](https://github.com/facebook/docusaurus/pull/9838) feat(blog): add blog pageBasePath plugin option ([@ilg-ul](https://github.com/ilg-ul))
|
||||
- `docusaurus`
|
||||
- [#9681](https://github.com/facebook/docusaurus/pull/9681) feat(swizzle): ask user preferred language if no language CLI option provided ([@yixiaojiu](https://github.com/yixiaojiu))
|
||||
- `create-docusaurus`, `docusaurus-utils`
|
||||
- [#9442](https://github.com/facebook/docusaurus/pull/9442) feat(create-docusaurus): ask user for preferred language when no language CLI option provided ([@Rafael-Martins](https://github.com/Rafael-Martins))
|
||||
- `docusaurus-plugin-vercel-analytics`
|
||||
- [#9687](https://github.com/facebook/docusaurus/pull/9687) feat(plugin-vercel-analytics): add new vercel analytics plugin ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- `docusaurus-mdx-loader`
|
||||
- [#9684](https://github.com/facebook/docusaurus/pull/9684) feat(mdx-loader): the table-of-contents should display toc/headings of imported MDX partials ([@anatolykopyl](https://github.com/anatolykopyl))
|
||||
|
||||
#### :bug: Bug Fix
|
||||
|
||||
- `docusaurus-mdx-loader`
|
||||
- [#9999](https://github.com/facebook/docusaurus/pull/9999) fix(mdx-loader): Ignore contentTitle coming after Markdown thematicBreak ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-theme-search-algolia`
|
||||
- [#9945](https://github.com/facebook/docusaurus/pull/9945) fix(a11y): move focus algolia-search focus back to search input on Escape ([@mxschmitt](https://github.com/mxschmitt))
|
||||
- `docusaurus-plugin-content-blog`
|
||||
- [#9920](https://github.com/facebook/docusaurus/pull/9920) fix(blog): apply trailing slash to blog feed ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- `docusaurus-theme-classic`
|
||||
- [#9944](https://github.com/facebook/docusaurus/pull/9944) fix(theme): improve a11y of DocSidebarItemCategory expand/collapsed button ([@mxschmitt](https://github.com/mxschmitt))
|
||||
- `docusaurus-theme-translations`
|
||||
- [#9915](https://github.com/facebook/docusaurus/pull/9915) fix(theme-translations): complete and modify Japanese translations ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
|
||||
- [#9910](https://github.com/facebook/docusaurus/pull/9910) fix(theme-translations): add Japanese translations ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
|
||||
- [#9872](https://github.com/facebook/docusaurus/pull/9872) fix(theme-translations): complete and improve Spanish theme translations ([@4troDev](https://github.com/4troDev))
|
||||
- [#9812](https://github.com/facebook/docusaurus/pull/9812) fix(i18n): add missing theme translations for fa locale ([@VahidNaderi](https://github.com/VahidNaderi))
|
||||
- `docusaurus-utils`
|
||||
- [#9897](https://github.com/facebook/docusaurus/pull/9897) fix(mdx-loader): mdx-code-block should support CRLF ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus`
|
||||
- [#9878](https://github.com/facebook/docusaurus/pull/9878) fix(core): fix default i18n calendar used, infer it from locale if possible ([@slorber](https://github.com/slorber))
|
||||
- [#9852](https://github.com/facebook/docusaurus/pull/9852) fix(core): ensure core error boundary is able to render theme layout ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-remark-plugin-npm2yarn`
|
||||
- [#9861](https://github.com/facebook/docusaurus/pull/9861) fix(remark-npm2yarn): update npm-to-yarn from 2.0.0 to 2.2.1, fix pnpm extra args syntax ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- `docusaurus-theme-classic`, `docusaurus-theme-translations`
|
||||
- [#9851](https://github.com/facebook/docusaurus/pull/9851) fix(theme-classic): should use plurals for category items description ([@baradusov](https://github.com/baradusov))
|
||||
|
||||
#### :running_woman: Performance
|
||||
|
||||
- `docusaurus-types`, `docusaurus-utils`, `docusaurus`
|
||||
- [#9975](https://github.com/facebook/docusaurus/pull/9975) refactor(core): improve dev perf, fine-grained site reloads - part 3 ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-types`, `docusaurus`
|
||||
- [#9968](https://github.com/facebook/docusaurus/pull/9968) refactor(core): improve dev perf, fine-grained site reloads - part2 ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-types`, `docusaurus`
|
||||
- [#9903](https://github.com/facebook/docusaurus/pull/9903) refactor(core): improve dev perf, fine-grained site reloads - part1 ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-utils`
|
||||
- [#9890](https://github.com/facebook/docusaurus/pull/9890) perf: optimize getFileCommitDate, make it async ([@slorber](https://github.com/slorber))
|
||||
- `docusaurus`
|
||||
- [#9798](https://github.com/facebook/docusaurus/pull/9798) refactor(core): internalize, simplify and optimize the SSG logic ([@slorber](https://github.com/slorber))
|
||||
|
||||
#### :nail_care: Polish
|
||||
|
||||
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`
|
||||
- [#9868](https://github.com/facebook/docusaurus/pull/9868) refactor(theme): dates should be formatted on the client-side instead of in nodejs code ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- `docusaurus-plugin-content-blog`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-types`
|
||||
- [#9669](https://github.com/facebook/docusaurus/pull/9669) refactor(theme): use JSON-LD instead of microdata for blog structured data ([@johnnyreilly](https://github.com/johnnyreilly))
|
||||
- `docusaurus-plugin-content-docs`
|
||||
- [#9839](https://github.com/facebook/docusaurus/pull/9839) refactor(blog): improve doc global data hook error message + add doc warning to blogOnly mode ([@OzakIOne](https://github.com/OzakIOne))
|
||||
|
||||
#### :memo: Documentation
|
||||
|
||||
- [#9937](https://github.com/facebook/docusaurus/pull/9937) docs: use official GitHub Action to deploy to GitHub Pages ([@vlad-nestorov](https://github.com/vlad-nestorov))
|
||||
- [#9971](https://github.com/facebook/docusaurus/pull/9971) docs: replace VuePress by VitePress on tool comparison section ([@sunkanmii](https://github.com/sunkanmii))
|
||||
- [#9914](https://github.com/facebook/docusaurus/pull/9914) docs: update legacy MDX v1 links to markdown links ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- [#9913](https://github.com/facebook/docusaurus/pull/9913) docs: update legacy MDX v1 links to markdown links ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- [#9906](https://github.com/facebook/docusaurus/pull/9906) docs: emphasize "index slug" convention ([@Josh-Cena](https://github.com/Josh-Cena))
|
||||
- [#9877](https://github.com/facebook/docusaurus/pull/9877) docs: fix typos in deployment.mdx ([@Oreoxmt](https://github.com/Oreoxmt))
|
||||
- [#9845](https://github.com/facebook/docusaurus/pull/9845) docs: typo ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- [#9816](https://github.com/facebook/docusaurus/pull/9816) docs: Add docs for Mermaid Component ([@Its-Just-Nans](https://github.com/Its-Just-Nans))
|
||||
|
||||
#### :robot: Dependencies
|
||||
|
||||
- [#9981](https://github.com/facebook/docusaurus/pull/9981) chore(deps): bump actions/dependency-review-action from 4.1.3 to 4.2.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9982](https://github.com/facebook/docusaurus/pull/9982) chore(deps): bump katex from 0.16.8 to 0.16.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9983](https://github.com/facebook/docusaurus/pull/9983) chore(deps): bump express from 4.18.2 to 4.19.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9977](https://github.com/facebook/docusaurus/pull/9977) chore(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9958](https://github.com/facebook/docusaurus/pull/9958) chore(deps): bump follow-redirects from 1.15.4 to 1.15.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9892](https://github.com/facebook/docusaurus/pull/9892) chore(deps): bump actions/dependency-review-action from 4.1.2 to 4.1.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9869](https://github.com/facebook/docusaurus/pull/9869) chore(deps): bump actions/dependency-review-action from 4.0.0 to 4.1.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9874](https://github.com/facebook/docusaurus/pull/9874) chore(deps): bump ip from 2.0.0 to 2.0.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9843](https://github.com/facebook/docusaurus/pull/9843) chore(deps): bump actions/setup-node from 4.0.1 to 4.0.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9824](https://github.com/facebook/docusaurus/pull/9824) chore(deps): bump treosh/lighthouse-ci-action from 10.1.0 to 11.4.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
- [#9823](https://github.com/facebook/docusaurus/pull/9823) chore(deps): bump marocchino/sticky-pull-request-comment from 2.8.0 to 2.9.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
|
||||
|
||||
#### :wrench: Maintenance
|
||||
|
||||
- `docusaurus-plugin-client-redirects`, `docusaurus-plugin-content-docs`, `docusaurus-utils-common`, `docusaurus-utils-validation`, `docusaurus-utils`, `docusaurus`
|
||||
- [#9972](https://github.com/facebook/docusaurus/pull/9972) refactor(utils): remove duplicated function ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- Other
|
||||
- [#9965](https://github.com/facebook/docusaurus/pull/9965) refactor(website): organise blog posts by year ([@GingerGeek](https://github.com/GingerGeek))
|
||||
- [#9865](https://github.com/facebook/docusaurus/pull/9865) chore(website): update @crowdin/crowdin-api-client ([@chris-bateman](https://github.com/chris-bateman))
|
||||
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils`
|
||||
- [#9963](https://github.com/facebook/docusaurus/pull/9963) refactor(docs,blog): last update timestamp should be in milliseconds instead of seconds ([@slorber](https://github.com/slorber))
|
||||
|
||||
#### Committers: 22
|
||||
|
||||
- Aolin ([@Oreoxmt](https://github.com/Oreoxmt))
|
||||
- Anatoly Kopyl ([@anatolykopyl](https://github.com/anatolykopyl))
|
||||
- Chris Bateman ([@chris-bateman](https://github.com/chris-bateman))
|
||||
- Fafowora Sunkanmi ([@sunkanmii](https://github.com/sunkanmii))
|
||||
- Hallbjörn Magnússon ([@Hallinn](https://github.com/Hallinn))
|
||||
- John Reilly ([@johnnyreilly](https://github.com/johnnyreilly))
|
||||
- Joshua Chen ([@Josh-Cena](https://github.com/Josh-Cena))
|
||||
- Josue [4tro] A ([@4troDev](https://github.com/4troDev))
|
||||
- Liviu Ionescu ([@ilg-ul](https://github.com/ilg-ul))
|
||||
- Max Schmitt ([@mxschmitt](https://github.com/mxschmitt))
|
||||
- Rafael Martins ([@Rafael-Martins](https://github.com/Rafael-Martins))
|
||||
- Sébastien Lorber ([@slorber](https://github.com/slorber))
|
||||
- Vahid Naderi ([@VahidNaderi](https://github.com/VahidNaderi))
|
||||
- Vlad Nestorov ([@vlad-nestorov](https://github.com/vlad-nestorov))
|
||||
- Zed Spencer-Milnes ([@GingerGeek](https://github.com/GingerGeek))
|
||||
- axel7083 ([@axel7083](https://github.com/axel7083))
|
||||
- krinza.eth ([@kaymomin](https://github.com/kaymomin))
|
||||
- n4n5 ([@Its-Just-Nans](https://github.com/Its-Just-Nans))
|
||||
- ozaki ([@OzakIOne](https://github.com/OzakIOne))
|
||||
- suenryu ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
|
||||
- Нуриль Барадусов ([@baradusov](https://github.com/baradusov))
|
||||
- 翊小久 ([@yixiaojiu](https://github.com/yixiaojiu))
|
||||
|
||||
## 3.1.1 (2024-01-26)
|
||||
|
||||
#### :bug: Bug Fix
|
||||
|
|
|
|||
|
|
@ -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.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npx --package netlify-cli netlify dev"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "argos",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Argos visual diff tests",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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": [
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"useNx": false,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "create-docusaurus",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"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.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"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.0",
|
||||
"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.0",
|
||||
"@docusaurus/preset-classic": "3.2.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
|
|
@ -24,9 +24,9 @@
|
|||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.0.0",
|
||||
"@docusaurus/tsconfig": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/tsconfig": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"typescript": "~5.2.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docusaurus-2-classic-template",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"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.0",
|
||||
"@docusaurus/preset-classic": "3.2.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0"
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/cssnano-preset",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"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.0",
|
||||
"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.0",
|
||||
"description": "Docusaurus Loader for MDX",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@mdx-js/mdx": "^3.0.0",
|
||||
"@slorber/remark-comment": "^1.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
"webpack": "^5.88.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@types/escape-html": "^1.0.2",
|
||||
"@types/mdast": "^4.0.2",
|
||||
"@types/stringify-object": "^3.3.1",
|
||||
|
|
|
|||
|
|
@ -71,6 +71,21 @@ some **markdown** *content*
|
|||
expect(result.data.contentTitle).toBeUndefined();
|
||||
});
|
||||
|
||||
it('ignore contentTitle if after thematic break', async () => {
|
||||
const result = await process(`
|
||||
|
||||
Hey
|
||||
|
||||
---
|
||||
|
||||
# contentTitle 1
|
||||
|
||||
some **markdown** *content*
|
||||
`);
|
||||
|
||||
expect(result.data.contentTitle).toBeUndefined();
|
||||
});
|
||||
|
||||
it('is able to decently serialize Markdown syntax', async () => {
|
||||
const result = await process(`
|
||||
# some **markdown** \`content\` _italic_
|
||||
|
|
|
|||
|
|
@ -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.0",
|
||||
"description": "Docusaurus module type aliases.",
|
||||
"types": "./src/index.d.ts",
|
||||
"publishConfig": {
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/react-loadable": "5.5.2",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@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.0",
|
||||
"description": "Client redirects plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,18 +18,18 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"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.0"
|
||||
},
|
||||
"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 {removeTrailingSlash} from '@docusaurus/utils';
|
||||
import {applyTrailingSlash, addTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {
|
||||
applyTrailingSlash,
|
||||
addTrailingSlash,
|
||||
removeTrailingSlash,
|
||||
} from '@docusaurus/utils-common';
|
||||
import {
|
||||
createFromExtensionsRedirects,
|
||||
createToExtensionsRedirects,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {removeSuffix, removeTrailingSlash} from '@docusaurus/utils';
|
||||
import {addTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {
|
||||
addTrailingSlash,
|
||||
removeSuffix,
|
||||
removeTrailingSlash,
|
||||
} 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.0",
|
||||
"description": "Blog plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-content-blog.d.ts",
|
||||
|
|
@ -31,13 +31,13 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/mdx-loader": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
|||
import {
|
||||
posixPath,
|
||||
getFileCommitDate,
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
LAST_UPDATE_FALLBACK,
|
||||
} from '@docusaurus/utils';
|
||||
import pluginContentBlog from '../index';
|
||||
import {validateOptions} from '../options';
|
||||
|
|
@ -539,7 +538,7 @@ describe('last update', () => {
|
|||
'website-blog-with-last-update',
|
||||
);
|
||||
|
||||
const lastUpdateFor = (date: string) => new Date(date).getTime() / 1000;
|
||||
const lastUpdateFor = (date: string) => new Date(date).getTime();
|
||||
|
||||
it('author and time', async () => {
|
||||
const plugin = await getPlugin(
|
||||
|
|
@ -554,14 +553,14 @@ describe('last update', () => {
|
|||
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBe('seb');
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedAt,
|
||||
);
|
||||
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedBy,
|
||||
);
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedAt,
|
||||
);
|
||||
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBe('seb');
|
||||
|
|
@ -570,7 +569,7 @@ describe('last update', () => {
|
|||
);
|
||||
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedBy,
|
||||
);
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
|
||||
lastUpdateFor('2021-01-01'),
|
||||
|
|
@ -591,13 +590,13 @@ describe('last update', () => {
|
|||
expect(blogPosts[0]?.metadata.title).toBe('Author');
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedAt,
|
||||
);
|
||||
|
||||
expect(blogPosts[1]?.metadata.title).toBe('Nothing');
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedAt,
|
||||
);
|
||||
|
||||
expect(blogPosts[2]?.metadata.title).toBe('Both');
|
||||
|
|
@ -628,7 +627,7 @@ describe('last update', () => {
|
|||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedBy,
|
||||
);
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
|
|
@ -636,7 +635,7 @@ describe('last update', () => {
|
|||
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
LAST_UPDATE_FALLBACK.lastUpdatedBy,
|
||||
);
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
normalizeUrl,
|
||||
docuHash,
|
||||
aliasedSitePath,
|
||||
aliasedSitePathToRelativePath,
|
||||
getPluginI18nPath,
|
||||
posixPath,
|
||||
addTrailingPathSeparator,
|
||||
|
|
@ -33,7 +34,12 @@ import {createBlogFeedFiles} from './feed';
|
|||
|
||||
import {toTagProp, toTagsProp} from './props';
|
||||
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
|
||||
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
|
||||
import type {
|
||||
LoadContext,
|
||||
Plugin,
|
||||
HtmlTags,
|
||||
RouteMetadata,
|
||||
} from '@docusaurus/types';
|
||||
import type {
|
||||
PluginOptions,
|
||||
BlogPostFrontMatter,
|
||||
|
|
@ -273,6 +279,15 @@ export default async function pluginContentBlog(
|
|||
JSON.stringify(blogMetadata, null, 2),
|
||||
);
|
||||
|
||||
function createBlogPostRouteMetadata(
|
||||
blogPostMeta: BlogPostMetadata,
|
||||
): RouteMetadata {
|
||||
return {
|
||||
sourceFilePath: aliasedSitePathToRelativePath(blogPostMeta.source),
|
||||
lastUpdatedAt: blogPostMeta.lastUpdatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
// Create routes for blog entries.
|
||||
await Promise.all(
|
||||
blogPosts.map(async (blogPost) => {
|
||||
|
|
@ -292,6 +307,7 @@ export default async function pluginContentBlog(
|
|||
sidebar: aliasedSource(sidebarProp),
|
||||
content: metadata.source,
|
||||
},
|
||||
metadata: createBlogPostRouteMetadata(metadata),
|
||||
context: {
|
||||
blogMetadata: aliasedSource(blogMetadataPath),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Docs plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": false,
|
||||
|
|
@ -35,14 +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-common": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/mdx-loader": "3.2.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@types/react-router-config": "^5.0.7",
|
||||
"combine-promises": "^1.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
|||
|
|
@ -1482,6 +1482,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/hello.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/hello.md",
|
||||
},
|
||||
|
|
@ -1491,6 +1495,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/absoluteSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/absoluteSlug.md",
|
||||
},
|
||||
|
|
@ -1508,6 +1516,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/customLastUpdate.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/customLastUpdate.md",
|
||||
},
|
||||
|
|
@ -1516,6 +1528,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/doc with space.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/doc with space.md",
|
||||
},
|
||||
|
|
@ -1524,6 +1540,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/doc-draft.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/doc-draft.md",
|
||||
},
|
||||
|
|
@ -1532,6 +1552,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/doc-unlisted.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/doc-unlisted.md",
|
||||
},
|
||||
|
|
@ -1541,6 +1565,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/foo/bar.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/foo/bar.md",
|
||||
},
|
||||
|
|
@ -1550,6 +1578,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/foo/baz.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/foo/baz.md",
|
||||
},
|
||||
|
|
@ -1559,6 +1591,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/headingAsTitle.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/headingAsTitle.md",
|
||||
},
|
||||
|
|
@ -1568,6 +1604,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/rootResolvedSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/rootResolvedSlug.md",
|
||||
},
|
||||
|
|
@ -1577,6 +1617,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/ipsum.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/ipsum.md",
|
||||
},
|
||||
|
|
@ -1585,6 +1629,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/lastUpdateAuthorOnly.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/lastUpdateAuthorOnly.md",
|
||||
},
|
||||
|
|
@ -1593,6 +1641,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/lastUpdateDateOnly.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/lastUpdateDateOnly.md",
|
||||
},
|
||||
|
|
@ -1601,6 +1653,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/lorem.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/lorem.md",
|
||||
},
|
||||
|
|
@ -1609,6 +1665,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/rootAbsoluteSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/rootAbsoluteSlug.md",
|
||||
},
|
||||
|
|
@ -1618,6 +1678,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/rootRelativeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/rootRelativeSlug.md",
|
||||
},
|
||||
|
|
@ -1627,6 +1691,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/rootTryToEscapeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/rootTryToEscapeSlug.md",
|
||||
},
|
||||
|
|
@ -1636,6 +1704,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/resolvedSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/resolvedSlug.md",
|
||||
},
|
||||
|
|
@ -1644,6 +1716,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/relativeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/relativeSlug.md",
|
||||
},
|
||||
|
|
@ -1652,6 +1728,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/tryToEscapeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/tryToEscapeSlug.md",
|
||||
},
|
||||
|
|
@ -1660,6 +1740,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/unlisted-category/index.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/unlisted-category/index.md",
|
||||
},
|
||||
|
|
@ -1669,6 +1753,10 @@ exports[`simple website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/unlisted-category/unlisted-category-doc.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/unlisted-category/unlisted-category-doc.md",
|
||||
},
|
||||
|
|
@ -2940,6 +3028,10 @@ exports[`versioned website (community) content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "i18n/en/docusaurus-plugin-content-docs-community/current/team.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md",
|
||||
},
|
||||
|
|
@ -2967,6 +3059,10 @@ exports[`versioned website (community) content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "community_versioned_docs/version-1.0.0/team.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/community_versioned_docs/version-1.0.0/team.md",
|
||||
},
|
||||
|
|
@ -4174,6 +4270,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md",
|
||||
},
|
||||
|
|
@ -4183,6 +4283,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-1.0.0/foo/bar.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-1.0.0/foo/bar.md",
|
||||
},
|
||||
|
|
@ -4192,6 +4296,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-1.0.0/foo/baz.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-1.0.0/foo/baz.md",
|
||||
},
|
||||
|
|
@ -4251,6 +4359,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/hello.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/hello.md",
|
||||
},
|
||||
|
|
@ -4260,6 +4372,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/absoluteSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/absoluteSlug.md",
|
||||
},
|
||||
|
|
@ -4268,6 +4384,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/foo/bar.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/foo/bar.md",
|
||||
},
|
||||
|
|
@ -4277,6 +4397,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/resolvedSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/resolvedSlug.md",
|
||||
},
|
||||
|
|
@ -4285,6 +4409,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/relativeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/relativeSlug.md",
|
||||
},
|
||||
|
|
@ -4293,6 +4421,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "docs/slugs/tryToEscapeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/docs/slugs/tryToEscapeSlug.md",
|
||||
},
|
||||
|
|
@ -4319,6 +4451,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/absoluteSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md",
|
||||
},
|
||||
|
|
@ -4327,6 +4463,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/rootResolvedSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/rootResolvedSlug.md",
|
||||
},
|
||||
|
|
@ -4335,6 +4475,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/rootAbsoluteSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md",
|
||||
},
|
||||
|
|
@ -4344,6 +4488,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/rootRelativeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/rootRelativeSlug.md",
|
||||
},
|
||||
|
|
@ -4352,6 +4500,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/rootTryToEscapeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md",
|
||||
},
|
||||
|
|
@ -4360,6 +4512,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/resolvedSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md",
|
||||
},
|
||||
|
|
@ -4368,6 +4524,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/relativeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md",
|
||||
},
|
||||
|
|
@ -4376,6 +4536,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md",
|
||||
},
|
||||
|
|
@ -4402,6 +4566,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-1.0.1/hello.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-1.0.1/hello.md",
|
||||
},
|
||||
|
|
@ -4411,6 +4579,10 @@ exports[`versioned website content: route config 1`] = `
|
|||
{
|
||||
"component": "@theme/DocItem",
|
||||
"exact": true,
|
||||
"metadata": {
|
||||
"lastUpdatedAt": undefined,
|
||||
"sourceFilePath": "versioned_docs/version-1.0.1/foo/bar.md",
|
||||
},
|
||||
"modules": {
|
||||
"content": "@site/versioned_docs/version-1.0.1/foo/bar.md",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@
|
|||
import {jest} from '@jest/globals';
|
||||
import path from 'path';
|
||||
import {loadContext} from '@docusaurus/core/src/server/site';
|
||||
import {createSlugger, posixPath, DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
|
||||
import {
|
||||
createSlugger,
|
||||
posixPath,
|
||||
DEFAULT_PLUGIN_ID,
|
||||
LAST_UPDATE_FALLBACK,
|
||||
} from '@docusaurus/utils';
|
||||
import {createSidebarsUtils} from '../sidebars/utils';
|
||||
import {
|
||||
processDocMetadata,
|
||||
|
|
@ -474,8 +479,8 @@ describe('simple site', () => {
|
|||
custom_edit_url: 'https://github.com/customUrl/docs/lorem.md',
|
||||
unrelated_front_matter: "won't be part of metadata",
|
||||
},
|
||||
lastUpdatedAt: 1539502055,
|
||||
lastUpdatedBy: 'Author',
|
||||
lastUpdatedAt: LAST_UPDATE_FALLBACK.lastUpdatedAt,
|
||||
lastUpdatedBy: LAST_UPDATE_FALLBACK.lastUpdatedBy,
|
||||
tags: [],
|
||||
unlisted: false,
|
||||
});
|
||||
|
|
@ -571,7 +576,7 @@ describe('simple site', () => {
|
|||
},
|
||||
title: 'Custom Last Update',
|
||||
},
|
||||
lastUpdatedAt: new Date('1/1/2000').getTime() / 1000,
|
||||
lastUpdatedAt: new Date('1/1/2000').getTime(),
|
||||
lastUpdatedBy: 'Custom Author (processed by parseFrontMatter)',
|
||||
sidebarPosition: undefined,
|
||||
tags: [],
|
||||
|
|
@ -609,7 +614,7 @@ describe('simple site', () => {
|
|||
},
|
||||
title: 'Last Update Author Only',
|
||||
},
|
||||
lastUpdatedAt: 1539502055,
|
||||
lastUpdatedAt: LAST_UPDATE_FALLBACK.lastUpdatedAt,
|
||||
lastUpdatedBy: 'Custom Author (processed by parseFrontMatter)',
|
||||
sidebarPosition: undefined,
|
||||
tags: [],
|
||||
|
|
@ -647,7 +652,7 @@ describe('simple site', () => {
|
|||
},
|
||||
title: 'Last Update Date Only',
|
||||
},
|
||||
lastUpdatedAt: new Date('1/1/2000').getTime() / 1000,
|
||||
lastUpdatedAt: new Date('1/1/2000').getTime(),
|
||||
lastUpdatedBy: 'Author',
|
||||
sidebarPosition: undefined,
|
||||
tags: [],
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ declare module '@docusaurus/plugin-content-docs' {
|
|||
TagModule,
|
||||
Tag,
|
||||
FrontMatterLastUpdate,
|
||||
LastUpdateData,
|
||||
} from '@docusaurus/utils';
|
||||
import type {Plugin, LoadContext} from '@docusaurus/types';
|
||||
import type {Overwrite, Required} from 'utility-types';
|
||||
|
|
@ -397,13 +398,6 @@ declare module '@docusaurus/plugin-content-docs' {
|
|||
last_update?: FrontMatterLastUpdate;
|
||||
};
|
||||
|
||||
export type LastUpdateData = {
|
||||
/** A timestamp in **seconds**, directly acquired from `git log`. */
|
||||
lastUpdatedAt?: number;
|
||||
/** The author's name directly acquired from `git log`. */
|
||||
lastUpdatedBy?: string;
|
||||
};
|
||||
|
||||
export type DocMetadataBase = LastUpdateData & {
|
||||
/**
|
||||
* The document id.
|
||||
|
|
|
|||
|
|
@ -7,21 +7,38 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {docuHash, createSlugger, normalizeUrl} from '@docusaurus/utils';
|
||||
import {
|
||||
docuHash,
|
||||
createSlugger,
|
||||
normalizeUrl,
|
||||
aliasedSitePathToRelativePath,
|
||||
} from '@docusaurus/utils';
|
||||
import {
|
||||
toTagDocListProp,
|
||||
toTagsListTagsProp,
|
||||
toVersionMetadataProp,
|
||||
} from './props';
|
||||
import {getVersionTags} from './tags';
|
||||
import type {PluginContentLoadedActions, RouteConfig} from '@docusaurus/types';
|
||||
import type {
|
||||
PluginContentLoadedActions,
|
||||
RouteConfig,
|
||||
RouteMetadata,
|
||||
} from '@docusaurus/types';
|
||||
import type {FullVersion, VersionTag} from './types';
|
||||
import type {
|
||||
CategoryGeneratedIndexMetadata,
|
||||
DocMetadata,
|
||||
PluginOptions,
|
||||
PropTagsListPage,
|
||||
} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
function createDocRouteMetadata(docMeta: DocMetadata): RouteMetadata {
|
||||
return {
|
||||
sourceFilePath: aliasedSitePathToRelativePath(docMeta.source),
|
||||
lastUpdatedAt: docMeta.lastUpdatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildVersionCategoryGeneratedIndexRoutes({
|
||||
version,
|
||||
actions,
|
||||
|
|
@ -68,26 +85,27 @@ async function buildVersionDocRoutes({
|
|||
options,
|
||||
}: BuildVersionRoutesParam): Promise<RouteConfig[]> {
|
||||
return Promise.all(
|
||||
version.docs.map(async (metadataItem) => {
|
||||
version.docs.map(async (doc) => {
|
||||
await actions.createData(
|
||||
// Note that this created data path must be in sync with
|
||||
// metadataPath provided to mdx-loader.
|
||||
`${docuHash(metadataItem.source)}.json`,
|
||||
JSON.stringify(metadataItem, null, 2),
|
||||
`${docuHash(doc.source)}.json`,
|
||||
JSON.stringify(doc, null, 2),
|
||||
);
|
||||
|
||||
const docRoute: RouteConfig = {
|
||||
path: metadataItem.permalink,
|
||||
path: doc.permalink,
|
||||
component: options.docItemComponent,
|
||||
exact: true,
|
||||
modules: {
|
||||
content: metadataItem.source,
|
||||
content: doc.source,
|
||||
},
|
||||
metadata: createDocRouteMetadata(doc),
|
||||
// Because the parent (DocRoot) comp need to access it easily
|
||||
// This permits to render the sidebar once without unmount/remount when
|
||||
// navigating (and preserve sidebar state)
|
||||
...(metadataItem.sidebar && {
|
||||
sidebar: metadataItem.sidebar,
|
||||
...(doc.sidebar && {
|
||||
sidebar: doc.sidebar,
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,8 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
addLeadingSlash,
|
||||
isValidPathname,
|
||||
resolvePathname,
|
||||
} from '@docusaurus/utils';
|
||||
import {addTrailingSlash} from '@docusaurus/utils-common';
|
||||
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.0",
|
||||
"description": "Pages plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-content-pages.d.ts",
|
||||
|
|
@ -18,11 +18,11 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/mdx-loader": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"tslib": "^2.6.0",
|
||||
"webpack": "^5.88.1"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
encodePath,
|
||||
fileToPath,
|
||||
aliasedSitePath,
|
||||
aliasedSitePathToRelativePath,
|
||||
docuHash,
|
||||
getPluginI18nPath,
|
||||
getFolderContainingFile,
|
||||
|
|
@ -24,8 +25,7 @@ import {
|
|||
isDraft,
|
||||
} from '@docusaurus/utils';
|
||||
import {validatePageFrontMatter} from './frontMatter';
|
||||
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {LoadContext, Plugin, RouteMetadata} from '@docusaurus/types';
|
||||
import type {PagesContentPaths} from './types';
|
||||
import type {
|
||||
PluginOptions,
|
||||
|
|
@ -159,9 +159,20 @@ export default function pluginContentPages(
|
|||
|
||||
const {addRoute, createData} = actions;
|
||||
|
||||
function createPageRouteMetadata(metadata: Metadata): RouteMetadata {
|
||||
return {
|
||||
sourceFilePath: aliasedSitePathToRelativePath(metadata.source),
|
||||
// TODO add support for last updated date in the page plugin
|
||||
// at least for Markdown files
|
||||
// lastUpdatedAt: metadata.lastUpdatedAt,
|
||||
lastUpdatedAt: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
content.map(async (metadata) => {
|
||||
const {permalink, source} = metadata;
|
||||
const routeMetadata = createPageRouteMetadata(metadata);
|
||||
if (metadata.type === 'mdx') {
|
||||
await createData(
|
||||
// Note that this created data path must be in sync with
|
||||
|
|
@ -173,6 +184,7 @@ export default function pluginContentPages(
|
|||
path: permalink,
|
||||
component: options.mdxPageComponent,
|
||||
exact: true,
|
||||
metadata: routeMetadata,
|
||||
modules: {
|
||||
content: source,
|
||||
},
|
||||
|
|
@ -182,6 +194,7 @@ export default function pluginContentPages(
|
|||
path: permalink,
|
||||
component: source,
|
||||
exact: true,
|
||||
metadata: routeMetadata,
|
||||
modules: {
|
||||
config: `@generated/docusaurus.config`,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-debug",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Debug plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-debug.d.ts",
|
||||
|
|
@ -20,9 +20,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"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.0",
|
||||
"description": "Global analytics (analytics.js) plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-gtag",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Global Site Tag (gtag.js) plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@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.0",
|
||||
"description": "Google Tag Manager (gtm.js) plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-ideal-image",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-ideal-image.d.ts",
|
||||
|
|
@ -20,12 +20,12 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/lqip-loader": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/lqip-loader": "3.2.0",
|
||||
"@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.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@slorber/react-ideal-image": "^0.0.12",
|
||||
"react-waypoint": "^10.3.0",
|
||||
"sharp": "^0.32.3",
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
"webpack": "^5.88.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"fs-extra": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-pwa",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Docusaurus Plugin to add PWA support.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-pwa.d.ts",
|
||||
|
|
@ -22,12 +22,12 @@
|
|||
"dependencies": {
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/preset-env": "^7.23.3",
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/theme-common": "3.2.0",
|
||||
"@docusaurus/theme-translations": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"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.0",
|
||||
"fs-extra": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-sitemap",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Simple sitemap generation plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,16 +18,19 @@
|
|||
},
|
||||
"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.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"sitemap": "^7.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@total-typescript/shoehorn": "^0.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
|
|
|
|||
|
|
@ -6,95 +6,91 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {EnumChangefreq} from 'sitemap';
|
||||
import {fromPartial} from '@total-typescript/shoehorn';
|
||||
import createSitemap from '../createSitemap';
|
||||
import type {PluginOptions} from '../options';
|
||||
import type {DocusaurusConfig} from '@docusaurus/types';
|
||||
import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
|
||||
|
||||
const siteConfig: DocusaurusConfig = fromPartial({
|
||||
url: 'https://example.com',
|
||||
});
|
||||
|
||||
const options: PluginOptions = {
|
||||
changefreq: 'daily',
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
lastmod: 'datetime',
|
||||
};
|
||||
|
||||
const route = (routePath: string, routePaths?: string[]): RouteConfig => {
|
||||
return fromPartial({
|
||||
path: routePath,
|
||||
routes: routePaths?.map((p) => route(p)),
|
||||
});
|
||||
};
|
||||
|
||||
const routes = (routePaths: string[]): RouteConfig[] => {
|
||||
return routePaths.map((p) => route(p));
|
||||
};
|
||||
|
||||
describe('createSitemap', () => {
|
||||
it('simple site', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
} as DocusaurusConfig,
|
||||
['/', '/test'],
|
||||
{},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
},
|
||||
);
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routes: routes(['/', '/test']),
|
||||
head: {},
|
||||
options,
|
||||
});
|
||||
expect(sitemap).toContain(
|
||||
`<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">`,
|
||||
);
|
||||
});
|
||||
|
||||
it('empty site', () =>
|
||||
expect(async () => {
|
||||
// @ts-expect-error: test
|
||||
await createSitemap({}, [], {}, {} as PluginOptions);
|
||||
}).rejects.toThrow(
|
||||
'URL in docusaurus.config.js cannot be empty/undefined.',
|
||||
));
|
||||
|
||||
it('exclusion of 404 page', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
} as DocusaurusConfig,
|
||||
['/', '/404.html', '/my-page'],
|
||||
{},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
},
|
||||
);
|
||||
expect(sitemap).not.toContain('404');
|
||||
it('site with no routes', async () => {
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routes: routes([]),
|
||||
head: {},
|
||||
options,
|
||||
});
|
||||
expect(sitemap).toBeNull();
|
||||
});
|
||||
|
||||
it('excludes patterns configured to be ignored', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
} as DocusaurusConfig,
|
||||
['/', '/search/', '/tags/', '/search/foo', '/tags/foo/bar'],
|
||||
{},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routes: routes([
|
||||
'/',
|
||||
'/search/',
|
||||
'/tags/',
|
||||
'/search/foo',
|
||||
'/tags/foo/bar',
|
||||
]),
|
||||
head: {},
|
||||
options: {
|
||||
...options,
|
||||
ignorePatterns: [
|
||||
// Shallow ignore
|
||||
'/search/',
|
||||
// Deep ignore
|
||||
'/tags/**',
|
||||
],
|
||||
filename: 'sitemap.xml',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
expect(sitemap).not.toContain('/search/</loc>');
|
||||
expect(sitemap).toContain('/search/foo');
|
||||
expect(sitemap).not.toContain('/tags');
|
||||
});
|
||||
|
||||
it('keep trailing slash unchanged', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
trailingSlash: undefined,
|
||||
} as DocusaurusConfig,
|
||||
['/', '/test', '/nested/test', '/nested/test2/'],
|
||||
{},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
},
|
||||
);
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routes: routes(['/', '/test', '/nested/test', '/nested/test2/']),
|
||||
head: {},
|
||||
options,
|
||||
});
|
||||
|
||||
expect(sitemap).toContain('<loc>https://example.com/</loc>');
|
||||
expect(sitemap).toContain('<loc>https://example.com/test</loc>');
|
||||
|
|
@ -103,20 +99,12 @@ describe('createSitemap', () => {
|
|||
});
|
||||
|
||||
it('add trailing slash', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
trailingSlash: true,
|
||||
} as DocusaurusConfig,
|
||||
['/', '/test', '/nested/test', '/nested/test2/'],
|
||||
{},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
},
|
||||
);
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig: {...siteConfig, trailingSlash: true},
|
||||
routes: routes(['/', '/test', '/nested/test', '/nested/test2/']),
|
||||
head: {},
|
||||
options,
|
||||
});
|
||||
|
||||
expect(sitemap).toContain('<loc>https://example.com/</loc>');
|
||||
expect(sitemap).toContain('<loc>https://example.com/test/</loc>');
|
||||
|
|
@ -125,20 +113,16 @@ describe('createSitemap', () => {
|
|||
});
|
||||
|
||||
it('remove trailing slash', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig: {
|
||||
...siteConfig,
|
||||
url: 'https://example.com',
|
||||
trailingSlash: false,
|
||||
} as DocusaurusConfig,
|
||||
['/', '/test', '/nested/test', '/nested/test2/'],
|
||||
{},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
},
|
||||
);
|
||||
routes: routes(['/', '/test', '/nested/test', '/nested/test2/']),
|
||||
head: {},
|
||||
options,
|
||||
});
|
||||
|
||||
expect(sitemap).toContain('<loc>https://example.com/</loc>');
|
||||
expect(sitemap).toContain('<loc>https://example.com/test</loc>');
|
||||
|
|
@ -147,13 +131,11 @@ describe('createSitemap', () => {
|
|||
});
|
||||
|
||||
it('filters pages with noindex', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
trailingSlash: false,
|
||||
} as DocusaurusConfig,
|
||||
['/', '/noindex', '/nested/test', '/nested/test2/'],
|
||||
{
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routesPaths: ['/', '/noindex', '/nested/test', '/nested/test2/'],
|
||||
routes: routes(['/', '/noindex', '/nested/test', '/nested/test2/']),
|
||||
head: {
|
||||
'/noindex': {
|
||||
meta: {
|
||||
// @ts-expect-error: bad lib def
|
||||
|
|
@ -166,24 +148,18 @@ describe('createSitemap', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
},
|
||||
);
|
||||
options,
|
||||
});
|
||||
|
||||
expect(sitemap).not.toContain('/noindex');
|
||||
});
|
||||
|
||||
it('does not generate anything for all pages with noindex', async () => {
|
||||
const sitemap = await createSitemap(
|
||||
{
|
||||
url: 'https://example.com',
|
||||
trailingSlash: false,
|
||||
} as DocusaurusConfig,
|
||||
['/', '/noindex'],
|
||||
{
|
||||
const sitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routesPaths: ['/', '/noindex'],
|
||||
routes: routes(['/', '/noindex']),
|
||||
head: {
|
||||
'/': {
|
||||
meta: {
|
||||
// @ts-expect-error: bad lib def
|
||||
|
|
@ -201,12 +177,8 @@ describe('createSitemap', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
changefreq: EnumChangefreq.DAILY,
|
||||
priority: 0.7,
|
||||
ignorePatterns: [],
|
||||
},
|
||||
);
|
||||
options,
|
||||
});
|
||||
|
||||
expect(sitemap).toBeNull();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,229 @@
|
|||
/**
|
||||
* 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 {fromPartial} from '@total-typescript/shoehorn';
|
||||
import {createSitemapItem} from '../createSitemapItem';
|
||||
import {DEFAULT_OPTIONS} from '../options';
|
||||
import type {PluginOptions} from '../options';
|
||||
import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
|
||||
|
||||
const siteConfig: DocusaurusConfig = fromPartial({
|
||||
url: 'https://example.com',
|
||||
});
|
||||
|
||||
function test(params: {
|
||||
route: Partial<RouteConfig>;
|
||||
siteConfig?: Partial<DocusaurusConfig>;
|
||||
options?: Partial<PluginOptions>;
|
||||
}) {
|
||||
return createSitemapItem({
|
||||
route: params.route as unknown as RouteConfig,
|
||||
siteConfig: {...siteConfig, ...params.siteConfig},
|
||||
options: {...DEFAULT_OPTIONS, ...params.options},
|
||||
});
|
||||
}
|
||||
|
||||
function testRoute(route: Partial<RouteConfig>) {
|
||||
return test({
|
||||
route,
|
||||
});
|
||||
}
|
||||
|
||||
describe('createSitemapItem', () => {
|
||||
it('simple item', async () => {
|
||||
await expect(testRoute({path: '/routePath'})).resolves
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": null,
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('lastmod', () => {
|
||||
const date = new Date('2024/01/01');
|
||||
|
||||
describe('read from route metadata', () => {
|
||||
const route = {
|
||||
path: '/routePath',
|
||||
metadata: {lastUpdatedAt: date.getTime()},
|
||||
};
|
||||
|
||||
it('lastmod default option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": null,
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('lastmod date option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
options: {
|
||||
lastmod: 'date',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": "2024-01-01",
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('lastmod datetime option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
options: {
|
||||
lastmod: 'datetime',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": "2024-01-01T00:00:00.000Z",
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('read from git', () => {
|
||||
const route = {
|
||||
path: '/routePath',
|
||||
metadata: {sourceFilePath: 'route/file.md'},
|
||||
};
|
||||
|
||||
it('lastmod default option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": null,
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('lastmod date option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
options: {
|
||||
lastmod: 'date',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": "2018-10-14",
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('lastmod datetime option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
options: {
|
||||
lastmod: 'datetime',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": "2018-10-14T07:27:35.000Z",
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('read from both - route metadata takes precedence', () => {
|
||||
const route = {
|
||||
path: '/routePath',
|
||||
metadata: {
|
||||
sourceFilePath: 'route/file.md',
|
||||
lastUpdatedAt: date.getTime(),
|
||||
},
|
||||
};
|
||||
|
||||
it('lastmod default option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": null,
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('lastmod date option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
options: {
|
||||
lastmod: 'date',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": "2024-01-01",
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('lastmod datetime option', async () => {
|
||||
await expect(
|
||||
test({
|
||||
route,
|
||||
options: {
|
||||
lastmod: 'datetime',
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"changefreq": "weekly",
|
||||
"lastmod": "2024-01-01T00:00:00.000Z",
|
||||
"priority": 0.5,
|
||||
"url": "https://example.com/routePath",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -12,7 +12,6 @@ import {
|
|||
type Options,
|
||||
type PluginOptions,
|
||||
} from '../options';
|
||||
import type {EnumChangefreq} from 'sitemap';
|
||||
import type {Validate} from '@docusaurus/types';
|
||||
|
||||
function testValidate(options: Options) {
|
||||
|
|
@ -34,9 +33,10 @@ describe('validateOptions', () => {
|
|||
|
||||
it('accepts correctly defined user options', () => {
|
||||
const userOptions: Options = {
|
||||
changefreq: 'yearly' as EnumChangefreq,
|
||||
changefreq: 'yearly',
|
||||
priority: 0.9,
|
||||
ignorePatterns: ['/search/**'],
|
||||
lastmod: 'datetime',
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
|
|
@ -44,32 +44,209 @@ describe('validateOptions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('rejects out-of-range priority inputs', () => {
|
||||
expect(() =>
|
||||
testValidate({priority: 2}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""priority" must be less than or equal to 1"`,
|
||||
);
|
||||
describe('lastmod', () => {
|
||||
it('accepts lastmod undefined', () => {
|
||||
const userOptions: Options = {
|
||||
lastmod: undefined,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accepts lastmod null', () => {
|
||||
const userOptions: Options = {
|
||||
lastmod: null,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts lastmod datetime', () => {
|
||||
const userOptions: Options = {
|
||||
lastmod: 'datetime',
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects lastmod bad input', () => {
|
||||
const userOptions: Options = {
|
||||
// @ts-expect-error: bad value on purpose
|
||||
lastmod: 'dateTimeZone',
|
||||
};
|
||||
expect(() =>
|
||||
testValidate(userOptions),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""lastmod" must be one of [null, date, datetime]"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects bad changefreq inputs', () => {
|
||||
expect(() =>
|
||||
testValidate({changefreq: 'annually' as EnumChangefreq}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""changefreq" must be one of [daily, monthly, always, hourly, weekly, yearly, never]"`,
|
||||
);
|
||||
describe('priority', () => {
|
||||
it('accepts priority undefined', () => {
|
||||
const userOptions: Options = {
|
||||
priority: undefined,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accepts priority null', () => {
|
||||
const userOptions: Options = {
|
||||
priority: null,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts priority 0', () => {
|
||||
const userOptions: Options = {
|
||||
priority: 0,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts priority 0.4', () => {
|
||||
const userOptions: Options = {
|
||||
priority: 0.4,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts priority 1', () => {
|
||||
const userOptions: Options = {
|
||||
priority: 1,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects priority > 1', () => {
|
||||
const userOptions: Options = {
|
||||
priority: 2,
|
||||
};
|
||||
expect(() =>
|
||||
testValidate(userOptions),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""priority" must be less than or equal to 1"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects priority < 0', () => {
|
||||
const userOptions: Options = {
|
||||
priority: -3,
|
||||
};
|
||||
expect(() =>
|
||||
testValidate(userOptions),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""priority" must be greater than or equal to 0"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects bad ignorePatterns inputs', () => {
|
||||
expect(() =>
|
||||
// @ts-expect-error: test
|
||||
testValidate({ignorePatterns: '/search'}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`""ignorePatterns" must be an array"`);
|
||||
expect(() =>
|
||||
// @ts-expect-error: test
|
||||
testValidate({ignorePatterns: [/^\/search/]}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""ignorePatterns[0]" must be a string"`,
|
||||
);
|
||||
describe('changefreq', () => {
|
||||
it('accepts changefreq undefined', () => {
|
||||
const userOptions: Options = {
|
||||
changefreq: undefined,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accepts changefreq null', () => {
|
||||
const userOptions: Options = {
|
||||
changefreq: null,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts changefreq always', () => {
|
||||
const userOptions: Options = {
|
||||
changefreq: 'always',
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects changefreq bad inputs', () => {
|
||||
const userOptions: Options = {
|
||||
// @ts-expect-error: bad value on purpose
|
||||
changefreq: 'annually',
|
||||
};
|
||||
expect(() =>
|
||||
testValidate(userOptions),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""changefreq" must be one of [null, hourly, daily, weekly, monthly, yearly, always, never]"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ignorePatterns', () => {
|
||||
it('accept ignorePatterns undefined', () => {
|
||||
const userOptions: Options = {
|
||||
ignorePatterns: undefined,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accept ignorePatterns empty', () => {
|
||||
const userOptions: Options = {
|
||||
ignorePatterns: [],
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accept ignorePatterns valid', () => {
|
||||
const userOptions: Options = {
|
||||
ignorePatterns: ['/tags/**'],
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects ignorePatterns bad input array', () => {
|
||||
const userOptions: Options = {
|
||||
// @ts-expect-error: test
|
||||
ignorePatterns: '/search',
|
||||
};
|
||||
expect(() =>
|
||||
testValidate(userOptions),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""ignorePatterns" must be an array"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects ignorePatterns bad input item string', () => {
|
||||
const userOptions: Options = {
|
||||
// @ts-expect-error: test
|
||||
ignorePatterns: [/^\/search/],
|
||||
};
|
||||
expect(() =>
|
||||
testValidate(userOptions),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""ignorePatterns[0]" must be a string"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* 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 {sitemapItemsToXmlString} from '../xml';
|
||||
import type {SitemapItem} from '../types';
|
||||
|
||||
const options = {lastmod: 'datetime'} as const;
|
||||
|
||||
describe('createSitemap', () => {
|
||||
it('no items', async () => {
|
||||
const items: SitemapItem[] = [];
|
||||
|
||||
await expect(
|
||||
sitemapItemsToXmlString(items, options),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Can't generate a sitemap with no items"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('simple item', async () => {
|
||||
const items: SitemapItem[] = [{url: 'https://docusaurus.io/docs/doc1'}];
|
||||
|
||||
await expect(
|
||||
sitemapItemsToXmlString(items, options),
|
||||
).resolves.toMatchInlineSnapshot(
|
||||
`"<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://docusaurus.io/docs/doc1</loc></url></urlset>"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('complex item', async () => {
|
||||
const items: SitemapItem[] = [
|
||||
{
|
||||
url: 'https://docusaurus.io/docs/doc1',
|
||||
changefreq: 'always',
|
||||
priority: 1,
|
||||
lastmod: new Date('01/01/2024').toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
await expect(
|
||||
sitemapItemsToXmlString(items, options),
|
||||
).resolves.toMatchInlineSnapshot(
|
||||
`"<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://docusaurus.io/docs/doc1</loc><lastmod>2024-01-01T00:00:00.000Z</lastmod><changefreq>always</changefreq><priority>1.0</priority></url></urlset>"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('date only lastmod', async () => {
|
||||
const items: SitemapItem[] = [
|
||||
{
|
||||
url: 'https://docusaurus.io/docs/doc1',
|
||||
changefreq: 'always',
|
||||
priority: 1,
|
||||
lastmod: new Date('01/01/2024').toISOString(),
|
||||
},
|
||||
];
|
||||
|
||||
await expect(
|
||||
sitemapItemsToXmlString(items, {lastmod: 'date'}),
|
||||
).resolves.toMatchInlineSnapshot(
|
||||
`"<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://docusaurus.io/docs/doc1</loc><lastmod>2024-01-01</lastmod><changefreq>always</changefreq><priority>1.0</priority></url></urlset>"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -6,13 +6,23 @@
|
|||
*/
|
||||
|
||||
import type {ReactElement} from 'react';
|
||||
import {SitemapStream, streamToPromise} from 'sitemap';
|
||||
import {applyTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {createMatcher} from '@docusaurus/utils';
|
||||
import type {DocusaurusConfig} from '@docusaurus/types';
|
||||
import {createMatcher, flattenRoutes} from '@docusaurus/utils';
|
||||
import {sitemapItemsToXmlString} from './xml';
|
||||
import {createSitemapItem} from './createSitemapItem';
|
||||
import type {SitemapItem} from './types';
|
||||
import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
|
||||
import type {HelmetServerState} from 'react-helmet-async';
|
||||
import type {PluginOptions} from './options';
|
||||
|
||||
type CreateSitemapParams = {
|
||||
siteConfig: DocusaurusConfig;
|
||||
routes: RouteConfig[];
|
||||
head: {[location: string]: HelmetServerState};
|
||||
options: PluginOptions;
|
||||
};
|
||||
|
||||
// Maybe we want to add a routeConfig.metadata.noIndex instead?
|
||||
// But using Helmet is more reliable for third-party plugins...
|
||||
function isNoIndexMetaRoute({
|
||||
head,
|
||||
route,
|
||||
|
|
@ -47,50 +57,51 @@ function isNoIndexMetaRoute({
|
|||
);
|
||||
}
|
||||
|
||||
export default async function createSitemap(
|
||||
siteConfig: DocusaurusConfig,
|
||||
routesPaths: string[],
|
||||
head: {[location: string]: HelmetServerState},
|
||||
options: PluginOptions,
|
||||
): Promise<string | null> {
|
||||
const {url: hostname} = siteConfig;
|
||||
if (!hostname) {
|
||||
throw new Error('URL in docusaurus.config.js cannot be empty/undefined.');
|
||||
}
|
||||
const {changefreq, priority, ignorePatterns} = options;
|
||||
// Not all routes should appear in the sitemap, and we should filter:
|
||||
// - parent routes, used for layouts
|
||||
// - routes matching options.ignorePatterns
|
||||
// - routes with no index metadata
|
||||
function getSitemapRoutes({routes, head, options}: CreateSitemapParams) {
|
||||
const {ignorePatterns} = options;
|
||||
|
||||
const ignoreMatcher = createMatcher(ignorePatterns);
|
||||
|
||||
function isRouteExcluded(route: string) {
|
||||
function isRouteExcluded(route: RouteConfig) {
|
||||
return (
|
||||
route.endsWith('404.html') ||
|
||||
ignoreMatcher(route) ||
|
||||
isNoIndexMetaRoute({head, route})
|
||||
ignoreMatcher(route.path) || isNoIndexMetaRoute({head, route: route.path})
|
||||
);
|
||||
}
|
||||
|
||||
const includedRoutes = routesPaths.filter((route) => !isRouteExcluded(route));
|
||||
return flattenRoutes(routes).filter((route) => !isRouteExcluded(route));
|
||||
}
|
||||
|
||||
if (includedRoutes.length === 0) {
|
||||
async function createSitemapItems(
|
||||
params: CreateSitemapParams,
|
||||
): Promise<SitemapItem[]> {
|
||||
const sitemapRoutes = getSitemapRoutes(params);
|
||||
if (sitemapRoutes.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return Promise.all(
|
||||
sitemapRoutes.map((route) =>
|
||||
createSitemapItem({
|
||||
route,
|
||||
siteConfig: params.siteConfig,
|
||||
options: params.options,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export default async function createSitemap(
|
||||
params: CreateSitemapParams,
|
||||
): Promise<string | null> {
|
||||
const items = await createSitemapItems(params);
|
||||
if (items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sitemapStream = new SitemapStream({hostname});
|
||||
|
||||
includedRoutes.forEach((routePath) =>
|
||||
sitemapStream.write({
|
||||
url: applyTrailingSlash(routePath, {
|
||||
trailingSlash: siteConfig.trailingSlash,
|
||||
baseUrl: siteConfig.baseUrl,
|
||||
}),
|
||||
changefreq,
|
||||
priority,
|
||||
}),
|
||||
);
|
||||
|
||||
sitemapStream.end();
|
||||
|
||||
const generatedSitemap = (await streamToPromise(sitemapStream)).toString();
|
||||
|
||||
return generatedSitemap;
|
||||
const xmlString = await sitemapItemsToXmlString(items, {
|
||||
lastmod: params.options.lastmod,
|
||||
});
|
||||
return xmlString;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* 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 {applyTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {getLastUpdate, normalizeUrl} from '@docusaurus/utils';
|
||||
import type {LastModOption, SitemapItem} from './types';
|
||||
import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
|
||||
import type {PluginOptions} from './options';
|
||||
|
||||
async function getRouteLastUpdatedAt(
|
||||
route: RouteConfig,
|
||||
): Promise<number | undefined> {
|
||||
if (route.metadata?.lastUpdatedAt) {
|
||||
return route.metadata?.lastUpdatedAt;
|
||||
}
|
||||
if (route.metadata?.sourceFilePath) {
|
||||
const lastUpdate = await getLastUpdate(route.metadata?.sourceFilePath);
|
||||
return lastUpdate?.lastUpdatedAt;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
type LastModFormatter = (timestamp: number) => string;
|
||||
|
||||
const LastmodFormatters: Record<LastModOption, LastModFormatter> = {
|
||||
date: (timestamp) => new Date(timestamp).toISOString().split('T')[0]!,
|
||||
datetime: (timestamp) => new Date(timestamp).toISOString(),
|
||||
};
|
||||
|
||||
function formatLastmod(timestamp: number, lastmodOption: LastModOption) {
|
||||
const format = LastmodFormatters[lastmodOption];
|
||||
return format(timestamp);
|
||||
}
|
||||
|
||||
async function getRouteLastmod({
|
||||
route,
|
||||
lastmod,
|
||||
}: {
|
||||
route: RouteConfig;
|
||||
lastmod: LastModOption | null;
|
||||
}): Promise<string | null> {
|
||||
if (lastmod === null) {
|
||||
return null;
|
||||
}
|
||||
const lastUpdatedAt = (await getRouteLastUpdatedAt(route)) ?? null;
|
||||
return lastUpdatedAt ? formatLastmod(lastUpdatedAt, lastmod) : null;
|
||||
}
|
||||
|
||||
export async function createSitemapItem({
|
||||
route,
|
||||
siteConfig,
|
||||
options,
|
||||
}: {
|
||||
route: RouteConfig;
|
||||
siteConfig: DocusaurusConfig;
|
||||
options: PluginOptions;
|
||||
}): Promise<SitemapItem> {
|
||||
const {changefreq, priority, lastmod} = options;
|
||||
return {
|
||||
url: normalizeUrl([
|
||||
siteConfig.url,
|
||||
applyTrailingSlash(route.path, {
|
||||
trailingSlash: siteConfig.trailingSlash,
|
||||
baseUrl: siteConfig.baseUrl,
|
||||
}),
|
||||
]),
|
||||
changefreq,
|
||||
priority,
|
||||
lastmod: await getRouteLastmod({route, lastmod}),
|
||||
};
|
||||
}
|
||||
|
|
@ -19,17 +19,17 @@ export default function pluginSitemap(
|
|||
return {
|
||||
name: 'docusaurus-plugin-sitemap',
|
||||
|
||||
async postBuild({siteConfig, routesPaths, outDir, head}) {
|
||||
async postBuild({siteConfig, routes, outDir, head}) {
|
||||
if (siteConfig.noIndex) {
|
||||
return;
|
||||
}
|
||||
// Generate sitemap.
|
||||
const generatedSitemap = await createSitemap(
|
||||
const generatedSitemap = await createSitemap({
|
||||
siteConfig,
|
||||
routesPaths,
|
||||
routes,
|
||||
head,
|
||||
options,
|
||||
);
|
||||
});
|
||||
if (!generatedSitemap) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,33 +6,60 @@
|
|||
*/
|
||||
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
import {EnumChangefreq} from 'sitemap';
|
||||
import {ChangeFreqList, LastModOptionList} from './types';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
import type {ChangeFreq, LastModOption} from './types';
|
||||
|
||||
export type PluginOptions = {
|
||||
/** @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions */
|
||||
changefreq: EnumChangefreq;
|
||||
/** @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions */
|
||||
priority: number;
|
||||
/**
|
||||
* A list of glob patterns; matching route paths will be filtered from the
|
||||
* sitemap. Note that you may need to include the base URL in here.
|
||||
*/
|
||||
ignorePatterns: string[];
|
||||
/**
|
||||
* The path to the created sitemap file, relative to the output directory.
|
||||
* Useful if you have two plugin instances outputting two files.
|
||||
*/
|
||||
filename: string;
|
||||
|
||||
/**
|
||||
* A list of glob patterns; matching route paths will be filtered from the
|
||||
* sitemap. Note that you may need to include the base URL in here.
|
||||
*/
|
||||
ignorePatterns: string[];
|
||||
|
||||
/**
|
||||
* Defines the format of the "lastmod" sitemap item entry, between:
|
||||
* - null: do not compute/add a "lastmod" sitemap entry
|
||||
* - "date": add a "lastmod" sitemap entry without time (YYYY-MM-DD)
|
||||
* - "datetime": add a "lastmod" sitemap entry with time (ISO 8601 datetime)
|
||||
* @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions
|
||||
* @see https://www.w3.org/TR/NOTE-datetime
|
||||
*/
|
||||
lastmod: LastModOption | null;
|
||||
|
||||
/**
|
||||
* TODO Docusaurus v4 breaking change: remove useless option
|
||||
* @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions
|
||||
*/
|
||||
changefreq: ChangeFreq | null;
|
||||
|
||||
/**
|
||||
* TODO Docusaurus v4 breaking change: remove useless option
|
||||
* @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions
|
||||
*/
|
||||
priority: number | null;
|
||||
};
|
||||
|
||||
export type Options = Partial<PluginOptions>;
|
||||
|
||||
export const DEFAULT_OPTIONS: PluginOptions = {
|
||||
changefreq: EnumChangefreq.WEEKLY,
|
||||
priority: 0.5,
|
||||
ignorePatterns: [],
|
||||
filename: 'sitemap.xml',
|
||||
ignorePatterns: [],
|
||||
|
||||
// TODO Docusaurus v4 breaking change
|
||||
// change default to "date" if no bug or perf issue reported
|
||||
lastmod: null,
|
||||
|
||||
// TODO Docusaurus v4 breaking change
|
||||
// those options are useless and should be removed
|
||||
changefreq: 'weekly',
|
||||
priority: 0.5,
|
||||
};
|
||||
|
||||
const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||
|
|
@ -41,10 +68,28 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
|||
'any.unknown':
|
||||
'Option `cacheTime` in sitemap config is deprecated. Please remove it.',
|
||||
}),
|
||||
|
||||
// TODO remove for Docusaurus v4 breaking changes?
|
||||
// This is not even used by Google crawlers
|
||||
// See also https://github.com/facebook/docusaurus/issues/2604
|
||||
changefreq: Joi.string()
|
||||
.valid(...Object.values(EnumChangefreq))
|
||||
.valid(null, ...ChangeFreqList)
|
||||
.default(DEFAULT_OPTIONS.changefreq),
|
||||
priority: Joi.number().min(0).max(1).default(DEFAULT_OPTIONS.priority),
|
||||
|
||||
// TODO remove for Docusaurus v4 breaking changes?
|
||||
// This is not even used by Google crawlers
|
||||
// The priority is "relative", and using the same priority for all routes
|
||||
// does not make sense according to the spec
|
||||
// See also https://github.com/facebook/docusaurus/issues/2604
|
||||
// See also https://www.sitemaps.org/protocol.html
|
||||
priority: Joi.alternatives()
|
||||
.try(Joi.valid(null), Joi.number().min(0).max(1))
|
||||
.default(DEFAULT_OPTIONS.priority),
|
||||
|
||||
lastmod: Joi.string()
|
||||
.valid(null, ...LastModOptionList)
|
||||
.default(DEFAULT_OPTIONS.lastmod),
|
||||
|
||||
ignorePatterns: Joi.array()
|
||||
.items(Joi.string())
|
||||
.default(DEFAULT_OPTIONS.ignorePatterns),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
export const LastModOptionList = ['date', 'datetime'] as const;
|
||||
|
||||
export type LastModOption = (typeof LastModOptionList)[number];
|
||||
|
||||
// types are according to the sitemap spec:
|
||||
// see also https://www.sitemaps.org/protocol.html
|
||||
|
||||
export const ChangeFreqList = [
|
||||
'hourly',
|
||||
'daily',
|
||||
'weekly',
|
||||
'monthly',
|
||||
'yearly',
|
||||
'always',
|
||||
'never',
|
||||
] as const;
|
||||
|
||||
export type ChangeFreq = (typeof ChangeFreqList)[number];
|
||||
|
||||
// We re-recreate our own type because the "sitemap" lib types are not good
|
||||
export type SitemapItem = {
|
||||
/**
|
||||
* URL of the page.
|
||||
* This URL must begin with the protocol (such as http).
|
||||
* It should eventually end with a trailing slash.
|
||||
* It should be less than 2,048 characters.
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* ISO 8601 date string.
|
||||
* See also https://www.w3.org/TR/NOTE-datetime
|
||||
*
|
||||
* It is recommended to use one of:
|
||||
* - date.toISOString()
|
||||
* - YYYY-MM-DD
|
||||
*
|
||||
* Note: as of 2024, Google uses this value for crawling priority.
|
||||
* See also https://github.com/facebook/docusaurus/issues/2604
|
||||
*/
|
||||
lastmod?: string | null;
|
||||
|
||||
/**
|
||||
* One of the specified enum values
|
||||
*
|
||||
* Note: as of 2024, Google ignores this value.
|
||||
* See also https://github.com/facebook/docusaurus/issues/2604
|
||||
*/
|
||||
changefreq?: ChangeFreq | null;
|
||||
|
||||
/**
|
||||
* The priority of this URL relative to other URLs on your site.
|
||||
* Valid values range from 0.0 to 1.0.
|
||||
* The default priority of a page is 0.5.
|
||||
*
|
||||
* Note: as of 2024, Google ignores this value.
|
||||
* See also https://github.com/facebook/docusaurus/issues/2604
|
||||
*/
|
||||
priority?: number | null;
|
||||
};
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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 {SitemapStream, streamToPromise} from 'sitemap';
|
||||
import type {LastModOption, SitemapItem} from './types';
|
||||
|
||||
export async function sitemapItemsToXmlString(
|
||||
items: SitemapItem[],
|
||||
options: {lastmod: LastModOption | null},
|
||||
): Promise<string> {
|
||||
if (items.length === 0) {
|
||||
// Note: technically we could, but there is a bug in the lib code
|
||||
// and the code below would never resolve, so it's better to fail fast
|
||||
throw new Error("Can't generate a sitemap with no items");
|
||||
}
|
||||
|
||||
// TODO remove sitemap lib dependency?
|
||||
// https://github.com/ekalinin/sitemap.js
|
||||
// it looks like an outdated confusion super old lib
|
||||
// we might as well achieve the same result with a pure xml lib
|
||||
const sitemapStream = new SitemapStream({
|
||||
// WTF is this lib reformatting the string YYYY-MM-DD to datetime...
|
||||
lastmodDateOnly: options?.lastmod === 'date',
|
||||
});
|
||||
|
||||
items.forEach((item) => sitemapStream.write(item));
|
||||
sitemapStream.end();
|
||||
|
||||
const buffer = await streamToPromise(sitemapStream);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-vercel-analytics",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Global vercel analytics plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,11 +18,11 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@vercel/analytics": "^1.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/preset-classic",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Classic preset for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -18,19 +18,19 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/plugin-content-blog": "3.2.0",
|
||||
"@docusaurus/plugin-content-docs": "3.2.0",
|
||||
"@docusaurus/plugin-content-pages": "3.2.0",
|
||||
"@docusaurus/plugin-debug": "3.2.0",
|
||||
"@docusaurus/plugin-google-analytics": "3.2.0",
|
||||
"@docusaurus/plugin-google-gtag": "3.2.0",
|
||||
"@docusaurus/plugin-google-tag-manager": "3.2.0",
|
||||
"@docusaurus/plugin-sitemap": "3.2.0",
|
||||
"@docusaurus/theme-classic": "3.2.0",
|
||||
"@docusaurus/theme-common": "3.2.0",
|
||||
"@docusaurus/theme-search-algolia": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/remark-plugin-npm2yarn",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"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.0",
|
||||
"description": "Classic theme for Docusaurus",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-classic.d.ts",
|
||||
|
|
@ -20,18 +20,18 @@
|
|||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/mdx-loader": "3.0.0",
|
||||
"@docusaurus/module-type-aliases": "3.0.0",
|
||||
"@docusaurus/plugin-content-blog": "3.0.0",
|
||||
"@docusaurus/plugin-content-docs": "3.0.0",
|
||||
"@docusaurus/plugin-content-pages": "3.0.0",
|
||||
"@docusaurus/theme-common": "3.0.0",
|
||||
"@docusaurus/theme-translations": "3.0.0",
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/utils": "3.0.0",
|
||||
"@docusaurus/utils-common": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/mdx-loader": "3.2.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/plugin-content-blog": "3.2.0",
|
||||
"@docusaurus/plugin-content-docs": "3.2.0",
|
||||
"@docusaurus/plugin-content-pages": "3.2.0",
|
||||
"@docusaurus/theme-common": "3.2.0",
|
||||
"@docusaurus/theme-translations": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"copy-text-to-clipboard": "^3.2.0",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function LastUpdatedAtDate({
|
|||
}: {
|
||||
lastUpdatedAt: number;
|
||||
}): JSX.Element {
|
||||
const atDate = new Date(lastUpdatedAt * 1000);
|
||||
const atDate = new Date(lastUpdatedAt);
|
||||
|
||||
const dateTimeFormat = useDateTimeFormat({
|
||||
day: 'numeric',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-common",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Common code for Docusaurus themes.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
|
@ -30,13 +30,13 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/mdx-loader": "3.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.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/plugin-content-blog": "3.2.0",
|
||||
"@docusaurus/plugin-content-docs": "3.2.0",
|
||||
"@docusaurus/plugin-content-pages": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@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.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"schema-dts": "^1.1.2"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import type {
|
|||
} from '@docusaurus/plugin-content-blog';
|
||||
import type {DocusaurusConfig} from '@docusaurus/types';
|
||||
|
||||
const convertDate = (dateMs: number) => new Date(dateMs * 1000).toISOString();
|
||||
const convertDate = (dateMs: number) => new Date(dateMs).toISOString();
|
||||
|
||||
function getBlogPost(
|
||||
blogPostContent: PropBlogPostContent,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-live-codeblock",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Docusaurus live code block component.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-live-codeblock.d.ts",
|
||||
|
|
@ -23,10 +23,10 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/theme-common": "3.0.0",
|
||||
"@docusaurus/theme-translations": "3.0.0",
|
||||
"@docusaurus/utils-validation": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/theme-common": "3.2.0",
|
||||
"@docusaurus/theme-translations": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@philpl/buble": "^0.19.7",
|
||||
"clsx": "^2.0.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@types/buble": "^0.20.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-mermaid",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Mermaid components for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-mermaid.d.ts",
|
||||
|
|
@ -33,11 +33,11 @@
|
|||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/module-type-aliases": "3.2.0",
|
||||
"@docusaurus/theme-common": "3.2.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"mermaid": "^10.4.0",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-search-algolia",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Algolia search component for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": [
|
||||
|
|
@ -34,13 +34,13 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.5.2",
|
||||
"@docusaurus/core": "3.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.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/plugin-content-docs": "3.2.0",
|
||||
"@docusaurus/theme-common": "3.2.0",
|
||||
"@docusaurus/theme-translations": "3.2.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"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.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-translations",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Docusaurus theme translations.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "3.0.0",
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/core": "3.2.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/tsconfig",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Docusaurus base TypeScript configuration.",
|
||||
"main": "tsconfig.json",
|
||||
"publishConfig": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/types",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Common types for Docusaurus packages.",
|
||||
"types": "./src/index.d.ts",
|
||||
"publishConfig": {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export {
|
|||
|
||||
export {
|
||||
Plugin,
|
||||
PluginIdentifier,
|
||||
InitializedPlugin,
|
||||
LoadedPlugin,
|
||||
PluginModule,
|
||||
|
|
@ -69,6 +70,7 @@ export {
|
|||
|
||||
export {
|
||||
RouteConfig,
|
||||
RouteMetadata,
|
||||
RouteContext,
|
||||
PluginRouteContext,
|
||||
Registry,
|
||||
|
|
|
|||
|
|
@ -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> = {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import type {ParsedUrlQueryInput} from 'querystring';
|
|||
* A "module" represents a unit of serialized data emitted from the plugin. It
|
||||
* will be imported on client-side and passed as props, context, etc.
|
||||
*
|
||||
* If it's a string, it's a file path that Webpack can `require`; if it's
|
||||
* If it's a string, it's a file path that the bundler can `require`; if it's
|
||||
* an object, it can also contain `query` or other metadata.
|
||||
*/
|
||||
export type Module =
|
||||
|
|
@ -37,13 +37,44 @@ export type RouteModules = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Plugin authors can assign extra metadata to the created routes
|
||||
* It is only available on the Node.js side, and not sent to the browser
|
||||
* Optional: plugin authors are encouraged but not required to provide it
|
||||
*
|
||||
* Some plugins might use this data to provide additional features.
|
||||
* This is the case of the sitemap plugin to provide support for "lastmod".
|
||||
* See also: https://github.com/facebook/docusaurus/pull/9954
|
||||
*/
|
||||
export type RouteMetadata = {
|
||||
/**
|
||||
* The source code file path that led to the creation of the current route
|
||||
* In official content plugins, this is usually a Markdown or React file
|
||||
* This path is expected to be relative to the site directory
|
||||
*/
|
||||
sourceFilePath?: string;
|
||||
/**
|
||||
* The last updated date of this route
|
||||
* This is generally read from the Git history of the sourceFilePath
|
||||
* but can also be provided through other means (usually front matter)
|
||||
*
|
||||
* This has notably been introduced for adding "lastmod" support to the
|
||||
* sitemap plugin, see https://github.com/facebook/docusaurus/pull/9954
|
||||
*/
|
||||
lastUpdatedAt?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a "slice" of the final route structure returned from the plugin
|
||||
* `addRoute` action.
|
||||
*/
|
||||
export type RouteConfig = {
|
||||
/** With leading slash. Trailing slash will be normalized by config. */
|
||||
/**
|
||||
* With leading slash. Trailing slash will be normalized by config.
|
||||
*/
|
||||
path: string;
|
||||
/** Component used to render this route, a path that Webpack can `require`. */
|
||||
/**
|
||||
* Component used to render this route, a path that the bundler can `require`.
|
||||
*/
|
||||
component: string;
|
||||
/**
|
||||
* Props. Each entry should be `[propName]: pathToPropModule` (created with
|
||||
|
|
@ -56,18 +87,31 @@ export type RouteConfig = {
|
|||
* here will be namespaced under {@link RouteContext.data}.
|
||||
*/
|
||||
context?: RouteModules;
|
||||
/** Nested routes config. */
|
||||
/**
|
||||
* Nested routes config, useful for "layout routes" having subroutes.
|
||||
*/
|
||||
routes?: RouteConfig[];
|
||||
/** React router config option: `exact` routes would not match subroutes. */
|
||||
/**
|
||||
* React router config option: `exact` routes would not match subroutes.
|
||||
*/
|
||||
exact?: boolean;
|
||||
/**
|
||||
* React router config option: `strict` routes are sensitive to the presence
|
||||
* of a trailing slash.
|
||||
*/
|
||||
strict?: boolean;
|
||||
/** Used to sort routes. Higher-priority routes will be placed first. */
|
||||
/**
|
||||
* Used to sort routes.
|
||||
* Higher-priority routes will be matched first.
|
||||
*/
|
||||
priority?: number;
|
||||
/** Extra props; will be copied to routes.js. */
|
||||
/**
|
||||
* Optional route metadata
|
||||
*/
|
||||
metadata?: RouteMetadata;
|
||||
/**
|
||||
* Extra props; will be available on the client side.
|
||||
*/
|
||||
[propName: string]: unknown;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils-common",
|
||||
"version": "3.0.0",
|
||||
"version": "3.2.0",
|
||||
"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<
|
||||
|
|
@ -28,9 +29,6 @@ export default function applyTrailingSlash(
|
|||
return path;
|
||||
}
|
||||
|
||||
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, '/');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ 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';
|
||||
|
|
|
|||
|
|
@ -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.0",
|
||||
"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.0",
|
||||
"@docusaurus/utils": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"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.0",
|
||||
"description": "Node utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/utils-common": "3.0.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
"node": ">=18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.0.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/micromatch": "^4.0.2",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import fs from 'fs-extra';
|
|||
import path from 'path';
|
||||
import {createTempRepo} from '@testing-utils/git';
|
||||
import {FileNotTrackedError, getFileCommitDate} from '../gitUtils';
|
||||
import {getFileLastUpdate} from '../lastUpdateUtils';
|
||||
import {getGitLastUpdate} from '../lastUpdateUtils';
|
||||
|
||||
/* eslint-disable no-restricted-properties */
|
||||
function initializeTempRepo() {
|
||||
|
|
@ -52,13 +52,13 @@ describe('getFileCommitDate', () => {
|
|||
getFileCommitDate(path.join(repoDir, 'test.txt'), {}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-06-19'),
|
||||
timestamp: new Date('2020-06-19').getTime() / 1000,
|
||||
timestamp: new Date('2020-06-19').getTime(),
|
||||
});
|
||||
await expect(
|
||||
getFileCommitDate(path.join(repoDir, 'dest.txt'), {}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-09-13'),
|
||||
timestamp: new Date('2020-09-13').getTime() / 1000,
|
||||
timestamp: new Date('2020-09-13').getTime(),
|
||||
});
|
||||
});
|
||||
it('returns latest commit date', async () => {
|
||||
|
|
@ -66,13 +66,13 @@ describe('getFileCommitDate', () => {
|
|||
getFileCommitDate(path.join(repoDir, 'test.txt'), {age: 'newest'}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-09-13'),
|
||||
timestamp: new Date('2020-09-13').getTime() / 1000,
|
||||
timestamp: new Date('2020-09-13').getTime(),
|
||||
});
|
||||
await expect(
|
||||
getFileCommitDate(path.join(repoDir, 'dest.txt'), {age: 'newest'}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-11-13'),
|
||||
timestamp: new Date('2020-11-13').getTime() / 1000,
|
||||
timestamp: new Date('2020-11-13').getTime(),
|
||||
});
|
||||
});
|
||||
it('returns latest commit date with author', async () => {
|
||||
|
|
@ -83,7 +83,7 @@ describe('getFileCommitDate', () => {
|
|||
}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-06-19'),
|
||||
timestamp: new Date('2020-06-19').getTime() / 1000,
|
||||
timestamp: new Date('2020-06-19').getTime(),
|
||||
author: 'Caroline',
|
||||
});
|
||||
await expect(
|
||||
|
|
@ -93,7 +93,7 @@ describe('getFileCommitDate', () => {
|
|||
}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-09-13'),
|
||||
timestamp: new Date('2020-09-13').getTime() / 1000,
|
||||
timestamp: new Date('2020-09-13').getTime(),
|
||||
author: 'Caroline',
|
||||
});
|
||||
});
|
||||
|
|
@ -105,7 +105,7 @@ describe('getFileCommitDate', () => {
|
|||
}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-09-13'),
|
||||
timestamp: new Date('2020-09-13').getTime() / 1000,
|
||||
timestamp: new Date('2020-09-13').getTime(),
|
||||
author: 'Caroline',
|
||||
});
|
||||
await expect(
|
||||
|
|
@ -115,7 +115,7 @@ describe('getFileCommitDate', () => {
|
|||
}),
|
||||
).resolves.toEqual({
|
||||
date: new Date('2020-11-13'),
|
||||
timestamp: new Date('2020-11-13').getTime() / 1000,
|
||||
timestamp: new Date('2020-11-13').getTime(),
|
||||
author: 'Josh-Cena',
|
||||
});
|
||||
});
|
||||
|
|
@ -146,8 +146,9 @@ describe('getFileCommitDate', () => {
|
|||
const tempFilePath2 = path.join(repoDir, 'file2.md');
|
||||
await fs.writeFile(tempFilePath1, 'Lorem ipsum :)');
|
||||
await fs.writeFile(tempFilePath2, 'Lorem ipsum :)');
|
||||
await expect(getFileLastUpdate(tempFilePath1)).resolves.toBeNull();
|
||||
await expect(getFileLastUpdate(tempFilePath2)).resolves.toBeNull();
|
||||
// TODO this is not the correct place to test "getGitLastUpdate"
|
||||
await expect(getGitLastUpdate(tempFilePath1)).resolves.toBeNull();
|
||||
await expect(getGitLastUpdate(tempFilePath2)).resolves.toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(/not tracked by git./),
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -11,13 +11,12 @@ import path from 'path';
|
|||
import {createTempRepo} from '@testing-utils/git';
|
||||
import shell from 'shelljs';
|
||||
import {
|
||||
getFileLastUpdate,
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
getGitLastUpdate,
|
||||
LAST_UPDATE_FALLBACK,
|
||||
readLastUpdateData,
|
||||
} from '@docusaurus/utils';
|
||||
|
||||
describe('getFileLastUpdate', () => {
|
||||
describe('getGitLastUpdate', () => {
|
||||
const {repoDir} = createTempRepo();
|
||||
|
||||
const existingFilePath = path.join(
|
||||
|
|
@ -25,15 +24,15 @@ describe('getFileLastUpdate', () => {
|
|||
'__fixtures__/simple-site/hello.md',
|
||||
);
|
||||
it('existing test file in repository with Git timestamp', async () => {
|
||||
const lastUpdateData = await getFileLastUpdate(existingFilePath);
|
||||
const lastUpdateData = await getGitLastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {author, timestamp} = lastUpdateData!;
|
||||
expect(author).not.toBeNull();
|
||||
expect(typeof author).toBe('string');
|
||||
const {lastUpdatedAt, lastUpdatedBy} = lastUpdateData!;
|
||||
expect(lastUpdatedBy).not.toBeNull();
|
||||
expect(typeof lastUpdatedBy).toBe('string');
|
||||
|
||||
expect(timestamp).not.toBeNull();
|
||||
expect(typeof timestamp).toBe('number');
|
||||
expect(lastUpdatedAt).not.toBeNull();
|
||||
expect(typeof lastUpdatedAt).toBe('number');
|
||||
});
|
||||
|
||||
it('existing test file with spaces in path', async () => {
|
||||
|
|
@ -41,15 +40,15 @@ describe('getFileLastUpdate', () => {
|
|||
__dirname,
|
||||
'__fixtures__/simple-site/doc with space.md',
|
||||
);
|
||||
const lastUpdateData = await getFileLastUpdate(filePathWithSpace);
|
||||
const lastUpdateData = await getGitLastUpdate(filePathWithSpace);
|
||||
expect(lastUpdateData).not.toBeNull();
|
||||
|
||||
const {author, timestamp} = lastUpdateData!;
|
||||
expect(author).not.toBeNull();
|
||||
expect(typeof author).toBe('string');
|
||||
const {lastUpdatedBy, lastUpdatedAt} = lastUpdateData!;
|
||||
expect(lastUpdatedBy).not.toBeNull();
|
||||
expect(typeof lastUpdatedBy).toBe('string');
|
||||
|
||||
expect(timestamp).not.toBeNull();
|
||||
expect(typeof timestamp).toBe('number');
|
||||
expect(lastUpdatedAt).not.toBeNull();
|
||||
expect(typeof lastUpdatedAt).toBe('number');
|
||||
});
|
||||
|
||||
it('non-existing file', async () => {
|
||||
|
|
@ -62,7 +61,7 @@ describe('getFileLastUpdate', () => {
|
|||
'__fixtures__',
|
||||
nonExistingFileName,
|
||||
);
|
||||
await expect(getFileLastUpdate(nonExistingFilePath)).rejects.toThrow(
|
||||
await expect(getGitLastUpdate(nonExistingFilePath)).rejects.toThrow(
|
||||
/An error occurred when trying to get the last update date/,
|
||||
);
|
||||
expect(consoleMock).toHaveBeenCalledTimes(0);
|
||||
|
|
@ -74,7 +73,7 @@ describe('getFileLastUpdate', () => {
|
|||
const consoleMock = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
const lastUpdateData = await getFileLastUpdate(existingFilePath);
|
||||
const lastUpdateData = await getGitLastUpdate(existingFilePath);
|
||||
expect(lastUpdateData).toBeNull();
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(
|
||||
|
|
@ -92,7 +91,7 @@ describe('getFileLastUpdate', () => {
|
|||
.mockImplementation(() => {});
|
||||
const tempFilePath = path.join(repoDir, 'file.md');
|
||||
await fs.writeFile(tempFilePath, 'Lorem ipsum :)');
|
||||
await expect(getFileLastUpdate(tempFilePath)).resolves.toBeNull();
|
||||
await expect(getGitLastUpdate(tempFilePath)).resolves.toBeNull();
|
||||
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||
expect(consoleMock).toHaveBeenLastCalledWith(
|
||||
expect.stringMatching(/not tracked by git./),
|
||||
|
|
@ -103,7 +102,7 @@ describe('getFileLastUpdate', () => {
|
|||
|
||||
describe('readLastUpdateData', () => {
|
||||
const testDate = '2021-01-01';
|
||||
const testDateTime = new Date(testDate).getTime() / 1000;
|
||||
const testTimestamp = new Date(testDate).getTime();
|
||||
const testAuthor = 'ozaki';
|
||||
|
||||
it('read last time show author time', async () => {
|
||||
|
|
@ -112,8 +111,8 @@ describe('readLastUpdateData', () => {
|
|||
{showLastUpdateAuthor: true, showLastUpdateTime: true},
|
||||
{date: testDate},
|
||||
);
|
||||
expect(lastUpdatedAt).toEqual(testDateTime);
|
||||
expect(lastUpdatedBy).toBe(GIT_FALLBACK_LAST_UPDATE_AUTHOR);
|
||||
expect(lastUpdatedAt).toEqual(testTimestamp);
|
||||
expect(lastUpdatedBy).toBe(LAST_UPDATE_FALLBACK.lastUpdatedBy);
|
||||
});
|
||||
|
||||
it('read last author show author time', async () => {
|
||||
|
|
@ -123,7 +122,7 @@ describe('readLastUpdateData', () => {
|
|||
{author: testAuthor},
|
||||
);
|
||||
expect(lastUpdatedBy).toEqual(testAuthor);
|
||||
expect(lastUpdatedAt).toBe(GIT_FALLBACK_LAST_UPDATE_DATE);
|
||||
expect(lastUpdatedAt).toBe(LAST_UPDATE_FALLBACK.lastUpdatedAt);
|
||||
});
|
||||
|
||||
it('read last all show author time', async () => {
|
||||
|
|
@ -133,7 +132,7 @@ describe('readLastUpdateData', () => {
|
|||
{author: testAuthor, date: testDate},
|
||||
);
|
||||
expect(lastUpdatedBy).toEqual(testAuthor);
|
||||
expect(lastUpdatedAt).toEqual(testDateTime);
|
||||
expect(lastUpdatedAt).toEqual(testTimestamp);
|
||||
});
|
||||
|
||||
it('read last default show none', async () => {
|
||||
|
|
@ -160,7 +159,7 @@ describe('readLastUpdateData', () => {
|
|||
{showLastUpdateAuthor: true, showLastUpdateTime: false},
|
||||
{date: testDate},
|
||||
);
|
||||
expect(lastUpdatedBy).toBe(GIT_FALLBACK_LAST_UPDATE_AUTHOR);
|
||||
expect(lastUpdatedBy).toBe(LAST_UPDATE_FALLBACK.lastUpdatedBy);
|
||||
expect(lastUpdatedAt).toBeUndefined();
|
||||
});
|
||||
|
||||
|
|
@ -180,7 +179,7 @@ describe('readLastUpdateData', () => {
|
|||
{showLastUpdateAuthor: true, showLastUpdateTime: false},
|
||||
{},
|
||||
);
|
||||
expect(lastUpdatedBy).toBe(GIT_FALLBACK_LAST_UPDATE_AUTHOR);
|
||||
expect(lastUpdatedBy).toBe(LAST_UPDATE_FALLBACK.lastUpdatedBy);
|
||||
expect(lastUpdatedAt).toBeUndefined();
|
||||
});
|
||||
|
||||
|
|
@ -191,7 +190,7 @@ describe('readLastUpdateData', () => {
|
|||
{date: testDate},
|
||||
);
|
||||
expect(lastUpdatedBy).toBeUndefined();
|
||||
expect(lastUpdatedAt).toEqual(testDateTime);
|
||||
expect(lastUpdatedAt).toEqual(testTimestamp);
|
||||
});
|
||||
|
||||
it('read last author show time', async () => {
|
||||
|
|
@ -201,7 +200,7 @@ describe('readLastUpdateData', () => {
|
|||
{author: testAuthor},
|
||||
);
|
||||
expect(lastUpdatedBy).toBeUndefined();
|
||||
expect(lastUpdatedAt).toEqual(GIT_FALLBACK_LAST_UPDATE_DATE);
|
||||
expect(lastUpdatedAt).toEqual(LAST_UPDATE_FALLBACK.lastUpdatedAt);
|
||||
});
|
||||
|
||||
it('read last author show time only - both front matter', async () => {
|
||||
|
|
@ -211,7 +210,7 @@ describe('readLastUpdateData', () => {
|
|||
{author: testAuthor, date: testDate},
|
||||
);
|
||||
expect(lastUpdatedBy).toBeUndefined();
|
||||
expect(lastUpdatedAt).toEqual(testDateTime);
|
||||
expect(lastUpdatedAt).toEqual(testTimestamp);
|
||||
});
|
||||
|
||||
it('read last author show author only - both front matter', async () => {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
aliasedSitePath,
|
||||
toMessageRelativeFilePath,
|
||||
addTrailingPathSeparator,
|
||||
aliasedSitePathToRelativePath,
|
||||
} from '../pathUtils';
|
||||
|
||||
describe('isNameTooLong', () => {
|
||||
|
|
@ -185,6 +186,20 @@ describe('aliasedSitePath', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('aliasedSitePathToRelativePath', () => {
|
||||
it('works', () => {
|
||||
expect(aliasedSitePathToRelativePath('@site/site/relative/path')).toBe(
|
||||
'site/relative/path',
|
||||
);
|
||||
});
|
||||
|
||||
it('is fail-fast', () => {
|
||||
expect(() => aliasedSitePathToRelativePath('/site/relative/path')).toThrow(
|
||||
/Unexpected, filePath is not site-aliased: \/site\/relative\/path/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addTrailingPathSeparator', () => {
|
||||
it('works', () => {
|
||||
expect(addTrailingPathSeparator('foo')).toEqual(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* 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 {flattenRoutes} from '../routeUtils';
|
||||
import type {RouteConfig} from '@docusaurus/types';
|
||||
|
||||
describe('flattenRoutes', () => {
|
||||
it('returns flattened routes without parents', () => {
|
||||
const routes: RouteConfig[] = [
|
||||
{
|
||||
path: '/docs',
|
||||
component: '',
|
||||
routes: [
|
||||
{path: '/docs/someDoc', component: ''},
|
||||
{path: '/docs/someOtherDoc', component: ''},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/community',
|
||||
component: '',
|
||||
},
|
||||
];
|
||||
expect(flattenRoutes(routes)).toEqual([
|
||||
routes[0]!.routes![0],
|
||||
routes[0]!.routes![1],
|
||||
routes[1],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -5,14 +5,11 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {addTrailingSlash} from '@docusaurus/utils-common';
|
||||
import {
|
||||
normalizeUrl,
|
||||
getEditUrl,
|
||||
fileToPath,
|
||||
isValidPathname,
|
||||
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 {}
|
||||
|
|
@ -39,7 +48,7 @@ export async function getFileCommitDate(
|
|||
): Promise<{
|
||||
/** Relevant commit date. */
|
||||
date: Date;
|
||||
/** Timestamp in **seconds**, as returned from git. */
|
||||
/** Timestamp returned from git, converted to **milliseconds**. */
|
||||
timestamp: number;
|
||||
}>;
|
||||
/**
|
||||
|
|
@ -66,7 +75,7 @@ export async function getFileCommitDate(
|
|||
): Promise<{
|
||||
/** Relevant commit date. */
|
||||
date: Date;
|
||||
/** Timestamp in **seconds**, as returned from git. */
|
||||
/** Timestamp returned from git, converted to **milliseconds**. */
|
||||
timestamp: number;
|
||||
/** The author's name, as returned from git. */
|
||||
author: string;
|
||||
|
|
@ -86,13 +95,13 @@ 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.`,
|
||||
);
|
||||
|
|
@ -150,8 +159,9 @@ export async function getFileCommitDate(
|
|||
);
|
||||
}
|
||||
|
||||
const timestamp = Number(match.groups!.timestamp);
|
||||
const date = new Date(timestamp * 1000);
|
||||
const timestampInSeconds = Number(match.groups!.timestamp);
|
||||
const timestamp = timestampInSeconds * 1_000;
|
||||
const date = new Date(timestamp);
|
||||
|
||||
if (includeAuthor) {
|
||||
return {date, timestamp, author: match.groups!.author!};
|
||||
|
|
|
|||
|
|
@ -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,8 +45,6 @@ export {
|
|||
resolvePathname,
|
||||
parseURLPath,
|
||||
serializeURLPath,
|
||||
addLeadingSlash,
|
||||
removeTrailingSlash,
|
||||
hasSSHProtocol,
|
||||
buildHttpsUrl,
|
||||
buildSshUrl,
|
||||
|
|
@ -90,6 +83,7 @@ export {
|
|||
posixPath,
|
||||
toMessageRelativeFilePath,
|
||||
aliasedSitePath,
|
||||
aliasedSitePathToRelativePath,
|
||||
escapePath,
|
||||
addTrailingPathSeparator,
|
||||
} from './pathUtils';
|
||||
|
|
@ -117,12 +111,13 @@ export {
|
|||
export {isDraft, isUnlisted} from './contentVisibilityUtils';
|
||||
export {escapeRegexp} from './regExpUtils';
|
||||
export {askPreferredLanguage} from './cliUtils';
|
||||
export {flattenRoutes} from './routeUtils';
|
||||
|
||||
export {
|
||||
getFileLastUpdate,
|
||||
getGitLastUpdate,
|
||||
getLastUpdate,
|
||||
readLastUpdateData,
|
||||
LAST_UPDATE_FALLBACK,
|
||||
type LastUpdateData,
|
||||
type FrontMatterLastUpdate,
|
||||
readLastUpdateData,
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
} from './lastUpdateUtils';
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -14,43 +14,19 @@ import {
|
|||
} from './gitUtils';
|
||||
import type {PluginOptions} from '@docusaurus/types';
|
||||
|
||||
export const GIT_FALLBACK_LAST_UPDATE_DATE = 1539502055;
|
||||
|
||||
export const GIT_FALLBACK_LAST_UPDATE_AUTHOR = 'Author';
|
||||
|
||||
async function getGitLastUpdate(filePath: string): Promise<LastUpdateData> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// Use fake data in dev/test for faster development.
|
||||
return {
|
||||
lastUpdatedBy: GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
lastUpdatedAt: GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
};
|
||||
}
|
||||
const {author, timestamp} = (await getFileLastUpdate(filePath)) ?? {};
|
||||
return {lastUpdatedBy: author, lastUpdatedAt: timestamp};
|
||||
}
|
||||
|
||||
export type LastUpdateData = {
|
||||
/** A timestamp in **seconds**, directly acquired from `git log`. */
|
||||
/** A timestamp in **milliseconds**, usually read from `git log` */
|
||||
lastUpdatedAt?: number;
|
||||
/** The author's name directly acquired from `git log`. */
|
||||
/** The author's name, usually coming from `git log` */
|
||||
lastUpdatedBy?: string;
|
||||
};
|
||||
|
||||
export type FrontMatterLastUpdate = {
|
||||
author?: string;
|
||||
/** Date can be any
|
||||
* [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
|
||||
*/
|
||||
date?: Date | string;
|
||||
};
|
||||
|
||||
let showedGitRequirementError = false;
|
||||
let showedFileNotTrackedError = false;
|
||||
|
||||
export async function getFileLastUpdate(
|
||||
export async function getGitLastUpdate(
|
||||
filePath: string,
|
||||
): Promise<{timestamp: number; author: string} | null> {
|
||||
): Promise<LastUpdateData | null> {
|
||||
if (!filePath) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -63,7 +39,7 @@ export async function getFileLastUpdate(
|
|||
includeAuthor: true,
|
||||
});
|
||||
|
||||
return {timestamp: result.timestamp, author: result.author};
|
||||
return {lastUpdatedAt: result.timestamp, lastUpdatedBy: result.author};
|
||||
} catch (err) {
|
||||
if (err instanceof GitNotFoundError) {
|
||||
if (!showedGitRequirementError) {
|
||||
|
|
@ -87,11 +63,35 @@ export async function getFileLastUpdate(
|
|||
}
|
||||
}
|
||||
|
||||
export const LAST_UPDATE_FALLBACK: LastUpdateData = {
|
||||
lastUpdatedAt: 1539502055000,
|
||||
lastUpdatedBy: 'Author',
|
||||
};
|
||||
|
||||
export async function getLastUpdate(
|
||||
filePath: string,
|
||||
): Promise<LastUpdateData | null> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// Use fake data in dev/test for faster development.
|
||||
return LAST_UPDATE_FALLBACK;
|
||||
}
|
||||
return getGitLastUpdate(filePath);
|
||||
}
|
||||
|
||||
type LastUpdateOptions = Pick<
|
||||
PluginOptions,
|
||||
'showLastUpdateAuthor' | 'showLastUpdateTime'
|
||||
>;
|
||||
|
||||
export type FrontMatterLastUpdate = {
|
||||
author?: string;
|
||||
/**
|
||||
* Date can be any
|
||||
* [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
|
||||
*/
|
||||
date?: Date | string;
|
||||
};
|
||||
|
||||
export async function readLastUpdateData(
|
||||
filePath: string,
|
||||
options: LastUpdateOptions,
|
||||
|
|
@ -105,24 +105,24 @@ export async function readLastUpdateData(
|
|||
|
||||
const frontMatterAuthor = lastUpdateFrontMatter?.author;
|
||||
const frontMatterTimestamp = lastUpdateFrontMatter?.date
|
||||
? new Date(lastUpdateFrontMatter.date).getTime() / 1000
|
||||
? new Date(lastUpdateFrontMatter.date).getTime()
|
||||
: undefined;
|
||||
|
||||
// We try to minimize git last update calls
|
||||
// We call it at most once
|
||||
// If all the data is provided as front matter, we do not call it
|
||||
const getGitLastUpdateMemoized = _.memoize(() => getGitLastUpdate(filePath));
|
||||
const getGitLastUpdateBy = () =>
|
||||
getGitLastUpdateMemoized().then((update) => update.lastUpdatedBy);
|
||||
const getGitLastUpdateAt = () =>
|
||||
getGitLastUpdateMemoized().then((update) => update.lastUpdatedAt);
|
||||
const getLastUpdateMemoized = _.memoize(() => getLastUpdate(filePath));
|
||||
const getLastUpdateBy = () =>
|
||||
getLastUpdateMemoized().then((update) => update?.lastUpdatedBy);
|
||||
const getLastUpdateAt = () =>
|
||||
getLastUpdateMemoized().then((update) => update?.lastUpdatedAt);
|
||||
|
||||
const lastUpdatedBy = showLastUpdateAuthor
|
||||
? frontMatterAuthor ?? (await getGitLastUpdateBy())
|
||||
? frontMatterAuthor ?? (await getLastUpdateBy())
|
||||
: undefined;
|
||||
|
||||
const lastUpdatedAt = showLastUpdateTime
|
||||
? frontMatterTimestamp ?? (await getGitLastUpdateAt())
|
||||
? frontMatterTimestamp ?? (await getLastUpdateAt())
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,20 @@ export function aliasedSitePath(filePath: string, siteDir: string): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts back the aliased site path (starting with "@site/...") to a relative path
|
||||
*
|
||||
* TODO method this is a workaround, we shouldn't need to alias/un-alias paths
|
||||
* we should refactor the codebase to not have aliased site paths everywhere
|
||||
* We probably only need aliasing for client-only paths required by Webpack
|
||||
*/
|
||||
export function aliasedSitePathToRelativePath(filePath: string): string {
|
||||
if (filePath.startsWith('@site/')) {
|
||||
return filePath.replace('@site/', '');
|
||||
}
|
||||
throw new Error(`Unexpected, filePath is not site-aliased: ${filePath}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* When you have a path like C:\X\Y
|
||||
* It is not safe to use directly when generating code
|
||||
* For example, this would fail due to unescaped \:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {RouteConfig} from '@docusaurus/types';
|
||||
|
||||
/**
|
||||
* Recursively flatten routes and only return the "leaf routes"
|
||||
* Parent routes are filtered out
|
||||
*/
|
||||
export function flattenRoutes(routeConfig: RouteConfig[]): RouteConfig[] {
|
||||
function flatten(route: RouteConfig): RouteConfig[] {
|
||||
return route.routes ? route.routes.flatMap(flatten) : [route];
|
||||
}
|
||||
return routeConfig.flatMap(flatten);
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import resolvePathnameUnsafe from 'resolve-pathname';
|
||||
import {addPrefix, removeSuffix} from './jsUtils';
|
||||
|
||||
/**
|
||||
* Much like `path.join`, but much better. Takes an array of URL segments, and
|
||||
|
|
@ -232,16 +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, '/');
|
||||
}
|
||||
|
||||
/** 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.0",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
@ -43,13 +43,13 @@
|
|||
"@babel/runtime": "^7.22.6",
|
||||
"@babel/runtime-corejs3": "^7.22.6",
|
||||
"@babel/traverse": "^7.22.8",
|
||||
"@docusaurus/cssnano-preset": "3.0.0",
|
||||
"@docusaurus/logger": "3.0.0",
|
||||
"@docusaurus/mdx-loader": "3.0.0",
|
||||
"@docusaurus/cssnano-preset": "3.2.0",
|
||||
"@docusaurus/logger": "3.2.0",
|
||||
"@docusaurus/mdx-loader": "3.2.0",
|
||||
"@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.0",
|
||||
"@docusaurus/utils-common": "3.2.0",
|
||||
"@docusaurus/utils-validation": "3.2.0",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.3",
|
||||
|
|
@ -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.0",
|
||||
"@docusaurus/types": "3.2.0",
|
||||
"@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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue