mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 03:26:57 +02:00
chore: release Docusaurus 3.1.1 (#9793)
Co-authored-by: Joshua Chen <sidachen2003@gmail.com> Co-authored-by: Joey Clover <joey@popos.local> Co-authored-by: reece-white <93522192+reece-white@users.noreply.github.com> Co-authored-by: Shreesh Nautiyal <114166000+Shreesh09@users.noreply.github.com> Co-authored-by: Nick Gerleman <nick@nickgerleman.com> Co-authored-by: Chongyi Zheng <git@zcy.dev> Co-authored-by: MCR Studio <99176216+mcrstudio@users.noreply.github.com> Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com> Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com> Co-authored-by: Ivan Mar (sOkam!) <7308253+heysokam@users.noreply.github.com> Co-authored-by: c0h1b4 <dwidman@gmail.com> Co-authored-by: Janessa Garrow <janessa.garrow@gmail.com> Co-authored-by: ozaki <29860391+OzakIOne@users.noreply.github.com> Co-authored-by: axmmisaka <6500159+axmmisaka@users.noreply.github.com> Co-authored-by: Tatsunori Uchino <tats.u@live.jp> Co-authored-by: Simen Bekkhus <sbekkhus91@gmail.com> Co-authored-by: Sanjaiyan Parthipan <parthipankalayini@gmail.com> Co-authored-by: Jack Robson <143492403+jack-robson@users.noreply.github.com> Co-authored-by: dawei-wang <dawei-wang@users.noreply.github.com> Co-authored-by: eitsupi <50911393+eitsupi@users.noreply.github.com> fix(create-docusaurus): fix readme docusaurus 2 ref (#9487) fix(theme): fix firefox CSS :has() support bug (#9530) fix(theme): docs html sidebar items should always be visible (#9531) fix: v3 admonitions should support v2 title syntax for nested admonitions (#9535) fix(theme-classic): fixed wrong cursor on dropdown menu in navbar, when window is small (#9398) fix(theme): upgrade prism-react-renderer, fix html script and style tag highlighting (#9567) fix: add v2 retrocompatible support for quoted admonitions (#9570) fix(i18n): complete translations for theme-common.json Brazilian Portuguese (pt-BR) (#9477) fix(content-blog): add baseUrl for author.image_url (#9581) fix(type-aliases): add `title` prop for imported inline SVG React components (#9612) fix(utils): Markdown link replacement with <> but no spaces (#9617) fix(live-codeblock): stabilize react-live transformCode callback, fix editor/preview desync (#9631) fix(cli): output help when no conventional config + no subcommand (#9648) fix CI job (#9604) fix Lint Autofix workflow (#9632) fix(pwa-plugin): upgrade workbox (#9668) fix(create-docusaurus): fix init template code blocks, and little improvements (#9696) fix(theme): allow empty code blocks and live playgrounds (#9704) fix(core): various broken anchor link fixes (#9732) fix: remove old useless mdx typedefs (#9733) fix(theme-common): fix missing code block MagicComments style in Visual Basic (.NET) 16 (#9727) fix(core): conditionally include `hostname` parameter when using… (#9407) fix(create-docusaurus): fix typo in init template sample docs (#9783) fix(mdx-loader): allow spaces before `mdx-code-block` info string (#9776) fix(core): links with target "_blank" should no be checked by the broken link checker (#9788) fix(core): broken links optimization behaves differently than non-optimized logic (#9791)
This commit is contained in:
parent
7b1b89041f
commit
72dcd0d8c5
112 changed files with 3782 additions and 350 deletions
5
.eslintrc.js
vendored
5
.eslintrc.js
vendored
|
@ -203,7 +203,10 @@ module.exports = {
|
|||
})),
|
||||
],
|
||||
'no-template-curly-in-string': WARNING,
|
||||
'no-unused-expressions': [WARNING, {allowTaggedTemplates: true}],
|
||||
'no-unused-expressions': [
|
||||
WARNING,
|
||||
{allowTaggedTemplates: true, allowShortCircuit: true},
|
||||
],
|
||||
'no-useless-escape': WARNING,
|
||||
'no-void': [ERROR, {allowAsStatement: true}],
|
||||
'prefer-destructuring': WARNING,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "new.docusaurus.io",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npx --package netlify-cli netlify dev"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "argos",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Argos visual diff tests",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"useNx": false,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "create-docusaurus",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Create Docusaurus apps easily.",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
|
@ -22,8 +22,8 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"commander": "^5.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docusaurus-2-classic-typescript-template",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
@ -15,8 +15,8 @@
|
|||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/preset-classic": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/preset-classic": "3.1.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
|
@ -24,9 +24,9 @@
|
|||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/tsconfig": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/tsconfig": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"typescript": "~5.2.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "docusaurus-2-classic-template",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
@ -14,8 +14,8 @@
|
|||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/preset-classic": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/preset-classic": "3.1.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
|
@ -23,8 +23,8 @@
|
|||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0"
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
@ -28,7 +28,7 @@ tags: [greetings]
|
|||
|
||||
Congratulations, you have made your first post!
|
||||
|
||||
Feel free to play around and edit this post as much you like.
|
||||
Feel free to play around and edit this post as much as you like.
|
||||
```
|
||||
|
||||
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/cssnano-preset",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Advanced cssnano preset for maximum optimization.",
|
||||
"main": "lib/index.js",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/logger",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "An encapsulated logger for semantically formatting console messages.",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/mdx-loader",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docusaurus Loader for MDX",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -20,9 +20,9 @@
|
|||
"dependencies": {
|
||||
"@babel/parser": "^7.22.7",
|
||||
"@babel/traverse": "^7.22.8",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@mdx-js/mdx": "^3.0.0",
|
||||
"@slorber/remark-comment": "^1.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
|
@ -46,7 +46,7 @@
|
|||
"webpack": "^5.88.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@types/escape-html": "^1.0.2",
|
||||
"@types/mdast": "^4.0.2",
|
||||
"@types/stringify-object": "^3.3.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/module-type-aliases",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.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.1.0",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router-config": "*",
|
||||
|
|
|
@ -262,8 +262,8 @@ declare module '@docusaurus/useRouteContext' {
|
|||
|
||||
declare module '@docusaurus/useBrokenLinks' {
|
||||
export type BrokenLinks = {
|
||||
collectLink: (link: string) => void;
|
||||
collectAnchor: (anchor: string) => void;
|
||||
collectLink: (link: string | undefined) => void;
|
||||
collectAnchor: (anchor: string | undefined) => void;
|
||||
};
|
||||
|
||||
export default function useBrokenLinks(): BrokenLinks;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-client-redirects",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.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.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"eta": "^2.2.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.1.0"
|
||||
"@docusaurus/types": "3.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-blog",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Blog plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-content-blog.d.ts",
|
||||
|
@ -19,13 +19,13 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/mdx-loader": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/mdx-loader": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"feed": "^4.2.2",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-docs",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docs plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": false,
|
||||
|
@ -35,13 +35,13 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/mdx-loader": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/mdx-loader": "3.1.1",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@types/react-router-config": "^5.0.7",
|
||||
"combine-promises": "^1.1.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-content-pages",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Pages plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-content-pages.d.ts",
|
||||
|
@ -18,11 +18,11 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/mdx-loader": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/mdx-loader": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"tslib": "^2.6.0",
|
||||
"webpack": "^5.88.1"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-debug",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Debug plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-debug.d.ts",
|
||||
|
@ -20,9 +20,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"react-json-view-lite": "^1.2.0",
|
||||
"tslib": "^2.6.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-analytics",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Global analytics (analytics.js) plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-gtag",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Global Site Tag (gtag.js) plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-google-tag-manager",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Google Tag Manager (gtm.js) plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-ideal-image",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-ideal-image.d.ts",
|
||||
|
@ -20,12 +20,12 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/lqip-loader": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/lqip-loader": "3.1.1",
|
||||
"@docusaurus/responsive-loader": "^1.7.0",
|
||||
"@docusaurus/theme-translations": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/theme-translations": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@slorber/react-ideal-image": "^0.0.12",
|
||||
"react-waypoint": "^10.3.0",
|
||||
"sharp": "^0.32.3",
|
||||
|
@ -33,7 +33,7 @@
|
|||
"webpack": "^5.88.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"fs-extra": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-pwa",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docusaurus Plugin to add PWA support.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin-pwa.d.ts",
|
||||
|
@ -22,12 +22,12 @@
|
|||
"dependencies": {
|
||||
"@babel/core": "^7.23.3",
|
||||
"@babel/preset-env": "^7.23.3",
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/theme-translations": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-translations": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"babel-loader": "^9.1.3",
|
||||
"clsx": "^2.0.0",
|
||||
"core-js": "^3.31.1",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"workbox-window": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"fs-extra": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/plugin-sitemap",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Simple sitemap generation plugin for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,12 +18,12 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"sitemap": "^7.1.1",
|
||||
"tslib": "^2.6.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/preset-classic",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Classic preset for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -18,19 +18,19 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/plugin-content-blog": "3.1.0",
|
||||
"@docusaurus/plugin-content-docs": "3.1.0",
|
||||
"@docusaurus/plugin-content-pages": "3.1.0",
|
||||
"@docusaurus/plugin-debug": "3.1.0",
|
||||
"@docusaurus/plugin-google-analytics": "3.1.0",
|
||||
"@docusaurus/plugin-google-gtag": "3.1.0",
|
||||
"@docusaurus/plugin-google-tag-manager": "3.1.0",
|
||||
"@docusaurus/plugin-sitemap": "3.1.0",
|
||||
"@docusaurus/theme-classic": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/theme-search-algolia": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0"
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/plugin-content-blog": "3.1.1",
|
||||
"@docusaurus/plugin-content-docs": "3.1.1",
|
||||
"@docusaurus/plugin-content-pages": "3.1.1",
|
||||
"@docusaurus/plugin-debug": "3.1.1",
|
||||
"@docusaurus/plugin-google-analytics": "3.1.1",
|
||||
"@docusaurus/plugin-google-gtag": "3.1.1",
|
||||
"@docusaurus/plugin-google-tag-manager": "3.1.1",
|
||||
"@docusaurus/plugin-sitemap": "3.1.1",
|
||||
"@docusaurus/theme-classic": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-search-algolia": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/remark-plugin-npm2yarn",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Remark plugin for converting npm commands to Yarn commands as tabs.",
|
||||
"main": "lib/index.js",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-classic",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Classic theme for Docusaurus",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-classic.d.ts",
|
||||
|
@ -20,18 +20,18 @@
|
|||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/mdx-loader": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/plugin-content-blog": "3.1.0",
|
||||
"@docusaurus/plugin-content-docs": "3.1.0",
|
||||
"@docusaurus/plugin-content-pages": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/theme-translations": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/mdx-loader": "3.1.1",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/plugin-content-blog": "3.1.1",
|
||||
"@docusaurus/plugin-content-docs": "3.1.1",
|
||||
"@docusaurus/plugin-content-pages": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-translations": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"copy-text-to-clipboard": "^3.2.0",
|
||||
|
@ -47,7 +47,6 @@
|
|||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdx-js__react": "^1.5.5",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/rtlcss": "^3.5.1",
|
||||
|
|
|
@ -867,6 +867,14 @@ declare module '@theme/MDXComponents/Ul' {
|
|||
export default function MDXUl(props: Props): JSX.Element;
|
||||
}
|
||||
|
||||
declare module '@theme/MDXComponents/Li' {
|
||||
import type {ComponentProps} from 'react';
|
||||
|
||||
export interface Props extends ComponentProps<'li'> {}
|
||||
|
||||
export default function MDXLi(props: Props): JSX.Element;
|
||||
}
|
||||
|
||||
declare module '@theme/MDXComponents/Img' {
|
||||
import type {ComponentProps} from 'react';
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ export default function MDXImg(props: Props): JSX.Element {
|
|||
return (
|
||||
// eslint-disable-next-line jsx-a11y/alt-text
|
||||
<img
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
{...props}
|
||||
className={transformImgClassName(props.className)}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* 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 React, {type ReactNode} from 'react';
|
||||
import useBrokenLinks from '@docusaurus/useBrokenLinks';
|
||||
import type {Props} from '@theme/MDXComponents/Li';
|
||||
|
||||
export default function MDXLi(props: Props): ReactNode | undefined {
|
||||
// MDX Footnotes have ids such as <li id="user-content-fn-1-953011">
|
||||
useBrokenLinks().collectAnchor(props.id);
|
||||
|
||||
return <li {...props} />;
|
||||
}
|
|
@ -13,6 +13,7 @@ import MDXPre from '@theme/MDXComponents/Pre';
|
|||
import MDXDetails from '@theme/MDXComponents/Details';
|
||||
import MDXHeading from '@theme/MDXComponents/Heading';
|
||||
import MDXUl from '@theme/MDXComponents/Ul';
|
||||
import MDXLi from '@theme/MDXComponents/Li';
|
||||
import MDXImg from '@theme/MDXComponents/Img';
|
||||
import Admonition from '@theme/Admonition';
|
||||
import Mermaid from '@theme/Mermaid';
|
||||
|
@ -27,6 +28,7 @@ const MDXComponents: MDXComponentsObject = {
|
|||
a: MDXA,
|
||||
pre: MDXPre,
|
||||
ul: MDXUl,
|
||||
li: MDXLi,
|
||||
img: MDXImg,
|
||||
h1: (props: ComponentProps<'h1'>) => <MDXHeading as="h1" {...props} />,
|
||||
h2: (props: ComponentProps<'h2'>) => <MDXHeading as="h2" {...props} />,
|
||||
|
|
|
@ -89,6 +89,9 @@ function DropdownNavbarItemDesktop({
|
|||
aria-haspopup="true"
|
||||
aria-expanded={showDropdown}
|
||||
role="button"
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
href={props.to ? undefined : '#'}
|
||||
className={clsx('navbar__link', className)}
|
||||
{...props}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-common",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Common code for Docusaurus themes.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
@ -30,13 +30,13 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/mdx-loader": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/plugin-content-blog": "3.1.0",
|
||||
"@docusaurus/plugin-content-docs": "3.1.0",
|
||||
"@docusaurus/plugin-content-pages": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/mdx-loader": "3.1.1",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/plugin-content-blog": "3.1.1",
|
||||
"@docusaurus/plugin-content-docs": "3.1.1",
|
||||
"@docusaurus/plugin-content-pages": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@types/history": "^4.7.11",
|
||||
"@types/react": "*",
|
||||
"@types/react-router-config": "*",
|
||||
|
@ -47,8 +47,8 @@
|
|||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ import React, {
|
|||
type ReactElement,
|
||||
} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import useBrokenLinks from '@docusaurus/useBrokenLinks';
|
||||
import useIsBrowser from '@docusaurus/useIsBrowser';
|
||||
import {useCollapsible, Collapsible} from '../Collapsible';
|
||||
import styles from './styles.module.css';
|
||||
|
@ -47,6 +48,8 @@ export function Details({
|
|||
children,
|
||||
...props
|
||||
}: DetailsProps): JSX.Element {
|
||||
useBrokenLinks().collectAnchor(props.id);
|
||||
|
||||
const isBrowser = useIsBrowser();
|
||||
const detailsRef = useRef<HTMLDetailsElement>(null);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ const commentPatterns = {
|
|||
wasm: {start: '\\;\\;', end: ''},
|
||||
tex: {start: '%', end: ''},
|
||||
vb: {start: "['‘’]", end: ''},
|
||||
vbnet: {start: "(?:_\\s*)?['‘’]", end: ''}, // Visual Studio 2019 or later
|
||||
rem: {start: '[Rr][Ee][Mm]\\b', end: ''},
|
||||
f90: {start: '!', end: ''}, // Free format only
|
||||
ml: {start: '\\(\\*', end: '\\*\\)'},
|
||||
|
@ -113,10 +114,11 @@ function getAllMagicCommentDirectiveStyles(
|
|||
return getCommentPattern(['wasm'], magicCommentDirectives);
|
||||
|
||||
case 'vb':
|
||||
case 'vbnet':
|
||||
case 'vba':
|
||||
case 'visual-basic':
|
||||
return getCommentPattern(['vb', 'rem'], magicCommentDirectives);
|
||||
case 'vbnet':
|
||||
return getCommentPattern(['vbnet', 'rem'], magicCommentDirectives);
|
||||
|
||||
case 'batch':
|
||||
return getCommentPattern(['rem'], magicCommentDirectives);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-live-codeblock",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docusaurus live code block component.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-live-codeblock.d.ts",
|
||||
|
@ -23,10 +23,10 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/theme-translations": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-translations": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@philpl/buble": "^0.19.7",
|
||||
"clsx": "^2.0.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@types/buble": "^0.20.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-mermaid",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Mermaid components for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme-mermaid.d.ts",
|
||||
|
@ -33,16 +33,15 @@
|
|||
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"mermaid": "^10.4.0",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdx-js__react": "^1.5.5",
|
||||
"react-test-renderer": "^18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-search-algolia",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Algolia search component for Docusaurus.",
|
||||
"main": "lib/index.js",
|
||||
"sideEffects": [
|
||||
|
@ -34,13 +34,13 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@docsearch/react": "^3.5.2",
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/plugin-content-docs": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/theme-translations": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/plugin-content-docs": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-translations": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"algoliasearch": "^4.18.0",
|
||||
"algoliasearch-helper": "^3.13.3",
|
||||
"clsx": "^2.0.0",
|
||||
|
@ -51,7 +51,7 @@
|
|||
"utility-types": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.1.0"
|
||||
"@docusaurus/module-type-aliases": "3.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/theme-translations",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docusaurus theme translations.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -23,8 +23,8 @@
|
|||
"tslib": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/tsconfig",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Docusaurus base TypeScript configuration.",
|
||||
"main": "tsconfig.json",
|
||||
"publishConfig": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/types",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Common types for Docusaurus packages.",
|
||||
"types": "./src/index.d.ts",
|
||||
"publishConfig": {
|
||||
|
|
5
packages/docusaurus-types/src/routing.d.ts
vendored
5
packages/docusaurus-types/src/routing.d.ts
vendored
|
@ -60,6 +60,11 @@ export type RouteConfig = {
|
|||
routes?: RouteConfig[];
|
||||
/** React router config option: `exact` routes would not match subroutes. */
|
||||
exact?: boolean;
|
||||
/**
|
||||
* React router config option: `strict` routes are sensitive to the presence
|
||||
* of a trailing slash.
|
||||
*/
|
||||
strict?: boolean;
|
||||
/** Used to sort routes. Higher-priority routes will be placed first. */
|
||||
priority?: number;
|
||||
/** Extra props; will be copied to routes.js. */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils-common",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Common (Node/Browser) utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils-validation",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Node validation utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
@ -18,8 +18,8 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"joi": "^17.9.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"tslib": "^2.6.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/utils",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Node utility functions for Docusaurus packages.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"node": ">=18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@types/dedent": "^0.7.0",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/micromatch": "^4.0.2",
|
||||
|
|
|
@ -1251,6 +1251,38 @@ describe('unwrapMdxCodeBlocks', () => {
|
|||
</VersionsProvider>
|
||||
`);
|
||||
});
|
||||
|
||||
it('allow spaces before mdx-code-block info string', () => {
|
||||
expect(
|
||||
unwrapMdxCodeBlocks(dedent`
|
||||
# Title
|
||||
|
||||
\`\`\` mdx-code-block
|
||||
import Comp, {User} from "@site/components/comp"
|
||||
|
||||
<Comp prop="test">
|
||||
<User user={{firstName: "Sébastien"}} />
|
||||
</Comp>
|
||||
|
||||
export const age = 36
|
||||
\`\`\`
|
||||
|
||||
text
|
||||
`),
|
||||
).toEqual(dedent`
|
||||
# Title
|
||||
|
||||
import Comp, {User} from "@site/components/comp"
|
||||
|
||||
<Comp prop="test">
|
||||
<User user={{firstName: "Sébastien"}} />
|
||||
</Comp>
|
||||
|
||||
export const age = 36
|
||||
|
||||
text
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('admonitionTitleToDirectiveLabel', () => {
|
||||
|
|
|
@ -301,6 +301,29 @@ describe('parseURLPath', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('parse anchor', () => {
|
||||
expect(parseURLPath('#anchor')).toEqual({
|
||||
pathname: '/',
|
||||
search: undefined,
|
||||
hash: 'anchor',
|
||||
});
|
||||
expect(parseURLPath('#anchor', '/page')).toEqual({
|
||||
pathname: '/page',
|
||||
search: undefined,
|
||||
hash: 'anchor',
|
||||
});
|
||||
expect(parseURLPath('#')).toEqual({
|
||||
pathname: '/',
|
||||
search: undefined,
|
||||
hash: '',
|
||||
});
|
||||
expect(parseURLPath('#', '/page')).toEqual({
|
||||
pathname: '/page',
|
||||
search: undefined,
|
||||
hash: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('parse hash', () => {
|
||||
expect(parseURLPath('/page')).toEqual({
|
||||
pathname: '/page',
|
||||
|
|
|
@ -70,9 +70,9 @@ export function escapeMarkdownHeadingIds(content: string): string {
|
|||
export function unwrapMdxCodeBlocks(content: string): string {
|
||||
// We only support 3/4 backticks on purpose, should be good enough
|
||||
const regexp3 =
|
||||
/(?<begin>^|\n)```mdx-code-block\n(?<children>.*?)\n```(?<end>\n|$)/gs;
|
||||
/(?<begin>^|\n)```(?<spaces>\x20*)mdx-code-block\n(?<children>.*?)\n```(?<end>\n|$)/gs;
|
||||
const regexp4 =
|
||||
/(?<begin>^|\n)````mdx-code-block\n(?<children>.*?)\n````(?<end>\n|$)/gs;
|
||||
/(?<begin>^|\n)````(?<spaces>\x20*)mdx-code-block\n(?<children>.*?)\n````(?<end>\n|$)/gs;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const replacer = (substring: string, ...args: any[]) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@docusaurus/core",
|
||||
"description": "Easy to Maintain Open Source Documentation Websites",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -43,13 +43,13 @@
|
|||
"@babel/runtime": "^7.22.6",
|
||||
"@babel/runtime-corejs3": "^7.22.6",
|
||||
"@babel/traverse": "^7.22.8",
|
||||
"@docusaurus/cssnano-preset": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/mdx-loader": "3.1.0",
|
||||
"@docusaurus/cssnano-preset": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/mdx-loader": "3.1.1",
|
||||
"@docusaurus/react-loadable": "5.5.2",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/utils-validation": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@docusaurus/utils-validation": "3.1.1",
|
||||
"@slorber/static-site-generator-webpack-plugin": "^4.0.7",
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
|
@ -104,8 +104,8 @@
|
|||
"webpackbar": "^5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.1.0",
|
||||
"@docusaurus/types": "3.1.0",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/types": "3.1.1",
|
||||
"@types/detect-port": "^1.3.3",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-router-config": "^5.0.7",
|
||||
|
|
|
@ -18,11 +18,11 @@ export const createStatefulBrokenLinks = (): StatefulBrokenLinks => {
|
|||
const allAnchors = new Set<string>();
|
||||
const allLinks = new Set<string>();
|
||||
return {
|
||||
collectAnchor: (anchor: string): void => {
|
||||
allAnchors.add(anchor);
|
||||
collectAnchor: (anchor: string | undefined): void => {
|
||||
typeof anchor !== 'undefined' && allAnchors.add(anchor);
|
||||
},
|
||||
collectLink: (link: string): void => {
|
||||
allLinks.add(link);
|
||||
collectLink: (link: string | undefined): void => {
|
||||
typeof link !== 'undefined' && allLinks.add(link);
|
||||
},
|
||||
getCollectedAnchors: (): string[] => [...allAnchors],
|
||||
getCollectedLinks: (): string[] => [...allLinks],
|
||||
|
|
|
@ -140,13 +140,25 @@ function Link(
|
|||
};
|
||||
}, [ioRef, targetLink, IOSupported, isInternal]);
|
||||
|
||||
// It is simple local anchor link targeting current page?
|
||||
const isAnchorLink = targetLink?.startsWith('#') ?? false;
|
||||
const isRegularHtmlLink = !targetLink || !isInternal || isAnchorLink;
|
||||
|
||||
if (!isRegularHtmlLink && !noBrokenLinkCheck) {
|
||||
// See also RR logic:
|
||||
// https://github.com/remix-run/react-router/blob/v5/packages/react-router-dom/modules/Link.js#L47
|
||||
const hasInternalTarget = !props.target || props.target === '_self';
|
||||
|
||||
// Should we use a regular <a> tag instead of React-Router Link component?
|
||||
const isRegularHtmlLink =
|
||||
!targetLink || !isInternal || !hasInternalTarget || isAnchorLink;
|
||||
|
||||
if (!noBrokenLinkCheck && (isAnchorLink || !isRegularHtmlLink)) {
|
||||
brokenLinks.collectLink(targetLink!);
|
||||
}
|
||||
|
||||
if (props.id) {
|
||||
brokenLinks.collectAnchor(props.id);
|
||||
}
|
||||
|
||||
return isRegularHtmlLink ? (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, @docusaurus/no-html-links
|
||||
<a
|
||||
|
|
|
@ -6,13 +6,19 @@
|
|||
*/
|
||||
|
||||
import {jest} from '@jest/globals';
|
||||
import reactRouterConfig from 'react-router-config';
|
||||
import {handleBrokenLinks} from '../brokenLinks';
|
||||
import type {RouteConfig} from '@docusaurus/types';
|
||||
|
||||
type Params = Parameters<typeof handleBrokenLinks>[0];
|
||||
|
||||
// We don't need all the routes attributes for our tests
|
||||
type SimpleRoute = {path: string; routes?: SimpleRoute[]};
|
||||
type SimpleRoute = {
|
||||
path: string;
|
||||
routes?: SimpleRoute[];
|
||||
exact?: boolean;
|
||||
strict?: boolean;
|
||||
};
|
||||
|
||||
// Conveniently apply defaults to function under test
|
||||
async function testBrokenLinks(params: {
|
||||
|
@ -42,6 +48,52 @@ describe('handleBrokenLinks', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('accepts valid non-exact link', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page1', exact: false}, {path: '/page2/'}],
|
||||
collectedLinks: {
|
||||
'/page1': {
|
||||
links: [
|
||||
'/page1',
|
||||
'/page1/',
|
||||
'/page2',
|
||||
'/page2/',
|
||||
'/page1/subPath',
|
||||
'/page2/subPath',
|
||||
],
|
||||
anchors: [],
|
||||
},
|
||||
'/page2/': {
|
||||
links: [
|
||||
'/page1',
|
||||
'/page1/',
|
||||
'/page2',
|
||||
'/page2/',
|
||||
'/page1/subPath',
|
||||
'/page2/subPath',
|
||||
],
|
||||
anchors: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid non-strict link', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page1', strict: false}, {path: '/page2/'}],
|
||||
collectedLinks: {
|
||||
'/page1': {
|
||||
links: ['/page1', '/page1/', '/page2', '/page2/'],
|
||||
anchors: [],
|
||||
},
|
||||
'/page2/': {
|
||||
links: ['/page1', '/page1/', '/page2', '/page2/'],
|
||||
anchors: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid link to uncollected page', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page1'}, {path: '/page2'}],
|
||||
|
@ -86,6 +138,71 @@ describe('handleBrokenLinks', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('accepts valid link with anchor reported with hash prefix', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page1'}, {path: '/page2'}],
|
||||
collectedLinks: {
|
||||
'/page1': {links: ['/page2#page2anchor'], anchors: []},
|
||||
'/page2': {links: [], anchors: ['#page2anchor']},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid links and anchors, sparse arrays', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page1'}, {path: '/page2'}],
|
||||
collectedLinks: {
|
||||
'/page1': {
|
||||
links: [
|
||||
'/page1',
|
||||
// @ts-expect-error: invalid type on purpose
|
||||
undefined,
|
||||
// @ts-expect-error: invalid type on purpose
|
||||
null,
|
||||
// @ts-expect-error: invalid type on purpose
|
||||
42,
|
||||
'/page2',
|
||||
'/page2#page2anchor1',
|
||||
'/page2#page2anchor2',
|
||||
],
|
||||
anchors: [],
|
||||
},
|
||||
'/page2': {
|
||||
links: [],
|
||||
anchors: [
|
||||
'page2anchor1',
|
||||
// @ts-expect-error: invalid type on purpose
|
||||
undefined,
|
||||
// @ts-expect-error: invalid type on purpose
|
||||
null,
|
||||
// @ts-expect-error: invalid type on purpose
|
||||
42,
|
||||
'page2anchor2',
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid link and anchor to collected pages that are not in routes', async () => {
|
||||
// This tests the edge-case of the 404 page:
|
||||
// We don't have a {path: '404.html'} route
|
||||
// But yet we collect links/anchors to it and allow linking to it
|
||||
await testBrokenLinks({
|
||||
routes: [],
|
||||
collectedLinks: {
|
||||
'/page 1': {
|
||||
links: ['/page 2#anchor-page-2'],
|
||||
anchors: ['anchor-page-1'],
|
||||
},
|
||||
'/page 2': {
|
||||
links: ['/page 1#anchor-page-1', '/page%201#anchor-page-1'],
|
||||
anchors: ['anchor-page-2'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid link with querystring + anchor', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page1'}, {path: '/page2'}],
|
||||
|
@ -132,10 +249,75 @@ describe('handleBrokenLinks', () => {
|
|||
'/page%202',
|
||||
'/page%202?age=42',
|
||||
'/page%202?age=42#page2anchor',
|
||||
|
||||
'/some dir/page 3',
|
||||
'/some dir/page 3#page3anchor',
|
||||
'/some%20dir/page%203',
|
||||
'/some%20dir/page%203#page3anchor',
|
||||
'/some%20dir/page 3',
|
||||
'/some dir/page%203',
|
||||
'/some dir/page%203#page3anchor',
|
||||
],
|
||||
anchors: [],
|
||||
},
|
||||
'/page 2': {links: [], anchors: ['page2anchor']},
|
||||
'/some dir/page 3': {links: [], anchors: ['page3anchor']},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid link with anchor with spaces and encoding', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page 1'}, {path: '/page 2'}],
|
||||
collectedLinks: {
|
||||
'/page 1': {
|
||||
links: [
|
||||
'/page 1#a b',
|
||||
'#a b',
|
||||
'#a%20b',
|
||||
'#c d',
|
||||
'#c%20d',
|
||||
|
||||
'/page 2#你好',
|
||||
'/page%202#你好',
|
||||
'/page 2#%E4%BD%A0%E5%A5%BD',
|
||||
'/page%202#%E4%BD%A0%E5%A5%BD',
|
||||
|
||||
'/page 2#schrödingers-cat-principle',
|
||||
'/page%202#schrödingers-cat-principle',
|
||||
'/page 2#schr%C3%B6dingers-cat-principle',
|
||||
'/page%202#schr%C3%B6dingers-cat-principle',
|
||||
],
|
||||
anchors: ['a b', 'c%20d'],
|
||||
},
|
||||
'/page 2': {
|
||||
links: ['/page 1#a b', '/page%201#c d'],
|
||||
anchors: ['你好', '#schr%C3%B6dingers-cat-principle'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid link with empty anchor', async () => {
|
||||
await testBrokenLinks({
|
||||
routes: [{path: '/page 1'}, {path: '/page 2'}],
|
||||
collectedLinks: {
|
||||
'/page 1': {
|
||||
links: [
|
||||
'/page 1',
|
||||
'/page 2',
|
||||
'/page 1#',
|
||||
'/page 2#',
|
||||
'/page 1?age=42#',
|
||||
'/page 2?age=42#',
|
||||
'#',
|
||||
'#',
|
||||
'./page 1#',
|
||||
'./page 2#',
|
||||
],
|
||||
anchors: [],
|
||||
},
|
||||
'/page 2': {links: [], anchors: []},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -161,6 +343,76 @@ describe('handleBrokenLinks', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
it('rejects broken link due to strict matching', async () => {
|
||||
await expect(() =>
|
||||
testBrokenLinks({
|
||||
routes: [
|
||||
{path: '/page1', strict: true},
|
||||
{path: '/page2/', strict: true},
|
||||
],
|
||||
|
||||
collectedLinks: {
|
||||
'/page1': {
|
||||
links: ['/page1', '/page1/', '/page2', '/page2/'],
|
||||
anchors: [],
|
||||
},
|
||||
'/page2/': {
|
||||
links: ['/page1', '/page1/', '/page2', '/page2/'],
|
||||
anchors: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus found broken links!
|
||||
|
||||
Please check the pages of your site in the list below, and make sure you don't reference any path that does not exist.
|
||||
Note: it's possible to ignore broken links with the 'onBrokenLinks' Docusaurus configuration, and let the build pass.
|
||||
|
||||
Exhaustive list of all broken links found:
|
||||
- Broken link on source page path = /page1:
|
||||
-> linking to /page2
|
||||
- Broken link on source page path = /page2/:
|
||||
-> linking to /page2
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects broken link due to strict exact matching', async () => {
|
||||
await expect(() =>
|
||||
testBrokenLinks({
|
||||
routes: [
|
||||
{path: '/page1', exact: true, strict: true},
|
||||
{path: '/page2/', exact: true, strict: true},
|
||||
],
|
||||
|
||||
collectedLinks: {
|
||||
'/page1': {
|
||||
links: ['/page1', '/page1/', '/page2', '/page2/'],
|
||||
anchors: [],
|
||||
},
|
||||
'/page2/': {
|
||||
links: ['/page1', '/page1/', '/page2', '/page2/'],
|
||||
anchors: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus found broken links!
|
||||
|
||||
Please check the pages of your site in the list below, and make sure you don't reference any path that does not exist.
|
||||
Note: it's possible to ignore broken links with the 'onBrokenLinks' Docusaurus configuration, and let the build pass.
|
||||
|
||||
Exhaustive list of all broken links found:
|
||||
- Broken link on source page path = /page1:
|
||||
-> linking to /page1/
|
||||
-> linking to /page2
|
||||
- Broken link on source page path = /page2/:
|
||||
-> linking to /page1/
|
||||
-> linking to /page2
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects broken link with anchor', async () => {
|
||||
await expect(() =>
|
||||
testBrokenLinks({
|
||||
|
@ -225,28 +477,6 @@ describe('handleBrokenLinks', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
it('rejects valid link with empty broken anchor', async () => {
|
||||
await expect(() =>
|
||||
testBrokenLinks({
|
||||
routes: [{path: '/page1'}, {path: '/page2'}],
|
||||
collectedLinks: {
|
||||
'/page1': {links: ['/page2#'], anchors: []},
|
||||
'/page2': {links: [], anchors: []},
|
||||
},
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus found broken anchors!
|
||||
|
||||
Please check the pages of your site in the list below, and make sure you don't reference any anchor that does not exist.
|
||||
Note: it's possible to ignore broken anchors with the 'onBrokenAnchors' Docusaurus configuration, and let the build pass.
|
||||
|
||||
Exhaustive list of all broken anchors found:
|
||||
- Broken anchor on source page path = /page1:
|
||||
-> linking to /page2#
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects valid link with broken anchor + query-string', async () => {
|
||||
await expect(() =>
|
||||
testBrokenLinks({
|
||||
|
@ -610,4 +840,65 @@ describe('handleBrokenLinks', () => {
|
|||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('is performant and minimize calls to matchRoutes', async () => {
|
||||
const matchRoutesMock = jest.spyOn(reactRouterConfig, 'matchRoutes');
|
||||
|
||||
const scale = 100;
|
||||
|
||||
const routes: SimpleRoute[] = [
|
||||
...Array.from<SimpleRoute>({length: scale}).map((_, i) => ({
|
||||
path: `/page${i}`,
|
||||
exact: true,
|
||||
})),
|
||||
...Array.from<SimpleRoute>({length: scale}).map((_, i) => ({
|
||||
path: `/page/nonExact/${i}`,
|
||||
})),
|
||||
...Array.from<SimpleRoute>({length: scale}).fill({
|
||||
path: '/pageDynamic/:subpath1',
|
||||
}),
|
||||
];
|
||||
|
||||
const collectedLinks: Params['collectedLinks'] = Object.fromEntries(
|
||||
Array.from<SimpleRoute>({length: scale}).map((_, i) => [
|
||||
`/page${i}`,
|
||||
{
|
||||
links: [
|
||||
...Array.from<SimpleRoute>({length: scale}).flatMap((_2, j) => [
|
||||
`/page${j}`,
|
||||
`/page/nonExact/${j}`,
|
||||
`/page${j}?age=42`,
|
||||
`/page${j}#anchor${j}`,
|
||||
`/page${j}?age=42#anchor${j}`,
|
||||
`/pageDynamic/subPath${j}`,
|
||||
`/pageDynamic/subPath${j}?age=42`,
|
||||
// `/pageDynamic/subPath${j}#anchor${j}`,
|
||||
// `/pageDynamic/subPath${j}?age=42#anchor${j}`,
|
||||
]),
|
||||
],
|
||||
anchors: Array.from<SimpleRoute>({length: scale}).map(
|
||||
(_2, j) => `anchor${j}`,
|
||||
),
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
// console.time('testBrokenLinks');
|
||||
await testBrokenLinks({
|
||||
routes,
|
||||
collectedLinks,
|
||||
});
|
||||
// console.timeEnd('testBrokenLinks');
|
||||
|
||||
// Idiomatic code calling matchRoutes multiple times is not performant
|
||||
// We try to minimize the calls to this expensive function
|
||||
// Otherwise large sites will have super long execution times
|
||||
// See https://github.com/facebook/docusaurus/issues/9754
|
||||
// See https://twitter.com/sebastienlorber/status/1749392773415858587
|
||||
// We expect no more matchRoutes calls than number of dynamic route links
|
||||
expect(matchRoutesMock).toHaveBeenCalledTimes(scale * 2);
|
||||
// We expect matchRoutes to be called with a reduced number of routes
|
||||
expect(routes).toHaveLength(scale * 3);
|
||||
expect(matchRoutesMock.mock.calls[0]![0]).toHaveLength(scale * 2);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,11 +7,24 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import logger from '@docusaurus/logger';
|
||||
import {matchRoutes} from 'react-router-config';
|
||||
import {parseURLPath, serializeURLPath, type URLPath} from '@docusaurus/utils';
|
||||
import {matchRoutes as reactRouterMatchRoutes} from 'react-router-config';
|
||||
import {
|
||||
addTrailingSlash,
|
||||
parseURLPath,
|
||||
removeTrailingSlash,
|
||||
serializeURLPath,
|
||||
type URLPath,
|
||||
} from '@docusaurus/utils';
|
||||
import {getAllFinalRoutes} from './utils';
|
||||
import type {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
||||
|
||||
function matchRoutes(routeConfig: RouteConfig[], pathname: string) {
|
||||
// @ts-expect-error: React router types RouteConfig with an actual React
|
||||
// component, but we load route components with string paths.
|
||||
// We don't actually access component here, so it's fine.
|
||||
return reactRouterMatchRoutes(routeConfig, pathname);
|
||||
}
|
||||
|
||||
type BrokenLink = {
|
||||
link: string;
|
||||
resolvedLink: string;
|
||||
|
@ -26,72 +39,142 @@ type CollectedLinks = {
|
|||
[pathname: string]: {links: string[]; anchors: string[]};
|
||||
};
|
||||
|
||||
function getBrokenLinksForPage({
|
||||
// We use efficient data structures for performance reasons
|
||||
// See https://github.com/facebook/docusaurus/issues/9754
|
||||
type CollectedLinksNormalized = Map<
|
||||
string,
|
||||
{links: Set<string>; anchors: Set<string>}
|
||||
>;
|
||||
|
||||
type BrokenLinksHelper = {
|
||||
collectedLinks: CollectedLinksNormalized;
|
||||
isPathBrokenLink: (linkPath: URLPath) => boolean;
|
||||
isAnchorBrokenLink: (linkPath: URLPath) => boolean;
|
||||
};
|
||||
|
||||
function createBrokenLinksHelper({
|
||||
collectedLinks,
|
||||
pagePath,
|
||||
pageLinks,
|
||||
routes,
|
||||
}: {
|
||||
collectedLinks: CollectedLinks;
|
||||
pagePath: string;
|
||||
pageLinks: string[];
|
||||
pageAnchors: string[];
|
||||
collectedLinks: CollectedLinksNormalized;
|
||||
routes: RouteConfig[];
|
||||
}): BrokenLink[] {
|
||||
// console.log('routes:', routes);
|
||||
}): BrokenLinksHelper {
|
||||
const validPathnames = new Set(collectedLinks.keys());
|
||||
|
||||
// IMPORTANT: this is an optimization
|
||||
// See https://github.com/facebook/docusaurus/issues/9754
|
||||
// Matching against the route array can be expensive
|
||||
// If the route is already in the valid pathnames,
|
||||
// we can avoid matching against it
|
||||
const remainingRoutes = (function filterRoutes() {
|
||||
// Goal: unit tests should behave the same with this enabled or disabled
|
||||
const disableOptimization = false;
|
||||
if (disableOptimization) {
|
||||
return routes;
|
||||
}
|
||||
// We must consider the "exact" and "strict" match attribute
|
||||
// We can only infer pre-validated pathnames from a route from exact routes
|
||||
const [validPathnameRoutes, otherRoutes] = _.partition(
|
||||
routes,
|
||||
(route) => route.exact && validPathnames.has(route.path),
|
||||
);
|
||||
// If a route is non-strict (non-sensitive to trailing slashes)
|
||||
// We must pre-validate all possible paths
|
||||
validPathnameRoutes.forEach((validPathnameRoute) => {
|
||||
if (!validPathnameRoute.strict) {
|
||||
validPathnames.add(addTrailingSlash(validPathnameRoute.path));
|
||||
validPathnames.add(removeTrailingSlash(validPathnameRoute.path));
|
||||
}
|
||||
});
|
||||
return otherRoutes;
|
||||
})();
|
||||
|
||||
function isPathnameMatchingAnyRoute(pathname: string): boolean {
|
||||
if (matchRoutes(remainingRoutes, pathname).length > 0) {
|
||||
// IMPORTANT: this is an optimization
|
||||
// See https://github.com/facebook/docusaurus/issues/9754
|
||||
// Large Docusaurus sites have many routes!
|
||||
// We try to minimize calls to a possibly expensive matchRoutes function
|
||||
validPathnames.add(pathname);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isPathBrokenLink(linkPath: URLPath) {
|
||||
const matchedRoutes = [linkPath.pathname, decodeURI(linkPath.pathname)]
|
||||
// @ts-expect-error: React router types RouteConfig with an actual React
|
||||
// component, but we load route components with string paths.
|
||||
// We don't actually access component here, so it's fine.
|
||||
.map((l) => matchRoutes(routes, l))
|
||||
.flat();
|
||||
return matchedRoutes.length === 0;
|
||||
const pathnames = [linkPath.pathname, decodeURI(linkPath.pathname)];
|
||||
if (pathnames.some((p) => validPathnames.has(p))) {
|
||||
return false;
|
||||
}
|
||||
if (pathnames.some(isPathnameMatchingAnyRoute)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isAnchorBrokenLink(linkPath: URLPath) {
|
||||
const {pathname, hash} = linkPath;
|
||||
|
||||
// Link has no hash: it can't be a broken anchor link
|
||||
if (hash === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Link has empty hash ("#", "/page#"...): we do not report it as broken
|
||||
// Empty hashes are used for various weird reasons, by us and other users...
|
||||
// See for example: https://github.com/facebook/docusaurus/pull/6003
|
||||
if (hash === '') {
|
||||
return false;
|
||||
}
|
||||
const targetPage =
|
||||
collectedLinks[pathname] || collectedLinks[decodeURI(pathname)];
|
||||
|
||||
collectedLinks.get(pathname) || collectedLinks.get(decodeURI(pathname));
|
||||
// link with anchor to a page that does not exist (or did not collect any
|
||||
// link/anchor) is considered as a broken anchor
|
||||
if (!targetPage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// it's a broken anchor if the target page exists
|
||||
// but the anchor does not exist on that page
|
||||
return !targetPage.anchors.includes(hash);
|
||||
// it's a not broken anchor if the anchor exists on the target page
|
||||
if (
|
||||
targetPage.anchors.has(hash) ||
|
||||
targetPage.anchors.has(decodeURIComponent(hash))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const brokenLinks = pageLinks.flatMap((link) => {
|
||||
return {
|
||||
collectedLinks,
|
||||
isPathBrokenLink,
|
||||
isAnchorBrokenLink,
|
||||
};
|
||||
}
|
||||
|
||||
function getBrokenLinksForPage({
|
||||
pagePath,
|
||||
helper,
|
||||
}: {
|
||||
pagePath: string;
|
||||
helper: BrokenLinksHelper;
|
||||
}): BrokenLink[] {
|
||||
const pageData = helper.collectedLinks.get(pagePath)!;
|
||||
|
||||
const brokenLinks: BrokenLink[] = [];
|
||||
|
||||
pageData.links.forEach((link) => {
|
||||
const linkPath = parseURLPath(link, pagePath);
|
||||
if (isPathBrokenLink(linkPath)) {
|
||||
return [
|
||||
{
|
||||
link,
|
||||
resolvedLink: serializeURLPath(linkPath),
|
||||
anchor: false,
|
||||
},
|
||||
];
|
||||
if (helper.isPathBrokenLink(linkPath)) {
|
||||
brokenLinks.push({
|
||||
link,
|
||||
resolvedLink: serializeURLPath(linkPath),
|
||||
anchor: false,
|
||||
});
|
||||
} else if (helper.isAnchorBrokenLink(linkPath)) {
|
||||
brokenLinks.push({
|
||||
link,
|
||||
resolvedLink: serializeURLPath(linkPath),
|
||||
anchor: true,
|
||||
});
|
||||
}
|
||||
if (isAnchorBrokenLink(linkPath)) {
|
||||
return [
|
||||
{
|
||||
link,
|
||||
resolvedLink: serializeURLPath(linkPath),
|
||||
anchor: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
return brokenLinks;
|
||||
|
@ -112,20 +195,30 @@ function getBrokenLinks({
|
|||
collectedLinks,
|
||||
routes,
|
||||
}: {
|
||||
collectedLinks: CollectedLinks;
|
||||
collectedLinks: CollectedLinksNormalized;
|
||||
routes: RouteConfig[];
|
||||
}): BrokenLinksMap {
|
||||
const filteredRoutes = filterIntermediateRoutes(routes);
|
||||
|
||||
return _.mapValues(collectedLinks, (pageCollectedData, pagePath) =>
|
||||
getBrokenLinksForPage({
|
||||
collectedLinks,
|
||||
pageLinks: pageCollectedData.links,
|
||||
pageAnchors: pageCollectedData.anchors,
|
||||
pagePath,
|
||||
routes: filteredRoutes,
|
||||
}),
|
||||
);
|
||||
const helper = createBrokenLinksHelper({
|
||||
collectedLinks,
|
||||
routes: filteredRoutes,
|
||||
});
|
||||
|
||||
const result: BrokenLinksMap = {};
|
||||
collectedLinks.forEach((_unused, pagePath) => {
|
||||
try {
|
||||
result[pagePath] = getBrokenLinksForPage({
|
||||
pagePath,
|
||||
helper,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to get broken links for page ${pagePath}.`, {
|
||||
cause: e,
|
||||
});
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function brokenLinkMessage(brokenLink: BrokenLink): string {
|
||||
|
@ -277,6 +370,28 @@ function reportBrokenLinks({
|
|||
}
|
||||
}
|
||||
|
||||
// Users might use the useBrokenLinks() API in weird unexpected ways
|
||||
// JS users might call "collectLink(undefined)" for example
|
||||
// TS users might call "collectAnchor('#hash')" with/without #
|
||||
// We clean/normalize the collected data to avoid obscure errors being thrown
|
||||
// We also use optimized data structures for a faster algorithm
|
||||
function normalizeCollectedLinks(
|
||||
collectedLinks: CollectedLinks,
|
||||
): CollectedLinksNormalized {
|
||||
const result: CollectedLinksNormalized = new Map();
|
||||
Object.entries(collectedLinks).forEach(([pathname, pageCollectedData]) => {
|
||||
result.set(pathname, {
|
||||
links: new Set(pageCollectedData.links.filter(_.isString)),
|
||||
anchors: new Set(
|
||||
pageCollectedData.anchors
|
||||
.filter(_.isString)
|
||||
.map((anchor) => (anchor.startsWith('#') ? anchor.slice(1) : anchor)),
|
||||
),
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function handleBrokenLinks({
|
||||
collectedLinks,
|
||||
onBrokenLinks,
|
||||
|
@ -291,6 +406,9 @@ export async function handleBrokenLinks({
|
|||
if (onBrokenLinks === 'ignore' && onBrokenAnchors === 'ignore') {
|
||||
return;
|
||||
}
|
||||
const brokenLinks = getBrokenLinks({routes, collectedLinks});
|
||||
const brokenLinks = getBrokenLinks({
|
||||
routes,
|
||||
collectedLinks: normalizeCollectedLinks(collectedLinks),
|
||||
});
|
||||
reportBrokenLinks({brokenLinks, onBrokenLinks, onBrokenAnchors});
|
||||
}
|
||||
|
|
|
@ -54,7 +54,10 @@ async function choosePort(
|
|||
defaultPort: number,
|
||||
): Promise<number | null> {
|
||||
try {
|
||||
const port = await detect({port: defaultPort, hostname: host});
|
||||
const port = await detect({
|
||||
port: defaultPort,
|
||||
...(host !== 'localhost' && {hostname: host}),
|
||||
});
|
||||
if (port === defaultPort) {
|
||||
return port;
|
||||
}
|
||||
|
|
|
@ -276,6 +276,15 @@ ${JSON.stringify(routeConfig)}`,
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Old stuff
|
||||
* As far as I understand, this is what permits to SSG the 404.html file
|
||||
* This is rendered through the catch-all ComponentCreator("*") route
|
||||
* Note CDNs only understand the 404.html file by convention
|
||||
* The extension probably permits to avoid emitting "/404/index.html"
|
||||
*/
|
||||
const NotFoundRoutePath = '/404.html';
|
||||
|
||||
/**
|
||||
* Routes are prepared into three temp files:
|
||||
*
|
||||
|
@ -296,7 +305,7 @@ export function loadRoutes(
|
|||
routesConfig: '',
|
||||
routesChunkNames: {},
|
||||
registry: {},
|
||||
routesPaths: [normalizeUrl([baseUrl, '404.html'])],
|
||||
routesPaths: [normalizeUrl([baseUrl, NotFoundRoutePath])],
|
||||
};
|
||||
|
||||
// `genRouteCode` would mutate `res`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/eslint-plugin",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "ESLint plugin to enforce best Docusaurus practices.",
|
||||
"main": "lib/index.js",
|
||||
"keywords": [
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@docusaurus/lqip-loader",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Low Quality Image Placeholders (LQIP) loader for webpack.",
|
||||
"main": "lib/index.js",
|
||||
"publishConfig": {
|
||||
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"sharp": "^0.32.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "stylelint-copyright",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"description": "Stylelint plugin to check CSS files for a copyright header.",
|
||||
"main": "lib/index.js",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -65,6 +65,7 @@ datagit
|
|||
Datagit's
|
||||
dedup
|
||||
devto
|
||||
dingers
|
||||
Dmitry
|
||||
docsearch
|
||||
Docsify
|
||||
|
@ -396,3 +397,4 @@ yangshunz
|
|||
Zhou
|
||||
zoomable
|
||||
zpao
|
||||
ödingers
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# Broken Anchors tests
|
||||
|
||||
import Link from '@docusaurus/Link';
|
||||
|
||||
<Link id="test-link-anchor">#test-link-anchor</Link>
|
||||
|
||||
[Markdown link to above anchor](#test-link-anchor)
|
||||
|
||||
[Markdown link to above anchor](#)
|
|
@ -384,16 +384,32 @@ y = times2(x);
|
|||
\end{document}
|
||||
```
|
||||
|
||||
```vbnet title="vbnet.vb"
|
||||
Dim languages As New Set(Of String) From {
|
||||
```vba title="vba.vb"
|
||||
Function Factorial(ByVal n As Long) As Long
|
||||
If n < 0 Then
|
||||
Err.Raise 5 ' Invalid argument
|
||||
End If
|
||||
'highlight-next-line
|
||||
Factorial = 1 ' return value
|
||||
Dim i As Long
|
||||
' highlight-start
|
||||
For i = 2 To n
|
||||
Factorial = Factorial * i
|
||||
Next
|
||||
' highlight-end
|
||||
End Function
|
||||
```
|
||||
|
||||
```vbnet title="vbnet.vb"
|
||||
' highlight-next-line
|
||||
Dim languages As New HashSet(Of String) From {
|
||||
"C#",
|
||||
"Visual Basic",
|
||||
_ ' highlight-start
|
||||
"F#",
|
||||
' highlight-end
|
||||
"PowerShell",
|
||||
' highlight-next-line
|
||||
"TypeScript"
|
||||
_' highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -361,6 +361,37 @@ See [#3309](https://github.com/facebook/docusaurus/issues/3309)
|
|||
|
||||
- [pathname://../dogfooding/javadoc/index.html](pathname://../dogfooding/javadoc/index.html)
|
||||
|
||||
### Linking to non-SPA page with Link component
|
||||
|
||||
See [#9758](https://github.com/facebook/docusaurus/issues/9758), these external urls should not be reported by the broken links checker:
|
||||
|
||||
```mdx-code-block
|
||||
import Link from '@docusaurus/Link';
|
||||
|
||||
export function TestLink({noCheck, ...props}) {
|
||||
return (
|
||||
<Link {...props} data-noBrokenLinkCheck={noCheck}>
|
||||
{(noCheck ? '❌' : '✅') +
|
||||
' ' +
|
||||
(props.to ?? props.href) +
|
||||
(props.target ? ` (target=${props.target})` : '')}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- <TestLink to="pathname:///dogfooding/javadoc#goodlink1" />
|
||||
- <TestLink href="pathname:///dogfooding/javadoc#goodlink2" />
|
||||
- <TestLink to="/dogfooding/javadoc#goodlink3" target="_blank" />
|
||||
- <TestLink href="/dogfooding/javadoc#goodlink4" target="_blank" />
|
||||
|
||||
These links are broken (try to single click on them) and should be reported. We need to explicitly disable the broken link checker for them:
|
||||
|
||||
- <TestLink to="/dogfooding/javadoc#badlink1" noCheck />
|
||||
- <TestLink href="/dogfooding/javadoc#badlink2" noCheck />
|
||||
- <TestLink to="/dogfooding/javadoc#badlink3" target="_self" noCheck />
|
||||
- <TestLink href="/dogfooding/javadoc#badlink4" target="_self" noCheck />
|
||||
|
||||
### Linking to JSON
|
||||
|
||||
- [./script.js](./_script.js)
|
||||
|
|
|
@ -14,7 +14,7 @@ image: ./img/social-card.png
|
|||
|
||||
[DocSearch](https://docsearch.algolia.com/) is migrating to a new, more powerful system, which gives users their own Algolia application and new credentials.
|
||||
|
||||
Docusaurus site owners should upgrade their configuration with [their new credentials](#im-using-docusaurus-and-docsearch-can-i-migrate) **by February 1, 2022**, existing search indexes will be frozen and become read-only after this date.
|
||||
Docusaurus site owners should upgrade their configuration with their new credentials **by February 1, 2022**, existing search indexes will be frozen and become read-only after this date.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
|
@ -92,7 +92,7 @@ And of course, **a lot more, for free**.
|
|||
|
||||
## FAQ
|
||||
|
||||
### I'm using Docusaurus and DocSearch, can I migrate?
|
||||
### I'm using Docusaurus and DocSearch, can I migrate? {#im-using-docusaurus-and-docsearch-can-i-migrate}
|
||||
|
||||
At the time we are writing this, we are still at an early stage of the migration. We are doing small batches every week but will increase the load shortly, so please be patient and keep an eye out in your mailbox, you'll be contacted as soon as your Algolia app is ready!
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ website
|
|||
|
||||
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target `@theme/Navbar` is pointing to!
|
||||
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](../swizzling.mdx#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
|
||||
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ npm install --save @docusaurus/plugin-content-blog
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the [preset options](#ex-config-preset).
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ npm install --save @docusaurus/plugin-content-docs
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ npm install --save @docusaurus/plugin-content-pages
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ npm install --save @docusaurus/plugin-debug
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ npm install --save @docusaurus/plugin-google-analytics
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ npm install --save @docusaurus/plugin-google-gtag
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ npm install --save @docusaurus/plugin-google-tag-manager
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ npm install --save @docusaurus/plugin-sitemap
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the [preset options](#ex-config-preset).
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -627,24 +627,18 @@ Usage example:
|
|||
```js title="MyHeading.js"
|
||||
import useBrokenLinks from '@docusaurus/useBrokenLinks';
|
||||
|
||||
export default function MyHeading({id, ...props}): JSX.Element {
|
||||
const brokenLinks = useBrokenLinks();
|
||||
|
||||
brokenLinks.collectAnchor(id);
|
||||
|
||||
return <h2 id={id}>Heading</h2>;
|
||||
export default function MyHeading(props) {
|
||||
useBrokenLinks().collectAnchor(props.id);
|
||||
return <h2 {...props} />;
|
||||
}
|
||||
```
|
||||
|
||||
```js title="MyLink.js"
|
||||
import useBrokenLinks from '@docusaurus/useBrokenLinks';
|
||||
|
||||
export default function MyLink({targetLink, ...props}): JSX.Element {
|
||||
const brokenLinks = useBrokenLinks();
|
||||
|
||||
brokenLinks.collectLink(targetLink);
|
||||
|
||||
return <a href={targetLink}>Link</a>;
|
||||
export default function MyLink(props) {
|
||||
useBrokenLinks().collectLink(props.href);
|
||||
return <a {...props} />;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -61,11 +61,11 @@ export default {
|
|||
};
|
||||
```
|
||||
|
||||
If you use the doc shorthand or [autogenerated](#sidebar-item-autogenerated) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
|
||||
If you use the doc shorthand or [autogenerated](autogenerated.mdx) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
|
||||
|
||||
:::note
|
||||
|
||||
A `doc` item sets an [implicit sidebar association](#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
|
||||
A `doc` item sets an [implicit sidebar association](./multiple-sidebars.mdx#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ The migration CLI migrates:
|
|||
|
||||
To use the migration CLI, follow these steps:
|
||||
|
||||
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the [structure](#) shown at the start of this page.
|
||||
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the expected structure.
|
||||
|
||||
2. To migrate your v1 website, run the migration CLI with the appropriate filesystem paths:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "website",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
@ -36,19 +36,19 @@
|
|||
"dependencies": {
|
||||
"@crowdin/cli": "^3.13.0",
|
||||
"@crowdin/crowdin-api-client": "^1.23.3",
|
||||
"@docusaurus/core": "3.1.0",
|
||||
"@docusaurus/logger": "3.1.0",
|
||||
"@docusaurus/plugin-client-redirects": "3.1.0",
|
||||
"@docusaurus/plugin-ideal-image": "3.1.0",
|
||||
"@docusaurus/plugin-pwa": "3.1.0",
|
||||
"@docusaurus/preset-classic": "3.1.0",
|
||||
"@docusaurus/remark-plugin-npm2yarn": "3.1.0",
|
||||
"@docusaurus/theme-classic": "3.1.0",
|
||||
"@docusaurus/theme-common": "3.1.0",
|
||||
"@docusaurus/theme-live-codeblock": "3.1.0",
|
||||
"@docusaurus/theme-mermaid": "3.1.0",
|
||||
"@docusaurus/utils": "3.1.0",
|
||||
"@docusaurus/utils-common": "3.1.0",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/logger": "3.1.1",
|
||||
"@docusaurus/plugin-client-redirects": "3.1.1",
|
||||
"@docusaurus/plugin-ideal-image": "3.1.1",
|
||||
"@docusaurus/plugin-pwa": "3.1.1",
|
||||
"@docusaurus/preset-classic": "3.1.1",
|
||||
"@docusaurus/remark-plugin-npm2yarn": "3.1.1",
|
||||
"@docusaurus/theme-classic": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-live-codeblock": "3.1.1",
|
||||
"@docusaurus/theme-mermaid": "3.1.1",
|
||||
"@docusaurus/utils": "3.1.1",
|
||||
"@docusaurus/utils-common": "3.1.1",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@swc/core": "1.2.197",
|
||||
"clsx": "^2.0.0",
|
||||
|
@ -83,8 +83,8 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/eslint-plugin": "3.1.0",
|
||||
"@docusaurus/tsconfig": "3.1.0",
|
||||
"@docusaurus/eslint-plugin": "3.1.1",
|
||||
"@docusaurus/tsconfig": "3.1.1",
|
||||
"@types/color": "^3.0.4",
|
||||
"@types/jest": "^29.5.3",
|
||||
"cross-env": "^7.0.3",
|
||||
|
|
|
@ -13,6 +13,7 @@ import React, {
|
|||
useRef,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import useBrokenLinks from '@docusaurus/useBrokenLinks';
|
||||
import {useHistory} from '@docusaurus/router';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
|
@ -41,6 +42,7 @@ function APITableRow(
|
|||
const id = name ? `${name}-${entryName}` : entryName;
|
||||
const anchor = `#${id}`;
|
||||
const history = useHistory();
|
||||
useBrokenLinks().collectAnchor(id);
|
||||
return (
|
||||
<tr
|
||||
id={id}
|
||||
|
|
|
@ -33,7 +33,7 @@ website
|
|||
|
||||
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target `@theme/Navbar` is pointing to!
|
||||
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](../swizzling.mdx#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
|
||||
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ npm install --save @docusaurus/plugin-content-blog
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the [preset options](#ex-config-preset).
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ npm install --save @docusaurus/plugin-content-docs
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ npm install --save @docusaurus/plugin-content-pages
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ npm install --save @docusaurus/plugin-debug
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ npm install --save @docusaurus/plugin-google-analytics
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ npm install --save @docusaurus/plugin-google-gtag
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ npm install --save @docusaurus/plugin-google-tag-manager
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ npm install --save @docusaurus/plugin-sitemap
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the [preset options](#ex-config-preset).
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -61,11 +61,11 @@ module.exports = {
|
|||
};
|
||||
```
|
||||
|
||||
If you use the doc shorthand or [autogenerated](#sidebar-item-autogenerated) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
|
||||
If you use the doc shorthand or [autogenerated](autogenerated.mdx) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
|
||||
|
||||
:::note
|
||||
|
||||
A `doc` item sets an [implicit sidebar association](#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
|
||||
A `doc` item sets an [implicit sidebar association](./multiple-sidebars.mdx#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ The migration CLI migrates:
|
|||
|
||||
To use the migration CLI, follow these steps:
|
||||
|
||||
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the [structure](#) shown at the start of this page.
|
||||
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the expected structure.
|
||||
|
||||
2. To migrate your v1 website, run the migration CLI with the appropriate filesystem paths:
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ website
|
|||
|
||||
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target `@theme/Navbar` is pointing to!
|
||||
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](../swizzling.mdx#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
|
||||
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ npm install --save @docusaurus/plugin-content-blog
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the [preset options](#ex-config-preset).
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ npm install --save @docusaurus/plugin-content-docs
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ npm install --save @docusaurus/plugin-content-pages
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ npm install --save @docusaurus/plugin-debug
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ npm install --save @docusaurus/plugin-google-analytics
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ npm install --save @docusaurus/plugin-google-gtag
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ npm install --save @docusaurus/plugin-google-tag-manager
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the preset options.
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ npm install --save @docusaurus/plugin-sitemap
|
|||
|
||||
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
|
||||
|
||||
You can configure this plugin through the [preset options](#ex-config-preset).
|
||||
You can configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -61,11 +61,11 @@ export default {
|
|||
};
|
||||
```
|
||||
|
||||
If you use the doc shorthand or [autogenerated](#sidebar-item-autogenerated) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
|
||||
If you use the doc shorthand or [autogenerated](autogenerated.mdx) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
|
||||
|
||||
:::note
|
||||
|
||||
A `doc` item sets an [implicit sidebar association](#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
|
||||
A `doc` item sets an [implicit sidebar association](./multiple-sidebars.mdx#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ The migration CLI migrates:
|
|||
|
||||
To use the migration CLI, follow these steps:
|
||||
|
||||
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the [structure](#) shown at the start of this page.
|
||||
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the expected structure.
|
||||
|
||||
2. To migrate your v1 website, run the migration CLI with the appropriate filesystem paths:
|
||||
|
||||
|
|
184
website/versioned_docs/version-3.1.0/advanced/client.mdx
Normal file
184
website/versioned_docs/version-3.1.0/advanced/client.mdx
Normal file
|
@ -0,0 +1,184 @@
|
|||
---
|
||||
description: How the Docusaurus client is structured
|
||||
---
|
||||
|
||||
# Client architecture
|
||||
|
||||
## Theme aliases {#theme-aliases}
|
||||
|
||||
A theme works by exporting a set of components, e.g. `Navbar`, `Layout`, `Footer`, to render the data passed down from plugins. Docusaurus and users use these components by importing them using the `@theme` webpack alias:
|
||||
|
||||
```js
|
||||
import Navbar from '@theme/Navbar';
|
||||
```
|
||||
|
||||
The alias `@theme` can refer to a few directories, in the following priority:
|
||||
|
||||
1. A user's `website/src/theme` directory, which is a special directory that has the higher precedence.
|
||||
2. A Docusaurus theme package's `theme` directory.
|
||||
3. Fallback components provided by Docusaurus core (usually not needed).
|
||||
|
||||
This is called a _layered architecture_: a higher-priority layer providing the component would shadow a lower-priority layer, making swizzling possible. Given the following structure:
|
||||
|
||||
```
|
||||
website
|
||||
├── node_modules
|
||||
│ └── @docusaurus/theme-classic
|
||||
│ └── theme
|
||||
│ └── Navbar.js
|
||||
└── src
|
||||
└── theme
|
||||
└── Navbar.js
|
||||
```
|
||||
|
||||
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target `@theme/Navbar` is pointing to!
|
||||
|
||||
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](../swizzling.mdx#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
|
||||
|
||||
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
|
||||
|
||||
```js
|
||||
import InitialCodeBlock from '@theme-init/CodeBlock';
|
||||
import React from 'react';
|
||||
|
||||
export default function CodeBlock(props) {
|
||||
return props.live ? (
|
||||
<ReactLivePlayground {...props} />
|
||||
) : (
|
||||
<InitialCodeBlock {...props} />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Check the code of `@docusaurus/theme-live-codeblock` for details.
|
||||
|
||||
:::warning
|
||||
|
||||
Unless you want to publish a re-usable "theme enhancer" (like `@docusaurus/theme-live-codeblock`), you likely don't need `@theme-init`.
|
||||
|
||||
:::
|
||||
|
||||
It can be quite hard to wrap your mind around these aliases. Let's imagine the following case with a super convoluted setup with three themes/plugins and the site itself all trying to define the same component. Internally, Docusaurus loads these themes as a "stack".
|
||||
|
||||
```text
|
||||
+-------------------------------------------------+
|
||||
| `website/src/theme/CodeBlock.js` | <-- `@theme/CodeBlock` always points to the top
|
||||
+-------------------------------------------------+
|
||||
| `theme-live-codeblock/theme/CodeBlock/index.js` | <-- `@theme-original/CodeBlock` points to the topmost non-swizzled component
|
||||
+-------------------------------------------------+
|
||||
| `plugin-awesome-codeblock/theme/CodeBlock.js` |
|
||||
+-------------------------------------------------+
|
||||
| `theme-classic/theme/CodeBlock/index.js` | <-- `@theme-init/CodeBlock` always points to the bottom
|
||||
+-------------------------------------------------+
|
||||
```
|
||||
|
||||
The components in this "stack" are pushed in the order of `preset plugins > preset themes > plugins > themes > site`, so the swizzled component in `website/src/theme` always comes out on top because it's loaded last.
|
||||
|
||||
`@theme/*` always points to the topmost component—when `CodeBlock` is swizzled, all other components requesting `@theme/CodeBlock` receive the swizzled version.
|
||||
|
||||
`@theme-original/*` always points to the topmost non-swizzled component. That's why you can import `@theme-original/CodeBlock` in the swizzled component—it points to the next one in the "component stack", a theme-provided one. Plugin authors should not try to use this because your component could be the topmost component and cause a self-import.
|
||||
|
||||
`@theme-init/*` always points to the bottommost component—usually, this comes from the theme or plugin that first provides this component. Individual plugins / themes trying to enhance code block can safely use `@theme-init/CodeBlock` to get its basic version. Site creators should generally not use this because you likely want to enhance the _topmost_ instead of the _bottommost_ component. It's also possible that the `@theme-init/CodeBlock` alias does not exist at all—Docusaurus only creates it when it points to a different one from `@theme-original/CodeBlock`, i.e. when it's provided by more than one theme. We don't waste aliases!
|
||||
|
||||
## Client modules {#client-modules}
|
||||
|
||||
Client modules are part of your site's bundle, just like theme components. However, they are usually side-effect-ful. Client modules are anything that can be `import`ed by Webpack—CSS, JS, etc. JS scripts usually work on the global context, like registering event listeners, creating global variables...
|
||||
|
||||
These modules are imported globally before React even renders the initial UI.
|
||||
|
||||
```js title="@docusaurus/core/App.tsx"
|
||||
// How it works under the hood
|
||||
import '@generated/client-modules';
|
||||
```
|
||||
|
||||
Plugins and sites can both declare client modules, through [`getClientModules`](../api/plugin-methods/lifecycle-apis.mdx#getClientModules) and [`siteConfig.clientModules`](../api/docusaurus.config.js.mdx#clientModules), respectively.
|
||||
|
||||
Client modules are called during server-side rendering as well, so remember to check the [execution environment](./ssg.mdx#escape-hatches) before accessing client-side globals.
|
||||
|
||||
```js title="mySiteGlobalJs.js"
|
||||
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
// As soon as the site loads in the browser, register a global event listener
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Period') {
|
||||
location.assign(location.href.replace('.com', '.dev'));
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
CSS stylesheets imported as client modules are [global](../styling-layout.mdx#global-styles).
|
||||
|
||||
```css title="mySiteGlobalCss.css"
|
||||
/* This stylesheet is global. */
|
||||
.globalSelector {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
### Client module lifecycles {#client-module-lifecycles}
|
||||
|
||||
Besides introducing side-effects, client modules can optionally export two lifecycle functions: `onRouteUpdate` and `onRouteDidUpdate`.
|
||||
|
||||
Because Docusaurus builds a single-page application, `script` tags will only be executed the first time the page loads, but will not re-execute on page transitions. These lifecycles are useful if you have some imperative JS logic that should execute every time a new page has loaded, e.g., to manipulate DOM elements, to send analytics data, etc.
|
||||
|
||||
For every route transition, there will be several important timings:
|
||||
|
||||
1. The user clicks a link, which causes the router to change its current location.
|
||||
2. Docusaurus preloads the next route's assets, while keeping displaying the current page's content.
|
||||
3. The next route's assets have loaded.
|
||||
4. The new location's route component gets rendered to DOM.
|
||||
|
||||
`onRouteUpdate` will be called at event (2), and `onRouteDidUpdate` will be called at (4). They both receive the current location and the previous location (which can be `null`, if this is the first screen).
|
||||
|
||||
`onRouteUpdate` can optionally return a "cleanup" callback, which will be called at (3). For example, if you want to display a progress bar, you can start a timeout in `onRouteUpdate`, and clear the timeout in the callback. (The classic theme already provides an `nprogress` integration this way.)
|
||||
|
||||
Note that the new page's DOM is only available during event (4). If you need to manipulate the new page's DOM, you'll likely want to use `onRouteDidUpdate`, which will be fired as soon as the DOM on the new page has mounted.
|
||||
|
||||
```js title="myClientModule.js"
|
||||
export function onRouteDidUpdate({location, previousLocation}) {
|
||||
// Don't execute if we are still on the same page; the lifecycle may be fired
|
||||
// because the hash changes (e.g. when navigating between headings)
|
||||
if (location.pathname !== previousLocation?.pathname) {
|
||||
const title = document.getElementsByTagName('h1')[0];
|
||||
if (title) {
|
||||
title.innerText += '❤️';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function onRouteUpdate({location, previousLocation}) {
|
||||
if (location.pathname !== previousLocation?.pathname) {
|
||||
const progressBarTimeout = window.setTimeout(() => {
|
||||
nprogress.start();
|
||||
}, delay);
|
||||
return () => window.clearTimeout(progressBarTimeout);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
```
|
||||
|
||||
Or, if you are using TypeScript and you want to leverage contextual typing:
|
||||
|
||||
```ts title="myClientModule.ts"
|
||||
import type {ClientModule} from '@docusaurus/types';
|
||||
|
||||
const module: ClientModule = {
|
||||
onRouteUpdate({location, previousLocation}) {
|
||||
// ...
|
||||
},
|
||||
onRouteDidUpdate({location, previousLocation}) {
|
||||
// ...
|
||||
},
|
||||
};
|
||||
export default module;
|
||||
```
|
||||
|
||||
Both lifecycles will fire on first render, but they will not fire on server-side, so you can safely access browser globals in them.
|
||||
|
||||
:::tip Prefer using React
|
||||
|
||||
Client module lifecycles are purely imperative, and you can't use React hooks or access React contexts within them. If your operations are state-driven or involve complicated DOM manipulations, you should consider [swizzling components](../swizzling.mdx) instead.
|
||||
|
||||
:::
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue