Merge remote-tracking branch 'upstream/main'

This commit is contained in:
mamon aminyar 2025-12-20 20:57:47 +00:00
commit 0e1337cc22
470 changed files with 37214 additions and 5396 deletions

4
.eslintrc.js vendored
View File

@ -214,7 +214,7 @@ module.exports = {
],
'no-useless-escape': WARNING,
'no-void': [ERROR, {allowAsStatement: true}],
'prefer-destructuring': WARNING,
'prefer-destructuring': OFF,
'prefer-named-capture-group': WARNING,
'prefer-template': WARNING,
yoda: WARNING,
@ -304,7 +304,7 @@ module.exports = {
'jest/prefer-expect-resolves': WARNING,
'jest/prefer-lowercase-title': [WARNING, {ignore: ['describe']}],
'jest/prefer-spy-on': WARNING,
'jest/prefer-to-be': WARNING,
'jest/prefer-to-be': OFF,
'jest/prefer-to-have-length': WARNING,
'jest/require-top-level-describe': ERROR,
'jest/valid-title': [

View File

@ -27,10 +27,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -22,9 +22,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -32,3 +32,5 @@ jobs:
run: yarn || yarn || yarn
- name: Build blog-only
run: yarn workspace website build:blogOnly
env:
DOCUSAURUS_PERF_LOGGER: 'true'

View File

@ -25,9 +25,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -37,19 +37,20 @@ jobs:
- name: Build Hash Router
run: yarn build:website:fast
env:
DOCUSAURUS_PERF_LOGGER: 'true'
DOCUSAURUS_ROUTER: 'hash'
# Note: hash router + baseUrl do not play well together
# This would host at https://facebook.github.io/docusaurus/#/docusaurus/
# BASE_URL: '/docusaurus/' # hash router +
- name: Upload Website artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: website-hash-router-archive
path: website/build
#- name: Upload Website Pages artifact
# uses: actions/upload-pages-artifact@v3
# uses: actions/upload-pages-artifact@v4
# with:
# path: website/build

View File

@ -41,9 +41,9 @@ jobs:
DOCUSAURUS_INFRA: ['SLOWER', 'FASTER']
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -62,6 +62,7 @@ jobs:
comment-key: DOCUSAURUS_INFRA_${{ matrix.DOCUSAURUS_INFRA }}
env:
DOCUSAURUS_SLOWER: ${{ matrix.DOCUSAURUS_INFRA == 'SLOWER' && 'true' || 'false' }}
DOCUSAURUS_PERF_LOGGER: 'true'
# Ensures build times stay under reasonable thresholds
build-time:
@ -73,9 +74,9 @@ jobs:
DOCUSAURUS_INFRA: ['SLOWER', 'FASTER']
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -88,6 +89,7 @@ jobs:
timeout-minutes: ${{ matrix.DOCUSAURUS_INFRA == 'SLOWER' && 3 || 2 }}
env:
DOCUSAURUS_SLOWER: ${{ matrix.DOCUSAURUS_INFRA == 'SLOWER' && 'true' || 'false' }}
DOCUSAURUS_PERF_LOGGER: 'true'
# Ensure build with a warm cache does not increase too much
- name: Build (warm cache)
@ -96,5 +98,6 @@ jobs:
timeout-minutes: ${{ matrix.DOCUSAURUS_INFRA == 'SLOWER' && 1 || 2 }}
env:
DOCUSAURUS_SLOWER: ${{ matrix.DOCUSAURUS_INFRA == 'SLOWER' && 'true' || 'false' }}
DOCUSAURUS_PERF_LOGGER: 'true'
# TODO post a GitHub comment with build with perf warnings?

View File

@ -20,11 +20,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0 # Needed to get the commit number with "git rev-list --count HEAD"
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -33,12 +33,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Initialize CodeQL
uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # 3.26.5
uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # 4.31.0
with:
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # 3.26.5
uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # 4.31.0

View File

@ -18,10 +18,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -13,6 +13,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Dependency Review
uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # 4.7.1
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # 4.8.2

View File

@ -1,7 +1,7 @@
name: Lighthouse Report
on:
pull_request_target:
pull_request:
branches:
- main
- docusaurus-v**
@ -21,10 +21,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -53,7 +53,7 @@ jobs:
- name: Format lighthouse score
id: format_lighthouse_score
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # 7.0.1
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # 8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@ -65,7 +65,7 @@ jobs:
- name: Add Lighthouse stats as comment
id: comment_to_pr
uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943 # 2.9.3
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # 2.9.4
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number }}

View File

@ -19,7 +19,7 @@ jobs:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6.0.1
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
@ -42,6 +42,6 @@ jobs:
- name: Print Diff
run: git diff
- uses: stefanzweifel/git-auto-commit-action@v6
- uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: 'refactor: apply lint autofix'

View File

@ -20,9 +20,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -22,9 +22,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -38,12 +38,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['18.0', '20', '22', '24']
node: ['20.0', '20', '22', '24', '25.1']
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: ${{ matrix.node }}
cache: yarn
@ -78,9 +78,9 @@ jobs:
runs-on: windows-8-core
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js LTS
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -109,7 +109,7 @@ jobs:
DOCUSAURUS_PERF_LOGGER: 'true'
working-directory: test-website-in-workspace
- name: Upload Website artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: website-e2e-windows
path: test-website-in-workspace/build
@ -124,9 +124,9 @@ jobs:
variant: [-s, -st]
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js LTS
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -193,9 +193,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js LTS
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn
@ -233,9 +233,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js LTS
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -26,9 +26,9 @@ jobs:
variant: ['js', 'ts']
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Node LTS
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: lts/*
cache: yarn

View File

@ -27,14 +27,14 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
node: ['18.0', '20', '22', '24']
node: ['20.0', '20', '22', '24', '25.1']
steps:
- name: Support longpaths
run: git config --system core.longpaths true
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: ${{ matrix.node }}
cache: yarn
@ -54,14 +54,20 @@ jobs:
run: yarn workspace website test:swizzle:wrap:ts
- name: Docusaurus Build
run: yarn build:website:fast
env:
DOCUSAURUS_PERF_LOGGER: 'true'
- name: TypeCheck website
# TODO temporary, remove TS skipLibCheck
# see https://github.com/facebook/docusaurus/pull/10486
run: yarn workspace website typecheck --project tsconfig.skipLibCheck.json
run: yarn workspace website typecheck
- name: TypeCheck website - min version - v5.1
run: |
yarn add typescript@5.1.6 --exact -D -W --ignore-scripts
# DocSearch@4/ai@5 doesn't support TS 5.1 (with skipLibCheck=false)
jq '.resolutions."@docsearch/react" = "^3.9.0"' package.json > package.json.tmp && mv -Force package.json.tmp package.json
yarn add @docsearch/react@^3.9.0 --exact -D -W --ignore-scripts
yarn workspace website typecheck
- name: TypeCheck website - max version - Latest
# For latest TS there are often lib check errors, so we disable it

View File

@ -27,12 +27,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['18.0', '20', '22', '24']
node: ['20.0', '20', '22', '24', '25.1']
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: ${{ matrix.node }}
cache: yarn
@ -55,12 +55,16 @@ jobs:
run: yarn workspace website test:css-order
- name: TypeCheck website
# TODO temporary, remove TS skipLibCheck
# see https://github.com/facebook/docusaurus/pull/10486
run: yarn workspace website typecheck --project tsconfig.skipLibCheck.json
run: yarn workspace website typecheck
- name: TypeCheck website - min version - v5.1
run: |
yarn add typescript@5.1.6 --exact -D -W --ignore-scripts
# DocSearch@4/ai@5 doesn't support TS 5.1 (with skipLibCheck=false)
jq '.resolutions."@docsearch/react" = "^3.9.0"' package.json > package.json.tmp && mv -f package.json.tmp package.json
yarn add @docsearch/react@^3.9.0 --exact -D -W --ignore-scripts
yarn workspace website typecheck
- name: TypeCheck website - max version - Latest
# For latest TS there are often lib check errors, so we disable it

View File

@ -1,7 +1,7 @@
dist
node_modules
.yarn
build
**/build/**
coverage
.docusaurus
.idea
@ -11,6 +11,8 @@ coverage
jest/vendor
argos/test-results
packages/lqip-loader/lib/
packages/docusaurus/lib/
packages/docusaurus-*/lib/*

View File

@ -1,5 +1,186 @@
# Docusaurus Changelog
## 3.9.2 (2025-10-17)
#### :bug: Bug Fix
- `docusaurus-plugin-content-docs`
- [#11490](https://github.com/facebook/docusaurus/pull/11490) fix(docs): add support for missing `sidebar_key` front matter attribute ([@slorber](https://github.com/slorber))
- `docusaurus-cssnano-preset`
- [#11487](https://github.com/facebook/docusaurus/pull/11487) fix(cssnano-preset): disable CSS counter minification ([@YDKK](https://github.com/YDKK))
- `docusaurus-theme-search-algolia`
- [#11468](https://github.com/facebook/docusaurus/pull/11468) fix(theme-search-algolia): Fix Algolia AskAI validation logic ([@slorber](https://github.com/slorber))
- `docusaurus-theme-translations`
- [#11431](https://github.com/facebook/docusaurus/pull/11431) fix(theme-translation): add missing Polish (pl) theme translations ([@mariuszkrzaczkowski](https://github.com/mariuszkrzaczkowski))
- `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#11466](https://github.com/facebook/docusaurus/pull/11466) fix(theme): Fix CSS `scroll-margin-top` when clicking footnote items, factorize code ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#11452](https://github.com/facebook/docusaurus/pull/11452) fix(core): allow `i18n.localeConfigs.translate` in validation ([@trofim24](https://github.com/trofim24))
- `docusaurus-theme-mermaid`
- [#11437](https://github.com/facebook/docusaurus/pull/11437) fix(theme-mermaid): Fix Mermaid ELK layout dependency required bug on v3.9 ([@slorber](https://github.com/slorber))
#### :running_woman: Performance
- `docusaurus-theme-mermaid`
- [#11438](https://github.com/facebook/docusaurus/pull/11438) perf(theme-mermaid): lazy load the Mermaid library ([@slorber](https://github.com/slorber))
#### :nail_care: Polish
- `docusaurus-theme-classic`
- [#11463](https://github.com/facebook/docusaurus/pull/11463) fix(theme): remove "Edit this page" button from print view ([@richk21](https://github.com/richk21))
#### :robot: Dependencies
- [#11479](https://github.com/facebook/docusaurus/pull/11479) chore(deps): bump stefanzweifel/git-auto-commit-action from 6 to 7 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11480](https://github.com/facebook/docusaurus/pull/11480) chore(deps): bump github/codeql-action from 3.26.5 to 4.30.8 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11481](https://github.com/facebook/docusaurus/pull/11481) chore(deps): bump actions/dependency-review-action from 4.8.0 to 4.8.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11446](https://github.com/facebook/docusaurus/pull/11446) chore(deps): bump actions/dependency-review-action from 4.7.3 to 4.8.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### :globe_with_meridians: Translations
- `docusaurus-theme-translations`
- [#11484](https://github.com/facebook/docusaurus/pull/11484) fix(translations): improve Arabic theme translations ([@maysara-elshewehy](https://github.com/maysara-elshewehy))
#### Committers: 9
- Alexander Trofimov ([@trofim24](https://github.com/trofim24))
- Dan Roscigno ([@DanRoscigno](https://github.com/DanRoscigno))
- Eleni Grosdouli ([@egrosdou01](https://github.com/egrosdou01))
- Ethan ([@ethanppl](https://github.com/ethanppl))
- Mariusz Krzaczkowski ([@mariuszkrzaczkowski](https://github.com/mariuszkrzaczkowski))
- Maysara ([@maysara-elshewehy](https://github.com/maysara-elshewehy))
- Richa Kiran ([@richk21](https://github.com/richk21))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- [@YDKK](https://github.com/YDKK)
## 3.9.1 (2025-09-26)
#### :bug: Bug Fix
- `docusaurus`
- [#11434](https://github.com/facebook/docusaurus/pull/11434) fix(core): fix Docusaurus outDir for sites using baseUrl ([@slorber](https://github.com/slorber))
#### Committers: 1
- Sébastien Lorber ([@slorber](https://github.com/slorber))
## 3.9.0 (2025-09-25)
#### :rocket: New Feature
- `docusaurus-theme-search-algolia`
- [#11421](https://github.com/facebook/docusaurus/pull/11421) feat(theme-search-algolia): use DocSearch v4.1, optimize integration ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-blog`, `docusaurus-theme-classic`
- [#11425](https://github.com/facebook/docusaurus/pull/11425) feat(blog): Add support for email social icon + resize default social icon a bit ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#11426](https://github.com/facebook/docusaurus/pull/11426) feat(theme): Add theme-tabs-container stable className ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`, `docusaurus-theme-search-algolia`, `docusaurus-theme-translations`
- [#11327](https://github.com/facebook/docusaurus/pull/11327) feat(search): add runtime support for DocSearch v4 ([@dylantientcheu](https://github.com/dylantientcheu))
- `docusaurus-faster`, `docusaurus`
- [#11415](https://github.com/facebook/docusaurus/pull/11415) feat(faster): upgrade Rspack to 1.5, use lazyBarrel experiment, remove deprecated option ([@slorber](https://github.com/slorber))
- [#11294](https://github.com/facebook/docusaurus/pull/11294) feat(faster): Upgrade to Rspack 1.4 ([@slorber](https://github.com/slorber))
- `docusaurus-utils`
- [#11397](https://github.com/facebook/docusaurus/pull/11397) feat(mdx): resolve `@site/*` markdown links, fix resolution priority bugs ([@slorber](https://github.com/slorber))
- `docusaurus-theme-mermaid`
- [#11357](https://github.com/facebook/docusaurus/pull/11357) feat(mermaid): support elk layout ([@Feez2403](https://github.com/Feez2403))
- `docusaurus-plugin-pwa`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#11316](https://github.com/facebook/docusaurus/pull/11316) feat(core): Add `i18n.localeConfigs[locale].{url,baseUrl}` config options, fix multi-domain deployments ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#11304](https://github.com/facebook/docusaurus/pull/11304) feat(core): add `i18n.localeConfigs.translate` + skip translation process if `i18n/<locale>` dir doesn't exist ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-docs`
- [#11228](https://github.com/facebook/docusaurus/pull/11228) feat(docs): sidebar item `key` attribute - fix docs translations key conflicts ([@slorber](https://github.com/slorber))
- `create-docusaurus`
- [#11293](https://github.com/facebook/docusaurus/pull/11293) feat(create-docusaurus): use respectPrefersColorScheme in init template ([@slorber](https://github.com/slorber))
- `docusaurus-mdx-loader`, `docusaurus-types`, `docusaurus`
- [#11282](https://github.com/facebook/docusaurus/pull/11282) feat(core): add `siteConfig.markdown.emoji` config option to disable `remark-emoji` ([@slorber](https://github.com/slorber))
- `create-docusaurus`, `docusaurus-mdx-loader`, `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-types`, `docusaurus`
- [#11283](https://github.com/facebook/docusaurus/pull/11283) feat(core): Add `siteConfig.markdown.hooks`, deprecate `siteConfig.onBrokenMarkdownLinks` ([@slorber](https://github.com/slorber))
#### :bug: Bug Fix
- `docusaurus-theme-classic`, `docusaurus`
- [#11422](https://github.com/facebook/docusaurus/pull/11422) fix(theme): fix copy of indented code blocks, replace copy-text-to-clipboard by clipboard API ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`
- [#11407](https://github.com/facebook/docusaurus/pull/11407) fix(theme): remove hardcoded fill from Bluesky and LinkedIn icons ([@Simek](https://github.com/Simek))
- [#11389](https://github.com/facebook/docusaurus/pull/11389) fix(theme): render sidebar category index with unlisted children as a simple doc/link item ([@slorber](https://github.com/slorber))
- [#11360](https://github.com/facebook/docusaurus/pull/11360) fix(theme): Add translate no to heading anchors and blog authors ([@slorber](https://github.com/slorber))
- [#11356](https://github.com/facebook/docusaurus/pull/11356) fix(theme): Doc sidebar links/categories with long labels should display properly ([@slorber](https://github.com/slorber))
- [#11338](https://github.com/facebook/docusaurus/pull/11338) fix(theme-classic): fix collapsed sidebar category expansion when navigating to another link within that category ([@qqq614](https://github.com/qqq614))
- [#11289](https://github.com/facebook/docusaurus/pull/11289) fix(theme): Fix footnote ref scrolling behind the navbar when footnote back reference clicked ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#11410](https://github.com/facebook/docusaurus/pull/11410) fix(deps): upgrade webpack-dev-server to v5, fix security warning ([@slorber](https://github.com/slorber))
- [#11347](https://github.com/facebook/docusaurus/pull/11347) fix(core): Fix docusaurus start on macOS when exec throws a synchronous error ([@slorber](https://github.com/slorber))
- [#11271](https://github.com/facebook/docusaurus/pull/11271) fix(dev-server): use correct dev server HTML lang attribute ([@enumura1](https://github.com/enumura1))
- `docusaurus-theme-common`
- [#11405](https://github.com/facebook/docusaurus/pull/11405) fix(theme): fix `useColorMode()` visual glitches due to provider unmounts/remounts ([@slorber](https://github.com/slorber))
- [#11280](https://github.com/facebook/docusaurus/pull/11280) fix(theme-common): Export FooterColumnItem type ([@stubinubin](https://github.com/stubinubin))
- `docusaurus-bundler`, `docusaurus-faster`
- [#11383](https://github.com/facebook/docusaurus/pull/11383) fix(ssg): HTML minifier should preserve `<head>` for `og:image` crawlers ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`, `docusaurus-theme-translations`
- [#11331](https://github.com/facebook/docusaurus/pull/11331) fix(theme): Add `aria-label` to `IconExternalLink` with value `'(opens in new tab)'` ([@WestonThayer](https://github.com/WestonThayer))
- `docusaurus-plugin-content-docs`
- [#11281](https://github.com/facebook/docusaurus/pull/11281) fix(docs): Fix empty sidebar item category `className` lost when post-processed to a doc ([@slorber](https://github.com/slorber))
- [#11251](https://github.com/facebook/docusaurus/pull/11251) fix(docs): prevent docs ids conflicts within a version ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#11263](https://github.com/facebook/docusaurus/pull/11263) fix(theme): make `useHistorySelector()` hydration-safe + use it read search/hash in theme ([@slorber](https://github.com/slorber))
#### :memo: Documentation
- [#11339](https://github.com/facebook/docusaurus/pull/11339) docs: clarify impact of document ID on the URL ([@shanti2530](https://github.com/shanti2530))
#### :robot: Dependencies
- [#11402](https://github.com/facebook/docusaurus/pull/11402) chore(deps): bump actions/github-script from 7.0.1 to 8.0.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11401](https://github.com/facebook/docusaurus/pull/11401) chore(deps): bump actions/dependency-review-action from 4.7.2 to 4.7.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11403](https://github.com/facebook/docusaurus/pull/11403) chore(deps): bump actions/setup-node from 4.4.0 to 5.0.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11373](https://github.com/facebook/docusaurus/pull/11373) chore(deps): bump actions/dependency-review-action from 4.7.1 to 4.7.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11365](https://github.com/facebook/docusaurus/pull/11365) chore(deps): bump actions/checkout from 4 to 5 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11342](https://github.com/facebook/docusaurus/pull/11342) chore(deps): bump form-data from 4.0.1 to 4.0.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11341](https://github.com/facebook/docusaurus/pull/11341) chore(deps): bump marocchino/sticky-pull-request-comment from 2.9.3 to 2.9.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11285](https://github.com/facebook/docusaurus/pull/11285) chore(deps): bump marocchino/sticky-pull-request-comment from 2.9.2 to 2.9.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11272](https://github.com/facebook/docusaurus/pull/11272) chore(deps): bump stefanzweifel/git-auto-commit-action from 5 to 6 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#11273](https://github.com/facebook/docusaurus/pull/11273) chore(deps): bump treosh/lighthouse-ci-action from 12.1.0 to 12.6.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### :wrench: Maintenance
- `create-docusaurus`, `docusaurus-babel`, `docusaurus-bundler`, `docusaurus-cssnano-preset`, `docusaurus-faster`, `docusaurus-logger`, `docusaurus-mdx-loader`, `docusaurus-plugin-client-redirects`, `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-plugin-css-cascade-layers`, `docusaurus-plugin-debug`, `docusaurus-plugin-google-analytics`, `docusaurus-plugin-google-gtag`, `docusaurus-plugin-google-tag-manager`, `docusaurus-plugin-ideal-image`, `docusaurus-plugin-pwa`, `docusaurus-plugin-rsdoctor`, `docusaurus-plugin-sitemap`, `docusaurus-plugin-svgr`, `docusaurus-plugin-vercel-analytics`, `docusaurus-preset-classic`, `docusaurus-remark-plugin-npm2yarn`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-theme-live-codeblock`, `docusaurus-theme-mermaid`, `docusaurus-theme-search-algolia`, `docusaurus-theme-translations`, `docusaurus-utils-common`, `docusaurus-utils-validation`, `docusaurus-utils`, `docusaurus`, `eslint-plugin`, `lqip-loader`
- [#11408](https://github.com/facebook/docusaurus/pull/11408) chore: drop support for Node 18, that reached End-of-Life ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`
- [#11317](https://github.com/facebook/docusaurus/pull/11317) chore: minor reduction to inline svg/js code ([@SethFalco](https://github.com/SethFalco))
- `docusaurus-plugin-content-docs`
- [#11307](https://github.com/facebook/docusaurus/pull/11307) test(docs): fix docs tests issues ([@slorber](https://github.com/slorber))
- `docusaurus-bundler`
- [#11290](https://github.com/facebook/docusaurus/pull/11290) chore: upgrade website to Rspack 1.4 + fix Rspack internal performance tracing ([@slorber](https://github.com/slorber))
- Other
- [#11287](https://github.com/facebook/docusaurus/pull/11287) chore(website): split changelog per version + adjust changelog plugin implementation ([@slorber](https://github.com/slorber))
#### :globe_with_meridians: Translations
- `docusaurus-theme-translations`
- [#11315](https://github.com/facebook/docusaurus/pull/11315) fix(theme-translations): Add missing Portuguese (pt-BR) theme translations and improve some of it. ([@marcelocell](https://github.com/marcelocell))
- [#11305](https://github.com/facebook/docusaurus/pull/11305) fix(translations): Add missing Ukrainian translations ([@maluke](https://github.com/maluke))
#### Committers: 18
- Akshat Sinha ([@akshatsinha0](https://github.com/akshatsinha0))
- Bartosz Kaszubowski ([@Simek](https://github.com/Simek))
- Dylan Tientcheu ([@dylantientcheu](https://github.com/dylantientcheu))
- Guo Ci ([@guoci](https://github.com/guoci))
- Jaime Iniesta ([@jaimeiniesta](https://github.com/jaimeiniesta))
- Joshua Chen ([@Josh-Cena](https://github.com/Josh-Cena))
- Marcelo Junior ([@marcelocell](https://github.com/marcelocell))
- Maria Stellini ([@shanti2530](https://github.com/shanti2530))
- Riccardo ([@3v0k4](https://github.com/3v0k4))
- Sergey Schetinin ([@maluke](https://github.com/maluke))
- Seth Falco ([@SethFalco](https://github.com/SethFalco))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- Weston Thayer ([@WestonThayer](https://github.com/WestonThayer))
- [@Feez2403](https://github.com/Feez2403)
- [@stubinubin](https://github.com/stubinubin)
- [@ya-dvorovenko](https://github.com/ya-dvorovenko)
- enumura ([@enumura1](https://github.com/enumura1))
- hjcho ([@qqq614](https://github.com/qqq614))
## 3.8.1 (2025-06-06)
#### :bug: Bug Fix

View File

@ -82,7 +82,7 @@ Apart from the `good first issue`, the following labels are also worth looking a
- [`help wanted`](https://github.com/facebook/docusaurus/labels/help%20wanted): if you have specific knowledge in one domain, working on these issues can make your expertise shine.
- [`status: accepting pr`](https://github.com/facebook/docusaurus/labels/status%3A%20accepting%20pr): community contributors can feel free to claim any of these.
If you want to work on any of these issues, just drop a message saying "I'd like to work on this", and we will assign the issue to you and update the issue's status as "claimed". **You are expected to send a pull request within seven days** after that, so we can still delegate the issue to someone else if you are unavailable.
If you want to work on any of these issues, just drop a message saying "I am working on this". **You do not need to ask for assignment to work on any issue explicitly marked as welcoming external contributions.** However, don't "cookie lick", or squat on an issue without actually sending a PR. You are automatically considered as giving up if you don't **send a PR within seven days after your comment**, and the issue automatically becomes up for grabs again.
Alternatively, when opening an issue, you can also click the "self service" checkbox to indicate that you'd like to work on the issue yourself, which will also make us see the issue as "claimed".
@ -214,6 +214,16 @@ After you have signed the CLA, the CLA bot would automatically update the PR sta
If it happens that you were unavailable and your PR gets closed, feel free to reopen once it's ready! We are still happy to review it, help you complete it, and eventually merge it.
### AI-assisted PRs
We welcome the use of AI tools for authoring PRs, and we love to see people pushing the boundaries of AI capabilities. The core team actively uses different AI tools in our development process. However, we are aware that **many people are sending entirely AI-generated PRs as a low-effort way to farm OSS contributions**, so please be mindful of the following etiquette to show your respect for our time and our codebase:
- **Be transparent**: If a significant portion of your code is AI generated, please indicate that in your PR description.
- **Be accountable**: You are responsible for the code you submit, regardless of whether it was generated by AI or written by you. You should be able to explain every line of the code, ensure all tests pass, and address our reviews.
- **Be reasonable**: Sometimes we receive 1k LOC PRs that are obviously AI-generated and implement unsolicited features. Please note that significant changes require prior communication and approval from the team in the form of an issue.
We retain the right to close any PR that we deem as unproductive or low-effort, even when we agree with the spirit of the change.
### Breaking Changes
When adding a new breaking change, follow this template in your pull request:
@ -227,6 +237,10 @@ When adding a new breaking change, follow this template in your pull request:
- **Severity (number of people affected x effort)**:
```
> [!NOTE]
>
> Breaking changes should be discussed in the issue tracker before being implemented.
### What Happens Next?
The core Docusaurus team will be monitoring pull requests. Do help us by keeping pull requests consistent by following the guidelines above.

View File

@ -45,7 +45,7 @@ Short on time? Check out our [5-minute tutorial ⏱️](https://tutorial.docusau
- **Customizable**
> While Docusaurus ships with the key pages and sections you need to get started, including a home page, a docs section, a [blog](https://docusaurus.io/docs/blog), and additional support pages, it is also [customizable](https://docusaurus.io/docs/creating-pages) as well to ensure you have a site that is [uniquely yours](https://docusaurus.io/docs/styling-layout).
> While Docusaurus ships with the key pages and sections you need to get started, including a home page, a docs section, a [blog](https://docusaurus.io/docs/blog), and additional support pages, it is also [customizable](https://docusaurus.io/docs/creating-pages) to ensure you have a site that is [uniquely yours](https://docusaurus.io/docs/styling-layout).
## Installation
@ -120,4 +120,4 @@ The Docusaurus documentation (e.g., `.md` files in the `/docs` folder) is [Creat
[![Rocket Validator logo](./admin/img/rocketvalidator-logo.png)](https://rocketvalidator.com/)
[Rocket Validator](https://rocketvalidator.com/) helps us find HTML markup or accessibility issues.
[Rocket Validator](https://rocketvalidator.com/) helps us find [HTML markup and accessibility issues](https://rocketvalidator.com/stats/docusaurus.io).

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "test-bad-package",
"version": "3.8.1",
"version": "3.9.2",
"private": true,
"dependencies": {
"@mdx-js/react": "1.0.1",

View File

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

View File

@ -20,6 +20,19 @@ languages_mapping: &languages_mapping
two_letters_code:
pt-BR: pt-BR
# Crowdin regularly update their MDX parser
# Unfortunately, their v2 parser is more "MDX compliant" and thus can't parse
# Docusaurus MDX files correctly due to our custom {#headingId} syntax.
# Adding this type param permits using their older v1.2 parser.
# Note: you can find the version of a file using browser DevTools
# The source file icons will have a class such as "file_type_mdx_v1_2"
#
# TODO fix our headingId syntax
# providing an explicit type is annoying and not future-proof
# there's a risk that when adding an image in /docs, it will be parsed as mdx
# and duplicating source file configs for various extensions is not great either
mdx_file_type: &mdx_file_type mdx_v1_2
#
# Files configuration
#
@ -27,18 +40,33 @@ files:
- source: /website/i18n/en/**/*
translation: /website/i18n/%two_letters_code%/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/docs/**/*.mdx
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
languages_mapping: *languages_mapping
type: *mdx_file_type
- source: /website/docs/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/community/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs-community/current/**/%original_file_name%
ignore: [/**/*.mdx]
- source: /website/versioned_docs/**/*.mdx
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%
languages_mapping: *languages_mapping
type: *mdx_file_type
- source: /website/versioned_docs/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%
languages_mapping: *languages_mapping
ignore: [/**/*.mdx]
- source: /website/community/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs-community/current/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/blog/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/src/pages/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-pages/**/%original_file_name%
ignore: [/**/*.js, /**/*.jsx, /**/*.ts, /**/*.tsx, /**/*.css]

View File

@ -14,7 +14,7 @@ Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new
### What you'll need
- [Node.js](https://nodejs.org/en/download/) version 18.0 or above:
- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
## Generate a new site

View File

@ -26,7 +26,6 @@ const config: Config = {
projectName: 'docusaurus', // Usually your repo name.
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
@ -72,6 +71,9 @@ const config: Config = {
themeConfig: {
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
colorMode: {
respectPrefersColorScheme: true,
},
navbar: {
title: 'My Site',
logo: {

View File

@ -16,8 +16,8 @@
"dev": "docusaurus start"
},
"dependencies": {
"@docusaurus/core": "3.8.0",
"@docusaurus/preset-classic": "3.8.0",
"@docusaurus/core": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -25,9 +25,9 @@
"react-dom": "^19.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.8.0",
"@docusaurus/tsconfig": "3.8.0",
"@docusaurus/types": "3.8.0",
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/tsconfig": "3.9.2",
"@docusaurus/types": "3.9.2",
"typescript": "~5.6.2"
},
"browserslist": {
@ -43,7 +43,7 @@
]
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
},
"description": "Docusaurus example project (classic-typescript template)"
}

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new
### What you'll need
- [Node.js](https://nodejs.org/en/download/) version 18.0 or above:
- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
## Generate a new site

View File

@ -31,7 +31,6 @@ const config = {
projectName: 'docusaurus', // Usually your repo name.
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
@ -80,6 +79,9 @@ const config = {
({
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
colorMode: {
respectPrefersColorScheme: true,
},
navbar: {
title: 'My Site',
logo: {

View File

@ -15,8 +15,8 @@
"dev": "docusaurus start"
},
"dependencies": {
"@docusaurus/core": "3.8.0",
"@docusaurus/preset-classic": "3.8.0",
"@docusaurus/core": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -24,8 +24,8 @@
"react-dom": "^19.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.8.0",
"@docusaurus/types": "3.8.0"
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/types": "3.9.2"
},
"browserslist": {
"production": [
@ -40,7 +40,7 @@
]
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
},
"description": "Docusaurus example project"
}

File diff suppressed because it is too large Load Diff

5
jest/deps.d.ts vendored
View File

@ -12,8 +12,3 @@ declare module 'to-vfile' {
export function read(path: string, encoding?: string): Promise<VFile>;
}
declare module '@testing-utils/git' {
const createTempRepo: typeof import('./utils/git').createTempRepo;
export {createTempRepo};
}

View File

@ -82,7 +82,7 @@ function normalizePaths<T>(value: T): T {
(val) => val.split(cwdReal).join('<PROJECT_ROOT>'),
(val) => val.split(cwd).join('<PROJECT_ROOT>'),
// Replace home directory with <TEMP_DIR>
// Replace temp directory with <TEMP_DIR>
(val) => val.split(tempDirReal).join('<TEMP_DIR>'),
(val) => val.split(tempDir).join('<TEMP_DIR>'),

63
jest/utils/git.ts vendored
View File

@ -1,63 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
import shell from 'shelljs';
class Git {
constructor(private dir: string) {
const res = shell.exec('git init', {cwd: dir, silent: true});
if (res.code !== 0) {
throw new Error(`git init exited with code ${res.code}.
stderr: ${res.stderr}
stdout: ${res.stdout}`);
}
// Doesn't matter currently
shell.exec('git config user.email "test@jc-verse.com"', {
cwd: dir,
silent: true,
});
shell.exec('git config user.name "Test"', {cwd: dir, silent: true});
shell.exec('git commit --allow-empty -m "First commit"', {
cwd: dir,
silent: true,
});
}
commit(msg: string, date: string, author: string): void {
const addRes = shell.exec('git add .', {cwd: this.dir, silent: true});
const commitRes = shell.exec(
`git commit -m "${msg}" --date "${date}T00:00:00Z" --author "${author}"`,
{
cwd: this.dir,
env: {GIT_COMMITTER_DATE: `${date}T00:00:00Z`},
silent: true,
},
);
if (addRes.code !== 0) {
throw new Error(`git add exited with code ${addRes.code}.
stderr: ${addRes.stderr}
stdout: ${addRes.stdout}`);
}
if (commitRes.code !== 0) {
throw new Error(`git commit exited with code ${commitRes.code}.
stderr: ${commitRes.stderr}
stdout: ${commitRes.stdout}`);
}
}
}
// This function is sync so the same mock repo can be shared across tests
export function createTempRepo(): {repoDir: string; git: Git} {
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-test-repo'));
const git = new Git(repoDir);
return {repoDir, git};
}

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "create-docusaurus",
"version": "3.8.1",
"version": "3.9.2",
"description": "Create Docusaurus apps easily.",
"type": "module",
"repository": {
@ -22,10 +22,10 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/logger": "3.9.2",
"@docusaurus/utils": "3.9.2",
"commander": "^5.1.0",
"execa": "5.1.1",
"execa": "^5.1.1",
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",
"prompts": "^2.4.2",
@ -37,6 +37,6 @@
"@types/supports-color": "^8.1.1"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "docusaurus-2-classic-typescript-template",
"version": "3.8.1",
"version": "3.9.2",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -15,8 +15,8 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/preset-classic": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -24,9 +24,9 @@
"react-dom": "^19.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/tsconfig": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/tsconfig": "3.9.2",
"@docusaurus/types": "3.9.2",
"typescript": "~5.6.2"
},
"browserslist": {
@ -42,6 +42,6 @@
]
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "docusaurus-2-classic-template",
"version": "3.8.1",
"version": "3.9.2",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@ -14,8 +14,8 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/preset-classic": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/preset-classic": "3.9.2",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
@ -23,8 +23,8 @@
"react-dom": "^19.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/types": "3.8.1"
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/types": "3.9.2"
},
"browserslist": {
"production": [
@ -39,6 +39,6 @@
]
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -14,7 +14,7 @@ Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new
### What you'll need
- [Node.js](https://nodejs.org/en/download/) version 18.0 or above:
- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
## Generate a new site

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/babel",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docusaurus package for Babel-related utils.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -36,15 +36,14 @@
"@babel/preset-react": "^7.25.9",
"@babel/preset-typescript": "^7.25.9",
"@babel/runtime": "^7.25.9",
"@babel/runtime-corejs3": "^7.25.9",
"@babel/traverse": "^7.25.9",
"@docusaurus/logger": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/logger": "3.9.2",
"@docusaurus/utils": "3.9.2",
"babel-plugin-dynamic-import-node": "^2.3.3",
"fs-extra": "^11.1.1",
"tslib": "^2.6.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/bundler",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docusaurus util package to abstract the current bundler.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -19,11 +19,11 @@
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.9",
"@docusaurus/babel": "3.8.1",
"@docusaurus/cssnano-preset": "3.8.1",
"@docusaurus/logger": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/babel": "3.9.2",
"@docusaurus/cssnano-preset": "3.9.2",
"@docusaurus/logger": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"babel-loader": "^9.2.1",
"clean-css": "^5.3.3",
"copy-webpack-plugin": "^11.0.0",
@ -55,6 +55,6 @@
"@total-typescript/shoehorn": "^0.1.2"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -28,7 +28,7 @@ async function createSwcJsLoaderFactory(): Promise<
return ({isServer}) => {
return {
loader,
options: getOptions({isServer}),
options: getOptions({isServer, bundlerName: 'webpack'}),
};
};
}
@ -42,7 +42,7 @@ async function createRspackSwcJsLoaderFactory(): Promise<
return ({isServer}) => {
return {
loader,
options: getOptions({isServer}),
options: getOptions({isServer, bundlerName: 'rspack'}),
};
};
}

View File

@ -142,7 +142,10 @@ async function getRspackMinimizers({
}: MinimizersConfig): Promise<WebpackPluginInstance[]> {
const rspack = getCurrentBundlerAsRspack({currentBundler});
const getBrowserslistQueries = await importGetBrowserslistQueries();
const browserslistQueries = getBrowserslistQueries({isServer: false});
const browserslistQueries = getBrowserslistQueries({
isServer: false,
bundlerName: 'rspack',
});
const swcJsMinimizerOptions = await importSwcJsMinimizerOptions();
return [
// See https://rspack.dev/plugins/rspack/swc-js-minimizer-rspack-plugin

View File

@ -84,6 +84,10 @@ async function getSwcMinifier(): Promise<HtmlMinifier> {
// TODO maybe it's fine to only keep <!-- --> React comments?
preserveComments: [],
// Keep <head> tag: important for social image crawlers like LinkedIn
// See https://github.com/swc-project/swc/issues/10994
tagOmission: 'keep-head-and-body',
// Sorting these attributes (class) can lead to React hydration errors
sortSpaceSeparatedAttributeValues: false,
sortAttributes: false,

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/cssnano-preset",
"version": "3.8.1",
"version": "3.9.2",
"description": "Advanced cssnano preset for maximum optimization.",
"main": "lib/index.js",
"license": "MIT",
@ -26,6 +26,6 @@
"to-vfile": "^6.1.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -13,6 +13,9 @@ const preset: typeof advancedBasePreset = function preset(opts) {
const advancedPreset = advancedBasePreset({
autoprefixer: {add: false},
discardComments: {removeAll: true},
// See CodeBlock custom line number bug: https://github.com/facebook/docusaurus/pull/11487
/* cSpell:ignore Idents */
reduceIdents: {counter: false},
/* cSpell:ignore zindex */
zindex: false,
...opts,

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/faster",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docusaurus experimental package exposing new modern dependencies to make the build faster.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -18,18 +18,19 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/types": "3.8.1",
"@rspack/core": "^1.4.0",
"@docusaurus/types": "3.9.2",
"@rspack/core": "^1.5.0",
"@swc/core": "^1.7.39",
"@swc/html": "^1.7.39",
"@swc/html": "^1.13.5",
"browserslist": "^4.24.2",
"lightningcss": "^1.27.0",
"semver": "^7.5.4",
"swc-loader": "^0.2.6",
"tslib": "^2.6.0",
"webpack": "^5.95.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
},
"peerDependencies": {
"@docusaurus/types": "*"

View File

@ -9,18 +9,22 @@ import Rspack from '@rspack/core';
import * as lightningcss from 'lightningcss';
import browserslist from 'browserslist';
import {minify as swcHtmlMinifier} from '@swc/html';
import semver from 'semver';
import type {JsMinifyOptions, Options as SwcOptions} from '@swc/core';
import type {CurrentBundler} from '@docusaurus/types';
export const swcLoader = require.resolve('swc-loader');
export const getSwcLoaderOptions = ({
isServer,
bundlerName,
}: {
isServer: boolean;
bundlerName: CurrentBundler['name'];
}): SwcOptions => {
return {
env: {
targets: getBrowserslistQueries({isServer}),
targets: getBrowserslistQueries({isServer, bundlerName}),
},
jsc: {
parser: {
@ -63,20 +67,53 @@ export function getSwcJsMinimizerOptions(): JsMinifyOptions {
};
}
// TODO this is not accurate
// for Rspack we should read from the built-in browserslist data
// see https://github.com/facebook/docusaurus/pull/11496
function getLastBrowserslistKnownNodeVersion(
bundlerName: CurrentBundler['name'],
): string {
if (bundlerName === 'rspack') {
// TODO hardcoded value until Rspack exposes its Browserslist data
// see https://github.com/facebook/docusaurus/pull/11496
return '22.0.0';
}
// browserslist('last 1 node versions')[0]!.replace('node ', '')
return browserslist.nodeVersions.at(-1)!;
}
function getMinVersion(v1: string, v2: string): string {
return semver.lt(v1, v2) ? v1 : v2;
}
// We need this because of Rspack built-in LightningCSS integration
// See https://github.com/orgs/browserslist/discussions/846
export function getBrowserslistQueries({
isServer,
bundlerName,
}: {
isServer: boolean;
bundlerName: CurrentBundler['name'];
}): string[] {
if (isServer) {
return [`node ${process.versions.node}`];
// Escape hatch env variable
if (process.env.DOCUSAURUS_SERVER_NODE_TARGET) {
return [`node ${process.env.DOCUSAURUS_SERVER_NODE_TARGET}`];
}
// For server builds, we want to use the current Node version as target
// But we can't pass a target that Browserslist doesn't know about yet
const nodeTarget = getMinVersion(
process.versions.node,
getLastBrowserslistKnownNodeVersion(bundlerName),
);
return [`node ${nodeTarget}`];
}
const queries = browserslist.loadConfig({path: process.cwd()}) ?? [
...browserslist.defaults,
];
return queries;
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/logger",
"version": "3.8.1",
"version": "3.9.2",
"description": "An encapsulated logger for semantically formatting console messages.",
"main": "./lib/index.js",
"repository": {
@ -24,7 +24,7 @@
"tslib": "^2.6.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
},
"devDependencies": {
"@types/supports-color": "^8.1.1"

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/mdx-loader",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docusaurus Loader for MDX",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,9 +18,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/logger": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/logger": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"@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.8.1",
"@docusaurus/types": "3.9.2",
"@types/escape-html": "^1.0.2",
"@types/mdast": "^4.0.2",
"@types/stringify-object": "^3.3.1",
@ -62,6 +62,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -6,7 +6,6 @@
*/
import path from 'path';
import url from 'url';
import fs from 'fs-extra';
import {
toMessageRelativeFilePath,
@ -15,6 +14,7 @@ import {
findAsyncSequential,
getFileLoaderUtils,
parseURLOrPath,
parseLocalURLPath,
} from '@docusaurus/utils';
import escapeHtml from 'escape-html';
import {imageSizeFromFile} from 'image-size/fromFile';
@ -207,11 +207,11 @@ async function processImageNode(target: Target, context: Context) {
return;
}
const parsedUrl = url.parse(node.url);
if (parsedUrl.protocol || !parsedUrl.pathname) {
// pathname:// is an escape hatch, in case user does not want her images to
const localUrlPath = parseLocalURLPath(node.url);
if (!localUrlPath) {
// pathname:// is an escape hatch, in case the user does not want images to
// be converted to require calls going through webpack loader
if (parsedUrl.protocol === 'pathname:') {
if (parseURLOrPath(node.url).protocol === 'pathname:') {
node.url = node.url.replace('pathname://', '');
}
return;
@ -220,7 +220,7 @@ async function processImageNode(target: Target, context: Context) {
// We decode it first because Node Url.pathname is always encoded
// while the image file-system path are not.
// See https://github.com/facebook/docusaurus/discussions/10720
const decodedPathname = decodeURIComponent(parsedUrl.pathname);
const decodedPathname = decodeURIComponent(localUrlPath.pathname);
// We try to convert image urls without protocol to images with require calls
// going through webpack ensures that image assets exist at build time

View File

@ -6,7 +6,6 @@
*/
import path from 'path';
import url from 'url';
import fs from 'fs-extra';
import {
toMessageRelativeFilePath,
@ -15,6 +14,7 @@ import {
findAsyncSequential,
getFileLoaderUtils,
parseURLOrPath,
parseLocalURLPath,
} from '@docusaurus/utils';
import escapeHtml from 'escape-html';
import logger from '@docusaurus/logger';
@ -209,21 +209,22 @@ async function processLinkNode(target: Target, context: Context) {
return;
}
const parsedUrl = url.parse(node.url);
if (parsedUrl.protocol || !parsedUrl.pathname) {
const localUrlPath = parseLocalURLPath(node.url);
if (!localUrlPath) {
// Don't process pathname:// here, it's used by the <Link> component
return;
}
const hasSiteAlias = parsedUrl.pathname.startsWith('@site/');
const hasSiteAlias = localUrlPath.pathname.startsWith('@site/');
const hasAssetLikeExtension =
path.extname(parsedUrl.pathname) &&
!parsedUrl.pathname.match(/\.(?:mdx?|html)(?:#|$)/);
path.extname(localUrlPath.pathname) &&
!localUrlPath.pathname.match(/\.(?:mdx?|html)(?:#|$)/);
if (!hasSiteAlias && !hasAssetLikeExtension) {
return;
}
const localFilePath = await getLocalFileAbsolutePath(
decodeURIComponent(parsedUrl.pathname),
decodeURIComponent(localUrlPath.pathname),
context,
);

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/module-type-aliases",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docusaurus module type aliases.",
"types": "./src/index.d.ts",
"publishConfig": {
@ -12,7 +12,7 @@
"directory": "packages/docusaurus-module-type-aliases"
},
"dependencies": {
"@docusaurus/types": "3.8.1",
"@docusaurus/types": "3.9.2",
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router-config": "*",

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-client-redirects",
"version": "3.8.1",
"version": "3.9.2",
"description": "Client redirects plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,24 +18,24 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/logger": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-common": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/logger": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-common": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"eta": "^2.2.0",
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",
"tslib": "^2.6.0"
},
"devDependencies": {
"@docusaurus/types": "3.8.1"
"@docusaurus/types": "3.9.2"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-content-blog",
"version": "3.8.1",
"version": "3.9.2",
"description": "Blog plugin for Docusaurus.",
"main": "lib/index.js",
"types": "src/plugin-content-blog.d.ts",
@ -31,14 +31,14 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/logger": "3.8.1",
"@docusaurus/mdx-loader": "3.8.1",
"@docusaurus/theme-common": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-common": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/logger": "3.9.2",
"@docusaurus/mdx-loader": "3.9.2",
"@docusaurus/theme-common": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-common": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"cheerio": "1.0.0-rc.12",
"feed": "^4.2.2",
"fs-extra": "^11.1.1",
@ -56,7 +56,7 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
},
"devDependencies": {
"@total-typescript/shoehorn": "^0.1.2",

View File

@ -0,0 +1,637 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`buildAllRoutes works for realistic blog post 2`] = `
[
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/post1.md",
},
"modules": {
"content": "@site/blog/post1.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/post1",
},
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/post2.md",
},
"modules": {
"content": "@site/blog/post2.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/post2",
},
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/post3.md",
},
"modules": {
"content": "@site/blog/post3.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/post3",
},
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/post4.md",
},
"modules": {
"content": "@site/blog/post4.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/post4",
},
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/post5.md",
},
"modules": {
"content": "@site/blog/post5.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/post5",
},
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/post6.md",
},
"modules": {
"content": "@site/blog/post6.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/post6",
},
{
"component": "@theme/BlogListPage",
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/post1.md",
"query": {
"truncated": true,
},
},
},
{
"content": {
"__import": true,
"path": "@site/blog/post2.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog",
"props": {
"metadata": {
"blogDescription": "Custom blog description",
"blogTitle": "Custom blog title",
"nextPage": "/blog/page/2",
"page": 1,
"permalink": "/blog",
"postsPerPage": 2,
"previousPage": undefined,
"totalCount": 5,
"totalPages": 3,
},
},
},
{
"component": "@theme/BlogListPage",
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/post4.md",
"query": {
"truncated": true,
},
},
},
{
"content": {
"__import": true,
"path": "@site/blog/post5.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/page/2",
"props": {
"metadata": {
"blogDescription": "Custom blog description",
"blogTitle": "Custom blog title",
"nextPage": "/blog/page/3",
"page": 2,
"permalink": "/blog/page/2",
"postsPerPage": 2,
"previousPage": "/blog",
"totalCount": 5,
"totalPages": 3,
},
},
},
{
"component": "@theme/BlogListPage",
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/post6.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/page/3",
"props": {
"metadata": {
"blogDescription": "Custom blog description",
"blogTitle": "Custom blog title",
"nextPage": undefined,
"page": 3,
"permalink": "/blog/page/3",
"postsPerPage": 2,
"previousPage": "/blog/page/2",
"totalCount": 5,
"totalPages": 3,
},
},
},
{
"component": "@theme/BlogArchivePage",
"exact": true,
"path": "/blog/archive",
"props": {
"archive": {
"blogPosts": [
{
"content": "Content for post1",
"id": "post1",
"metadata": {
"authors": [
{
"key": "author1",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post1",
"frontMatter": {},
"permalink": "/blog/post1",
"readingTime": 2,
"source": "@site/blog/post1.md",
"tags": [],
"title": "Title for post1",
},
},
{
"content": "Content for post2",
"id": "post2",
"metadata": {
"authors": [
{
"key": "author1",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post2",
"frontMatter": {},
"permalink": "/blog/post2",
"readingTime": 2,
"source": "@site/blog/post2.md",
"tags": [],
"title": "Title for post2",
},
},
{
"content": "Content for post4",
"id": "post4",
"metadata": {
"authors": [
{
"key": "author1",
},
{
"key": "author2",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post4",
"frontMatter": {},
"permalink": "/blog/post4",
"readingTime": 2,
"source": "@site/blog/post4.md",
"tags": [],
"title": "Title for post4",
},
},
{
"content": "Content for post5",
"id": "post5",
"metadata": {
"authors": [
{
"key": "author2",
},
{
"key": "author3",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post5",
"frontMatter": {},
"permalink": "/blog/post5",
"readingTime": 2,
"source": "@site/blog/post5.md",
"tags": [],
"title": "Title for post5",
},
},
{
"content": "Content for post6",
"id": "post6",
"metadata": {
"authors": [],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post6",
"frontMatter": {},
"permalink": "/blog/post6",
"readingTime": 2,
"source": "@site/blog/post6.md",
"tags": [],
"title": "Title for post6",
},
},
],
},
},
},
{
"component": "@theme/Blog/Pages/BlogAuthorsListPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"modules": {
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/authors",
"props": {
"authors": [
{
"count": 3,
"key": "author1",
"name": "Author 1",
"page": {
"permalink": "/blog/authors/author1",
},
},
{
"count": 2,
"key": "author2",
"name": "Author 2",
"page": null,
},
{
"count": 1,
"key": "author3",
"name": "Author 3",
"page": {
"permalink": "/blog/authors/author3",
},
},
],
},
},
{
"component": "@theme/Blog/Pages/BlogAuthorsPostsPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/post1.md",
"query": {
"truncated": true,
},
},
},
{
"content": {
"__import": true,
"path": "@site/blog/post2.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/authors/author1",
"props": {
"author": {
"count": 3,
"key": "author1",
"name": "Author 1",
"page": {
"permalink": "/blog/authors/author1",
},
},
"listMetadata": {
"blogDescription": "Custom blog description",
"blogTitle": "Custom blog title",
"nextPage": "/blog/authors/author1/page/2",
"page": 1,
"permalink": "/blog/authors/author1",
"postsPerPage": 2,
"previousPage": undefined,
"totalCount": 3,
"totalPages": 2,
},
},
},
{
"component": "@theme/Blog/Pages/BlogAuthorsPostsPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/post4.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/authors/author1/page/2",
"props": {
"author": {
"count": 3,
"key": "author1",
"name": "Author 1",
"page": {
"permalink": "/blog/authors/author1",
},
},
"listMetadata": {
"blogDescription": "Custom blog description",
"blogTitle": "Custom blog title",
"nextPage": undefined,
"page": 2,
"permalink": "/blog/authors/author1/page/2",
"postsPerPage": 2,
"previousPage": "/blog/authors/author1",
"totalCount": 3,
"totalPages": 2,
},
},
},
{
"component": "@theme/Blog/Pages/BlogAuthorsPostsPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/post5.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/authors/author3",
"props": {
"author": {
"count": 1,
"key": "author3",
"name": "Author 3",
"page": {
"permalink": "/blog/authors/author3",
},
},
"listMetadata": {
"blogDescription": "Custom blog description",
"blogTitle": "Custom blog title",
"nextPage": undefined,
"page": 1,
"permalink": "/blog/authors/author3",
"postsPerPage": 2,
"previousPage": undefined,
"totalCount": 1,
"totalPages": 1,
},
},
},
]
`;
exports[`buildAllRoutes works for realistic blog post 3`] = `
{
"blog-post-list-prop-default.json": {
"items": [
{
"date": 2020-01-01T00:00:00.000Z,
"permalink": "/blog/post1",
"title": "Title for post1",
"unlisted": undefined,
},
{
"date": 2020-01-01T00:00:00.000Z,
"permalink": "/blog/post2",
"title": "Title for post2",
"unlisted": undefined,
},
{
"date": 2020-01-01T00:00:00.000Z,
"permalink": "/blog/post3",
"title": "Title for post3",
"unlisted": true,
},
{
"date": 2020-01-01T00:00:00.000Z,
"permalink": "/blog/post4",
"title": "Title for post4",
"unlisted": undefined,
},
{
"date": 2020-01-01T00:00:00.000Z,
"permalink": "/blog/post5",
"title": "Title for post5",
"unlisted": undefined,
},
],
"title": "Custom blog sidebar title",
},
"blogMetadata-default.json": {
"authorsListPath": "/blog/authors",
"blogBasePath": "/blog",
"blogTitle": "Custom blog title",
},
"site-blog-post-1-md-235.json": {
"authors": [
{
"key": "author1",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post1",
"frontMatter": {},
"permalink": "/blog/post1",
"readingTime": 2,
"source": "@site/blog/post1.md",
"tags": [],
"title": "Title for post1",
},
"site-blog-post-2-md-b42.json": {
"authors": [
{
"key": "author1",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post2",
"frontMatter": {},
"permalink": "/blog/post2",
"readingTime": 2,
"source": "@site/blog/post2.md",
"tags": [],
"title": "Title for post2",
},
"site-blog-post-3-md-3b7.json": {
"authors": [
{
"key": "author3",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post3",
"frontMatter": {},
"permalink": "/blog/post3",
"readingTime": 2,
"source": "@site/blog/post3.md",
"tags": [],
"title": "Title for post3",
"unlisted": true,
},
"site-blog-post-4-md-15a.json": {
"authors": [
{
"key": "author1",
},
{
"key": "author2",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post4",
"frontMatter": {},
"permalink": "/blog/post4",
"readingTime": 2,
"source": "@site/blog/post4.md",
"tags": [],
"title": "Title for post4",
},
"site-blog-post-5-md-274.json": {
"authors": [
{
"key": "author2",
},
{
"key": "author3",
},
],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post5",
"frontMatter": {},
"permalink": "/blog/post5",
"readingTime": 2,
"source": "@site/blog/post5.md",
"tags": [],
"title": "Title for post5",
},
"site-blog-post-6-md-3ca.json": {
"authors": [],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for post6",
"frontMatter": {},
"permalink": "/blog/post6",
"readingTime": 2,
"source": "@site/blog/post6.md",
"tags": [],
"title": "Title for post6",
},
}
`;

View File

@ -24,24 +24,7 @@ exports[`getContentTranslationFiles returns translation files matching snapshot
exports[`translateContent falls back when translation is incomplete 1`] = `
{
"blogListPaginated": [
{
"items": [
"hello",
],
"metadata": {
"blogDescription": "Someone's random blog",
"blogTitle": "My blog",
"nextPage": undefined,
"page": 1,
"permalink": "/",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 1,
"totalPages": 1,
},
},
],
"blogDescription": "Someone's random blog",
"blogPosts": [
{
"content": "",
@ -63,29 +46,13 @@ exports[`translateContent falls back when translation is incomplete 1`] = `
"blogSidebarTitle": "All my posts",
"blogTags": {},
"blogTagsListPath": "/tags",
"blogTitle": "My blog",
}
`;
exports[`translateContent returns translated loaded 1`] = `
{
"blogListPaginated": [
{
"items": [
"hello",
],
"metadata": {
"blogDescription": "Someone's random blog (translated)",
"blogTitle": "My blog (translated)",
"nextPage": undefined,
"page": 1,
"permalink": "/",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 1,
"totalPages": 1,
},
},
],
"blogDescription": "Someone's random blog (translated)",
"blogPosts": [
{
"content": "",
@ -107,5 +74,6 @@ exports[`translateContent returns translated loaded 1`] = `
"blogSidebarTitle": "All my posts (translated)",
"blogTags": {},
"blogTagsListPath": "/tags",
"blogTitle": "My blog (translated)",
}
`;

View File

@ -21,11 +21,13 @@ describe('normalizeSocials', () => {
twitch: 'gingergeek',
youtube: 'gingergeekuk',
mastodon: 'Mastodon',
email: 'seb@example.com',
};
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
{
"bluesky": "https://bsky.app/profile/gingergeek.co.uk",
"email": "mailto:seb@example.com",
"github": "https://github.com/ozakione",
"instagram": "https://www.instagram.com/thisweekinreact",
"linkedin": "https://www.linkedin.com/in/ozakione/",
@ -48,11 +50,13 @@ describe('normalizeSocials', () => {
instaGRam: 'thisweekinreact',
BLUESKY: 'gingergeek.co.uk',
tHrEaDs: 'gingergeekuk',
eMAil: 'seb@example.com',
};
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
{
"bluesky": "https://bsky.app/profile/gingergeek.co.uk",
"email": "mailto:seb@example.com",
"github": "https://github.com/ozakione",
"instagram": "https://www.instagram.com/thisweekinreact",
"linkedin": "https://www.linkedin.com/in/ozakione/",
@ -69,6 +73,7 @@ describe('normalizeSocials', () => {
linkedin: 'https://linkedin.com/ozakione',
github: 'https://github.com/ozakione',
stackoverflow: 'https://stackoverflow.com/ozakione',
email: 'mailto:seb@example.com',
};
expect(normalizeSocials(socials)).toEqual(socials);
@ -81,10 +86,12 @@ describe('normalizeSocials', () => {
github: 'https://github.com/ozakione',
stackoverflow: 'https://stackoverflow.com/ozakione',
mastodon: 'https://hachyderm.io/@hachyderm',
email: 'mailto:seb@example.com',
};
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
{
"email": "mailto:seb@example.com",
"github": "https://github.com/ozakione",
"linkedin": "https://www.linkedin.com/in/ozakione/",
"mastodon": "https://hachyderm.io/@hachyderm",

View File

@ -8,7 +8,10 @@
import {jest} from '@jest/globals';
import path from 'path';
import fs from 'fs-extra';
import {DEFAULT_PARSE_FRONT_MATTER} from '@docusaurus/utils';
import {
DEFAULT_PARSE_FRONT_MATTER,
DEFAULT_VCS_CONFIG,
} from '@docusaurus/utils';
import {fromPartial} from '@total-typescript/shoehorn';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import tree from 'tree-node-cli';
@ -51,7 +54,7 @@ function getBlogContentPaths(siteDir: string): BlogContentPaths {
}
async function testGenerateFeeds(
context: LoadContext,
contextInput: LoadContext,
optionsInput: Options,
): Promise<void> {
const options = validateOptions({
@ -62,6 +65,17 @@ async function testGenerateFeeds(
options: optionsInput,
});
const context: LoadContext = {
...contextInput,
siteConfig: {
...contextInput.siteConfig,
future: {
...contextInput.siteConfig?.future,
experimental_vcs: DEFAULT_VCS_CONFIG,
},
},
};
const contentPaths = getBlogContentPaths(context.siteDir);
const authorsMap = await getAuthorsMap({
contentPaths,

View File

@ -8,12 +8,7 @@
import {jest} from '@jest/globals';
import * as path from 'path';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {
posixPath,
getFileCommitDate,
LAST_UPDATE_FALLBACK,
getLocaleConfig,
} from '@docusaurus/utils';
import {posixPath, getLocaleConfig, TEST_VCS} from '@docusaurus/utils';
import {DEFAULT_FUTURE_CONFIG} from '@docusaurus/core/src/server/configValidation';
import pluginContentBlog from '../index';
import {validateOptions} from '../options';
@ -32,6 +27,10 @@ import type {
EditUrlFunction,
} from '@docusaurus/plugin-content-blog';
async function getFileCreationDate(filePath: string): Promise<Date> {
return new Date((await TEST_VCS.getFileCreationInfo(filePath)).timestamp);
}
const markdown: MarkdownConfig = {
format: 'mdx',
mermaid: true,
@ -561,9 +560,7 @@ describe('blog plugin', () => {
const blogPosts = await getBlogPosts(siteDir);
const noDateSource = path.posix.join('@site', PluginPath, 'no date.md');
const noDateSourceFile = path.posix.join(siteDir, PluginPath, 'no date.md');
// We know the file exists and we know we have git
const result = await getFileCommitDate(noDateSourceFile, {age: 'oldest'});
const noDateSourceTime = result.date;
const noDateSourceTime = await getFileCreationDate(noDateSourceFile);
expect({
...getByTitle(blogPosts, 'no date').metadata,
@ -641,10 +638,7 @@ describe('blog plugin', () => {
},
DefaultI18N,
);
const {blogPosts, blogTags, blogListPaginated} =
(await plugin.loadContent!())!;
expect(blogListPaginated).toHaveLength(3);
const {blogPosts, blogTags} = (await plugin.loadContent!())!;
expect(Object.keys(blogTags)).toHaveLength(2);
expect(blogTags).toMatchSnapshot();
@ -674,29 +668,23 @@ describe('last update', () => {
);
const {blogPosts} = (await plugin.loadContent!())!;
const TestLastUpdate = await TEST_VCS.getFileLastUpdateInfo('any path');
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBe('seb');
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedBy,
);
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(TestLastUpdate.author);
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBe('seb');
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(TestLastUpdate.timestamp);
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedBy,
);
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(TestLastUpdate.author);
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(TestLastUpdate.timestamp);
});
it('time only', async () => {
@ -710,29 +698,27 @@ describe('last update', () => {
);
const {blogPosts} = (await plugin.loadContent!())!;
expect(blogPosts[0]?.metadata.title).toBe('Author');
const TestLastUpdate = await TEST_VCS.getFileLastUpdateInfo('any path');
expect(blogPosts[0]?.metadata.title).toBe('Both');
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[1]?.metadata.title).toBe('Nothing');
expect(blogPosts[1]?.metadata.title).toBe('Last update date');
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[2]?.metadata.title).toBe('Both');
expect(blogPosts[2]?.metadata.title).toBe('Author');
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(TestLastUpdate.timestamp);
expect(blogPosts[3]?.metadata.title).toBe('Last update date');
expect(blogPosts[3]?.metadata.title).toBe('Nothing');
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(TestLastUpdate.timestamp);
});
it('author only', async () => {
@ -746,20 +732,18 @@ describe('last update', () => {
);
const {blogPosts} = (await plugin.loadContent!())!;
const TestLastUpdate = await TEST_VCS.getFileLastUpdateInfo('any path');
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBe('seb');
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBeUndefined();
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedBy,
);
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(TestLastUpdate.author);
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBeUndefined();
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBe('seb');
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBeUndefined();
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedBy,
);
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(TestLastUpdate.author);
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBeUndefined();
});

View File

@ -0,0 +1,324 @@
/**
* 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 * as _ from 'lodash';
import {fromPartial} from '@total-typescript/shoehorn';
import {buildAllRoutes} from '../routes';
import {DEFAULT_OPTIONS} from '../options';
import type {PartialDeep} from '@total-typescript/shoehorn';
import type {BlogPost, BlogPostMetadata} from '@docusaurus/plugin-content-blog';
type Params = Parameters<typeof buildAllRoutes>[0];
async function testBuildAllRoutes(overrides: PartialDeep<Params> = {}) {
const createData = jest.fn(
async (name: string, _data: unknown) => `/data/${name}`,
);
const params: Params = fromPartial<Params>({
baseUrl: '/',
aliasedSource: (str: string) => `@aliased${str}`,
...overrides,
content: {
blogTitle: 'Blog Title',
blogDescription: 'Blog Description',
blogSidebarTitle: 'Blog Sidebar Title',
authorsMap: {},
blogTagsListPath: '',
blogTags: {},
blogPosts: [],
...overrides?.content,
},
options: {
...DEFAULT_OPTIONS,
...overrides?.options,
},
actions: {
createData,
...overrides?.actions,
},
});
const routes = await buildAllRoutes(params);
const data = Object.fromEntries(
createData.mock.calls.map((call) => [call[0], call[1]]),
);
function getRouteByPath(path: string) {
const route = routes.find((r) => r.path === path);
if (!route) {
throw new Error(`Route not found for path: ${path}`);
}
return route;
}
function getRoutesByComponent(component: string) {
return routes.filter((r) => r.component === component);
}
return {routes, data, utils: {getRouteByPath, getRoutesByComponent}};
}
function blogPost(overrides: PartialDeep<BlogPost> = {}): BlogPost {
const id = overrides.id ?? 'blog-post';
return fromPartial<BlogPost>({
id,
content: `Content for ${id}`,
...overrides,
metadata: fromPartial<BlogPostMetadata>({
title: `Title for ${id}`,
description: `Description for ${id}`,
permalink: `/blog/${id}`,
source: `@site/blog/${id}.md`,
date: new Date('2020-01-01'),
tags: [],
readingTime: 2,
authors: [],
frontMatter: {
...overrides?.metadata?.frontMatter,
},
...overrides?.metadata,
}),
});
}
describe('buildAllRoutes', () => {
it('works for empty blog', async () => {
const {routes, data} = await testBuildAllRoutes({
content: {
blogPosts: [],
},
});
expect(routes).toMatchInlineSnapshot(`
[
{
"component": "@theme/BlogListPage",
"exact": true,
"modules": {
"items": [],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog",
"props": {
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": undefined,
"page": 1,
"permalink": "/blog",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 0,
"totalPages": 1,
},
},
},
]
`);
expect(data).toMatchInlineSnapshot(`
{
"blog-post-list-prop-default.json": {
"items": [],
"title": "Blog Sidebar Title",
},
"blogMetadata-default.json": {
"authorsListPath": "/blog/authors",
"blogBasePath": "/blog",
"blogTitle": "Blog Title",
},
}
`);
});
it('works for single blog post', async () => {
const {routes, data} = await testBuildAllRoutes({
content: {
blogPosts: [blogPost()],
},
});
expect(routes).toMatchInlineSnapshot(`
[
{
"component": "@theme/BlogPostPage",
"context": {
"blogMetadata": "@aliased/data/blogMetadata-default.json",
},
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "blog/blog-post.md",
},
"modules": {
"content": "@site/blog/blog-post.md",
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog/blog-post",
},
{
"component": "@theme/BlogListPage",
"exact": true,
"modules": {
"items": [
{
"content": {
"__import": true,
"path": "@site/blog/blog-post.md",
"query": {
"truncated": true,
},
},
},
],
"sidebar": "@aliased/data/blog-post-list-prop-default.json",
},
"path": "/blog",
"props": {
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": undefined,
"page": 1,
"permalink": "/blog",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 1,
"totalPages": 1,
},
},
},
{
"component": "@theme/BlogArchivePage",
"exact": true,
"path": "/blog/archive",
"props": {
"archive": {
"blogPosts": [
{
"content": "Content for blog-post",
"id": "blog-post",
"metadata": {
"authors": [],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for blog-post",
"frontMatter": {},
"permalink": "/blog/blog-post",
"readingTime": 2,
"source": "@site/blog/blog-post.md",
"tags": [],
"title": "Title for blog-post",
},
},
],
},
},
},
]
`);
expect(data).toMatchInlineSnapshot(`
{
"blog-post-list-prop-default.json": {
"items": [
{
"date": 2020-01-01T00:00:00.000Z,
"permalink": "/blog/blog-post",
"title": "Title for blog-post",
"unlisted": undefined,
},
],
"title": "Blog Sidebar Title",
},
"blogMetadata-default.json": {
"authorsListPath": "/blog/authors",
"blogBasePath": "/blog",
"blogTitle": "Blog Title",
},
"site-blog-blog-post-md-0d7.json": {
"authors": [],
"date": 2020-01-01T00:00:00.000Z,
"description": "Description for blog-post",
"frontMatter": {},
"permalink": "/blog/blog-post",
"readingTime": 2,
"source": "@site/blog/blog-post.md",
"tags": [],
"title": "Title for blog-post",
},
}
`);
});
it('works for realistic blog post', async () => {
const {routes, data} = await testBuildAllRoutes({
options: {
postsPerPage: 2,
},
content: {
blogTitle: 'Custom blog title',
blogDescription: 'Custom blog description',
blogSidebarTitle: 'Custom blog sidebar title',
blogPosts: [
blogPost({id: 'post1', metadata: {authors: [{key: 'author1'}]}}),
blogPost({id: 'post2', metadata: {authors: [{key: 'author1'}]}}),
blogPost({
id: 'post3',
metadata: {
authors: [{key: 'author3'}],
unlisted: true,
},
}),
blogPost({
id: 'post4',
metadata: {
authors: [{key: 'author1'}, {key: 'author2'}],
},
}),
blogPost({
id: 'post5',
metadata: {authors: [{key: 'author2'}, {key: 'author3'}]},
}),
blogPost({id: 'post6'}),
],
authorsMap: {
author1: {
key: 'author1',
name: 'Author 1',
page: {permalink: '/blog/authors/author1'},
},
author2: {
key: 'author2',
name: 'Author 2',
page: null,
},
author3: {
key: 'author3',
name: 'Author 3',
page: {permalink: '/blog/authors/author3'},
},
},
},
});
expect(_.countBy(routes, 'component')).toMatchInlineSnapshot(`
{
"@theme/Blog/Pages/BlogAuthorsListPage": 1,
"@theme/Blog/Pages/BlogAuthorsPostsPage": 3,
"@theme/BlogArchivePage": 1,
"@theme/BlogListPage": 3,
"@theme/BlogPostPage": 6,
}
`);
expect(routes).toMatchSnapshot();
expect(data).toMatchSnapshot();
});
});

View File

@ -6,6 +6,7 @@
*/
import {updateTranslationFileMessages} from '@docusaurus/utils';
import {fromPartial} from '@total-typescript/shoehorn';
import {getTranslationFiles, translateContent} from '../translations';
import {DEFAULT_OPTIONS} from '../options';
import type {
@ -16,13 +17,13 @@ import type {
const sampleBlogOptions: PluginOptions = {
...DEFAULT_OPTIONS,
blogSidebarTitle: 'All my posts',
blogTitle: 'My blog',
blogDescription: "Someone's random blog",
blogSidebarTitle: 'All my posts',
};
const sampleBlogPosts: BlogPost[] = [
{
fromPartial({
id: 'hello',
metadata: {
permalink: '/blog/2021/06/19/hello',
@ -37,27 +38,13 @@ const sampleBlogPosts: BlogPost[] = [
unlisted: false,
},
content: '',
},
}),
];
const sampleBlogContent: BlogContent = {
blogTitle: sampleBlogOptions.blogTitle,
blogDescription: sampleBlogOptions.blogDescription,
blogSidebarTitle: sampleBlogOptions.blogSidebarTitle,
blogListPaginated: [
{
items: ['hello'],
metadata: {
permalink: '/',
page: 1,
postsPerPage: 10,
totalPages: 1,
totalCount: 1,
previousPage: undefined,
nextPage: undefined,
blogTitle: sampleBlogOptions.blogTitle,
blogDescription: sampleBlogOptions.blogDescription,
},
},
],
blogPosts: sampleBlogPosts,
blogTags: {},
blogTagsListPath: '/tags',

View File

@ -27,6 +27,7 @@ export const AuthorSocialsSchema = Joi.object<AuthorSocials>({
mastodon: Joi.string(),
twitch: Joi.string(),
youtube: Joi.string(),
email: Joi.string(),
}).unknown();
type PredefinedPlatformNormalizer = (value: string) => string;
@ -47,12 +48,12 @@ const PredefinedPlatformNormalizers: Record<
mastodon: (handle: string) => `https://mastodon.social/@${handle}`, // can be in format user@other.server and it will redirect if needed
twitch: (handle: string) => `https://twitch.tv/${handle}`,
youtube: (handle: string) => `https://youtube.com/@${handle}`, // https://support.google.com/youtube/answer/6180214?hl=en
email: (email: string) => `mailto:${email}`,
};
type SocialEntry = [string, string];
function normalizeSocialEntry([platform, value]: SocialEntry): SocialEntry {
const normalizer = PredefinedPlatformNormalizers[platform.toLowerCase()];
if (typeof value !== 'string') {
throw new Error(
`Author socials should be usernames/userIds/handles, or fully qualified HTTP(s) absolute URLs.
@ -60,7 +61,9 @@ Social platform '${platform}' has illegal value '${value}'`,
);
}
const isAbsoluteUrl =
value.startsWith('http://') || value.startsWith('https://');
value.startsWith('http://') ||
value.startsWith('https://') ||
value.startsWith('mailto:');
if (isAbsoluteUrl) {
return [platform, value];
} else if (value.includes('/')) {
@ -69,6 +72,7 @@ Social platform '${platform}' has illegal value '${value}'`,
Social platform '${platform}' has illegal value '${value}'`,
);
}
const normalizer = PredefinedPlatformNormalizers[platform.toLowerCase()];
if (normalizer && !isAbsoluteUrl) {
const normalizedPlatform = platform.toLowerCase();
const normalizedValue = normalizer(value);

View File

@ -19,7 +19,6 @@ import {
Globby,
groupTaggedItems,
getTagVisibility,
getFileCommitDate,
getContentPathList,
isUnlisted,
isDraft,
@ -225,6 +224,7 @@ async function processBlogSourceFile(
siteConfig: {
baseUrl,
markdown: {parseFrontMatter},
future: {experimental_vcs: vcs},
},
siteDir,
i18n,
@ -257,6 +257,7 @@ async function processBlogSourceFile(
blogSourceAbsolute,
options,
frontMatter.last_update,
vcs,
);
const draft = isDraft({frontMatter});
@ -285,17 +286,11 @@ async function processBlogSourceFile(
return parsedBlogFileName.date;
}
try {
const result = await getFileCommitDate(blogSourceAbsolute, {
age: 'oldest',
includeAuthor: false,
});
return result.date;
} catch (err) {
logger.warn(err);
const result = await vcs.getFileCreationInfo(blogSourceAbsolute);
if (result == null) {
return (await fs.stat(blogSourceAbsolute)).birthtime;
}
return new Date(result.timestamp);
}
const date = await getDate();
@ -406,6 +401,8 @@ export async function generateBlogPosts(
ignore: exclude,
});
// TODO this should be done outside of this function
// directly in plugin loadContent()
const tagsFile = await getTagsFile({contentPaths, tags: options.tags});
async function doProcessBlogSourceFile(blogSourceFile: string) {

View File

@ -17,7 +17,6 @@ import {
createAbsoluteFilePathMatcher,
getContentPathList,
getDataFilePath,
DEFAULT_PLUGIN_ID,
resolveMarkdownLinkPathname,
getLocaleConfig,
} from '@docusaurus/utils';
@ -25,7 +24,6 @@ import {getTagsFilePathsToWatch} from '@docusaurus/utils-validation';
import {createMDXLoaderItem} from '@docusaurus/mdx-loader';
import {
getBlogTags,
paginateBlogPosts,
shouldBeListed,
applyProcessBlogPosts,
generateBlogPosts,
@ -45,7 +43,6 @@ import type {
Assets,
BlogTags,
BlogContent,
BlogPaginated,
} from '@docusaurus/plugin-content-blog';
import type {RuleSetRule, RuleSetUseItem} from 'webpack';
@ -85,7 +82,7 @@ export default async function pluginContentBlog(
})
: undefined,
};
const pluginId = options.id ?? DEFAULT_PLUGIN_ID;
const pluginId = options.id;
const pluginDataDirRoot = path.join(generatedFilesDir, PluginName);
const dataDir = path.join(pluginDataDirRoot, pluginId);
@ -260,9 +257,10 @@ export default async function pluginContentBlog(
if (!blogPosts.length) {
return {
blogTitle,
blogDescription,
blogSidebarTitle,
blogPosts: [],
blogListPaginated: [],
blogTags: {},
blogTagsListPath,
authorsMap,
@ -291,15 +289,9 @@ export default async function pluginContentBlog(
}
});
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
blogPosts: listedBlogPosts,
blogTitle,
blogDescription,
postsPerPageOption,
basePageUrl: baseBlogUrl,
pageBasePath,
});
// TODO this is not the correct place to aggregate and paginate tags
// for reasons similar to https://github.com/facebook/docusaurus/pull/11562
// What we should do here is only read the tags file (similar to authors)
const blogTags: BlogTags = getBlogTags({
blogPosts,
postsPerPageOption,
@ -309,9 +301,10 @@ export default async function pluginContentBlog(
});
return {
blogTitle,
blogDescription,
blogSidebarTitle,
blogPosts,
blogListPaginated,
blogTags,
blogTagsListPath,
authorsMap,

View File

@ -15,7 +15,7 @@ import {
RouteBasePathSchema,
URISchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import {DEFAULT_PLUGIN_ID, GlobExcludeDefault} from '@docusaurus/utils';
import type {
PluginOptions,
Options,
@ -25,6 +25,7 @@ import type {
import type {OptionValidationContext} from '@docusaurus/types';
export const DEFAULT_OPTIONS: PluginOptions = {
id: DEFAULT_PLUGIN_ID,
feedOptions: {
type: ['rss', 'atom'],
copyright: '',

View File

@ -431,7 +431,7 @@ declare module '@docusaurus/plugin-content-blog' {
export type PluginOptions = MDXOptions &
TagsPluginOptions & {
/** Plugin ID. */
id?: string;
id: string;
/**
* Path to the blog content directory on the file system, relative to site
* directory.
@ -583,9 +583,10 @@ declare module '@docusaurus/plugin-content-blog' {
export type AuthorsMap = {[authorKey: string]: AuthorWithKey};
export type BlogContent = {
blogSidebarTitle: string;
blogTitle: string; // for translation purposes
blogDescription: string; // for translation purposes
blogSidebarTitle: string; // for translation purposes
blogPosts: BlogPost[];
blogListPaginated: BlogPaginated[];
blogTags: BlogTags;
blogTagsListPath: string;
authorsMap?: AuthorsMap;

View File

@ -67,27 +67,24 @@ export async function buildAllRoutes({
blogArchiveComponent,
routeBasePath,
archiveBasePath,
blogTitle,
authorsBasePath,
postsPerPage,
blogDescription,
pageBasePath,
} = options;
const pluginId = options.id!;
const pluginId = options.id;
const {createData} = actions;
const {
blogTitle,
blogDescription,
blogSidebarTitle,
blogPosts,
blogListPaginated,
blogTags,
blogTagsListPath,
authorsMap,
} = content;
const authorsListPath = normalizeUrl([
baseUrl,
routeBasePath,
authorsBasePath,
]);
const blogBasePath = normalizeUrl([baseUrl, routeBasePath]);
const authorsListPath = normalizeUrl([blogBasePath, authorsBasePath]);
const listedBlogPosts = blogPosts.filter(shouldBeListed);
@ -119,7 +116,7 @@ export async function buildAllRoutes({
async function createBlogMetadataModule() {
const blogMetadata: BlogMetadata = {
blogBasePath: normalizeUrl([baseUrl, routeBasePath]),
blogBasePath,
blogTitle,
authorsListPath,
};
@ -156,7 +153,7 @@ export async function buildAllRoutes({
if (archiveBasePath && listedBlogPosts.length) {
return [
{
path: normalizeUrl([baseUrl, routeBasePath, archiveBasePath]),
path: normalizeUrl([blogBasePath, archiveBasePath]),
component: blogArchiveComponent,
exact: true,
props: {
@ -210,6 +207,15 @@ export async function buildAllRoutes({
}
function createBlogPostsPaginatedRoutes(): RouteConfig[] {
const blogListPaginated = paginateBlogPosts({
blogPosts: listedBlogPosts,
blogTitle,
blogDescription,
postsPerPageOption: postsPerPage,
basePageUrl: blogBasePath,
pageBasePath,
});
return blogListPaginated.map((paginated) => {
return {
path: paginated.metadata.permalink,
@ -294,12 +300,14 @@ export async function buildAllRoutes({
sidebar: sidebarModulePath,
},
props: {
authors: authors.map((author) =>
toAuthorItemProp({
authors: authors.map((author) => {
const authorPosts = blogPostsByAuthorKey[author.key] ?? [];
const listedAuthorPosts = authorPosts.filter(shouldBeListed);
return toAuthorItemProp({
author,
count: blogPostsByAuthorKey[author.key]?.length ?? 0,
}),
),
count: listedAuthorPosts.length,
});
}),
},
context: {
blogMetadata: blogMetadataModulePath,
@ -309,16 +317,17 @@ export async function buildAllRoutes({
function createAuthorPaginatedRoute(author: AuthorWithKey): RouteConfig[] {
const authorBlogPosts = blogPostsByAuthorKey[author.key] ?? [];
const listedAuthorBlogPosts = authorBlogPosts.filter(shouldBeListed);
if (!author.page) {
return [];
}
const pages = paginateBlogPosts({
blogPosts: authorBlogPosts,
blogPosts: listedAuthorBlogPosts,
basePageUrl: author.page.permalink,
blogDescription,
blogTitle,
pageBasePath: authorsBasePath,
pageBasePath,
postsPerPageOption: postsPerPage,
});
@ -332,7 +341,10 @@ export async function buildAllRoutes({
sidebar: sidebarModulePath,
},
props: {
author: toAuthorItemProp({author, count: authorBlogPosts.length}),
author: toAuthorItemProp({
author,
count: listedAuthorBlogPosts.length,
}),
listMetadata: metadata,
},
context: {

View File

@ -5,30 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/
import type {TranslationFileContent, TranslationFile} from '@docusaurus/types';
import type {
PluginOptions,
BlogContent,
BlogPaginated,
} from '@docusaurus/plugin-content-blog';
function translateListPage(
blogListPaginated: BlogPaginated[],
translations: TranslationFileContent,
) {
return blogListPaginated.map((page) => {
const {items, metadata} = page;
return {
items,
metadata: {
...metadata,
blogTitle: translations.title?.message ?? page.metadata.blogTitle,
blogDescription:
translations.description?.message ?? page.metadata.blogDescription,
},
};
});
}
import type {TranslationFile} from '@docusaurus/types';
import type {PluginOptions, BlogContent} from '@docusaurus/plugin-content-blog';
export function getTranslationFiles(options: PluginOptions): TranslationFile[] {
return [
@ -56,14 +34,13 @@ export function translateContent(
content: BlogContent,
translationFiles: TranslationFile[],
): BlogContent {
const {content: optionsTranslations} = translationFiles[0]!;
const {content: translations} = translationFiles[0]!;
return {
...content,
blogTitle: translations.title?.message ?? content.blogTitle,
blogDescription:
translations.description?.message ?? content.blogDescription,
blogSidebarTitle:
optionsTranslations['sidebar.title']?.message ?? content.blogSidebarTitle,
blogListPaginated: translateListPage(
content.blogListPaginated,
optionsTranslations,
),
translations['sidebar.title']?.message ?? content.blogSidebarTitle,
};
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-content-docs",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docs plugin for Docusaurus.",
"main": "lib/index.js",
"sideEffects": false,
@ -35,15 +35,15 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/logger": "3.8.1",
"@docusaurus/mdx-loader": "3.8.1",
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/theme-common": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-common": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/logger": "3.9.2",
"@docusaurus/mdx-loader": "3.9.2",
"@docusaurus/module-type-aliases": "3.9.2",
"@docusaurus/theme-common": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-common": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"@types/react-router-config": "^5.0.7",
"combine-promises": "^1.1.0",
"fs-extra": "^11.1.1",
@ -65,6 +65,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -5,27 +5,27 @@ exports[`getLoadedContentTranslationFiles returns translation files 1`] = `
{
"content": {
"sidebar.docs.category.Getting started": {
"description": "The label for category Getting started in sidebar docs",
"description": "The label for category 'Getting started' in sidebar 'docs'",
"message": "Getting started",
},
"sidebar.docs.category.Getting started.link.generated-index.description": {
"description": "The generated-index page description for category Getting started in sidebar docs",
"description": "The generated-index page description for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index description",
},
"sidebar.docs.category.Getting started.link.generated-index.title": {
"description": "The generated-index page title for category Getting started in sidebar docs",
"description": "The generated-index page title for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index title",
},
"sidebar.docs.doc.Second doc translatable": {
"description": "The label for the doc item Second doc translatable in sidebar docs, linking to the doc doc2",
"description": "The label for the doc item 'Second doc translatable' in sidebar 'docs', linking to the doc doc2",
"message": "Second doc translatable",
},
"sidebar.docs.link.Link label": {
"description": "The label for link Link label in sidebar docs, linking to https://facebook.com",
"description": "The label for link 'Link label' in sidebar 'docs', linking to 'https://facebook.com'",
"message": "Link label",
},
"sidebar.otherSidebar.doc.Fifth doc translatable": {
"description": "The label for the doc item Fifth doc translatable in sidebar otherSidebar, linking to the doc doc5",
"description": "The label for the doc item 'Fifth doc translatable' in sidebar 'otherSidebar', linking to the doc doc5",
"message": "Fifth doc translatable",
},
"version.label": {
@ -38,27 +38,27 @@ exports[`getLoadedContentTranslationFiles returns translation files 1`] = `
{
"content": {
"sidebar.docs.category.Getting started": {
"description": "The label for category Getting started in sidebar docs",
"description": "The label for category 'Getting started' in sidebar 'docs'",
"message": "Getting started",
},
"sidebar.docs.category.Getting started.link.generated-index.description": {
"description": "The generated-index page description for category Getting started in sidebar docs",
"description": "The generated-index page description for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index description",
},
"sidebar.docs.category.Getting started.link.generated-index.title": {
"description": "The generated-index page title for category Getting started in sidebar docs",
"description": "The generated-index page title for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index title",
},
"sidebar.docs.doc.Second doc translatable": {
"description": "The label for the doc item Second doc translatable in sidebar docs, linking to the doc doc2",
"description": "The label for the doc item 'Second doc translatable' in sidebar 'docs', linking to the doc doc2",
"message": "Second doc translatable",
},
"sidebar.docs.link.Link label": {
"description": "The label for link Link label in sidebar docs, linking to https://facebook.com",
"description": "The label for link 'Link label' in sidebar 'docs', linking to 'https://facebook.com'",
"message": "Link label",
},
"sidebar.otherSidebar.doc.Fifth doc translatable": {
"description": "The label for the doc item Fifth doc translatable in sidebar otherSidebar, linking to the doc doc5",
"description": "The label for the doc item 'Fifth doc translatable' in sidebar 'otherSidebar', linking to the doc doc5",
"message": "Fifth doc translatable",
},
"version.label": {
@ -71,27 +71,27 @@ exports[`getLoadedContentTranslationFiles returns translation files 1`] = `
{
"content": {
"sidebar.docs.category.Getting started": {
"description": "The label for category Getting started in sidebar docs",
"description": "The label for category 'Getting started' in sidebar 'docs'",
"message": "Getting started",
},
"sidebar.docs.category.Getting started.link.generated-index.description": {
"description": "The generated-index page description for category Getting started in sidebar docs",
"description": "The generated-index page description for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index description",
},
"sidebar.docs.category.Getting started.link.generated-index.title": {
"description": "The generated-index page title for category Getting started in sidebar docs",
"description": "The generated-index page title for category 'Getting started' in sidebar 'docs'",
"message": "Getting started index title",
},
"sidebar.docs.doc.Second doc translatable": {
"description": "The label for the doc item Second doc translatable in sidebar docs, linking to the doc doc2",
"description": "The label for the doc item 'Second doc translatable' in sidebar 'docs', linking to the doc doc2",
"message": "Second doc translatable",
},
"sidebar.docs.link.Link label": {
"description": "The label for link Link label in sidebar docs, linking to https://facebook.com",
"description": "The label for link 'Link label' in sidebar 'docs', linking to 'https://facebook.com'",
"message": "Link label",
},
"sidebar.otherSidebar.doc.Fifth doc translatable": {
"description": "The label for the doc item Fifth doc translatable in sidebar otherSidebar, linking to the doc doc5",
"description": "The label for the doc item 'Fifth doc translatable' in sidebar 'otherSidebar', linking to the doc doc5",
"message": "Fifth doc translatable",
},
"version.label": {

View File

@ -12,8 +12,8 @@ import {
createSlugger,
posixPath,
DEFAULT_PLUGIN_ID,
LAST_UPDATE_FALLBACK,
getLocaleConfig,
TEST_VCS,
} from '@docusaurus/utils';
import {getTagsFile} from '@docusaurus/utils-validation';
import {createSidebarsUtils} from '../sidebars/utils';
@ -529,8 +529,8 @@ describe('simple site', () => {
custom_edit_url: 'https://github.com/customUrl/docs/lorem.md',
unrelated_front_matter: "won't be part of metadata",
},
lastUpdatedAt: LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdatedBy: LAST_UPDATE_FALLBACK.lastUpdatedBy,
lastUpdatedAt: TEST_VCS.LAST_UPDATE_INFO.timestamp,
lastUpdatedBy: TEST_VCS.LAST_UPDATE_INFO.author,
tags: [],
unlisted: false,
});
@ -664,7 +664,7 @@ describe('simple site', () => {
},
title: 'Last Update Author Only',
},
lastUpdatedAt: LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdatedAt: TEST_VCS.LAST_UPDATE_INFO.timestamp,
lastUpdatedBy: 'Custom Author (processed by parseFrontMatter)',
sidebarPosition: undefined,
tags: [],

View File

@ -186,10 +186,24 @@ describe('validateDocFrontMatter slug', () => {
});
});
describe('validateDocFrontMatter sidebar_key', () => {
testField({
prefix: 'sidebar_key',
validFrontMatters: [
{sidebar_key: undefined},
{sidebar_key: 'Awesome docs'},
],
invalidFrontMatters: [[{sidebar_key: ''}, 'is not allowed to be empty']],
});
});
describe('validateDocFrontMatter sidebar_label', () => {
testField({
prefix: 'sidebar_label',
validFrontMatters: [{sidebar_label: 'Awesome docs'}],
validFrontMatters: [
{sidebar_label: undefined},
{sidebar_label: 'Awesome docs'},
],
invalidFrontMatters: [[{sidebar_label: ''}, 'is not allowed to be empty']],
});
});

View File

@ -232,35 +232,35 @@ describe('getLoadedContentTranslationFiles', () => {
{
"content": {
"sidebar.sidebarWithConflicts.category.key-cat1": {
"description": "The label for category COMMON LABEL in sidebar sidebarWithConflicts",
"description": "The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts'",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.category.key-cat2": {
"description": "The label for category COMMON LABEL in sidebar sidebarWithConflicts",
"description": "The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts'",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-doc4": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-doc5": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-ref4": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.doc.key-ref5": {
"description": "The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5",
"description": "The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.link.key-link1": {
"description": "The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com",
"description": "The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com'",
"message": "COMMON LABEL",
},
"sidebar.sidebarWithConflicts.link.key-link2": {
"description": "The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com",
"description": "The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com'",
"message": "COMMON LABEL",
},
"version.label": {
@ -279,21 +279,24 @@ describe('getLoadedContentTranslationFiles', () => {
.toThrowErrorMatchingInlineSnapshot(`
"Multiple docs sidebar items produce the same translation key.
- \`sidebar.sidebarWithConflicts.category.COMMON LABEL\`: 2 duplicates found:
- COMMON LABEL (The label for category COMMON LABEL in sidebar sidebarWithConflicts)
- COMMON LABEL (The label for category COMMON LABEL in sidebar sidebarWithConflicts)
- COMMON LABEL (The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts')
- COMMON LABEL (The label for category 'COMMON LABEL' in sidebar 'sidebarWithConflicts')
- \`sidebar.sidebarWithConflicts.link.COMMON LABEL\`: 2 duplicates found:
- COMMON LABEL (The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com)
- COMMON LABEL (The label for link COMMON LABEL in sidebar sidebarWithConflicts, linking to https://example.com)
- COMMON LABEL (The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com')
- COMMON LABEL (The label for link 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to 'https://example.com')
- \`sidebar.sidebarWithConflicts.doc.COMMON LABEL\`: 4 duplicates found:
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4)
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5)
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc4)
- COMMON LABEL (The label for the doc item COMMON LABEL in sidebar sidebarWithConflicts, linking to the doc doc5)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc4)
- COMMON LABEL (The label for the doc item 'COMMON LABEL' in sidebar 'sidebarWithConflicts', linking to the doc doc5)
To avoid translation key conflicts, use the \`key\` attribute on the sidebar items above to uniquely identify them.
"
When using autogenerated sidebars, you can provide a unique translation key by adding:
- the \`key\` attribute to category item metadata (\`_category_.json\` / \`_category_.yml\`)
- the \`sidebar_key\` attribute to doc item metadata (front matter in \`Category/index.mdx\`)"
`);
});
});

View File

@ -97,6 +97,7 @@ async function doProcessDocMetadata({
siteDir,
siteConfig: {
markdown: {parseFrontMatter},
future: {experimental_vcs: vcs},
},
} = context;
@ -125,6 +126,7 @@ async function doProcessDocMetadata({
filePath,
options,
lastUpdateFrontMatter,
vcs,
);
// E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc

View File

@ -30,6 +30,7 @@ export const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
description: Joi.string().allow(''),
slug: Joi.string(),
sidebar_key: Joi.string(),
sidebar_label: Joi.string(),
sidebar_position: Joi.number(),
sidebar_class_name: Joi.string(),

View File

@ -339,7 +339,15 @@ declare module '@docusaurus/plugin-content-docs' {
* @see {@link DocMetadata.slug}
*/
slug?: string;
/** Customizes the sidebar label for this doc. Will default to its title. */
/**
* Customizes the sidebar key for this doc,
* to uniquely identify it in translations.
*/
sidebar_key?: string;
/**
* Customizes the sidebar label for this doc.
* Will default to its title.
*/
sidebar_label?: string;
/**
* Controls the position of a doc inside the generated sidebar slice when

View File

@ -91,6 +91,7 @@ exports[`DefaultSidebarItemsGenerator generates simple flat sidebar 1`] = `
"custom": "prop",
},
"id": "doc1",
"key": "doc1-sidebar-key",
"label": "doc1 sidebar label",
"type": "doc",
},
@ -120,6 +121,7 @@ exports[`DefaultSidebarItemsGenerator generates subfolder sidebar 1`] = `
"type": "doc",
},
],
"key": "doc1-sidebar-key",
"label": "Subsubsubfolder category label",
"link": {
"id": "doc1",
@ -142,6 +144,7 @@ exports[`DefaultSidebarItemsGenerator generates subfolder sidebar 1`] = `
},
{
"id": "doc1",
"key": "doc1-sidebar-key",
"type": "doc",
},
{

View File

@ -63,6 +63,7 @@ describe('DefaultSidebarItemsGenerator', () => {
sourceDirName: '.',
sidebarPosition: 2,
frontMatter: {
sidebar_key: 'doc1-sidebar-key',
sidebar_label: 'doc1 sidebar label',
sidebar_custom_props: {custom: 'prop'},
},
@ -254,7 +255,9 @@ describe('DefaultSidebarItemsGenerator', () => {
sourceDirName: 'subfolder/subsubfolder',
title: 'Subsubsubfolder category label',
sidebarPosition: undefined,
frontMatter: {},
frontMatter: {
sidebar_key: 'doc1-sidebar-key',
},
},
{
id: 'doc2',

View File

@ -139,6 +139,7 @@ Available doc IDs:
const {
sidebarPosition: position,
frontMatter: {
sidebar_key: key,
sidebar_label: label,
sidebar_class_name: className,
sidebar_custom_props: customProps,
@ -151,6 +152,7 @@ Available doc IDs:
source: fileName,
// We don't want these fields to magically appear in the generated
// sidebar
...(key !== undefined && {key}),
...(label !== undefined && {label}),
...(className !== undefined && {className}),
...(customProps !== undefined && {customProps}),
@ -191,6 +193,7 @@ Available doc IDs:
function getCategoryLinkedDocMetadata():
| {
id: string;
key?: string;
position?: number;
label?: string;
customProps?: {[key: string]: unknown};
@ -212,6 +215,7 @@ Available doc IDs:
return {
id,
position: doc.sidebarPosition,
key: doc.frontMatter.sidebar_key,
label: doc.frontMatter.sidebar_label ?? doc.title,
customProps: doc.frontMatter.sidebar_custom_props,
className: doc.frontMatter.sidebar_class_name,
@ -236,6 +240,8 @@ Available doc IDs:
categoryMetadata?.customProps ?? categoryLinkedDoc?.customProps;
const {filename, numberPrefix} = numberPrefixParser(folderName);
const key = categoryMetadata?.key ?? categoryLinkedDoc?.key;
return {
type: 'category',
label: categoryMetadata?.label ?? categoryLinkedDoc?.label ?? filename,
@ -252,7 +258,7 @@ Available doc IDs:
...(categoryMetadata?.description && {
description: categoryMetadata?.description,
}),
...(categoryMetadata?.key && {key: categoryMetadata?.key}),
...(key && {key}),
...(link && {link}),
};
}

View File

@ -71,7 +71,16 @@ function ensureNoSidebarDuplicateEntries(
To avoid translation key conflicts, use the ${logger.code(
'key',
)} attribute on the sidebar items above to uniquely identify them.
`);
When using autogenerated sidebars, you can provide a unique translation key by adding:
- the ${logger.code('key')} attribute to category item metadata (${logger.code(
'_category_.json',
)} / ${logger.code('_category_.yml')})
- the ${logger.code(
'sidebar_key',
)} attribute to doc item metadata (front matter in ${logger.code(
'Category/index.mdx',
)})`);
}
}
@ -90,7 +99,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.category.${categoryKey}`,
{
message: category.label,
description: `The label for category ${category.label} in sidebar ${sidebarName}`,
description: `The label for category '${category.label}' in sidebar '${sidebarName}'`,
},
]);
@ -100,7 +109,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.title`,
{
message: category.link.title,
description: `The generated-index page title for category ${category.label} in sidebar ${sidebarName}`,
description: `The generated-index page title for category '${category.label}' in sidebar '${sidebarName}'`,
},
]);
}
@ -109,7 +118,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.category.${categoryKey}.link.generated-index.description`,
{
message: category.link.description,
description: `The generated-index page description for category ${category.label} in sidebar ${sidebarName}`,
description: `The generated-index page description for category '${category.label}' in sidebar '${sidebarName}'`,
},
]);
}
@ -126,7 +135,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.link.${linkKey}`,
{
message: link.label,
description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
description: `The label for link '${link.label}' in sidebar '${sidebarName}', linking to '${link.href}'`,
},
];
});
@ -140,7 +149,7 @@ function getSidebarTranslationFileContent(
`sidebar.${sidebarName}.doc.${docKey}`,
{
message: doc.label!,
description: `The label for the doc item ${doc.label!} in sidebar ${sidebarName}, linking to the doc ${
description: `The label for the doc item '${doc.label!}' in sidebar '${sidebarName}', linking to the doc ${
doc.id
}`,
},

View File

@ -8,6 +8,7 @@
import * as path from 'path';
import {fromPartial} from '@total-typescript/shoehorn';
import {DEFAULT_PARSE_FRONT_MATTER} from '@docusaurus/utils/src';
import {DEFAULT_VCS_CONFIG} from '@docusaurus/utils';
import {readVersionsMetadata} from '../version';
import {DEFAULT_OPTIONS} from '../../options';
import {loadVersion} from '../loadVersion';
@ -37,6 +38,9 @@ async function siteFixture(fixture: string) {
markdown: {
parseFrontMatter: DEFAULT_PARSE_FRONT_MATTER,
},
future: {
experimental_vcs: DEFAULT_VCS_CONFIG,
},
},
});

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-content-pages",
"version": "3.8.1",
"version": "3.9.2",
"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.8.1",
"@docusaurus/mdx-loader": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/mdx-loader": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"fs-extra": "^11.1.1",
"tslib": "^2.6.0",
"webpack": "^5.88.1"
@ -32,6 +32,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -98,6 +98,7 @@ async function processPageSourceFile(
): Promise<Metadata | undefined> {
const {context, options, contentPaths} = params;
const {siteConfig, baseUrl, siteDir, i18n} = context;
const vcs = siteConfig.future.experimental_vcs;
const {editUrl} = options;
// Lookup in localized folder in priority
@ -180,6 +181,7 @@ async function processPageSourceFile(
source,
options,
frontMatter.last_update,
vcs,
);
if (isDraft({frontMatter})) {

View File

@ -13,7 +13,6 @@ import {
addTrailingPathSeparator,
createAbsoluteFilePathMatcher,
getContentPathList,
DEFAULT_PLUGIN_ID,
} from '@docusaurus/utils';
import {createMDXLoaderRule} from '@docusaurus/mdx-loader';
import {createAllRoutes} from './routes';
@ -38,7 +37,7 @@ export default async function pluginContentPages(
generatedFilesDir,
'docusaurus-plugin-content-pages',
);
const dataDir = path.join(pluginDataDirRoot, options.id ?? DEFAULT_PLUGIN_ID);
const dataDir = path.join(pluginDataDirRoot, options.id);
async function createPagesMDXLoaderRule(): Promise<RuleSetRule> {
const {

View File

@ -14,11 +14,12 @@ import {
RouteBasePathSchema,
URISchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import {DEFAULT_PLUGIN_ID, GlobExcludeDefault} from '@docusaurus/utils';
import type {OptionValidationContext} from '@docusaurus/types';
import type {PluginOptions, Options} from '@docusaurus/plugin-content-pages';
export const DEFAULT_OPTIONS: PluginOptions = {
id: DEFAULT_PLUGIN_ID,
path: 'src/pages', // Path to data on filesystem, relative to site dir.
routeBasePath: '/', // URL Route.
include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], // Extensions to include.

View File

@ -19,7 +19,7 @@ declare module '@docusaurus/plugin-content-pages' {
};
export type PluginOptions = MDXOptions & {
id?: string;
id: string;
path: string;
routeBasePath: string;
include: string[];

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-css-cascade-layers",
"version": "3.8.1",
"version": "3.9.2",
"description": "CSS Cascade Layer plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,13 +18,13 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"tslib": "^2.6.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-debug",
"version": "3.8.1",
"version": "3.9.2",
"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.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"fs-extra": "^11.1.1",
"react-json-view-lite": "^2.3.0",
"tslib": "^2.6.0"
@ -32,6 +32,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-google-analytics",
"version": "3.8.1",
"version": "3.9.2",
"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.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"tslib": "^2.6.0"
},
"peerDependencies": {
@ -28,6 +28,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-google-gtag",
"version": "3.8.1",
"version": "3.9.2",
"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.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"@types/gtag.js": "^0.0.12",
"tslib": "^2.6.0"
},
@ -29,6 +29,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-google-tag-manager",
"version": "3.8.1",
"version": "3.9.2",
"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.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"tslib": "^2.6.0"
},
"peerDependencies": {
@ -28,6 +28,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-ideal-image",
"version": "3.8.1",
"version": "3.9.2",
"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,18 +20,18 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/lqip-loader": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/lqip-loader": "3.9.2",
"@docusaurus/responsive-loader": "^1.7.0",
"@docusaurus/theme-translations": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/theme-translations": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"sharp": "^0.32.3",
"tslib": "^2.6.0",
"webpack": "^5.88.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/module-type-aliases": "3.9.2",
"fs-extra": "^11.1.0"
},
"peerDependencies": {
@ -45,6 +45,6 @@
}
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-pwa",
"version": "3.8.1",
"version": "3.9.2",
"description": "Docusaurus Plugin to add PWA support.",
"main": "lib/index.js",
"types": "src/plugin-pwa.d.ts",
@ -22,14 +22,14 @@
"dependencies": {
"@babel/core": "^7.25.9",
"@babel/preset-env": "^7.25.9",
"@docusaurus/bundler": "3.8.1",
"@docusaurus/core": "3.8.1",
"@docusaurus/logger": "3.8.1",
"@docusaurus/theme-common": "3.8.1",
"@docusaurus/theme-translations": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/bundler": "3.9.2",
"@docusaurus/core": "3.9.2",
"@docusaurus/logger": "3.9.2",
"@docusaurus/theme-common": "3.9.2",
"@docusaurus/theme-translations": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"babel-loader": "^9.2.1",
"clsx": "^2.0.0",
"core-js": "^3.31.1",
@ -41,7 +41,7 @@
"workbox-window": "^7.0.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.8.1",
"@docusaurus/module-type-aliases": "3.9.2",
"fs-extra": "^11.1.0"
},
"peerDependencies": {
@ -49,6 +49,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -9,12 +9,11 @@ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import {createStorageSlot} from '@docusaurus/theme-common';
// First: read the env variables (provided by Webpack)
/* eslint-disable prefer-destructuring */
const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL!;
const PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES = process.env
.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES as unknown as (keyof typeof OfflineModeActivationStrategiesImplementations)[];
const PWA_DEBUG = process.env.PWA_DEBUG;
/* eslint-enable prefer-destructuring */
const MAX_MOBILE_WIDTH = 996;

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-rsdoctor",
"version": "3.8.1",
"version": "3.9.2",
"description": "Rsdoctor plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,9 +18,9 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"@rsdoctor/rspack-plugin": "^0.4.6",
"@rsdoctor/webpack-plugin": "^0.4.6",
"tslib": "^2.6.0"
@ -30,6 +30,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@docusaurus/plugin-sitemap",
"version": "3.8.1",
"version": "3.9.2",
"description": "Simple sitemap generation plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -18,12 +18,12 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.8.1",
"@docusaurus/logger": "3.8.1",
"@docusaurus/types": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-common": "3.8.1",
"@docusaurus/utils-validation": "3.8.1",
"@docusaurus/core": "3.9.2",
"@docusaurus/logger": "3.9.2",
"@docusaurus/types": "3.9.2",
"@docusaurus/utils": "3.9.2",
"@docusaurus/utils-common": "3.9.2",
"@docusaurus/utils-validation": "3.9.2",
"fs-extra": "^11.1.1",
"sitemap": "^7.1.1",
"tslib": "^2.6.0"
@ -36,6 +36,6 @@
"react-dom": "^18.0.0 || ^19.0.0"
},
"engines": {
"node": ">=18.0"
"node": ">=20.0"
}
}

View File

@ -6,12 +6,14 @@
*/
import {fromPartial} from '@total-typescript/shoehorn';
import {DEFAULT_VCS_CONFIG} from '@docusaurus/utils';
import createSitemap from '../createSitemap';
import type {PluginOptions} from '../options';
import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
const siteConfig: DocusaurusConfig = fromPartial({
url: 'https://example.com',
future: {experimental_vcs: DEFAULT_VCS_CONFIG},
});
const options: PluginOptions = {

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