chore: fix ESLint warnings, restrict export all syntax (#6605)

* chore: fix ESLint warnings, forbid export all syntax

* fix...

* reorder
This commit is contained in:
Joshua Chen 2022-02-04 21:57:10 +08:00 committed by GitHub
parent 3fd99ad8d4
commit 45f6f8b869
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 220 additions and 148 deletions

View file

@ -69,8 +69,6 @@ module.exports = {
'no-param-reassign': [WARNING, {props: false}], 'no-param-reassign': [WARNING, {props: false}],
'no-prototype-builtins': WARNING, 'no-prototype-builtins': WARNING,
'no-restricted-exports': OFF, 'no-restricted-exports': OFF,
'no-useless-escape': WARNING,
'no-template-curly-in-string': WARNING,
'no-restricted-imports': [ 'no-restricted-imports': [
ERROR, 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-unused-expressions': [WARNING, {allowTaggedTemplates: true}],
'no-useless-escape': WARNING,
'prefer-destructuring': WARNING, 'prefer-destructuring': WARNING,
'prefer-named-capture-group': WARNING, 'prefer-named-capture-group': WARNING,
'prefer-template': WARNING, 'prefer-template': WARNING,

View file

@ -182,7 +182,6 @@ const templates = (
await fs.readdir('./packages/create-docusaurus/templates') await fs.readdir('./packages/create-docusaurus/templates')
).filter((name) => !excludes.includes(name)); ).filter((name) => !excludes.includes(name));
console.log(`Will generate examples for templates: ${templates.join(',')}`); console.log(`Will generate examples for templates: ${templates.join(',')}`);
// eslint-disable-next-line no-restricted-syntax
for (const template of templates) { for (const template of templates) {
await generateTemplateExample(template); await generateTemplateExample(template);
} }

View file

@ -240,11 +240,11 @@ declare module '@docusaurus/Translate' {
} }
declare module '@docusaurus/router' { 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'; export * from 'react-router-dom';
} }
declare module '@docusaurus/history' { 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'; export * from 'history';
} }

View file

@ -27,7 +27,8 @@ export interface BlogContent {
export interface BlogTags { export interface BlogTags {
// TODO, the key is the tag slug/permalink // 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 // Soon we should forbid declaring permalink through frontmatter
[tagKey: string]: BlogTag; [tagKey: string]: BlogTag;
} }

View file

@ -5,7 +5,7 @@
"main": "lib/index.js", "main": "lib/index.js",
"exports": { "exports": {
"./client": "./lib/client/index.js", "./client": "./lib/client/index.js",
"./server": "./lib/server/index.js", "./server": "./lib/server-export.js",
".": "./lib/index.js" ".": "./lib/index.js"
}, },
"types": "src/plugin-content-docs.d.ts", "types": "src/plugin-content-docs.d.ts",

View file

@ -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<string, GlobalPluginData> =>
// 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);
};

View file

@ -5,4 +5,100 @@
* LICENSE file in the root directory of this source tree. * 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<string, GlobalPluginData> =>
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);
};

View file

@ -6,7 +6,12 @@
*/ */
// APIs available to Node.js // APIs available to Node.js
export * from '../constants'; export {
CURRENT_VERSION_NAME,
VERSIONED_DOCS_DIR,
VERSIONED_SIDEBARS_DIR,
VERSIONS_JSON_FILE,
} from './constants';
export { export {
filterVersions, filterVersions,
@ -16,4 +21,4 @@ export {
getVersionsFilePath, getVersionsFilePath,
readVersionsFile, readVersionsFile,
readVersionNames, readVersionNames,
} from '../versions'; } from './versions';

View file

@ -303,7 +303,6 @@ Available document ids are:
label: string; label: string;
} }
| undefined { | undefined {
// eslint-disable-next-line no-restricted-syntax
for (const item of sidebar) { for (const item of sidebar) {
if (item.type === 'doc') { if (item.type === 'doc') {
return { return {

View file

@ -9,4 +9,5 @@
// If you swizzled this, it is your responsibility to provide an implementation // If you swizzled this, it is your responsibility to provide an implementation
// Tip: swizzle the SearchBar from the Algolia theme for inspiration: // Tip: swizzle the SearchBar from the Algolia theme for inspiration:
// npm run swizzle @docusaurus/theme-search-algolia SearchBar // npm run swizzle @docusaurus/theme-search-algolia SearchBar
export {default} from '@docusaurus/Noop'; export {default} from '@docusaurus/Noop';

View file

@ -96,7 +96,6 @@ export function findSidebarCategory(
sidebar: PropSidebar, sidebar: PropSidebar,
predicate: (category: PropSidebarItemCategory) => boolean, predicate: (category: PropSidebarItemCategory) => boolean,
): PropSidebarItemCategory | undefined { ): PropSidebarItemCategory | undefined {
// eslint-disable-next-line no-restricted-syntax
for (const item of sidebar) { for (const item of sidebar) {
if (item.type === 'category') { if (item.type === 'category') {
if (predicate(item)) { if (predicate(item)) {
@ -119,7 +118,6 @@ export function findFirstCategoryLink(
return item.href; return item.href;
} }
// eslint-disable-next-line no-restricted-syntax
for (const subItem of item.items) { for (const subItem of item.items) {
if (subItem.type === 'link') { if (subItem.type === 'link') {
return subItem.href; return subItem.href;

View file

@ -7,7 +7,7 @@
declare module '@philpl/buble' { declare module '@philpl/buble' {
import type {TransformOptions as OriginalTransformOptions} from '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 * from 'buble';
export const features: string[]; export const features: string[];
export interface TransformOptions extends OriginalTransformOptions { export interface TransformOptions extends OriginalTransformOptions {

View file

@ -44,7 +44,6 @@ export async function readDefaultCodeTranslationMessages({
// Return the content of the first file that match // Return the content of the first file that match
// fr_FR.json => fr.json => nothing // fr_FR.json => fr.json => nothing
// eslint-disable-next-line no-restricted-syntax
for (const localeToTry of localesToTry) { for (const localeToTry of localesToTry) {
const filePath = path.resolve(dirPath, localeToTry, `${name}.json`); const filePath = path.resolve(dirPath, localeToTry, `${name}.json`);

View file

@ -268,8 +268,6 @@ async function updateCodeTranslations() {
const stats = {}; const stats = {};
let messageCount = 0; let messageCount = 0;
const {2: newLocale} = process.argv; 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) { for (const theme of Themes) {
const {baseFile, localesFiles} = await getCodeTranslationFiles(theme.name); const {baseFile, localesFiles} = await getCodeTranslationFiles(theme.name);
logger.info`Will update base file for name=${theme.name}\n`; logger.info`Will update base file for name=${theme.name}\n`;
@ -289,7 +287,6 @@ async function updateCodeTranslations() {
)} was already created!`; )} was already created!`;
} }
} else { } else {
// eslint-disable-next-line no-restricted-syntax
for (const localeFile of localesFiles) { for (const localeFile of localesFiles) {
const localeName = path.basename(path.dirname(localeFile)); const localeName = path.basename(path.dirname(localeFile));
const pluginName = path.basename(localeFile, path.extname(localeFile)); const pluginName = path.basename(localeFile, path.extname(localeFile));

View file

@ -9,5 +9,21 @@
export {default as Joi} from './Joi'; export {default as Joi} from './Joi';
export {JoiFrontMatter} from './JoiFrontMatter'; export {JoiFrontMatter} from './JoiFrontMatter';
export * from './validationUtils'; export {
export * from './validationSchemas'; isValidationDisabledEscapeHatch,
logValidationBugReportHint,
printWarning,
normalizePluginOptions,
normalizeThemeConfig,
validateFrontMatter,
} from './validationUtils';
export {
PluginIdSchema,
RemarkPluginsSchema,
RehypePluginsSchema,
AdmonitionsSchema,
URISchema,
PathnameSchema,
FrontMatterTagsSchema,
FrontMatterTOCHeadingLevels,
} from './validationSchemas';

View file

@ -22,17 +22,68 @@ import resolvePathnameUnsafe from 'resolve-pathname';
import {simpleHash, docuHash} from './hashUtils'; import {simpleHash, docuHash} from './hashUtils';
import {DEFAULT_PLUGIN_ID} from './constants'; import {DEFAULT_PLUGIN_ID} from './constants';
export * from './constants'; export {
export * from './urlUtils'; NODE_MAJOR_VERSION,
export * from './tags'; NODE_MINOR_VERSION,
export * from './markdownParser'; DEFAULT_BUILD_DIR_NAME,
export * from './markdownLinks'; DEFAULT_CONFIG_FILE_NAME,
export * from './slugger'; BABEL_CONFIG_FILE_NAME,
export * from './pathUtils'; GENERATED_FILES_DIR_NAME,
export * from './hashUtils'; SRC_DIR_NAME,
export * from './globUtils'; STATIC_DIR_NAME,
export * from './webpackUtils'; OUTPUT_STATIC_ASSETS_DIR_NAME,
export * from './dataFileUtils'; 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<string, string>(); const fileHash = new Map<string, string>();
export async function generate( export async function generate(
@ -251,7 +302,6 @@ export async function mapAsyncSequential<T, R>(
action: (t: T) => Promise<R>, action: (t: T) => Promise<R>,
): Promise<R[]> { ): Promise<R[]> {
const results: R[] = []; const results: R[] = [];
// eslint-disable-next-line no-restricted-syntax
for (const t of array) { for (const t of array) {
const result = await action(t); const result = await action(t);
results.push(result); results.push(result);
@ -263,7 +313,6 @@ export async function findAsyncSequential<T>(
array: T[], array: T[],
predicate: (t: T) => Promise<boolean>, predicate: (t: T) => Promise<boolean>,
): Promise<T | undefined> { ): Promise<T | undefined> {
// eslint-disable-next-line no-restricted-syntax
for (const t of array) { for (const t of array) {
if (await predicate(t)) { if (await predicate(t)) {
return t; return t;

View file

@ -38,7 +38,6 @@ export function createExcerpt(fileString: string): string | undefined {
let lastCodeFence = ''; let lastCodeFence = '';
/* eslint-disable no-continue */ /* eslint-disable no-continue */
// eslint-disable-next-line no-restricted-syntax
for (const fileLine of fileLines) { for (const fileLine of fileLines) {
// Skip empty line. // Skip empty line.
if (!fileLine.trim()) { if (!fileLine.trim()) {

View file

@ -5,4 +5,5 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// eslint-disable-next-line no-restricted-syntax
export * from 'react-router-dom'; export * from 'react-router-dom';

View file

@ -13,7 +13,6 @@ import {
getDocusaurusAliases, getDocusaurusAliases,
createBaseConfig, createBaseConfig,
} from '../base'; } from '../base';
// TODO seems to be a bug with how TS does star exports
import * as utils from '@docusaurus/utils/lib/webpackUtils'; import * as utils from '@docusaurus/utils/lib/webpackUtils';
import {posixPath} from '@docusaurus/utils'; import {posixPath} from '@docusaurus/utils';
import {mapValues} from 'lodash'; import {mapValues} from 'lodash';

View file

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import {Props as Tweet} from '../../components/Tweet'; import type {Props as Tweet} from '../../components/Tweet';
export interface TweetItem extends Tweet { export interface TweetItem extends Tweet {
showOnHomepage: boolean; showOnHomepage: boolean;

View file

@ -17,7 +17,7 @@ import Image from '@theme/IdealImage';
import Layout from '@theme/Layout'; import Layout from '@theme/Layout';
import Tweet from '@site/src/components/Tweet'; 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'; import clsx from 'clsx';

View file

@ -55,7 +55,6 @@ async function syncAvatars(authorsMap, generateDir) {
*/ */
const lastUpdateCache = await fs.readJSON(lastUpdateCachePath); const lastUpdateCache = await fs.readJSON(lastUpdateCachePath);
let limitReached = false; let limitReached = false;
// eslint-disable-next-line no-restricted-syntax
for (const username of Object.keys(authorsMap)) { for (const username of Object.keys(authorsMap)) {
if (!limitReached && !lastUpdateCache[username]) { if (!limitReached && !lastUpdateCache[username]) {
if (!(await fetchImage(username, lastUpdateCache, authorsMap))) { if (!(await fetchImage(username, lastUpdateCache, authorsMap))) {
@ -69,7 +68,6 @@ async function syncAvatars(authorsMap, generateDir) {
const usersByLastUpdate = Object.entries(lastUpdateCache) const usersByLastUpdate = Object.entries(lastUpdateCache)
.sort((a, b) => a[1] - b[1]) .sort((a, b) => a[1] - b[1])
.map((a) => a[0]); .map((a) => a[0]);
// eslint-disable-next-line no-restricted-syntax
for (const username of usersByLastUpdate) { for (const username of usersByLastUpdate) {
if ( if (
!limitReached && !limitReached &&