chore: clean up ESLint config, enable a few rules (#6514)

* chore: clean up ESLint config, enable a few rules

* enable max-len for comments

* fix build
This commit is contained in:
Joshua Chen 2022-01-31 10:31:24 +08:00 committed by GitHub
parent b8ccb869f1
commit aa446b7a9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
167 changed files with 1157 additions and 960 deletions

View file

@ -67,7 +67,8 @@ function applyCollapsedStyle(el: HTMLElement, collapsed: boolean) {
}
/*
Lex111: Dynamic transition duration is used in Material design, this technique is good for a large number of items.
Lex111: Dynamic transition duration is used in Material design, this technique
is good for a large number of items.
https://material.io/archive/guidelines/motion/duration-easing.html#duration-easing-dynamic-durations
https://github.com/mui-org/material-ui/blob/e724d98eba018e55e1a684236a2037e24bcf050c/packages/material-ui/src/styles/createTransitions.js#L40-L43
*/
@ -151,7 +152,8 @@ type CollapsibleElementType = React.ElementType<
Pick<React.HTMLAttributes<unknown>, 'className' | 'onTransitionEnd' | 'style'>
>;
// Prevent hydration layout shift before animations are handled imperatively with JS
// Prevent hydration layout shift before animations are handled imperatively
// with JS
function getSSRStyle(collapsed: boolean) {
if (ExecutionEnvironment.canUseDOM) {
return undefined;
@ -167,8 +169,9 @@ type CollapsibleBaseProps = {
onCollapseTransitionEnd?: (collapsed: boolean) => void;
className?: string;
// This is mostly useful for details/summary component where ssrStyle is not needed (as details are hidden natively)
// and can mess-up with the default native behavior of the browser when JS fails to load or is disabled
// This is mostly useful for details/summary component where ssrStyle is not
// needed (as details are hidden natively) and can mess up with the default
// native behavior of the browser when JS fails to load or is disabled
disableSSRStyle?: boolean;
};
@ -189,7 +192,8 @@ function CollapsibleBase({
return (
<As
// @ts-expect-error: the "too complicated type" is produced from "CollapsibleElementType" being a huge union
// @ts-expect-error: the "too complicated type" is produced from
// "CollapsibleElementType" being a huge union
ref={collapsibleRef}
style={disableSSRStyle ? undefined : getSSRStyle(collapsed)}
onTransitionEnd={(e: React.TransitionEvent) => {
@ -215,7 +219,7 @@ function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
}
}, [collapsed]);
// lazyCollapsed updated in effect so that the first expansion transition can work
// lazyCollapsed updated in effect so that first expansion transition can work
const [lazyCollapsed, setLazyCollapsed] = useState(collapsed);
useLayoutEffect(() => {
if (mounted) {
@ -229,9 +233,10 @@ function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
}
type CollapsibleProps = CollapsibleBaseProps & {
// Lazy allows to delay the rendering when collapsed => it will render children only after hydration, on first expansion
// Required prop: it forces to think if content should be server-rendered or not!
// This has perf impact on the SSR output and html file sizes
// Lazy allows to delay the rendering when collapsed => it will render
// children only after hydration, on first expansion
// Required prop: it forces to think if content should be server-rendered
// or not! This has perf impact on the SSR output and html file sizes
// See https://github.com/facebook/docusaurus/issues/4753
lazy: boolean;
};

View file

@ -41,7 +41,7 @@ function Details({summary, children, ...props}: DetailsProps): JSX.Element {
const {collapsed, setCollapsed} = useCollapsible({
initialState: !props.open,
});
// We use a separate prop because it must be set only after animation completes
// Use a separate prop because it must be set only after animation completes
// Otherwise close anim won't work
const [open, setOpen] = useState(props.open);

View file

@ -15,7 +15,8 @@ const windowSizes = {
// This "ssr" value is very important to handle hydration FOUC / layout shifts
// You have to handle server-rendering explicitly on the call-site
// On the server, you may need to render BOTH the mobile/desktop elements (and hide one of them with mediaquery)
// On the server, you may need to render BOTH the mobile/desktop elements (and
// hide one of them with mediaquery)
// We don't return "undefined" on purpose, to make it more explicit
ssr: 'ssr',
} as const;
@ -33,7 +34,8 @@ function getWindowSize() {
: windowSizes.mobile;
}
// Simulate the SSR window size in dev, so that potential hydration FOUC/layout shift problems can be seen in dev too!
// Simulate the SSR window size in dev, so that potential hydration FOUC/layout
// shift problems can be seen in dev too!
const DevSimulateSSR = process.env.NODE_ENV === 'development' && true;
// This hook returns an enum value on purpose!

View file

@ -7,7 +7,8 @@
// These class names are used to style page layouts in Docusaurus
// Those are meant to be targeted by user-provided custom CSS selectors
// /!\ Please do not modify the classnames! This is a breaking change, and annoying for users!
// Please do not modify the classnames! This is a breaking change, and annoying
// for users!
export const ThemeClassNames = {
page: {
blogListPage: 'blog-list-page',

View file

@ -112,8 +112,9 @@ describe('filterTOC', () => {
]);
});
// It's not 100% clear exactly how the TOC should behave under weird heading levels provided by the user
// Adding a test so that behavior stays the same over time
// It's not 100% clear exactly how the TOC should behave under weird heading
// levels provided by the user. Adding a test so that behavior stays the same
// over time
test('filter invalid heading levels (but possible) TOC', () => {
const toc: TOCItem[] = [
{

View file

@ -76,10 +76,9 @@ function readStorageState({
);
if (versionExists) {
return {preferredVersionName: preferredVersionNameUnsafe};
} else {
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
return {preferredVersionName: null};
}
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
return {preferredVersionName: null};
}
const initialState: DocsPreferredVersionState = {};
@ -144,9 +143,8 @@ export function DocsPreferredVersionContextProvider({
{children}
</DocsPreferredVersionContextProviderUnsafe>
);
} else {
return children;
}
return children;
}
function DocsPreferredVersionContextProviderUnsafe({

View file

@ -20,7 +20,7 @@ import {useLocation} from '@docusaurus/router';
// TODO not ideal, see also "useDocs"
export const isDocsPluginEnabled: boolean = !!useAllDocsData;
// Using a Symbol because null is a valid context value (a doc can have no sidebar)
// Using a Symbol because null is a valid context value (a doc with no sidebar)
// Inspired by https://github.com/jamiebuilds/unstated-next/blob/master/src/unstated-next.tsx
const EmptyContextValue: unique symbol = Symbol('EmptyContext');
@ -101,11 +101,10 @@ export function findSidebarCategory(
if (item.type === 'category') {
if (predicate(item)) {
return item;
} else {
const subItem = findSidebarCategory(item.items, predicate);
if (subItem) {
return subItem;
}
}
const subItem = findSidebarCategory(item.items, predicate);
if (subItem) {
return subItem;
}
}
}

View file

@ -11,9 +11,10 @@ import type {Location, Action} from '@docusaurus/history';
type HistoryBlockHandler = (location: Location, action: Action) => void | false;
/*
Permits to register a handler that will be called on history actions (pop,push,replace)
If the handler returns false, the navigation transition will be blocked/cancelled
/**
* Permits to register a handler that will be called on history actions (pop,
* push, replace) If the handler returns false, the navigation transition will
* be blocked/cancelled
*/
export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
const {block} = useHistory();
@ -32,11 +33,11 @@ export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
);
}
/*
Permits to register a handler that will be called on history pop navigation (backward/forward)
If the handler returns false, the backward/forward transition will be blocked
Unfortunately there's no good way to detect the "direction" (backward/forward) of the POP event.
/**
* Permits to register a handler that will be called on history pop navigation
* (backward/forward) If the handler returns false, the backward/forward
* transition will be blocked. Unfortunately there's no good way to detect the
* "direction" (backward/forward) of the POP event.
*/
export function useHistoryPopHandler(handler: HistoryBlockHandler): void {
useHistoryActionHandler((location, action) => {

View file

@ -10,8 +10,11 @@
/**
* Gets the duplicate values in an array.
* @param arr The array.
* @param comparator Compares two values and returns `true` if they are equal (duplicated).
* @returns Value of the elements `v` that have a preceding element `u` where `comparator(u, v) === true`. Values within the returned array are not guaranteed to be unique.
* @param comparator Compares two values and returns `true` if they are equal
* (duplicated).
* @returns Value of the elements `v` that have a preceding element `u` where
* `comparator(u, v) === true`. Values within the returned array are not
* guaranteed to be unique.
*/
export function duplicates<T>(
arr: readonly T[],

View file

@ -16,12 +16,13 @@ import React, {
} from 'react';
/*
The idea behind all this is that a specific component must be able to fill a placeholder in the generic layout
The doc page should be able to fill the secondary menu of the main mobile navbar.
This permits to reduce coupling between the main layout and the specific page.
The idea behind all this is that a specific component must be able to fill a
placeholder in the generic layout. The doc page should be able to fill the
secondary menu of the main mobile navbar. This permits to reduce coupling
between the main layout and the specific page.
This kind of feature is often called portal/teleport/gateway... various unmaintained React libs exist
Most up-to-date one: https://github.com/gregberge/react-teleporter
This kind of feature is often called portal/teleport/gateway... various
unmaintained React libs exist. Most up-to-date one: https://github.com/gregberge/react-teleporter
Not sure any of those is safe regarding concurrent mode.
*/

View file

@ -7,19 +7,27 @@
import {useCallback, useEffect, useLayoutEffect, useRef} from 'react';
// This hook is like useLayoutEffect, but without the SSR warning
// It seems hacky but it's used in many React libs (Redux, Formik...)
// Also mentioned here: https://github.com/facebook/react/issues/16956
// It is useful when you need to update a ref as soon as possible after a React render (before useEffect)
/**
* This hook is like useLayoutEffect, but without the SSR warning
* It seems hacky but it's used in many React libs (Redux, Formik...)
* Also mentioned here: https://github.com/facebook/react/issues/16956
* It is useful when you need to update a ref as soon as possible after a React
* render (before `useEffect`)
*/
export const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
// Permits to transform an unstable callback (like an arrow function provided as props)
// to a "stable" callback that is safe to use in a useEffect dependency array
// Useful to avoid React stale closure problems + avoid useless effect re-executions
//
// Workaround until the React team recommends a good solution, see https://github.com/facebook/react/issues/16956
// This generally works has some potential drawbacks, such as https://github.com/facebook/react/issues/16956#issuecomment-536636418
/**
* Permits to transform an unstable callback (like an arrow function provided as
* props) to a "stable" callback that is safe to use in a useEffect dependency
* array. Useful to avoid React stale closure problems + avoid useless effect
* re-executions
*
* Workaround until the React team recommends a good solution, see
* https://github.com/facebook/react/issues/16956
* This generally works but has some potential drawbacks, such as
* https://github.com/facebook/react/issues/16956#issuecomment-536636418
*/
export function useDynamicCallback<T extends (...args: never[]) => unknown>(
callback: T,
): T {
@ -29,6 +37,7 @@ export function useDynamicCallback<T extends (...args: never[]) => unknown>(
ref.current = callback;
}, [callback]);
// @ts-expect-error: TS is right that this callback may be a supertype of T, but good enough for our use
// @ts-expect-error: TS is right that this callback may be a supertype of T,
// but good enough for our use
return useCallback<T>((...args) => ref.current(...args), []);
}

View file

@ -6,7 +6,7 @@
*/
/**
* Utility to convert an optional string into a Regex case insensitive and global
* Converts an optional string into a Regex case insensitive and global
*/
export function isRegexpStringMatch(
regexAsString?: string,

View file

@ -11,8 +11,8 @@ export type StorageType = typeof StorageTypes[number];
const DefaultStorageType: StorageType = 'localStorage';
// Will return null browser storage is unavailable (like running Docusaurus in iframe)
// See https://github.com/facebook/docusaurus/pull/4501
// Will return null browser storage is unavailable (like running Docusaurus in
// iframe) See https://github.com/facebook/docusaurus/pull/4501
function getBrowserStorage(
storageType: StorageType = DefaultStorageType,
): Storage | null {
@ -23,13 +23,12 @@ function getBrowserStorage(
}
if (storageType === 'none') {
return null;
} else {
try {
return window[storageType];
} catch (e) {
logOnceBrowserStorageNotAvailableWarning(e as Error);
return null;
}
}
try {
return window[storageType];
} catch (e) {
logOnceBrowserStorageNotAvailableWarning(e as Error);
return null;
}
}
@ -79,10 +78,10 @@ Please only call storage APIs in effects and event handlers.`);
/**
* Creates an object for accessing a particular key in localStorage.
* The API is fail-safe, and usage of browser storage should be considered unreliable
* Local storage might simply be unavailable (iframe + browser security) or operations might fail individually
* Please assume that using this API can be a NO-OP
* See also https://github.com/facebook/docusaurus/issues/6036
* The API is fail-safe, and usage of browser storage should be considered
* unreliable. Local storage might simply be unavailable (iframe + browser
* security) or operations might fail individually. Please assume that using
* this API can be a NO-OP. See also https://github.com/facebook/docusaurus/issues/6036
*/
export const createStorageSlot = (
key: string,

View file

@ -36,9 +36,8 @@ export function filterTOC({
children: filteredChildren,
},
];
} else {
return filteredChildren;
}
return filteredChildren;
});
}

View file

@ -39,14 +39,15 @@ export function useAlternatePageUtils(): {
: `${baseUrlUnlocalized}${locale}/`;
}
// TODO support correct alternate url when localized site is deployed on another domain
// TODO support correct alternate url when localized site is deployed on
// another domain
function createUrl({
locale,
fullyQualified,
}: {
locale: string;
// For hreflang SEO headers, we need it to be fully qualified (full protocol/domain/path...)
// For locale dropdown, using a path is good enough
// For hreflang SEO headers, we need it to be fully qualified (full
// protocol/domain/path...) or locale dropdown, using a path is good enough
fullyQualified: boolean;
}) {
return `${fullyQualified ? url : ''}${getLocalizedBaseUrl(

View file

@ -18,8 +18,8 @@ export type useContextualSearchFiltersReturns = {
tags: string[];
};
// We may want to support multiple search engines, don't couple that to Algolia/DocSearch
// Maybe users will want to use its own search engine solution
// We may want to support multiple search engines, don't couple that to
// Algolia/DocSearch. Maybe users want to use their own search engine solution
export function useContextualSearchFilters(): useContextualSearchFiltersReturns {
const {i18n} = useDocusaurusContext();
const allDocsData = useAllDocsData();

View file

@ -49,23 +49,29 @@ function createLocalePluralForms(locale: string): LocalePluralForms {
};
}
// Poor man's PluralSelector implementation, using an english fallback.
// We want a lightweight, future-proof and good-enough solution.
// We don't want a perfect and heavy solution.
//
// Docusaurus classic theme has only 2 deeply nested labels requiring complex plural rules
// We don't want to use Intl + PluralRules polyfills + full ICU syntax (react-intl) just for that.
//
// Notes:
// - 2021: 92+% Browsers support Intl.PluralRules, and support will increase in the future
// - NodeJS >= 13 has full ICU support by default
// - In case of "mismatch" between SSR and Browser ICU support, React keeps working!
/**
* Poor man's PluralSelector implementation, using an english fallback. We want
* a lightweight, future-proof and good-enough solution. We don't want a perfect
* and heavy solution.
*
* Docusaurus classic theme has only 2 deeply nested labels requiring complex
* plural rules. We don't want to use Intl + PluralRules polyfills + full ICU
* syntax (react-intl) just for that.
*
* Notes:
* - 2021: 92+% Browsers support Intl.PluralRules, and support will increase in
* the future
* - NodeJS >= 13 has full ICU support by default
* - In case of "mismatch" between SSR and Browser ICU support, React keeps
* working!
*/
function useLocalePluralForms(): LocalePluralForms {
const {
i18n: {currentLocale},
} = useDocusaurusContext();
return useMemo(() => {
// @ts-expect-error checking Intl.PluralRules in case browser doesn't have it (e.g Safari 12-)
// @ts-expect-error checking Intl.PluralRules in case browser doesn't
// have it (e.g Safari 12-)
if (Intl.PluralRules) {
try {
return createLocalePluralForms(currentLocale);
@ -94,17 +100,17 @@ function selectPluralMessage(
if (parts.length === 1) {
return parts[0];
} else {
if (parts.length > localePluralForms.pluralForms.length) {
console.error(
`For locale=${localePluralForms.locale}, a maximum of ${localePluralForms.pluralForms.length} plural forms are expected (${localePluralForms.pluralForms}), but the message contains ${parts.length} plural forms: ${pluralMessages} `,
);
}
const pluralForm = localePluralForms.select(count);
const pluralFormIndex = localePluralForms.pluralForms.indexOf(pluralForm);
// In case of not enough plural form messages, we take the last one (other) instead of returning undefined
return parts[Math.min(pluralFormIndex, parts.length - 1)];
}
if (parts.length > localePluralForms.pluralForms.length) {
console.error(
`For locale=${localePluralForms.locale}, a maximum of ${localePluralForms.pluralForms.length} plural forms are expected (${localePluralForms.pluralForms}), but the message contains ${parts.length} plural forms: ${pluralMessages} `,
);
}
const pluralForm = localePluralForms.select(count);
const pluralFormIndex = localePluralForms.pluralForms.indexOf(pluralForm);
// In case of not enough plural form messages, we take the last one (other)
// instead of returning undefined
return parts[Math.min(pluralFormIndex, parts.length - 1)];
}
export function usePluralForm(): {

View file

@ -13,7 +13,8 @@ TODO make the hardcoded theme-classic classnames configurable
(or add them to ThemeClassNames?)
*/
// If the anchor has no height and is just a "marker" in the dom; we'll use the parent (normally the link text) rect boundaries instead
// If the anchor has no height and is just a "marker" in the dom; we'll use the
// parent (normally the link text) rect boundaries instead
function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
const rect = element.getBoundingClientRect();
const hasNoHeight = rect.top === rect.bottom;
@ -23,8 +24,10 @@ function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
return rect;
}
// Considering we divide viewport into 2 zones of each 50vh
// This returns true if an element is in the first zone (ie, appear in viewport, near the top)
/**
* Considering we divide viewport into 2 zones of each 50vh, this returns true
* if an element is in the first zone (ie, appear in viewport, near the top)
*/
function isInViewportTopHalf(boundingRect: DOMRect) {
return boundingRect.top > 0 && boundingRect.bottom < window.innerHeight / 2;
}
@ -54,9 +57,10 @@ function getActiveAnchor(
anchorTopOffset: number;
},
): Element | null {
// Naming is hard
// The "nextVisibleAnchor" is the first anchor that appear under the viewport top boundary
// Note: it does not mean this anchor is visible yet, but if user continues scrolling down, it will be the first to become visible
// Naming is hard: The "nextVisibleAnchor" is the first anchor that appear
// under the viewport top boundary. It does not mean this anchor is visible
// yet, but if user continues scrolling down, it will be the first to become
// visible
const nextVisibleAnchor = anchors.find((anchor) => {
const boundingRect = getVisibleBoundingClientRect(anchor);
return boundingRect.top >= anchorTopOffset;
@ -64,23 +68,22 @@ function getActiveAnchor(
if (nextVisibleAnchor) {
const boundingRect = getVisibleBoundingClientRect(nextVisibleAnchor);
// If anchor is in the top half of the viewport: it is the one we consider "active"
// (unless it's too close to the top and and soon to be scrolled outside viewport)
// If anchor is in the top half of the viewport: it is the one we consider
// "active" (unless it's too close to the top and and soon to be scrolled
// outside viewport)
if (isInViewportTopHalf(boundingRect)) {
return nextVisibleAnchor;
}
// If anchor is in the bottom half of the viewport, or under the viewport, we consider the active anchor is the previous one
// This is because the main text appearing in the user screen mostly belong to the previous anchor
else {
// Returns null for the first anchor, see https://github.com/facebook/docusaurus/issues/5318
return anchors[anchors.indexOf(nextVisibleAnchor) - 1] ?? null;
}
// If anchor is in the bottom half of the viewport, or under the viewport,
// we consider the active anchor is the previous one. This is because the
// main text appearing in the user screen mostly belong to the previous
// anchor. Returns null for the first anchor, see
// https://github.com/facebook/docusaurus/issues/5318
return anchors[anchors.indexOf(nextVisibleAnchor) - 1] ?? null;
}
// no anchor under viewport top? (ie we are at the bottom of the page)
// => highlight the last anchor found
else {
return anchors[anchors.length - 1];
}
return anchors[anchors.length - 1];
}
function getLinkAnchorValue(link: HTMLAnchorElement): string {