Compare commits

..

14 Commits
main ... v3.0.1

Author SHA1 Message Date
sebastienlorber 29d816067f v3.0.1 2023-11-30 19:38:20 +01:00
sebastienlorber 9cdec241d4 empty 2023-11-30 19:30:39 +01:00
MCR Studio fba460c75e chore(debug-plugin): migrate to a new maintained JSON Viewer (#9566)
Co-authored-by: Joey Clover <joey@popos.local>
2023-11-30 18:50:30 +01:00
Chongyi Zheng b1c2645a4b chore: upgrade prism-react-renderer to 2.3.0 to avoid older clsx (#9572) 2023-11-30 18:50:15 +01:00
Sébastien Lorber 0b34acf30f fix: add v2 retrocompatible support for quoted admonitions (#9570) 2023-11-30 18:50:08 +01:00
Chongyi Zheng 1875ca41c3 chore: Upgrade clsx to 2.0.0 (#9464)
Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
2023-11-30 18:49:50 +01:00
Sébastien Lorber d79eacc68c fix(theme): upgrade prism-react-renderer, fix html script and style tag highlighting (#9567) 2023-11-30 18:49:29 +01:00
Nick Gerleman 9aaaae8237 chore(core): replace `wait-on` dependency with custom lighter code (#9547) 2023-11-30 18:49:13 +01:00
Shreesh Nautiyal 4eb4693a97 fix(theme-classic): fixed wrong cursor on dropdown menu in navbar, when window is small (#9398) 2023-11-30 18:48:46 +01:00
Sébastien Lorber 1700a293c4 fix: v3 admonitions should support v2 title syntax for nested admonitions (#9535)
Fix admonition title mdx v1 compat option when admonition is nested
2023-11-30 18:47:50 +01:00
reece-white 01030044e0 chore: ugrade babel dependencies to v7.23.3 (#9529) 2023-11-30 18:47:34 +01:00
Sébastien Lorber 345ea6cf17 fix(theme): docs html sidebar items should always be visible (#9531) 2023-11-30 18:46:51 +01:00
Sébastien Lorber d019fc63df fix(theme): fix firefox CSS :has() support bug (#9530) 2023-11-30 18:46:34 +01:00
Sébastien Lorber ca7a279c02 fix(create-docusaurus): fix readme docusaurus 2 ref (#9487) 2023-11-30 18:45:33 +01:00
825 changed files with 9161 additions and 59182 deletions

View File

@ -37,6 +37,5 @@
"*.min.*",
"jest/vendor"
],
"ignoreRegExpList": ["Email", "Urls", "#[\\w-]*"],
"enableFiletypes": ["mdx"]
"ignoreRegExpList": ["Email", "Urls", "#[\\w-]*"]
}

13
.eslintrc.js vendored
View File

@ -85,14 +85,13 @@ module.exports = {
ignorePattern: '(eslint-disable|@)',
},
],
'arrow-body-style': OFF,
'no-await-in-loop': OFF,
'no-case-declarations': WARNING,
'no-console': OFF,
'no-constant-binary-expression': ERROR,
'no-continue': OFF,
'no-control-regex': WARNING,
'no-else-return': OFF,
'no-else-return': [WARNING, {allowElseIf: true}],
'no-empty': [WARNING, {allowEmptyCatch: true}],
'no-lonely-if': WARNING,
'no-nested-ternary': WARNING,
@ -204,10 +203,7 @@ module.exports = {
})),
],
'no-template-curly-in-string': WARNING,
'no-unused-expressions': [
WARNING,
{allowTaggedTemplates: true, allowShortCircuit: true},
],
'no-unused-expressions': [WARNING, {allowTaggedTemplates: true}],
'no-useless-escape': WARNING,
'no-void': [ERROR, {allowAsStatement: true}],
'prefer-destructuring': WARNING,
@ -348,7 +344,10 @@ module.exports = {
ERROR,
{'ts-expect-error': 'allow-with-description'},
],
'@typescript-eslint/consistent-indexed-object-style': OFF,
'@typescript-eslint/consistent-indexed-object-style': [
WARNING,
'index-signature',
],
'@typescript-eslint/consistent-type-imports': [
WARNING,
{disallowTypeAnnotations: false},

View File

@ -23,14 +23,14 @@ jobs:
# Argos is heavy to run
# We only want to trigger Argos on PRs with the 'Argos' label
# See https://stackoverflow.com/questions/62325286/run-github-actions-when-pull-requests-have-a-specific-label
if: ${{ (github.event_name != 'pull_request' && github.ref_name == 'main') || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Argos')) }}
if: ${{ github.ref_name == 'main' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Argos')) }}
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 18

View File

@ -24,7 +24,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn

View File

@ -38,7 +38,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn
@ -60,7 +60,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
cache: yarn
- name: Installation

View File

@ -21,7 +21,7 @@ jobs:
with:
fetch-depth: 0 # Needed to get the commit number with "git rev-list --count HEAD"
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn

View File

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

View File

@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 18
@ -36,7 +36,7 @@ jobs:
- name: Audit URLs using Lighthouse
id: lighthouse_audit
uses: treosh/lighthouse-ci-action@1b0e7c33270fbba31a18a0fbb1de7cc5256b6d39 # 11.4.0
uses: treosh/lighthouse-ci-action@03becbfc543944dd6e7534f7ff768abb8a296826 # 10.1.0
with:
urls: |
http://localhost:3000
@ -52,7 +52,7 @@ jobs:
- name: Format lighthouse score
id: format_lighthouse_score
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # 7.0.1
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@ -64,7 +64,7 @@ jobs:
- name: Add Lighthouse stats as comment
id: comment_to_pr
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # 2.9.0
uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd # 2.8.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number }}

View File

@ -1,44 +0,0 @@
name: Lint AutoFix
on:
pull_request:
branches:
- main
- docusaurus-v**
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
lint-autofix:
name: Lint AutoFix
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
- name: Installation
run: yarn
- name: AutoFix Format
run: yarn format
- name: AutoFix JS
run: yarn lint:js:fix
- name: AutoFix Style
run: yarn lint:style:fix
- name: AutoFix Spelling
run: yarn lint:spelling:fix
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'refactor: apply lint autofix'

View File

@ -22,7 +22,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn

View File

@ -24,9 +24,9 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '20'
node-version: '16'
cache: yarn
- name: Installation
run: yarn

View File

@ -43,7 +43,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: ${{ matrix.node }}
cache: yarn
@ -77,7 +77,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js 18
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn
@ -131,7 +131,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js 18
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn
@ -161,7 +161,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js 18
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn

View File

@ -28,7 +28,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: '18'
cache: yarn

View File

@ -33,7 +33,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: ${{ matrix.node }}
- name: Installation

View File

@ -31,7 +31,7 @@ jobs:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: ${{ matrix.node }}
cache: yarn

View File

@ -1,5 +1,8 @@
{
"*.{js,jsx,ts,tsx,mjs}": ["eslint --fix"],
"*.css": ["stylelint --allow-empty-input --fix"],
"*": ["prettier --ignore-unknown --write"]
"*": [
"prettier --ignore-unknown --write",
"cspell --no-must-find-files --no-progress"
]
}

View File

@ -24,5 +24,5 @@ website/versioned_sidebars/*.json
examples/
website/static/katex/katex.min.css
website/changelog
website/changelog/_swizzle_theme_tests
website/_dogfooding/_swizzle_theme_tests

View File

@ -1,271 +1,5 @@
# Docusaurus 2 Changelog
## 3.2.0 (2024-03-29)
#### :rocket: New Feature
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-plugin-sitemap`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#9954](https://github.com/facebook/docusaurus/pull/9954) feat(sitemap): add support for "lastmod" ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils-validation`, `docusaurus-utils`
- [#9912](https://github.com/facebook/docusaurus/pull/9912) feat(blog): add LastUpdateAuthor & LastUpdateTime ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-plugin-debug`, `docusaurus-types`, `docusaurus`
- [#9931](https://github.com/facebook/docusaurus/pull/9931) feat(core): add new plugin allContentLoaded lifecycle ([@slorber](https://github.com/slorber))
- `docusaurus-theme-translations`
- [#9928](https://github.com/facebook/docusaurus/pull/9928) feat(theme-translations) Icelandic (is) ([@Hallinn](https://github.com/Hallinn))
- `docusaurus-plugin-content-blog`
- [#9886](https://github.com/facebook/docusaurus/pull/9886) feat(blog): allow processing blog posts through a processBlogPosts function ([@OzakIOne](https://github.com/OzakIOne))
- [#9838](https://github.com/facebook/docusaurus/pull/9838) feat(blog): add blog pageBasePath plugin option ([@ilg-ul](https://github.com/ilg-ul))
- `docusaurus`
- [#9681](https://github.com/facebook/docusaurus/pull/9681) feat(swizzle): ask user preferred language if no language CLI option provided ([@yixiaojiu](https://github.com/yixiaojiu))
- `create-docusaurus`, `docusaurus-utils`
- [#9442](https://github.com/facebook/docusaurus/pull/9442) feat(create-docusaurus): ask user for preferred language when no language CLI option provided ([@Rafael-Martins](https://github.com/Rafael-Martins))
- `docusaurus-plugin-vercel-analytics`
- [#9687](https://github.com/facebook/docusaurus/pull/9687) feat(plugin-vercel-analytics): add new vercel analytics plugin ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-mdx-loader`
- [#9684](https://github.com/facebook/docusaurus/pull/9684) feat(mdx-loader): the table-of-contents should display toc/headings of imported MDX partials ([@anatolykopyl](https://github.com/anatolykopyl))
#### :bug: Bug Fix
- `docusaurus-mdx-loader`
- [#9999](https://github.com/facebook/docusaurus/pull/9999) fix(mdx-loader): Ignore contentTitle coming after Markdown thematicBreak ([@slorber](https://github.com/slorber))
- `docusaurus-theme-search-algolia`
- [#9945](https://github.com/facebook/docusaurus/pull/9945) fix(a11y): move focus algolia-search focus back to search input on Escape ([@mxschmitt](https://github.com/mxschmitt))
- `docusaurus-plugin-content-blog`
- [#9920](https://github.com/facebook/docusaurus/pull/9920) fix(blog): apply trailing slash to blog feed ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-theme-classic`
- [#9944](https://github.com/facebook/docusaurus/pull/9944) fix(theme): improve a11y of DocSidebarItemCategory expand/collapsed button ([@mxschmitt](https://github.com/mxschmitt))
- `docusaurus-theme-translations`
- [#9915](https://github.com/facebook/docusaurus/pull/9915) fix(theme-translations): complete and modify Japanese translations ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
- [#9910](https://github.com/facebook/docusaurus/pull/9910) fix(theme-translations): add Japanese translations ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
- [#9872](https://github.com/facebook/docusaurus/pull/9872) fix(theme-translations): complete and improve Spanish theme translations ([@4troDev](https://github.com/4troDev))
- [#9812](https://github.com/facebook/docusaurus/pull/9812) fix(i18n): add missing theme translations for fa locale ([@VahidNaderi](https://github.com/VahidNaderi))
- `docusaurus-utils`
- [#9897](https://github.com/facebook/docusaurus/pull/9897) fix(mdx-loader): mdx-code-block should support CRLF ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#9878](https://github.com/facebook/docusaurus/pull/9878) fix(core): fix default i18n calendar used, infer it from locale if possible ([@slorber](https://github.com/slorber))
- [#9852](https://github.com/facebook/docusaurus/pull/9852) fix(core): ensure core error boundary is able to render theme layout ([@slorber](https://github.com/slorber))
- `docusaurus-remark-plugin-npm2yarn`
- [#9861](https://github.com/facebook/docusaurus/pull/9861) fix(remark-npm2yarn): update npm-to-yarn from 2.0.0 to 2.2.1, fix pnpm extra args syntax ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-theme-classic`, `docusaurus-theme-translations`
- [#9851](https://github.com/facebook/docusaurus/pull/9851) fix(theme-classic): should use plurals for category items description ([@baradusov](https://github.com/baradusov))
#### :running_woman: Performance
- `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#9975](https://github.com/facebook/docusaurus/pull/9975) refactor(core): improve dev perf, fine-grained site reloads - part 3 ([@slorber](https://github.com/slorber))
- `docusaurus-types`, `docusaurus`
- [#9968](https://github.com/facebook/docusaurus/pull/9968) refactor(core): improve dev perf, fine-grained site reloads - part2 ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-types`, `docusaurus`
- [#9903](https://github.com/facebook/docusaurus/pull/9903) refactor(core): improve dev perf, fine-grained site reloads - part1 ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-utils`
- [#9890](https://github.com/facebook/docusaurus/pull/9890) perf: optimize getFileCommitDate, make it async ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#9798](https://github.com/facebook/docusaurus/pull/9798) refactor(core): internalize, simplify and optimize the SSG logic ([@slorber](https://github.com/slorber))
#### :nail_care: Polish
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#9868](https://github.com/facebook/docusaurus/pull/9868) refactor(theme): dates should be formatted on the client-side instead of in nodejs code ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-plugin-content-blog`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-types`
- [#9669](https://github.com/facebook/docusaurus/pull/9669) refactor(theme): use JSON-LD instead of microdata for blog structured data ([@johnnyreilly](https://github.com/johnnyreilly))
- `docusaurus-plugin-content-docs`
- [#9839](https://github.com/facebook/docusaurus/pull/9839) refactor(blog): improve doc global data hook error message + add doc warning to blogOnly mode ([@OzakIOne](https://github.com/OzakIOne))
#### :memo: Documentation
- [#9937](https://github.com/facebook/docusaurus/pull/9937) docs: use official GitHub Action to deploy to GitHub Pages ([@vlad-nestorov](https://github.com/vlad-nestorov))
- [#9971](https://github.com/facebook/docusaurus/pull/9971) docs: replace VuePress by VitePress on tool comparison section ([@sunkanmii](https://github.com/sunkanmii))
- [#9914](https://github.com/facebook/docusaurus/pull/9914) docs: update legacy MDX v1 links to markdown links ([@OzakIOne](https://github.com/OzakIOne))
- [#9913](https://github.com/facebook/docusaurus/pull/9913) docs: update legacy MDX v1 links to markdown links ([@OzakIOne](https://github.com/OzakIOne))
- [#9906](https://github.com/facebook/docusaurus/pull/9906) docs: emphasize "index slug" convention ([@Josh-Cena](https://github.com/Josh-Cena))
- [#9877](https://github.com/facebook/docusaurus/pull/9877) docs: fix typos in deployment.mdx ([@Oreoxmt](https://github.com/Oreoxmt))
- [#9845](https://github.com/facebook/docusaurus/pull/9845) docs: typo ([@OzakIOne](https://github.com/OzakIOne))
- [#9816](https://github.com/facebook/docusaurus/pull/9816) docs: Add docs for Mermaid Component ([@Its-Just-Nans](https://github.com/Its-Just-Nans))
#### :robot: Dependencies
- [#9981](https://github.com/facebook/docusaurus/pull/9981) chore(deps): bump actions/dependency-review-action from 4.1.3 to 4.2.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9982](https://github.com/facebook/docusaurus/pull/9982) chore(deps): bump katex from 0.16.8 to 0.16.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9983](https://github.com/facebook/docusaurus/pull/9983) chore(deps): bump express from 4.18.2 to 4.19.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9977](https://github.com/facebook/docusaurus/pull/9977) chore(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9958](https://github.com/facebook/docusaurus/pull/9958) chore(deps): bump follow-redirects from 1.15.4 to 1.15.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9892](https://github.com/facebook/docusaurus/pull/9892) chore(deps): bump actions/dependency-review-action from 4.1.2 to 4.1.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9869](https://github.com/facebook/docusaurus/pull/9869) chore(deps): bump actions/dependency-review-action from 4.0.0 to 4.1.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9874](https://github.com/facebook/docusaurus/pull/9874) chore(deps): bump ip from 2.0.0 to 2.0.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9843](https://github.com/facebook/docusaurus/pull/9843) chore(deps): bump actions/setup-node from 4.0.1 to 4.0.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9824](https://github.com/facebook/docusaurus/pull/9824) chore(deps): bump treosh/lighthouse-ci-action from 10.1.0 to 11.4.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
- [#9823](https://github.com/facebook/docusaurus/pull/9823) chore(deps): bump marocchino/sticky-pull-request-comment from 2.8.0 to 2.9.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### :wrench: Maintenance
- `docusaurus-plugin-client-redirects`, `docusaurus-plugin-content-docs`, `docusaurus-utils-common`, `docusaurus-utils-validation`, `docusaurus-utils`, `docusaurus`
- [#9972](https://github.com/facebook/docusaurus/pull/9972) refactor(utils): remove duplicated function ([@OzakIOne](https://github.com/OzakIOne))
- Other
- [#9965](https://github.com/facebook/docusaurus/pull/9965) refactor(website): organise blog posts by year ([@GingerGeek](https://github.com/GingerGeek))
- [#9865](https://github.com/facebook/docusaurus/pull/9865) chore(website): update @crowdin/crowdin-api-client ([@chris-bateman](https://github.com/chris-bateman))
- `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils`
- [#9963](https://github.com/facebook/docusaurus/pull/9963) refactor(docs,blog): last update timestamp should be in milliseconds instead of seconds ([@slorber](https://github.com/slorber))
#### Committers: 22
- Aolin ([@Oreoxmt](https://github.com/Oreoxmt))
- Anatoly Kopyl ([@anatolykopyl](https://github.com/anatolykopyl))
- Chris Bateman ([@chris-bateman](https://github.com/chris-bateman))
- Fafowora Sunkanmi ([@sunkanmii](https://github.com/sunkanmii))
- Hallbjörn Magnússon ([@Hallinn](https://github.com/Hallinn))
- John Reilly ([@johnnyreilly](https://github.com/johnnyreilly))
- Joshua Chen ([@Josh-Cena](https://github.com/Josh-Cena))
- Josue [4tro] A ([@4troDev](https://github.com/4troDev))
- Liviu Ionescu ([@ilg-ul](https://github.com/ilg-ul))
- Max Schmitt ([@mxschmitt](https://github.com/mxschmitt))
- Rafael Martins ([@Rafael-Martins](https://github.com/Rafael-Martins))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- Vahid Naderi ([@VahidNaderi](https://github.com/VahidNaderi))
- Vlad Nestorov ([@vlad-nestorov](https://github.com/vlad-nestorov))
- Zed Spencer-Milnes ([@GingerGeek](https://github.com/GingerGeek))
- axel7083 ([@axel7083](https://github.com/axel7083))
- krinza.eth ([@kaymomin](https://github.com/kaymomin))
- n4n5 ([@Its-Just-Nans](https://github.com/Its-Just-Nans))
- ozaki ([@OzakIOne](https://github.com/OzakIOne))
- suenryu ([@Suenaga-Ryuya](https://github.com/Suenaga-Ryuya))
- Нуриль Барадусов ([@baradusov](https://github.com/baradusov))
- 翊小久 ([@yixiaojiu](https://github.com/yixiaojiu))
## 3.1.1 (2024-01-26)
#### :bug: Bug Fix
- `docusaurus-types`, `docusaurus`
- [#9791](https://github.com/facebook/docusaurus/pull/9791) fix(core): broken links optimization behaves differently than non-optimized logic ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#9788](https://github.com/facebook/docusaurus/pull/9788) fix(core): links with target "\_blank" should no be checked by the broken link checker ([@slorber](https://github.com/slorber))
- [#9407](https://github.com/facebook/docusaurus/pull/9407) fix(core): conditionally include `hostname` parameter when using… ([@jack-robson](https://github.com/jack-robson))
- `docusaurus-utils`
- [#9776](https://github.com/facebook/docusaurus/pull/9776) fix(mdx-loader): allow spaces before `mdx-code-block` info string ([@eitsupi](https://github.com/eitsupi))
- `create-docusaurus`
- [#9783](https://github.com/facebook/docusaurus/pull/9783) fix(create-docusaurus): fix typo in init template sample docs ([@dawei-wang](https://github.com/dawei-wang))
- `docusaurus-theme-common`
- [#9727](https://github.com/facebook/docusaurus/pull/9727) fix(theme-common): fix missing code block MagicComments style in Visual Basic (.NET) 16 ([@tats-u](https://github.com/tats-u))
- `docusaurus-theme-classic`, `docusaurus-theme-mermaid`
- [#9733](https://github.com/facebook/docusaurus/pull/9733) fix: remove old useless mdx typedefs ([@slorber](https://github.com/slorber))
- `docusaurus-module-type-aliases`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils`, `docusaurus`
- [#9732](https://github.com/facebook/docusaurus/pull/9732) fix(core): various broken anchor link fixes ([@slorber](https://github.com/slorber))
#### :running_woman: Performance
- `docusaurus`
- [#9778](https://github.com/facebook/docusaurus/pull/9778) perf(core): optimize broken links checker ([@slorber](https://github.com/slorber))
#### :nail_care: Polish
- `docusaurus-theme-classic`
- [#9470](https://github.com/facebook/docusaurus/pull/9470) polish(theme): MDX images should use async decoding ([@sanjaiyan-dev](https://github.com/sanjaiyan-dev))
#### Committers: 6
- Jack Robson ([@jack-robson](https://github.com/jack-robson))
- Sanjaiyan Parthipan ([@sanjaiyan-dev](https://github.com/sanjaiyan-dev))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- Tatsunori Uchino ([@tats-u](https://github.com/tats-u))
- [@dawei-wang](https://github.com/dawei-wang)
- [@eitsupi](https://github.com/eitsupi)
## 3.1.0 (2024-01-05)
#### :rocket: New Feature
- `docusaurus-mdx-loader`, `docusaurus-module-type-aliases`, `docusaurus-theme-classic`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#9528](https://github.com/facebook/docusaurus/pull/9528) feat(core): make broken link checker detect broken anchors - add `onBrokenAnchors` config ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-mdx-loader`, `docusaurus-types`, `docusaurus`
- [#9674](https://github.com/facebook/docusaurus/pull/9674) feat(mdx-loader): add support for siteConfig.markdown.remarkRehypeOptions ([@slorber](https://github.com/slorber))
- `docusaurus-theme-common`
- [#9671](https://github.com/facebook/docusaurus/pull/9671) feat(theme-common): code block MagicComments support for (Visual) Basic/Batch/Fortran/COBOL/ML ([@tats-u](https://github.com/tats-u))
- `docusaurus-mdx-loader`, `docusaurus-plugin-content-blog`, `docusaurus-plugin-content-docs`, `docusaurus-plugin-content-pages`, `docusaurus-types`, `docusaurus-utils`, `docusaurus`
- [#9624](https://github.com/facebook/docusaurus/pull/9624) feat: siteConfig.markdown.parseFrontMatter hook ([@slorber](https://github.com/slorber))
- `docusaurus-utils`
- [#9610](https://github.com/facebook/docusaurus/pull/9610) feat(core): enable port configuration via environment variable ([@OzakIOne](https://github.com/OzakIOne))
#### :bug: Bug Fix
- `docusaurus-theme-classic`, `docusaurus-theme-live-codeblock`
- [#9704](https://github.com/facebook/docusaurus/pull/9704) fix(theme): allow empty code blocks and live playgrounds ([@slorber](https://github.com/slorber))
- `create-docusaurus`
- [#9696](https://github.com/facebook/docusaurus/pull/9696) fix(create-docusaurus): fix init template code blocks, and little improvements ([@slorber](https://github.com/slorber))
- `docusaurus-plugin-pwa`
- [#9668](https://github.com/facebook/docusaurus/pull/9668) fix(pwa-plugin): upgrade workbox ([@SimenB](https://github.com/SimenB))
- `docusaurus`
- [#9648](https://github.com/facebook/docusaurus/pull/9648) fix(cli): output help when no conventional config + no subcommand ([@Josh-Cena](https://github.com/Josh-Cena))
- `docusaurus-theme-live-codeblock`
- [#9631](https://github.com/facebook/docusaurus/pull/9631) fix(live-codeblock): stabilize react-live transformCode callback, fix editor/preview desync ([@slorber](https://github.com/slorber))
- `docusaurus-utils`
- [#9617](https://github.com/facebook/docusaurus/pull/9617) fix(utils): Markdown link replacement with <> but no spaces ([@Josh-Cena](https://github.com/Josh-Cena))
- `docusaurus-module-type-aliases`
- [#9612](https://github.com/facebook/docusaurus/pull/9612) fix(type-aliases): add `title` prop for imported inline SVG React components ([@axmmisaka](https://github.com/axmmisaka))
- `docusaurus-plugin-content-blog`
- [#9581](https://github.com/facebook/docusaurus/pull/9581) fix(content-blog): add baseUrl for author.image_url ([@OzakIOne](https://github.com/OzakIOne))
- `docusaurus-theme-translations`
- [#9477](https://github.com/facebook/docusaurus/pull/9477) fix(i18n): complete translations for theme-common.json Brazilian Portuguese (pt-BR) ([@c0h1b4](https://github.com/c0h1b4))
#### :nail_care: Polish
- `docusaurus-theme-common`
- [#9335](https://github.com/facebook/docusaurus/pull/9335) refactor(theme-common): allow optional desktopBreakpoint param in useWindowSize ([@jgarrow](https://github.com/jgarrow))
#### :wrench: Maintenance
- `docusaurus-theme-search-algolia`
- [#9604](https://github.com/facebook/docusaurus/pull/9604) chore: add lint autofix CI job ([@slorber](https://github.com/slorber))
#### Committers: 8
- Janessa Garrow ([@jgarrow](https://github.com/jgarrow))
- Joshua Chen ([@Josh-Cena](https://github.com/Josh-Cena))
- Simen Bekkhus ([@SimenB](https://github.com/SimenB))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- Tatsunori Uchino ([@tats-u](https://github.com/tats-u))
- [@c0h1b4](https://github.com/c0h1b4)
- axmmisaka ([@axmmisaka](https://github.com/axmmisaka))
- ozaki ([@OzakIOne](https://github.com/OzakIOne))
## 3.0.1 (2023-11-30)
#### :bug: Bug Fix
- `docusaurus-utils`
- [#9570](https://github.com/facebook/docusaurus/pull/9570) fix: add v2 retrocompatible support for quoted admonitions ([@slorber](https://github.com/slorber))
- [#9535](https://github.com/facebook/docusaurus/pull/9535) fix: v3 admonitions should support v2 title syntax for nested admonitions ([@slorber](https://github.com/slorber))
- `create-docusaurus`, `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#9567](https://github.com/facebook/docusaurus/pull/9567) fix(theme): upgrade prism-react-renderer, fix html script and style tag highlighting ([@slorber](https://github.com/slorber))
- `docusaurus-theme-common`
- [#9531](https://github.com/facebook/docusaurus/pull/9531) fix(theme): docs html sidebar items should always be visible ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`
- [#9530](https://github.com/facebook/docusaurus/pull/9530) fix(theme): fix firefox CSS :has() support bug ([@slorber](https://github.com/slorber))
- `create-docusaurus`
- [#9487](https://github.com/facebook/docusaurus/pull/9487) fix(create-docusaurus): fix readme docusaurus 2 ref ([@slorber](https://github.com/slorber))
#### :robot: Dependencies
- `docusaurus-plugin-debug`
- [#9566](https://github.com/facebook/docusaurus/pull/9566) chore(debug-plugin): migrate to a new maintained JSON Viewer ([@mcrstudio](https://github.com/mcrstudio))
- `create-docusaurus`, `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#9572](https://github.com/facebook/docusaurus/pull/9572) chore: upgrade prism-react-renderer to 2.3.0 to avoid older clsx ([@harryzcy](https://github.com/harryzcy))
- [#9567](https://github.com/facebook/docusaurus/pull/9567) fix(theme): upgrade prism-react-renderer, fix html script and style tag highlighting ([@slorber](https://github.com/slorber))
- `create-docusaurus`, `docusaurus-plugin-pwa`, `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-theme-live-codeblock`, `docusaurus-theme-search-algolia`
- [#9464](https://github.com/facebook/docusaurus/pull/9464) chore: Upgrade clsx to 2.0.0 ([@harryzcy](https://github.com/harryzcy))
- `docusaurus`
- [#9547](https://github.com/facebook/docusaurus/pull/9547) chore(core): replace `wait-on` dependency with custom lighter code ([@NickGerleman](https://github.com/NickGerleman))
- `docusaurus-plugin-pwa`, `docusaurus`
- [#9529](https://github.com/facebook/docusaurus/pull/9529) chore: ugrade babel dependencies to v7.23.3 ([@reece-white](https://github.com/reece-white))
#### Committers: 6
- Chongyi Zheng ([@harryzcy](https://github.com/harryzcy))
- MCR Studio ([@mcrstudio](https://github.com/mcrstudio))
- Nick Gerleman ([@NickGerleman](https://github.com/NickGerleman))
- Shreesh Nautiyal ([@Shreesh09](https://github.com/Shreesh09))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- [@reece-white](https://github.com/reece-white)
## 3.0.0 (2023-10-31)
#### :boom: Breaking Change

View File

@ -1,13 +1,7 @@
<div align="center">
<h1 align="center">
Docusaurus
<br />
<br />
<a href="https://docusaurus.io">
<img src="https://docusaurus.io/img/slash-introducing.svg" alt="Docusaurus">
</a>
</h1>
</div>
<h1 align="center">
<p align="center">Docusaurus</p>
<a href="https://docusaurus.io"><img src="https://docusaurus.io/img/slash-introducing.svg" alt="Docusaurus"></a>
</h1>
<p align="center">
<a href="https://twitter.com/docusaurus"><img src="https://img.shields.io/twitter/follow/docusaurus.svg?style=social" align="right" alt="Twitter Follow" /></a>
@ -20,13 +14,17 @@
<a href= "https://github.com/prettier/prettier"><img alt="code style: prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg"></a>
<a href="#license"><img src="https://img.shields.io/github/license/sourcerer-io/hall-of-fame.svg?colorB=ff0000"></a>
<a href="https://github.com/facebook/jest"><img src="https://img.shields.io/badge/tested_with-jest-99424f.svg" alt="Tested with Jest"></a>
<a href="https://argos-ci.com" target="_blank" rel="noreferrer noopener" aria-label="Covered by Argos"><img src="https://argos-ci.com/badge.svg" alt="Covered by Argos" width="133" height="20" /></a>
<a href="https://gitpod.io/#https://github.com/facebook/docusaurus"><img src="https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod" alt="Gitpod Ready-to-Code"/></a>
<a href="https://app.netlify.com/sites/docusaurus-2/deploys"><img src="https://api.netlify.com/api/v1/badges/9e1ff559-4405-4ebe-8718-5e21c0774bc8/deploy-status" alt="Netlify Status"></a>
<a href="https://meercode.io/facebook/docusaurus"><img src="https://meercode.io/badge/facebook/docusaurus?type=ci-score" alt="CI Score"></a>
<a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Ffacebook%2Fdocusaurus%2Ftree%2Fmain%2Fexamples%2Fclassic&project-name=my-docusaurus-site&repo-name=my-docusaurus-site"><img src="https://vercel.com/button" alt="Deploy with Vercel"/></a>
<a href="https://app.netlify.com/start/deploy?repository=https://github.com/slorber/docusaurus-starter"><img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify"></a>
</p>
> **We are working hard on Docusaurus v2. If you are new to Docusaurus, try using the new version instead of v1. See the [Docusaurus v2 website](https://docusaurus.io/) for more details.**
> Docusaurus v1 doc is available at [v1.docusaurus.io](https://v1.docusaurus.io) and code is available on branch [docusaurus-v1](https://github.com/facebook/docusaurus/tree/docusaurus-v1)
## Introduction
Docusaurus is a project for building, deploying, and maintaining open source project websites easily.

View File

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

View File

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

View File

@ -25,8 +25,7 @@ async function generateTemplateExample(template) {
// Run the docusaurus script to create the template in the examples folder
const command = template.endsWith('-typescript')
? template.replace('-typescript', ' -- --typescript')
: `${template} -- --javascript`;
: template;
shell.exec(
// We use the published init script on purpose, because the local init is
// too new and could generate upcoming/unavailable config options.

View File

@ -52,7 +52,7 @@ git diff --name-only -- '*.json' | sed 's, ,\\&,g' | xargs git checkout --
cd ..
# Build skeleton website with new version
npm_config_registry="$CUSTOM_REGISTRY_URL" npx create-docusaurus@"$NEW_VERSION" test-website classic --javascript $EXTRA_OPTS
npm_config_registry="$CUSTOM_REGISTRY_URL" npx create-docusaurus@"$NEW_VERSION" test-website classic $EXTRA_OPTS
# Stop Docker container
if [[ -z "${KEEP_CONTAINER:-true}" ]] && ( $(docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1) ); then

View File

@ -1,6 +1,6 @@
{
"name": "argos",
"version": "3.2.0",
"version": "3.0.1",
"description": "Argos visual diff tests",
"license": "MIT",
"private": true,
@ -10,8 +10,8 @@
"report": "playwright show-report"
},
"dependencies": {
"@argos-ci/playwright": "^1.9.3",
"@playwright/test": "^1.41.2",
"@argos-ci/playwright": "^1.0.1",
"@playwright/test": "^1.36.1",
"cheerio": "^1.0.0-rc.12"
}
}

View File

@ -28,7 +28,7 @@ tags: [greetings]
Congratulations, you have made your first post!
Feel free to play around and edit this post as much as you like.
Feel free to play around and edit this post as much you like.
```
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).

View File

@ -61,13 +61,13 @@ You can reference images relative to the current file as well. This is particula
Markdown code blocks are supported with Syntax highlighting.
````md
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
return <h1>Hello, Docusaurus!</h1>;
}
```
````
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
return (
<h1>Hello, Docusaurus!</h1>
)
}
```
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
@ -79,19 +79,17 @@ function HelloDocusaurus() {
Docusaurus has a special syntax to create admonitions and callouts:
```md
:::tip My tip
:::tip My tip
Use this awesome feature option
Use this awesome feature option
:::
:::
:::danger Take care
:::danger Take care
This action is dangerous
This action is dangerous
:::
```
:::
:::tip My tip

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ tags: [greetings]
Congratulations, you have made your first post!
Feel free to play around and edit this post as much as you like.
Feel free to play around and edit this post as much you like.
```
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).

View File

@ -61,13 +61,13 @@ You can reference images relative to the current file as well. This is particula
Markdown code blocks are supported with Syntax highlighting.
````md
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
return <h1>Hello, Docusaurus!</h1>;
}
```
````
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
return (
<h1>Hello, Docusaurus!</h1>
)
}
```
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
@ -79,19 +79,17 @@ function HelloDocusaurus() {
Docusaurus has a special syntax to create admonitions and callouts:
```md
:::tip My tip
:::tip My tip
Use this awesome feature option
Use this awesome feature option
:::
:::
:::danger Take care
:::danger Take care
This action is dangerous
This action is dangerous
:::
```
:::
:::tip My tip

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -51,11 +51,8 @@
"lint": "yarn lint:js && yarn lint:style && yarn lint:spelling",
"lint:ci": "yarn lint:js --quiet && yarn lint:style && yarn lint:spelling",
"lint:js": "eslint --cache --report-unused-disable-directives \"**/*.{js,jsx,ts,tsx,mjs}\"",
"lint:js:fix": "yarn lint:js --fix",
"lint:spelling": "cspell \"**\" --no-progress --show-context --show-suggestions",
"lint:spelling:fix": "yarn rimraf project-words.txt && echo \"# Project Words - DO NOT TOUCH - This is updated through CI\" >> project-words.txt && yarn -s lint:spelling --words-only --unique --no-exit-code --no-summary \"**\" | sort --ignore-case >> project-words.txt",
"lint:spelling": "cspell \"**\" --no-progress",
"lint:style": "stylelint \"**/*.css\"",
"lint:style:fix": "yarn lint:style --fix",
"lerna": "lerna",
"test": "jest",
"test:build:website": "./admin/scripts/test-release.sh",
@ -83,7 +80,7 @@
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"cross-env": "^7.0.3",
"cspell": "^8.1.0",
"cspell": "^6.31.2",
"eslint": "^8.45.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.8.0",

View File

@ -10,10 +10,6 @@ npm init docusaurus
yarn create docusaurus
```
```bash
npx create-docusaurus@latest
```
## Usage
Please see the [installation documentation](https://docusaurus.io/docs/installation).
@ -25,7 +21,7 @@ For Docusaurus maintainers, templates can be tested with:
```bash
cd `git rev-parse --show-toplevel` # Back to repo root
rm -rf test-website
yarn create-docusaurus test-website classic --javascript
yarn create-docusaurus test-website classic
cd test-website
yarn start
```
@ -37,7 +33,7 @@ Use the following to test the templates against local packages:
```bash
cd `git rev-parse --show-toplevel` # Back to repo root
rm -rf test-website-in-workspace
yarn create-docusaurus test-website-in-workspace classic --javascript
yarn create-docusaurus test-website-in-workspace classic
cd test-website-in-workspace
yarn build
yarn start

View File

@ -38,7 +38,6 @@ program
'Do not run package manager immediately after scaffolding',
)
.option('-t, --typescript', 'Use the TypeScript template variant')
.option('-j, --javascript', 'Use the JavaScript template variant')
.option(
'-g, --git-strategy <strategy>',
`Only used if the template is a git repository.

View File

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

View File

@ -13,29 +13,15 @@ import logger from '@docusaurus/logger';
import shell from 'shelljs';
import prompts, {type Choice} from 'prompts';
import supportsColor from 'supports-color';
import {escapeShellArg, askPreferredLanguage} from '@docusaurus/utils';
import {escapeShellArg} from '@docusaurus/utils';
type LanguagesOptions = {
javascript?: boolean;
typescript?: boolean;
};
type CLIOptions = LanguagesOptions & {
type CLIOptions = {
packageManager?: PackageManager;
skipInstall?: boolean;
typescript?: boolean;
gitStrategy?: GitStrategy;
};
async function getLanguage(options: LanguagesOptions) {
if (options.typescript) {
return 'typescript';
}
if (options.javascript) {
return 'javascript';
}
return askPreferredLanguage();
}
// Only used in the rare, rare case of running globally installed create +
// using --skip-install. We need a default name to show the tip text
const defaultPackageManager = 'npm';
@ -167,14 +153,11 @@ async function readTemplates(): Promise<Template[]> {
async function copyTemplate(
template: Template,
dest: string,
language: 'javascript' | 'typescript',
typescript: boolean,
): Promise<void> {
await fs.copy(path.join(templatesDir, 'shared'), dest);
const sourcePath =
language === 'typescript' ? template.tsVariantPath! : template.path;
await fs.copy(sourcePath, dest, {
await fs.copy(typescript ? template.tsVariantPath! : template.path, dest, {
// Symlinks don't exist in published npm packages anymore, so this is only
// to prevent errors during local testing
filter: async (filePath) => !(await fs.lstat(filePath)).isSymbolicLink(),
@ -200,33 +183,6 @@ function createTemplateChoices(templates: Template[]): Choice[] {
];
}
async function askTemplateChoice({
templates,
cliOptions,
}: {
templates: Template[];
cliOptions: CLIOptions;
}) {
return cliOptions.gitStrategy
? 'Git repository'
: (
(await prompts(
{
type: 'select',
name: 'template',
message: 'Select a template below...',
choices: createTemplateChoices(templates),
},
{
onCancel() {
logger.error('A choice is required.');
process.exit(1);
},
},
)) as {template: Template | 'Git repository' | 'Local template'}
).template;
}
function isValidGitRepoUrl(gitRepoUrl: string): boolean {
return ['https://', 'git@'].some((item) => gitRepoUrl.startsWith(item));
}
@ -304,7 +260,7 @@ type Source =
| {
type: 'template';
template: Template;
language: 'javascript' | 'typescript';
typescript: boolean;
}
| {
type: 'git';
@ -316,193 +272,166 @@ type Source =
path: string;
};
async function createTemplateSource({
template,
cliOptions,
}: {
template: Template;
cliOptions: CLIOptions;
}): Promise<Source> {
const language = await getLanguage(cliOptions);
if (language === 'typescript' && !template.tsVariantPath) {
logger.error`Template name=${template.name} doesn't provide a TypeScript variant.`;
process.exit(1);
}
return {
type: 'template',
template,
language,
};
}
async function getTemplateSource({
templateName,
templates,
cliOptions,
}: {
templateName: string;
templates: Template[];
cliOptions: CLIOptions;
}): Promise<Source> {
const template = templates.find((t) => t.name === templateName);
if (!template) {
logger.error('Invalid template.');
process.exit(1);
}
return createTemplateSource({template, cliOptions});
}
// Get the template source explicitly requested by the user provided cli option
async function getUserProvidedSource({
reqTemplate,
templates,
cliOptions,
}: {
reqTemplate: string;
templates: Template[];
cliOptions: CLIOptions;
}): Promise<Source> {
if (isValidGitRepoUrl(reqTemplate)) {
if (
cliOptions.gitStrategy &&
!gitStrategies.includes(cliOptions.gitStrategy)
) {
logger.error`Invalid git strategy: name=${
cliOptions.gitStrategy
}. Value must be one of ${gitStrategies.join(', ')}.`;
process.exit(1);
}
return {
type: 'git',
url: reqTemplate,
strategy: cliOptions.gitStrategy ?? 'deep',
};
}
if (await fs.pathExists(path.resolve(reqTemplate))) {
return {
type: 'local',
path: path.resolve(reqTemplate),
};
}
return getTemplateSource({
templateName: reqTemplate,
templates,
cliOptions,
});
}
async function askGitRepositorySource({
cliOptions,
}: {
cliOptions: CLIOptions;
}): Promise<Source> {
const {gitRepoUrl} = (await prompts(
{
type: 'text',
name: 'gitRepoUrl',
validate: (url?: string) => {
if (url && isValidGitRepoUrl(url)) {
return true;
}
return logger.red('Invalid repository URL');
},
message: logger.interpolate`Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo.
(e.g: url=${'https://github.com/ownerName/repoName.git'})`,
},
{
onCancel() {
logger.error('A git repo URL is required.');
process.exit(1);
},
},
)) as {gitRepoUrl: string};
let strategy = cliOptions.gitStrategy;
if (!strategy) {
({strategy} = (await prompts(
{
type: 'select',
name: 'strategy',
message: 'How should we clone this repo?',
choices: [
{title: 'Deep clone: preserve full history', value: 'deep'},
{title: 'Shallow clone: clone with --depth=1', value: 'shallow'},
{
title: 'Copy: do a shallow clone, but do not create a git repo',
value: 'copy',
},
{
title: 'Custom: enter your custom git clone command',
value: 'custom',
},
],
},
{
onCancel() {
logger.info`Falling back to name=${'deep'}`;
},
},
)) as {strategy?: GitStrategy});
}
return {
type: 'git',
url: gitRepoUrl,
strategy: strategy ?? 'deep',
};
}
async function askLocalSource(): Promise<Source> {
const {templateDir} = (await prompts(
{
type: 'text',
name: 'templateDir',
validate: async (dir?: string) => {
if (dir) {
const fullDir = path.resolve(dir);
if (await fs.pathExists(fullDir)) {
return true;
}
return logger.red(
logger.interpolate`path=${fullDir} does not exist.`,
);
}
return logger.red('Please enter a valid path.');
},
message:
'Enter a local folder path, relative to the current working directory.',
},
{
onCancel() {
logger.error('A file path is required.');
process.exit(1);
},
},
)) as {templateDir: string};
return {
type: 'local',
path: templateDir,
};
}
async function getSource(
reqTemplate: string | undefined,
templates: Template[],
cliOptions: CLIOptions,
): Promise<Source> {
if (reqTemplate) {
return getUserProvidedSource({reqTemplate, templates, cliOptions});
if (isValidGitRepoUrl(reqTemplate)) {
if (
cliOptions.gitStrategy &&
!gitStrategies.includes(cliOptions.gitStrategy)
) {
logger.error`Invalid git strategy: name=${
cliOptions.gitStrategy
}. Value must be one of ${gitStrategies.join(', ')}.`;
process.exit(1);
}
return {
type: 'git',
url: reqTemplate,
strategy: cliOptions.gitStrategy ?? 'deep',
};
} else if (await fs.pathExists(path.resolve(reqTemplate))) {
return {
type: 'local',
path: path.resolve(reqTemplate),
};
}
const template = templates.find((t) => t.name === reqTemplate);
if (!template) {
logger.error('Invalid template.');
process.exit(1);
}
if (cliOptions.typescript && !template.tsVariantPath) {
logger.error`Template name=${reqTemplate} doesn't provide the TypeScript variant.`;
process.exit(1);
}
return {
type: 'template',
template,
typescript: cliOptions.typescript ?? false,
};
}
const template = await askTemplateChoice({templates, cliOptions});
const template = cliOptions.gitStrategy
? 'Git repository'
: (
(await prompts(
{
type: 'select',
name: 'template',
message: 'Select a template below...',
choices: createTemplateChoices(templates),
},
{
onCancel() {
logger.error('A choice is required.');
process.exit(1);
},
},
)) as {template: Template | 'Git repository' | 'Local template'}
).template;
if (template === 'Git repository') {
return askGitRepositorySource({cliOptions});
const {gitRepoUrl} = (await prompts(
{
type: 'text',
name: 'gitRepoUrl',
validate: (url?: string) => {
if (url && isValidGitRepoUrl(url)) {
return true;
}
return logger.red('Invalid repository URL');
},
message: logger.interpolate`Enter a repository URL from GitHub, Bitbucket, GitLab, or any other public repo.
(e.g: url=${'https://github.com/ownerName/repoName.git'})`,
},
{
onCancel() {
logger.error('A git repo URL is required.');
process.exit(1);
},
},
)) as {gitRepoUrl: string};
let strategy = cliOptions.gitStrategy;
if (!strategy) {
({strategy} = (await prompts(
{
type: 'select',
name: 'strategy',
message: 'How should we clone this repo?',
choices: [
{title: 'Deep clone: preserve full history', value: 'deep'},
{title: 'Shallow clone: clone with --depth=1', value: 'shallow'},
{
title: 'Copy: do a shallow clone, but do not create a git repo',
value: 'copy',
},
{
title: 'Custom: enter your custom git clone command',
value: 'custom',
},
],
},
{
onCancel() {
logger.info`Falling back to name=${'deep'}`;
},
},
)) as {strategy?: GitStrategy});
}
return {
type: 'git',
url: gitRepoUrl,
strategy: strategy ?? 'deep',
};
} else if (template === 'Local template') {
const {templateDir} = (await prompts(
{
type: 'text',
name: 'templateDir',
validate: async (dir?: string) => {
if (dir) {
const fullDir = path.resolve(dir);
if (await fs.pathExists(fullDir)) {
return true;
}
return logger.red(
logger.interpolate`path=${fullDir} does not exist.`,
);
}
return logger.red('Please enter a valid path.');
},
message:
'Enter a local folder path, relative to the current working directory.',
},
{
onCancel() {
logger.error('A file path is required.');
process.exit(1);
},
},
)) as {templateDir: string};
return {
type: 'local',
path: templateDir,
};
}
if (template === 'Local template') {
return askLocalSource();
let useTS = cliOptions.typescript;
if (!useTS && template.tsVariantPath) {
({useTS} = (await prompts({
type: 'confirm',
name: 'useTS',
message:
'This template is available in TypeScript. Do you want to use the TS variant?',
initial: false,
})) as {useTS?: boolean});
}
return createTemplateSource({
return {
type: 'template',
template,
cliOptions,
});
typescript: useTS ?? false,
};
}
async function updatePkg(pkgPath: string, obj: {[key: string]: unknown}) {
@ -523,7 +452,6 @@ export default async function init(
getSiteName(reqName, rootDir),
]);
const dest = path.resolve(rootDir, siteName);
const source = await getSource(reqTemplate, templates, cliOptions);
logger.info('Creating new Docusaurus project...');
@ -542,7 +470,7 @@ export default async function init(
}
} else if (source.type === 'template') {
try {
await copyTemplate(source.template, dest, source.language);
await copyTemplate(source.template, dest, source.typescript);
} catch (err) {
logger.error`Copying Docusaurus template name=${source.template.name} failed!`;
throw err;

View File

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

View File

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

View File

@ -28,7 +28,7 @@ tags: [greetings]
Congratulations, you have made your first post!
Feel free to play around and edit this post as much as you like.
Feel free to play around and edit this post as much you like.
```
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).

View File

@ -61,13 +61,13 @@ You can reference images relative to the current file as well. This is particula
Markdown code blocks are supported with Syntax highlighting.
````md
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
return <h1>Hello, Docusaurus!</h1>;
}
```
````
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
return (
<h1>Hello, Docusaurus!</h1>
)
}
```
```jsx title="src/components/HelloDocusaurus.js"
function HelloDocusaurus() {
@ -79,19 +79,17 @@ function HelloDocusaurus() {
Docusaurus has a special syntax to create admonitions and callouts:
```md
:::tip My tip
:::tip My tip
Use this awesome feature option
Use this awesome feature option
:::
:::
:::danger Take care
:::danger Take care
This action is dangerous
This action is dangerous
:::
```
:::
:::tip My tip

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
import {mdxLoader} from './loader';
import type {TOCItem as TOCItemImported} from './remark/toc/types';
import type {TOCItem as TOCItemImported} from './remark/toc';
export default mdxLoader;

View File

@ -8,7 +8,7 @@
import fs from 'fs-extra';
import logger from '@docusaurus/logger';
import {
DEFAULT_PARSE_FRONT_MATTER,
parseFrontMatter,
escapePath,
getFileLoaderUtils,
getWebpackLoaderCompilerName,
@ -133,7 +133,7 @@ function extractContentTitleData(data: {
export async function mdxLoader(
this: LoaderContext<Options>,
fileContent: string,
fileString: string,
): Promise<void> {
const compilerName = getWebpackLoaderCompilerName(this);
const callback = this.async();
@ -143,15 +143,11 @@ export async function mdxLoader(
ensureMarkdownConfig(reqOptions);
const {frontMatter} = await reqOptions.markdownConfig.parseFrontMatter({
filePath,
fileContent,
defaultParseFrontMatter: DEFAULT_PARSE_FRONT_MATTER,
});
const {frontMatter} = parseFrontMatter(fileString);
const mdxFrontMatter = validateMDXFrontMatter(frontMatter.mdx);
const preprocessedContent = preprocessor({
fileContent,
fileContent: fileString,
filePath,
admonitions: reqOptions.admonitions,
markdownConfig: reqOptions.markdownConfig,

View File

@ -165,7 +165,6 @@ async function createProcessorFactory() {
const mdxProcessor = createMdxProcessor({
...processorOptions,
remarkRehypeOptions: options.markdownConfig.remarkRehypeOptions,
format,
});

View File

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

View File

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

View File

@ -1,5 +0,0 @@
import React from 'react';
export default function SomeComponent() {
return <div>Some component</div>;
}

View File

@ -1,7 +0,0 @@
## Partial 1
Partial 1
### Partial 1 Sub Heading
Content

View File

@ -1,3 +0,0 @@
## Partial 2 Nested
Partial 2 Nested

View File

@ -1,11 +0,0 @@
## Partial 2
Partial 2
### Partial 2 Sub Heading
Content
import Partial2Nested from './partial2-nested.md';
<Partial2Nested />

View File

@ -1,7 +0,0 @@
## Partial 3
Partial 3
### Partial 3 Sub Heading
Content

View File

@ -1,49 +0,0 @@
import Partial1 from './_partial1.md';
import SomeComponent from './SomeComponent';
# Index
Some text
import Partial2 from './_partial2.md';
## Index section 1
Foo
<Partial1 />
Some text
<SomeComponent />
## Index section 2
<Partial2 />
## Unused partials
Unused partials (that are only imported but not rendered) shouldn't alter the TOC
import UnusedPartialImport from './_partial3.md';
## NonExisting Partials
Partials that do not exist should alter the TOC
It's not the responsibility of the Remark plugin to check for their existence
import DoesNotExist from './_doesNotExist.md';
<DoesNotExist />
## Duplicate partials
It's fine if we use partials at the end
<Partial1 />
And we can use the partial multiple times!
<Partial1 />

View File

@ -1,7 +0,0 @@
# Partial used before import
While it looks weird to import after usage, this remains valid MDX usage.
<Partial />
import Partial from './_partial.md';

View File

@ -1,601 +1,238 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`toc remark plugin does not overwrite TOC var if no TOC 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
"foo
\`bar\`
\`\`\`js
baz
\`\`\`
export const toc = 1;
function _createMdxContent(props) {
const _components = {
code: "code",
p: "p",
pre: "pre",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.p, {
children: "foo"
}), "/n", _jsx(_components.p, {
children: _jsx(_components.code, {
children: "bar"
})
}), "/n", _jsx(_components.pre, {
children: _jsx(_components.code, {
className: "language-js",
children: "baz/n"
})
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"
`;
exports[`toc remark plugin escapes inline code 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
export const toc = [{
"value": "<code>&lt;Head /&gt;</code>",
"id": "head-",
"level": 2
}, {
"value": "<code>&lt;Head&gt;Test&lt;/Head&gt;</code>",
"id": "headtesthead",
"level": 3
}, {
"value": "<code>&lt;div /&gt;</code>",
"id": "div-",
"level": 2
}, {
"value": "<code>&lt;div&gt; Test &lt;/div&gt;</code>",
"id": "div-test-div",
"level": 2
}, {
"value": "<code>&lt;div&gt;&lt;i&gt;Test&lt;/i&gt;&lt;/div&gt;</code>",
"id": "divitestidiv",
"level": 2
}, {
"value": "<code>&lt;div&gt;&lt;i&gt;Test&lt;/i&gt;&lt;/div&gt;</code>",
"id": "divitestidiv-1",
"level": 2
}];
function _createMdxContent(props) {
const _components = {
a: "a",
code: "code",
h2: "h2",
h3: "h3",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
id: "head-",
children: _jsx(_components.code, {
children: "<Head />"
})
}), "/n", _jsx(_components.h3, {
id: "headtesthead",
children: _jsx(_components.code, {
children: "<Head>Test</Head>"
})
}), "/n", _jsx(_components.h2, {
id: "div-",
children: _jsx(_components.code, {
children: "<div />"
})
}), "/n", _jsx(_components.h2, {
id: "div-test-div",
children: _jsx(_components.code, {
children: "<div> Test </div>"
})
}), "/n", _jsx(_components.h2, {
id: "divitestidiv",
children: _jsx(_components.code, {
children: "<div><i>Test</i></div>"
})
}), "/n", _jsx(_components.h2, {
id: "divitestidiv-1",
children: _jsx(_components.a, {
href: "/some/link",
children: _jsx(_components.code, {
children: "<div><i>Test</i></div>"
})
})
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"export const toc = [
{
value: '<code>&lt;Head /&gt;</code>',
id: 'head-',
level: 2
},
{
value: '<code>&lt;Head&gt;Test&lt;/Head&gt;</code>',
id: 'headtesthead',
level: 3
},
{
value: '<code>&lt;div /&gt;</code>',
id: 'div-',
level: 2
},
{
value: '<code>&lt;div&gt; Test &lt;/div&gt;</code>',
id: 'div-test-div',
level: 2
},
{
value: '<code>&lt;div&gt;&lt;i&gt;Test&lt;/i&gt;&lt;/div&gt;</code>',
id: 'divitestidiv',
level: 2
},
{
value: '<code>&lt;div&gt;&lt;i&gt;Test&lt;/i&gt;&lt;/div&gt;</code>',
id: 'divitestidiv-1',
level: 2
}
]
## \`<Head />\`
### \`<Head>Test</Head>\`
## \`<div />\`
## \`<div> Test </div>\`
## \`<div><i>Test</i></div>\`
## [\`<div><i>Test</i></div>\`](/some/link)
"
`;
exports[`toc remark plugin exports even with existing name 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
export const toc = ['replaceMe'];
function _createMdxContent(props) {
const _components = {
h2: "h2",
h3: "h3",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
id: "thanos",
children: "Thanos"
}), "/n", _jsx(_components.h2, {
id: "tony-stark",
children: "Tony Stark"
}), "/n", _jsx(_components.h3, {
id: "avengers",
children: "Avengers"
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"export const toc = [
{
value: 'Thanos',
id: 'thanos',
level: 2
},
{
value: 'Tony Stark',
id: 'tony-stark',
level: 2
},
{
value: 'Avengers',
id: 'avengers',
level: 3
}
]
## Thanos
## Tony Stark
### Avengers
"
`;
exports[`toc remark plugin handles empty headings 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
export const toc = [];
function _createMdxContent(props) {
const _components = {
h1: "h1",
h2: "h2",
img: "img",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
id: "ignore-this",
children: "Ignore this"
}), "/n", _jsx(_components.h2, {
id: ""
}), "/n", _jsx(_components.h2, {
id: "-1",
children: _jsx(_components.img, {
src: "an-image.svg",
alt: ""
})
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"export const toc = []
# Ignore this
##
## ![](an-image.svg)
"
`;
exports[`toc remark plugin inserts below imports 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import something from 'something';
"import something from 'something';
import somethingElse from 'something-else';
export const toc = [{
"value": "Title",
"id": "title",
"level": 2
}, {
"value": "Test",
"id": "test",
"level": 2
}, {
"value": "Again",
"id": "again",
"level": 3
}];
function _createMdxContent(props) {
const _components = {
h2: "h2",
h3: "h3",
p: "p",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
id: "title",
children: "Title"
}), "/n", _jsx(_components.h2, {
id: "test",
children: "Test"
}), "/n", _jsx(_components.h3, {
id: "again",
children: "Again"
}), "/n", _jsx(_components.p, {
children: "Content."
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
export const toc = [
{
value: 'Title',
id: 'title',
level: 2
},
{
value: 'Test',
id: 'test',
level: 2
},
{
value: 'Again',
id: 'again',
level: 3
}
]
## Title
## Test
### Again
Content.
"
`;
exports[`toc remark plugin outputs empty array for no TOC 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
export const toc = [];
function _createMdxContent(props) {
const _components = {
code: "code",
p: "p",
pre: "pre",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.p, {
children: "foo"
}), "/n", _jsx(_components.p, {
children: _jsx(_components.code, {
children: "bar"
})
}), "/n", _jsx(_components.pre, {
children: _jsx(_components.code, {
className: "language-js",
children: "baz/n"
})
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"export const toc = []
foo
\`bar\`
\`\`\`js
baz
\`\`\`
"
`;
exports[`toc remark plugin works on non text phrasing content 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
export const toc = [{
"value": "<em>Emphasis</em>",
"id": "emphasis",
"level": 2
}, {
"value": "<strong>Importance</strong>",
"id": "importance",
"level": 3
}, {
"value": "<del>Strikethrough</del>",
"id": "strikethrough",
"level": 2
}, {
"value": "<i>HTML</i>",
"id": "html",
"level": 2
}, {
"value": "<code>inline.code()</code>",
"id": "inlinecode",
"level": 2
}, {
"value": "some <span class=\\"some-class\\">styled</span> <strong>heading</strong> <span class=\\"myClassName &lt;&gt; weird char\\"></span> test",
"id": "some-styled-heading--test",
"level": 2
}];
function _createMdxContent(props) {
const _components = {
code: "code",
del: "del",
em: "em",
h2: "h2",
h3: "h3",
strong: "strong",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
id: "emphasis",
children: _jsx(_components.em, {
children: "Emphasis"
})
}), "/n", _jsx(_components.h3, {
id: "importance",
children: _jsx(_components.strong, {
children: "Importance"
})
}), "/n", _jsx(_components.h2, {
id: "strikethrough",
children: _jsx(_components.del, {
children: "Strikethrough"
})
}), "/n", _jsx(_components.h2, {
id: "html",
children: _jsx("i", {
children: "HTML"
})
}), "/n", _jsx(_components.h2, {
id: "inlinecode",
children: _jsx(_components.code, {
children: "inline.code()"
})
}), "/n", _jsxs(_components.h2, {
id: "some-styled-heading--test",
children: ["some ", _jsx("span", {
className: "some-class",
style: {
border: "solid"
},
children: "styled"
}), " ", _jsx("strong", {
children: "heading"
}), " ", _jsx("span", {
class: "myClass",
className: "myClassName <> weird char",
"data-random-attr": "456"
}), " test"]
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"export const toc = [
{
value: '<em>Emphasis</em>',
id: 'emphasis',
level: 2
},
{
value: '<strong>Importance</strong>',
id: 'importance',
level: 3
},
{
value: '<del>Strikethrough</del>',
id: 'strikethrough',
level: 2
},
{
value: '<i>HTML</i>',
id: 'html',
level: 2
},
{
value: '<code>inline.code()</code>',
id: 'inlinecode',
level: 2
},
{
value: 'some <span class="some-class">styled</span> <strong>heading</strong> <span class="myClassName &lt;&gt; weird char"></span> test',
id: 'some-styled-heading--test',
level: 2
}
]
## *Emphasis*
### **Importance**
## ~~Strikethrough~~
## <i>HTML</i>
## \`inline.code()\`
## some <span className="some-class" style={{border: "solid"}}>styled</span> <strong>heading</strong> <span class="myClass" className="myClassName <> weird char" data-random-attr="456" /> test
"
`;
exports[`toc remark plugin works on text content 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
"export const toc = [
{
value: 'Endi',
id: 'endi',
level: 3
},
{
value: 'Endi',
id: 'endi-1',
level: 2
},
{
value: 'Yangshun',
id: 'yangshun',
level: 3
},
{
value: 'I ♥ unicode.',
id: 'i--unicode',
level: 2
}
]
### Endi
\`\`\`md
## This is ignored
\`\`\`
## Endi
Lorem ipsum
### Yangshun
Some content here
## I ♥ unicode.
export const c = 1;
export const toc = [{
"value": "Endi",
"id": "endi",
"level": 3
}, {
"value": "Endi",
"id": "endi-1",
"level": 2
}, {
"value": "Yangshun",
"id": "yangshun",
"level": 3
}, {
"value": "I ♥ unicode.",
"id": "i--unicode",
"level": 2
}];
function _createMdxContent(props) {
const _components = {
code: "code",
h2: "h2",
h3: "h3",
p: "p",
pre: "pre",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h3, {
id: "endi",
children: "Endi"
}), "/n", _jsx(_components.pre, {
children: _jsx(_components.code, {
className: "language-md",
children: "## This is ignored/n"
})
}), "/n", _jsx(_components.h2, {
id: "endi-1",
children: "Endi"
}), "/n", _jsx(_components.p, {
children: "Lorem ipsum"
}), "/n", _jsx(_components.h3, {
id: "yangshun",
children: "Yangshun"
}), "/n", _jsx(_components.p, {
children: "Some content here"
}), "/n", _jsx(_components.h2, {
id: "i--unicode",
children: "I ♥ unicode."
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"
`;
exports[`toc remark plugin works with imported markdown 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import Partial1, {toc as __tocPartial1} from './_partial1.md';
import SomeComponent from './SomeComponent';
import Partial2, {toc as __tocPartial2} from './_partial2.md';
import UnusedPartialImport from './_partial3.md';
import DoesNotExist, {toc as __tocDoesNotExist} from './_doesNotExist.md';
export const toc = [{
"value": "Index section 1",
"id": "index-section-1",
"level": 2
}, ...__tocPartial1, {
"value": "Index section 2",
"id": "index-section-2",
"level": 2
}, ...__tocPartial2, {
"value": "Unused partials",
"id": "unused-partials",
"level": 2
}, {
"value": "NonExisting Partials",
"id": "nonexisting-partials",
"level": 2
}, ...__tocDoesNotExist, {
"value": "Duplicate partials",
"id": "duplicate-partials",
"level": 2
}, ...__tocPartial1, ...__tocPartial1];
function _createMdxContent(props) {
const _components = {
h1: "h1",
h2: "h2",
p: "p",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
id: "index",
children: "Index"
}), "/n", _jsx(_components.p, {
children: "Some text"
}), "/n", "/n", _jsx(_components.h2, {
id: "index-section-1",
children: "Index section 1"
}), "/n", _jsx(_components.p, {
children: "Foo"
}), "/n", _jsx(Partial1, {}), "/n", _jsx(_components.p, {
children: "Some text"
}), "/n", _jsx(SomeComponent, {}), "/n", _jsx(_components.h2, {
id: "index-section-2",
children: "Index section 2"
}), "/n", _jsx(Partial2, {}), "/n", _jsx(_components.h2, {
id: "unused-partials",
children: "Unused partials"
}), "/n", _jsx(_components.p, {
children: "Unused partials (that are only imported but not rendered) shouldn't alter the TOC"
}), "/n", "/n", _jsx(_components.h2, {
id: "nonexisting-partials",
children: "NonExisting Partials"
}), "/n", _jsx(_components.p, {
children: "Partials that do not exist should alter the TOC"
}), "/n", _jsx(_components.p, {
children: "It's not the responsibility of the Remark plugin to check for their existence"
}), "/n", "/n", _jsx(DoesNotExist, {}), "/n", _jsx(_components.h2, {
id: "duplicate-partials",
children: "Duplicate partials"
}), "/n", _jsx(_components.p, {
children: "It's fine if we use partials at the end"
}), "/n", _jsx(Partial1, {}), "/n", _jsx(_components.p, {
children: "And we can use the partial multiple times!"
}), "/n", _jsx(Partial1, {})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"
`;
exports[`toc remark plugin works with partial imported after its usage 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import Partial, {toc as __tocPartial} from './_partial.md';
export const toc = [...__tocPartial];
function _createMdxContent(props) {
const _components = {
h1: "h1",
p: "p",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
id: "partial-used-before-import",
children: "Partial used before import"
}), "/n", _jsx(_components.p, {
children: "While it looks weird to import after usage, this remains valid MDX usage."
}), "/n", _jsx(Partial, {})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"
`;
exports[`toc remark plugin works with partials importing other partials 1`] = `
"import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import Partial2Nested, {toc as __tocPartial2Nested} from './partial2-nested.md';
export const toc = [{
"value": "Partial 2",
"id": "partial-2",
"level": 2
}, {
"value": "Partial 2 Sub Heading",
"id": "partial-2-sub-heading",
"level": 3
}, ...__tocPartial2Nested];
function _createMdxContent(props) {
const _components = {
h2: "h2",
h3: "h3",
p: "p",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h2, {
id: "partial-2",
children: "Partial 2"
}), "/n", _jsx(_components.p, {
children: "Partial 2"
}), "/n", _jsx(_components.h3, {
id: "partial-2-sub-heading",
children: "Partial 2 Sub Heading"
}), "/n", _jsx(_components.p, {
children: "Content"
}), "/n", "/n", _jsx(Partial2Nested, {})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || ({});
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
"
`;

View File

@ -11,23 +11,18 @@ import plugin from '../index';
import headings from '../../headings/index';
const processFixture = async (name: string) => {
const {remark} = await import('remark');
const {default: gfm} = await import('remark-gfm');
const {default: mdx} = await import('remark-mdx');
const {compile} = await import('@mdx-js/mdx');
const filePath = path.join(
__dirname,
'__fixtures__',
name.endsWith('.mdx') ? name : `${name}.md`,
);
const filePath = path.join(__dirname, '__fixtures__', `${name}.md`);
const file = await vfile.read(filePath);
const result = await compile(file, {
format: 'mdx',
remarkPlugins: [headings, gfm, plugin],
rehypePlugins: [],
});
const result = await remark()
.use(headings)
.use(gfm)
.use(mdx)
.use(plugin)
.process(file);
return result.value;
};
@ -75,21 +70,4 @@ describe('toc remark plugin', () => {
const result = await processFixture('empty-headings');
expect(result).toMatchSnapshot();
});
it('works with imported markdown', async () => {
const result = await processFixture('partials/index.mdx');
expect(result).toMatchSnapshot();
});
it('works with partials importing other partials', async () => {
const result = await processFixture('partials/_partial2.mdx');
expect(result).toMatchSnapshot();
});
it('works with partial imported after its usage', async () => {
const result = await processFixture(
'partials/partial-used-before-import.mdx',
);
expect(result).toMatchSnapshot();
});
});

View File

@ -5,183 +5,154 @@
* LICENSE file in the root directory of this source tree.
*/
import {
addTocSliceImportIfNeeded,
createTOCExportNodeAST,
findDefaultImportName,
getImportDeclarations,
isMarkdownImport,
isNamedExport,
} from './utils';
import type {Heading, Root} from 'mdast';
import {parse, type ParserOptions} from '@babel/parser';
import traverse from '@babel/traverse';
import stringifyObject from 'stringify-object';
import {toValue} from '../utils';
import type {Identifier} from '@babel/types';
import type {Node, Parent} from 'unist';
import type {Heading, Literal} from 'mdast';
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
import type {Transformer} from 'unified';
import type {
MdxjsEsm,
MdxJsxFlowElement,
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
} from 'mdast-util-mdx';
import type {TOCItems} from './types';
import type {ImportDeclaration} from 'estree';
// TODO as of April 2023, no way to import/re-export this ESM type easily :/
// TODO upgrade to TS 5.3
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
// import type {Plugin} from 'unified';
type Plugin = any; // TODO fix this asap
export type TOCItem = {
readonly value: string;
readonly id: string;
readonly level: number;
};
const parseOptions: ParserOptions = {
plugins: ['jsx'],
sourceType: 'module',
};
const isImport = (child: any): child is Literal =>
child.type === 'mdxjsEsm' && child.value.startsWith('import');
const hasImports = (index: number) => index > -1;
const isExport = (child: any): child is Literal =>
child.type === 'mdxjsEsm' && child.value.startsWith('export');
interface PluginOptions {
name?: string;
}
// ComponentName (default export) => ImportDeclaration mapping
type MarkdownImports = Map<string, {declaration: ImportDeclaration}>;
// MdxjsEsm node representing an already existing "export const toc" declaration
type ExistingTOCExport = MdxjsEsm | null;
function createTocSliceImportName({
tocExportName,
componentName,
}: {
tocExportName: string;
componentName: string;
}) {
// The name of the toc slice import alias doesn't matter much
// We just need to ensure it's valid and won't conflict with other names
return `__${tocExportName}${componentName}`;
}
async function collectImportsExports({
root,
tocExportName,
}: {
root: Root;
tocExportName: string;
}): Promise<{
markdownImports: MarkdownImports;
existingTocExport: ExistingTOCExport;
}> {
const {visit} = await import('unist-util-visit');
const markdownImports = new Map<string, {declaration: ImportDeclaration}>();
let existingTocExport: MdxjsEsm | null = null;
visit(root, 'mdxjsEsm', (node) => {
if (!node.data?.estree) {
return;
}
if (isNamedExport(node, tocExportName)) {
existingTocExport = node;
}
getImportDeclarations(node.data.estree).forEach((declaration) => {
if (!isMarkdownImport(declaration)) {
return;
const isTarget = (child: Literal, name: string) => {
let found = false;
const ast = parse(child.value, parseOptions);
traverse(ast, {
VariableDeclarator: (path) => {
if ((path.node.id as Identifier).name === name) {
found = true;
}
const componentName = findDefaultImportName(declaration);
if (!componentName) {
return;
}
markdownImports.set(componentName, {
declaration,
});
});
},
});
return found;
};
return {markdownImports, existingTocExport};
}
const getOrCreateExistingTargetIndex = async (
children: Node[],
name: string,
) => {
let importsIndex = -1;
let targetIndex = -1;
async function collectTOCItems({
root,
tocExportName,
markdownImports,
}: {
root: Root;
tocExportName: string;
markdownImports: MarkdownImports;
}): Promise<{
// The toc items we collected in the tree
tocItems: TOCItems;
}> {
const {toString} = await import('mdast-util-to-string');
const {visit} = await import('unist-util-visit');
const tocItems: TOCItems = [];
visit(root, (child) => {
if (child.type === 'heading') {
visitHeading(child);
} else if (child.type === 'mdxJsxFlowElement') {
visitJSXElement(child);
children.forEach((child, index) => {
if (isImport(child)) {
importsIndex = index;
} else if (isExport(child) && isTarget(child, name)) {
targetIndex = index;
}
});
return {tocItems};
if (targetIndex === -1) {
const target = await createExportNode(name, []);
// Visit Markdown headings
function visitHeading(node: Heading) {
const value = toString(node);
// depth:1 headings are titles and not included in the TOC
if (!value || node.depth < 2) {
return;
}
tocItems.push({
type: 'heading',
heading: node,
});
targetIndex = hasImports(importsIndex) ? importsIndex + 1 : 0;
children.splice(targetIndex, 0, target);
}
// Visit JSX elements, such as <Partial/>
function visitJSXElement(node: MdxJsxFlowElement) {
const componentName = node.name;
if (!componentName) {
return;
}
const importDeclaration = markdownImports.get(componentName)?.declaration;
if (!importDeclaration) {
return;
}
return targetIndex;
};
const tocSliceImportName = createTocSliceImportName({
tocExportName,
componentName,
});
tocItems.push({
type: 'slice',
importName: tocSliceImportName,
});
addTocSliceImportIfNeeded({
importDeclaration,
tocExportName,
tocSliceImportName,
});
}
}
export default function plugin(options: PluginOptions = {}): Transformer<Root> {
const tocExportName = options.name || 'toc';
const plugin: Plugin = function plugin(
options: PluginOptions = {},
): Transformer {
const name = options.name || 'toc';
return async (root) => {
const {markdownImports, existingTocExport} = await collectImportsExports({
root,
tocExportName,
const {toString} = await import('mdast-util-to-string');
const {visit} = await import('unist-util-visit');
const headings: TOCItem[] = [];
visit(root, 'heading', (child: Heading) => {
const value = toString(child);
// depth:1 headings are titles and not included in the TOC
if (!value || child.depth < 2) {
return;
}
headings.push({
value: toValue(child, toString),
id: child.data!.id!,
level: child.depth,
});
});
// If user explicitly writes "export const toc" in his mdx file
// We keep it as is do not override their explicit toc structure
// See https://github.com/facebook/docusaurus/pull/7530#discussion_r1458087876
if (existingTocExport) {
return;
const {children} = root as Parent;
const targetIndex = await getOrCreateExistingTargetIndex(children, name);
if (headings?.length) {
children[targetIndex] = await createExportNode(name, headings);
}
};
};
const {tocItems} = await collectTOCItems({
root,
tocExportName,
markdownImports,
});
export default plugin;
root.children.push(
await createTOCExportNodeAST({
tocExportName,
tocItems,
}),
);
async function createExportNode(name: string, object: any): Promise<MdxjsEsm> {
const {valueToEstree} = await import('estree-util-value-to-estree');
return {
type: 'mdxjsEsm',
value: `export const ${name} = ${stringifyObject(object)}`,
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExportNamedDeclaration',
declaration: {
type: 'VariableDeclaration',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name,
},
init: valueToEstree(object),
},
],
kind: 'const',
},
specifiers: [],
source: null,
},
],
sourceType: 'module',
},
},
};
}

View File

@ -1,29 +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 type {Heading} from 'mdast';
// Note: this type is exported from mdx-loader and used in theme
// Need to keep it retro compatible
export type TOCItem = {
readonly value: string;
readonly id: string;
readonly level: number;
};
export type TOCHeading = {
readonly type: 'heading';
readonly heading: Heading;
};
// A TOC slice represents a TOCItem[] imported from a partial
export type TOCSlice = {
readonly type: 'slice';
readonly importName: string;
};
export type TOCItems = (TOCHeading | TOCSlice)[];

View File

@ -1,177 +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 {toValue} from '../utils';
import type {Node} from 'unist';
import type {
MdxjsEsm,
// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721
} from 'mdast-util-mdx';
import type {TOCHeading, TOCItem, TOCItems, TOCSlice} from './types';
import type {
Program,
SpreadElement,
ImportDeclaration,
ImportSpecifier,
} from 'estree';
export function getImportDeclarations(program: Program): ImportDeclaration[] {
return program.body.filter(
(item): item is ImportDeclaration => item.type === 'ImportDeclaration',
);
}
export function isMarkdownImport(node: Node): node is ImportDeclaration {
if (node.type !== 'ImportDeclaration') {
return false;
}
const importPath = (node as ImportDeclaration).source.value;
return typeof importPath === 'string' && /\.mdx?$/.test(importPath);
}
export function findDefaultImportName(
importDeclaration: ImportDeclaration,
): string | undefined {
return importDeclaration.specifiers.find(
(o: Node) => o.type === 'ImportDefaultSpecifier',
)?.local.name;
}
export function findNamedImportSpecifier(
importDeclaration: ImportDeclaration,
localName: string,
): ImportSpecifier | undefined {
return importDeclaration?.specifiers.find(
(specifier): specifier is ImportSpecifier =>
specifier.type === 'ImportSpecifier' &&
specifier.local.name === localName,
);
}
// Before: import Partial from "partial"
// After: import Partial, {toc as __tocPartial} from "partial"
export function addTocSliceImportIfNeeded({
importDeclaration,
tocExportName,
tocSliceImportName,
}: {
importDeclaration: ImportDeclaration;
tocExportName: string;
tocSliceImportName: string;
}): void {
// We only add the toc slice named import if it doesn't exist already
if (!findNamedImportSpecifier(importDeclaration, tocSliceImportName)) {
importDeclaration.specifiers.push({
type: 'ImportSpecifier',
imported: {type: 'Identifier', name: tocExportName},
local: {type: 'Identifier', name: tocSliceImportName},
});
}
}
export function isNamedExport(
node: Node,
exportName: string,
): node is MdxjsEsm {
if (node.type !== 'mdxjsEsm') {
return false;
}
const program = (node as MdxjsEsm).data?.estree;
if (!program) {
return false;
}
if (program.body.length !== 1) {
return false;
}
const exportDeclaration = program.body[0]!;
if (exportDeclaration.type !== 'ExportNamedDeclaration') {
return false;
}
const variableDeclaration = exportDeclaration.declaration;
if (variableDeclaration?.type !== 'VariableDeclaration') {
return false;
}
const {id} = variableDeclaration.declarations[0]!;
if (id.type !== 'Identifier') {
return false;
}
return id.name === exportName;
}
export async function createTOCExportNodeAST({
tocExportName,
tocItems,
}: {
tocExportName: string;
tocItems: TOCItems;
}): Promise<MdxjsEsm> {
function createTOCSliceAST(tocSlice: TOCSlice): SpreadElement {
return {
type: 'SpreadElement',
argument: {type: 'Identifier', name: tocSlice.importName},
};
}
async function createTOCHeadingAST({heading}: TOCHeading) {
const {toString} = await import('mdast-util-to-string');
const {valueToEstree} = await import('estree-util-value-to-estree');
const value: TOCItem = {
value: toValue(heading, toString),
id: heading.data!.id!,
level: heading.depth,
};
return valueToEstree(value);
}
async function createTOCItemAST(tocItem: TOCItems[number]) {
switch (tocItem.type) {
case 'slice':
return createTOCSliceAST(tocItem);
case 'heading':
return createTOCHeadingAST(tocItem);
default: {
throw new Error(`unexpected toc item type`);
}
}
}
return {
type: 'mdxjsEsm',
value: '', // See https://github.com/facebook/docusaurus/pull/9684#discussion_r1457595181
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExportNamedDeclaration',
declaration: {
type: 'VariableDeclaration',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: tocExportName,
},
init: {
type: 'ArrayExpression',
elements: await Promise.all(tocItems.map(createTOCItemAST)),
},
},
],
kind: 'const',
},
specifiers: [],
source: null,
},
],
sourceType: 'module',
},
},
};
}

View File

@ -12,17 +12,17 @@ exports[`transformAsset plugin pathname protocol 1`] = `
exports[`transformAsset plugin transform md links to <a /> 1`] = `
"[asset](https://example.com/asset.pdf)
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default} />
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default} />
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}>asset</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}>asset</a>
in paragraph <a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}>asset</a>
in paragraph <a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}>asset</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset (2).pdf").default}>asset with URL encoded chars</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset (2).pdf").default}>asset with URL encoded chars</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default + '#page=2'}>asset with hash</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default + '#page=2'}>asset with hash</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default} title="Title">asset</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default} title="Title">asset</a>
[page](noUrl.md)
@ -36,24 +36,24 @@ in paragraph <a target="_blank" data-noBrokenLinkCheck={true} href={require("!<P
[assets](/github/!file-loader!/assets.pdf)
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}>asset</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}>asset</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static2/asset2.pdf").default}>asset2</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static2/asset2.pdf").default}>asset2</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>staticAsset.pdf</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>staticAsset.pdf</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>@site/static/staticAsset.pdf</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>@site/static/staticAsset.pdf</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default + '#page=2'} title="Title">@site/static/staticAsset.pdf</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default + '#page=2'} title="Title">@site/static/staticAsset.pdf</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>Just staticAsset.pdf</a>, and <a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>**awesome** staticAsset 2.pdf 'It is really "AWESOME"'</a>, but also <a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>coded \`staticAsset 3.pdf\`</a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>Just staticAsset.pdf</a>, and <a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>**awesome** staticAsset 2.pdf 'It is really "AWESOME"'</a>, but also <a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAsset.pdf").default}>coded \`staticAsset 3.pdf\`</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAssetImage.png").default}><img alt="Clickable Docusaurus logo" src={require("!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/staticAssetImage.png").default} width="200" height="200" /></a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/staticAssetImage.png").default}><img alt="Clickable Docusaurus logo" src={require("!<PROJECT_ROOT>/node_modules/url-loader/dist/cjs.js?limit=10000&name=assets/images/[name]-[contenthash].[ext]&fallback=<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js!./static/staticAssetImage.png").default} width="200" height="200" /></a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}><span style={{color: "red"}}>Stylized link to asset file</span></a>
<a target="_blank" href={require("!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./asset.pdf").default}><span style={{color: "red"}}>Stylized link to asset file</span></a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("./data.raw!=!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./data.json").default}>JSON</a>
<a target="_blank" href={require("./data.raw!=!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./data.json").default}>JSON</a>
<a target="_blank" data-noBrokenLinkCheck={true} href={require("./static/static-json.raw!=!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/static-json.json").default}>static JSON</a>
<a target="_blank" href={require("./static/static-json.raw!=!<PROJECT_ROOT>/node_modules/file-loader/dist/cjs.js?name=assets/files/[name]-[contenthash].[ext]!./static/static-json.json").default}>static JSON</a>
"
`;

View File

@ -73,34 +73,6 @@ async function toAssetRequireNode(
value: '_blank',
});
// Assets are not routes, and are required by Webpack already
// They should not trigger the broken link checker
attributes.push({
type: 'mdxJsxAttribute',
name: 'data-noBrokenLinkCheck',
value: {
type: 'mdxJsxAttributeValueExpression',
value: 'true',
data: {
estree: {
type: 'Program',
body: [
{
type: 'ExpressionStatement',
expression: {
type: 'Literal',
value: true,
raw: 'true',
},
},
],
sourceType: 'module',
comments: [],
},
},
},
});
attributes.push({
type: 'mdxJsxAttribute',
name: 'href',

View File

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

View File

@ -260,15 +260,6 @@ declare module '@docusaurus/useRouteContext' {
export default function useRouteContext(): PluginRouteContext;
}
declare module '@docusaurus/useBrokenLinks' {
export type BrokenLinks = {
collectLink: (link: string | undefined) => void;
collectAnchor: (anchor: string | undefined) => void;
};
export default function useBrokenLinks(): BrokenLinks;
}
declare module '@docusaurus/useIsBrowser' {
export default function useIsBrowser(): boolean;
}
@ -365,9 +356,7 @@ declare module '@docusaurus/useGlobalData' {
declare module '*.svg' {
import type {ComponentType, SVGProps} from 'react';
const ReactComponent: ComponentType<
SVGProps<SVGSVGElement> & {title?: string}
>;
const ReactComponent: ComponentType<SVGProps<SVGSVGElement>>;
export default ReactComponent;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,24 +1,12 @@
{
"name": "@docusaurus/plugin-content-blog",
"version": "3.2.0",
"version": "3.0.1",
"description": "Blog plugin for Docusaurus.",
"main": "lib/index.js",
"types": "src/plugin-content-blog.d.ts",
"exports": {
"./lib/*": "./lib/*",
"./src/*": "./src/*",
"./client": {
"type": "./lib/client/index.d.ts",
"default": "./lib/client/index.js"
},
".": {
"types": "./src/plugin-content-blog.d.ts",
"default": "./lib/index.js"
}
},
"scripts": {
"build": "tsc --build",
"watch": "tsc --build --watch",
"build": "tsc",
"watch": "tsc --watch",
"test:generate-build-snap": "yarn docusaurus build src/__tests__/__fixtures__/website --out-dir build-snap && yarn rimraf src/__tests__/__fixtures__/website/.docusaurus && yarn rimraf src/__tests__/__fixtures__/website/build-snap/assets && git add src/__tests__/__fixtures__/website/build-snap"
},
"repository": {
@ -31,13 +19,13 @@
},
"license": "MIT",
"dependencies": {
"@docusaurus/core": "3.2.0",
"@docusaurus/logger": "3.2.0",
"@docusaurus/mdx-loader": "3.2.0",
"@docusaurus/types": "3.2.0",
"@docusaurus/utils": "3.2.0",
"@docusaurus/utils-common": "3.2.0",
"@docusaurus/utils-validation": "3.2.0",
"@docusaurus/core": "3.0.1",
"@docusaurus/logger": "3.0.1",
"@docusaurus/mdx-loader": "3.0.1",
"@docusaurus/types": "3.0.1",
"@docusaurus/utils": "3.0.1",
"@docusaurus/utils-common": "3.0.1",
"@docusaurus/utils-validation": "3.0.1",
"cheerio": "^1.0.0-rc.12",
"feed": "^4.2.2",
"fs-extra": "^11.1.1",
@ -55,8 +43,5 @@
},
"engines": {
"node": ">=18.0"
},
"devDependencies": {
"@total-typescript/shoehorn": "^0.1.2"
}
}

View File

@ -1,9 +0,0 @@
---
title: Author
slug: author
author: ozaki
last_update:
author: seb
---
author

View File

@ -1,11 +0,0 @@
---
title: Both
slug: both
date: 2020-01-01
last_update:
date: 2021-01-01
author: seb
author: ozaki
---
last update date

View File

@ -1,9 +0,0 @@
---
title: Last update date
slug: lastUpdateDate
date: 2020-01-01
last_update:
date: 2021-01-01
---
last update date

View File

@ -1,6 +0,0 @@
---
title: Nothing
slug: nothing
---
nothing

View File

@ -23,32 +23,7 @@ title: This post links to another one!
[Linked post](/blog/2018/12/14/Happy-First-Birthday-Slash)"
`;
exports[`paginateBlogPosts generates a single page 1`] = `
[
{
"items": [
"post1",
"post2",
"post3",
"post4",
"post5",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": undefined,
"page": 1,
"permalink": "/",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 5,
"totalPages": 1,
},
},
]
`;
exports[`paginateBlogPosts generates pages 1`] = `
exports[`paginateBlogPosts generates right pages 1`] = `
[
{
"items": [
@ -103,7 +78,7 @@ exports[`paginateBlogPosts generates pages 1`] = `
]
`;
exports[`paginateBlogPosts generates pages at blog root 1`] = `
exports[`paginateBlogPosts generates right pages 2`] = `
[
{
"items": [
@ -158,56 +133,26 @@ exports[`paginateBlogPosts generates pages at blog root 1`] = `
]
`;
exports[`paginateBlogPosts generates pages with custom pageBasePath 1`] = `
exports[`paginateBlogPosts generates right pages 3`] = `
[
{
"items": [
"post1",
"post2",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": "/blog/customPageBasePath/2",
"page": 1,
"permalink": "/blog",
"postsPerPage": 2,
"previousPage": undefined,
"totalCount": 5,
"totalPages": 3,
},
},
{
"items": [
"post3",
"post4",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": "/blog/customPageBasePath/3",
"page": 2,
"permalink": "/blog/customPageBasePath/2",
"postsPerPage": 2,
"previousPage": "/blog",
"totalCount": 5,
"totalPages": 3,
},
},
{
"items": [
"post5",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": undefined,
"page": 3,
"permalink": "/blog/customPageBasePath/3",
"postsPerPage": 2,
"previousPage": "/blog/customPageBasePath/2",
"page": 1,
"permalink": "/",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 5,
"totalPages": 3,
"totalPages": 1,
},
},
]

View File

@ -220,134 +220,6 @@ exports[`atom has feed item for each post 1`] = `
]
`;
exports[`atom has feed item for each post - with trailing slash 1`] = `
[
"<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://docusaurus.io/myBaseUrl/blog/</id>
<title>Hello Blog</title>
<updated>2023-07-23T00:00:00.000Z</updated>
<generator>https://github.com/jpmonette/feed</generator>
<link rel="alternate" href="https://docusaurus.io/myBaseUrl/blog/"/>
<subtitle>Hello Blog</subtitle>
<icon>https://docusaurus.io/myBaseUrl/image/favicon.ico</icon>
<rights>Copyright</rights>
<entry>
<title type="html"><![CDATA[test links]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/blog-with-links/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/blog-with-links/"/>
<updated>2023-07-23T00:00:00.000Z</updated>
<summary type="html"><![CDATA[absolute full url]]></summary>
<content type="html"><![CDATA[<p><a href="https://github.com/facebook/docusaurus" target="_blank" rel="noopener noreferrer">absolute full url</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title">absolute pathname</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title">relative pathname</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title">md link</a></p>
<p><a href="https://docusaurus.io/myBaseUrl/blog/blog-with-links/#title">anchor</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title#title">relative pathname + anchor</a></p>
<p><img loading="lazy" src="https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png" width="760" height="160" class="img_yGFe"></p>
<p><img loading="lazy" src="https://docusaurus.io/assets/images/slash-introducing-411a16dd05086935b8e9ddae38ae9b45.svg" alt="" class="img_yGFe"></p>
<img srcset="https://docusaurus.io/img/test-image.png 300w, https://docusaurus.io/img/docusaurus-social-card.png 500w">
<img src="https://docusaurus.io/img/test-image.png">]]></content>
</entry>
<entry>
<title type="html"><![CDATA[MDX Blog Sample with require calls]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post/"/>
<updated>2021-03-06T00:00:00.000Z</updated>
<summary type="html"><![CDATA[Test MDX with require calls]]></summary>
<content type="html"><![CDATA[<p>Test MDX with require calls</p>
<!-- -->
<!-- -->
<img src="https://docusaurus.io/img/test-image.png">
<img src="https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png">
<img src="https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png">]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Full Blog Sample]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/"/>
<updated>2021-03-05T00:00:00.000Z</updated>
<summary type="html"><![CDATA[HTML Heading 1]]></summary>
<content type="html"><![CDATA[<h1>HTML Heading 1</h1>
<h2>HTML Heading 2</h2>
<p>HTML Paragraph</p>
<!-- -->
<!-- -->
<p>Import DOM</p>
<h1>Heading 1</h1>
<h2 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-2">Heading 2<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-2" class="hash-link" aria-label="Direct link to Heading 2" title="Direct link to Heading 2"></a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-3">Heading 3<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-3" class="hash-link" aria-label="Direct link to Heading 3" title="Direct link to Heading 3"></a></h3>
<h4 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-4">Heading 4<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-4" class="hash-link" aria-label="Direct link to Heading 4" title="Direct link to Heading 4"></a></h4>
<h5 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-5">Heading 5<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-5" class="hash-link" aria-label="Direct link to Heading 5" title="Direct link to Heading 5"></a></h5>
<ul>
<li>list1</li>
<li>list2</li>
<li>list3</li>
</ul>
<ul>
<li>list1</li>
<li>list2</li>
<li>list3</li>
</ul>
<p>Normal Text <em>Italics Text</em> <strong>Bold Text</strong></p>
<p><a href="https://v2.docusaurus.io/" target="_blank" rel="noopener noreferrer">link</a> <img loading="lazy" src="https://v2.docusaurus.io/" alt="image" class="img_yGFe"></p>]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Complex Slug]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô/"/>
<updated>2020-08-16T00:00:00.000Z</updated>
<summary type="html"><![CDATA[complex url slug]]></summary>
<content type="html"><![CDATA[<p>complex url slug</p>]]></content>
<category label="date" term="date"/>
<category label="complex" term="complex"/>
</entry>
<entry>
<title type="html"><![CDATA[Simple Slug]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/simple/slug/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/simple/slug/"/>
<updated>2020-08-15T00:00:00.000Z</updated>
<summary type="html"><![CDATA[simple url slug]]></summary>
<content type="html"><![CDATA[<p>simple url slug</p>]]></content>
<author>
<name>Sébastien Lorber</name>
<uri>https://sebastienlorber.com</uri>
</author>
</entry>
<entry>
<title type="html"><![CDATA[some heading]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/heading-as-title/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/heading-as-title/"/>
<updated>2019-01-02T00:00:00.000Z</updated>
</entry>
<entry>
<title type="html"><![CDATA[date-matter]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/date-matter/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/date-matter/"/>
<updated>2019-01-01T00:00:00.000Z</updated>
<summary type="html"><![CDATA[date inside front matter]]></summary>
<content type="html"><![CDATA[<p>date inside front matter</p>]]></content>
<category label="date" term="date"/>
</entry>
<entry>
<title type="html"><![CDATA[Happy 1st Birthday Slash! (translated)]]></title>
<id>https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash/</id>
<link href="https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash/"/>
<updated>2018-12-14T00:00:00.000Z</updated>
<summary type="html"><![CDATA[Happy birthday! (translated)]]></summary>
<content type="html"><![CDATA[<p>Happy birthday! (translated)</p>]]></content>
<author>
<name>Yangshun Tay (translated)</name>
</author>
<author>
<name>Sébastien Lorber (translated)</name>
<email>lorber.sebastien@gmail.com</email>
</author>
</entry>
</feed>",
]
`;
exports[`json filters to the first two entries 1`] = `
[
"{
@ -506,102 +378,6 @@ exports[`json has feed item for each post 1`] = `
]
`;
exports[`json has feed item for each post - with trailing slash 1`] = `
[
"{
"version": "https://jsonfeed.org/version/1",
"title": "Hello Blog",
"home_page_url": "https://docusaurus.io/myBaseUrl/blog/",
"description": "Hello Blog",
"items": [
{
"id": "https://docusaurus.io/myBaseUrl/blog/blog-with-links/",
"content_html": "<p><a href=\\"https://github.com/facebook/docusaurus\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">absolute full url</a></p>/n<p><a href=\\"https://docusaurus.io/blog/heading-as-title\\">absolute pathname</a></p>/n<p><a href=\\"https://docusaurus.io/blog/heading-as-title\\">relative pathname</a></p>/n<p><a href=\\"https://docusaurus.io/blog/heading-as-title\\">md link</a></p>/n<p><a href=\\"https://docusaurus.io/myBaseUrl/blog/blog-with-links/#title\\">anchor</a></p>/n<p><a href=\\"https://docusaurus.io/blog/heading-as-title#title\\">relative pathname + anchor</a></p>/n<p><img loading=\\"lazy\\" src=\\"https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png\\" width=\\"760\\" height=\\"160\\" class=\\"img_yGFe\\"></p>/n<p><img loading=\\"lazy\\" src=\\"https://docusaurus.io/assets/images/slash-introducing-411a16dd05086935b8e9ddae38ae9b45.svg\\" alt=\\"\\" class=\\"img_yGFe\\"></p>/n<img srcset=\\"https://docusaurus.io/img/test-image.png 300w, https://docusaurus.io/img/docusaurus-social-card.png 500w\\">/n<img src=\\"https://docusaurus.io/img/test-image.png\\">",
"url": "https://docusaurus.io/myBaseUrl/blog/blog-with-links/",
"title": "test links",
"summary": "absolute full url",
"date_modified": "2023-07-23T00:00:00.000Z",
"tags": []
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post/",
"content_html": "<p>Test MDX with require calls</p>/n<!-- -->/n<!-- -->/n<img src=\\"https://docusaurus.io/img/test-image.png\\">/n<img src=\\"https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png\\">/n<img src=\\"https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png\\">",
"url": "https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post/",
"title": "MDX Blog Sample with require calls",
"summary": "Test MDX with require calls",
"date_modified": "2021-03-06T00:00:00.000Z",
"tags": []
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/",
"content_html": "<h1>HTML Heading 1</h1>/n<h2>HTML Heading 2</h2>/n<p>HTML Paragraph</p>/n<!-- -->/n<!-- -->/n<p>Import DOM</p>/n<h1>Heading 1</h1>/n<h2 class=\\"anchor anchorWithHideOnScrollNavbar_G5V2\\" id=\\"heading-2\\">Heading 2<a href=\\"https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-2\\" class=\\"hash-link\\" aria-label=\\"Direct link to Heading 2\\" title=\\"Direct link to Heading 2\\"></a></h2>/n<h3 class=\\"anchor anchorWithHideOnScrollNavbar_G5V2\\" id=\\"heading-3\\">Heading 3<a href=\\"https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-3\\" class=\\"hash-link\\" aria-label=\\"Direct link to Heading 3\\" title=\\"Direct link to Heading 3\\"></a></h3>/n<h4 class=\\"anchor anchorWithHideOnScrollNavbar_G5V2\\" id=\\"heading-4\\">Heading 4<a href=\\"https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-4\\" class=\\"hash-link\\" aria-label=\\"Direct link to Heading 4\\" title=\\"Direct link to Heading 4\\"></a></h4>/n<h5 class=\\"anchor anchorWithHideOnScrollNavbar_G5V2\\" id=\\"heading-5\\">Heading 5<a href=\\"https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-5\\" class=\\"hash-link\\" aria-label=\\"Direct link to Heading 5\\" title=\\"Direct link to Heading 5\\"></a></h5>/n<ul>/n<li>list1</li>/n<li>list2</li>/n<li>list3</li>/n</ul>/n<ul>/n<li>list1</li>/n<li>list2</li>/n<li>list3</li>/n</ul>/n<p>Normal Text <em>Italics Text</em> <strong>Bold Text</strong></p>/n<p><a href=\\"https://v2.docusaurus.io/\\" target=\\"_blank\\" rel=\\"noopener noreferrer\\">link</a> <img loading=\\"lazy\\" src=\\"https://v2.docusaurus.io/\\" alt=\\"image\\" class=\\"img_yGFe\\"></p>",
"url": "https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/",
"title": "Full Blog Sample",
"summary": "HTML Heading 1",
"date_modified": "2021-03-05T00:00:00.000Z",
"tags": []
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô/",
"content_html": "<p>complex url slug</p>",
"url": "https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô/",
"title": "Complex Slug",
"summary": "complex url slug",
"date_modified": "2020-08-16T00:00:00.000Z",
"tags": [
"date",
"complex"
]
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/simple/slug/",
"content_html": "<p>simple url slug</p>",
"url": "https://docusaurus.io/myBaseUrl/blog/simple/slug/",
"title": "Simple Slug",
"summary": "simple url slug",
"date_modified": "2020-08-15T00:00:00.000Z",
"author": {
"name": "Sébastien Lorber",
"url": "https://sebastienlorber.com"
},
"tags": []
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/heading-as-title/",
"content_html": "",
"url": "https://docusaurus.io/myBaseUrl/blog/heading-as-title/",
"title": "some heading",
"date_modified": "2019-01-02T00:00:00.000Z",
"tags": []
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/date-matter/",
"content_html": "<p>date inside front matter</p>",
"url": "https://docusaurus.io/myBaseUrl/blog/date-matter/",
"title": "date-matter",
"summary": "date inside front matter",
"date_modified": "2019-01-01T00:00:00.000Z",
"tags": [
"date"
]
},
{
"id": "https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash/",
"content_html": "<p>Happy birthday! (translated)</p>",
"url": "https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash/",
"title": "Happy 1st Birthday Slash! (translated)",
"summary": "Happy birthday! (translated)",
"date_modified": "2018-12-14T00:00:00.000Z",
"author": {
"name": "Yangshun Tay (translated)"
},
"tags": []
}
]
}",
]
`;
exports[`rss filters to the first two entries 1`] = `
[
"<?xml version="1.0" encoding="utf-8"?>
@ -817,123 +593,3 @@ exports[`rss has feed item for each post 1`] = `
</rss>",
]
`;
exports[`rss has feed item for each post - with trailing slash 1`] = `
[
"<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Hello Blog</title>
<link>https://docusaurus.io/myBaseUrl/blog/</link>
<description>Hello Blog</description>
<lastBuildDate>Sun, 23 Jul 2023 00:00:00 GMT</lastBuildDate>
<docs>https://validator.w3.org/feed/docs/rss2.html</docs>
<generator>https://github.com/jpmonette/feed</generator>
<language>en</language>
<copyright>Copyright</copyright>
<item>
<title><![CDATA[test links]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/blog-with-links/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/blog-with-links/</guid>
<pubDate>Sun, 23 Jul 2023 00:00:00 GMT</pubDate>
<description><![CDATA[absolute full url]]></description>
<content:encoded><![CDATA[<p><a href="https://github.com/facebook/docusaurus" target="_blank" rel="noopener noreferrer">absolute full url</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title">absolute pathname</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title">relative pathname</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title">md link</a></p>
<p><a href="https://docusaurus.io/myBaseUrl/blog/blog-with-links/#title">anchor</a></p>
<p><a href="https://docusaurus.io/blog/heading-as-title#title">relative pathname + anchor</a></p>
<p><img loading="lazy" src="https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png" width="760" height="160" class="img_yGFe"></p>
<p><img loading="lazy" src="https://docusaurus.io/assets/images/slash-introducing-411a16dd05086935b8e9ddae38ae9b45.svg" alt="" class="img_yGFe"></p>
<img srcset="https://docusaurus.io/img/test-image.png 300w, https://docusaurus.io/img/docusaurus-social-card.png 500w">
<img src="https://docusaurus.io/img/test-image.png">]]></content:encoded>
</item>
<item>
<title><![CDATA[MDX Blog Sample with require calls]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post/</guid>
<pubDate>Sat, 06 Mar 2021 00:00:00 GMT</pubDate>
<description><![CDATA[Test MDX with require calls]]></description>
<content:encoded><![CDATA[<p>Test MDX with require calls</p>
<!-- -->
<!-- -->
<img src="https://docusaurus.io/img/test-image.png">
<img src="https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png">
<img src="https://docusaurus.io/assets/images/test-image-742d39e51f41482e8132e79c09ad4eea.png">]]></content:encoded>
</item>
<item>
<title><![CDATA[Full Blog Sample]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/</guid>
<pubDate>Fri, 05 Mar 2021 00:00:00 GMT</pubDate>
<description><![CDATA[HTML Heading 1]]></description>
<content:encoded><![CDATA[<h1>HTML Heading 1</h1>
<h2>HTML Heading 2</h2>
<p>HTML Paragraph</p>
<!-- -->
<!-- -->
<p>Import DOM</p>
<h1>Heading 1</h1>
<h2 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-2">Heading 2<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-2" class="hash-link" aria-label="Direct link to Heading 2" title="Direct link to Heading 2"></a></h2>
<h3 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-3">Heading 3<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-3" class="hash-link" aria-label="Direct link to Heading 3" title="Direct link to Heading 3"></a></h3>
<h4 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-4">Heading 4<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-4" class="hash-link" aria-label="Direct link to Heading 4" title="Direct link to Heading 4"></a></h4>
<h5 class="anchor anchorWithHideOnScrollNavbar_G5V2" id="heading-5">Heading 5<a href="https://docusaurus.io/myBaseUrl/blog/mdx-blog-post/#heading-5" class="hash-link" aria-label="Direct link to Heading 5" title="Direct link to Heading 5"></a></h5>
<ul>
<li>list1</li>
<li>list2</li>
<li>list3</li>
</ul>
<ul>
<li>list1</li>
<li>list2</li>
<li>list3</li>
</ul>
<p>Normal Text <em>Italics Text</em> <strong>Bold Text</strong></p>
<p><a href="https://v2.docusaurus.io/" target="_blank" rel="noopener noreferrer">link</a> <img loading="lazy" src="https://v2.docusaurus.io/" alt="image" class="img_yGFe"></p>]]></content:encoded>
</item>
<item>
<title><![CDATA[Complex Slug]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/hey/my super path/héllô/</guid>
<pubDate>Sun, 16 Aug 2020 00:00:00 GMT</pubDate>
<description><![CDATA[complex url slug]]></description>
<content:encoded><![CDATA[<p>complex url slug</p>]]></content:encoded>
<category>date</category>
<category>complex</category>
</item>
<item>
<title><![CDATA[Simple Slug]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/simple/slug/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/simple/slug/</guid>
<pubDate>Sat, 15 Aug 2020 00:00:00 GMT</pubDate>
<description><![CDATA[simple url slug]]></description>
<content:encoded><![CDATA[<p>simple url slug</p>]]></content:encoded>
</item>
<item>
<title><![CDATA[some heading]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/heading-as-title/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/heading-as-title/</guid>
<pubDate>Wed, 02 Jan 2019 00:00:00 GMT</pubDate>
</item>
<item>
<title><![CDATA[date-matter]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/date-matter/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/date-matter/</guid>
<pubDate>Tue, 01 Jan 2019 00:00:00 GMT</pubDate>
<description><![CDATA[date inside front matter]]></description>
<content:encoded><![CDATA[<p>date inside front matter</p>]]></content:encoded>
<category>date</category>
</item>
<item>
<title><![CDATA[Happy 1st Birthday Slash! (translated)]]></title>
<link>https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash/</link>
<guid>https://docusaurus.io/myBaseUrl/blog/2018/12/14/Happy-First-Birthday-Slash/</guid>
<pubDate>Fri, 14 Dec 2018 00:00:00 GMT</pubDate>
<description><![CDATA[Happy birthday! (translated)]]></description>
<content:encoded><![CDATA[<p>Happy birthday! (translated)</p>]]></content:encoded>
<author>lorber.sebastien@gmail.com (Sébastien Lorber (translated))</author>
</item>
</channel>
</rss>",
]
`;

View File

@ -1,250 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`blog plugin process blog posts load content 1`] = `
{
"/blog/tags/tag-1": {
"items": [
"/simple/slug/another",
"/another/tags",
"/another/tags2",
],
"label": "tag1",
"pages": [
{
"items": [
"/simple/slug/another",
],
"metadata": {
"blogDescription": "Blog",
"blogTitle": "Blog",
"nextPage": "/blog/tags/tag-1/page/2",
"page": 1,
"permalink": "/blog/tags/tag-1",
"postsPerPage": 1,
"previousPage": undefined,
"totalCount": 3,
"totalPages": 3,
},
},
{
"items": [
"/another/tags",
],
"metadata": {
"blogDescription": "Blog",
"blogTitle": "Blog",
"nextPage": "/blog/tags/tag-1/page/3",
"page": 2,
"permalink": "/blog/tags/tag-1/page/2",
"postsPerPage": 1,
"previousPage": "/blog/tags/tag-1",
"totalCount": 3,
"totalPages": 3,
},
},
{
"items": [
"/another/tags2",
],
"metadata": {
"blogDescription": "Blog",
"blogTitle": "Blog",
"nextPage": undefined,
"page": 3,
"permalink": "/blog/tags/tag-1/page/3",
"postsPerPage": 1,
"previousPage": "/blog/tags/tag-1/page/2",
"totalCount": 3,
"totalPages": 3,
},
},
],
"permalink": "/blog/tags/tag-1",
"unlisted": false,
},
"/blog/tags/tag-2": {
"items": [
"/another/tags",
"/another/tags2",
],
"label": "tag2",
"pages": [
{
"items": [
"/another/tags",
],
"metadata": {
"blogDescription": "Blog",
"blogTitle": "Blog",
"nextPage": "/blog/tags/tag-2/page/2",
"page": 1,
"permalink": "/blog/tags/tag-2",
"postsPerPage": 1,
"previousPage": undefined,
"totalCount": 2,
"totalPages": 2,
},
},
{
"items": [
"/another/tags2",
],
"metadata": {
"blogDescription": "Blog",
"blogTitle": "Blog",
"nextPage": undefined,
"page": 2,
"permalink": "/blog/tags/tag-2/page/2",
"postsPerPage": 1,
"previousPage": "/blog/tags/tag-2",
"totalCount": 2,
"totalPages": 2,
},
},
],
"permalink": "/blog/tags/tag-2",
"unlisted": false,
},
}
`;
exports[`blog plugin process blog posts load content 2`] = `
[
{
"content": "simple url slug",
"id": "/simple/slug/another",
"metadata": {
"authors": [
{
"imageURL": undefined,
"name": "Sébastien Lorber",
"title": "Docusaurus maintainer",
"url": "https://sebastienlorber.com",
},
],
"date": 2020-08-15T00:00:00.000Z,
"description": "simple url slug",
"editUrl": "https://baseEditUrl.com/edit/blog/another-simple-slug-with-tags.md",
"frontMatter": {
"author": "Sébastien Lorber",
"author_title": "Docusaurus maintainer",
"author_url": "https://sebastienlorber.com",
"date": 2020-08-15T00:00:00.000Z,
"slug": "/simple/slug/another",
"tags": [
"tag1",
],
"title": "Another Simple Slug",
},
"hasTruncateMarker": false,
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"nextItem": {
"permalink": "/blog/another/tags",
"title": "Another With Tag",
},
"permalink": "/blog/simple/slug/another",
"readingTime": 0.015,
"source": "@site/blog/another-simple-slug-with-tags.md",
"tags": [
{
"label": "tag1",
"permalink": "/blog/tags/tag-1",
},
],
"title": "Another Simple Slug",
"unlisted": false,
},
},
{
"content": "with tag",
"id": "/another/tags",
"metadata": {
"authors": [],
"date": 2020-08-15T00:00:00.000Z,
"description": "with tag",
"editUrl": "https://baseEditUrl.com/edit/blog/another-with-tags.md",
"frontMatter": {
"date": 2020-08-15T00:00:00.000Z,
"slug": "/another/tags",
"tags": [
"tag1",
"tag2",
],
"title": "Another With Tag",
},
"hasTruncateMarker": false,
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"nextItem": {
"permalink": "/blog/another/tags2",
"title": "Another With Tag",
},
"permalink": "/blog/another/tags",
"prevItem": {
"permalink": "/blog/simple/slug/another",
"title": "Another Simple Slug",
},
"readingTime": 0.01,
"source": "@site/blog/another-with-tags.md",
"tags": [
{
"label": "tag1",
"permalink": "/blog/tags/tag-1",
},
{
"label": "tag2",
"permalink": "/blog/tags/tag-2",
},
],
"title": "Another With Tag",
"unlisted": false,
},
},
{
"content": "with tag",
"id": "/another/tags2",
"metadata": {
"authors": [],
"date": 2020-08-15T00:00:00.000Z,
"description": "with tag",
"editUrl": "https://baseEditUrl.com/edit/blog/another-with-tags2.md",
"frontMatter": {
"date": 2020-08-15T00:00:00.000Z,
"slug": "/another/tags2",
"tags": [
"tag1",
"tag2",
],
"title": "Another With Tag",
},
"hasTruncateMarker": false,
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/blog/another/tags2",
"prevItem": {
"permalink": "/blog/another/tags",
"title": "Another With Tag",
},
"readingTime": 0.01,
"source": "@site/blog/another-with-tags2.md",
"tags": [
{
"label": "tag1",
"permalink": "/blog/tags/tag-1",
},
{
"label": "tag2",
"permalink": "/blog/tags/tag-2",
},
],
"title": "Another With Tag",
"unlisted": false,
},
},
]
`;
exports[`blog plugin works on blog tags without pagination 1`] = `
{
"/blog/tags/tag-1": {

View File

@ -50,6 +50,7 @@ exports[`translateContent falls back when translation is incomplete 1`] = `
"authors": [],
"date": 2021-07-19T00:00:00.000Z,
"description": "/blog/2021/06/19/hello",
"formattedDate": "June 19, 2021",
"frontMatter": {},
"hasTruncateMarker": true,
"permalink": "/blog/2021/06/19/hello",
@ -93,6 +94,7 @@ exports[`translateContent returns translated loaded 1`] = `
"authors": [],
"date": 2021-07-19T00:00:00.000Z,
"description": "/blog/2021/06/19/hello",
"formattedDate": "June 19, 2021",
"frontMatter": {},
"hasTruncateMarker": true,
"permalink": "/blog/2021/06/19/hello",

View File

@ -19,7 +19,6 @@ describe('getBlogPostAuthors', () => {
getBlogPostAuthors({
frontMatter: {},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([]);
expect(
@ -28,7 +27,6 @@ describe('getBlogPostAuthors', () => {
authors: [],
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([]);
});
@ -40,7 +38,6 @@ describe('getBlogPostAuthors', () => {
author: 'Sébastien Lorber',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([{name: 'Sébastien Lorber'}]);
expect(
@ -49,7 +46,6 @@ describe('getBlogPostAuthors', () => {
authorTitle: 'maintainer',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([{title: 'maintainer'}]);
expect(
@ -58,30 +54,11 @@ describe('getBlogPostAuthors', () => {
authorImageURL: 'https://github.com/slorber.png',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([{imageURL: 'https://github.com/slorber.png'}]);
expect(
getBlogPostAuthors({
frontMatter: {
authorImageURL: '/img/slorber.png',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([{imageURL: '/img/slorber.png'}]);
expect(
getBlogPostAuthors({
frontMatter: {
authorImageURL: '/img/slorber.png',
},
authorsMap: undefined,
baseUrl: '/baseURL',
}),
).toEqual([{imageURL: '/baseURL/img/slorber.png'}]);
expect(
getBlogPostAuthors({
frontMatter: {
author: 'Sébastien Lorber',
author_title: 'maintainer1',
authorTitle: 'maintainer2',
@ -91,7 +68,6 @@ describe('getBlogPostAuthors', () => {
authorURL: 'https://github.com/slorber2',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([
{
@ -110,69 +86,8 @@ describe('getBlogPostAuthors', () => {
authors: 'slorber',
},
authorsMap: {slorber: {name: 'Sébastien Lorber'}},
baseUrl: '/',
}),
).toEqual([{key: 'slorber', name: 'Sébastien Lorber'}]);
expect(
getBlogPostAuthors({
frontMatter: {
authors: 'slorber',
},
authorsMap: {
slorber: {
name: 'Sébastien Lorber',
imageURL: 'https://github.com/slorber.png',
},
},
baseUrl: '/',
}),
).toEqual([
{
key: 'slorber',
name: 'Sébastien Lorber',
imageURL: 'https://github.com/slorber.png',
},
]);
expect(
getBlogPostAuthors({
frontMatter: {
authors: 'slorber',
},
authorsMap: {
slorber: {
name: 'Sébastien Lorber',
imageURL: '/img/slorber.png',
},
},
baseUrl: '/',
}),
).toEqual([
{
key: 'slorber',
name: 'Sébastien Lorber',
imageURL: '/img/slorber.png',
},
]);
expect(
getBlogPostAuthors({
frontMatter: {
authors: 'slorber',
},
authorsMap: {
slorber: {
name: 'Sébastien Lorber',
imageURL: '/img/slorber.png',
},
},
baseUrl: '/baseUrl',
}),
).toEqual([
{
key: 'slorber',
name: 'Sébastien Lorber',
imageURL: '/baseUrl/img/slorber.png',
},
]);
});
it('can read authors string[]', () => {
@ -185,7 +100,6 @@ describe('getBlogPostAuthors', () => {
slorber: {name: 'Sébastien Lorber', title: 'maintainer'},
yangshun: {name: 'Yangshun Tay'},
},
baseUrl: '/',
}),
).toEqual([
{key: 'slorber', name: 'Sébastien Lorber', title: 'maintainer'},
@ -200,7 +114,6 @@ describe('getBlogPostAuthors', () => {
authors: {name: 'Sébastien Lorber', title: 'maintainer'},
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([{name: 'Sébastien Lorber', title: 'maintainer'}]);
});
@ -215,7 +128,6 @@ describe('getBlogPostAuthors', () => {
],
},
authorsMap: undefined,
baseUrl: '/',
}),
).toEqual([
{name: 'Sébastien Lorber', title: 'maintainer'},
@ -241,7 +153,6 @@ describe('getBlogPostAuthors', () => {
slorber: {name: 'Sébastien Lorber', title: 'maintainer'},
yangshun: {name: 'Yangshun Tay', title: 'Yangshun title original'},
},
baseUrl: '/',
}),
).toEqual([
{key: 'slorber', name: 'Sébastien Lorber', title: 'maintainer'},
@ -262,7 +173,6 @@ describe('getBlogPostAuthors', () => {
authors: 'slorber',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"Can't reference blog post authors by a key (such as 'slorber') because no authors map file could be loaded.
@ -277,7 +187,6 @@ describe('getBlogPostAuthors', () => {
authors: 'slorber',
},
authorsMap: {},
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"Can't reference blog post authors by a key (such as 'slorber') because no authors map file could be loaded.
@ -296,7 +205,6 @@ describe('getBlogPostAuthors', () => {
yangshun: {name: 'Yangshun Tay'},
jmarcey: {name: 'Joel Marcey'},
},
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"Blog author with key "slorber" not found in the authors map file.
@ -317,7 +225,6 @@ describe('getBlogPostAuthors', () => {
yangshun: {name: 'Yangshun Tay'},
jmarcey: {name: 'Joel Marcey'},
},
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"Blog author with key "slorber" not found in the authors map file.
@ -338,7 +245,6 @@ describe('getBlogPostAuthors', () => {
yangshun: {name: 'Yangshun Tay'},
jmarcey: {name: 'Joel Marcey'},
},
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"Blog author with key "slorber" not found in the authors map file.
@ -356,7 +262,6 @@ describe('getBlogPostAuthors', () => {
author: 'Yangshun Tay',
},
authorsMap: undefined,
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"To declare blog post authors, use the 'authors' front matter in priority.
@ -370,7 +275,6 @@ describe('getBlogPostAuthors', () => {
author_title: 'legacy title',
},
authorsMap: {slorber: {name: 'Sébastien Lorber'}},
baseUrl: '/',
}),
).toThrowErrorMatchingInlineSnapshot(`
"To declare blog post authors, use the 'authors' front matter in priority.

View File

@ -8,14 +8,12 @@
import {jest} from '@jest/globals';
import fs from 'fs-extra';
import path from 'path';
import {fromPartial} from '@total-typescript/shoehorn';
import {
truncate,
parseBlogFileName,
linkify,
getSourceToPermalink,
paginateBlogPosts,
applyProcessBlogPosts,
type LinkifyParams,
} from '../blogUtils';
import type {BlogBrokenMarkdownLink, BlogContentPaths} from '../types';
@ -40,15 +38,14 @@ describe('truncate', () => {
});
describe('paginateBlogPosts', () => {
const blogPosts = [
{id: 'post1', metadata: {}, content: 'Foo 1'},
{id: 'post2', metadata: {}, content: 'Foo 2'},
{id: 'post3', metadata: {}, content: 'Foo 3'},
{id: 'post4', metadata: {}, content: 'Foo 4'},
{id: 'post5', metadata: {}, content: 'Foo 5'},
] as BlogPost[];
it('generates pages', () => {
it('generates right pages', () => {
const blogPosts = [
{id: 'post1', metadata: {}, content: 'Foo 1'},
{id: 'post2', metadata: {}, content: 'Foo 2'},
{id: 'post3', metadata: {}, content: 'Foo 3'},
{id: 'post4', metadata: {}, content: 'Foo 4'},
{id: 'post5', metadata: {}, content: 'Foo 5'},
] as BlogPost[];
expect(
paginateBlogPosts({
blogPosts,
@ -56,12 +53,8 @@ describe('paginateBlogPosts', () => {
blogTitle: 'Blog Title',
blogDescription: 'Blog Description',
postsPerPageOption: 2,
pageBasePath: 'page',
}),
).toMatchSnapshot();
});
it('generates pages at blog root', () => {
expect(
paginateBlogPosts({
blogPosts,
@ -69,12 +62,8 @@ describe('paginateBlogPosts', () => {
blogTitle: 'Blog Title',
blogDescription: 'Blog Description',
postsPerPageOption: 2,
pageBasePath: 'page',
}),
).toMatchSnapshot();
});
it('generates a single page', () => {
expect(
paginateBlogPosts({
blogPosts,
@ -82,20 +71,6 @@ describe('paginateBlogPosts', () => {
blogTitle: 'Blog Title',
blogDescription: 'Blog Description',
postsPerPageOption: 10,
pageBasePath: 'page',
}),
).toMatchSnapshot();
});
it('generates pages with custom pageBasePath', () => {
expect(
paginateBlogPosts({
blogPosts,
basePageUrl: '/blog',
blogTitle: 'Blog Title',
blogDescription: 'Blog Description',
postsPerPageOption: 2,
pageBasePath: 'customPageBasePath',
}),
).toMatchSnapshot();
});
@ -238,7 +213,7 @@ describe('linkify', () => {
hasTruncateMarker: false,
frontMatter: {},
authors: [],
unlisted: false,
formattedDate: '',
},
content: '',
},
@ -297,81 +272,3 @@ describe('linkify', () => {
} as BlogBrokenMarkdownLink);
});
});
describe('processBlogPosts', () => {
const blogPost2022: BlogPost = fromPartial({
metadata: {date: new Date('2022-01-01')},
});
const blogPost2023: BlogPost = fromPartial({
metadata: {date: new Date('2023-01-01')},
});
const blogPost2024: BlogPost = fromPartial({
metadata: {date: new Date('2024-01-01')},
});
it('filter blogs only from 2024', async () => {
const processedBlogPosts = await applyProcessBlogPosts({
blogPosts: [blogPost2022, blogPost2023, blogPost2024],
processBlogPosts: async ({blogPosts}: {blogPosts: BlogPost[]}) =>
blogPosts.filter(
(blogPost) => blogPost.metadata.date.getFullYear() === 2024,
),
});
expect(processedBlogPosts).toEqual([blogPost2024]);
});
it('sort blogs by date in ascending order', async () => {
const processedBlogPosts = await applyProcessBlogPosts({
blogPosts: [blogPost2023, blogPost2022, blogPost2024],
processBlogPosts: async ({blogPosts}: {blogPosts: BlogPost[]}) =>
blogPosts.sort(
(a, b) => a.metadata.date.getTime() - b.metadata.date.getTime(),
),
});
expect(processedBlogPosts).toEqual([
blogPost2022,
blogPost2023,
blogPost2024,
]);
});
it('sort blogs by date in descending order', async () => {
const processedBlogPosts = await applyProcessBlogPosts({
blogPosts: [blogPost2023, blogPost2022, blogPost2024],
processBlogPosts: async ({blogPosts}: {blogPosts: BlogPost[]}) =>
blogPosts.sort(
(a, b) => b.metadata.date.getTime() - a.metadata.date.getTime(),
),
});
expect(processedBlogPosts).toEqual([
blogPost2024,
blogPost2023,
blogPost2022,
]);
});
it('processBlogPosts return 2022 only', async () => {
const processedBlogPosts = await applyProcessBlogPosts({
blogPosts: [blogPost2023, blogPost2022, blogPost2024],
processBlogPosts: async () => [blogPost2022],
});
expect(processedBlogPosts).toEqual([blogPost2022]);
});
it('processBlogPosts return undefined', async () => {
const processedBlogPosts = await applyProcessBlogPosts({
blogPosts: [blogPost2023, blogPost2022, blogPost2024],
processBlogPosts: async () => {},
});
expect(processedBlogPosts).toEqual([
blogPost2023,
blogPost2022,
blogPost2024,
]);
});
});

View File

@ -8,8 +8,6 @@
import {jest} from '@jest/globals';
import path from 'path';
import fs from 'fs-extra';
import {DEFAULT_PARSE_FRONT_MATTER} from '@docusaurus/utils';
import {fromPartial} from '@total-typescript/shoehorn';
import {DEFAULT_OPTIONS} from '../options';
import {generateBlogPosts} from '../blogUtils';
import {createBlogFeedFiles} from '../feed';
@ -33,8 +31,6 @@ const DefaultI18N: I18n = {
},
};
const markdown = {parseFrontMatter: DEFAULT_PARSE_FRONT_MATTER};
function getBlogContentPaths(siteDir: string): BlogContentPaths {
return {
contentPath: path.resolve(siteDir, 'blog'),
@ -76,17 +72,16 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
baseUrl: '/',
url: 'https://docusaurus.io',
favicon: 'image/favicon.ico',
markdown,
};
const outDir = path.join(siteDir, 'build-snap');
await testGenerateFeeds(
fromPartial({
{
siteDir,
siteConfig,
i18n: DefaultI18N,
outDir,
}),
} as LoadContext,
{
path: 'invalid-blog-path',
routeBasePath: 'blog',
@ -115,18 +110,17 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
baseUrl: '/myBaseUrl/',
url: 'https://docusaurus.io',
favicon: 'image/favicon.ico',
markdown,
};
// Build is quite difficult to mock, so we built the blog beforehand and
// copied the output to the fixture...
await testGenerateFeeds(
fromPartial({
{
siteDir,
siteConfig,
i18n: DefaultI18N,
outDir,
}),
} as LoadContext,
{
path: 'blog',
routeBasePath: 'blog',
@ -158,18 +152,17 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
baseUrl: '/myBaseUrl/',
url: 'https://docusaurus.io',
favicon: 'image/favicon.ico',
markdown,
};
// Build is quite difficult to mock, so we built the blog beforehand and
// copied the output to the fixture...
await testGenerateFeeds(
fromPartial({
{
siteDir,
siteConfig,
i18n: DefaultI18N,
outDir,
}),
} as LoadContext,
{
path: 'blog',
routeBasePath: 'blog',
@ -211,18 +204,17 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
baseUrl: '/myBaseUrl/',
url: 'https://docusaurus.io',
favicon: 'image/favicon.ico',
markdown,
};
// Build is quite difficult to mock, so we built the blog beforehand and
// copied the output to the fixture...
await testGenerateFeeds(
fromPartial({
{
siteDir,
siteConfig,
i18n: DefaultI18N,
outDir,
}),
} as LoadContext,
{
path: 'blog',
routeBasePath: 'blog',
@ -246,48 +238,4 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
).toMatchSnapshot();
fsMock.mockClear();
});
it('has feed item for each post - with trailing slash', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const outDir = path.join(siteDir, 'build-snap');
const siteConfig = {
title: 'Hello',
baseUrl: '/myBaseUrl/',
url: 'https://docusaurus.io',
favicon: 'image/favicon.ico',
trailingSlash: true,
markdown,
};
// Build is quite difficult to mock, so we built the blog beforehand and
// copied the output to the fixture...
await testGenerateFeeds(
fromPartial({
siteDir,
siteConfig,
i18n: DefaultI18N,
outDir,
}),
{
path: 'blog',
routeBasePath: 'blog',
tagsBasePath: 'tags',
authorsMapPath: 'authors.yml',
include: DEFAULT_OPTIONS.include,
exclude: DEFAULT_OPTIONS.exclude,
feedOptions: {
type: [feedType],
copyright: 'Copyright',
},
readingTime: ({content, defaultReadingTime}) =>
defaultReadingTime({content}),
truncateMarker: /<!--\s*truncate\s*-->/,
} as PluginOptions,
);
expect(
fsMock.mock.calls.map((call) => call[1] as string),
).toMatchSnapshot();
fsMock.mockClear();
});
});

View File

@ -8,11 +8,7 @@
import {jest} from '@jest/globals';
import path from 'path';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {
posixPath,
getFileCommitDate,
LAST_UPDATE_FALLBACK,
} from '@docusaurus/utils';
import {posixPath, getFileCommitDate} from '@docusaurus/utils';
import pluginContentBlog from '../index';
import {validateOptions} from '../options';
import type {
@ -20,7 +16,6 @@ import type {
LoadContext,
I18n,
Validate,
MarkdownConfig,
} from '@docusaurus/types';
import type {
BlogPost,
@ -29,25 +24,6 @@ import type {
EditUrlFunction,
} from '@docusaurus/plugin-content-blog';
const markdown: MarkdownConfig = {
format: 'mdx',
mermaid: true,
mdx1Compat: {
comments: true,
headingIds: true,
admonitions: true,
},
parseFrontMatter: async (params) => {
// Reuse the default parser
const result = await params.defaultParseFrontMatter(params);
if (result.frontMatter.title === 'Complex Slug') {
result.frontMatter.custom_frontMatter = 'added by parseFrontMatter';
}
return result;
},
remarkRehypeOptions: undefined,
};
function findByTitle(
blogPosts: BlogPost[],
title: string,
@ -105,7 +81,6 @@ const getPlugin = async (
title: 'Hello',
baseUrl: '/',
url: 'https://docusaurus.io',
markdown,
} as DocusaurusConfig;
return pluginContentBlog(
{
@ -180,6 +155,7 @@ describe('blog plugin', () => {
description: `date inside front matter`,
authors: [],
date: new Date('2019-01-01'),
formattedDate: 'January 1, 2019',
frontMatter: {
date: new Date('2019-01-01'),
tags: ['date'],
@ -224,6 +200,7 @@ describe('blog plugin', () => {
},
],
date: new Date('2018-12-14'),
formattedDate: 'December 14, 2018',
frontMatter: {
authors: [
{
@ -259,12 +236,12 @@ describe('blog plugin', () => {
title: 'Simple Slug',
},
date: new Date('2020-08-16'),
formattedDate: 'August 16, 2020',
frontMatter: {
date: '2020/08/16',
slug: '/hey/my super path/héllô',
title: 'Complex Slug',
tags: ['date', 'complex'],
custom_frontMatter: 'added by parseFrontMatter',
},
tags: [
{
@ -304,6 +281,7 @@ describe('blog plugin', () => {
title: 'draft',
},
date: new Date('2020-08-15'),
formattedDate: 'August 15, 2020',
frontMatter: {
author: 'Sébastien Lorber',
author_title: 'Docusaurus maintainer',
@ -329,6 +307,7 @@ describe('blog plugin', () => {
description: '',
authors: [],
date: new Date('2019-01-02'),
formattedDate: 'January 2, 2019',
frontMatter: {
date: new Date('2019-01-02'),
},
@ -343,6 +322,39 @@ describe('blog plugin', () => {
});
});
it('builds simple website blog with localized dates', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const blogPostsFrench = await getBlogPosts(siteDir, {}, getI18n('fr'));
expect(blogPostsFrench).toHaveLength(10);
expect(blogPostsFrench[0]!.metadata.formattedDate).toMatchInlineSnapshot(
`"23 juillet 2023"`,
);
expect(blogPostsFrench[1]!.metadata.formattedDate).toMatchInlineSnapshot(
`"6 mars 2021"`,
);
expect(blogPostsFrench[2]!.metadata.formattedDate).toMatchInlineSnapshot(
`"5 mars 2021"`,
);
expect(blogPostsFrench[3]!.metadata.formattedDate).toMatchInlineSnapshot(
`"16 août 2020"`,
);
expect(blogPostsFrench[4]!.metadata.formattedDate).toMatchInlineSnapshot(
`"15 août 2020"`,
);
expect(blogPostsFrench[5]!.metadata.formattedDate).toMatchInlineSnapshot(
`"27 février 2020"`,
);
expect(blogPostsFrench[6]!.metadata.formattedDate).toMatchInlineSnapshot(
`"27 février 2020"`,
);
expect(blogPostsFrench[7]!.metadata.formattedDate).toMatchInlineSnapshot(
`"2 janvier 2019"`,
);
expect(blogPostsFrench[8]!.metadata.formattedDate).toMatchInlineSnapshot(
`"1 janvier 2019"`,
);
});
it('handles edit URL with editLocalizedBlogs: true', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const blogPosts = await getBlogPosts(siteDir, {editLocalizedFiles: true});
@ -441,8 +453,13 @@ describe('blog plugin', () => {
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 result = getFileCommitDate(noDateSourceFile, {age: 'oldest'});
const noDateSourceTime = result.date;
const formattedDate = Intl.DateTimeFormat('en', {
day: 'numeric',
month: 'long',
year: 'numeric',
}).format(noDateSourceTime);
expect({
...getByTitle(blogPosts, 'no date').metadata,
@ -456,6 +473,7 @@ describe('blog plugin', () => {
description: `no date`,
authors: [],
date: noDateSourceTime,
formattedDate,
frontMatter: {},
tags: [],
prevItem: undefined,
@ -502,165 +520,4 @@ describe('blog plugin', () => {
expect(blogTags).toMatchSnapshot();
});
it('process blog posts load content', async () => {
const siteDir = path.join(
__dirname,
'__fixtures__',
'website-blog-with-tags',
);
const plugin = await getPlugin(
siteDir,
{
postsPerPage: 1,
processBlogPosts: async ({blogPosts}) =>
blogPosts.filter((blog) => blog.metadata.tags[0]?.label === 'tag1'),
},
DefaultI18N,
);
const {blogPosts, blogTags, blogListPaginated} =
(await plugin.loadContent!())!;
expect(blogListPaginated).toHaveLength(3);
expect(Object.keys(blogTags)).toHaveLength(2);
expect(blogTags).toMatchSnapshot();
expect(blogPosts).toHaveLength(3);
expect(blogPosts).toMatchSnapshot();
});
});
describe('last update', () => {
const siteDir = path.join(
__dirname,
'__fixtures__',
'website-blog-with-last-update',
);
const lastUpdateFor = (date: string) => new Date(date).getTime();
it('author and time', async () => {
const plugin = await getPlugin(
siteDir,
{
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
DefaultI18N,
);
const {blogPosts} = (await plugin.loadContent!())!;
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBe('seb');
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
);
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedBy,
);
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
);
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBe('seb');
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedBy,
);
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
});
it('time only', async () => {
const plugin = await getPlugin(
siteDir,
{
showLastUpdateAuthor: false,
showLastUpdateTime: true,
},
DefaultI18N,
);
const {blogPosts} = (await plugin.loadContent!())!;
expect(blogPosts[0]?.metadata.title).toBe('Author');
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
);
expect(blogPosts[1]?.metadata.title).toBe('Nothing');
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
LAST_UPDATE_FALLBACK.lastUpdatedAt,
);
expect(blogPosts[2]?.metadata.title).toBe('Both');
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
expect(blogPosts[3]?.metadata.title).toBe('Last update date');
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
lastUpdateFor('2021-01-01'),
);
});
it('author only', async () => {
const plugin = await getPlugin(
siteDir,
{
showLastUpdateAuthor: true,
showLastUpdateTime: false,
},
DefaultI18N,
);
const {blogPosts} = (await plugin.loadContent!())!;
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.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.lastUpdatedAt).toBeUndefined();
});
it('none', async () => {
const plugin = await getPlugin(
siteDir,
{
showLastUpdateAuthor: false,
showLastUpdateTime: false,
},
DefaultI18N,
);
const {blogPosts} = (await plugin.loadContent!())!;
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBeUndefined();
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBeUndefined();
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBeUndefined();
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBeUndefined();
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBeUndefined();
});
});

View File

@ -29,6 +29,7 @@ const sampleBlogPosts: BlogPost[] = [
source: '/blog/2021/06/19/hello',
description: '/blog/2021/06/19/hello',
date: new Date(2021, 6, 19),
formattedDate: 'June 19, 2021',
tags: [],
title: 'Hello',
hasTruncateMarker: true,

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {getDataFileData, normalizeUrl} from '@docusaurus/utils';
import {getDataFileData} from '@docusaurus/utils';
import {Joi, URISchema} from '@docusaurus/utils-validation';
import type {BlogContentPaths} from './types';
import type {
@ -68,37 +68,17 @@ export async function getAuthorsMap(params: {
type AuthorsParam = {
frontMatter: BlogPostFrontMatter;
authorsMap: AuthorsMap | undefined;
baseUrl: string;
};
function normalizeImageUrl({
imageURL,
baseUrl,
}: {
imageURL: string | undefined;
baseUrl: string;
}) {
return imageURL?.startsWith('/')
? normalizeUrl([baseUrl, imageURL])
: imageURL;
}
// Legacy v1/early-v2 front matter fields
// We may want to deprecate those in favor of using only frontMatter.authors
function getFrontMatterAuthorLegacy({
baseUrl,
frontMatter,
}: {
baseUrl: string;
frontMatter: BlogPostFrontMatter;
}): Author | undefined {
function getFrontMatterAuthorLegacy(
frontMatter: BlogPostFrontMatter,
): Author | undefined {
const name = frontMatter.author;
const title = frontMatter.author_title ?? frontMatter.authorTitle;
const url = frontMatter.author_url ?? frontMatter.authorURL;
const imageURL = normalizeImageUrl({
imageURL: frontMatter.author_image_url ?? frontMatter.authorImageURL,
baseUrl,
});
const imageURL = frontMatter.author_image_url ?? frontMatter.authorImageURL;
if (name || title || url || imageURL) {
return {
@ -168,26 +148,14 @@ ${Object.keys(authorsMap)
return frontMatterAuthors.map(toAuthor);
}
function fixAuthorImageBaseURL(
authors: Author[],
{baseUrl}: {baseUrl: string},
) {
return authors.map((author) => ({
...author,
imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}),
}));
}
export function getBlogPostAuthors(params: AuthorsParam): Author[] {
const authorLegacy = getFrontMatterAuthorLegacy(params);
const authorLegacy = getFrontMatterAuthorLegacy(params.frontMatter);
const authors = getFrontMatterAuthors(params);
const updatedAuthors = fixAuthorImageBaseURL(authors, params);
if (authorLegacy) {
// Technically, we could allow mixing legacy/authors front matter, but do we
// really want to?
if (updatedAuthors.length > 0) {
if (authors.length > 0) {
throw new Error(
`To declare blog post authors, use the 'authors' front matter in priority.
Don't mix 'authors' with other existing 'author_*' front matter. Choose one or the other, not both at the same time.`,
@ -196,5 +164,5 @@ Don't mix 'authors' with other existing 'author_*' front matter. Choose one or t
return [authorLegacy];
}
return updatedAuthors;
return authors;
}

View File

@ -11,7 +11,7 @@ import _ from 'lodash';
import logger from '@docusaurus/logger';
import readingTime from 'reading-time';
import {
parseMarkdownFile,
parseMarkdownString,
normalizeUrl,
aliasedSitePath,
getEditUrl,
@ -26,11 +26,10 @@ import {
getContentPathList,
isUnlisted,
isDraft,
readLastUpdateData,
} from '@docusaurus/utils';
import {validateBlogPostFrontMatter} from './frontMatter';
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
import type {LoadContext, ParseFrontMatter} from '@docusaurus/types';
import type {LoadContext} from '@docusaurus/types';
import type {
PluginOptions,
ReadingTimeFunction,
@ -58,14 +57,12 @@ export function paginateBlogPosts({
blogTitle,
blogDescription,
postsPerPageOption,
pageBasePath,
}: {
blogPosts: BlogPost[];
basePageUrl: string;
blogTitle: string;
blogDescription: string;
postsPerPageOption: number | 'ALL';
pageBasePath: string;
}): BlogPaginated[] {
const totalCount = blogPosts.length;
const postsPerPage =
@ -76,7 +73,7 @@ export function paginateBlogPosts({
function permalink(page: number) {
return page > 0
? normalizeUrl([basePageUrl, pageBasePath, `${page + 1}`])
? normalizeUrl([basePageUrl, `page/${page + 1}`])
: basePageUrl;
}
@ -114,7 +111,6 @@ export function getBlogTags({
blogTitle: string;
blogDescription: string;
postsPerPageOption: number | 'ALL';
pageBasePath: string;
}): BlogTags {
const groups = groupTaggedItems(
blogPosts,
@ -165,19 +161,29 @@ export function parseBlogFileName(
return {date: undefined, text, slug};
}
async function parseBlogPostMarkdownFile({
filePath,
parseFrontMatter,
}: {
filePath: string;
parseFrontMatter: ParseFrontMatter;
}) {
const fileContent = await fs.readFile(filePath, 'utf-8');
function formatBlogPostDate(
locale: string,
date: Date,
calendar: string,
): string {
try {
const result = await parseMarkdownFile({
filePath,
fileContent,
parseFrontMatter,
return new Intl.DateTimeFormat(locale, {
day: 'numeric',
month: 'long',
year: 'numeric',
timeZone: 'UTC',
calendar,
}).format(date);
} catch (err) {
logger.error`Can't format blog post date "${String(date)}"`;
throw err;
}
}
async function parseBlogPostMarkdownFile(blogSourceAbsolute: string) {
const markdownString = await fs.readFile(blogSourceAbsolute, 'utf-8');
try {
const result = parseMarkdownString(markdownString, {
removeContentTitle: true,
});
return {
@ -185,7 +191,7 @@ async function parseBlogPostMarkdownFile({
frontMatter: validateBlogPostFrontMatter(result.frontMatter),
};
} catch (err) {
logger.error`Error while parsing blog post file path=${filePath}.`;
logger.error`Error while parsing blog post file path=${blogSourceAbsolute}.`;
throw err;
}
}
@ -201,10 +207,7 @@ async function processBlogSourceFile(
authorsMap?: AuthorsMap,
): Promise<BlogPost | undefined> {
const {
siteConfig: {
baseUrl,
markdown: {parseFrontMatter},
},
siteConfig: {baseUrl},
siteDir,
i18n,
} = context;
@ -225,19 +228,10 @@ async function processBlogSourceFile(
const blogSourceAbsolute = path.join(blogDirPath, blogSourceRelative);
const {frontMatter, content, contentTitle, excerpt} =
await parseBlogPostMarkdownFile({
filePath: blogSourceAbsolute,
parseFrontMatter,
});
await parseBlogPostMarkdownFile(blogSourceAbsolute);
const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir);
const lastUpdate = await readLastUpdateData(
blogSourceAbsolute,
options,
frontMatter.last_update,
);
const draft = isDraft({frontMatter});
const unlisted = isUnlisted({frontMatter});
@ -265,11 +259,10 @@ async function processBlogSourceFile(
}
try {
const result = await getFileCommitDate(blogSourceAbsolute, {
const result = getFileCommitDate(blogSourceAbsolute, {
age: 'oldest',
includeAuthor: false,
});
return result.date;
} catch (err) {
logger.warn(err);
@ -278,6 +271,11 @@ async function processBlogSourceFile(
}
const date = await getDate();
const formattedDate = formatBlogPostDate(
i18n.currentLocale,
date,
i18n.localeConfigs[i18n.currentLocale]!.calendar,
);
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
const description = frontMatter.description ?? excerpt ?? '';
@ -321,7 +319,7 @@ async function processBlogSourceFile(
routeBasePath,
tagsRouteBasePath,
]);
const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl});
const authors = getBlogPostAuthors({authorsMap, frontMatter});
return {
id: slug,
@ -332,6 +330,7 @@ async function processBlogSourceFile(
title,
description,
date,
formattedDate,
tags: normalizeFrontMatterTags(tagsBasePath, frontMatter.tags),
readingTime: showReadingTime
? options.readingTime({
@ -344,8 +343,6 @@ async function processBlogSourceFile(
authors,
frontMatter,
unlisted,
lastUpdatedAt: lastUpdate.lastUpdatedAt,
lastUpdatedBy: lastUpdate.lastUpdatedBy,
},
content,
};
@ -431,19 +428,3 @@ export function linkify({
return newContent;
}
export async function applyProcessBlogPosts({
blogPosts,
processBlogPosts,
}: {
blogPosts: BlogPost[];
processBlogPosts: PluginOptions['processBlogPosts'];
}): Promise<BlogPost[]> {
const processedBlogPosts = await processBlogPosts({blogPosts});
if (Array.isArray(processedBlogPosts)) {
return processedBlogPosts;
}
return blogPosts;
}

View File

@ -1,20 +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 useRouteContext from '@docusaurus/useRouteContext';
import type {BlogMetadata} from '@docusaurus/plugin-content-blog';
export function useBlogMetadata(): BlogMetadata {
const routeContext = useRouteContext();
const blogMetadata = routeContext?.data?.blogMetadata;
if (!blogMetadata) {
throw new Error(
"useBlogMetadata() can't be called on the current route because the blog metadata could not be found in route context",
);
}
return blogMetadata as BlogMetadata;
}

View File

@ -11,10 +11,7 @@ import logger from '@docusaurus/logger';
import {Feed, type Author as FeedAuthor} from 'feed';
import * as srcset from 'srcset';
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
import {
blogPostContainerID,
applyTrailingSlash,
} from '@docusaurus/utils-common';
import {blogPostContainerID} from '@docusaurus/utils-common';
import {load as cheerioLoad} from 'cheerio';
import type {DocusaurusConfig} from '@docusaurus/types';
import type {
@ -43,14 +40,8 @@ async function generateBlogFeed({
}
const {feedOptions, routeBasePath} = options;
const {url: siteUrl, baseUrl, title, favicon, trailingSlash} = siteConfig;
const blogBaseUrl = applyTrailingSlash(
normalizeUrl([siteUrl, baseUrl, routeBasePath]),
{
trailingSlash,
baseUrl,
},
);
const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
const blogPostsForFeed =
feedOptions.limit === false || feedOptions.limit === null
@ -94,7 +85,7 @@ async function defaultCreateFeedItems({
siteConfig: DocusaurusConfig;
outDir: string;
}): Promise<BlogFeedItem[]> {
const {url: siteUrl, baseUrl, trailingSlash} = siteConfig;
const {url: siteUrl} = siteConfig;
function toFeedAuthor(author: Author): FeedAuthor {
return {name: author.name, link: author.url, email: author.email};
@ -114,19 +105,13 @@ async function defaultCreateFeedItems({
} = post;
const content = await readOutputHTMLFile(
permalink.replace(baseUrl, ''),
permalink.replace(siteConfig.baseUrl, ''),
outDir,
trailingSlash,
siteConfig.trailingSlash,
);
const $ = cheerioLoad(content);
const blogPostAbsoluteUrl = applyTrailingSlash(
normalizeUrl([siteUrl, permalink]),
{
trailingSlash,
baseUrl,
},
);
const blogPostAbsoluteUrl = normalizeUrl([siteUrl, permalink]);
const toAbsoluteUrl = (src: string) =>
String(new URL(src, blogPostAbsoluteUrl));

View File

@ -4,14 +4,14 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {
ContentVisibilitySchema,
FrontMatterLastUpdateSchema,
FrontMatterTOCHeadingLevels,
FrontMatterTagsSchema,
JoiFrontMatter as Joi, // Custom instance for front matter
URISchema,
validateFrontMatter,
FrontMatterTagsSchema,
FrontMatterTOCHeadingLevels,
ContentVisibilitySchema,
} from '@docusaurus/utils-validation';
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
@ -69,7 +69,6 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
hide_table_of_contents: Joi.boolean(),
...FrontMatterTOCHeadingLevels,
last_update: FrontMatterLastUpdateSchema,
})
.messages({
'deprecate.error':

View File

@ -11,7 +11,6 @@ import {
normalizeUrl,
docuHash,
aliasedSitePath,
aliasedSitePathToRelativePath,
getPluginI18nPath,
posixPath,
addTrailingPathSeparator,
@ -21,12 +20,11 @@ import {
DEFAULT_PLUGIN_ID,
} from '@docusaurus/utils';
import {
generateBlogPosts,
getSourceToPermalink,
getBlogTags,
paginateBlogPosts,
shouldBeListed,
applyProcessBlogPosts,
generateBlogPosts,
} from './blogUtils';
import footnoteIDFixer from './remark/footnoteIDFixer';
import {translateContent, getTranslationFiles} from './translations';
@ -34,12 +32,7 @@ import {createBlogFeedFiles} from './feed';
import {toTagProp, toTagsProp} from './props';
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
import type {
LoadContext,
Plugin,
HtmlTags,
RouteMetadata,
} from '@docusaurus/types';
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
import type {
PluginOptions,
BlogPostFrontMatter,
@ -49,7 +42,6 @@ import type {
BlogTags,
BlogContent,
BlogPaginated,
BlogMetadata,
} from '@docusaurus/plugin-content-blog';
export default async function pluginContentBlog(
@ -115,16 +107,11 @@ export default async function pluginContentBlog(
blogDescription,
blogTitle,
blogSidebarTitle,
pageBasePath,
} = options;
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
let blogPosts = await generateBlogPosts(contentPaths, context, options);
blogPosts = await applyProcessBlogPosts({
blogPosts,
processBlogPosts: options.processBlogPosts,
});
const blogPosts = await generateBlogPosts(contentPaths, context, options);
const listedBlogPosts = blogPosts.filter(shouldBeListed);
if (!blogPosts.length) {
@ -134,10 +121,11 @@ export default async function pluginContentBlog(
blogListPaginated: [],
blogTags: {},
blogTagsListPath,
blogTagsPaginated: [],
};
}
// Collocate next and prev metadata.
// Colocate next and prev metadata.
listedBlogPosts.forEach((blogPost, index) => {
const prevItem = index > 0 ? listedBlogPosts[index - 1] : null;
if (prevItem) {
@ -165,7 +153,6 @@ export default async function pluginContentBlog(
blogDescription,
postsPerPageOption,
basePageUrl: baseBlogUrl,
pageBasePath,
});
const blogTags: BlogTags = getBlogTags({
@ -173,7 +160,6 @@ export default async function pluginContentBlog(
postsPerPageOption,
blogDescription,
blogTitle,
pageBasePath,
});
return {
@ -194,7 +180,6 @@ export default async function pluginContentBlog(
blogArchiveComponent,
routeBasePath,
archiveBasePath,
blogTitle,
} = options;
const {addRoute, createData} = actions;
@ -270,24 +255,6 @@ export default async function pluginContentBlog(
),
);
const blogMetadata: BlogMetadata = {
blogBasePath: normalizeUrl([baseUrl, routeBasePath]),
blogTitle,
};
const blogMetadataPath = await createData(
`blogMetadata-${pluginId}.json`,
JSON.stringify(blogMetadata, null, 2),
);
function createBlogPostRouteMetadata(
blogPostMeta: BlogPostMetadata,
): RouteMetadata {
return {
sourceFilePath: aliasedSitePathToRelativePath(blogPostMeta.source),
lastUpdatedAt: blogPostMeta.lastUpdatedAt,
};
}
// Create routes for blog entries.
await Promise.all(
blogPosts.map(async (blogPost) => {
@ -307,10 +274,6 @@ export default async function pluginContentBlog(
sidebar: aliasedSource(sidebarProp),
content: metadata.source,
},
metadata: createBlogPostRouteMetadata(metadata),
context: {
blogMetadata: aliasedSource(blogMetadataPath),
},
});
blogItemsToMetadata[id] = metadata;

View File

@ -45,15 +45,11 @@ export const DEFAULT_OPTIONS: PluginOptions = {
routeBasePath: 'blog',
tagsBasePath: 'tags',
archiveBasePath: 'archive',
pageBasePath: 'page',
path: 'blog',
editLocalizedFiles: false,
authorsMapPath: 'authors.yml',
readingTime: ({content, defaultReadingTime}) => defaultReadingTime({content}),
sortPosts: 'descending',
showLastUpdateTime: false,
showLastUpdateAuthor: false,
processBlogPosts: async () => undefined,
};
const PluginOptionSchema = Joi.object<PluginOptions>({
@ -63,7 +59,6 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
.allow(null),
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath),
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
pageBasePath: Joi.string().default(DEFAULT_OPTIONS.pageBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
postsPerPage: Joi.alternatives()
@ -137,13 +132,6 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
sortPosts: Joi.string()
.valid('descending', 'ascending')
.default(DEFAULT_OPTIONS.sortPosts),
showLastUpdateTime: Joi.bool().default(DEFAULT_OPTIONS.showLastUpdateTime),
showLastUpdateAuthor: Joi.bool().default(
DEFAULT_OPTIONS.showLastUpdateAuthor,
),
processBlogPosts: Joi.function()
.optional()
.default(() => DEFAULT_OPTIONS.processBlogPosts),
}).default(DEFAULT_OPTIONS);
export function validateOptions({

View File

@ -5,17 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/
/// <reference types="@docusaurus/module-type-aliases" />
declare module '@docusaurus/plugin-content-blog' {
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
import type {MDXOptions} from '@docusaurus/mdx-loader';
import type {
FrontMatterTag,
Tag,
LastUpdateData,
FrontMatterLastUpdate,
} from '@docusaurus/utils';
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types';
import type {Item as FeedItem} from 'feed';
import type {Overwrite} from 'utility-types';
@ -161,8 +154,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
toc_min_heading_level?: number;
/** Maximum TOC heading level. Must be between 2 and 6. */
toc_max_heading_level?: number;
/** Allows overriding the last updated author and/or date. */
last_update?: FrontMatterLastUpdate;
};
export type BlogPostFrontMatterAuthor = Author & {
@ -187,7 +178,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
| BlogPostFrontMatterAuthor
| (string | BlogPostFrontMatterAuthor)[];
export type BlogPostMetadata = LastUpdateData & {
export type BlogPostMetadata = {
/** Path to the Markdown source, with `@site` alias. */
readonly source: string;
/**
@ -199,6 +190,11 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
* into a string.
*/
readonly date: Date;
/**
* Publish date formatted according to the locale, so that the client can
* render the date regardless of the existence of `Intl.DateTimeFormat`.
*/
readonly formattedDate: string;
/** Full link including base URL. */
readonly permalink: string;
/**
@ -337,11 +333,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
defaultReadingTime: ReadingTimeFunction;
},
) => number | undefined;
export type ProcessBlogPostsFn = (params: {
blogPosts: BlogPost[];
}) => Promise<void | BlogPost[]>;
/**
* Plugin options after normalization.
*/
@ -360,15 +351,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
routeBasePath: string;
/**
* URL route for the tags section of your blog. Will be appended to
* `routeBasePath`.
* `routeBasePath`. **DO NOT** include a trailing slash.
*/
tagsBasePath: string;
/**
* URL route for the pages section of your blog. Will be appended to
* `routeBasePath`.
*/
pageBasePath: string;
/**
* URL route for the archive section of your blog. Will be appended to
* `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to
* disable generation of archive.
@ -433,14 +419,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
readingTime: ReadingTimeFunctionOption;
/** Governs the direction of blog post sorting. */
sortPosts: 'ascending' | 'descending';
/** Whether to display the last date the doc was updated. */
showLastUpdateTime: boolean;
/** Whether to display the author who last updated the doc. */
showLastUpdateAuthor: boolean;
/** An optional function which can be used to transform blog posts
* (filter, modify, delete, etc...).
*/
processBlogPosts: ProcessBlogPostsFn;
};
/**
@ -483,13 +461,6 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
blogTagsListPath: string;
};
export type BlogMetadata = {
/** the path to the base of the blog */
blogBasePath: string;
/** title of the overall blog */
blogTitle: string;
};
export type BlogTags = {
[permalink: string]: BlogTag;
};
@ -561,7 +532,6 @@ declare module '@theme/BlogPostPage' {
BlogPostFrontMatter,
BlogSidebar,
PropBlogPostContent,
BlogMetadata,
} from '@docusaurus/plugin-content-blog';
export type FrontMatter = BlogPostFrontMatter;
@ -573,8 +543,6 @@ declare module '@theme/BlogPostPage' {
readonly sidebar: BlogSidebar;
/** Content of this post as an MDX component, with useful metadata. */
readonly content: Content;
/** Metadata about the blog. */
readonly blogMetadata: BlogMetadata;
}
export default function BlogPostPage(props: Props): JSX.Element;
@ -584,10 +552,6 @@ declare module '@theme/BlogPostPage/Metadata' {
export default function BlogPostPageMetadata(): JSX.Element;
}
declare module '@theme/BlogPostPage/StructuredData' {
export default function BlogPostStructuredData(): JSX.Element;
}
declare module '@theme/BlogListPage' {
import type {Content} from '@theme/BlogPostPage';
import type {
@ -610,28 +574,6 @@ declare module '@theme/BlogListPage' {
export default function BlogListPage(props: Props): JSX.Element;
}
declare module '@theme/BlogListPage/StructuredData' {
import type {Content} from '@theme/BlogPostPage';
import type {
BlogSidebar,
BlogPaginatedMetadata,
} from '@docusaurus/plugin-content-blog';
export interface Props {
/** Blog sidebar. */
readonly sidebar: BlogSidebar;
/** Metadata of the current listing page. */
readonly metadata: BlogPaginatedMetadata;
/**
* Array of blog posts included on this page. Every post's metadata is also
* available.
*/
readonly items: readonly {readonly content: Content}[];
}
export default function BlogListPageStructuredData(props: Props): JSX.Element;
}
declare module '@theme/BlogTagsListPage' {
import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
import type {TagsListItem} from '@docusaurus/utils';

View File

@ -1,16 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"noEmit": false,
"composite": true,
"incremental": true,
"tsBuildInfoFile": "./lib/.tsbuildinfo-client",
"moduleResolution": "bundler",
"module": "esnext",
"target": "esnext",
"rootDir": "src",
"outDir": "lib"
},
"include": ["src/client", "src/*.d.ts"],
"exclude": ["**/__tests__/**"]
}

View File

@ -1,6 +1,5 @@
{
"extends": "../../tsconfig.json",
"references": [{"path": "./tsconfig.client.json"}],
"compilerOptions": {
"noEmit": false,
"incremental": true,
@ -9,5 +8,5 @@
"outDir": "lib"
},
"include": ["src"],
"exclude": ["src/client", "**/__tests__/**"]
"exclude": ["**/__tests__/**"]
}

View File

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

View File

@ -11,16 +11,4 @@ module.exports = {
url: 'https://your-docusaurus-site.example.com',
baseUrl: '/',
favicon: 'img/favicon.ico',
markdown: {
parseFrontMatter: async (params) => {
// Reuse the default parser
const result = await params.defaultParseFrontMatter(params);
if (result.frontMatter.last_update?.author) {
result.frontMatter.last_update.author =
result.frontMatter.last_update.author +
' (processed by parseFrontMatter)';
}
return result;
},
},
};

View File

@ -52,6 +52,7 @@ exports[`simple website content 1`] = `
"description": "Images",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "baz",
"pagination_label": "baz pagination_label",
@ -104,6 +105,7 @@ exports[`simple website content 2`] = `
"description": "Hi, Endilie here :)",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "hello",
"sidebar_label": "Hello sidebar_label",
@ -149,6 +151,7 @@ exports[`simple website content 3`] = `
"description": "This is custom description",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"description": "This is custom description",
"id": "bar",
@ -460,7 +463,7 @@ exports[`simple website content: data 1`] = `
"frontMatter": {
"title": "Custom Last Update",
"last_update": {
"author": "Custom Author (processed by parseFrontMatter)",
"author": "Custom Author",
"date": "1/1/2000"
}
}
@ -683,7 +686,7 @@ exports[`simple website content: data 1`] = `
"frontMatter": {
"title": "Last Update Author Only",
"last_update": {
"author": "Custom Author (processed by parseFrontMatter)"
"author": "Custom Author"
}
}
}",
@ -1482,10 +1485,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/hello.md",
},
"modules": {
"content": "@site/docs/hello.md",
},
@ -1495,10 +1494,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/absoluteSlug.md",
},
"modules": {
"content": "@site/docs/slugs/absoluteSlug.md",
},
@ -1516,10 +1511,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/customLastUpdate.md",
},
"modules": {
"content": "@site/docs/customLastUpdate.md",
},
@ -1528,10 +1519,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/doc with space.md",
},
"modules": {
"content": "@site/docs/doc with space.md",
},
@ -1540,10 +1527,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/doc-draft.md",
},
"modules": {
"content": "@site/docs/doc-draft.md",
},
@ -1552,10 +1535,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/doc-unlisted.md",
},
"modules": {
"content": "@site/docs/doc-unlisted.md",
},
@ -1565,10 +1544,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/foo/bar.md",
},
"modules": {
"content": "@site/docs/foo/bar.md",
},
@ -1578,10 +1553,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/foo/baz.md",
},
"modules": {
"content": "@site/docs/foo/baz.md",
},
@ -1591,10 +1562,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/headingAsTitle.md",
},
"modules": {
"content": "@site/docs/headingAsTitle.md",
},
@ -1604,10 +1571,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/rootResolvedSlug.md",
},
"modules": {
"content": "@site/docs/rootResolvedSlug.md",
},
@ -1617,10 +1580,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/ipsum.md",
},
"modules": {
"content": "@site/docs/ipsum.md",
},
@ -1629,10 +1588,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/lastUpdateAuthorOnly.md",
},
"modules": {
"content": "@site/docs/lastUpdateAuthorOnly.md",
},
@ -1641,10 +1596,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/lastUpdateDateOnly.md",
},
"modules": {
"content": "@site/docs/lastUpdateDateOnly.md",
},
@ -1653,10 +1604,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/lorem.md",
},
"modules": {
"content": "@site/docs/lorem.md",
},
@ -1665,10 +1612,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/rootAbsoluteSlug.md",
},
"modules": {
"content": "@site/docs/rootAbsoluteSlug.md",
},
@ -1678,10 +1621,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/rootRelativeSlug.md",
},
"modules": {
"content": "@site/docs/rootRelativeSlug.md",
},
@ -1691,10 +1630,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/rootTryToEscapeSlug.md",
},
"modules": {
"content": "@site/docs/rootTryToEscapeSlug.md",
},
@ -1704,10 +1639,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/resolvedSlug.md",
},
"modules": {
"content": "@site/docs/slugs/resolvedSlug.md",
},
@ -1716,10 +1647,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/relativeSlug.md",
},
"modules": {
"content": "@site/docs/slugs/relativeSlug.md",
},
@ -1728,10 +1655,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/tryToEscapeSlug.md",
},
"modules": {
"content": "@site/docs/slugs/tryToEscapeSlug.md",
},
@ -1740,10 +1663,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/unlisted-category/index.md",
},
"modules": {
"content": "@site/docs/unlisted-category/index.md",
},
@ -1753,10 +1672,6 @@ exports[`simple website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/unlisted-category/unlisted-category-doc.md",
},
"modules": {
"content": "@site/docs/unlisted-category/unlisted-category-doc.md",
},
@ -2056,6 +1971,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Getting started text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "getting-started",
"lastUpdatedAt": undefined,
@ -2083,6 +1999,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Installation text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "installation",
"lastUpdatedAt": undefined,
@ -2113,6 +2030,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Guide 1 text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "guide1",
"sidebar_position": 1,
@ -2146,6 +2064,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Guide 2 text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "guide2",
},
@ -2178,6 +2097,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Guide 2.5 text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "guide2.5",
"sidebar_position": 2.5,
@ -2211,6 +2131,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Guide 3 text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "guide3",
"sidebar_position": 3,
@ -2244,6 +2165,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Guide 4 text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "guide4",
},
@ -2276,6 +2198,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Guide 5 text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"id": "guide5",
},
@ -2308,6 +2231,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "API Overview text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/api-overview",
"lastUpdatedAt": undefined,
@ -2338,6 +2262,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Client API text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/Core APIs/Client API",
"lastUpdatedAt": undefined,
@ -2368,6 +2293,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Server API text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/Core APIs/Server API",
"lastUpdatedAt": undefined,
@ -2398,6 +2324,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Plugin API text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/Extension APIs/Plugin API",
"lastUpdatedAt": undefined,
@ -2428,6 +2355,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "Theme API text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/Extension APIs/Theme API",
"lastUpdatedAt": undefined,
@ -2458,6 +2386,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"description": "API End text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/api-end",
"lastUpdatedAt": undefined,
@ -2637,6 +2566,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"description": "API End text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/api-end",
"lastUpdatedAt": undefined,
@ -2664,6 +2594,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"description": "API Overview text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/api-overview",
"lastUpdatedAt": undefined,
@ -2694,6 +2625,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"description": "Plugin API text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/Extension APIs/Plugin API",
"lastUpdatedAt": undefined,
@ -2724,6 +2656,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"description": "Theme API text",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "API/Extension APIs/Theme API",
"lastUpdatedAt": undefined,
@ -2783,6 +2716,7 @@ exports[`versioned website (community) content 1`] = `
"description": "Team current version (translated)",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"title": "Team title translated",
},
@ -2809,6 +2743,7 @@ exports[`versioned website (community) content 2`] = `
"description": "Team 1.0.0",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "team",
"lastUpdatedAt": undefined,
@ -3028,10 +2963,6 @@ exports[`versioned website (community) content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "i18n/en/docusaurus-plugin-content-docs-community/current/team.md",
},
"modules": {
"content": "@site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md",
},
@ -3059,10 +2990,6 @@ exports[`versioned website (community) content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "community_versioned_docs/version-1.0.0/team.md",
},
"modules": {
"content": "@site/community_versioned_docs/version-1.0.0/team.md",
},
@ -3096,6 +3023,7 @@ exports[`versioned website content 1`] = `
"description": "This is next version of bar.",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"slug": "barSlug",
"tags": [
@ -3146,6 +3074,7 @@ exports[`versioned website content 2`] = `
"description": "Bar 1.0.1 !",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "foo/bar",
"lastUpdatedAt": undefined,
@ -3173,6 +3102,7 @@ exports[`versioned website content 3`] = `
"description": "Hello next !",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"slug": "/",
},
@ -3202,6 +3132,7 @@ exports[`versioned website content 4`] = `
"description": "Hello 1.0.1 !",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {
"slug": "/",
},
@ -3231,6 +3162,7 @@ exports[`versioned website content 5`] = `
"description": "Baz 1.0.0 ! This will be deleted in next subsequent versions.",
"draft": false,
"editUrl": undefined,
"formattedLastUpdatedAt": undefined,
"frontMatter": {},
"id": "foo/baz",
"lastUpdatedAt": undefined,
@ -4270,10 +4202,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md",
},
"modules": {
"content": "@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md",
},
@ -4283,10 +4211,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-1.0.0/foo/bar.md",
},
"modules": {
"content": "@site/versioned_docs/version-1.0.0/foo/bar.md",
},
@ -4296,10 +4220,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-1.0.0/foo/baz.md",
},
"modules": {
"content": "@site/versioned_docs/version-1.0.0/foo/baz.md",
},
@ -4359,10 +4279,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/hello.md",
},
"modules": {
"content": "@site/docs/hello.md",
},
@ -4372,10 +4288,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/absoluteSlug.md",
},
"modules": {
"content": "@site/docs/slugs/absoluteSlug.md",
},
@ -4384,10 +4296,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/foo/bar.md",
},
"modules": {
"content": "@site/docs/foo/bar.md",
},
@ -4397,10 +4305,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/resolvedSlug.md",
},
"modules": {
"content": "@site/docs/slugs/resolvedSlug.md",
},
@ -4409,10 +4313,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/relativeSlug.md",
},
"modules": {
"content": "@site/docs/slugs/relativeSlug.md",
},
@ -4421,10 +4321,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "docs/slugs/tryToEscapeSlug.md",
},
"modules": {
"content": "@site/docs/slugs/tryToEscapeSlug.md",
},
@ -4451,10 +4347,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/absoluteSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md",
},
@ -4463,10 +4355,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/rootResolvedSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/rootResolvedSlug.md",
},
@ -4475,10 +4363,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/rootAbsoluteSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md",
},
@ -4488,10 +4372,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/rootRelativeSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/rootRelativeSlug.md",
},
@ -4500,10 +4380,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/rootTryToEscapeSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md",
},
@ -4512,10 +4388,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/resolvedSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md",
},
@ -4524,10 +4396,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/relativeSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md",
},
@ -4536,10 +4404,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md",
},
"modules": {
"content": "@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md",
},
@ -4566,10 +4430,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-1.0.1/hello.md",
},
"modules": {
"content": "@site/versioned_docs/version-1.0.1/hello.md",
},
@ -4579,10 +4439,6 @@ exports[`versioned website content: route config 1`] = `
{
"component": "@theme/DocItem",
"exact": true,
"metadata": {
"lastUpdatedAt": undefined,
"sourceFilePath": "versioned_docs/version-1.0.1/foo/bar.md",
},
"modules": {
"content": "@site/versioned_docs/version-1.0.1/foo/bar.md",
},

View File

@ -7,13 +7,8 @@
import {jest} from '@jest/globals';
import path from 'path';
import {loadContext} from '@docusaurus/core/src/server/site';
import {
createSlugger,
posixPath,
DEFAULT_PLUGIN_ID,
LAST_UPDATE_FALLBACK,
} from '@docusaurus/utils';
import {loadContext} from '@docusaurus/core/src/server/index';
import {createSlugger, posixPath, DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import {createSidebarsUtils} from '../sidebars/utils';
import {
processDocMetadata,
@ -479,8 +474,9 @@ 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: 1539502055,
formattedLastUpdatedAt: 'Oct 14, 2018',
lastUpdatedBy: 'Author',
tags: [],
unlisted: false,
});
@ -571,13 +567,14 @@ describe('simple site', () => {
description: 'Custom last update',
frontMatter: {
last_update: {
author: 'Custom Author (processed by parseFrontMatter)',
author: 'Custom Author',
date: '1/1/2000',
},
title: 'Custom Last Update',
},
lastUpdatedAt: new Date('1/1/2000').getTime(),
lastUpdatedBy: 'Custom Author (processed by parseFrontMatter)',
lastUpdatedAt: new Date('1/1/2000').getTime() / 1000,
formattedLastUpdatedAt: 'Jan 1, 2000',
lastUpdatedBy: 'Custom Author',
sidebarPosition: undefined,
tags: [],
unlisted: false,
@ -610,12 +607,13 @@ describe('simple site', () => {
description: 'Only custom author, so it will still use the date from Git',
frontMatter: {
last_update: {
author: 'Custom Author (processed by parseFrontMatter)',
author: 'Custom Author',
},
title: 'Last Update Author Only',
},
lastUpdatedAt: LAST_UPDATE_FALLBACK.lastUpdatedAt,
lastUpdatedBy: 'Custom Author (processed by parseFrontMatter)',
lastUpdatedAt: 1539502055,
formattedLastUpdatedAt: 'Oct 14, 2018',
lastUpdatedBy: 'Custom Author',
sidebarPosition: undefined,
tags: [],
unlisted: false,
@ -652,7 +650,8 @@ describe('simple site', () => {
},
title: 'Last Update Date Only',
},
lastUpdatedAt: new Date('1/1/2000').getTime(),
lastUpdatedAt: new Date('1/1/2000').getTime() / 1000,
formattedLastUpdatedAt: 'Jan 1, 2000',
lastUpdatedBy: 'Author',
sidebarPosition: undefined,
tags: [],
@ -686,12 +685,13 @@ describe('simple site', () => {
description: 'Custom last update',
frontMatter: {
last_update: {
author: 'Custom Author (processed by parseFrontMatter)',
author: 'Custom Author',
date: '1/1/2000',
},
title: 'Custom Last Update',
},
lastUpdatedAt: undefined,
formattedLastUpdatedAt: undefined,
lastUpdatedBy: undefined,
sidebarPosition: undefined,
tags: [],

View File

@ -444,19 +444,19 @@ describe('validateDocFrontMatter last_update', () => {
invalidFrontMatters: [
[
{last_update: null},
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
],
[
{last_update: {}},
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
],
[
{last_update: ''},
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
],
[
{last_update: {invalid: 'key'}},
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
],
[
{last_update: {author: 'test author', date: 'I am not a date :('}},

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