diff --git a/.cspell.json b/.cspell.json index ba31d09cfd..7eb6731582 100644 --- a/.cspell.json +++ b/.cspell.json @@ -28,6 +28,7 @@ "__snapshots__", "website/src/data/users.tsx", "website/src/data/tweets.tsx", + "website/docusaurus.config.localized.json", "*.xyz", "*.docx", "versioned_docs", diff --git a/admin/new.docusaurus.io/package.json b/admin/new.docusaurus.io/package.json index 2a30ef8bda..d12196b7ca 100644 --- a/admin/new.docusaurus.io/package.json +++ b/admin/new.docusaurus.io/package.json @@ -1,6 +1,6 @@ { "name": "new.docusaurus.io", - "version": "2.3.1", + "version": "2.4.0", "private": true, "scripts": { "start": "npx --package netlify-cli netlify dev" diff --git a/lerna.json b/lerna.json index 2c27934e86..1df32d556b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.3.1", + "version": "2.4.0", "npmClient": "yarn", "useWorkspaces": true, "changelog": { diff --git a/packages/create-docusaurus/package.json b/packages/create-docusaurus/package.json index 1e0e270f08..33a79840d1 100755 --- a/packages/create-docusaurus/package.json +++ b/packages/create-docusaurus/package.json @@ -1,6 +1,6 @@ { "name": "create-docusaurus", - "version": "2.3.1", + "version": "2.4.0", "description": "Create Docusaurus apps easily.", "type": "module", "repository": { @@ -22,8 +22,8 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", "commander": "^5.1.0", "fs-extra": "^10.1.0", "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 418bbddf8c..6cbe169936 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": "2.3.1", + "version": "2.4.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -15,8 +15,8 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/preset-classic": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/preset-classic": "2.4.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "prism-react-renderer": "^1.3.5", @@ -24,7 +24,7 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1", + "@docusaurus/module-type-aliases": "2.4.0", "@tsconfig/docusaurus": "^1.0.5", "typescript": "^4.7.4" }, diff --git a/packages/create-docusaurus/templates/classic/docusaurus.config.js b/packages/create-docusaurus/templates/classic/docusaurus.config.js index d664b65021..a8561d1a6b 100644 --- a/packages/create-docusaurus/templates/classic/docusaurus.config.js +++ b/packages/create-docusaurus/templates/classic/docusaurus.config.js @@ -71,8 +71,8 @@ const config = { }, items: [ { - type: 'doc', - docId: 'intro', + type: 'docSidebar', + sidebarId: 'tutorialSidebar', position: 'left', label: 'Tutorial', }, diff --git a/packages/create-docusaurus/templates/classic/package.json b/packages/create-docusaurus/templates/classic/package.json index c792154ba0..370a4d4231 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": "2.3.1", + "version": "2.4.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -14,8 +14,8 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/preset-classic": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/preset-classic": "2.4.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "prism-react-renderer": "^1.3.5", @@ -23,7 +23,7 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1" + "@docusaurus/module-type-aliases": "2.4.0" }, "browserslist": { "production": [ diff --git a/packages/create-docusaurus/templates/facebook/docusaurus.config.js b/packages/create-docusaurus/templates/facebook/docusaurus.config.js index 69844a2af9..a683166751 100644 --- a/packages/create-docusaurus/templates/facebook/docusaurus.config.js +++ b/packages/create-docusaurus/templates/facebook/docusaurus.config.js @@ -67,8 +67,8 @@ const config = { }, items: [ { - type: 'doc', - docId: 'intro', + type: 'docSidebar', + sidebarId: 'tutorialSidebar', position: 'left', label: 'Tutorial', }, diff --git a/packages/create-docusaurus/templates/facebook/package.json b/packages/create-docusaurus/templates/facebook/package.json index e70f7b017b..e876b63405 100644 --- a/packages/create-docusaurus/templates/facebook/package.json +++ b/packages/create-docusaurus/templates/facebook/package.json @@ -1,6 +1,6 @@ { "name": "docusaurus-2-facebook-template", - "version": "2.3.1", + "version": "2.4.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -18,8 +18,8 @@ "format:diff": "prettier --config .prettierrc --list-different \"**/*.{js,jsx,ts,tsx,md,mdx}\"" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/preset-classic": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/preset-classic": "2.4.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "react": "^17.0.2", diff --git a/packages/docusaurus-cssnano-preset/package.json b/packages/docusaurus-cssnano-preset/package.json index 67f64b46b3..f88e255d46 100644 --- a/packages/docusaurus-cssnano-preset/package.json +++ b/packages/docusaurus-cssnano-preset/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/cssnano-preset", - "version": "2.3.1", + "version": "2.4.0", "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 ef1cfeb8c2..1d1cfe23ed 100644 --- a/packages/docusaurus-logger/package.json +++ b/packages/docusaurus-logger/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/logger", - "version": "2.3.1", + "version": "2.4.0", "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 15f6f49ba3..1bf60aa401 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/mdx-loader", - "version": "2.3.1", + "version": "2.4.0", "description": "Docusaurus Loader for MDX", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -20,8 +20,8 @@ "dependencies": { "@babel/parser": "^7.18.8", "@babel/traverse": "^7.18.8", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", "@mdx-js/mdx": "^1.6.22", "escape-html": "^1.0.3", "file-loader": "^6.2.0", @@ -37,7 +37,7 @@ "webpack": "^5.73.0" }, "devDependencies": { - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.0", "@types/escape-html": "^1.0.2", "@types/mdast": "^3.0.10", "@types/stringify-object": "^3.3.1", diff --git a/packages/docusaurus-migrate/package.json b/packages/docusaurus-migrate/package.json index b99c886aae..b76a328cef 100644 --- a/packages/docusaurus-migrate/package.json +++ b/packages/docusaurus-migrate/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/migrate", - "version": "2.3.1", + "version": "2.4.0", "description": "A CLI tool to migrate from older versions of Docusaurus.", "license": "MIT", "engines": { @@ -24,8 +24,8 @@ "dependencies": { "@babel/core": "^7.18.6", "@babel/preset-env": "^7.18.6", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", "@mapbox/hast-util-to-jsx": "^2.0.0", "color": "^4.2.3", "commander": "^5.1.0", diff --git a/packages/docusaurus-module-type-aliases/package.json b/packages/docusaurus-module-type-aliases/package.json index e3d23c030a..66c0bb2cad 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": "2.3.1", + "version": "2.4.0", "description": "Docusaurus module type aliases.", "types": "./src/index.d.ts", "publishConfig": { @@ -13,7 +13,7 @@ }, "dependencies": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", diff --git a/packages/docusaurus-plugin-client-redirects/package.json b/packages/docusaurus-plugin-client-redirects/package.json index b3f2db9e68..2375778c90 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": "2.3.1", + "version": "2.4.0", "description": "Client redirects plugin for Docusaurus.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -18,18 +18,18 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "eta": "^2.0.0", "fs-extra": "^10.1.0", "lodash": "^4.17.21", "tslib": "^2.4.0" }, "devDependencies": { - "@docusaurus/types": "2.3.1" + "@docusaurus/types": "2.4.0" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index 92ce58e7f9..a3d95905da 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": "2.3.1", + "version": "2.4.0", "description": "Blog plugin for Docusaurus.", "main": "lib/index.js", "types": "src/plugin-content-blog.d.ts", @@ -18,13 +18,13 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^10.1.0", diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index ef9f23b644..2dbd09accd 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": "2.3.1", + "version": "2.4.0", "description": "Docs plugin for Docusaurus.", "main": "lib/index.js", "sideEffects": false, @@ -35,13 +35,13 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@types/react-router-config": "^5.0.6", "combine-promises": "^1.1.0", "fs-extra": "^10.1.0", diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts index 555004846e..6b6dd0f862 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts @@ -45,6 +45,7 @@ export type SidebarItemLink = SidebarItemBase & { href: string; label: string; autoAddBaseUrl?: boolean; + description?: string; }; export type SidebarItemAutogenerated = SidebarItemBase & { @@ -57,6 +58,7 @@ type SidebarItemCategoryBase = SidebarItemBase & { label: string; collapsed: boolean; collapsible: boolean; + description?: string; }; export type SidebarItemCategoryLinkDoc = {type: 'doc'; id: string}; diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts index 0273132360..e65eb41960 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts @@ -63,6 +63,9 @@ const sidebarItemLinkSchema = sidebarItemBaseSchema.append({ label: Joi.string() .required() .messages({'any.unknown': '"label" must be a string'}), + description: Joi.string().optional().messages({ + 'any.unknown': '"description" must be a string', + }), }); const sidebarItemCategoryLinkSchema = Joi.object() @@ -116,6 +119,9 @@ const sidebarItemCategorySchema = collapsible: Joi.boolean().messages({ 'any.unknown': '"collapsible" must be a boolean', }), + description: Joi.string().optional().messages({ + 'any.unknown': '"description" must be a string', + }), }); const sidebarItemSchema = Joi.object().when('.type', { diff --git a/packages/docusaurus-plugin-content-pages/package.json b/packages/docusaurus-plugin-content-pages/package.json index 0c9f0146f0..34d2909dba 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": "2.3.1", + "version": "2.4.0", "description": "Pages plugin for Docusaurus.", "main": "lib/index.js", "types": "src/plugin-content-pages.d.ts", @@ -18,11 +18,11 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "fs-extra": "^10.1.0", "tslib": "^2.4.0", "webpack": "^5.73.0" diff --git a/packages/docusaurus-plugin-debug/package.json b/packages/docusaurus-plugin-debug/package.json index f76580c784..8197cd1872 100644 --- a/packages/docusaurus-plugin-debug/package.json +++ b/packages/docusaurus-plugin-debug/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/plugin-debug", - "version": "2.3.1", + "version": "2.4.0", "description": "Debug plugin for Docusaurus.", "main": "lib/index.js", "types": "src/plugin-debug.d.ts", @@ -20,9 +20,9 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", "fs-extra": "^10.1.0", "react-json-view": "^1.21.3", "tslib": "^2.4.0" diff --git a/packages/docusaurus-plugin-google-analytics/package.json b/packages/docusaurus-plugin-google-analytics/package.json index 002e5cb638..f1e1f2569d 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": "2.3.1", + "version": "2.4.0", "description": "Global analytics (analytics.js) plugin for Docusaurus.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -18,9 +18,9 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "tslib": "^2.4.0" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-google-gtag/package.json b/packages/docusaurus-plugin-google-gtag/package.json index cd32f819a0..e2e893839d 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": "2.3.1", + "version": "2.4.0", "description": "Global Site Tag (gtag.js) plugin for Docusaurus.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -18,9 +18,9 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "tslib": "^2.4.0" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-google-gtag/src/__tests__/options.test.ts b/packages/docusaurus-plugin-google-gtag/src/__tests__/options.test.ts new file mode 100644 index 0000000000..a33ce7aff3 --- /dev/null +++ b/packages/docusaurus-plugin-google-gtag/src/__tests__/options.test.ts @@ -0,0 +1,156 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {normalizePluginOptions} from '@docusaurus/utils-validation'; +import { + validateOptions, + type PluginOptions, + type Options, + DEFAULT_OPTIONS, +} from '../options'; +import type {Validate} from '@docusaurus/types'; + +function testValidateOptions(options: Options) { + return validateOptions({ + validate: normalizePluginOptions as Validate, + options, + }); +} + +function validationResult(options: Options) { + return { + id: 'default', + ...DEFAULT_OPTIONS, + ...options, + trackingID: + typeof options.trackingID === 'string' + ? [options.trackingID] + : options.trackingID, + }; +} + +const MinimalConfig: Options = { + trackingID: 'G-XYZ12345', +}; + +describe('validateOptions', () => { + it('throws for undefined options', () => { + expect( + // @ts-expect-error: TS should error + () => testValidateOptions(undefined), + ).toThrowErrorMatchingInlineSnapshot(`""trackingID" is required"`); + }); + + it('throws for null options', () => { + expect( + // @ts-expect-error: TS should error + () => testValidateOptions(null), + ).toThrowErrorMatchingInlineSnapshot(`""value" must be of type object"`); + }); + + it('throws for empty object options', () => { + expect( + // @ts-expect-error: TS should error + () => testValidateOptions({}), + ).toThrowErrorMatchingInlineSnapshot(`""trackingID" is required"`); + }); + + it('throws for number options', () => { + expect( + // @ts-expect-error: TS should error + () => testValidateOptions(42), + ).toThrowErrorMatchingInlineSnapshot(`""value" must be of type object"`); + }); + + it('throws for null trackingID', () => { + expect( + // @ts-expect-error: TS should error + () => testValidateOptions({trackingID: null}), + ).toThrowErrorMatchingInlineSnapshot( + `""trackingID" does not match any of the allowed types"`, + ); + }); + it('throws for number trackingID', () => { + expect( + // @ts-expect-error: TS should error + () => testValidateOptions({trackingID: 42}), + ).toThrowErrorMatchingInlineSnapshot( + `""trackingID" does not match any of the allowed types"`, + ); + }); + it('throws for empty trackingID', () => { + expect(() => + testValidateOptions({trackingID: ''}), + ).toThrowErrorMatchingInlineSnapshot( + `""trackingID" does not match any of the allowed types"`, + ); + }); + + it('accepts minimal config', () => { + expect(testValidateOptions(MinimalConfig)).toEqual( + validationResult(MinimalConfig), + ); + }); + + it('accepts anonymizeIP', () => { + const config: Options = { + ...MinimalConfig, + anonymizeIP: true, + }; + expect(testValidateOptions(config)).toEqual(validationResult(config)); + }); + + it('accepts single trackingID', () => { + const config: Options = { + trackingID: 'G-ABCDEF123', + }; + expect(testValidateOptions(config)).toEqual(validationResult(config)); + }); + + it('accepts multiple trackingIDs', () => { + const config: Options = { + trackingID: ['G-ABCDEF123', 'UA-XYZ456789'], + }; + expect(testValidateOptions(config)).toEqual(validationResult(config)); + }); + + it('throws for empty trackingID arrays', () => { + const config: Options = { + // @ts-expect-error: TS should error + trackingID: [], + }; + expect(() => + testValidateOptions(config), + ).toThrowErrorMatchingInlineSnapshot( + `""trackingID" does not match any of the allowed types"`, + ); + }); + + it('throws for sparse trackingID arrays', () => { + const config: Options = { + // @ts-expect-error: TS should error + trackingID: ['G-ABCDEF123', null, 'UA-XYZ456789'], + }; + expect(() => + testValidateOptions(config), + ).toThrowErrorMatchingInlineSnapshot( + `""trackingID" does not match any of the allowed types"`, + ); + }); + + it('throws for bad trackingID arrays', () => { + const config: Options = { + // @ts-expect-error: TS should error + trackingID: ['G-ABCDEF123', 42], + }; + expect(() => + testValidateOptions(config), + ).toThrowErrorMatchingInlineSnapshot( + `""trackingID" does not match any of the allowed types"`, + ); + }); +}); diff --git a/packages/docusaurus-plugin-google-gtag/src/index.ts b/packages/docusaurus-plugin-google-gtag/src/index.ts index 2e278094e1..a3ea84ddc4 100644 --- a/packages/docusaurus-plugin-google-gtag/src/index.ts +++ b/packages/docusaurus-plugin-google-gtag/src/index.ts @@ -5,23 +5,38 @@ * LICENSE file in the root directory of this source tree. */ -import {Joi} from '@docusaurus/utils-validation'; -import type { - LoadContext, - Plugin, - OptionValidationContext, - ThemeConfig, - ThemeConfigValidationContext, -} from '@docusaurus/types'; +import type {LoadContext, Plugin} from '@docusaurus/types'; import type {PluginOptions, Options} from './options'; +function createConfigSnippet({ + trackingID, + anonymizeIP, +}: { + trackingID: string; + anonymizeIP: boolean; +}): string { + return `gtag('config', '${trackingID}', { ${ + anonymizeIP ? "'anonymize_ip': true" : '' + } });`; +} + +function createConfigSnippets({ + trackingID: trackingIDArray, + anonymizeIP, +}: PluginOptions): string { + return trackingIDArray + .map((trackingID) => createConfigSnippet({trackingID, anonymizeIP})) + .join('\n'); +} + export default function pluginGoogleGtag( context: LoadContext, options: PluginOptions, ): Plugin { - const {anonymizeIP, trackingID} = options; const isProd = process.env.NODE_ENV === 'production'; + const firstTrackingId = options.trackingID[0]; + return { name: 'docusaurus-plugin-google-gtag', @@ -60,7 +75,11 @@ export default function pluginGoogleGtag( tagName: 'script', attributes: { async: true, - src: `https://www.googletagmanager.com/gtag/js?id=${trackingID}`, + // We only include the first tracking id here because google says + // we shouldn't install multiple tags/scripts on the same page + // Instead we should load one script and use n * gtag("config",id) + // See https://developers.google.com/tag-platform/gtagjs/install#add-products + src: `https://www.googletagmanager.com/gtag/js?id=${firstTrackingId}`, }, }, { @@ -69,9 +88,8 @@ export default function pluginGoogleGtag( window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); - gtag('config', '${trackingID}', { ${ - anonymizeIP ? "'anonymize_ip': true" : '' - } });`, + ${createConfigSnippets(options)}; + `, }, ], }; @@ -79,27 +97,6 @@ export default function pluginGoogleGtag( }; } -const pluginOptionsSchema = Joi.object({ - trackingID: Joi.string().required(), - anonymizeIP: Joi.boolean().default(false), -}); - -export function validateOptions({ - validate, - options, -}: OptionValidationContext): PluginOptions { - return validate(pluginOptionsSchema, options); -} - -export function validateThemeConfig({ - themeConfig, -}: ThemeConfigValidationContext): ThemeConfig { - if ('gtag' in themeConfig) { - throw new Error( - 'The "gtag" field in themeConfig should now be specified as option for plugin-google-gtag. More information at https://github.com/facebook/docusaurus/pull/5832.', - ); - } - return themeConfig; -} +export {validateThemeConfig, validateOptions} from './options'; export type {PluginOptions, Options}; diff --git a/packages/docusaurus-plugin-google-gtag/src/options.ts b/packages/docusaurus-plugin-google-gtag/src/options.ts index c23d43a7b4..b2f6fc1bc6 100644 --- a/packages/docusaurus-plugin-google-gtag/src/options.ts +++ b/packages/docusaurus-plugin-google-gtag/src/options.ts @@ -4,10 +4,58 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +import {Joi} from '@docusaurus/utils-validation'; +import type { + OptionValidationContext, + ThemeConfig, + ThemeConfigValidationContext, +} from '@docusaurus/types'; export type PluginOptions = { - trackingID: string; + trackingID: [string, ...string[]]; + // TODO deprecate anonymizeIP after June 2023 + // "In Google Analytics 4, IP masking is not necessary + // since IP addresses are not logged or stored." + // https://support.google.com/analytics/answer/2763052?hl=en anonymizeIP: boolean; }; -export type Options = Partial; +export type Options = { + trackingID: string | [string, ...string[]]; + anonymizeIP?: boolean; +}; + +export const DEFAULT_OPTIONS: Partial = { + anonymizeIP: false, +}; + +const pluginOptionsSchema = Joi.object({ + // We normalize trackingID as a string[] + trackingID: Joi.alternatives() + .try( + Joi.alternatives().conditional(Joi.string().required(), { + then: Joi.custom((val: boolean) => [val]), + }), + Joi.array().items(Joi.string().required()), + ) + .required(), + anonymizeIP: Joi.boolean().default(DEFAULT_OPTIONS.anonymizeIP), +}); + +export function validateOptions({ + validate, + options, +}: OptionValidationContext): PluginOptions { + return validate(pluginOptionsSchema, options); +} + +export function validateThemeConfig({ + themeConfig, +}: ThemeConfigValidationContext): ThemeConfig { + if ('gtag' in themeConfig) { + throw new Error( + 'The "gtag" field in themeConfig should now be specified as option for plugin-google-gtag. More information at https://github.com/facebook/docusaurus/pull/5832.', + ); + } + return themeConfig; +} diff --git a/packages/docusaurus-plugin-google-gtag/tsconfig.client.json b/packages/docusaurus-plugin-google-gtag/tsconfig.client.json index 985b9a4580..4bf4b50bbf 100644 --- a/packages/docusaurus-plugin-google-gtag/tsconfig.client.json +++ b/packages/docusaurus-plugin-google-gtag/tsconfig.client.json @@ -10,6 +10,6 @@ "rootDir": "src", "outDir": "lib" }, - "include": ["src/gtag.ts", "src/options.ts", "src/*.d.ts"], + "include": ["src/gtag.ts", "src/*.d.ts"], "exclude": ["**/__tests__/**"] } diff --git a/packages/docusaurus-plugin-google-tag-manager/package.json b/packages/docusaurus-plugin-google-tag-manager/package.json index feeeb85292..291287550a 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": "2.3.1", + "version": "2.4.0", "description": "Google Tag Manager (gtm.js) plugin for Docusaurus.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -18,9 +18,9 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "tslib": "^2.4.0" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-ideal-image/package.json b/packages/docusaurus-plugin-ideal-image/package.json index 1cfd377565..a1de1bf416 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": "2.3.1", + "version": "2.4.0", "description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).", "main": "lib/index.js", "types": "src/plugin-ideal-image.d.ts", @@ -20,12 +20,12 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/lqip-loader": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/lqip-loader": "2.4.0", "@docusaurus/responsive-loader": "^1.7.0", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@endiliey/react-ideal-image": "^0.0.11", "react-waypoint": "^10.3.0", "sharp": "^0.30.7", @@ -33,7 +33,7 @@ "webpack": "^5.73.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1", + "@docusaurus/module-type-aliases": "2.4.0", "fs-extra": "^10.1.0" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-pwa/package.json b/packages/docusaurus-plugin-pwa/package.json index 0f1e318f2a..872a3286bb 100644 --- a/packages/docusaurus-plugin-pwa/package.json +++ b/packages/docusaurus-plugin-pwa/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/plugin-pwa", - "version": "2.3.1", + "version": "2.4.0", "description": "Docusaurus Plugin to add PWA support.", "main": "lib/index.js", "types": "src/plugin-pwa.d.ts", @@ -22,12 +22,12 @@ "dependencies": { "@babel/core": "^7.18.6", "@babel/preset-env": "^7.18.6", - "@docusaurus/core": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "babel-loader": "^8.2.5", "clsx": "^1.2.1", "core-js": "^3.23.3", @@ -40,7 +40,7 @@ "workbox-window": "^6.5.3" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1", + "@docusaurus/module-type-aliases": "2.4.0", "fs-extra": "^10.1.0" }, "peerDependencies": { diff --git a/packages/docusaurus-plugin-sitemap/package.json b/packages/docusaurus-plugin-sitemap/package.json index a3f9ec2a48..c579914503 100644 --- a/packages/docusaurus-plugin-sitemap/package.json +++ b/packages/docusaurus-plugin-sitemap/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/plugin-sitemap", - "version": "2.3.1", + "version": "2.4.0", "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": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "fs-extra": "^10.1.0", "sitemap": "^7.1.1", "tslib": "^2.4.0" diff --git a/packages/docusaurus-preset-classic/package.json b/packages/docusaurus-preset-classic/package.json index fde5fe41cb..5173dcdfd0 100644 --- a/packages/docusaurus-preset-classic/package.json +++ b/packages/docusaurus-preset-classic/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/preset-classic", - "version": "2.3.1", + "version": "2.4.0", "description": "Classic preset for Docusaurus.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -18,19 +18,19 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/plugin-debug": "2.3.1", - "@docusaurus/plugin-google-analytics": "2.3.1", - "@docusaurus/plugin-google-gtag": "2.3.1", - "@docusaurus/plugin-google-tag-manager": "2.3.1", - "@docusaurus/plugin-sitemap": "2.3.1", - "@docusaurus/theme-classic": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-search-algolia": "2.3.1", - "@docusaurus/types": "2.3.1" + "@docusaurus/core": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/plugin-debug": "2.4.0", + "@docusaurus/plugin-google-analytics": "2.4.0", + "@docusaurus/plugin-google-gtag": "2.4.0", + "@docusaurus/plugin-google-tag-manager": "2.4.0", + "@docusaurus/plugin-sitemap": "2.4.0", + "@docusaurus/theme-classic": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-search-algolia": "2.4.0", + "@docusaurus/types": "2.4.0" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", diff --git a/packages/docusaurus-remark-plugin-npm2yarn/README.md b/packages/docusaurus-remark-plugin-npm2yarn/README.md index 5fc6a1705e..055ca12d30 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/README.md +++ b/packages/docusaurus-remark-plugin-npm2yarn/README.md @@ -63,3 +63,30 @@ module.exports = { | Property | Type | Default | Description | | --- | --- | --- | --- | | `sync` | `boolean` | `false` | Syncing tab choices (Yarn and npm). See https://docusaurus.io/docs/markdown-features/#syncing-tab-choices for details. | +| `converters` | `array` | `'yarn'`, `'pnpm'` | The list of converters to use. The order of the converters is important, as the first converter will be used as the default choice. | + +## Custom converters + +In case you want to convert npm commands to something else than `yarn` or `pnpm`, you can use custom converters: + +```ts +type CustomConverter = [name: string, cb: (npmCode: string) => string]; +``` + +```ts +{ + remarkPlugins: [ + [ + require('@docusaurus/remark-plugin-npm2yarn'), + { + sync: true, + converters: [ + 'yarn', + 'pnpm', + ['Turbo', (code) => code.replace(/npm/g, 'turbo')], + ], + }, + ], + ]; +} +``` diff --git a/packages/docusaurus-remark-plugin-npm2yarn/package.json b/packages/docusaurus-remark-plugin-npm2yarn/package.json index a8b1829eac..95018e2567 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": "2.3.1", + "version": "2.4.0", "description": "Remark plugin for converting npm commands to Yarn commands as tabs.", "main": "lib/index.js", "publishConfig": { @@ -17,8 +17,8 @@ }, "license": "MIT", "dependencies": { - "npm-to-yarn": "^1.0.1", - "tslib": "^2.4.0", + "npm-to-yarn": "^2.0.0", + "tslib": "^2.4.1", "unist-util-visit": "^2.0.3" }, "devDependencies": { diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md new file mode 100644 index 0000000000..b0bf7f08a5 --- /dev/null +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md @@ -0,0 +1,16 @@ +```bash npm2yarn +npm run xxx -- --arg +``` + +```bash npm2yarn +npm install package +``` + +```bash npm2yarn +npm remove package-name +``` + +```bash npm2yarn +npm init docusaurus +npm init docusaurus@latest my-website classic +``` diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap index b8e77588d0..393470b312 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap @@ -6,6 +6,7 @@ exports[`npm2yarn plugin does not re-import tabs components when already importe import TabItem from '@theme/TabItem'; + \`\`\`bash @@ -13,19 +14,30 @@ import TabItem from '@theme/TabItem'; \`\`\` + \`\`\`bash - $ yarn add --global docusaurus + $ yarn global add docusaurus \`\`\` + + + +\`\`\`bash + $ pnpm add --global docusaurus +\`\`\` + + + " `; exports[`npm2yarn plugin does not re-import tabs components when already imported below 1`] = ` " + \`\`\`bash @@ -33,13 +45,23 @@ exports[`npm2yarn plugin does not re-import tabs components when already importe \`\`\` + \`\`\`bash - $ yarn add --global docusaurus + $ yarn global add docusaurus \`\`\` + + + +\`\`\`bash + $ pnpm add --global docusaurus +\`\`\` + + + import Tabs from '@theme/Tabs'; @@ -63,11 +85,102 @@ npm install --save docusaurus-plugin-name " `; +exports[`npm2yarn plugin work with custom converter 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Installing a plugin + +A plugin is usually a npm package, so you install them like other npm packages using npm. + + + + + +\`\`\`bash +npm install --save docusaurus-plugin-name +\`\`\` + + + + + +\`\`\`bash +turbo install --save docusaurus-plugin-name +\`\`\` + + + + +" +`; + +exports[`npm2yarn plugin work with pnpm converter 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Installing a plugin + +A plugin is usually a npm package, so you install them like other npm packages using npm. + + + + + +\`\`\`bash +npm install --save docusaurus-plugin-name +\`\`\` + + + + + +\`\`\`bash +pnpm add docusaurus-plugin-name +\`\`\` + + + + +" +`; + +exports[`npm2yarn plugin work with yarn converter 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Installing a plugin + +A plugin is usually a npm package, so you install them like other npm packages using npm. + + + + + +\`\`\`bash +npm install --save docusaurus-plugin-name +\`\`\` + + + + + +\`\`\`bash +yarn add docusaurus-plugin-name +\`\`\` + + + + +" +`; + exports[`npm2yarn plugin works on installation file 1`] = ` "import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; + \`\`\`bash @@ -75,13 +188,23 @@ import TabItem from '@theme/TabItem'; \`\`\` + \`\`\`bash - $ yarn add --global docusaurus + $ yarn global add docusaurus \`\`\` + + + +\`\`\`bash + $ pnpm add --global docusaurus +\`\`\` + + + " `; @@ -95,6 +218,7 @@ import TabItem from '@theme/TabItem'; A plugin is usually a npm package, so you install them like other npm packages using npm. + \`\`\`bash @@ -102,6 +226,7 @@ npm install --save docusaurus-plugin-name \`\`\` + \`\`\`bash @@ -109,6 +234,136 @@ yarn add docusaurus-plugin-name \`\`\` + + + +\`\`\`bash +pnpm add docusaurus-plugin-name +\`\`\` + + + + +" +`; + +exports[`npm2yarn plugin works with common commands 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + +\`\`\`bash +npm run xxx -- --arg +\`\`\` + + + + + +\`\`\`bash +yarn xxx --arg +\`\`\` + + + + + +\`\`\`bash +pnpm run xxx -- --arg +\`\`\` + + + + + + + + + +\`\`\`bash +npm install package +\`\`\` + + + + + +\`\`\`bash +yarn add package +\`\`\` + + + + + +\`\`\`bash +pnpm add package +\`\`\` + + + + + + + + + +\`\`\`bash +npm remove package-name +\`\`\` + + + + + +\`\`\`bash +yarn remove package-name +\`\`\` + + + + + +\`\`\`bash +pnpm remove package-name +\`\`\` + + + + + + + + + +\`\`\`bash +npm init docusaurus +npm init docusaurus@latest my-website classic +\`\`\` + + + + + +\`\`\`bash +yarn create docusaurus +yarn create docusaurus@latest my-website classic +\`\`\` + + + + + +\`\`\`bash +pnpm create docusaurus +pnpm create docusaurus@latest my-website classic +\`\`\` + + + " `; @@ -122,6 +377,7 @@ import TabItem from '@theme/TabItem'; A plugin is usually a npm package, so you install them like other npm packages using npm. + \`\`\`bash @@ -129,6 +385,7 @@ npm install --save docusaurus-plugin-name \`\`\` + \`\`\`bash @@ -136,6 +393,15 @@ yarn add docusaurus-plugin-name \`\`\` + + + +\`\`\`bash +pnpm add docusaurus-plugin-name +\`\`\` + + + " `; diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts index 2ed9613324..84afcc23ae 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts @@ -11,7 +11,10 @@ import mdx from 'remark-mdx'; import remark from 'remark'; import npm2yarn from '../index'; -const processFixture = async (name: string, options?: {sync?: boolean}) => { +const processFixture = async ( + name: string, + options?: Parameters[0], +) => { const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); const file = await vfile.read(filePath); const result = await remark().use(mdx).use(npm2yarn, options).process(file); @@ -32,6 +35,12 @@ describe('npm2yarn plugin', () => { expect(result).toMatchSnapshot(); }); + it('works with common commands', async () => { + const result = await processFixture('conversion-test', {sync: true}); + + expect(result).toMatchSnapshot(); + }); + it('works with sync option', async () => { const result = await processFixture('plugin', {sync: true}); @@ -55,4 +64,24 @@ describe('npm2yarn plugin', () => { expect(result).toMatchSnapshot(); }); + + it('work with yarn converter', async () => { + const result = await processFixture('plugin', {converters: ['yarn']}); + + expect(result).toMatchSnapshot(); + }); + + it('work with pnpm converter', async () => { + const result = await processFixture('plugin', {converters: ['pnpm']}); + + expect(result).toMatchSnapshot(); + }); + + it('work with custom converter', async () => { + const result = await processFixture('plugin', { + converters: [['Turbo', (code) => code.replace(/npm/g, 'turbo')]], + }); + + expect(result).toMatchSnapshot(); + }); }); diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts b/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts index 39236a1537..ee562f491d 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts @@ -11,39 +11,62 @@ import type {Code, Content, Literal} from 'mdast'; import type {Plugin} from 'unified'; import type {Node, Parent} from 'unist'; +type CustomConverter = [name: string, cb: (npmCode: string) => string]; + type PluginOptions = { sync?: boolean; + converters?: (CustomConverter | 'yarn' | 'pnpm')[]; }; -// E.g. global install: 'npm i' -> 'yarn' -const convertNpmToYarn = (npmCode: string) => npmToYarn(npmCode, 'yarn'); - -const transformNode = (node: Code, isSync: boolean) => { - const groupIdProp = isSync ? ' groupId="npm2yarn"' : ''; - const npmCode = node.value; - const yarnCode = convertNpmToYarn(node.value); +function createTabItem( + code: string, + node: Code, + value: string, + label?: string, +) { return [ { type: 'jsx', - value: `\n`, + value: ``, }, { type: node.type, lang: node.lang, - value: npmCode, + value: code, }, { type: 'jsx', - value: '\n', - }, - { - type: node.type, - lang: node.lang, - value: yarnCode, + value: '', }, + ] as Content[]; +} + +const transformNode = ( + node: Code, + isSync: boolean, + converters: (CustomConverter | 'yarn' | 'pnpm')[], +) => { + const groupIdProp = isSync ? ' groupId="npm2yarn"' : ''; + const npmCode = node.value; + return [ { type: 'jsx', - value: '\n', + value: ``, + }, + ...createTabItem(npmCode, node, 'npm'), + ...converters.flatMap((converter) => + typeof converter === 'string' + ? createTabItem( + npmToYarn(npmCode, converter), + node, + converter, + converter === 'yarn' ? 'Yarn' : converter, + ) + : createTabItem(converter[1](npmCode), node, converter[0]), + ), + { + type: 'jsx', + value: '', }, ] as Content[]; }; @@ -60,7 +83,7 @@ const nodeForImport: Literal = { }; const plugin: Plugin<[PluginOptions?]> = (options = {}) => { - const {sync = false} = options; + const {sync = false, converters = ['yarn', 'pnpm']} = options; return (root) => { let transformed = false as boolean; let alreadyImported = false as boolean; @@ -73,7 +96,7 @@ const plugin: Plugin<[PluginOptions?]> = (options = {}) => { while (index < node.children.length) { const child = node.children[index]!; if (matchNode(child)) { - const result = transformNode(child, sync); + const result = transformNode(child, sync, converters); node.children.splice(index, 1, ...result); index += result.length; transformed = true; diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index 0674e4c302..a92e5d1d0e 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/theme-classic", - "version": "2.3.1", + "version": "2.4.0", "description": "Classic theme for Docusaurus", "main": "lib/index.js", "types": "src/theme-classic.d.ts", @@ -20,22 +20,22 @@ "copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "copy-text-to-clipboard": "^3.0.1", - "infima": "0.2.0-alpha.42", + "infima": "0.2.0-alpha.43", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.14", diff --git a/packages/docusaurus-theme-classic/src/__tests__/__snapshots__/translations.test.ts.snap b/packages/docusaurus-theme-classic/src/__tests__/__snapshots__/translations.test.ts.snap index d36f4e6959..4b3c3f54a3 100644 --- a/packages/docusaurus-theme-classic/src/__tests__/__snapshots__/translations.test.ts.snap +++ b/packages/docusaurus-theme-classic/src/__tests__/__snapshots__/translations.test.ts.snap @@ -16,6 +16,10 @@ exports[`getTranslationFiles returns translation files matching snapshot 1`] = ` "description": "Navbar item with label Dropdown item 2", "message": "Dropdown item 2", }, + "logo.alt": { + "description": "The alt text of navbar logo", + "message": "navbar alt text logo", + }, "title": { "description": "The title in the navbar", "message": "navbar title", @@ -49,6 +53,10 @@ exports[`getTranslationFiles returns translation files matching snapshot 1`] = ` "description": "The title of the footer links column with title=Footer link column 2 in the footer", "message": "Footer link column 2", }, + "logo.alt": { + "description": "The alt text of footer logo", + "message": "footer alt text logo", + }, }, "path": "footer", }, @@ -71,6 +79,10 @@ exports[`getTranslationFiles returns translation files matching snapshot 2`] = ` "description": "Navbar item with label Dropdown item 2", "message": "Dropdown item 2", }, + "logo.alt": { + "description": "The alt text of navbar logo", + "message": "navbar alt text logo", + }, "title": { "description": "The title in the navbar", "message": "navbar title", @@ -92,6 +104,10 @@ exports[`getTranslationFiles returns translation files matching snapshot 2`] = ` "description": "The label of footer link with label=Link 2 linking to https://facebook.com", "message": "Link 2", }, + "logo.alt": { + "description": "The alt text of footer logo", + "message": "footer alt text logo", + }, }, "path": "footer", }, @@ -131,6 +147,10 @@ exports[`translateThemeConfig returns translated themeConfig 1`] = ` "title": "Footer link column 2 (translated)", }, ], + "logo": { + "alt": "footer alt text logo (translated)", + "src": "img/docusaurus.svg", + }, "style": "light", }, "navbar": { @@ -150,6 +170,10 @@ exports[`translateThemeConfig returns translated themeConfig 1`] = ` "label": "Dropdown (translated)", }, ], + "logo": { + "alt": "navbar alt text logo (translated)", + "src": "img/docusaurus.svg", + }, "style": "dark", "title": "navbar title (translated)", }, diff --git a/packages/docusaurus-theme-classic/src/__tests__/translations.test.ts b/packages/docusaurus-theme-classic/src/__tests__/translations.test.ts index ba4d3464ea..2bd55fb081 100644 --- a/packages/docusaurus-theme-classic/src/__tests__/translations.test.ts +++ b/packages/docusaurus-theme-classic/src/__tests__/translations.test.ts @@ -18,6 +18,10 @@ const ThemeConfigSample = { }, navbar: { title: 'navbar title', + logo: { + alt: 'navbar alt text logo', + src: 'img/docusaurus.svg', + }, style: 'dark', hideOnScroll: false, items: [ @@ -31,6 +35,10 @@ const ThemeConfigSample = { ], }, footer: { + logo: { + alt: 'footer alt text logo', + src: 'img/docusaurus.svg', + }, copyright: 'Copyright FB', style: 'light', links: [ @@ -52,6 +60,10 @@ const ThemeConfigSample = { const ThemeConfigSampleSimpleFooter: ThemeConfig = { ...ThemeConfigSample, footer: { + logo: { + alt: 'footer alt text logo', + src: 'img/docusaurus.svg', + }, copyright: 'Copyright FB', style: 'light', links: [ diff --git a/packages/docusaurus-theme-classic/src/index.ts b/packages/docusaurus-theme-classic/src/index.ts index b85fddbbf2..68b4cf4e82 100644 --- a/packages/docusaurus-theme-classic/src/index.ts +++ b/packages/docusaurus-theme-classic/src/index.ts @@ -26,6 +26,8 @@ const ContextReplacementPlugin = requireFromDocusaurusCore( // Need to be inlined to prevent dark mode FOUC // Make sure the key is the same as the one in `/theme/hooks/useTheme.js` const ThemeStorageKey = 'theme'; +const ThemeQueryStringKey = 'docusaurus-theme'; + const noFlashColorMode = ({ defaultMode, respectPrefersColorScheme, @@ -39,6 +41,14 @@ const noFlashColorMode = ({ document.documentElement.setAttribute('data-theme', theme); } + function getQueryStringTheme() { + var theme = null; + try { + theme = new URLSearchParams(window.location.search).get('${ThemeQueryStringKey}') + } catch(e) {} + return theme; + } + function getStoredTheme() { var theme = null; try { @@ -47,9 +57,9 @@ const noFlashColorMode = ({ return theme; } - var storedTheme = getStoredTheme(); - if (storedTheme !== null) { - setDataThemeAttribute(storedTheme); + var initialTheme = getQueryStringTheme() || getStoredTheme(); + if (initialTheme !== null) { + setDataThemeAttribute(initialTheme); } else { if ( respectPrefersColorScheme && diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index eac330fc4e..8a6cf84de9 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1243,6 +1243,7 @@ declare module '@theme/ColorModeToggle' { export interface Props { readonly className?: string; + readonly buttonClassName?: string; readonly value: ColorMode; /** * The parameter represents the "to-be" value. For example, if currently in diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css index 88ba4a7f9a..3760c530c4 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Content/styles.module.css @@ -59,6 +59,7 @@ display: flex; column-gap: 0.2rem; position: absolute; + /* rtl:ignore */ right: calc(var(--ifm-pre-padding) / 2); top: calc(var(--ifm-pre-padding) / 2); } @@ -72,7 +73,7 @@ border-radius: var(--ifm-global-radius); padding: 0.4rem; line-height: 0; - transition: opacity 200ms ease-in-out; + transition: opacity var(--ifm-transition-fast) ease-in-out; opacity: 0; } diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/CopyButton/styles.module.css b/packages/docusaurus-theme-classic/src/theme/CodeBlock/CopyButton/styles.module.css index 776c1960b5..c9e0ac5a3b 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/CopyButton/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/CopyButton/styles.module.css @@ -24,7 +24,7 @@ opacity: inherit; width: inherit; height: inherit; - transition: all 0.15s ease; + transition: all var(--ifm-transition-fast) ease; } .copyButtonSuccessIcon { diff --git a/packages/docusaurus-theme-classic/src/theme/ColorModeToggle/index.tsx b/packages/docusaurus-theme-classic/src/theme/ColorModeToggle/index.tsx index fe939a9d9a..e2d0c297f6 100644 --- a/packages/docusaurus-theme-classic/src/theme/ColorModeToggle/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/ColorModeToggle/index.tsx @@ -15,7 +15,12 @@ import type {Props} from '@theme/ColorModeToggle'; import styles from './styles.module.css'; -function ColorModeToggle({className, value, onChange}: Props): JSX.Element { +function ColorModeToggle({ + className, + buttonClassName, + value, + onChange, +}: Props): JSX.Element { const isBrowser = useIsBrowser(); const title = translate( @@ -47,6 +52,7 @@ function ColorModeToggle({className, value, onChange}: Props): JSX.Element { 'clean-btn', styles.toggleButton, !isBrowser && styles.toggleButtonDisabled, + buttonClassName, )} type="button" onClick={() => onChange(value === 'dark' ? 'light' : 'dark')} diff --git a/packages/docusaurus-theme-classic/src/theme/DocCard/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocCard/index.tsx index 7409ee9703..82d4598766 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocCard/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocCard/index.tsx @@ -82,15 +82,18 @@ function CardCategory({ href={href} icon="🗃️" title={item.label} - description={translate( - { - message: '{count} items', - id: 'theme.docs.DocCard.categoryDescription', - description: - 'The default description for a category card in the generated index about how many items this category includes', - }, - {count: item.items.length}, - )} + description={ + item.description ?? + translate( + { + message: '{count} items', + id: 'theme.docs.DocCard.categoryDescription', + description: + 'The default description for a category card in the generated index about how many items this category includes', + }, + {count: item.items.length}, + ) + } /> ); } @@ -103,7 +106,7 @@ function CardLink({item}: {item: PropSidebarItemLink}): JSX.Element { href={item.href} icon={icon} title={item.label} - description={doc?.description} + description={item.description ?? doc?.description} /> ); } diff --git a/packages/docusaurus-theme-classic/src/theme/ErrorPageContent.tsx b/packages/docusaurus-theme-classic/src/theme/ErrorPageContent.tsx index 9a64781260..f975277a3a 100644 --- a/packages/docusaurus-theme-classic/src/theme/ErrorPageContent.tsx +++ b/packages/docusaurus-theme-classic/src/theme/ErrorPageContent.tsx @@ -7,6 +7,10 @@ import React from 'react'; import Translate from '@docusaurus/Translate'; +import { + ErrorBoundaryError, + ErrorBoundaryTryAgainButton, +} from '@docusaurus/theme-common'; import type {Props} from '@theme/Error'; export default function ErrorPageContent({ @@ -24,15 +28,15 @@ export default function ErrorPageContent({ This page crashed. -

{error.message}

-
- +
+ +
+
+
+
diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/index.tsx b/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/index.tsx index d1fc9c3ffb..8e313b3a19 100644 --- a/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/index.tsx @@ -9,10 +9,12 @@ import React from 'react'; import {useColorMode, useThemeConfig} from '@docusaurus/theme-common'; import ColorModeToggle from '@theme/ColorModeToggle'; import type {Props} from '@theme/Navbar/ColorModeToggle'; +import styles from './styles.module.css'; export default function NavbarColorModeToggle({ className, }: Props): JSX.Element | null { + const navbarStyle = useThemeConfig().navbar.style; const disabled = useThemeConfig().colorMode.disableSwitch; const {colorMode, setColorMode} = useColorMode(); @@ -23,6 +25,9 @@ export default function NavbarColorModeToggle({ return ( diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/styles.module.css new file mode 100644 index 0000000000..21813e8791 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/Navbar/ColorModeToggle/styles.module.css @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.darkNavbarColorModeToggle:hover { + background: var(--ifm-color-gray-800); +} diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/Content/index.tsx b/packages/docusaurus-theme-classic/src/theme/Navbar/Content/index.tsx index e499198eca..ddc0972c67 100644 --- a/packages/docusaurus-theme-classic/src/theme/Navbar/Content/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Navbar/Content/index.tsx @@ -6,7 +6,7 @@ */ import React, {type ReactNode} from 'react'; -import {useThemeConfig} from '@docusaurus/theme-common'; +import {useThemeConfig, ErrorCauseBoundary} from '@docusaurus/theme-common'; import { splitNavbarItems, useNavbarMobileSidebar, @@ -29,7 +29,18 @@ function NavbarItems({items}: {items: NavbarItemConfig[]}): JSX.Element { return ( <> {items.map((item, i) => ( - + + new Error( + `A theme navbar item failed to render. +Please double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config: +${JSON.stringify(item, null, 2)}`, + {cause: error}, + ) + }> + + ))} ); diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx index f6fc318bbc..bc315a1fa7 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx @@ -54,7 +54,9 @@ function DropdownNavbarItemDesktop({ const [showDropdown, setShowDropdown] = useState(false); useEffect(() => { - const handleClickOutside = (event: MouseEvent | TouchEvent) => { + const handleClickOutside = ( + event: MouseEvent | TouchEvent | FocusEvent, + ) => { if ( !dropdownRef.current || dropdownRef.current.contains(event.target as Node) @@ -66,10 +68,12 @@ function DropdownNavbarItemDesktop({ document.addEventListener('mousedown', handleClickOutside); document.addEventListener('touchstart', handleClickOutside); + document.addEventListener('focusin', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('touchstart', handleClickOutside); + document.removeEventListener('focusin', handleClickOutside); }; }, [dropdownRef]); @@ -100,22 +104,6 @@ function DropdownNavbarItemDesktop({ {items.map((childItemProps, i) => ( { - if (i === items.length - 1 && e.key === 'Tab') { - e.preventDefault(); - setShowDropdown(false); - const nextNavbarItem = dropdownRef.current!.nextElementSibling; - if (nextNavbarItem) { - const targetItem = - nextNavbarItem instanceof HTMLAnchorElement - ? nextNavbarItem - : // Next item is another dropdown; focus on the inner - // anchor element instead so there's outline - nextNavbarItem.querySelector('a')!; - targetItem.focus(); - } - } - }} activeClassName="dropdown__link--active" {...childItemProps} key={i} diff --git a/packages/docusaurus-theme-classic/src/theme/Tabs/__tests__/index.test.tsx b/packages/docusaurus-theme-classic/src/theme/Tabs/__tests__/index.test.tsx index 90f150b6ef..58a1a636fd 100644 --- a/packages/docusaurus-theme-classic/src/theme/Tabs/__tests__/index.test.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Tabs/__tests__/index.test.tsx @@ -132,12 +132,9 @@ describe('Tabs', () => { renderer.create( ({label: t, value: idx}))} - // @ts-expect-error: for an edge-case that we didn't write types for defaultValue={0}> {tabs.map((t, idx) => ( - // @ts-expect-error: for an edge-case that we didn't write types for {t} @@ -199,4 +196,19 @@ describe('Tabs', () => { ); }).not.toThrow(); }); + + it('allows a tab to be falsy', () => { + expect(() => { + renderer.create( + + + Val1 + {null} + {false} + {undefined} + + , + ); + }).not.toThrow(); + }); }); diff --git a/packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx b/packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx index e989d082a5..7ccb52c9dc 100644 --- a/packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import React, {cloneElement} from 'react'; +import React, {cloneElement, type ReactElement} from 'react'; import clsx from 'clsx'; import { useScrollPositionBlocker, useTabs, + type TabItemProps, } from '@docusaurus/theme-common/internal'; import useIsBrowser from '@docusaurus/useIsBrowser'; import type {Props} from '@theme/Tabs'; @@ -109,10 +110,11 @@ function TabContent({ children, selectedValue, }: Props & ReturnType) { - // eslint-disable-next-line no-param-reassign - children = Array.isArray(children) ? children : [children]; + const childTabs = (Array.isArray(children) ? children : [children]).filter( + Boolean, + ) as ReactElement[]; if (lazy) { - const selectedTabItem = children.find( + const selectedTabItem = childTabs.find( (tabItem) => tabItem.props.value === selectedValue, ); if (!selectedTabItem) { @@ -123,7 +125,7 @@ function TabContent({ } return (
- {children.map((tabItem, i) => + {childTabs.map((tabItem, i) => cloneElement(tabItem, { key: i, hidden: tabItem.props.value !== selectedValue, diff --git a/packages/docusaurus-theme-classic/src/translations.ts b/packages/docusaurus-theme-classic/src/translations.ts index cc087a87d8..56258c86ee 100644 --- a/packages/docusaurus-theme-classic/src/translations.ts +++ b/packages/docusaurus-theme-classic/src/translations.ts @@ -45,7 +45,20 @@ function getNavbarTranslationFile(navbar: Navbar): TranslationFileContent { ? {title: {message: navbar.title, description: 'The title in the navbar'}} : {}; - return mergeTranslations([titleTranslations, navbarItemsTranslations]); + const logoAlt: TranslationFileContent = navbar.logo?.alt + ? { + 'logo.alt': { + message: navbar.logo.alt, + description: 'The alt text of navbar logo', + }, + } + : {}; + + return mergeTranslations([ + titleTranslations, + logoAlt, + navbarItemsTranslations, + ]); } function translateNavbar( navbar: Navbar, @@ -54,9 +67,18 @@ function translateNavbar( if (!navbarTranslations) { return navbar; } + + const logo = navbar.logo + ? { + ...navbar.logo, + alt: navbarTranslations[`logo.alt`]?.message ?? navbar.logo?.alt, + } + : undefined; + return { ...navbar, title: navbarTranslations.title?.message ?? navbar.title, + logo, // TODO handle properly all the navbar item types here! items: navbar.items.map((item) => { const subItems = item.items?.map((subItem) => ({ @@ -119,7 +141,21 @@ function getFooterTranslationFile(footer: Footer): TranslationFileContent { } : {}; - return mergeTranslations([footerLinkTitles, footerLinkLabels, copyright]); + const logoAlt: TranslationFileContent = footer.logo?.alt + ? { + 'logo.alt': { + message: footer.logo.alt, + description: 'The alt text of footer logo', + }, + } + : {}; + + return mergeTranslations([ + footerLinkTitles, + footerLinkLabels, + copyright, + logoAlt, + ]); } function translateFooter( footer: Footer, @@ -149,10 +185,18 @@ function translateFooter( const copyright = footerTranslations.copyright?.message ?? footer.copyright; + const logo = footer.logo + ? { + ...footer.logo, + alt: footerTranslations[`logo.alt`]?.message ?? footer.logo?.alt, + } + : undefined; + return { ...footer, links, copyright, + logo, }; } diff --git a/packages/docusaurus-theme-common/package.json b/packages/docusaurus-theme-common/package.json index 228b473d24..878fb79063 100644 --- a/packages/docusaurus-theme-common/package.json +++ b/packages/docusaurus-theme-common/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/theme-common", - "version": "2.3.1", + "version": "2.4.0", "description": "Common code for Docusaurus themes.", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -30,12 +30,13 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -47,8 +48,8 @@ "utility-types": "^3.10.0" }, "devDependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", "fs-extra": "^10.1.0", "lodash": "^4.17.21" }, diff --git a/packages/docusaurus-theme-common/src/components/Collapsible/index.tsx b/packages/docusaurus-theme-common/src/components/Collapsible/index.tsx index 39c6ce63fd..ca0da9d02a 100644 --- a/packages/docusaurus-theme-common/src/components/Collapsible/index.tsx +++ b/packages/docusaurus-theme-common/src/components/Collapsible/index.tsx @@ -65,6 +65,10 @@ function applyCollapsedStyle(el: HTMLElement, collapsed: boolean) { el.style.height = collapsedStyles.height; } +function userPrefersReducedMotion(): boolean { + return window.matchMedia('(prefers-reduced-motion: reduce)').matches; +} + /* Lex111: Dynamic transition duration is used in Material design, this technique is good for a large number of items. @@ -72,6 +76,9 @@ https://material.io/archive/guidelines/motion/duration-easing.html#duration-easi https://github.com/mui-org/material-ui/blob/e724d98eba018e55e1a684236a2037e24bcf050c/packages/material-ui/src/styles/createTransitions.js#L40-L43 */ function getAutoHeightDuration(height: number) { + if (userPrefersReducedMotion()) { + return 0; + } const constant = height / 36; return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10); } diff --git a/packages/docusaurus-theme-common/src/components/Details/index.tsx b/packages/docusaurus-theme-common/src/components/Details/index.tsx index 19952166bc..0876cf9b94 100644 --- a/packages/docusaurus-theme-common/src/components/Details/index.tsx +++ b/packages/docusaurus-theme-common/src/components/Details/index.tsx @@ -31,8 +31,11 @@ function hasParent(node: HTMLElement | null, parent: HTMLElement): boolean { } export type DetailsProps = { - /** Summary is provided as props, including the wrapping `` tag */ - summary?: ReactElement; + /** + * Summary is provided as props, optionally including the wrapping + * `` tag + */ + summary?: ReactElement | string; } & ComponentProps<'details'>; /** @@ -54,6 +57,12 @@ export function Details({ // only after animation completes, otherwise close animations won't work const [open, setOpen] = useState(props.open); + const summaryElement = React.isValidElement(summary) ? ( + summary + ) : ( + {summary ?? 'Details'} + ); + return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
- {/* eslint-disable-next-line @docusaurus/no-untranslated-text */} - {summary ?? Details} + {summaryElement} void; - /** - * Given a query, this handle generates the corresponding search page link, - * with base URL prepended. - */ - generateSearchPageLink: (targetSearchQuery: string) => string; -} { - const history = useHistory(); +/** + * Permits to read/write the current search query string + */ +export function useSearchQueryString(): [string, (newValue: string) => void] { + return useQueryString(SEARCH_PARAM_QUERY); +} + +/** + * Permits to create links to the search page with the appropriate query string + */ +export function useSearchLinkCreator(): (searchValue: string) => string { const { siteConfig: {baseUrl, themeConfig}, } = useDocusaurusContext(); @@ -38,47 +30,13 @@ export function useSearchPage(): { algolia: {searchPagePath}, } = themeConfig as AlgoliaThemeConfig; - const [searchQuery, setSearchQueryState] = useState(''); - - // Init search query just after React hydration - useEffect(() => { - const searchQueryStringValue = - new URLSearchParams(window.location.search).get(SEARCH_PARAM_QUERY) ?? ''; - - setSearchQueryState(searchQueryStringValue); - }, []); - - const setSearchQuery = useCallback( - (newSearchQuery: string) => { - const searchParams = new URLSearchParams(window.location.search); - - if (newSearchQuery) { - searchParams.set(SEARCH_PARAM_QUERY, newSearchQuery); - } else { - searchParams.delete(SEARCH_PARAM_QUERY); - } - - history.replace({ - search: searchParams.toString(), - }); - setSearchQueryState(newSearchQuery); - }, - [history], - ); - - const generateSearchPageLink = useCallback( - (targetSearchQuery: string) => + return useCallback( + (searchValue: string) => // Refer to https://github.com/facebook/docusaurus/pull/2838 // Note: if searchPagePath is falsy, useSearchPage() will not be called `${baseUrl}${ searchPagePath as string - }?${SEARCH_PARAM_QUERY}=${encodeURIComponent(targetSearchQuery)}`, + }?${SEARCH_PARAM_QUERY}=${encodeURIComponent(searchValue)}`, [baseUrl, searchPagePath], ); - - return { - searchQuery, - setSearchQuery, - generateSearchPageLink, - }; } diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 4c38630161..63ea4ee444 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -73,6 +73,11 @@ export { type TagLetterEntry, } from './utils/tagsUtils'; +export { + useSearchQueryString, + useSearchLinkCreator, +} from './hooks/useSearchPage'; + export {isMultiColumnFooterLinks} from './utils/footerUtils'; export {isRegexpStringMatch} from './utils/regexpUtils'; @@ -89,3 +94,9 @@ export { SkipToContentFallbackId, SkipToContentLink, } from './utils/skipToContentUtils'; + +export { + ErrorBoundaryTryAgainButton, + ErrorBoundaryError, + ErrorCauseBoundary, +} from './utils/errorBoundaryUtils'; diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 680d79ec94..7b5b75ca0e 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -117,7 +117,6 @@ export { keyboardFocusedClassName, } from './hooks/useKeyboardNavigation'; export {useLockBodyScroll} from './hooks/useLockBodyScroll'; -export {useSearchPage} from './hooks/useSearchPage'; export {useCodeWordWrap} from './hooks/useCodeWordWrap'; export {getPrismCssVariables} from './utils/codeBlockUtils'; export {useBackToTopButton} from './hooks/useBackToTopButton'; diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 726946be9e..1d03b8dc81 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -271,8 +271,8 @@ export function useLayoutDocsSidebar( `Can't find any sidebar with id "${sidebarId}" in version${ versions.length > 1 ? 's' : '' } ${versions.map((version) => version.name).join(', ')}". - Available sidebar ids are: - - ${Object.keys(allSidebars).join('\n- ')}`, +Available sidebar ids are: +- ${Object.keys(allSidebars).join('\n- ')}`, ); } return sidebarEntry[1]; @@ -304,9 +304,9 @@ export function useLayoutDoc( return null; } throw new Error( - `DocNavbarItem: couldn't find any doc with id "${docId}" in version${ + `Couldn't find any doc with id "${docId}" in version${ versions.length > 1 ? 's' : '' - } ${versions.map((version) => version.name).join(', ')}". + } "${versions.map((version) => version.name).join(', ')}". Available doc ids are: - ${uniq(allDocs.map((versionDoc) => versionDoc.id)).join('\n- ')}`, ); diff --git a/packages/docusaurus-theme-common/src/utils/errorBoundaryUtils.module.css b/packages/docusaurus-theme-common/src/utils/errorBoundaryUtils.module.css new file mode 100644 index 0000000000..6b6ecfa607 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/errorBoundaryUtils.module.css @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.errorBoundaryError { + white-space: pre-wrap; + color: red; +} diff --git a/packages/docusaurus-theme-common/src/utils/errorBoundaryUtils.tsx b/packages/docusaurus-theme-common/src/utils/errorBoundaryUtils.tsx new file mode 100644 index 0000000000..051dbe86eb --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/errorBoundaryUtils.tsx @@ -0,0 +1,56 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, {type ComponentProps} from 'react'; +import Translate from '@docusaurus/Translate'; +import {getErrorCausalChain} from '@docusaurus/utils-common'; +import styles from './errorBoundaryUtils.module.css'; + +export function ErrorBoundaryTryAgainButton( + props: ComponentProps<'button'>, +): JSX.Element { + return ( + + ); +} +export function ErrorBoundaryError({error}: {error: Error}): JSX.Element { + const causalChain = getErrorCausalChain(error); + const fullMessage = causalChain.map((e) => e.message).join('\n\nCause:\n'); + return

{fullMessage}

; +} + +/** + * This component is useful to wrap a low-level error into a more meaningful + * error with extra context, using the ES error-cause feature. + * + * new Error("extra context message",{cause: error})} + * > + * + * + */ +export class ErrorCauseBoundary extends React.Component< + { + children: React.ReactNode; + onError: (error: Error, errorInfo: React.ErrorInfo) => Error; + }, + unknown +> { + override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): never { + throw this.props.onError(error, errorInfo); + } + + override render(): React.ReactNode { + return this.props.children; + } +} diff --git a/packages/docusaurus-theme-common/src/utils/historyUtils.ts b/packages/docusaurus-theme-common/src/utils/historyUtils.ts index 06b124a229..686a876f84 100644 --- a/packages/docusaurus-theme-common/src/utils/historyUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/historyUtils.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {useEffect} from 'react'; +import {useCallback, useEffect} from 'react'; import {useHistory} from '@docusaurus/router'; // @ts-expect-error: TODO temporary until React 18 upgrade import {useSyncExternalStore} from 'use-sync-external-store/shim'; @@ -75,3 +75,42 @@ export function useQueryStringValue(key: string | null): string | null { return new URLSearchParams(history.location.search).get(key); }); } + +export function useQueryStringKeySetter(): ( + key: string, + newValue: string | null, + options?: {push: boolean}, +) => void { + const history = useHistory(); + return useCallback( + (key, newValue, options) => { + const searchParams = new URLSearchParams(history.location.search); + if (newValue) { + searchParams.set(key, newValue); + } else { + searchParams.delete(key); + } + const updaterFn = options?.push ? history.push : history.replace; + updaterFn({ + search: searchParams.toString(), + }); + }, + [history], + ); +} + +export function useQueryString( + key: string, +): [string, (newValue: string, options?: {push: boolean}) => void] { + const value = useQueryStringValue(key) ?? ''; + const setQueryString = useQueryStringKeySetter(); + return [ + value, + useCallback( + (newValue: string, options) => { + setQueryString(key, newValue, options); + }, + [setQueryString, key], + ), + ]; +} diff --git a/packages/docusaurus-theme-common/src/utils/tabsUtils.tsx b/packages/docusaurus-theme-common/src/utils/tabsUtils.tsx index 64b3d6446e..5b974b8af5 100644 --- a/packages/docusaurus-theme-common/src/utils/tabsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/tabsUtils.tsx @@ -29,12 +29,12 @@ export interface TabValue { readonly default?: boolean; } +type TabItem = ReactElement | null | false | undefined; + export interface TabsProps { readonly lazy?: boolean; readonly block?: boolean; - readonly children: - | readonly ReactElement[] - | ReactElement; + readonly children: TabItem[] | TabItem; readonly defaultValue?: string | null; readonly values?: readonly TabValue[]; readonly groupId?: string; @@ -55,14 +55,16 @@ export interface TabItemProps { // A very rough duck type, but good enough to guard against mistakes while // allowing customization function isTabItem( - comp: ReactElement, + comp: ReactElement, ): comp is ReactElement { - return 'value' in comp.props; + const {props} = comp; + return !!props && typeof props === 'object' && 'value' in props; } function ensureValidChildren(children: TabsProps['children']) { - return React.Children.map(children, (child) => { - if (isValidElement(child) && isTabItem(child)) { + return (React.Children.map(children, (child) => { + // Pass falsy values through: allow conditionally not rendering a tab + if (!child || (isValidElement(child) && isTabItem(child))) { return child; } // child.type.name will give non-sensical values in prod because of @@ -73,7 +75,7 @@ function ensureValidChildren(children: TabsProps['children']) { typeof child.type === 'string' ? child.type : child.type.name }>: all children of the component should be , and every should have a unique "value" prop.`, ); - }); + })?.filter(Boolean) ?? []) as ReactElement[]; } function extractChildrenTabValues(children: TabsProps['children']): TabValue[] { diff --git a/packages/docusaurus-theme-live-codeblock/package.json b/packages/docusaurus-theme-live-codeblock/package.json index 7f02b529c1..d9aeed0879 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": "2.3.1", + "version": "2.4.0", "description": "Docusaurus live code block component.", "main": "lib/index.js", "types": "src/theme-live-codeblock.d.ts", @@ -23,10 +23,10 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@philpl/buble": "^0.19.7", "clsx": "^1.2.1", "fs-extra": "^10.1.0", @@ -34,7 +34,7 @@ "tslib": "^2.4.0" }, "devDependencies": { - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.0", "@types/buble": "^0.20.1" }, "peerDependencies": { diff --git a/packages/docusaurus-theme-mermaid/package.json b/packages/docusaurus-theme-mermaid/package.json index a3cf3b0c1a..87e803f0c0 100644 --- a/packages/docusaurus-theme-mermaid/package.json +++ b/packages/docusaurus-theme-mermaid/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/theme-mermaid", - "version": "2.3.1", + "version": "2.4.0", "description": "Mermaid components for Docusaurus.", "main": "lib/index.js", "types": "src/theme-mermaid.d.ts", @@ -33,11 +33,11 @@ "copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@mdx-js/react": "^1.6.22", "mermaid": "^9.2.2", "tslib": "^2.4.0" diff --git a/packages/docusaurus-theme-search-algolia/package.json b/packages/docusaurus-theme-search-algolia/package.json index 0090458762..00309e029c 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": "2.3.1", + "version": "2.4.0", "description": "Algolia search component for Docusaurus.", "main": "lib/index.js", "sideEffects": [ @@ -34,13 +34,13 @@ }, "dependencies": { "@docsearch/react": "^3.1.1", - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "algoliasearch": "^4.13.1", "algoliasearch-helper": "^3.10.0", "clsx": "^1.2.1", @@ -51,7 +51,7 @@ "utility-types": "^3.10.0" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1" + "@docusaurus/module-type-aliases": "2.4.0" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx index b0456492d4..0f4d7ee000 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx @@ -10,8 +10,10 @@ import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react'; import Head from '@docusaurus/Head'; import Link from '@docusaurus/Link'; import {useHistory} from '@docusaurus/router'; -import {isRegexpStringMatch} from '@docusaurus/theme-common'; -import {useSearchPage} from '@docusaurus/theme-common/internal'; +import { + isRegexpStringMatch, + useSearchLinkCreator, +} from '@docusaurus/theme-common'; import { useAlgoliaContextualFacetFilters, useSearchResultUrlProcessor, @@ -59,10 +61,10 @@ type ResultsFooterProps = { }; function ResultsFooter({state, onClose}: ResultsFooterProps) { - const {generateSearchPageLink} = useSearchPage(); + const createSearchLink = useSearchLinkCreator(); return ( - + diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx index c85953a62e..24179847e5 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx @@ -21,11 +21,9 @@ import { HtmlClassNameProvider, useEvent, usePluralForm, + useSearchQueryString, } from '@docusaurus/theme-common'; -import { - useSearchPage, - useTitleFormatter, -} from '@docusaurus/theme-common/internal'; +import {useTitleFormatter} from '@docusaurus/theme-common/internal'; import Translate, {translate} from '@docusaurus/Translate'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { @@ -167,7 +165,7 @@ function SearchPageContent(): JSX.Element { const documentsFoundPlural = useDocumentsFoundPlural(); const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers(); - const {searchQuery, setSearchQuery} = useSearchPage(); + const [searchQuery, setSearchQuery] = useSearchQueryString(); const initialSearchResultState: ResultDispatcherState = { items: [], query: null, diff --git a/packages/docusaurus-theme-translations/locales/ar/theme-common.json b/packages/docusaurus-theme-translations/locales/ar/theme-common.json index 8c9d89a476..6503db01e8 100644 --- a/packages/docusaurus-theme-translations/locales/ar/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/ar/theme-common.json @@ -17,7 +17,7 @@ "theme.admonition.danger": "خطر", "theme.admonition.info": "معلومات", "theme.admonition.note": "ملاحظة", - "theme.admonition.tip": "تمليح", + "theme.admonition.tip": "تلميح", "theme.blog.archive.description": "أرشيف", "theme.blog.archive.title": "أرشيف", "theme.blog.paginator.navAriaLabel": "التنقل في صفحة قائمة المدونة", @@ -42,7 +42,7 @@ "theme.docs.breadcrumbs.home": "الرئيسية", "theme.docs.breadcrumbs.navAriaLabel": "التنقل التفصيلي", "theme.docs.paginator.navAriaLabel": "التنقل بين صفحات الددات", - "theme.docs.paginator.next": "التالى", + "theme.docs.paginator.next": "التالي", "theme.docs.paginator.previous": "السابق", "theme.docs.sidebar.closeSidebarButtonAriaLabel": "Close navigation bar", "theme.docs.sidebar.collapseButtonAriaLabel": "طي الشريط الجانبي", diff --git a/packages/docusaurus-theme-translations/locales/hu/plugin-ideal-image.json b/packages/docusaurus-theme-translations/locales/hu/plugin-ideal-image.json new file mode 100644 index 0000000000..15e51ba23a --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/hu/plugin-ideal-image.json @@ -0,0 +1,7 @@ +{ + "theme.IdealImageMessage.404error": "404. A kép nem található", + "theme.IdealImageMessage.error": "Hiba. Kattints újratöltéshez", + "theme.IdealImageMessage.load": "Kattints a(z) {sizeMessage} betöltéséhez", + "theme.IdealImageMessage.loading": "Betöltés...", + "theme.IdealImageMessage.offline": "A böngésződ offline módban van. A kép nem töltődött be" +} diff --git a/packages/docusaurus-theme-translations/locales/hu/plugin-pwa.json b/packages/docusaurus-theme-translations/locales/hu/plugin-pwa.json new file mode 100644 index 0000000000..bc7393f31c --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/hu/plugin-pwa.json @@ -0,0 +1,5 @@ +{ + "theme.PwaReloadPopup.closeButtonAriaLabel": "Bezárás", + "theme.PwaReloadPopup.info": "Új verzió elérhető el", + "theme.PwaReloadPopup.refreshButtonText": "Frissítés" +} diff --git a/packages/docusaurus-theme-translations/locales/hu/theme-common.json b/packages/docusaurus-theme-translations/locales/hu/theme-common.json new file mode 100644 index 0000000000..9ffbf2b9cc --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/hu/theme-common.json @@ -0,0 +1,70 @@ +{ + "theme.AnnouncementBar.closeButtonAriaLabel": "Bezárás", + "theme.BackToTopButton.buttonAriaLabel": "Vissza a tetejére", + "theme.CodeBlock.copied": "Másolva", + "theme.CodeBlock.copy": "Másolás", + "theme.CodeBlock.copyButtonAriaLabel": "Másolás a vágólapra", + "theme.CodeBlock.wordWrapToggle": "Szótörés váltása", + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Összecsukás/kibontás kategória '{label}'", + "theme.ErrorPageContent.title": "Hiba történt a oldalon.", + "theme.ErrorPageContent.tryAgain": "Próbáld újra", + "theme.NavBar.navAriaLabel": "Fő", + "theme.NotFound.p1": "Sajnos nem találtuk azt az oldalt, amit kerestél.", + "theme.NotFound.p2": "Kérjük, lépj kapcsolatba az oldal tulajdonosával, ahonnan erre a hivatkozásra léptél, hogy jelezd, hogy a hivatkozás nem működik.", + "theme.NotFound.title": "Az oldal nem található", + "theme.TOCCollapsible.toggleButtonLabel": "Ezen az oldalon tartalomjegyzék", + "theme.admonition.caution": "vigyázat", + "theme.admonition.danger": "veszély", + "theme.admonition.info": "információ", + "theme.admonition.note": "megjegyzés", + "theme.admonition.tip": "tanács", + "theme.blog.archive.description": "Archívum", + "theme.blog.archive.title": "Archívum", + "theme.blog.paginator.navAriaLabel": "Bloglista oldalának navigációja", + "theme.blog.paginator.newerEntries": "Újabb bejegyzések", + "theme.blog.paginator.olderEntries": "Régebbi bejegyzések", + "theme.blog.post.paginator.navAriaLabel": "Blogbejegyzés oldalának navigációja", + "theme.blog.post.paginator.newerPost": "Újabb bejegyzés", + "theme.blog.post.paginator.olderPost": "Régebbi bejegyzés", + "theme.blog.post.plurals": "Egy bejegyzés|{count} bejegyzés", + "theme.blog.post.readMore": "Olvass tovább", + "theme.blog.post.readMoreLabel": "További információ erről: {title}", + "theme.blog.post.readingTime.plurals": "Egy perc olvasás|{readingTime} perc olvasás", + "theme.blog.sidebar.navAriaLabel": "Navigáció a blog legutóbbi bejegyzései között", + "theme.blog.tagTitle": "{nPosts} a(z) \"{tagName}\" címkével", + "theme.colorToggle.ariaLabel": "Váltás a sötét és világos mód között (jelenleg {mode} van beállítva)", + "theme.colorToggle.ariaLabel.mode.dark": "Sötét mód", + "theme.colorToggle.ariaLabel.mode.light": "Világos mód", + "theme.common.editThisPage": "Szerkesztés GitHub-on", + "theme.common.headingLinkTitle": "Közvetlen hivatkozás erre: {heading}", + "theme.common.skipToMainContent": "Ugrás a fő tartalomhoz", + "theme.docs.DocCard.categoryDescription": "{count} elemek", + "theme.docs.breadcrumbs.home": "Kezdőlap", + "theme.docs.breadcrumbs.navAriaLabel": "Navigációs sáv a jelenlegi oldalhoz", + "theme.docs.paginator.navAriaLabel": "Dokumentációs oldal navigációja", + "theme.docs.paginator.next": "Következő oldal", + "theme.docs.paginator.previous": "Előző oldal", + "theme.docs.sidebar.closeSidebarButtonAriaLabel": "Navigációs sáv bezárása", + "theme.docs.sidebar.collapseButtonAriaLabel": "Oldalsáv összecsukása", + "theme.docs.sidebar.collapseButtonTitle": "Oldalsáv összecsukása", + "theme.docs.sidebar.expandButtonAriaLabel": "Oldalsáv kibontása", + "theme.docs.sidebar.expandButtonTitle": "Oldalsáv kibontása", + "theme.docs.sidebar.navAriaLabel": "Dokumentációs oldalsáv", + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Navigációs sáv ki- és bekapcsolása", + "theme.docs.tagDocListPageTitle": "{nDocsTagged} címkével: \"{tagName}\" ", + "theme.docs.tagDocListPageTitle.nDocsTagged": "Egy oldal|{count} oldal", + "theme.docs.versionBadge.label": "Verzió: {versionLabel}", + "theme.docs.versions.latestVersionLinkLabel": "legutolsó verzió", + "theme.docs.versions.latestVersionSuggestionLabel": "Az aktuális dokumentáció itt található: {latestVersionLink} ({versionLabel}).", + "theme.docs.versions.unmaintainedVersionLabel": "Ez a {siteTitle} dokumentáció a {versionLabel} verzióhoz, amely már nem támogatott.", + "theme.docs.versions.unreleasedVersionLabel": "Ez a dokumentáció a jövőbeli {siteTitle} {versionLabel} verzióhoz.", + "theme.lastUpdated.atDate": " {date}-kor", + "theme.lastUpdated.byUser": " {user}-tól", + "theme.lastUpdated.lastUpdatedAtBy": "Utolsó frissítés{atDate}{byUser}", + "theme.navbar.mobileLanguageDropdown.label": "Nyelvek", + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Vissza a főmenühöz", + "theme.navbar.mobileVersionsDropdown.label": "Verziók", + "theme.tags.tagsListLabel": "Címkék:", + "theme.tags.tagsPageLink": "Összes címke megtekintése", + "theme.tags.tagsPageTitle": "Címkék" +} diff --git a/packages/docusaurus-theme-translations/locales/hu/theme-live-codeblock.json b/packages/docusaurus-theme-translations/locales/hu/theme-live-codeblock.json new file mode 100644 index 0000000000..221baebd38 --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/hu/theme-live-codeblock.json @@ -0,0 +1,4 @@ +{ + "theme.Playground.liveEditor": "Interaktív szerkesztő", + "theme.Playground.result": "Eredmény" +} diff --git a/packages/docusaurus-theme-translations/locales/hu/theme-search-algolia.json b/packages/docusaurus-theme-translations/locales/hu/theme-search-algolia.json new file mode 100644 index 0000000000..93f5dd7ef3 --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/hu/theme-search-algolia.json @@ -0,0 +1,36 @@ +{ + "theme.SearchBar.label": "Keresés", + "theme.SearchBar.seeAll": "Összes eredmény megtekintése ({count})", + "theme.SearchModal.errorScreen.helpText": "Ellenőrizze a hálózati kapcsolatot.", + "theme.SearchModal.errorScreen.titleText": "Nem sikerült az eredményeket lekérni", + "theme.SearchModal.footer.closeKeyAriaLabel": "Escape billentyű", + "theme.SearchModal.footer.closeText": "bezárás", + "theme.SearchModal.footer.navigateDownKeyAriaLabel": "Lefelé nyíl", + "theme.SearchModal.footer.navigateText": "navigációhoz", + "theme.SearchModal.footer.navigateUpKeyAriaLabel": "Felfelé nyíl", + "theme.SearchModal.footer.searchByText": "Motor:", + "theme.SearchModal.footer.selectKeyAriaLabel": "Enter billentyű", + "theme.SearchModal.footer.selectText": "kiválasztás", + "theme.SearchModal.noResultsScreen.noResultsText": "Nincs eredmény", + "theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": "Adja meg nekünk.", + "theme.SearchModal.noResultsScreen.reportMissingResultsText": "Úgy gondolja, hogy ez a keresés eredményt kellene adnia?", + "theme.SearchModal.noResultsScreen.suggestedQueryText": "Próbálja meg keresni", + "theme.SearchModal.placeholder": "Dokumentumok keresése", + "theme.SearchModal.searchBox.cancelButtonText": "Mégse", + "theme.SearchModal.searchBox.resetButtonTitle": "Keresési kérés törlése", + "theme.SearchModal.startScreen.favoriteSearchesTitle": "Kedvencek", + "theme.SearchModal.startScreen.noRecentSearchesText": "Nincsenek legutóbbi keresések", + "theme.SearchModal.startScreen.recentSearchesTitle": "Legutóbbi", + "theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": "Törölje ezt a keresést a kedvencekből", + "theme.SearchModal.startScreen.removeRecentSearchButtonTitle": "Törölje ezt a keresést az előzményekből", + "theme.SearchModal.startScreen.saveRecentSearchButtonTitle": "Mentsük el ezt a keresést", + "theme.SearchPage.saveRecentSearchButtonTitle": "Mentse ezt a keresést", + "theme.SearchPage.algoliaLabel": "Keresés az Algolia segítségével", + "theme.SearchPage.documentsFound.plurals": "Egy dokumentum|{count} dokumentumok", + "theme.SearchPage.emptyResultsTitle": "Keresés a webhelyen", + "theme.SearchPage.existingResultsTitle": "\"{query}\" keresési eredményei", + "theme.SearchPage.fetchingNewResults": "Új keresési eredmények betöltése...", + "theme.SearchPage.inputLabel": "Keresés", + "theme.SearchPage.inputPlaceholder": "Adja meg a keresendő kifejezést", + "theme.SearchPage.noResultsText": "Nincs találat a keresésre" +} diff --git a/packages/docusaurus-theme-translations/locales/nb/plugin-ideal-image.json b/packages/docusaurus-theme-translations/locales/nb/plugin-ideal-image.json new file mode 100644 index 0000000000..49a18505dd --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/nb/plugin-ideal-image.json @@ -0,0 +1,7 @@ +{ + "theme.IdealImageMessage.404error": "404. Bildet kunne ikke lastes inn", + "theme.IdealImageMessage.error": "Noe gikk galt. Klikk for å laste på nytt", + "theme.IdealImageMessage.load": "Klikk for å laste inn på nytt{sizeMessage}", + "theme.IdealImageMessage.loading": "Laster inn...", + "theme.IdealImageMessage.offline": "Nettleseren din er offline. Bildet ble ikke lastet inn." +} diff --git a/packages/docusaurus-theme-translations/locales/nb/plugin-pwa.json b/packages/docusaurus-theme-translations/locales/nb/plugin-pwa.json new file mode 100644 index 0000000000..522f86c08f --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/nb/plugin-pwa.json @@ -0,0 +1,5 @@ +{ + "theme.PwaReloadPopup.closeButtonAriaLabel": "Lukk", + "theme.PwaReloadPopup.info": "Ny versjon tilgjengelig", + "theme.PwaReloadPopup.refreshButtonText": "Last inn på nytt" +} diff --git a/packages/docusaurus-theme-translations/locales/nb/theme-common.json b/packages/docusaurus-theme-translations/locales/nb/theme-common.json new file mode 100644 index 0000000000..40496d5017 --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/nb/theme-common.json @@ -0,0 +1,70 @@ +{ + "theme.AnnouncementBar.closeButtonAriaLabel": "Lukk", + "theme.BackToTopButton.buttonAriaLabel": "Rull tilbake til toppen", + "theme.CodeBlock.copied": "Kopiert", + "theme.CodeBlock.copy": "Kopiere", + "theme.CodeBlock.copyButtonAriaLabel": "Kopier koden til utklippstavlen", + "theme.CodeBlock.wordWrapToggle": "Slå tekstbryting av/på", + "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": "Veksle på den sammenleggbare sidefeltkategorien '{label}'", + "theme.ErrorPageContent.title": "Denne siden krasjet.", + "theme.ErrorPageContent.tryAgain": "Prøv på nytt", + "theme.NavBar.navAriaLabel": "Hoved", + "theme.NotFound.p1": "Vi kunne ikke finne det du lette etter.", + "theme.NotFound.p2": "Kontakt eieren av nettstedet som koblet deg til den opprinnelige nettadressen og la dem få vite at koblingen deres er ødelagt.", + "theme.NotFound.title": "Siden ble ikke funnet", + "theme.TOCCollapsible.toggleButtonLabel": "På denne siden", + "theme.admonition.caution": "forsiktig", + "theme.admonition.danger": "fare", + "theme.admonition.info": "info", + "theme.admonition.note": "merknad", + "theme.admonition.tip": "tips", + "theme.blog.archive.description": "Arkiv", + "theme.blog.archive.title": "Arkiv", + "theme.blog.paginator.navAriaLabel": "Navigering av bloggliste", + "theme.blog.paginator.newerEntries": "Nyere innlegg", + "theme.blog.paginator.olderEntries": "Eldre innlegg", + "theme.blog.post.paginator.navAriaLabel": "Navigering på blogginnlegg", + "theme.blog.post.paginator.newerPost": "Nyere innlegg", + "theme.blog.post.paginator.olderPost": "Eldre innlegg", + "theme.blog.post.plurals": "Ett innlegg|{count} innlegg", + "theme.blog.post.readMore": "Les mer", + "theme.blog.post.readMoreLabel": "Les mer om {title}", + "theme.blog.post.readingTime.plurals": "Ett min lesetid|{readingTime} min lesetid", + "theme.blog.sidebar.navAriaLabel": "Navigering av siste blogginnlegg", + "theme.blog.tagTitle": "{nPosts} merket med \"{tagName}\"", + "theme.colorToggle.ariaLabel": "Bytt mellom mørk og lys utseende (nå {mode})", + "theme.colorToggle.ariaLabel.mode.dark": "mørk utseende", + "theme.colorToggle.ariaLabel.mode.light": "lys utseende", + "theme.common.editThisPage": "Rediger denne siden", + "theme.common.headingLinkTitle": "Direkte lenke til {heading}", + "theme.common.skipToMainContent": "Gå til hovedinnhold", + "theme.docs.DocCard.categoryDescription": "{count} artikler", + "theme.docs.breadcrumbs.home": "Hjemmeside", + "theme.docs.breadcrumbs.navAriaLabel": "Søkvei", + "theme.docs.paginator.navAriaLabel": "Dokumenter-sidernavigasjon", + "theme.docs.paginator.next": "Neste", + "theme.docs.paginator.previous": "Forrige", + "theme.docs.sidebar.closeSidebarButtonAriaLabel": "Lukk navigasjonslinjen", + "theme.docs.sidebar.collapseButtonAriaLabel": "Skjul sidefeltet", + "theme.docs.sidebar.collapseButtonTitle": "Collapse sidebar", + "theme.docs.sidebar.expandButtonAriaLabel": "Utvid sidefeltet", + "theme.docs.sidebar.expandButtonTitle": "Utvid sidefeltet", + "theme.docs.sidebar.navAriaLabel": "Dokumenter sidefelt", + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": "Vis/skjul navigasjonslinjen", + "theme.docs.tagDocListPageTitle": "{nDocsTagged} med \"{tagName}\"", + "theme.docs.tagDocListPageTitle.nDocsTagged": "Ett dokument merket|{count} dokumenter merket", + "theme.docs.versionBadge.label": "Versjon: {versionLabel}", + "theme.docs.versions.latestVersionLinkLabel": "siste Versjon", + "theme.docs.versions.latestVersionSuggestionLabel": "For oppdatert dokumentasjon, se {latestVersionLink} ({versionLabel}).", + "theme.docs.versions.unmaintainedVersionLabel": "Dette er dokumentasjon for {siteTitle} {versionLabel}, som ikke lenger vedlikeholdes aktivt.", + "theme.docs.versions.unreleasedVersionLabel": "Dette er uutgitt dokumentasjon for {siteTitle} {versionLabel} versjon.", + "theme.lastUpdated.atDate": " den {date}", + "theme.lastUpdated.byUser": " av {user}", + "theme.lastUpdated.lastUpdatedAtBy": "Sist oppdatert{atDate}{byUser}", + "theme.navbar.mobileLanguageDropdown.label": "Språk", + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← Tilbake til hovedmenyen", + "theme.navbar.mobileVersionsDropdown.label": "Versjoner", + "theme.tags.tagsListLabel": "Tagger:", + "theme.tags.tagsPageLink": "Vis alle tagger", + "theme.tags.tagsPageTitle": "Tagger" +} diff --git a/packages/docusaurus-theme-translations/locales/nb/theme-live-codeblock.json b/packages/docusaurus-theme-translations/locales/nb/theme-live-codeblock.json new file mode 100644 index 0000000000..f8e1c70342 --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/nb/theme-live-codeblock.json @@ -0,0 +1,4 @@ +{ + "theme.Playground.liveEditor": "Live Editor", + "theme.Playground.result": "Resultat" +} diff --git a/packages/docusaurus-theme-translations/locales/nb/theme-search-algolia.json b/packages/docusaurus-theme-translations/locales/nb/theme-search-algolia.json new file mode 100644 index 0000000000..1871413677 --- /dev/null +++ b/packages/docusaurus-theme-translations/locales/nb/theme-search-algolia.json @@ -0,0 +1,35 @@ +{ + "theme.SearchBar.label": "Søk", + "theme.SearchBar.seeAll": "Se alle {count} resultat", + "theme.SearchModal.errorScreen.helpText": "Det kan være lurt å sjekke nettverkstilkoblingen.", + "theme.SearchModal.errorScreen.titleText": "Kan ikke hente resultater", + "theme.SearchModal.footer.closeKeyAriaLabel": "Escape-tasten", + "theme.SearchModal.footer.closeText": "Lukk", + "theme.SearchModal.footer.navigateDownKeyAriaLabel": "Pil ned", + "theme.SearchModal.footer.navigateText": "for å navigere", + "theme.SearchModal.footer.navigateUpKeyAriaLabel": "Pil opp", + "theme.SearchModal.footer.searchByText": "Søk på", + "theme.SearchModal.footer.selectKeyAriaLabel": "Enter-tasten", + "theme.SearchModal.footer.selectText": "for å velge", + "theme.SearchModal.noResultsScreen.noResultsText": "Ingen resultat", + "theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": "Gi oss beskjed.", + "theme.SearchModal.noResultsScreen.reportMissingResultsText": "Tenker du at denne spørringen bør gi resultater?", + "theme.SearchModal.noResultsScreen.suggestedQueryText": "Prøv å søke etter", + "theme.SearchModal.placeholder": "Søk i dokumenter", + "theme.SearchModal.searchBox.cancelButtonText": "Avbryt", + "theme.SearchModal.searchBox.resetButtonTitle": "Fjern søket", + "theme.SearchModal.startScreen.favoriteSearchesTitle": "Favoritt", + "theme.SearchModal.startScreen.noRecentSearchesText": "Ingen nylige søk", + "theme.SearchModal.startScreen.recentSearchesTitle": "Nylig", + "theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": "Fjern dette søket fra favoritter", + "theme.SearchModal.startScreen.removeRecentSearchButtonTitle": "Fjern dette søket fra loggen", + "theme.SearchModal.startScreen.saveRecentSearchButtonTitle": "Lagre dette søket", + "theme.SearchPage.algoliaLabel": "Søk med Algolia", + "theme.SearchPage.documentsFound.plurals": "Ett dokument funnet|{count} dokumenter funnet", + "theme.SearchPage.emptyResultsTitle": "Søk i dokumentasjonen", + "theme.SearchPage.existingResultsTitle": "Søkeresultater for \"{query}\"", + "theme.SearchPage.fetchingNewResults": "Henter nye resultater...", + "theme.SearchPage.inputLabel": "Søk", + "theme.SearchPage.inputPlaceholder": "Skriv inn søket ditt her", + "theme.SearchPage.noResultsText": "Ingen resultater ble funnet" +} diff --git a/packages/docusaurus-theme-translations/locales/sl/theme-common.json b/packages/docusaurus-theme-translations/locales/sl/theme-common.json index dbf9cd55f6..d93bb2ce0f 100644 --- a/packages/docusaurus-theme-translations/locales/sl/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/sl/theme-common.json @@ -66,7 +66,5 @@ "theme.navbar.mobileVersionsDropdown.label": "Verzije", "theme.tags.tagsListLabel": "Oznake:", "theme.tags.tagsPageLink": "Poglej vse oznake", - "theme.tags.tagsPageTitle": "Oznake", - "theme.unlistedContent.message": "Ta stran ni zabeležena. Iskalniki je ne bodo indeksirali, do nje bodo uporabniki lahko dostopali le z direktno povezavo.", - "theme.unlistedContent.title": "Nezabeležena stran" + "theme.tags.tagsPageTitle": "Oznake" } diff --git a/packages/docusaurus-theme-translations/locales/tr/theme-common.json b/packages/docusaurus-theme-translations/locales/tr/theme-common.json index 613b15158c..1e97990faf 100644 --- a/packages/docusaurus-theme-translations/locales/tr/theme-common.json +++ b/packages/docusaurus-theme-translations/locales/tr/theme-common.json @@ -66,7 +66,5 @@ "theme.navbar.mobileVersionsDropdown.label": "Versiyonlar", "theme.tags.tagsListLabel": "Etiketler:", "theme.tags.tagsPageLink": "Tüm Etiketleri Görüntüle", - "theme.tags.tagsPageTitle": "Etiketler", - "theme.unlistedContent.message": "Bu sayfa listelenmemiş. Arama motorları dizine eklemez ve yalnızca doğrudan bağlantısı olan kullanıcılar buna erişebilir.", - "theme.unlistedContent.title": "Listelenmeyen sayfa" + "theme.tags.tagsPageTitle": "Etiketler" } diff --git a/packages/docusaurus-theme-translations/package.json b/packages/docusaurus-theme-translations/package.json index de7e6d8146..04d04806e9 100644 --- a/packages/docusaurus-theme-translations/package.json +++ b/packages/docusaurus-theme-translations/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/theme-translations", - "version": "2.3.1", + "version": "2.4.0", "description": "Docusaurus theme translations.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -23,8 +23,8 @@ "tslib": "^2.4.0" }, "devDependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", "lodash": "^4.17.21" }, "engines": { diff --git a/packages/docusaurus-types/package.json b/packages/docusaurus-types/package.json index 41d47ad459..ea6ff70453 100644 --- a/packages/docusaurus-types/package.json +++ b/packages/docusaurus-types/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/types", - "version": "2.3.1", + "version": "2.4.0", "description": "Common types for Docusaurus packages.", "types": "./src/index.d.ts", "publishConfig": { diff --git a/packages/docusaurus-utils-common/package.json b/packages/docusaurus-utils-common/package.json index 62620141e0..51b6e464ca 100644 --- a/packages/docusaurus-utils-common/package.json +++ b/packages/docusaurus-utils-common/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/utils-common", - "version": "2.3.1", + "version": "2.4.0", "description": "Common (Node/Browser) utility functions for Docusaurus packages.", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/packages/docusaurus-utils-common/src/__tests__/errorUtils.test.ts b/packages/docusaurus-utils-common/src/__tests__/errorUtils.test.ts new file mode 100644 index 0000000000..1d29f66086 --- /dev/null +++ b/packages/docusaurus-utils-common/src/__tests__/errorUtils.test.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {getErrorCausalChain} from '../errorUtils'; + +describe('getErrorCausalChain', () => { + it('works for simple error', () => { + const error = new Error('msg'); + expect(getErrorCausalChain(error)).toEqual([error]); + }); + + it('works for nested errors', () => { + const error = new Error('msg', { + cause: new Error('msg', {cause: new Error('msg')}), + }); + expect(getErrorCausalChain(error)).toEqual([ + error, + error.cause, + (error.cause as Error).cause, + ]); + }); +}); diff --git a/packages/docusaurus-utils-common/src/errorUtils.ts b/packages/docusaurus-utils-common/src/errorUtils.ts new file mode 100644 index 0000000000..5e3161dc47 --- /dev/null +++ b/packages/docusaurus-utils-common/src/errorUtils.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +type CausalChain = [Error, ...Error[]]; + +export function getErrorCausalChain(error: Error): CausalChain { + if (error.cause) { + return [error, ...getErrorCausalChain(error.cause as Error)]; + } + return [error]; +} diff --git a/packages/docusaurus-utils-common/src/index.ts b/packages/docusaurus-utils-common/src/index.ts index 11b925a283..b1bbfb5237 100644 --- a/packages/docusaurus-utils-common/src/index.ts +++ b/packages/docusaurus-utils-common/src/index.ts @@ -10,3 +10,4 @@ export { default as applyTrailingSlash, type ApplyTrailingSlashParams, } from './applyTrailingSlash'; +export {getErrorCausalChain} from './errorUtils'; diff --git a/packages/docusaurus-utils-validation/package.json b/packages/docusaurus-utils-validation/package.json index 34816ef406..775898eaaf 100644 --- a/packages/docusaurus-utils-validation/package.json +++ b/packages/docusaurus-utils-validation/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/utils-validation", - "version": "2.3.1", + "version": "2.4.0", "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": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", "joi": "^17.6.0", "js-yaml": "^4.1.0", "tslib": "^2.4.0" diff --git a/packages/docusaurus-utils/package.json b/packages/docusaurus-utils/package.json index c5009ad2ea..b13aef63b0 100644 --- a/packages/docusaurus-utils/package.json +++ b/packages/docusaurus-utils/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/utils", - "version": "2.3.1", + "version": "2.4.0", "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": "2.3.1", + "@docusaurus/logger": "2.4.0", "@svgr/webpack": "^6.2.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -39,7 +39,7 @@ "node": ">=16.14" }, "devDependencies": { - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.0", "@types/dedent": "^0.7.0", "@types/github-slugger": "^1.3.0", "@types/micromatch": "^4.0.2", diff --git a/packages/docusaurus/bin/docusaurus.mjs b/packages/docusaurus/bin/docusaurus.mjs index 76cead11db..00670612b9 100755 --- a/packages/docusaurus/bin/docusaurus.mjs +++ b/packages/docusaurus/bin/docusaurus.mjs @@ -24,6 +24,12 @@ import { } from '../lib/index.js'; import beforeCli from './beforeCli.mjs'; +// Env variables are initialized to dev, but can be overridden by each command +// For example, "docusaurus build" overrides them to "production" +// See also https://github.com/facebook/docusaurus/issues/8599 +process.env.BABEL_ENV ??= 'development'; +process.env.NODE_ENV ??= 'development'; + await beforeCli(); cli.version(DOCUSAURUS_VERSION).usage(' [options]'); diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 5ef2262daa..c5e242d9c4 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": "2.3.1", + "version": "2.4.0", "license": "MIT", "publishConfig": { "access": "public" @@ -43,13 +43,13 @@ "@babel/runtime": "^7.18.6", "@babel/runtime-corejs3": "^7.18.6", "@babel/traverse": "^7.18.8", - "@docusaurus/cssnano-preset": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", + "@docusaurus/cssnano-preset": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@slorber/static-site-generator-webpack-plugin": "^4.0.7", "@svgr/webpack": "^6.2.1", "autoprefixer": "^10.4.7", @@ -106,8 +106,8 @@ "webpackbar": "^5.0.2" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/types": "2.3.1", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/types": "2.4.0", "@types/detect-port": "^1.3.2", "@types/react-dom": "^18.0.6", "@types/react-router-config": "^5.0.6", diff --git a/packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx b/packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx index 5edad3f7c1..8bc3dd9258 100644 --- a/packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx +++ b/packages/docusaurus/src/client/BaseUrlIssueBanner/index.tsx @@ -32,7 +32,7 @@ function createInlineHtmlBanner(baseUrl: string) { return `

Your Docusaurus site did not load properly.

-

A very common reason is a wrong site baseUrl configuration.

+

A very common reason is a wrong site baseUrl configuration.

Current configured baseUrl = ${baseUrl} ${ baseUrl === '/' ? ' (default value)' : '' }

diff --git a/packages/docusaurus/src/client/theme-fallback/Error/index.tsx b/packages/docusaurus/src/client/theme-fallback/Error/index.tsx index 513348cce4..79ace57d76 100644 --- a/packages/docusaurus/src/client/theme-fallback/Error/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/Error/index.tsx @@ -11,6 +11,7 @@ import React from 'react'; import Head from '@docusaurus/Head'; import ErrorBoundary from '@docusaurus/ErrorBoundary'; +import {getErrorCausalChain} from '@docusaurus/utils-common'; import Layout from '@theme/Layout'; import type {Props} from '@theme/Error'; @@ -21,20 +22,38 @@ function ErrorDisplay({error, tryAgain}: Props): JSX.Element { display: 'flex', flexDirection: 'column', justifyContent: 'center', - alignItems: 'center', - height: '50vh', + alignItems: 'flex-start', + minHeight: '100vh', width: '100%', + maxWidth: '80ch', fontSize: '20px', + margin: '0 auto', + padding: '1rem', }}> -

This page crashed.

-

{error.message}

- +
); } +function ErrorBoundaryError({error}: {error: Error}): JSX.Element { + const causalChain = getErrorCausalChain(error); + const fullMessage = causalChain.map((e) => e.message).join('\n\nCause:\n'); + return

{fullMessage}

; +} + export default function Error({error, tryAgain}: Props): JSX.Element { // We wrap the error in its own error boundary because the layout can actually // throw too... Only the ErrorDisplay component is simple enough to be diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index 6250f0095b..9d33f8bc83 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -46,6 +46,10 @@ export async function build( // See https://github.com/facebook/docusaurus/pull/2496 forceTerminate: boolean = true, ): Promise { + process.env.BABEL_ENV = 'production'; + process.env.NODE_ENV = 'production'; + process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale; + const siteDir = await fs.realpath(siteDirParam); ['SIGINT', 'SIGTERM'].forEach((sig) => { @@ -117,8 +121,11 @@ async function buildLocale({ forceTerminate: boolean; isLastLocale: boolean; }): Promise { - process.env.BABEL_ENV = 'production'; - process.env.NODE_ENV = 'production'; + // Temporary workaround to unlock the ability to translate the site config + // We'll remove it if a better official API can be designed + // See https://github.com/facebook/docusaurus/issues/4542 + process.env.DOCUSAURUS_CURRENT_LOCALE = locale; + logger.info`name=${`[${locale}]`} Creating an optimized production build...`; const props: Props = await load({ diff --git a/packages/docusaurus/src/commands/start.ts b/packages/docusaurus/src/commands/start.ts index 849ee610bf..fa80c81365 100644 --- a/packages/docusaurus/src/commands/start.ts +++ b/packages/docusaurus/src/commands/start.ts @@ -39,10 +39,13 @@ export async function start( siteDirParam: string = '.', cliOptions: Partial = {}, ): Promise { + // Temporary workaround to unlock the ability to translate the site config + // We'll remove it if a better official API can be designed + // See https://github.com/facebook/docusaurus/issues/4542 + process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale; + const siteDir = await fs.realpath(siteDirParam); - process.env.NODE_ENV = 'development'; - process.env.BABEL_ENV = 'development'; logger.info('Starting the development server...'); function loadSite() { diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 8f4c954541..5ef11d761c 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/eslint-plugin", - "version": "2.3.1", + "version": "2.4.0", "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 2379036f4b..0212f5c57c 100644 --- a/packages/lqip-loader/package.json +++ b/packages/lqip-loader/package.json @@ -1,6 +1,6 @@ { "name": "@docusaurus/lqip-loader", - "version": "2.3.1", + "version": "2.4.0", "description": "Low Quality Image Placeholders (LQIP) loader for webpack.", "main": "lib/index.js", "publishConfig": { @@ -17,7 +17,7 @@ }, "license": "MIT", "dependencies": { - "@docusaurus/logger": "2.3.1", + "@docusaurus/logger": "2.4.0", "file-loader": "^6.2.0", "lodash": "^4.17.21", "sharp": "^0.30.7", diff --git a/packages/stylelint-copyright/package.json b/packages/stylelint-copyright/package.json index 46292b26e3..ceba53bd91 100644 --- a/packages/stylelint-copyright/package.json +++ b/packages/stylelint-copyright/package.json @@ -1,6 +1,6 @@ { "name": "stylelint-copyright", - "version": "2.3.1", + "version": "2.4.0", "description": "Stylelint plugin to check CSS files for a copyright header.", "main": "lib/index.js", "license": "MIT", diff --git a/website/_dogfooding/_pages tests/embeds.tsx b/website/_dogfooding/_pages tests/embeds.tsx new file mode 100644 index 0000000000..36bc066688 --- /dev/null +++ b/website/_dogfooding/_pages tests/embeds.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import Layout from '@theme/Layout'; +import Heading from '@theme/Heading'; +import BrowserWindow from '@site/src/components/BrowserWindow'; + +function IframeTest({url}: {url: string}) { + return ( +
+ +