diff --git a/.eslintrc.js b/.eslintrc.js index 6a0b15228fd0..c6cea664f8dd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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, diff --git a/admin/new.docusaurus.io/package.json b/admin/new.docusaurus.io/package.json index 9ff47cd054d5..db29486a4596 100644 --- a/admin/new.docusaurus.io/package.json +++ b/admin/new.docusaurus.io/package.json @@ -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" diff --git a/argos/package.json b/argos/package.json index 437f323dcd37..2e081d8685b0 100644 --- a/argos/package.json +++ b/argos/package.json @@ -1,6 +1,6 @@ { "name": "argos", - "version": "3.1.0", + "version": "3.1.1", "description": "Argos visual diff tests", "license": "MIT", "private": true, diff --git a/lerna.json b/lerna.json index 54946d797440..b198438b4baa 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "3.1.0", + "version": "3.1.1", "npmClient": "yarn", "useWorkspaces": true, "useNx": false, diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json index 9722cf2c6ab3..3e840d6cac78 100755 --- a/packages/create-docusaurus/package.json +++ b/packages/create-docusaurus/package.json @@ -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", diff --git a/packages/create-docusaurus/templates/classic-typescript/package.json b/packages/create-docusaurus/templates/classic-typescript/package.json index b9f64f45d1a5..5e0a3432e4d1 100644 --- a/packages/create-docusaurus/templates/classic-typescript/package.json +++ b/packages/create-docusaurus/templates/classic-typescript/package.json @@ -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": { diff --git a/packages/create-docusaurus/templates/classic/package.json b/packages/create-docusaurus/templates/classic/package.json index cd100edb37b4..3b491abda561 100644 --- a/packages/create-docusaurus/templates/classic/package.json +++ b/packages/create-docusaurus/templates/classic/package.json @@ -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": [ diff --git a/packages/create-docusaurus/templates/shared/docs/tutorial-basics/create-a-blog-post.md b/packages/create-docusaurus/templates/shared/docs/tutorial-basics/create-a-blog-post.md index ea472bbaf871..550ae17ee1a2 100644 --- a/packages/create-docusaurus/templates/shared/docs/tutorial-basics/create-a-blog-post.md +++ b/packages/create-docusaurus/templates/shared/docs/tutorial-basics/create-a-blog-post.md @@ -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). diff --git a/packages/docusaurus-cssnano-preset/package.json b/packages/docusaurus-cssnano-preset/package.json index 8c39736b9890..918fddedad75 100644 --- a/packages/docusaurus-cssnano-preset/package.json +++ b/packages/docusaurus-cssnano-preset/package.json @@ -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", diff --git a/packages/docusaurus-logger/package.json b/packages/docusaurus-logger/package.json index 5aeb4fb18652..f9fcb4f6f4be 100644 --- a/packages/docusaurus-logger/package.json +++ b/packages/docusaurus-logger/package.json @@ -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": { diff --git a/packages/docusaurus-mdx-loader/package.json b/packages/docusaurus-mdx-loader/package.json index 8c24743332bd..3608aa0a82ca 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -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", diff --git a/packages/docusaurus-module-type-aliases/package.json b/packages/docusaurus-module-type-aliases/package.json index 1886e871b07b..0b38b19c1b4e 100644 --- a/packages/docusaurus-module-type-aliases/package.json +++ b/packages/docusaurus-module-type-aliases/package.json @@ -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": "*", diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 5cb44f06e402..0819c6efeaad 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -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; diff --git a/packages/docusaurus-plugin-client-redirects/package.json b/packages/docusaurus-plugin-client-redirects/package.json index 3b9311e0819c..2241ec940414 100644 --- a/packages/docusaurus-plugin-client-redirects/package.json +++ b/packages/docusaurus-plugin-client-redirects/package.json @@ -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", diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index fcd2680deabe..b4e6c58987e2 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -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", diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index daf26de3c6d4..a2afdf81e18a 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -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", diff --git a/packages/docusaurus-plugin-content-pages/package.json b/packages/docusaurus-plugin-content-pages/package.json index 956473b074ce..9b23bb0f54d1 100644 --- a/packages/docusaurus-plugin-content-pages/package.json +++ b/packages/docusaurus-plugin-content-pages/package.json @@ -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" diff --git a/packages/docusaurus-plugin-debug/package.json b/packages/docusaurus-plugin-debug/package.json index 21abf9355108..1544c25d874b 100644 --- a/packages/docusaurus-plugin-debug/package.json +++ b/packages/docusaurus-plugin-debug/package.json @@ -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" diff --git a/packages/docusaurus-plugin-google-analytics/package.json b/packages/docusaurus-plugin-google-analytics/package.json index f580a915f77b..b1304a2e850c 100644 --- a/packages/docusaurus-plugin-google-analytics/package.json +++ b/packages/docusaurus-plugin-google-analytics/package.json @@ -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": { diff --git a/packages/docusaurus-plugin-google-gtag/package.json b/packages/docusaurus-plugin-google-gtag/package.json index 979fb7097882..6e9f4986f115 100644 --- a/packages/docusaurus-plugin-google-gtag/package.json +++ b/packages/docusaurus-plugin-google-gtag/package.json @@ -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" }, diff --git a/packages/docusaurus-plugin-google-tag-manager/package.json b/packages/docusaurus-plugin-google-tag-manager/package.json index bb86182bc11f..62f0e0aebebb 100644 --- a/packages/docusaurus-plugin-google-tag-manager/package.json +++ b/packages/docusaurus-plugin-google-tag-manager/package.json @@ -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": { diff --git a/packages/docusaurus-plugin-ideal-image/package.json b/packages/docusaurus-plugin-ideal-image/package.json index 196e3a0158e8..05157a718d9f 100644 --- a/packages/docusaurus-plugin-ideal-image/package.json +++ b/packages/docusaurus-plugin-ideal-image/package.json @@ -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": { diff --git a/packages/docusaurus-plugin-pwa/package.json b/packages/docusaurus-plugin-pwa/package.json index 62309c14daaf..25e6f1153c62 100644 --- a/packages/docusaurus-plugin-pwa/package.json +++ b/packages/docusaurus-plugin-pwa/package.json @@ -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": { diff --git a/packages/docusaurus-plugin-sitemap/package.json b/packages/docusaurus-plugin-sitemap/package.json index 76ef51d10d05..997204005ba0 100644 --- a/packages/docusaurus-plugin-sitemap/package.json +++ b/packages/docusaurus-plugin-sitemap/package.json @@ -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" diff --git a/packages/docusaurus-preset-classic/package.json b/packages/docusaurus-preset-classic/package.json index ea304bf47e14..49de28b3a421 100644 --- a/packages/docusaurus-preset-classic/package.json +++ b/packages/docusaurus-preset-classic/package.json @@ -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", diff --git a/packages/docusaurus-remark-plugin-npm2yarn/package.json b/packages/docusaurus-remark-plugin-npm2yarn/package.json index 95381be9a92a..64e3bbaba07a 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/package.json +++ b/packages/docusaurus-remark-plugin-npm2yarn/package.json @@ -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": { diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index d334a8ed9c5f..f749777fcd39 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -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", diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index b894974807d8..a54f0799a81e 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -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'; diff --git a/packages/docusaurus-theme-classic/src/theme/MDXComponents/Img/index.tsx b/packages/docusaurus-theme-classic/src/theme/MDXComponents/Img/index.tsx index c3b76dee9fc5..6eea009d0e10 100644 --- a/packages/docusaurus-theme-classic/src/theme/MDXComponents/Img/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/MDXComponents/Img/index.tsx @@ -19,6 +19,7 @@ export default function MDXImg(props: Props): JSX.Element { return ( // eslint-disable-next-line jsx-a11y/alt-text + useBrokenLinks().collectAnchor(props.id); + + return
  • ; +} diff --git a/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx b/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx index 5d7ce92f61f6..49d438bc5f68 100644 --- a/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx @@ -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'>) => , h2: (props: ComponentProps<'h2'>) => , diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem/index.tsx index fc1d8d356048..7a99b6fb0a50 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem/index.tsx @@ -89,6 +89,9 @@ function DropdownNavbarItemDesktop({ aria-haspopup="true" aria-expanded={showDropdown} role="button" + // # hash permits to make the 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} diff --git a/packages/docusaurus-theme-common/package.json b/packages/docusaurus-theme-common/package.json index 8e4af2c8494d..200f580ef165 100644 --- a/packages/docusaurus-theme-common/package.json +++ b/packages/docusaurus-theme-common/package.json @@ -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" }, diff --git a/packages/docusaurus-theme-common/src/components/Details/index.tsx b/packages/docusaurus-theme-common/src/components/Details/index.tsx index 0876cf9b94d3..1046cae05c41 100644 --- a/packages/docusaurus-theme-common/src/components/Details/index.tsx +++ b/packages/docusaurus-theme-common/src/components/Details/index.tsx @@ -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(null); diff --git a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts index 16a45f856d84..3d545655f673 100644 --- a/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/codeBlockUtils.ts @@ -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); diff --git a/packages/docusaurus-theme-live-codeblock/package.json b/packages/docusaurus-theme-live-codeblock/package.json index 98f02045ef8e..a352db799442 100644 --- a/packages/docusaurus-theme-live-codeblock/package.json +++ b/packages/docusaurus-theme-live-codeblock/package.json @@ -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": { diff --git a/packages/docusaurus-theme-mermaid/package.json b/packages/docusaurus-theme-mermaid/package.json index d817de8ec417..f9ffa3411811 100644 --- a/packages/docusaurus-theme-mermaid/package.json +++ b/packages/docusaurus-theme-mermaid/package.json @@ -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": { diff --git a/packages/docusaurus-theme-search-algolia/package.json b/packages/docusaurus-theme-search-algolia/package.json index 94e83143892e..00176538d2d1 100644 --- a/packages/docusaurus-theme-search-algolia/package.json +++ b/packages/docusaurus-theme-search-algolia/package.json @@ -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", diff --git a/packages/docusaurus-theme-translations/package.json b/packages/docusaurus-theme-translations/package.json index 14d099b24ed6..04ec3c19d59f 100644 --- a/packages/docusaurus-theme-translations/package.json +++ b/packages/docusaurus-theme-translations/package.json @@ -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": { diff --git a/packages/docusaurus-tsconfig/package.json b/packages/docusaurus-tsconfig/package.json index ae0132a236d7..85b3efea013a 100644 --- a/packages/docusaurus-tsconfig/package.json +++ b/packages/docusaurus-tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/tsconfig", - "version": "3.1.0", + "version": "3.1.1", "description": "Docusaurus base TypeScript configuration.", "main": "tsconfig.json", "publishConfig": { diff --git a/packages/docusaurus-types/package.json b/packages/docusaurus-types/package.json index 4c4e6944ea49..2c0648620cc4 100644 --- a/packages/docusaurus-types/package.json +++ b/packages/docusaurus-types/package.json @@ -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": { diff --git a/packages/docusaurus-types/src/routing.d.ts b/packages/docusaurus-types/src/routing.d.ts index 7d1129c2a0a6..3e2ae2ab0bbd 100644 --- a/packages/docusaurus-types/src/routing.d.ts +++ b/packages/docusaurus-types/src/routing.d.ts @@ -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. */ diff --git a/packages/docusaurus-utils-common/package.json b/packages/docusaurus-utils-common/package.json index 4b453ad41093..01b4693b656d 100644 --- a/packages/docusaurus-utils-common/package.json +++ b/packages/docusaurus-utils-common/package.json @@ -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", diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index d60487f6d78a..8e79998f143b 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -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" diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json index c1c49b4a6184..cb5494e25f7f 100644 --- a/packages/docusaurus-utils/package.json +++ b/packages/docusaurus-utils/package.json @@ -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", diff --git a/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts b/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts index 0e04dbf5c280..734baea200bd 100644 --- a/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/markdownUtils.test.ts @@ -1251,6 +1251,38 @@ describe('unwrapMdxCodeBlocks', () => { `); }); + + it('allow spaces before mdx-code-block info string', () => { + expect( + unwrapMdxCodeBlocks(dedent` + # Title + + \`\`\` mdx-code-block + import Comp, {User} from "@site/components/comp" + + + + + + export const age = 36 + \`\`\` + + text + `), + ).toEqual(dedent` + # Title + + import Comp, {User} from "@site/components/comp" + + + + + + export const age = 36 + + text + `); + }); }); describe('admonitionTitleToDirectiveLabel', () => { diff --git a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts index 301a91ae3224..7ed1cbbf9cea 100644 --- a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts @@ -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', diff --git a/packages/docusaurus-utils/src/markdownUtils.ts b/packages/docusaurus-utils/src/markdownUtils.ts index 87aac88f09b1..ce0766de6ce2 100644 --- a/packages/docusaurus-utils/src/markdownUtils.ts +++ b/packages/docusaurus-utils/src/markdownUtils.ts @@ -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 = - /(?^|\n)```mdx-code-block\n(?.*?)\n```(?\n|$)/gs; + /(?^|\n)```(?\x20*)mdx-code-block\n(?.*?)\n```(?\n|$)/gs; const regexp4 = - /(?^|\n)````mdx-code-block\n(?.*?)\n````(?\n|$)/gs; + /(?^|\n)````(?\x20*)mdx-code-block\n(?.*?)\n````(?\n|$)/gs; // eslint-disable-next-line @typescript-eslint/no-explicit-any const replacer = (substring: string, ...args: any[]) => { diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 1edbc79b9e25..8746dbf8b589 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -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", diff --git a/packages/docusaurus/src/client/BrokenLinksContext.tsx b/packages/docusaurus/src/client/BrokenLinksContext.tsx index e04e8ab14731..8c8512ef81e8 100644 --- a/packages/docusaurus/src/client/BrokenLinksContext.tsx +++ b/packages/docusaurus/src/client/BrokenLinksContext.tsx @@ -18,11 +18,11 @@ export const createStatefulBrokenLinks = (): StatefulBrokenLinks => { const allAnchors = new Set(); const allLinks = new Set(); 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], diff --git a/packages/docusaurus/src/client/exports/Link.tsx b/packages/docusaurus/src/client/exports/Link.tsx index 8b886c8e7073..0e67536f885e 100644 --- a/packages/docusaurus/src/client/exports/Link.tsx +++ b/packages/docusaurus/src/client/exports/Link.tsx @@ -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 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 [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,12 +343,23 @@ describe('handleBrokenLinks', () => { `); }); - it('rejects broken link with anchor', async () => { + it('rejects broken link due to strict matching', async () => { await expect(() => testBrokenLinks({ - routes: [{path: '/page1'}, {path: '/page2'}], + routes: [ + {path: '/page1', strict: true}, + {path: '/page2/', strict: true}, + ], + collectedLinks: { - '/page1': {links: ['/brokenLink#anchor'], anchors: []}, + '/page1': { + links: ['/page1', '/page1/', '/page2', '/page2/'], + anchors: [], + }, + '/page2/': { + links: ['/page1', '/page1/', '/page2', '/page2/'], + anchors: [], + }, }, }), ).rejects.toThrowErrorMatchingInlineSnapshot(` @@ -177,17 +370,55 @@ describe('handleBrokenLinks', () => { Exhaustive list of all broken links found: - Broken link on source page path = /page1: - -> linking to /brokenLink#anchor + -> linking to /page2 + - Broken link on source page path = /page2/: + -> linking to /page2 " `); }); - it('rejects broken link with querystring + anchor', async () => { + 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({ routes: [{path: '/page1'}, {path: '/page2'}], collectedLinks: { - '/page1': {links: ['/brokenLink?age=42#anchor'], anchors: []}, + '/page1': {links: ['/brokenLink#anchor'], anchors: []}, }, }), ).rejects.toThrowErrorMatchingInlineSnapshot(` @@ -198,39 +429,38 @@ describe('handleBrokenLinks', () => { Exhaustive list of all broken links found: - Broken link on source page path = /page1: - -> linking to /brokenLink?age=42#anchor + -> linking to /brokenLink#anchor " `); }); - it('rejects valid link with broken anchor', async () => { + it('rejects broken link with querystring + anchor', async () => { await expect(() => testBrokenLinks({ routes: [{path: '/page1'}, {path: '/page2'}], collectedLinks: { - '/page1': {links: ['/page2#brokenAnchor'], anchors: []}, - '/page2': {links: [], anchors: []}, + '/page1': {links: ['/brokenLink?age=42#anchor'], anchors: []}, }, }), ).rejects.toThrowErrorMatchingInlineSnapshot(` - "Docusaurus found broken anchors! + "Docusaurus found broken links! - 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. + 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 anchors found: - - Broken anchor on source page path = /page1: - -> linking to /page2#brokenAnchor + Exhaustive list of all broken links found: + - Broken link on source page path = /page1: + -> linking to /brokenLink?age=42#anchor " `); }); - it('rejects valid link with empty broken anchor', async () => { + it('rejects valid link with broken anchor', async () => { await expect(() => testBrokenLinks({ routes: [{path: '/page1'}, {path: '/page2'}], collectedLinks: { - '/page1': {links: ['/page2#'], anchors: []}, + '/page1': {links: ['/page2#brokenAnchor'], anchors: []}, '/page2': {links: [], anchors: []}, }, }), @@ -242,7 +472,7 @@ describe('handleBrokenLinks', () => { Exhaustive list of all broken anchors found: - Broken anchor on source page path = /page1: - -> linking to /page2# + -> linking to /page2#brokenAnchor " `); }); @@ -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({length: scale}).map((_, i) => ({ + path: `/page${i}`, + exact: true, + })), + ...Array.from({length: scale}).map((_, i) => ({ + path: `/page/nonExact/${i}`, + })), + ...Array.from({length: scale}).fill({ + path: '/pageDynamic/:subpath1', + }), + ]; + + const collectedLinks: Params['collectedLinks'] = Object.fromEntries( + Array.from({length: scale}).map((_, i) => [ + `/page${i}`, + { + links: [ + ...Array.from({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({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); + }); }); diff --git a/packages/docusaurus/src/server/brokenLinks.ts b/packages/docusaurus/src/server/brokenLinks.ts index ccbaadcd3ffb..c91c2f1faecc 100644 --- a/packages/docusaurus/src/server/brokenLinks.ts +++ b/packages/docusaurus/src/server/brokenLinks.ts @@ -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; anchors: Set} +>; + +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 (isAnchorBrokenLink(linkPath)) { - return [ - { - link, - resolvedLink: serializeURLPath(linkPath), - anchor: true, - }, - ]; + 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, + }); } - 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}); } diff --git a/packages/docusaurus/src/server/getHostPort.ts b/packages/docusaurus/src/server/getHostPort.ts index 4ae80708704f..893f754d46ea 100644 --- a/packages/docusaurus/src/server/getHostPort.ts +++ b/packages/docusaurus/src/server/getHostPort.ts @@ -54,7 +54,10 @@ async function choosePort( defaultPort: number, ): Promise { try { - const port = await detect({port: defaultPort, hostname: host}); + const port = await detect({ + port: defaultPort, + ...(host !== 'localhost' && {hostname: host}), + }); if (port === defaultPort) { return port; } diff --git a/packages/docusaurus/src/server/routes.ts b/packages/docusaurus/src/server/routes.ts index 6a63fa71ca73..907f77816b8f 100644 --- a/packages/docusaurus/src/server/routes.ts +++ b/packages/docusaurus/src/server/routes.ts @@ -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` diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 1d1c73d42188..cb896846e1c9 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -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": [ diff --git a/packages/lqip-loader/package.json b/packages/lqip-loader/package.json index c7f6a59c2255..257ef5e8aa5d 100644 --- a/packages/lqip-loader/package.json +++ b/packages/lqip-loader/package.json @@ -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", diff --git a/packages/stylelint-copyright/package.json b/packages/stylelint-copyright/package.json index 3113e41560c3..5e03a2e2874f 100644 --- a/packages/stylelint-copyright/package.json +++ b/packages/stylelint-copyright/package.json @@ -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", diff --git a/project-words.txt b/project-words.txt index 51136e928328..57e4e1380536 100644 --- a/project-words.txt +++ b/project-words.txt @@ -65,6 +65,7 @@ datagit Datagit's dedup devto +dingers Dmitry docsearch Docsify @@ -396,3 +397,4 @@ yangshunz Zhou zoomable zpao +ödingers diff --git a/website/_dogfooding/_docs tests/tests/links/broken-anchors-tests.mdx b/website/_dogfooding/_docs tests/tests/links/broken-anchors-tests.mdx new file mode 100644 index 000000000000..dbc3c790b763 --- /dev/null +++ b/website/_dogfooding/_docs tests/tests/links/broken-anchors-tests.mdx @@ -0,0 +1,9 @@ +# Broken Anchors tests + +import Link from '@docusaurus/Link'; + +#test-link-anchor + +[Markdown link to above anchor](#test-link-anchor) + +[Markdown link to above anchor](#) diff --git a/website/_dogfooding/_pages tests/code-block-tests.mdx b/website/_dogfooding/_pages tests/code-block-tests.mdx index 27e751cfa6cc..6c294b11c38a 100644 --- a/website/_dogfooding/_pages tests/code-block-tests.mdx +++ b/website/_dogfooding/_pages tests/code-block-tests.mdx @@ -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 } ``` diff --git a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx index 037a1f9e6458..feaf146f059d 100644 --- a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx +++ b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx @@ -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 ( + + {(noCheck ? '❌' : '✅') + + ' ' + + (props.to ?? props.href) + + (props.target ? ` (target=${props.target})` : '')} + + ); +} +``` + +- +- +- +- + +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: + +- +- +- +- + ### Linking to JSON - [./script.js](./_script.js) diff --git a/website/blog/2021-11-21-algolia-docsearch-migration/index.mdx b/website/blog/2021-11-21-algolia-docsearch-migration/index.mdx index 583054e5410b..823f10a1659d 100644 --- a/website/blog/2021-11-21-algolia-docsearch-migration/index.mdx +++ b/website/blog/2021-11-21-algolia-docsearch-migration/index.mdx @@ -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. @@ -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! diff --git a/website/docs/advanced/client.mdx b/website/docs/advanced/client.mdx index dd77268610f3..f4d37d296ded 100644 --- a/website/docs/advanced/client.mdx +++ b/website/docs/advanced/client.mdx @@ -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. diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 54d9b340a4a0..bacdb23519a7 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-content-docs.mdx b/website/docs/api/plugins/plugin-content-docs.mdx index 754a56d293d9..6427ff88c830 100644 --- a/website/docs/api/plugins/plugin-content-docs.mdx +++ b/website/docs/api/plugins/plugin-content-docs.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-content-pages.mdx b/website/docs/api/plugins/plugin-content-pages.mdx index ee9ff06016e1..b115eac1dccb 100644 --- a/website/docs/api/plugins/plugin-content-pages.mdx +++ b/website/docs/api/plugins/plugin-content-pages.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-debug.mdx b/website/docs/api/plugins/plugin-debug.mdx index cd37b1f0834a..e580466ce5b0 100644 --- a/website/docs/api/plugins/plugin-debug.mdx +++ b/website/docs/api/plugins/plugin-debug.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-google-analytics.mdx b/website/docs/api/plugins/plugin-google-analytics.mdx index 555e5bea7277..45d5189b4810 100644 --- a/website/docs/api/plugins/plugin-google-analytics.mdx +++ b/website/docs/api/plugins/plugin-google-analytics.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-google-gtag.mdx b/website/docs/api/plugins/plugin-google-gtag.mdx index 501b6c2f42c8..16fab6fbd270 100644 --- a/website/docs/api/plugins/plugin-google-gtag.mdx +++ b/website/docs/api/plugins/plugin-google-gtag.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-google-tag-manager.mdx b/website/docs/api/plugins/plugin-google-tag-manager.mdx index 43182aec075d..e444a5387760 100644 --- a/website/docs/api/plugins/plugin-google-tag-manager.mdx +++ b/website/docs/api/plugins/plugin-google-tag-manager.mdx @@ -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). ::: diff --git a/website/docs/api/plugins/plugin-sitemap.mdx b/website/docs/api/plugins/plugin-sitemap.mdx index 0d6b72763c03..29832b4ddb21 100644 --- a/website/docs/api/plugins/plugin-sitemap.mdx +++ b/website/docs/api/plugins/plugin-sitemap.mdx @@ -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). ::: diff --git a/website/docs/docusaurus-core.mdx b/website/docs/docusaurus-core.mdx index 4249f47bec24..eda97c78cb55 100644 --- a/website/docs/docusaurus-core.mdx +++ b/website/docs/docusaurus-core.mdx @@ -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

    Heading

    ; +export default function MyHeading(props) { + useBrokenLinks().collectAnchor(props.id); + return

    ; } ``` ```js title="MyLink.js" import useBrokenLinks from '@docusaurus/useBrokenLinks'; -export default function MyLink({targetLink, ...props}): JSX.Element { - const brokenLinks = useBrokenLinks(); - - brokenLinks.collectLink(targetLink); - - return Link; +export default function MyLink(props) { + useBrokenLinks().collectLink(props.href); + return ; } ``` diff --git a/website/docs/guides/docs/sidebar/items.mdx b/website/docs/guides/docs/sidebar/items.mdx index 8b2ac4e764e2..1dd0c0100e78 100644 --- a/website/docs/guides/docs/sidebar/items.mdx +++ b/website/docs/guides/docs/sidebar/items.mdx @@ -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. ::: diff --git a/website/docs/migration/v2/migration-automated.mdx b/website/docs/migration/v2/migration-automated.mdx index 61e07cc4c1f5..ff4139d2e71d 100644 --- a/website/docs/migration/v2/migration-automated.mdx +++ b/website/docs/migration/v2/migration-automated.mdx @@ -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: diff --git a/website/package.json b/website/package.json index 5a6535804c1d..b0e7f456000d 100644 --- a/website/package.json +++ b/website/package.json @@ -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", diff --git a/website/src/components/APITable/index.tsx b/website/src/components/APITable/index.tsx index 049e2d4bcfd4..29e9890bea46 100644 --- a/website/src/components/APITable/index.tsx +++ b/website/src/components/APITable/index.tsx @@ -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 ( + ) : ( + + ); +} +``` + +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. + +::: diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-blog.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-blog.mdx new file mode 100644 index 000000000000..bacdb23519a7 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-blog.mdx @@ -0,0 +1,292 @@ +--- +sidebar_position: 2 +slug: /api/plugins/@docusaurus/plugin-content-blog +--- + +# 📦 plugin-content-blog + +import APITable from '@site/src/components/APITable'; + +Provides the [Blog](blog.mdx) feature and is the default blog plugin for Docusaurus. + +:::warning some features production only + +The [feed feature](../../blog.mdx#feed) works by extracting the build output, and is **only active in production**. + +::: + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-content-blog +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `path` | `string` | `'blog'` | Path to the blog content directory on the file system, relative to site dir. | +| `editUrl` | string \| EditUrlFn | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativePostPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. | +| `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. | +| `blogTitle` | `string` | `'Blog'` | Blog page title for better SEO. | +| `blogDescription` | `string` | `'Blog'` | Blog page meta description for better SEO. | +| `blogSidebarCount` | number \| 'ALL' | `5` | Number of blog post elements to show in the blog sidebar. `'ALL'` to show all blog posts; `0` to disable. | +| `blogSidebarTitle` | `string` | `'Recent posts'` | Title of the blog sidebar. | +| `routeBasePath` | `string` | `'blog'` | URL route for the blog section of your site. **DO NOT** include a trailing slash. Use `/` to put the blog at root path. | +| `tagsBasePath` | `string` | `'tags'` | URL route for the tags section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. | +| `archiveBasePath` | string \| null | `'archive'` | URL route for the archive section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to disable generation of archive. | +| `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. | +| `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. | +| `postsPerPage` | number \| 'ALL' | `10` | Number of posts to show per page in the listing page. Use `'ALL'` to display all posts on one listing page. | +| `blogListComponent` | `string` | `'@theme/BlogListPage'` | Root component of the blog listing page. | +| `blogPostComponent` | `string` | `'@theme/BlogPostPage'` | Root component of each blog post page. | +| `blogTagsListComponent` | `string` | `'@theme/BlogTagsListPage'` | Root component of the tags list page. | +| `blogTagsPostsComponent` | `string` | `'@theme/BlogTagsPostsPage'` | Root component of the "posts containing tag" page. | +| `blogArchiveComponent` | `string` | `'@theme/BlogArchivePage'` | Root component of the blog archive page. | +| `remarkPlugins` | `any[]` | `[]` | Remark plugins passed to MDX. | +| `rehypePlugins` | `any[]` | `[]` | Rehype plugins passed to MDX. | +| `beforeDefaultRemarkPlugins` | `any[]` | `[]` | Custom Remark plugins passed to MDX before the default Docusaurus Remark plugins. | +| `beforeDefaultRehypePlugins` | `any[]` | `[]` | Custom Rehype plugins passed to MDX before the default Docusaurus Rehype plugins. | +| `truncateMarker` | `RegExp` | `//` \| `\{\/\*\s*truncate\s*\*\/\}/` | Truncate marker marking where the summary ends. | +| `showReadingTime` | `boolean` | `true` | Show estimated reading time for the blog post. | +| `readingTime` | `ReadingTimeFn` | The default reading time | A callback to customize the reading time number displayed. | +| `authorsMapPath` | `string` | `'authors.yml'` | Path to the authors map file, relative to the blog content directory. | +| `feedOptions` | _See below_ | `{type: ['rss', 'atom']}` | Blog feed. | +| `feedOptions.type` | FeedType \| FeedType[] \| 'all' \| null | **Required** | Type of feed to be generated. Use `null` to disable generation. | +| `feedOptions.createFeedItems` | CreateFeedItemsFn \| undefined | `undefined` | An optional function which can be used to transform and / or filter the items in the feed. | +| `feedOptions.limit` | `number \| null \| false` | `20` | Limits the feed to the specified number of posts, `false` or `null` for all entries. Defaults to `20`. | +| `feedOptions.title` | `string` | `siteConfig.title` | Title of the feed. | +| `feedOptions.description` | `string` | \`$\{siteConfig.title} Blog\` | Description of the feed. | +| `feedOptions.copyright` | `string` | `undefined` | Copyright message. | +| `feedOptions.language` | `string` (See [documentation](http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes) for possible values) | `undefined` | Language metadata of the feed. | +| `sortPosts` | 'descending' \| 'ascending' | `'descending'` | Governs the direction of blog post sorting. | + +```mdx-code-block + +``` + +### Types {#types} + +#### `EditUrlFn` {#EditUrlFn} + +```ts +type EditUrlFunction = (params: { + blogDirPath: string; + blogPath: string; + permalink: string; + locale: string; +}) => string | undefined; +``` + +#### `ReadingTimeFn` {#ReadingTimeFn} + +```ts +type ReadingTimeOptions = { + wordsPerMinute: number; + wordBound: (char: string) => boolean; +}; + +type ReadingTimeCalculator = (params: { + content: string; + frontMatter?: BlogPostFrontMatter & Record; + options?: ReadingTimeOptions; +}) => number; + +type ReadingTimeFn = (params: { + content: string; + frontMatter: BlogPostFrontMatter & Record; + defaultReadingTime: ReadingTimeCalculator; +}) => number | undefined; +``` + +#### `FeedType` {#FeedType} + +```ts +type FeedType = 'rss' | 'atom' | 'json'; +``` + +#### `CreateFeedItemsFn` {#CreateFeedItemsFn} + +```ts +type CreateFeedItemsFn = (params: { + blogPosts: BlogPost[]; + siteConfig: DocusaurusConfig; + outDir: string; + defaultCreateFeedItemsFn: CreateFeedItemsFn; +}) => Promise; +``` + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: blog +// Plugin Options: @docusaurus/plugin-content-blog + +const config = { + path: 'blog', + // Simple use-case: string editUrl + // editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/', + // Advanced use-case: functional editUrl + editUrl: ({locale, blogDirPath, blogPath, permalink}) => + `https://github.com/facebook/docusaurus/edit/main/website/${blogDirPath}/${blogPath}`, + editLocalizedFiles: false, + blogTitle: 'Blog title', + blogDescription: 'Blog', + blogSidebarCount: 5, + blogSidebarTitle: 'All our posts', + routeBasePath: 'blog', + include: ['**/*.{md,mdx}'], + exclude: [ + '**/_*.{js,jsx,ts,tsx,md,mdx}', + '**/_*/**', + '**/*.test.{js,jsx,ts,tsx}', + '**/__tests__/**', + ], + postsPerPage: 10, + blogListComponent: '@theme/BlogListPage', + blogPostComponent: '@theme/BlogPostPage', + blogTagsListComponent: '@theme/BlogTagsListPage', + blogTagsPostsComponent: '@theme/BlogTagsPostsPage', + remarkPlugins: [require('./my-remark-plugin')], + rehypePlugins: [], + beforeDefaultRemarkPlugins: [], + beforeDefaultRehypePlugins: [], + truncateMarker: //, + showReadingTime: true, + feedOptions: { + type: '', + title: '', + description: '', + copyright: '', + language: undefined, + createFeedItems: async (params) => { + const {blogPosts, defaultCreateFeedItems, ...rest} = params; + return defaultCreateFeedItems({ + // keep only the 10 most recent blog posts in the feed + blogPosts: blogPosts.filter((item, index) => index < 10), + ...rest, + }); + }, + }, +}; +``` + +## Markdown front matter {#markdown-front-matter} + +Markdown documents can use the following Markdown [front matter](../../guides/markdown-features/markdown-features-intro.mdx#front-matter) metadata fields, enclosed by a line `---` on either side. + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `authors` | `Authors` | `undefined` | List of blog post authors (or unique author). Read the [`authors` guide](../../blog.mdx#blog-post-authors) for more explanations. Prefer `authors` over the `author_*` front matter fields, even for single author blog posts. | +| `author` | `string` | `undefined` | ⚠️ Prefer using `authors`. The blog post author's name. | +| `author_url` | `string` | `undefined` | ⚠️ Prefer using `authors`. The URL that the author's name will be linked to. This could be a GitHub, Twitter, Facebook profile URL, etc. | +| `author_image_url` | `string` | `undefined` | ⚠️ Prefer using `authors`. The URL to the author's thumbnail image. | +| `author_title` | `string` | `undefined` | ⚠️ Prefer using `authors`. A description of the author. | +| `title` | `string` | Markdown title | The blog post title. | +| `date` | `string` | File name or file creation time | The blog post creation date. If not specified, this can be extracted from the file or folder name, e.g, `2021-04-15-blog-post.mdx`, `2021-04-15-blog-post/index.mdx`, `2021/04/15/blog-post.mdx`. Otherwise, it is the Markdown file creation time. | +| `tags` | `Tag[]` | `undefined` | A list of strings or objects of two string fields `label` and `permalink` to tag to your post. | +| `draft` | `boolean` | `false` | Draft blog posts will only be available during development. | +| `unlisted` | `boolean` | `false` | Unlisted blog posts will be available in both development and production. They will be "hidden" in production, not indexed, excluded from sitemaps, and can only be accessed by users having a direct link. | +| `hide_table_of_contents` | `boolean` | `false` | Whether to hide the table of contents to the right. | +| `toc_min_heading_level` | `number` | `2` | The minimum heading level shown in the table of contents. Must be between 2 and 6 and lower or equal to the max value. | +| `toc_max_heading_level` | `number` | `3` | The max heading level shown in the table of contents. Must be between 2 and 6. | +| `keywords` | `string[]` | `undefined` | Keywords meta tag, which will become the `` in ``, used by search engines. | +| `description` | `string` | The first line of Markdown content | The description of your document, which will become the `` and `` in ``, used by search engines. | +| `image` | `string` | `undefined` | Cover or thumbnail image that will be used when displaying the link to your post. | +| `slug` | `string` | File path | Allows to customize the blog post URL (`//`). Support multiple patterns: `slug: my-blog-post`, `slug: /my/path/to/blog/post`, slug: `/`. | + +```mdx-code-block + +``` + +```ts +type Tag = string | {label: string; permalink: string}; + +// An author key references an author from the global plugin authors.yml file +type AuthorKey = string; + +type Author = { + key?: AuthorKey; + name: string; + title?: string; + url?: string; + image_url?: string; +}; + +// The front matter authors field allows various possible shapes +type Authors = AuthorKey | Author | (AuthorKey | Author)[]; +``` + +Example: + +```md +--- +title: Welcome Docusaurus +authors: + - slorber + - yangshun + - name: Joel Marcey + title: Co-creator of Docusaurus 1 + url: https://github.com/JoelMarcey + image_url: https://github.com/JoelMarcey.png +tags: [hello, docusaurus-v2] +description: This is my first post on Docusaurus. +image: https://i.imgur.com/mErPwqL.png +hide_table_of_contents: false +--- + +A Markdown blog post +``` + +## i18n {#i18n} + +Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first. + +### Translation files location {#translation-files-location} + +- **Base path**: `website/i18n/[locale]/docusaurus-plugin-content-blog` +- **Multi-instance path**: `website/i18n/[locale]/docusaurus-plugin-content-blog-[pluginId]` +- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.mdx#docusaurus-write-translations-sitedir) +- **Markdown files**: `website/i18n/[locale]/docusaurus-plugin-content-blog` + +### Example file-system structure {#example-file-system-structure} + +```bash +website/i18n/[locale]/docusaurus-plugin-content-blog +│ +│ # translations for website/blog +├── authors.yml +├── first-blog-post.md +├── second-blog-post.md +│ +│ # translations for the plugin options that will be rendered +└── options.json +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-docs.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-docs.mdx new file mode 100644 index 000000000000..6427ff88c830 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-docs.mdx @@ -0,0 +1,368 @@ +--- +sidebar_position: 1 +slug: /api/plugins/@docusaurus/plugin-content-docs +--- + +# 📦 plugin-content-docs + +import APITable from '@site/src/components/APITable'; + +Provides the [Docs](../../guides/docs/docs-introduction.mdx) functionality and is the default docs plugin for Docusaurus. + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-content-docs +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `path` | `string` | `'docs'` | Path to the docs content directory on the file system, relative to site directory. | +| `editUrl` | string \| EditUrlFunction | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativeDocPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. | +| `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. | +| `editCurrentVersion` | `boolean` | `false` | The edit URL will always target the current version doc instead of older versions. Ignored when `editUrl` is a function. | +| `routeBasePath` | `string` | `'docs'` | URL route for the docs section of your site. **DO NOT** include a trailing slash. Use `/` for shipping docs without base path. | +| `tagsBasePath` | `string` | `'tags'` | URL route for the tags list page of your site. It is prepended to the `routeBasePath`. | +| `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. | +| `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. | +| `sidebarPath` | false \| string | `undefined` | Path to sidebar configuration. Use `false` to disable sidebars, or `undefined` to create a fully autogenerated sidebar. | +| `sidebarCollapsible` | `boolean` | `true` | Whether sidebar categories are collapsible by default. See also [Collapsible categories](/docs/sidebar/items#collapsible-categories) | +| `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. See also [Expanded categories by default](/docs/sidebar/items#expanded-categories-by-default) | +| `sidebarItemsGenerator` | SidebarGenerator | _Omitted_ | Function used to replace the sidebar items of type `'autogenerated'` with real sidebar items (docs, categories, links...). See also [Customize the sidebar items generator](/docs/sidebar/autogenerated#customize-the-sidebar-items-generator) | +| `numberPrefixParser` | boolean \| PrefixParser | _Omitted_ | Custom parsing logic to extract number prefixes from file names. Use `false` to disable this behavior and leave the docs untouched, and `true` to use the default parser. See also [Using number prefixes](/docs/sidebar/autogenerated#using-number-prefixes) | +| `docsRootComponent` | `string` | `'@theme/DocsRoot'` | Parent component of all the docs plugin pages (including all versions). Stays mounted when navigation between docs pages and versions. | +| `docVersionRootComponent` | `string` | `'@theme/DocVersionLayout'` | Parent component of all docs pages of an individual version (doc pages with sidebars, tags pages). Stays mounted when navigation between pages of that specific version. | +| `docRootComponent` | `string` | `'@theme/DocPage'` | Parent component of all doc pages with sidebars (regular docs pages, category generated index pages). Stays mounted when navigation between such pages. | +| `docItemComponent` | `string` | `'@theme/DocItem'` | Main doc container, with TOC, pagination, etc. | +| `docTagsListComponent` | `string` | `'@theme/DocTagsListPage'` | Root component of the tags list page | +| `docTagDocListComponent` | `string` | `'@theme/DocTagDocListPage'` | Root component of the "docs containing tag X" page. | +| `docCategoryGeneratedIndexComponent` | `string` | `'@theme/DocCategoryGeneratedIndexPage'` | Root component of the generated category index page. | +| `remarkPlugins` | `any[]` | `[]` | Remark plugins passed to MDX. | +| `rehypePlugins` | `any[]` | `[]` | Rehype plugins passed to MDX. | +| `beforeDefaultRemarkPlugins` | `any[]` | `[]` | Custom Remark plugins passed to MDX before the default Docusaurus Remark plugins. | +| `beforeDefaultRehypePlugins` | `any[]` | `[]` | Custom Rehype plugins passed to MDX before the default Docusaurus Rehype plugins. | +| `showLastUpdateAuthor` | `boolean` | `false` | Whether to display the author who last updated the doc. | +| `showLastUpdateTime` | `boolean` | `false` | Whether to display the last date the doc was updated. | +| `breadcrumbs` | `boolean` | `true` | Enable or disable the breadcrumbs on doc pages. | +| `disableVersioning` | `boolean` | `false` | Explicitly disable versioning even when multiple versions exist. This will make the site only include the current version. Will error if `includeCurrentVersion: false` and `disableVersioning: true`. | +| `includeCurrentVersion` | `boolean` | `true` | Include the current version of your docs. | +| `lastVersion` | `string` | First version in `versions.json` | The version navigated to in priority and displayed by default for docs navbar items. | +| `onlyIncludeVersions` | `string[]` | All versions available | Only include a subset of all available versions. | +| `versions` | VersionsConfig | `{}` | Independent customization of each version's properties. | + +```mdx-code-block + +``` + +### Types {#types} + +#### `EditUrlFunction` {#EditUrlFunction} + +```ts +type EditUrlFunction = (params: { + version: string; + versionDocsDirPath: string; + docPath: string; + permalink: string; + locale: string; +}) => string | undefined; +``` + +#### `PrefixParser` {#PrefixParser} + +```ts +type PrefixParser = (filename: string) => { + filename: string; + numberPrefix?: number; +}; +``` + +#### `SidebarGenerator` {#SidebarGenerator} + +```ts +type SidebarGenerator = (generatorArgs: { + /** The sidebar item with type "autogenerated" to be transformed. */ + item: {type: 'autogenerated'; dirName: string}; + /** Useful metadata for the version this sidebar belongs to. */ + version: {contentPath: string; versionName: string}; + /** All the docs of that version (unfiltered). */ + docs: { + id: string; + title: string; + frontMatter: DocFrontMatter & Record; + source: string; + sourceDirName: string; + sidebarPosition?: number | undefined; + }[]; + /** Number prefix parser configured for this plugin. */ + numberPrefixParser: PrefixParser; + /** The default category index matcher which you can override. */ + isCategoryIndex: CategoryIndexMatcher; + /** + * key is the path relative to the doc content directory, value is the + * category metadata file's content. + */ + categoriesMetadata: {[filePath: string]: CategoryMetadata}; + /** + * Useful to re-use/enhance the default sidebar generation logic from + * Docusaurus. + */ + defaultSidebarItemsGenerator: SidebarGenerator; + // Returns an array of sidebar items — same as what you can declare in + // sidebars.js, except for shorthands. See https://docusaurus.io/docs/sidebar/items +}) => Promise; + +type CategoryIndexMatcher = (param: { + /** The file name, without extension */ + fileName: string; + /** + * The list of directories, from lowest level to highest. + * If there's no dir name, directories is ['.'] + */ + directories: string[]; + /** The extension, with a leading dot */ + extension: string; +}) => boolean; +``` + +#### `VersionsConfig` {#VersionsConfig} + +```ts +type VersionConfig = { + /** + * The base path of the version, will be appended to `baseUrl` + + * `routeBasePath`. + */ + path?: string; + /** The label of the version to be used in badges, dropdowns, etc. */ + label?: string; + /** The banner to show at the top of a doc of that version. */ + banner?: 'none' | 'unreleased' | 'unmaintained'; + /** Show a badge with the version label at the top of each doc. */ + badge?: boolean; + /** Prevents search engines from indexing this version */ + noIndex?: boolean; + /** Add a custom class name to the element of each doc */ + className?: string; +}; + +type VersionsConfig = {[versionName: string]: VersionConfig}; +``` + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: docs +// Plugin Options: @docusaurus/plugin-content-docs + +const config = { + path: 'docs', + breadcrumbs: true, + // Simple use-case: string editUrl + // editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/', + // Advanced use-case: functional editUrl + editUrl: ({versionDocsDirPath, docPath}) => + `https://github.com/facebook/docusaurus/edit/main/website/${versionDocsDirPath}/${docPath}`, + editLocalizedFiles: false, + editCurrentVersion: false, + routeBasePath: 'docs', + include: ['**/*.md', '**/*.mdx'], + exclude: [ + '**/_*.{js,jsx,ts,tsx,md,mdx}', + '**/_*/**', + '**/*.test.{js,jsx,ts,tsx}', + '**/__tests__/**', + ], + sidebarPath: 'sidebars.js', + async sidebarItemsGenerator({ + defaultSidebarItemsGenerator, + numberPrefixParser, + item, + version, + docs, + isCategoryIndex, + }) { + // Use the provided data to generate a custom sidebar slice + return [ + {type: 'doc', id: 'intro'}, + { + type: 'category', + label: 'Tutorials', + items: [ + {type: 'doc', id: 'tutorial1'}, + {type: 'doc', id: 'tutorial2'}, + ], + }, + ]; + }, + numberPrefixParser(filename) { + // Implement your own logic to extract a potential number prefix + const numberPrefix = findNumberPrefix(filename); + // Prefix found: return it with the cleaned filename + if (numberPrefix) { + return { + numberPrefix, + filename: filename.replace(prefix, ''), + }; + } + // No number prefix found + return {numberPrefix: undefined, filename}; + }, + docLayoutComponent: '@theme/DocPage', + docItemComponent: '@theme/DocItem', + remarkPlugins: [require('./my-remark-plugin')], + rehypePlugins: [], + beforeDefaultRemarkPlugins: [], + beforeDefaultRehypePlugins: [], + showLastUpdateAuthor: false, + showLastUpdateTime: false, + disableVersioning: false, + includeCurrentVersion: true, + lastVersion: undefined, + versions: { + current: { + label: 'Android SDK v2.0.0 (WIP)', + path: 'android-2.0.0', + banner: 'none', + }, + '1.0.0': { + label: 'Android SDK v1.0.0', + path: 'android-1.0.0', + banner: 'unmaintained', + }, + }, + onlyIncludeVersions: ['current', '1.0.0', '2.0.0'], +}; +``` + +## Markdown front matter {#markdown-front-matter} + +Markdown documents can use the following Markdown [front matter](../../guides/markdown-features/markdown-features-intro.mdx#front-matter) metadata fields, enclosed by a line `---` on either side. + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `id` | `string` | file path (including folders, without the extension) | A unique document ID. | +| `title` | `string` | Markdown title or `id` | The text title of your document. Used for the page metadata and as a fallback value in multiple places (sidebar, next/previous buttons...). Automatically added at the top of your doc if it does not contain any Markdown title. | +| `pagination_label` | `string` | `sidebar_label` or `title` | The text used in the document next/previous buttons for this document. | +| `sidebar_label` | `string` | `title` | The text shown in the document sidebar for this document. | +| `sidebar_position` | `number` | Default ordering | Controls the position of a doc inside the generated sidebar slice when using `autogenerated` sidebar items. See also [Autogenerated sidebar metadata](/docs/sidebar/autogenerated#autogenerated-sidebar-metadata). | +| `sidebar_class_name` | `string` | `undefined` | Gives the corresponding sidebar label a special class name when using autogenerated sidebars. | +| `sidebar_custom_props` | `object` | `undefined` | Assign [custom props](../../guides/docs/sidebar/index.mdx#passing-custom-props) to the sidebar item referencing this doc | +| `displayed_sidebar` | `string` | `undefined` | Force the display of a given sidebar when browsing the current document. Read the [multiple sidebars guide](../../guides/docs/sidebar/multiple-sidebars.mdx) for details. | +| `hide_title` | `boolean` | `false` | Whether to hide the title at the top of the doc. It only hides a title declared through the front matter, and have no effect on a Markdown title at the top of your document. | +| `hide_table_of_contents` | `boolean` | `false` | Whether to hide the table of contents to the right. | +| `toc_min_heading_level` | `number` | `2` | The minimum heading level shown in the table of contents. Must be between 2 and 6 and lower or equal to the max value. | +| `toc_max_heading_level` | `number` | `3` | The max heading level shown in the table of contents. Must be between 2 and 6. | +| `pagination_next` | string \| null | Next doc in the sidebar | The ID of the documentation you want the "Next" pagination to link to. Use `null` to disable showing "Next" for this page. | +| `pagination_prev` | string \| null | Previous doc in the sidebar | The ID of the documentation you want the "Previous" pagination to link to. Use `null` to disable showing "Previous" for this page. | +| `parse_number_prefixes` | `boolean` | `numberPrefixParser` plugin option | Whether number prefix parsing is disabled on this doc. See also [Using number prefixes](/docs/sidebar/autogenerated#using-number-prefixes). | +| `custom_edit_url` | string \| null | Computed using the `editUrl` plugin option | The URL for editing this document. Use `null` to disable showing "Edit this page" for this page. | +| `keywords` | `string[]` | `undefined` | Keywords meta tag for the document page, for search engines. | +| `description` | `string` | The first line of Markdown content | The description of your document, which will become the `` and `` in ``, used by search engines. | +| `image` | `string` | `undefined` | Cover or thumbnail image that will be used when displaying the link to your post. | +| `slug` | `string` | File path | Allows to customize the document URL (`//`). Support multiple patterns: `slug: my-doc`, `slug: /my/path/myDoc`, `slug: /`. | +| `tags` | `Tag[]` | `undefined` | A list of strings or objects of two string fields `label` and `permalink` to tag to your docs. | +| `draft` | `boolean` | `false` | Draft documents will only be available during development. | +| `unlisted` | `boolean` | `false` | Unlisted documents will be available in both development and production. They will be "hidden" in production, not indexed, excluded from sitemaps, and can only be accessed by users having a direct link. | +| `last_update` | `FileChange` | `undefined` | Allows overriding the last updated author and/or date. Date can be any [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). | + +```mdx-code-block + +``` + +```ts +type Tag = string | {label: string; permalink: string}; +``` + +```ts +type FileChange = {date: string; author: string}; +``` + +Example: + +```md +--- +id: doc-markdown +title: Docs Markdown Features +hide_title: false +hide_table_of_contents: false +sidebar_label: Markdown +sidebar_position: 3 +pagination_label: Markdown features +custom_edit_url: https://github.com/facebook/docusaurus/edit/main/docs/api-doc-markdown.md +description: How do I find you when I cannot solve this problem +keywords: + - docs + - docusaurus +image: https://i.imgur.com/mErPwqL.png +slug: /myDoc +last_update: + date: 1/1/2000 + author: custom author name +--- + +# Markdown Features + +My Document Markdown content +``` + +## i18n {#i18n} + +Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first. + +### Translation files location {#translation-files-location} + +- **Base path**: `website/i18n/[locale]/docusaurus-plugin-content-docs` +- **Multi-instance path**: `website/i18n/[locale]/docusaurus-plugin-content-docs-[pluginId]` +- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.mdx#docusaurus-write-translations-sitedir) +- **Markdown files**: `website/i18n/[locale]/docusaurus-plugin-content-docs/[versionName]` + +### Example file-system structure {#example-file-system-structure} + +```bash +website/i18n/[locale]/docusaurus-plugin-content-docs +│ +│ # translations for website/docs +├── current +│ ├── api +│ │ └── config.md +│ └── getting-started.md +├── current.json +│ +│ # translations for website/versioned_docs/version-1.0.0 +├── version-1.0.0 +│ ├── api +│ │ └── config.md +│ └── getting-started.md +└── version-1.0.0.json +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-pages.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-pages.mdx new file mode 100644 index 000000000000..b115eac1dccb --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-content-pages.mdx @@ -0,0 +1,140 @@ +--- +sidebar_position: 3 +slug: /api/plugins/@docusaurus/plugin-content-pages +--- + +# 📦 plugin-content-pages + +import APITable from '@site/src/components/APITable'; + +The default pages plugin for Docusaurus. The classic template ships with this plugin with default configurations. This plugin provides [creating pages](guides/creating-pages.mdx) functionality. + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-content-pages +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `path` | `string` | `'src/pages'` | Path to data on filesystem relative to site dir. Components in this directory will be automatically converted to pages. | +| `routeBasePath` | `string` | `'/'` | URL route for the pages section of your site. **DO NOT** include a trailing slash. | +| `include` | `string[]` | `['**/*.{js,jsx,ts,tsx,md,mdx}']` | Matching files will be included and processed. | +| `exclude` | `string[]` | _See example configuration_ | No route will be created for matching files. | +| `mdxPageComponent` | `string` | `'@theme/MDXPage'` | Component used by each MDX page. | +| `remarkPlugins` | `[]` | `any[]` | Remark plugins passed to MDX. | +| `rehypePlugins` | `[]` | `any[]` | Rehype plugins passed to MDX. | +| `beforeDefaultRemarkPlugins` | `any[]` | `[]` | Custom Remark plugins passed to MDX before the default Docusaurus Remark plugins. | +| `beforeDefaultRehypePlugins` | `any[]` | `[]` | Custom Rehype plugins passed to MDX before the default Docusaurus Rehype plugins. | + +```mdx-code-block + +``` + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: pages +// Plugin Options: @docusaurus/plugin-content-pages + +const config = { + path: 'src/pages', + routeBasePath: '', + include: ['**/*.{js,jsx,ts,tsx,md,mdx}'], + exclude: [ + '**/_*.{js,jsx,ts,tsx,md,mdx}', + '**/_*/**', + '**/*.test.{js,jsx,ts,tsx}', + '**/__tests__/**', + ], + mdxPageComponent: '@theme/MDXPage', + remarkPlugins: [require('./my-remark-plugin')], + rehypePlugins: [], + beforeDefaultRemarkPlugins: [], + beforeDefaultRehypePlugins: [], +}; +``` + +## Markdown front matter {#markdown-front-matter} + +Markdown pages can use the following Markdown [front matter](../../guides/markdown-features/markdown-features-intro.mdx#front-matter) metadata fields, enclosed by a line `---` on either side. + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `title` | `string` | Markdown title | The blog post title. | +| `description` | `string` | The first line of Markdown content | The description of your page, which will become the `` and `` in ``, used by search engines. | +| `keywords` | `string[]` | `undefined` | Keywords meta tag, which will become the `` in ``, used by search engines. | +| `image` | `string` | `undefined` | Cover or thumbnail image that will be used when displaying the link to your post. | +| `wrapperClassName` | `string` | | Class name to be added to the wrapper element to allow targeting specific page content. | +| `hide_table_of_contents` | `boolean` | `false` | Whether to hide the table of contents to the right. | +| `draft` | `boolean` | `false` | Draft pages will only be available during development. | +| `unlisted` | `boolean` | `false` | Unlisted pages will be available in both development and production. They will be "hidden" in production, not indexed, excluded from sitemaps, and can only be accessed by users having a direct link. | + +```mdx-code-block + +``` + +Example: + +```md +--- +title: Markdown Page +description: Markdown page SEO description +wrapperClassName: markdown-page +hide_table_of_contents: false +draft: true +--- + +Markdown page content +``` + +## i18n {#i18n} + +Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first. + +### Translation files location {#translation-files-location} + +- **Base path**: `website/i18n/[locale]/docusaurus-plugin-content-pages` +- **Multi-instance path**: `website/i18n/[locale]/docusaurus-plugin-content-pages-[pluginId]` +- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.mdx#docusaurus-write-translations-sitedir) +- **Markdown files**: `website/i18n/[locale]/docusaurus-plugin-content-pages` + +### Example file-system structure {#example-file-system-structure} + +```bash +website/i18n/[locale]/docusaurus-plugin-content-pages +│ +│ # translations for website/src/pages +├── first-markdown-page.md +└── second-markdown-page.md +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-debug.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-debug.mdx new file mode 100644 index 000000000000..e580466ce5b0 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-debug.mdx @@ -0,0 +1,108 @@ +--- +sidebar_position: 5 +slug: /api/plugins/@docusaurus/plugin-debug +--- + +# 📦 plugin-debug + +```mdx-code-block +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +``` + +The debug plugin will display useful debug information at [`http://localhost:3000/__docusaurus/debug`](http://localhost:3000/__docusaurus/debug). + +It is mostly useful for plugin authors, that will be able to inspect more easily the content of the `.docusaurus` folder (like the creates routes), but also be able to inspect data structures that are never written to disk, like the plugin data loaded through the `contentLoaded` lifecycle. + +:::info + +If you use the plugin via the classic preset, the preset will **enable the plugin in development and disable it in production** by default (`debug: undefined`) to avoid exposing potentially sensitive information. You can use `debug: true` to always enable it or `debug: false` to always disable it. + +If you use a standalone plugin, you may need to achieve the same effect by checking the environment: + +```js title="docusaurus.config.js" +export default { + plugins: [ + // highlight-next-line + process.env.NODE_ENV === 'production' && '@docusaurus/plugin-debug', + ].filter(Boolean), +}; +``` + +::: + +:::note + +If you report a bug, we will probably ask you to have this plugin turned on in the production, so that we can inspect your deployment config more easily. + +If you don't have any sensitive information, you can keep it on in production [like we do](/__docusaurus/debug). + +::: + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-debug +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +This plugin currently has no options. + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```mdx-code-block + + +``` + +If you use a preset, configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic): + +```js title="docusaurus.config.js" +export default { + presets: [ + [ + '@docusaurus/preset-classic', + { + // highlight-next-line + debug: true, // This will enable the plugin in production + }, + ], + ], +}; +``` + +```mdx-code-block + + +``` + +If you are using a standalone plugin, provide options directly to the plugin: + +```js title="docusaurus.config.js" +export default { + // highlight-next-line + plugins: ['@docusaurus/plugin-debug'], +}; +``` + +```mdx-code-block + + +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-analytics.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-analytics.mdx new file mode 100644 index 000000000000..45d5189b4810 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-analytics.mdx @@ -0,0 +1,77 @@ +--- +sidebar_position: 6 +slug: /api/plugins/@docusaurus/plugin-google-analytics +--- + +# 📦 plugin-google-analytics + +import APITable from '@site/src/components/APITable'; + +The default [Google Analytics](https://developers.google.com/analytics/devguides/collection/analyticsjs/) plugin. It is a JavaScript library for measuring how users interact with your website **in the production build**. If you are using Google Analytics 4 you might need to consider using [plugin-google-gtag](./plugin-google-gtag.mdx) instead. + +:::danger Deprecated + +This plugin is **deprecated**, and will become useless on July 1, 2023. + +Google is [moving away from Universal Analytics](https://blog.google/products/marketingplatform/analytics/prepare-for-future-with-google-analytics-4/). + +If you are still using this plugin with a `UA-*` tracking id, you should create a Google Analytics 4 account as soon as possible, and use [`@docusaurus/plugin-google-gtag`](./plugin-google-gtag.mdx) instead of this plugin. More details [here](https://github.com/facebook/docusaurus/issues/7221). + +::: + +:::warning production only + +This plugin is always inactive in development and **only active in production** to avoid polluting the analytics statistics. + +::: + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-google-analytics +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `trackingID` | `string` | **Required** | The tracking ID of your analytics service. | +| `anonymizeIP` | `boolean` | `false` | Whether the IP should be anonymized when sending requests. | + +```mdx-code-block + +``` + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: googleAnalytics +// Plugin Options: @docusaurus/plugin-google-analytics + +const config = { + trackingID: 'UA-141789564-1', + anonymizeIP: true, +}; +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-gtag.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-gtag.mdx new file mode 100644 index 000000000000..16fab6fbd270 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-gtag.mdx @@ -0,0 +1,73 @@ +--- +sidebar_position: 7 +slug: /api/plugins/@docusaurus/plugin-google-gtag +--- + +# 📦 plugin-google-gtag + +import APITable from '@site/src/components/APITable'; + +The default [Global Site Tag (gtag.js)](https://developers.google.com/analytics/devguides/collection/gtagjs/) plugin. It is a JavaScript tagging framework and API that allows you to send event data to Google Analytics, Google Ads, and Google Marketing Platform. This section describes how to configure a Docusaurus site to enable global site tag for Google Analytics. + +:::tip + +You can use [Google's Tag Assistant](https://tagassistant.google.com/) tool to check if your gtag is set up correctly! + +::: + +:::warning production only + +This plugin is always inactive in development and **only active in production** to avoid polluting the analytics statistics. + +::: + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-google-gtag +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `trackingID` | string \| string[] | **Required** | The tracking ID of your gtag service. It is possible to provide multiple ids. | +| `anonymizeIP` | `boolean` | `false` | Whether the IP should be anonymized when sending requests. | + +```mdx-code-block + +``` + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: gtag +// Plugin Options: @docusaurus/plugin-google-gtag + +const config = { + trackingID: 'G-999X9XX9XX', + anonymizeIP: true, +}; +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-tag-manager.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-tag-manager.mdx new file mode 100644 index 000000000000..e444a5387760 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-google-tag-manager.mdx @@ -0,0 +1,71 @@ +--- +sidebar_position: 8 +slug: /api/plugins/@docusaurus/plugin-google-tag-manager +--- + +# 📦 plugin-google-tag-manager + +import APITable from '@site/src/components/APITable'; + +A plugin for adding [Google Tag Manager (gtm.js)](https://developers.google.com/tag-platform/tag-manager) to a Docusaurus site. Use this plugin in conjunction with the standard [gtag plugin](./plugin-google-gtag.mdx) for in-depth analysis of how users are using your site. + +:::tip + +You can use [Google's Tag Assistant](https://tagassistant.google.com/) tool to check if tag manager is set up correctly! + +::: + +:::warning production only + +This plugin is always inactive in development and **only active in production** to avoid polluting the analytics statistics. + +::: + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-google-tag-manager +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `containerId` | `string` | **Required** | Your Tag Manager container Id (usually starts with `GTM-`). | + +```mdx-code-block + +``` + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: googleTagManager +// Plugin Options: @docusaurus/plugin-google-tag-manager + +const config = { + containerId: 'GTM-12345', +}; +``` diff --git a/website/versioned_docs/version-3.1.0/api/plugins/plugin-sitemap.mdx b/website/versioned_docs/version-3.1.0/api/plugins/plugin-sitemap.mdx new file mode 100644 index 000000000000..29832b4ddb21 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/api/plugins/plugin-sitemap.mdx @@ -0,0 +1,82 @@ +--- +sidebar_position: 10 +slug: /api/plugins/@docusaurus/plugin-sitemap +--- + +# 📦 plugin-sitemap + +import APITable from '@site/src/components/APITable'; + +This plugin creates sitemaps for your site so that search engine crawlers can crawl your site more accurately. + +:::warning production only + +This plugin is always inactive in development and **only active in production** because it works on the build output. + +::: + +## Installation {#installation} + +```bash npm2yarn +npm install --save @docusaurus/plugin-sitemap +``` + +:::tip + +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](../../using-plugins.mdx#docusauruspreset-classic). + +::: + +## Configuration {#configuration} + +Accepted fields: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `changefreq` | `string` | `'weekly'` | See [sitemap docs](https://www.sitemaps.org/protocol.html#xmlTagDefinitions) | +| `priority` | `number` | `0.5` | See [sitemap docs](https://www.sitemaps.org/protocol.html#xmlTagDefinitions) | +| `ignorePatterns` | `string[]` | `[]` | A list of glob patterns; matching route paths will be filtered from the sitemap. Note that you may need to include the base URL in here. | +| `filename` | `string` | `sitemap.xml` | The path to the created sitemap file, relative to the output directory. Useful if you have two plugin instances outputting two files. | + +```mdx-code-block + +``` + +:::info + +This plugin also respects some site config: + +- [`noIndex`](../docusaurus.config.js.mdx#noIndex): results in no sitemap generated +- [`trailingSlash`](../docusaurus.config.js.mdx#trailingSlash): determines if the URLs in the sitemap have trailing slashes + +::: + +### Example configuration {#ex-config} + +You can configure this plugin through preset options or plugin options. + +:::tip + +Most Docusaurus users configure this plugin through the preset options. + +::: + +```js config-tabs +// Preset Options: sitemap +// Plugin Options: @docusaurus/plugin-sitemap + +const config = { + changefreq: 'weekly', + priority: 0.5, + ignorePatterns: ['/tags/**'], + filename: 'sitemap.xml', +}; +``` + +You can find your sitemap at `/sitemap.xml`. diff --git a/website/versioned_docs/version-3.1.0/docusaurus-core.mdx b/website/versioned_docs/version-3.1.0/docusaurus-core.mdx new file mode 100644 index 000000000000..6222bb3bd1f9 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/docusaurus-core.mdx @@ -0,0 +1,766 @@ +--- +sidebar_label: Client API +--- + +# Docusaurus Client API + +Docusaurus provides some APIs on the clients that can be helpful to you when building your site. + +## Components {#components} + +### `` {#errorboundary} + +This component creates a [React error boundary](https://reactjs.org/docs/error-boundaries.html). + +Use it to wrap components that might throw, and display a fallback when that happens instead of crashing the whole app. + +```jsx +import React from 'react'; +import ErrorBoundary from '@docusaurus/ErrorBoundary'; + +const SafeComponent = () => ( + ( +
    +

    This component crashed because of error: {error.message}.

    + +
    + )}> + +
    +); +``` + +```mdx-code-block +import ErrorBoundaryTestButton from '@site/src/components/ErrorBoundaryTestButton' +``` + +:::tip + +To see it in action, click here: + +::: + +:::info + +Docusaurus uses this component to catch errors within the theme's layout, and also within the entire app. + +::: + +:::note + +This component doesn't catch build-time errors and only protects against client-side render errors that can happen when using stateful React components. + +::: + +#### Props {#errorboundary-props} + +- `fallback`: an optional render callback returning a JSX element. It will receive an object with 2 attributes: `error`, the error that was caught, and `tryAgain`, a function (`() => void`) callback to reset the error in the component and try rendering it again. If not present, `@theme/Error` will be rendered instead. `@theme/Error` is used for the error boundaries wrapping the site, above the layout. + +:::warning + +The `fallback` prop is a callback, and **not a React functional component**. You can't use React hooks inside this callback. + +::: + +### `` {#head} + +This reusable React component will manage all of your changes to the document head. It takes plain HTML tags and outputs plain HTML tags and is beginner-friendly. It is a wrapper around [React Helmet](https://github.com/nfl/react-helmet). + +Usage Example: + +```jsx +import React from 'react'; +// highlight-next-line +import Head from '@docusaurus/Head'; + +const MySEO = () => ( + // highlight-start + + + + My Title + + + // highlight-end +); +``` + +Nested or latter components will override duplicate usages: + +```jsx + + {/* highlight-start */} + + My Title + + + {/* highlight-end */} + + {/* highlight-start */} + + Nested Title + + + {/* highlight-end */} + + +``` + +Outputs: + +```html + + Nested Title + + +``` + +### `` {#link} + +This component enables linking to internal pages as well as a powerful performance feature called preloading. Preloading is used to prefetch resources so that the resources are fetched by the time the user navigates with this component. We use an `IntersectionObserver` to fetch a low-priority request when the `` is in the viewport and then use an `onMouseOver` event to trigger a high-priority request when it is likely that a user will navigate to the requested resource. + +The component is a wrapper around react-router’s `` component that adds useful enhancements specific to Docusaurus. All props are passed through to react-router’s `` component. + +External links also work, and automatically have these props: `target="_blank" rel="noopener noreferrer"`. + +```jsx +import React from 'react'; +// highlight-next-line +import Link from '@docusaurus/Link'; + +const Page = () => ( +
    +

    + {/* highlight-next-line */} + Check out my blog! +

    +

    + {/* highlight-next-line */} + Follow me on Twitter! +

    +
    +); +``` + +#### `to`: string {#to-string} + +The target location to navigate to. Example: `/docs/introduction`. + +```jsx + +``` + +:::tip + +Prefer this component to vanilla `` tags because Docusaurus does a lot of optimizations (e.g. broken path detection, prefetching, applying base URL...) if you use ``. + +::: + +### `` {#redirect} + +Rendering a `` will navigate to a new location. The new location will override the current location in the history stack like server-side redirects (HTTP 3xx) do. You can refer to [React Router's Redirect documentation](https://reacttraining.com/react-router/web/api/Redirect) for more info on available props. + +Example usage: + +```jsx +import React from 'react'; +// highlight-next-line +import {Redirect} from '@docusaurus/router'; + +const Home = () => { + // highlight-next-line + return ; +}; +``` + +:::note + +`@docusaurus/router` implements [React Router](https://reacttraining.com/react-router/web/guides/quick-start) and supports its features. + +::: + +### `` {#browseronly} + +The `` component permits to render React components only in the browser after the React app has hydrated. + +:::tip + +Use it for integrating with code that can't run in Node.js, because the `window` or `document` objects are being accessed. + +::: + +#### Props {#browseronly-props} + +- `children`: render function prop returning browser-only JSX. Will not be executed in Node.js +- `fallback` (optional): JSX to render on the server (Node.js) and until React hydration completes. + +#### Example with code {#browseronly-example-code} + +```jsx +// highlight-start +import BrowserOnly from '@docusaurus/BrowserOnly'; +// highlight-end + +const MyComponent = () => { + return ( + // highlight-start + + {() => page url = {window.location.href}} + + // highlight-end + ); +}; +``` + +#### Example with a library {#browseronly-example-library} + +```jsx +// highlight-start +import BrowserOnly from '@docusaurus/BrowserOnly'; +// highlight-end + +const MyComponent = (props) => { + return ( + // highlight-start + Loading...}> + {() => { + const LibComponent = require('some-lib').LibComponent; + return ; + }} + + // highlight-end + ); +}; +``` + +### `` {#interpolate} + +A simple interpolation component for text containing dynamic placeholders. + +The placeholders will be replaced with the provided dynamic values and JSX elements of your choice (strings, links, styled elements...). + +#### Props {#interpolate-props} + +- `children`: text containing interpolation placeholders like `{placeholderName}` +- `values`: object containing interpolation placeholder values + +```jsx +import React from 'react'; +import Link from '@docusaurus/Link'; +import Interpolate from '@docusaurus/Interpolate'; + +export default function VisitMyWebsiteMessage() { + return ( + // highlight-start + + website + + ), + }}> + {'Hello, {firstName}! How are you? Take a look at my {website}'} + + // highlight-end + ); +} +``` + +### `` {#translate} + +When [localizing your site](./i18n/i18n-introduction.mdx), the `` component will allow providing **translation support to React components**, such as your homepage. The `` component supports [interpolation](#interpolate). + +The translation strings will statically extracted from your code with the [`docusaurus write-translations`](./cli.mdx#docusaurus-write-translations-sitedir) CLI and a `code.json` translation file will be created in `website/i18n/[locale]`. + +:::note + +The `` props **must be hardcoded strings**. + +Apart from the `values` prop used for interpolation, it is **not possible to use variables**, or the static extraction wouldn't work. + +::: + +#### Props {#translate-props} + +- `children`: untranslated string in the default site locale (can contain [interpolation placeholders](#interpolate)) +- `id`: optional value to be used as the key in JSON translation files +- `description`: optional text to help the translator +- `values`: optional object containing interpolation placeholder values + +#### Example {#example} + +```jsx title="src/pages/index.js" +import React from 'react'; +import Layout from '@theme/Layout'; + +// highlight-start +import Translate from '@docusaurus/Translate'; +// highlight-end + +export default function Home() { + return ( + +

    + {/* highlight-start */} + + Welcome to my website + + {/* highlight-end */} +

    +
    + {/* highlight-start */} + + {'Welcome, {firstName}! How are you?'} + + {/* highlight-end */} +
    +
    + ); +} +``` + +:::note + +You can even omit the children prop and specify a translation string in your `code.json` file manually after running the `docusaurus write-translations` CLI command. + +```jsx + +``` + +::: + +:::info + +The `` component supports interpolation. You can also implement [string pluralization](https://github.com/facebook/docusaurus/pull/i18n/i18n-tutorial.mdx#pluralization) through some custom code and the [`translate` imperative API](#translate-imperative). + +::: + +## Hooks {#hooks} + +### `useDocusaurusContext` {#useDocusaurusContext} + +React hook to access Docusaurus Context. The context contains the `siteConfig` object from [docusaurus.config.js](api/docusaurus.config.js.mdx) and some additional site metadata. + +```ts +type PluginVersionInformation = + | {readonly type: 'package'; readonly version?: string} + | {readonly type: 'project'} + | {readonly type: 'local'} + | {readonly type: 'synthetic'}; + +type SiteMetadata = { + readonly docusaurusVersion: string; + readonly siteVersion?: string; + readonly pluginVersions: Record; +}; + +type I18nLocaleConfig = { + label: string; + direction: string; +}; + +type I18n = { + defaultLocale: string; + locales: [string, ...string[]]; + currentLocale: string; + localeConfigs: Record; +}; + +type DocusaurusContext = { + siteConfig: DocusaurusConfig; + siteMetadata: SiteMetadata; + globalData: Record; + i18n: I18n; + codeTranslations: Record; +}; +``` + +Usage example: + +```jsx +import React from 'react'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; + +const MyComponent = () => { + // highlight-next-line + const {siteConfig, siteMetadata} = useDocusaurusContext(); + return ( +
    + {/* highlight-start */} +

    {siteConfig.title}

    +
    {siteMetadata.siteVersion}
    +
    {siteMetadata.docusaurusVersion}
    + {/* highlight-end */} +
    + ); +}; +``` + +:::note + +The `siteConfig` object only contains **serializable values** (values that are preserved after `JSON.stringify()`). Functions, regexes, etc. would be lost on the client side. + +::: + +### `useIsBrowser` {#useIsBrowser} + +Returns `true` when the React app has successfully hydrated in the browser. + +:::warning + +Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic. + +The first client-side render output (in the browser) **must be exactly the same** as the server-side render output (Node.js). Not following this rule can lead to unexpected hydration behaviors, as described in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/). + +::: + +Usage example: + +```jsx +import React from 'react'; +import useIsBrowser from '@docusaurus/useIsBrowser'; + +const MyComponent = () => { + // highlight-start + const isBrowser = useIsBrowser(); + // highlight-end + return
    {isBrowser ? 'Client' : 'Server'}
    ; +}; +``` + +### `useBaseUrl` {#useBaseUrl} + +React hook to prepend your site `baseUrl` to a string. + +:::warning + +**Don't use it for regular links!** + +The `/baseUrl/` prefix is automatically added to all **absolute paths** by default: + +- Markdown: `[link](/my/path)` will link to `/baseUrl/my/path` +- React: `link` will link to `/baseUrl/my/path` + +::: + +#### Options {#options} + +```ts +type BaseUrlOptions = { + forcePrependBaseUrl: boolean; + absolute: boolean; +}; +``` + +#### Example usage: {#example-usage} + +```jsx +import React from 'react'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +const SomeImage = () => { + // highlight-start + const imgSrc = useBaseUrl('/img/myImage.png'); + // highlight-end + return ; +}; +``` + +:::tip + +In most cases, you don't need `useBaseUrl`. + +Prefer a `require()` call for [assets](./guides/markdown-features/markdown-features-assets.mdx): + +```jsx + +``` + +::: + +### `useBaseUrlUtils` {#useBaseUrlUtils} + +Sometimes `useBaseUrl` is not good enough. This hook return additional utils related to your site's base URL. + +- `withBaseUrl`: useful if you need to add base URLs to multiple URLs at once. + +```jsx +import React from 'react'; +import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; + +const Component = () => { + const urls = ['/a', '/b']; + // highlight-start + const {withBaseUrl} = useBaseUrlUtils(); + const urlsWithBaseUrl = urls.map(withBaseUrl); + // highlight-end + return
    {/* ... */}
    ; +}; +``` + +### `useGlobalData` {#useGlobalData} + +React hook to access Docusaurus global data created by all the plugins. + +Global data is namespaced by plugin name then by plugin ID. + +:::info + +Plugin ID is only useful when a plugin is used multiple times on the same site. Each plugin instance is able to create its own global data. + +::: + +```ts +type GlobalData = Record< + PluginName, + Record< + PluginId, // "default" by default + any // plugin-specific data + > +>; +``` + +Usage example: + +```jsx +import React from 'react'; +// highlight-next-line +import useGlobalData from '@docusaurus/useGlobalData'; + +const MyComponent = () => { + // highlight-start + const globalData = useGlobalData(); + const myPluginData = globalData['my-plugin']['default']; + return
    {myPluginData.someAttribute}
    ; + // highlight-end +}; +``` + +:::tip + +Inspect your site's global data at `./docusaurus/globalData.json` + +::: + +### `usePluginData` {#usePluginData} + +Access global data created by a specific plugin instance. + +This is the most convenient hook to access plugin global data and should be used most of the time. + +`pluginId` is optional if you don't use multi-instance plugins. + +```ts +function usePluginData( + pluginName: string, + pluginId?: string, + options?: {failfast?: boolean}, +); +``` + +Usage example: + +```jsx +import React from 'react'; +// highlight-next-line +import {usePluginData} from '@docusaurus/useGlobalData'; + +const MyComponent = () => { + // highlight-start + const myPluginData = usePluginData('my-plugin'); + return
    {myPluginData.someAttribute}
    ; + // highlight-end +}; +``` + +### `useAllPluginInstancesData` {#useAllPluginInstancesData} + +Access global data created by a specific plugin. Given a plugin name, it returns the data of all the plugins instances of that name, by plugin id. + +```ts +function useAllPluginInstancesData( + pluginName: string, + options?: {failfast?: boolean}, +); +``` + +Usage example: + +```jsx +import React from 'react'; +// highlight-next-line +import {useAllPluginInstancesData} from '@docusaurus/useGlobalData'; + +const MyComponent = () => { + // highlight-start + const allPluginInstancesData = useAllPluginInstancesData('my-plugin'); + const myPluginData = allPluginInstancesData['default']; + return
    {myPluginData.someAttribute}
    ; + // highlight-end +}; +``` + +### `useBrokenLinks` {#useBrokenLinks} + +React hook to access the Docusaurus broken link checker APIs, exposing a way for a Docusaurus pages to report and collect their links and anchors. :::warning This is an **advanced** API that **most Docusaurus users don't need to use directly**. + +It is already **built-in** in existing high-level components: + +- the [``](#link) component will collect links for you +- the `@theme/Heading` (used for Markdown headings) will collect anchors + +Use `useBrokenLinks()` if you implement your own `` or `` component. + +::: + +Usage example: + +```js title="MyHeading.js" +import useBrokenLinks from '@docusaurus/useBrokenLinks'; + +export default function MyHeading(props) { + useBrokenLinks().collectAnchor(props.id); + return

    ; +} +``` + +```js title="MyLink.js" +import useBrokenLinks from '@docusaurus/useBrokenLinks'; + +export default function MyLink(props) { + useBrokenLinks().collectLink(props.href); + return ; +} +``` + +## Functions {#functions} + +### `interpolate` {#interpolate-1} + +The imperative counterpart of the [``](#interpolate) component. + +#### Signature {#signature} + +```ts +// Simple string interpolation +function interpolate(text: string, values: Record): string; + +// JSX interpolation +function interpolate( + text: string, + values: Record, +): ReactNode; +``` + +#### Example {#example-1} + +```js +// highlight-next-line +import {interpolate} from '@docusaurus/Interpolate'; + +const message = interpolate('Welcome {firstName}', {firstName: 'Sébastien'}); +``` + +### `translate` {#translate-imperative} + +The imperative counterpart of the [``](#translate) component. Also supporting [placeholders interpolation](#interpolate). + +:::tip + +Use the imperative API for the **rare cases** where a **component cannot be used**, such as: + +- the page `title` metadata +- the `placeholder` props of form inputs +- the `aria-label` props for accessibility + +::: + +#### Signature {#signature-1} + +```ts +function translate( + translation: {message: string; id?: string; description?: string}, + values: Record, +): string; +``` + +#### Example {#example-2} + +```jsx title="src/pages/index.js" +import React from 'react'; +import Layout from '@theme/Layout'; + +// highlight-next-line +import {translate} from '@docusaurus/Translate'; + +export default function Home() { + return ( + + + + ); +} +``` + +## Modules {#modules} + +### `ExecutionEnvironment` {#executionenvironment} + +A module that exposes a few boolean variables to check the current rendering environment. + +:::warning + +For React rendering logic, use [`useIsBrowser()`](#useIsBrowser) or [``](#browseronly) instead. + +::: + +Example: + +```js +import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; + +if (ExecutionEnvironment.canUseDOM) { + require('lib-that-only-works-client-side'); +} +``` + +| Field | Description | +| --- | --- | +| `ExecutionEnvironment.canUseDOM` | `true` if on client/browser, `false` on Node.js/prerendering. | +| `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. | +| `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. | +| `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. | + +### `constants` {#constants} + +A module exposing useful constants to client-side theme code. + +```js +import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; +``` + +| Named export | Value | +| ------------------- | --------- | +| `DEFAULT_PLUGIN_ID` | `default` | diff --git a/website/versioned_docs/version-3.1.0/guides/docs/sidebar/items.mdx b/website/versioned_docs/version-3.1.0/guides/docs/sidebar/items.mdx new file mode 100644 index 000000000000..1dd0c0100e78 --- /dev/null +++ b/website/versioned_docs/version-3.1.0/guides/docs/sidebar/items.mdx @@ -0,0 +1,619 @@ +--- +toc_max_heading_level: 4 +slug: /sidebar/items +--- + +# Sidebar items + +```mdx-code-block +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import BrowserWindow from '@site/src/components/BrowserWindow'; +``` + +We have introduced three types of item types in the example in the previous section: `doc`, `category`, and `link`, whose usages are fairly intuitive. We will formally introduce their APIs. There's also a fourth type: `autogenerated`, which we will explain in detail later. + +- **[Doc](#sidebar-item-doc)**: link to a doc page, associating it with the sidebar +- **[Link](#sidebar-item-link)**: link to any internal or external page +- **[Category](#sidebar-item-category)**: creates a dropdown of sidebar items +- **[Autogenerated](autogenerated.mdx)**: generate a sidebar slice automatically +- **[HTML](#sidebar-item-html)**: renders pure HTML in the item's position +- **[\*Ref](multiple-sidebars.mdx#sidebar-item-ref)**: link to a doc page, without making the item take part in navigation generation + +## Doc: link to a doc {#sidebar-item-doc} + +Use the `doc` type to link to a doc page and assign that doc to a sidebar: + +```ts +type SidebarItemDoc = + // Normal syntax + | { + type: 'doc'; + id: string; + label: string; // Sidebar label text + className?: string; // Class name for sidebar label + customProps?: Record; // Custom props + } + + // Shorthand syntax + | string; // docId shortcut +``` + +Example: + +```js title="sidebars.js" +export default { + mySidebar: [ + // Normal syntax: + // highlight-start + { + type: 'doc', + id: 'doc1', // document ID + label: 'Getting started', // sidebar label + }, + // highlight-end + + // Shorthand syntax: + // highlight-start + 'doc2', // document ID + // highlight-end + ], +}; +``` + +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](./multiple-sidebars.mdx#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead. + +::: + +:::tip + +Sidebar custom props is a useful way to propagate arbitrary doc metadata to the client side, so you can get additional information when using any doc-related hook that fetches a doc object. + +::: + +## Link: link to any page {#sidebar-item-link} + +Use the `link` type to link to any page (internal or external) that is not a doc. + +```ts +type SidebarItemLink = { + type: 'link'; + label: string; + href: string; + className?: string; + description?: string; +}; +``` + +Example: + +```js title="sidebars.js" +export default { + myLinksSidebar: [ + // highlight-start + // External link + { + type: 'link', + label: 'Facebook', // The link label + href: 'https://facebook.com', // The external URL + }, + // highlight-end + + // highlight-start + // Internal link + { + type: 'link', + label: 'Home', // The link label + href: '/', // The internal path + }, + // highlight-end + ], +}; +``` + +## HTML: render custom markup {#sidebar-item-html} + +Use the `html` type to render custom HTML within the item's `
  • ` tag. + +This can be useful for inserting custom items such as dividers, section titles, ads, and images. + +```ts +type SidebarItemHtml = { + type: 'html'; + value: string; + defaultStyle?: boolean; // Use default menu item styles + className?: string; +}; +``` + +Example: + +```js title="sidebars.js" +export default { + myHtmlSidebar: [ + // highlight-start + { + type: 'html', + value: 'Sponsor', // The HTML to be rendered + defaultStyle: true, // Use the default menu item styling + }, + // highlight-end + ], +}; +``` + +:::tip + +The menu item is already wrapped in an `
  • ` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it: + +```js title="sidebars.js" +export default { + myHtmlSidebar: [ + { + type: 'html', + value: 'Core concepts', + className: 'sidebar-title', + }, + ], +}; +``` + +::: + +## Category: create a hierarchy {#sidebar-item-category} + +Use the `category` type to create a hierarchy of sidebar items. + +```ts +type SidebarItemCategory = { + type: 'category'; + label: string; // Sidebar label text. + items: SidebarItem[]; // Array of sidebar items. + className?: string; + description?: string; + + // Category options: + collapsible: boolean; // Set the category to be collapsible + collapsed: boolean; // Set the category to be initially collapsed or open by default + link: SidebarItemCategoryLinkDoc | SidebarItemCategoryLinkGeneratedIndex; +}; +``` + +Example: + +```js title="sidebars.js" +export default { + docs: [ + { + type: 'category', + label: 'Guides', + collapsible: true, + collapsed: false, + items: [ + 'creating-pages', + { + type: 'category', + label: 'Docs', + items: ['introduction', 'sidebar', 'markdown-features', 'versioning'], + }, + ], + }, + ], +}; +``` + +:::tip + +Use the [**shorthand syntax**](#category-shorthand) when you don't need customizations: + +```js title="sidebars.js" +export default { + docs: { + Guides: [ + 'creating-pages', + { + Docs: ['introduction', 'sidebar', 'markdown-features', 'versioning'], + }, + ], + }, +}; +``` + +::: + +### Category links {#category-link} + +With category links, clicking on a category can navigate you to another page. + +:::tip + +Use category links to introduce a category of documents. + +Autogenerated categories can use the [`_category_.yml`](./autogenerated.mdx#category-item-metadata) file to declare the link. + +::: + +#### Generated index page {#generated-index-page} + +You can auto-generate an index page that displays all the direct children of this category. The `slug` allows you to customize the generated page's route, which defaults to `/category/[categoryName]`. + +```js title="sidebars.js" +export default { + docs: [ + { + type: 'category', + label: 'Guides', + // highlight-start + link: { + type: 'generated-index', + title: 'Docusaurus Guides', + description: 'Learn about the most important Docusaurus concepts!', + slug: '/category/docusaurus-guides', + keywords: ['guides'], + image: '/img/docusaurus.png', + }, + // highlight-end + items: ['pages', 'docs', 'blog', 'search'], + }, + ], +}; +``` + +See it in action on the [Docusaurus Guides page](/docs/category/guides). + +:::tip + +Use `generated-index` links as a quick way to get an introductory document. + +::: + +#### Doc link {#category-doc-link} + +A category can link to an existing document. + +```js title="sidebars.js" +export default { + docs: [ + { + type: 'category', + label: 'Guides', + // highlight-start + link: {type: 'doc', id: 'introduction'}, + // highlight-end + items: ['pages', 'docs', 'blog', 'search'], + }, + ], +}; +``` + +See it in action on the [i18n introduction page](../../../i18n/i18n-introduction.mdx). + +#### Embedding generated index in doc page {#embedding-generated-index-in-doc-page} + +You can embed the generated cards list in a normal doc page as well with the `DocCardList` component. It will display all the sidebar items of the parent category of the current document. + +```md title="docs/sidebar/index.md" +import DocCardList from '@theme/DocCardList'; + + +``` + +```mdx-code-block + + +import DocCardList from '@theme/DocCardList'; + + + + +``` + +### Collapsible categories {#collapsible-categories} + +We support the option to expand/collapse categories. Categories are collapsible by default, but you can disable collapsing with `collapsible: false`. + +```js title="sidebars.js" +export default { + docs: [ + { + type: 'category', + label: 'Guides', + items: [ + 'creating-pages', + { + type: 'category', + // highlight-next-line + collapsible: false, + label: 'Docs', + items: ['introduction', 'sidebar', 'markdown-features', 'versioning'], + }, + ], + }, + ], +}; +``` + +To make all categories non-collapsible by default, set the `sidebarCollapsible` option in `plugin-content-docs` to `false`: + +```js title="docusaurus.config.js" +export default { + presets: [ + [ + '@docusaurus/preset-classic', + { + docs: { + // highlight-next-line + sidebarCollapsible: false, + }, + }, + ], + ], +}; +``` + +:::note + +The option in `sidebars.js` takes precedence over plugin configuration, so it is possible to make certain categories collapsible when `sidebarCollapsible` is set to `false` globally. + +::: + +### Expanded categories by default {#expanded-categories-by-default} + +Collapsible categories are collapsed by default. If you want them to be expanded on the first render, you can set `collapsed` to `false`: + +```js title="sidebars.js" +export default { + docs: { + Guides: [ + 'creating-pages', + { + type: 'category', + label: 'Docs', + // highlight-next-line + collapsed: false, + items: ['markdown-features', 'sidebar', 'versioning'], + }, + ], + }, +}; +``` + +Similar to `collapsible`, you can also set the global configuration `options.sidebarCollapsed` to `false`. Individual `collapsed` options in `sidebars.js` will still take precedence over this configuration. + +```js title="docusaurus.config.js" +export default { + presets: [ + [ + '@docusaurus/preset-classic', + { + docs: { + // highlight-next-line + sidebarCollapsed: false, + }, + }, + ], + ], +}; +``` + +:::warning + +When a category has `collapsed: true` but `collapsible: false` (either through `sidebars.js` or through plugin configuration), the latter takes precedence and the category is still rendered as expanded. + +::: + +## Using shorthands {#using-shorthands} + +You can express typical sidebar items without much customization more concisely with **shorthand syntaxes**. There are two parts to this: [**doc shorthand**](#doc-shorthand) and [**category shorthand**](#category-shorthand). + +### Doc shorthand {#doc-shorthand} + +An item with type `doc` can be simply a string representing its ID: + +```mdx-code-block + + +``` + +```js title="sidebars.js" +export default { + sidebar: [ + // highlight-start + { + type: 'doc', + id: 'myDoc', + }, + // highlight-end + ], +}; +``` + +```mdx-code-block + + +``` + +```js title="sidebars.js" +export default { + sidebar: [ + // highlight-start + 'myDoc', + // highlight-end + ], +}; +``` + +```mdx-code-block + + +``` + +So it's possible to simplify the example above to: + +```js title="sidebars.js" +export default { + mySidebar: [ + { + type: 'category', + label: 'Getting Started', + items: [ + // highlight-next-line + 'doc1', + ], + }, + { + type: 'category', + label: 'Docusaurus', + items: [ + // highlight-start + 'doc2', + 'doc3', + // highlight-end + ], + }, + { + type: 'link', + label: 'Learn more', + href: 'https://example.com', + }, + ], +}; +``` + +### Category shorthand {#category-shorthand} + +A category item can be represented by an object whose key is its label, and the value is an array of subitems. + +```mdx-code-block + + +``` + +```js title="sidebars.js" +export default { + sidebar: [ + // highlight-start + { + type: 'category', + label: 'Getting started', + items: ['doc1', 'doc2'], + }, + // highlight-end + ], +}; +``` + +```mdx-code-block + + +``` + +```js title="sidebars.js" +export default { + sidebar: [ + // highlight-start + { + 'Getting started': ['doc1', 'doc2'], + }, + // highlight-end + ], +}; +``` + +```mdx-code-block + + +``` + +This permits us to simplify that example to: + +```js title="sidebars.js" +export default { + mySidebar: [ + // highlight-start + { + 'Getting started': ['doc1'], + }, + { + Docusaurus: ['doc2', 'doc3'], + }, + // highlight-end + { + type: 'link', + label: 'Learn more', + href: 'https://example.com', + }, + ], +}; +``` + +Each shorthand object after this transformation will contain exactly one entry. Now consider the further simplified example below: + +```js title="sidebars.js" +export default { + mySidebar: [ + // highlight-start + { + 'Getting started': ['doc1'], + Docusaurus: ['doc2', 'doc3'], + }, + // highlight-end + { + type: 'link', + label: 'Learn more', + href: 'https://example.com', + }, + ], +}; +``` + +Note how the two consecutive category shorthands are compressed into one object with two entries. This syntax generates a **sidebar slice**: you shouldn't see that object as one bulk item—this object is unwrapped, with each entry becoming a separate item, and they spliced together with the rest of the items (in this case, the "Learn more" link) to form the final sidebar level. Sidebar slices are also important when discussing [autogenerated sidebars](autogenerated.mdx). + +Wherever you have an array of items that is reduced to one category shorthand, you can omit that enclosing array as well. + +```mdx-code-block + + +``` + +```js title="sidebars.js" +export default { + sidebar: [ + { + 'Getting started': ['doc1'], + Docusaurus: [ + { + 'Basic guides': ['doc2', 'doc3'], + 'Advanced guides': ['doc4', 'doc5'], + }, + ], + }, + ], +}; +``` + +```mdx-code-block + + +``` + +```js title="sidebars.js" +export default { + sidebar: { + 'Getting started': ['doc1'], + Docusaurus: { + 'Basic guides': ['doc2', 'doc3'], + 'Advanced guides': ['doc4', 'doc5'], + }, + }, +}; +``` + +```mdx-code-block + + +``` diff --git a/website/versioned_docs/version-3.1.0/migration/v2/migration-automated.mdx b/website/versioned_docs/version-3.1.0/migration/v2/migration-automated.mdx new file mode 100644 index 000000000000..ff4139d2e71d --- /dev/null +++ b/website/versioned_docs/version-3.1.0/migration/v2/migration-automated.mdx @@ -0,0 +1,75 @@ +--- +slug: /migration/v2/automated +--- + +# Automated migration + +The migration CLI automatically migrates your v1 website to a v2 website. + +:::info + +Manual work is still required after using the migration CLI, as we can't automate a full migration + +::: + +The migration CLI migrates: + +- Site configurations (from `siteConfig.js` to `docusaurus.config.js`) +- `package.json` +- `sidebars.json` +- `/docs` +- `/blog` +- `/static` +- `versioned_sidebar.json` and `/versioned_docs` if your site uses versioning + +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 expected structure. + +2. To migrate your v1 website, run the migration CLI with the appropriate filesystem paths: + +```bash +# migration command format +npx @docusaurus/migrate migrate + +# example +npx @docusaurus/migrate migrate ./v1-website ./v2-website +``` + +3. To view your new website locally, go into your v2 website's directory and start your development server. + +```bash npm2yarn +cd ./v2-website +npm install +npm start +``` + +:::danger + +The migration CLI updates existing files. Be sure to have committed them first! + +::: + +#### Options {#options} + +You can add option flags to the migration CLI to automatically migrate Markdown content and pages to v2. It is likely that you will still need to make some manual changes to achieve your desired result. + +| Name | Description | +| -------- | ------------------------------------------------------ | +| `--mdx` | Add this flag to convert Markdown to MDX automatically | +| `--page` | Add this flag to migrate pages automatically | + +```bash +# example using options +npx @docusaurus/migrate migrate --mdx --page ./v1-website ./v2-website +``` + +:::danger + +The migration of pages and MDX is still a work in progress. + +We recommend you to try to run the pages without these options, commit, and then try to run the migration again with the `--page` and `--mdx` options. + +This way, you'd be able to easily inspect and fix the diff. + +::: diff --git a/yarn.lock b/yarn.lock index b7eb7ddca010..6ff7aff3da2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3378,13 +3378,6 @@ dependencies: "@types/unist" "*" -"@types/mdx-js__react@^1.5.5": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@types/mdx-js__react/-/mdx-js__react-1.5.5.tgz#fa6daa1a28336d77b6cf071aacc7e497600de9ee" - integrity sha512-k8pnaP6JXVlQh18HgL5X6sYFNC/qZnzO7R2+HsmwrwUd+JnnsU0d9lyyT0RQrHg1anxDU36S98TI/fsGtmYqqg== - dependencies: - "@types/react" "*" - "@types/mdx@^2.0.0": version "2.0.5" resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.5.tgz#9a85a8f70c7c4d9e695a21d5ae5c93645eda64b1"