diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index e731187806..ceca2f46d0 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -249,7 +249,7 @@ describe('simple website', () => { .spyOn(cliDocs, 'cliDocsVersionCommand') .mockImplementation(); const cli = new commander.Command(); - // @ts-expect-error: TODO annoying type incompatibility + // @ts-expect-error: in actual usage, we pass the static commander instead of the new command plugin.extendCli!(cli); cli.parse(['node', 'test', 'docs:version', '1.0.0']); expect(mock).toHaveBeenCalledTimes(1); @@ -373,7 +373,7 @@ describe('versioned website', () => { .spyOn(cliDocs, 'cliDocsVersionCommand') .mockImplementation(); const cli = new commander.Command(); - // @ts-expect-error: TODO annoying type incompatibility + // @ts-expect-error: in actual usage, we pass the static commander instead of the new command plugin.extendCli!(cli); cli.parse(['node', 'test', 'docs:version', '2.0.0']); expect(mock).toHaveBeenCalledTimes(1); @@ -522,7 +522,7 @@ describe('versioned website (community)', () => { .spyOn(cliDocs, 'cliDocsVersionCommand') .mockImplementation(); const cli = new commander.Command(); - // @ts-expect-error: TODO annoying type incompatibility + // @ts-expect-error: in actual usage, we pass the static commander instead of the new command plugin.extendCli!(cli); cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']); expect(mock).toHaveBeenCalledTimes(1); diff --git a/packages/docusaurus-theme-common/src/utils/reactUtils.tsx b/packages/docusaurus-theme-common/src/utils/reactUtils.tsx index 5a3ce9ec70..624b9c31de 100644 --- a/packages/docusaurus-theme-common/src/utils/reactUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/reactUtils.tsx @@ -29,6 +29,6 @@ export function useDynamicCallback unknown>( ref.current = callback; }, [callback]); - // @ts-expect-error: TODO, not sure how to fix this TS error + // @ts-expect-error: TS is right that this callback may be a supertype of T, but good enough for our use return useCallback((...args) => ref.current(...args), []); } diff --git a/packages/docusaurus-theme-search-algolia/src/deps.d.ts b/packages/docusaurus-theme-search-algolia/src/deps.d.ts new file mode 100644 index 0000000000..1b69f16b78 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/deps.d.ts @@ -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. + */ + +declare module '@docsearch/react/modal'; + +declare module '@docsearch/react/style'; 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 fff48fb90d..cddbbe9cf1 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -/* eslint-disable @typescript-eslint/ban-ts-comment */ import React, {useState, useRef, useCallback, useMemo} from 'react'; import {createPortal} from 'react-dom'; @@ -120,9 +119,7 @@ function DocSearch({ } return Promise.all([ - // @ts-ignore import('@docsearch/react/modal'), - // @ts-ignore import('@docsearch/react/style'), import('./styles.css'), ]).then(([{DocSearchModal: Modal}]) => { @@ -271,8 +268,7 @@ function DocSearch({ function SearchBar(): JSX.Element { const {siteConfig} = useDocusaurusContext(); - // @ts-ignore - return ; + return ; } export default SearchBar; 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 50be44a8b2..afd832a8bb 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx @@ -6,7 +6,6 @@ */ /* eslint-disable jsx-a11y/no-autofocus */ -/* eslint-disable @typescript-eslint/ban-ts-comment */ import React, {useEffect, useState, useReducer, useRef} from 'react'; @@ -29,6 +28,7 @@ import {useAllDocsData} from '@docusaurus/plugin-content-docs/client'; import Layout from '@theme/Layout'; import Translate, {translate} from '@docusaurus/Translate'; import styles from './styles.module.css'; +import type {ThemeConfig} from '@docusaurus/theme-search-algolia'; // Very simple pluralization: probably good enough for now function useDocumentsFoundPlural() { @@ -151,14 +151,12 @@ type ResultDispatcher = function SearchPage(): JSX.Element { const { - siteConfig: { - themeConfig: { - // @ts-ignore - algolia: {appId, apiKey, indexName, externalUrlRegex}, - }, - }, + siteConfig: {themeConfig}, i18n: {currentLocale}, } = useDocusaurusContext(); + const { + algolia: {appId, apiKey, indexName, externalUrlRegex}, + } = themeConfig as ThemeConfig; const documentsFoundPlural = useDocumentsFoundPlural(); const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers(); diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index bb5d564a12..2dca43bbe1 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -6,7 +6,7 @@ */ import type {RuleSetRule, Configuration} from 'webpack'; -import type {Command} from 'commander'; +import type {CommanderStatic} from 'commander'; import type {ParsedUrlQueryInput} from 'querystring'; import type Joi from 'joi'; import type {Overwrite, DeepPartial} from 'utility-types'; @@ -259,7 +259,6 @@ export interface Plugin { }) => Promise; routesLoaded?: (routes: RouteConfig[]) => void; // TODO remove soon, deprecated (alpha-60) postBuild?: (props: Props & {content: Content}) => Promise; - postStart?: (props: Props) => void; // TODO refactor the configureWebpack API surface: use an object instead of multiple params (requires breaking change) configureWebpack?: ( config: Configuration, @@ -272,7 +271,7 @@ export interface Plugin { getTypeScriptThemePath?: () => string; getPathsToWatch?: () => string[]; getClientModules?: () => string[]; - extendCli?: (cli: Command) => void; + extendCli?: (cli: CommanderStatic) => void; injectHtmlTags?: ({content}: {content: Content}) => { headTags?: HtmlTags; preBodyTags?: HtmlTags; diff --git a/packages/docusaurus-utils/src/urlUtils.ts b/packages/docusaurus-utils/src/urlUtils.ts index ec5e3ead44..1c02abb774 100644 --- a/packages/docusaurus-utils/src/urlUtils.ts +++ b/packages/docusaurus-utils/src/urlUtils.ts @@ -28,8 +28,7 @@ export function normalizeUrl(rawUrls: string[]): string { const replacement = urls[0].match(/^file:\/\/\//) ? '$1:///' : '$1://'; urls[0] = urls[0].replace(/^([^/:]+):\/*/, replacement); - // eslint-disable-next-line - for (let i = 0; i < urls.length; i++) { + for (let i = 0; i < urls.length; i += 1) { let component = urls[i]; if (typeof component !== 'string') { @@ -40,7 +39,7 @@ export function normalizeUrl(rawUrls: string[]): string { if (i === urls.length - 1 && hasEndingSlash) { resultArray.push('/'); } - // eslint-disable-next-line + // eslint-disable-next-line no-continue continue; } diff --git a/packages/docusaurus/bin/beforeCli.js b/packages/docusaurus/bin/beforeCli.js index 4c0cf28055..3bac5b6b26 100644 --- a/packages/docusaurus/bin/beforeCli.js +++ b/packages/docusaurus/bin/beforeCli.js @@ -20,105 +20,114 @@ const { engines: {node: requiredVersion}, } = require('../package.json'); -// Notify user if @docusaurus packages is outdated -// -// Note: this is a 2-step process to avoid delaying cli usage by awaiting a response: -// - 1st run: trigger background job to check releases + store result -// - 2nd run: display potential update to users -// -// Note: even if the -// -// cache data is stored in ~/.config/configstore/update-notifier-@docusaurus -// -const notifier = updateNotifier({ - pkg: { - name, - version, - }, - // Check is in background so it's fine to use a small value like 1h - // Use 0 for debugging - updateCheckInterval: 1000 * 60 * 60, - // updateCheckInterval: 0 -}); +// eslint-disable-next-line import/no-dynamic-require +const sitePkg = require(path.resolve(process.cwd(), 'package.json')); + +/** + * Notify user if `@docusaurus` packages are outdated + * + * Note: this is a 2-step process to avoid delaying cli usage by awaiting a + * response: + * - 1st run: trigger background job to check releases + store result + * - 2nd run: display potential update to users + * + * cache data is stored in `~/.config/configstore/update-notifier-@docusaurus` + */ +function beforeCli() { + const notifier = updateNotifier({ + pkg: { + name, + version, + }, + // Check is in background so it's fine to use a small value like 1h + // Use 0 for debugging + updateCheckInterval: 1000 * 60 * 60, + // updateCheckInterval: 0 + }); + + // Hacky way to ensure we check for updates on first run + // Note: the notification will only happen in the 2nd run + // See https://github.com/yeoman/update-notifier/issues/209 + try { + if ( + notifier.config && + // @ts-expect-error: this is an internal API + !notifier.disabled && + Date.now() - notifier.config.get('lastUpdateCheck') < 50 + ) { + notifier.config.set('lastUpdateCheck', 0); + notifier.check(); + } + } catch (e) { + // Do not stop cli if this fails, see https://github.com/facebook/docusaurus/issues/5400 + logger.error(e); + } + + /** + * We don't want to display update message for canary releases. + * See https://github.com/facebook/docusaurus/issues/5378 + * @param {import('update-notifier').UpdateInfo} update + */ + function ignoreUpdate(update) { + const isCanaryRelease = + update && update.current && update.current.startsWith('0.0.0'); + return isCanaryRelease; + } -// Hacky way to ensure we check for updates on first run -// Note: the notification will only happen in the 2nd run -// See https://github.com/yeoman/update-notifier/issues/209 -try { if ( notifier.config && - !notifier.disabled && - Date.now() - notifier.config.get('lastUpdateCheck') < 50 + notifier.update && + semver.lt(notifier.update.current, notifier.update.latest) ) { - notifier.config.set('lastUpdateCheck', 0); - notifier.check(); - } -} catch (e) { - // Do not stop cli if this fails, see https://github.com/facebook/docusaurus/issues/5400 - logger.error(e); -} + // Because notifier clears cached data after reading it, leading to notifier + // not consistently displaying the update. + // See https://github.com/yeoman/update-notifier/issues/209 + notifier.config.set('update', notifier.update); -// We don't want to display update message for canary releases -// See https://github.com/facebook/docusaurus/issues/5378 -function ignoreUpdate(update) { - const isCanaryRelease = - update && update.current && update.current.startsWith('0.0.0'); - return isCanaryRelease; -} + if (ignoreUpdate(notifier.update)) { + return; + } -if ( - notifier.config && - notifier.update && - semver.lt(notifier.update.current, notifier.update.latest) -) { - // Because notifier clears cached data after reading it, leading to notifier not consistently displaying the update - // See https://github.com/yeoman/update-notifier/issues/209 - notifier.config.set('update', notifier.update); + const siteDocusaurusPackagesForUpdate = Object.keys({ + ...sitePkg.dependencies, + ...sitePkg.devDependencies, + }) + .filter((p) => p.startsWith('@docusaurus')) + .map((p) => p.concat('@latest')) + .join(' '); + const isYarnUsed = fs.existsSync(path.resolve(process.cwd(), 'yarn.lock')); + const upgradeCommand = isYarnUsed + ? `yarn upgrade ${siteDocusaurusPackagesForUpdate}` + : `npm i ${siteDocusaurusPackagesForUpdate}`; - if (ignoreUpdate(notifier.update)) { - // @ts-expect-error: it works - return; + /** @type {import('boxen').Options} */ + const boxenOptions = { + padding: 1, + margin: 1, + align: 'center', + borderColor: 'yellow', + borderStyle: 'round', + }; + + const docusaurusUpdateMessage = boxen( + `Update available ${logger.dim( + `${notifier.update.current}`, + )} → ${logger.green(`${notifier.update.latest}`)} + + To upgrade Docusaurus packages with the latest version, run the following command: + ${logger.code(upgradeCommand)}`, + boxenOptions, + ); + + console.log(docusaurusUpdateMessage); } - // eslint-disable-next-line import/no-dynamic-require, global-require - const sitePkg = require(path.resolve(process.cwd(), 'package.json')); - const siteDocusaurusPackagesForUpdate = Object.keys({ - ...sitePkg.dependencies, - ...sitePkg.devDependencies, - }) - .filter((p) => p.startsWith('@docusaurus')) - .map((p) => p.concat('@latest')) - .join(' '); - const isYarnUsed = fs.existsSync(path.resolve(process.cwd(), 'yarn.lock')); - const upgradeCommand = isYarnUsed - ? `yarn upgrade ${siteDocusaurusPackagesForUpdate}` - : `npm i ${siteDocusaurusPackagesForUpdate}`; - - /** @type {import('boxen').Options} */ - const boxenOptions = { - padding: 1, - margin: 1, - align: 'center', - borderColor: 'yellow', - borderStyle: 'round', - }; - - const docusaurusUpdateMessage = boxen( - `Update available ${logger.dim( - `${notifier.update.current}`, - )} → ${logger.green(`${notifier.update.latest}`)} - -To upgrade Docusaurus packages with the latest version, run the following command: -${logger.code(upgradeCommand)}`, - boxenOptions, - ); - - console.log(docusaurusUpdateMessage); + // notify user if node version needs to be updated + if (!semver.satisfies(process.version, requiredVersion)) { + logger.error('Minimum Node.js version not met :('); + logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`; + process.exit(1); + } } -// notify user if node version needs to be updated -if (!semver.satisfies(process.version, requiredVersion)) { - logger.error('Minimum Node.js version not met :('); - logger.info`You are using Node.js number=${process.version}, Requirement: Node.js number=${requiredVersion}.`; - process.exit(1); -} +module.exports = beforeCli; diff --git a/packages/docusaurus/bin/docusaurus.js b/packages/docusaurus/bin/docusaurus.js index 551732ea31..594c997deb 100755 --- a/packages/docusaurus/bin/docusaurus.js +++ b/packages/docusaurus/bin/docusaurus.js @@ -23,7 +23,9 @@ const { writeHeadingIds, } = require('../lib'); -require('./beforeCli'); +const beforeCli = require('./beforeCli'); + +beforeCli(); const resolveDir = (dir = '.') => fs.realpathSync(dir); @@ -239,7 +241,6 @@ function isInternalCommand(command) { async function run() { if (!isInternalCommand(process.argv.slice(2)[0])) { - // @ts-expect-error: Hmmm await externalCommand(cli, resolveDir('.')); } diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index f423227c57..d8ed24d15b 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -116,6 +116,7 @@ "@types/react-router-config": "^5.0.1", "@types/rtl-detect": "^1.0.0", "@types/serve-handler": "^6.1.1", + "@types/update-notifier": "^5.1.0", "@types/wait-on": "^5.2.0", "@types/webpack-bundle-analyzer": "^4.4.1", "react-test-renderer": "^17.0.2", diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.tsx b/packages/docusaurus/src/client/exports/ComponentCreator.tsx index ad5f73c414..3b66b9bcc4 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.tsx +++ b/packages/docusaurus/src/client/exports/ComponentCreator.tsx @@ -47,11 +47,10 @@ function ComponentCreator( Object.keys(flatChunkNames).forEach((key) => { const chunkRegistry = registry[flatChunkNames[key]]; if (chunkRegistry) { - /* eslint-disable prefer-destructuring */ + // eslint-disable-next-line prefer-destructuring optsLoader[key] = chunkRegistry[0]; optsModules.push(chunkRegistry[1]); optsWebpack.push(chunkRegistry[2]); - /* eslint-enable prefer-destructuring */ } }); diff --git a/packages/docusaurus/src/commands/external.ts b/packages/docusaurus/src/commands/external.ts index 68e72db053..448b16a146 100644 --- a/packages/docusaurus/src/commands/external.ts +++ b/packages/docusaurus/src/commands/external.ts @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import type {Command} from 'commander'; +import type {CommanderStatic} from 'commander'; import {loadContext, loadPluginConfigs} from '../server'; import initPlugins from '../server/plugins/init'; export default async function externalCommand( - cli: Command, + cli: CommanderStatic, siteDir: string, ): Promise { const context = await loadContext(siteDir); diff --git a/website/docs/cli.md b/website/docs/cli.md index a491f7f618..5955f30f78 100644 --- a/website/docs/cli.md +++ b/website/docs/cli.md @@ -121,24 +121,13 @@ Running the command will copy the relevant theme files to your site folder. You | `--danger` | Allow swizzling of unstable components | | `--typescript` | Swizzle TypeScript components | -An example to use `--danger` flag let's consider the below code: - -```bash npm2yarn -npm run swizzle @docusaurus/theme-classic Logo -- --danger -``` - :::caution Unstable Components: components that have a higher risk of breaking changes due to internal refactorings. ::: -To unswizzle a component, simply delete the files of the swizzled component. - - +To learn more about swizzling, see the [swizzling guide](./advanced/swizzling.md). ### `docusaurus deploy [siteDir]` {#docusaurus-deploy-sitedir} diff --git a/yarn.lock b/yarn.lock index b36f776429..6f8108ada2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3686,6 +3686,11 @@ dependencies: "@types/color-convert" "*" +"@types/configstore@*": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/configstore/-/configstore-5.0.1.tgz#7be34d28ce29a408c98e717ada0488664eaf6173" + integrity sha512-c/QCznvk7bLKGhHETj29rqKufui3jaAxjBhK4R2zUrMG5UG0qTwfWYxBoUbH8JCyDjdCWMIxPJ7/Fdz1UcAnWg== + "@types/connect-history-api-fallback@*", "@types/connect-history-api-fallback@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" @@ -4283,6 +4288,14 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== +"@types/update-notifier@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/update-notifier/-/update-notifier-5.1.0.tgz#52ed6a2e9851fd6f1c88e93c85e8a0e1d5500fda" + integrity sha512-aGY5pH1Q/DcToKXl4MCj1c0uDUB+zSVFDRCI7Q7js5sguzBTqJV/5kJA2awofbtWYF3xnon1TYdZYnFditRPtQ== + dependencies: + "@types/configstore" "*" + boxen "^4.2.0" + "@types/wait-on@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@types/wait-on/-/wait-on-5.3.1.tgz#bc5520d1d8b90b9caab1bef23315685ded73320d" @@ -5572,6 +5585,20 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + boxen@^5.0.0, boxen@^5.0.1: version "5.1.2" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" @@ -6163,7 +6190,7 @@ clear-module@^4.1.2: parent-module "^2.0.0" resolve-from "^5.0.0" -cli-boxes@^2.2.1: +cli-boxes@^2.2.0, cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== @@ -17901,6 +17928,11 @@ tempy@^1.0.0: type-fest "^0.16.0" unique-string "^2.0.0" +term-size@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" + integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== + terminal-link@^2.0.0, terminal-link@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"