diff --git a/.eslintrc.js b/.eslintrc.js index 88a949af97..f1c1cf99b8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -69,8 +69,6 @@ module.exports = { 'no-param-reassign': [WARNING, {props: false}], 'no-prototype-builtins': WARNING, 'no-restricted-exports': OFF, - 'no-useless-escape': WARNING, - 'no-template-curly-in-string': WARNING, 'no-restricted-imports': [ ERROR, { @@ -97,8 +95,33 @@ module.exports = { ], }, ], - 'no-restricted-syntax': WARNING, + 'no-restricted-syntax': [ + WARNING, + // Copied from airbnb, removed for...of statement, added export all + { + selector: 'ForInStatement', + message: + 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.', + }, + { + selector: 'LabeledStatement', + message: + 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', + }, + { + selector: 'WithStatement', + message: + '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', + }, + { + selector: 'ExportAllDeclaration', + message: + "Export all does't work well if imported in ESM due to how they are transpiled, and they can also lead to unexpected exposure of internal methods.", + }, + ], + 'no-template-curly-in-string': WARNING, 'no-unused-expressions': [WARNING, {allowTaggedTemplates: true}], + 'no-useless-escape': WARNING, 'prefer-destructuring': WARNING, 'prefer-named-capture-group': WARNING, 'prefer-template': WARNING, diff --git a/admin/scripts/generateExamples.mjs b/admin/scripts/generateExamples.mjs index 54e3e92c19..e47b6b080b 100644 --- a/admin/scripts/generateExamples.mjs +++ b/admin/scripts/generateExamples.mjs @@ -182,7 +182,6 @@ const templates = ( await fs.readdir('./packages/create-docusaurus/templates') ).filter((name) => !excludes.includes(name)); console.log(`Will generate examples for templates: ${templates.join(',')}`); -// eslint-disable-next-line no-restricted-syntax for (const template of templates) { await generateTemplateExample(template); } diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 1ca6d07c00..7c7e4ec968 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -240,11 +240,11 @@ declare module '@docusaurus/Translate' { } declare module '@docusaurus/router' { - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line import/no-extraneous-dependencies, no-restricted-syntax export * from 'react-router-dom'; } declare module '@docusaurus/history' { - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line import/no-extraneous-dependencies, no-restricted-syntax export * from 'history'; } diff --git a/packages/docusaurus-plugin-content-blog/src/types.ts b/packages/docusaurus-plugin-content-blog/src/types.ts index d0a1ccec22..54dc73c501 100644 --- a/packages/docusaurus-plugin-content-blog/src/types.ts +++ b/packages/docusaurus-plugin-content-blog/src/types.ts @@ -27,7 +27,8 @@ export interface BlogContent { export interface BlogTags { // TODO, the key is the tag slug/permalink - // This is due to legacy frontmatter: tags: [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"} + // This is due to legacy frontmatter: tags: + // [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"}] // Soon we should forbid declaring permalink through frontmatter [tagKey: string]: BlogTag; } diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index 54916f75b6..ae422c4be7 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -5,7 +5,7 @@ "main": "lib/index.js", "exports": { "./client": "./lib/client/index.js", - "./server": "./lib/server/index.js", + "./server": "./lib/server-export.js", ".": "./lib/index.js" }, "types": "src/plugin-content-docs.d.ts", diff --git a/packages/docusaurus-plugin-content-docs/src/client/globalDataHooks.ts b/packages/docusaurus-plugin-content-docs/src/client/globalDataHooks.ts deleted file mode 100644 index bd225ce6cd..0000000000 --- a/packages/docusaurus-plugin-content-docs/src/client/globalDataHooks.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import {useLocation} from '@docusaurus/router'; -import useGlobalData, { - // useAllPluginInstancesData, - usePluginData, -} from '@docusaurus/useGlobalData'; - -import { - getActivePlugin, - getLatestVersion, - getActiveVersion, - getActiveDocContext, - getDocVersionSuggestions, -} from './docsClientUtils'; -import type { - GlobalPluginData, - GlobalVersion, - ActivePlugin, - ActiveDocContext, - DocVersionSuggestions, - GetActivePluginOptions, -} from '@docusaurus/plugin-content-docs/client'; - -// Important to use a constant object to avoid React useEffect executions etc. -// see https://github.com/facebook/docusaurus/issues/5089 -const StableEmptyObject = {}; - -// Not using useAllPluginInstancesData() because in blog-only mode, docs hooks -// are still used by the theme. We need a fail-safe fallback when the docs -// plugin is not in use -export const useAllDocsData = (): Record => - // useAllPluginInstancesData('docusaurus-plugin-content-docs'); - useGlobalData()['docusaurus-plugin-content-docs'] ?? StableEmptyObject; - -export const useDocsData = (pluginId: string | undefined): GlobalPluginData => - usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData; - -// TODO this feature should be provided by docusaurus core -export const useActivePlugin = ( - options: GetActivePluginOptions = {}, -): ActivePlugin | undefined => { - const data = useAllDocsData(); - const {pathname} = useLocation(); - return getActivePlugin(data, pathname, options); -}; - -export const useActivePluginAndVersion = ( - options: GetActivePluginOptions = {}, -): - | undefined - | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => { - const activePlugin = useActivePlugin(options); - const {pathname} = useLocation(); - if (activePlugin) { - const activeVersion = getActiveVersion(activePlugin.pluginData, pathname); - return { - activePlugin, - activeVersion, - }; - } - return undefined; -}; - -// versions are returned ordered (most recent first) -export const useVersions = (pluginId: string | undefined): GlobalVersion[] => { - const data = useDocsData(pluginId); - return data.versions; -}; - -export const useLatestVersion = ( - pluginId: string | undefined, -): GlobalVersion => { - const data = useDocsData(pluginId); - return getLatestVersion(data); -}; - -// Note: return undefined on doc-unrelated pages, -// because there's no version currently considered as active -export const useActiveVersion = ( - pluginId: string | undefined, -): GlobalVersion | undefined => { - const data = useDocsData(pluginId); - const {pathname} = useLocation(); - return getActiveVersion(data, pathname); -}; - -export const useActiveDocContext = ( - pluginId: string | undefined, -): ActiveDocContext => { - const data = useDocsData(pluginId); - const {pathname} = useLocation(); - return getActiveDocContext(data, pathname); -}; - -// Useful to say "hey, you are not on the latest docs version, please switch" -export const useDocVersionSuggestions = ( - pluginId: string | undefined, -): DocVersionSuggestions => { - const data = useDocsData(pluginId); - const {pathname} = useLocation(); - return getDocVersionSuggestions(data, pathname); -}; diff --git a/packages/docusaurus-plugin-content-docs/src/client/index.ts b/packages/docusaurus-plugin-content-docs/src/client/index.ts index d2d1cf887e..5529d38739 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/index.ts @@ -5,4 +5,100 @@ * LICENSE file in the root directory of this source tree. */ -export * from './globalDataHooks'; +import {useLocation} from '@docusaurus/router'; +import useGlobalData, {usePluginData} from '@docusaurus/useGlobalData'; + +import { + getActivePlugin, + getLatestVersion, + getActiveVersion, + getActiveDocContext, + getDocVersionSuggestions, +} from './docsClientUtils'; +import type { + GlobalPluginData, + GlobalVersion, + ActivePlugin, + ActiveDocContext, + DocVersionSuggestions, + GetActivePluginOptions, +} from '@docusaurus/plugin-content-docs/client'; + +// Important to use a constant object to avoid React useEffect executions etc. +// see https://github.com/facebook/docusaurus/issues/5089 +const StableEmptyObject = {}; + +// Not using useAllPluginInstancesData() because in blog-only mode, docs hooks +// are still used by the theme. We need a fail-safe fallback when the docs +// plugin is not in use +export const useAllDocsData = (): Record => + useGlobalData()['docusaurus-plugin-content-docs'] ?? StableEmptyObject; + +export const useDocsData = (pluginId: string | undefined): GlobalPluginData => + usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData; + +// TODO this feature should be provided by docusaurus core +export const useActivePlugin = ( + options: GetActivePluginOptions = {}, +): ActivePlugin | undefined => { + const data = useAllDocsData(); + const {pathname} = useLocation(); + return getActivePlugin(data, pathname, options); +}; + +export const useActivePluginAndVersion = ( + options: GetActivePluginOptions = {}, +): + | undefined + | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => { + const activePlugin = useActivePlugin(options); + const {pathname} = useLocation(); + if (activePlugin) { + const activeVersion = getActiveVersion(activePlugin.pluginData, pathname); + return { + activePlugin, + activeVersion, + }; + } + return undefined; +}; + +// versions are returned ordered (most recent first) +export const useVersions = (pluginId: string | undefined): GlobalVersion[] => { + const data = useDocsData(pluginId); + return data.versions; +}; + +export const useLatestVersion = ( + pluginId: string | undefined, +): GlobalVersion => { + const data = useDocsData(pluginId); + return getLatestVersion(data); +}; + +// Note: return undefined on doc-unrelated pages, +// because there's no version currently considered as active +export const useActiveVersion = ( + pluginId: string | undefined, +): GlobalVersion | undefined => { + const data = useDocsData(pluginId); + const {pathname} = useLocation(); + return getActiveVersion(data, pathname); +}; + +export const useActiveDocContext = ( + pluginId: string | undefined, +): ActiveDocContext => { + const data = useDocsData(pluginId); + const {pathname} = useLocation(); + return getActiveDocContext(data, pathname); +}; + +// Useful to say "hey, you are not on the latest docs version, please switch" +export const useDocVersionSuggestions = ( + pluginId: string | undefined, +): DocVersionSuggestions => { + const data = useDocsData(pluginId); + const {pathname} = useLocation(); + return getDocVersionSuggestions(data, pathname); +}; diff --git a/packages/docusaurus-plugin-content-docs/src/server/index.ts b/packages/docusaurus-plugin-content-docs/src/server-export.ts similarity index 72% rename from packages/docusaurus-plugin-content-docs/src/server/index.ts rename to packages/docusaurus-plugin-content-docs/src/server-export.ts index 1be860cede..a1942a1e8a 100644 --- a/packages/docusaurus-plugin-content-docs/src/server/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/server-export.ts @@ -6,7 +6,12 @@ */ // APIs available to Node.js -export * from '../constants'; +export { + CURRENT_VERSION_NAME, + VERSIONED_DOCS_DIR, + VERSIONED_SIDEBARS_DIR, + VERSIONS_JSON_FILE, +} from './constants'; export { filterVersions, @@ -16,4 +21,4 @@ export { getVersionsFilePath, readVersionsFile, readVersionNames, -} from '../versions'; +} from './versions'; diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts index 7b2e902fad..8c172e55de 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts @@ -303,7 +303,6 @@ Available document ids are: label: string; } | undefined { - // eslint-disable-next-line no-restricted-syntax for (const item of sidebar) { if (item.type === 'doc') { return { diff --git a/packages/docusaurus-theme-classic/src/theme/SearchBar.tsx b/packages/docusaurus-theme-classic/src/theme/SearchBar.tsx index 7508f67db9..08ee42e309 100644 --- a/packages/docusaurus-theme-classic/src/theme/SearchBar.tsx +++ b/packages/docusaurus-theme-classic/src/theme/SearchBar.tsx @@ -9,4 +9,5 @@ // If you swizzled this, it is your responsibility to provide an implementation // Tip: swizzle the SearchBar from the Algolia theme for inspiration: // npm run swizzle @docusaurus/theme-search-algolia SearchBar + export {default} from '@docusaurus/Noop'; diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 2ba9be962f..8be209b921 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -96,7 +96,6 @@ export function findSidebarCategory( sidebar: PropSidebar, predicate: (category: PropSidebarItemCategory) => boolean, ): PropSidebarItemCategory | undefined { - // eslint-disable-next-line no-restricted-syntax for (const item of sidebar) { if (item.type === 'category') { if (predicate(item)) { @@ -119,7 +118,6 @@ export function findFirstCategoryLink( return item.href; } - // eslint-disable-next-line no-restricted-syntax for (const subItem of item.items) { if (subItem.type === 'link') { return subItem.href; diff --git a/packages/docusaurus-theme-live-codeblock/src/deps.d.ts b/packages/docusaurus-theme-live-codeblock/src/deps.d.ts index af1b85719c..57918444ad 100644 --- a/packages/docusaurus-theme-live-codeblock/src/deps.d.ts +++ b/packages/docusaurus-theme-live-codeblock/src/deps.d.ts @@ -7,7 +7,7 @@ declare module '@philpl/buble' { import type {TransformOptions as OriginalTransformOptions} from 'buble'; - // eslint-disable-next-line import/no-extraneous-dependencies + // eslint-disable-next-line import/no-extraneous-dependencies, no-restricted-syntax export * from 'buble'; export const features: string[]; export interface TransformOptions extends OriginalTransformOptions { diff --git a/packages/docusaurus-theme-translations/src/index.ts b/packages/docusaurus-theme-translations/src/index.ts index 178c93ca1a..ef39ca61e9 100644 --- a/packages/docusaurus-theme-translations/src/index.ts +++ b/packages/docusaurus-theme-translations/src/index.ts @@ -44,7 +44,6 @@ export async function readDefaultCodeTranslationMessages({ // Return the content of the first file that match // fr_FR.json => fr.json => nothing - // eslint-disable-next-line no-restricted-syntax for (const localeToTry of localesToTry) { const filePath = path.resolve(dirPath, localeToTry, `${name}.json`); diff --git a/packages/docusaurus-theme-translations/update.js b/packages/docusaurus-theme-translations/update.js index 7f0239354c..f52bfad87e 100644 --- a/packages/docusaurus-theme-translations/update.js +++ b/packages/docusaurus-theme-translations/update.js @@ -268,8 +268,6 @@ async function updateCodeTranslations() { const stats = {}; let messageCount = 0; const {2: newLocale} = process.argv; - // Order is important. The log messages must be in the same order as execution - // eslint-disable-next-line no-restricted-syntax for (const theme of Themes) { const {baseFile, localesFiles} = await getCodeTranslationFiles(theme.name); logger.info`Will update base file for name=${theme.name}\n`; @@ -289,7 +287,6 @@ async function updateCodeTranslations() { )} was already created!`; } } else { - // eslint-disable-next-line no-restricted-syntax for (const localeFile of localesFiles) { const localeName = path.basename(path.dirname(localeFile)); const pluginName = path.basename(localeFile, path.extname(localeFile)); diff --git a/packages/docusaurus-utils-validation/src/index.ts b/packages/docusaurus-utils-validation/src/index.ts index daaac61bd4..0462958250 100644 --- a/packages/docusaurus-utils-validation/src/index.ts +++ b/packages/docusaurus-utils-validation/src/index.ts @@ -9,5 +9,21 @@ export {default as Joi} from './Joi'; export {JoiFrontMatter} from './JoiFrontMatter'; -export * from './validationUtils'; -export * from './validationSchemas'; +export { + isValidationDisabledEscapeHatch, + logValidationBugReportHint, + printWarning, + normalizePluginOptions, + normalizeThemeConfig, + validateFrontMatter, +} from './validationUtils'; +export { + PluginIdSchema, + RemarkPluginsSchema, + RehypePluginsSchema, + AdmonitionsSchema, + URISchema, + PathnameSchema, + FrontMatterTagsSchema, + FrontMatterTOCHeadingLevels, +} from './validationSchemas'; diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 4aed2648b2..730f908030 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -22,17 +22,68 @@ import resolvePathnameUnsafe from 'resolve-pathname'; import {simpleHash, docuHash} from './hashUtils'; import {DEFAULT_PLUGIN_ID} from './constants'; -export * from './constants'; -export * from './urlUtils'; -export * from './tags'; -export * from './markdownParser'; -export * from './markdownLinks'; -export * from './slugger'; -export * from './pathUtils'; -export * from './hashUtils'; -export * from './globUtils'; -export * from './webpackUtils'; -export * from './dataFileUtils'; +export { + NODE_MAJOR_VERSION, + NODE_MINOR_VERSION, + DEFAULT_BUILD_DIR_NAME, + DEFAULT_CONFIG_FILE_NAME, + BABEL_CONFIG_FILE_NAME, + GENERATED_FILES_DIR_NAME, + SRC_DIR_NAME, + STATIC_DIR_NAME, + OUTPUT_STATIC_ASSETS_DIR_NAME, + THEME_PATH, + DEFAULT_PORT, + DEFAULT_PLUGIN_ID, + WEBPACK_URL_LOADER_LIMIT, +} from './constants'; +export {normalizeUrl, getEditUrl} from './urlUtils'; +export { + type Tag, + type FrontMatterTag, + type TaggedItemGroup, + normalizeFrontMatterTag, + normalizeFrontMatterTags, + groupTaggedItems, +} from './tags'; +export { + parseMarkdownHeadingId, + createExcerpt, + parseFrontMatter, + parseMarkdownContentTitle, + parseMarkdownString, +} from './markdownParser'; +export { + type ContentPaths, + type BrokenMarkdownLink, + type ReplaceMarkdownLinksParams, + type ReplaceMarkdownLinksReturn, + replaceMarkdownLinks, +} from './markdownLinks'; +export {type SluggerOptions, type Slugger, createSlugger} from './slugger'; +export { + isNameTooLong, + shortName, + posixPath, + toMessageRelativeFilePath, + aliasedSitePath, + escapePath, +} from './pathUtils'; +export {md5Hash, simpleHash, docuHash} from './hashUtils'; +export { + Globby, + GlobExcludeDefault, + createMatcher, + createAbsoluteFilePathMatcher, +} from './globUtils'; +export {getFileLoaderUtils} from './webpackUtils'; +export { + getDataFilePath, + getDataFileData, + getContentPathList, + findFolderContainingFile, + getFolderContainingFile, +} from './dataFileUtils'; const fileHash = new Map(); export async function generate( @@ -251,7 +302,6 @@ export async function mapAsyncSequential( action: (t: T) => Promise, ): Promise { const results: R[] = []; - // eslint-disable-next-line no-restricted-syntax for (const t of array) { const result = await action(t); results.push(result); @@ -263,7 +313,6 @@ export async function findAsyncSequential( array: T[], predicate: (t: T) => Promise, ): Promise { - // eslint-disable-next-line no-restricted-syntax for (const t of array) { if (await predicate(t)) { return t; diff --git a/packages/docusaurus-utils/src/markdownParser.ts b/packages/docusaurus-utils/src/markdownParser.ts index 44dc31f9ee..2217542d8a 100644 --- a/packages/docusaurus-utils/src/markdownParser.ts +++ b/packages/docusaurus-utils/src/markdownParser.ts @@ -38,7 +38,6 @@ export function createExcerpt(fileString: string): string | undefined { let lastCodeFence = ''; /* eslint-disable no-continue */ - // eslint-disable-next-line no-restricted-syntax for (const fileLine of fileLines) { // Skip empty line. if (!fileLine.trim()) { diff --git a/packages/docusaurus/src/client/exports/router.ts b/packages/docusaurus/src/client/exports/router.ts index 18cfa08f7d..d1ba52fb3b 100644 --- a/packages/docusaurus/src/client/exports/router.ts +++ b/packages/docusaurus/src/client/exports/router.ts @@ -5,4 +5,5 @@ * LICENSE file in the root directory of this source tree. */ +// eslint-disable-next-line no-restricted-syntax export * from 'react-router-dom'; diff --git a/packages/docusaurus/src/webpack/__tests__/base.test.ts b/packages/docusaurus/src/webpack/__tests__/base.test.ts index 95d34dc70c..ca2ce707c6 100644 --- a/packages/docusaurus/src/webpack/__tests__/base.test.ts +++ b/packages/docusaurus/src/webpack/__tests__/base.test.ts @@ -13,7 +13,6 @@ import { getDocusaurusAliases, createBaseConfig, } from '../base'; -// TODO seems to be a bug with how TS does star exports import * as utils from '@docusaurus/utils/lib/webpackUtils'; import {posixPath} from '@docusaurus/utils'; import {mapValues} from 'lodash'; diff --git a/website/src/data/tweets/index.tsx b/website/src/data/tweets/index.tsx index 5a36dc881c..3c6d444a52 100644 --- a/website/src/data/tweets/index.tsx +++ b/website/src/data/tweets/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; -import {Props as Tweet} from '../../components/Tweet'; +import type {Props as Tweet} from '../../components/Tweet'; export interface TweetItem extends Tweet { showOnHomepage: boolean; diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx index e9aa3cd9c1..3426bc5ff3 100644 --- a/website/src/pages/index.tsx +++ b/website/src/pages/index.tsx @@ -17,7 +17,7 @@ import Image from '@theme/IdealImage'; import Layout from '@theme/Layout'; import Tweet from '@site/src/components/Tweet'; -import Tweets, {TweetItem} from '@site/src/data/tweets'; +import Tweets, {type TweetItem} from '@site/src/data/tweets'; import clsx from 'clsx'; diff --git a/website/src/plugins/changelog/syncAvatars.js b/website/src/plugins/changelog/syncAvatars.js index dc94ac1c91..48fbb33c41 100644 --- a/website/src/plugins/changelog/syncAvatars.js +++ b/website/src/plugins/changelog/syncAvatars.js @@ -55,7 +55,6 @@ async function syncAvatars(authorsMap, generateDir) { */ const lastUpdateCache = await fs.readJSON(lastUpdateCachePath); let limitReached = false; - // eslint-disable-next-line no-restricted-syntax for (const username of Object.keys(authorsMap)) { if (!limitReached && !lastUpdateCache[username]) { if (!(await fetchImage(username, lastUpdateCache, authorsMap))) { @@ -69,7 +68,6 @@ async function syncAvatars(authorsMap, generateDir) { const usersByLastUpdate = Object.entries(lastUpdateCache) .sort((a, b) => a[1] - b[1]) .map((a) => a[0]); - // eslint-disable-next-line no-restricted-syntax for (const username of usersByLastUpdate) { if ( !limitReached &&