mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-14 01:27:35 +02:00
refactor: fix more type-aware linting errors (#7479)
This commit is contained in:
parent
bf1513a3e3
commit
624735bd92
51 changed files with 192 additions and 189 deletions
4
.eslintrc.js
vendored
4
.eslintrc.js
vendored
|
@ -83,7 +83,7 @@ module.exports = {
|
||||||
'no-restricted-exports': OFF,
|
'no-restricted-exports': OFF,
|
||||||
'no-restricted-properties': [
|
'no-restricted-properties': [
|
||||||
ERROR,
|
ERROR,
|
||||||
...[
|
.../** @type {[string, string][]} */ ([
|
||||||
// TODO: TS doesn't make Boolean a narrowing function yet,
|
// TODO: TS doesn't make Boolean a narrowing function yet,
|
||||||
// so filter(Boolean) is problematic type-wise
|
// so filter(Boolean) is problematic type-wise
|
||||||
// ['compact', 'Array#filter(Boolean)'],
|
// ['compact', 'Array#filter(Boolean)'],
|
||||||
|
@ -114,7 +114,7 @@ module.exports = {
|
||||||
['take', 'Array#slice(0, n)'],
|
['take', 'Array#slice(0, n)'],
|
||||||
['takeRight', 'Array#slice(-n)'],
|
['takeRight', 'Array#slice(-n)'],
|
||||||
['tail', 'Array#slice(1)'],
|
['tail', 'Array#slice(1)'],
|
||||||
].map(([property, alternative]) => ({
|
]).map(([property, alternative]) => ({
|
||||||
object: '_',
|
object: '_',
|
||||||
property,
|
property,
|
||||||
message: `Use ${alternative} instead.`,
|
message: `Use ${alternative} instead.`,
|
||||||
|
|
|
@ -44,8 +44,9 @@ async function generateTemplateExample(template) {
|
||||||
`npm init docusaurus@latest examples/${template} ${command}`,
|
`npm init docusaurus@latest examples/${template} ${command}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const templatePackageJson = await fs.readJSON(
|
const templatePackageJson =
|
||||||
`examples/${template}/package.json`,
|
await /** @type {Promise<import("../../packages/create-docusaurus/templates/classic/package.json") & { scripts: { [name: string]: string }; description: string }>} */ (
|
||||||
|
fs.readJSON(`examples/${template}/package.json`)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attach the dev script which would be used in code sandbox by default
|
// Attach the dev script which would be used in code sandbox by default
|
||||||
|
|
|
@ -14,7 +14,9 @@ import logger from '@docusaurus/logger';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import {program} from 'commander';
|
import {program} from 'commander';
|
||||||
|
|
||||||
const packageJson = createRequire(import.meta.url)('../package.json');
|
const packageJson = /** @type {import("../package.json")} */ (
|
||||||
|
createRequire(import.meta.url)('../package.json')
|
||||||
|
);
|
||||||
const requiredVersion = packageJson.engines.node;
|
const requiredVersion = packageJson.engines.node;
|
||||||
|
|
||||||
if (!semver.satisfies(process.version, requiredVersion)) {
|
if (!semver.satisfies(process.version, requiredVersion)) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ async function askForPackageManagerChoice(): Promise<PackageManager> {
|
||||||
logger.info`Falling back to name=${defaultPackageManager}`;
|
logger.info`Falling back to name=${defaultPackageManager}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)) as {packageManager: PackageManager}
|
)) as {packageManager?: PackageManager}
|
||||||
).packageManager ?? defaultPackageManager
|
).packageManager ?? defaultPackageManager
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,9 @@ import semver from 'semver';
|
||||||
import cli from 'commander';
|
import cli from 'commander';
|
||||||
|
|
||||||
const moduleRequire = createRequire(import.meta.url);
|
const moduleRequire = createRequire(import.meta.url);
|
||||||
const requiredVersion = moduleRequire('../package.json').engines.node;
|
const requiredVersion = /** @type {import("../package.json")} */ (
|
||||||
|
moduleRequire('../package.json')
|
||||||
|
).engines.node;
|
||||||
|
|
||||||
if (!semver.satisfies(process.version, requiredVersion)) {
|
if (!semver.satisfies(process.version, requiredVersion)) {
|
||||||
logger.error('Minimum Node.js version not met :(');
|
logger.error('Minimum Node.js version not met :(');
|
||||||
|
@ -25,7 +27,7 @@ if (!semver.satisfies(process.version, requiredVersion)) {
|
||||||
|
|
||||||
// See https://github.com/facebook/docusaurus/pull/6860
|
// See https://github.com/facebook/docusaurus/pull/6860
|
||||||
const {migrateDocusaurusProject, migrateMDToMDX} =
|
const {migrateDocusaurusProject, migrateMDToMDX} =
|
||||||
moduleRequire('../lib/index.js');
|
/** @type {import("../lib/index.js")} */ (moduleRequire('../lib/index.js'));
|
||||||
|
|
||||||
cli
|
cli
|
||||||
.command('migrate [siteDir] [newDir]')
|
.command('migrate [siteDir] [newDir]')
|
||||||
|
|
|
@ -323,16 +323,27 @@ declare module '@docusaurus/renderRoutes' {
|
||||||
declare module '@docusaurus/useGlobalData' {
|
declare module '@docusaurus/useGlobalData' {
|
||||||
import type {GlobalData, UseDataOptions} from '@docusaurus/types';
|
import type {GlobalData, UseDataOptions} from '@docusaurus/types';
|
||||||
|
|
||||||
|
export function useAllPluginInstancesData(
|
||||||
|
pluginName: string,
|
||||||
|
options: {failfast: true},
|
||||||
|
): GlobalData[string];
|
||||||
|
|
||||||
export function useAllPluginInstancesData(
|
export function useAllPluginInstancesData(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
options?: UseDataOptions,
|
options?: UseDataOptions,
|
||||||
): GlobalData[string] | undefined;
|
): GlobalData[string] | undefined;
|
||||||
|
|
||||||
|
export function usePluginData(
|
||||||
|
pluginName: string,
|
||||||
|
pluginId: string | undefined,
|
||||||
|
options: {failfast: true},
|
||||||
|
): NonNullable<GlobalData[string][string]>;
|
||||||
|
|
||||||
export function usePluginData(
|
export function usePluginData(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
pluginId?: string,
|
pluginId?: string,
|
||||||
options?: UseDataOptions,
|
options?: UseDataOptions,
|
||||||
): GlobalData[string][string] | undefined;
|
): GlobalData[string][string];
|
||||||
|
|
||||||
export default function useGlobalData(): GlobalData;
|
export default function useGlobalData(): GlobalData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,9 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(fsMock.mock.calls.map((call) => call[1])).toMatchSnapshot();
|
expect(
|
||||||
|
fsMock.mock.calls.map((call) => call[1] as string),
|
||||||
|
).toMatchSnapshot();
|
||||||
fsMock.mockClear();
|
fsMock.mockClear();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
getVersionDocsDirPath,
|
getVersionDocsDirPath,
|
||||||
getVersionSidebarsPath,
|
getVersionSidebarsPath,
|
||||||
getDocsDirPathLocalized,
|
getDocsDirPathLocalized,
|
||||||
|
readVersionsFile,
|
||||||
} from './versions/files';
|
} from './versions/files';
|
||||||
import {validateVersionName} from './versions/validation';
|
import {validateVersionName} from './versions/validation';
|
||||||
import {loadSidebarsFileUnsafe} from './sidebars';
|
import {loadSidebarsFileUnsafe} from './sidebars';
|
||||||
|
@ -69,12 +70,7 @@ export async function cliDocsVersionCommand(
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load existing versions.
|
const versions = (await readVersionsFile(siteDir, pluginId)) ?? [];
|
||||||
let versions: string[] = [];
|
|
||||||
const versionsJSONFile = getVersionsFilePath(siteDir, pluginId);
|
|
||||||
if (await fs.pathExists(versionsJSONFile)) {
|
|
||||||
versions = await fs.readJSON(versionsJSONFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if version already exists.
|
// Check if version already exists.
|
||||||
if (versions.includes(version)) {
|
if (versions.includes(version)) {
|
||||||
|
@ -137,7 +133,7 @@ export async function cliDocsVersionCommand(
|
||||||
// Update versions.json file.
|
// Update versions.json file.
|
||||||
versions.unshift(version);
|
versions.unshift(version);
|
||||||
await fs.outputFile(
|
await fs.outputFile(
|
||||||
versionsJSONFile,
|
getVersionsFilePath(siteDir, pluginId),
|
||||||
`${JSON.stringify(versions, null, 2)}\n`,
|
`${JSON.stringify(versions, null, 2)}\n`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ export function getVersionsFilePath(siteDir: string, pluginId: string): string {
|
||||||
* @throws Throws if validation fails, i.e. `versions.json` doesn't contain an
|
* @throws Throws if validation fails, i.e. `versions.json` doesn't contain an
|
||||||
* array of valid version names.
|
* array of valid version names.
|
||||||
*/
|
*/
|
||||||
async function readVersionsFile(
|
export async function readVersionsFile(
|
||||||
siteDir: string,
|
siteDir: string,
|
||||||
pluginId: string,
|
pluginId: string,
|
||||||
): Promise<string[] | null> {
|
): Promise<string[] | null> {
|
||||||
|
|
|
@ -19,8 +19,9 @@ import type webpack from 'webpack';
|
||||||
const requireFromDocusaurusCore = createRequire(
|
const requireFromDocusaurusCore = createRequire(
|
||||||
require.resolve('@docusaurus/core/package.json'),
|
require.resolve('@docusaurus/core/package.json'),
|
||||||
);
|
);
|
||||||
const ContextReplacementPlugin: typeof webpack.ContextReplacementPlugin =
|
const ContextReplacementPlugin = requireFromDocusaurusCore(
|
||||||
requireFromDocusaurusCore('webpack/lib/ContextReplacementPlugin');
|
'webpack/lib/ContextReplacementPlugin',
|
||||||
|
) as typeof webpack.ContextReplacementPlugin;
|
||||||
|
|
||||||
// Need to be inlined to prevent dark mode FOUC
|
// Need to be inlined to prevent dark mode FOUC
|
||||||
// Make sure the key is the same as the one in `/theme/hooks/useTheme.js`
|
// Make sure the key is the same as the one in `/theme/hooks/useTheme.js`
|
||||||
|
|
|
@ -816,7 +816,6 @@ declare module '@theme/NavbarItem/NavbarNavLink' {
|
||||||
|
|
||||||
declare module '@theme/NavbarItem/DropdownNavbarItem' {
|
declare module '@theme/NavbarItem/DropdownNavbarItem' {
|
||||||
import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink';
|
import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink';
|
||||||
|
|
||||||
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
|
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
|
||||||
|
|
||||||
export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & {
|
export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & {
|
||||||
|
@ -976,7 +975,7 @@ declare module '@theme/NavbarItem' {
|
||||||
} & SearchNavbarItemProps)
|
} & SearchNavbarItemProps)
|
||||||
);
|
);
|
||||||
|
|
||||||
export type Types = Props['type'];
|
export type NavbarItemType = Props['type'];
|
||||||
|
|
||||||
export default function NavbarItem(props: Props): JSX.Element;
|
export default function NavbarItem(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,13 @@ import Details from '@theme/Details';
|
||||||
import type {Props} from '@theme/MDXComponents/Details';
|
import type {Props} from '@theme/MDXComponents/Details';
|
||||||
|
|
||||||
export default function MDXDetails(props: Props): JSX.Element {
|
export default function MDXDetails(props: Props): JSX.Element {
|
||||||
const items = React.Children.toArray(props.children) as ReactElement[];
|
const items = React.Children.toArray(props.children);
|
||||||
// Split summary item from the rest to pass it as a separate prop to the
|
// Split summary item from the rest to pass it as a separate prop to the
|
||||||
// Details theme component
|
// Details theme component
|
||||||
const summary: ReactElement<ComponentProps<'summary'>> | undefined =
|
const summary = items.find(
|
||||||
items.find((item) => item?.props?.mdxType === 'summary');
|
(item): item is ReactElement<ComponentProps<'summary'>> =>
|
||||||
|
React.isValidElement(item) && item.props?.mdxType === 'summary',
|
||||||
|
);
|
||||||
const children = <>{items.filter((item) => item !== summary)}</>;
|
const children = <>{items.filter((item) => item !== summary)}</>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -11,8 +11,10 @@ import type {Props} from '@theme/MDXComponents/Head';
|
||||||
|
|
||||||
// MDX elements are wrapped through the MDX pragma. In some cases (notably usage
|
// MDX elements are wrapped through the MDX pragma. In some cases (notably usage
|
||||||
// with Head/Helmet) we need to unwrap those elements.
|
// with Head/Helmet) we need to unwrap those elements.
|
||||||
function unwrapMDXElement(element: ReactElement) {
|
function unwrapMDXElement(
|
||||||
if (element?.props?.mdxType && element?.props?.originalType) {
|
element: ReactElement<{mdxType?: string; originalType?: string} | undefined>,
|
||||||
|
) {
|
||||||
|
if (element.props?.mdxType && element.props.originalType) {
|
||||||
const {mdxType, originalType, ...newProps} = element.props;
|
const {mdxType, originalType, ...newProps} = element.props;
|
||||||
return React.createElement(element.props.originalType, newProps);
|
return React.createElement(element.props.originalType, newProps);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +23,7 @@ function unwrapMDXElement(element: ReactElement) {
|
||||||
|
|
||||||
export default function MDXHead(props: Props): JSX.Element {
|
export default function MDXHead(props: Props): JSX.Element {
|
||||||
const unwrappedChildren = React.Children.map(props.children, (child) =>
|
const unwrappedChildren = React.Children.map(props.children, (child) =>
|
||||||
unwrapMDXElement(child as ReactElement),
|
React.isValidElement(child) ? unwrapMDXElement(child) : child,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Head {...(props as ComponentProps<typeof Head>)}>{unwrappedChildren}</Head>
|
<Head {...(props as ComponentProps<typeof Head>)}>{unwrappedChildren}</Head>
|
||||||
|
|
|
@ -14,8 +14,8 @@ export default function MDXPre(props: Props): JSX.Element {
|
||||||
<CodeBlock
|
<CodeBlock
|
||||||
// If this pre is created by a ``` fenced codeblock, unwrap the children
|
// If this pre is created by a ``` fenced codeblock, unwrap the children
|
||||||
{...(isValidElement(props.children) &&
|
{...(isValidElement(props.children) &&
|
||||||
props.children.props.originalType === 'code'
|
props.children.props?.originalType === 'code'
|
||||||
? props.children?.props
|
? props.children.props
|
||||||
: {...props})}
|
: {...props})}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,13 +11,12 @@ import {
|
||||||
useNavbarMobileSidebar,
|
useNavbarMobileSidebar,
|
||||||
useThemeConfig,
|
useThemeConfig,
|
||||||
} from '@docusaurus/theme-common';
|
} from '@docusaurus/theme-common';
|
||||||
import NavbarItem from '@theme/NavbarItem';
|
import NavbarItem, {type Props as NavbarItemConfig} from '@theme/NavbarItem';
|
||||||
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle';
|
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle';
|
||||||
import SearchBar from '@theme/SearchBar';
|
import SearchBar from '@theme/SearchBar';
|
||||||
import NavbarMobileSidebarToggle from '@theme/Navbar/MobileSidebar/Toggle';
|
import NavbarMobileSidebarToggle from '@theme/Navbar/MobileSidebar/Toggle';
|
||||||
import NavbarLogo from '@theme/Navbar/Logo';
|
import NavbarLogo from '@theme/Navbar/Logo';
|
||||||
import NavbarSearch from '@theme/Navbar/Search';
|
import NavbarSearch from '@theme/Navbar/Search';
|
||||||
import type {Props as NavbarItemConfig} from '@theme/NavbarItem';
|
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {useNavbarMobileSidebar, useThemeConfig} from '@docusaurus/theme-common';
|
import {useNavbarMobileSidebar, useThemeConfig} from '@docusaurus/theme-common';
|
||||||
import NavbarItem from '@theme/NavbarItem';
|
import NavbarItem, {type Props as NavbarItemConfig} from '@theme/NavbarItem';
|
||||||
import type {Props as NavbarItemConfig} from '@theme/NavbarItem';
|
|
||||||
|
|
||||||
function useNavbarItems() {
|
function useNavbarItems() {
|
||||||
// TODO temporary casting until ThemeConfig type is improved
|
// TODO temporary casting until ThemeConfig type is improved
|
||||||
|
|
|
@ -38,13 +38,13 @@ export default function DocsVersionDropdownNavbarItem({
|
||||||
// We try to link to the same doc, in another version
|
// We try to link to the same doc, in another version
|
||||||
// When not possible, fallback to the "main doc" of the version
|
// When not possible, fallback to the "main doc" of the version
|
||||||
const versionDoc =
|
const versionDoc =
|
||||||
activeDocContext?.alternateDocVersions[version.name] ??
|
activeDocContext.alternateDocVersions[version.name] ??
|
||||||
getVersionMainDoc(version);
|
getVersionMainDoc(version);
|
||||||
return {
|
return {
|
||||||
isNavLink: true,
|
isNavLink: true,
|
||||||
label: version.label,
|
label: version.label,
|
||||||
to: versionDoc.path,
|
to: versionDoc.path,
|
||||||
isActive: () => version === activeDocContext?.activeVersion,
|
isActive: () => version === activeDocContext.activeVersion,
|
||||||
onClick: () => savePreferredVersionName(version.name),
|
onClick: () => savePreferredVersionName(version.name),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,12 +15,11 @@ import {
|
||||||
useLocalPathname,
|
useLocalPathname,
|
||||||
} from '@docusaurus/theme-common';
|
} from '@docusaurus/theme-common';
|
||||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||||
import NavbarItem from '@theme/NavbarItem';
|
import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem';
|
||||||
import type {
|
import type {
|
||||||
DesktopOrMobileNavBarItemProps,
|
DesktopOrMobileNavBarItemProps,
|
||||||
Props,
|
Props,
|
||||||
} from '@theme/NavbarItem/DropdownNavbarItem';
|
} from '@theme/NavbarItem/DropdownNavbarItem';
|
||||||
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
|
|
||||||
|
|
||||||
const dropdownLinkActiveClass = 'dropdown__link--active';
|
const dropdownLinkActiveClass = 'dropdown__link--active';
|
||||||
|
|
||||||
|
|
|
@ -7,31 +7,22 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ComponentTypes from '@theme/NavbarItem/ComponentTypes';
|
import ComponentTypes from '@theme/NavbarItem/ComponentTypes';
|
||||||
import {type Props as DropdownNavbarItemProps} from '@theme/NavbarItem/DropdownNavbarItem';
|
import type {NavbarItemType, Props} from '@theme/NavbarItem';
|
||||||
import type {Types, Props} from '@theme/NavbarItem';
|
|
||||||
|
|
||||||
const getNavbarItemComponent = (type: NonNullable<Types>) => {
|
function normalizeComponentType(type: NavbarItemType, props: object) {
|
||||||
const component = ComponentTypes[type];
|
|
||||||
if (!component) {
|
|
||||||
throw new Error(`No NavbarItem component found for type "${type}".`);
|
|
||||||
}
|
|
||||||
return component;
|
|
||||||
};
|
|
||||||
|
|
||||||
function getComponentType(type: Types, isDropdown: boolean) {
|
|
||||||
// Backward compatibility: navbar item with no type set
|
// Backward compatibility: navbar item with no type set
|
||||||
// but containing dropdown items should use the type "dropdown"
|
// but containing dropdown items should use the type "dropdown"
|
||||||
if (!type || type === 'default') {
|
if (!type || type === 'default') {
|
||||||
return isDropdown ? 'dropdown' : 'default';
|
return 'items' in props ? 'dropdown' : 'default';
|
||||||
}
|
}
|
||||||
return type as NonNullable<Types>;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavbarItem({type, ...props}: Props): JSX.Element {
|
export default function NavbarItem({type, ...props}: Props): JSX.Element {
|
||||||
const componentType = getComponentType(
|
const componentType = normalizeComponentType(type, props);
|
||||||
type,
|
const NavbarItemComponent = ComponentTypes[componentType];
|
||||||
(props as DropdownNavbarItemProps).items !== undefined,
|
if (!NavbarItemComponent) {
|
||||||
);
|
throw new Error(`No NavbarItem component found for type "${type}".`);
|
||||||
const NavbarItemComponent = getNavbarItemComponent(componentType);
|
}
|
||||||
return <NavbarItemComponent {...(props as never)} />;
|
return <NavbarItemComponent {...(props as never)} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ function TabsComponent(props: Props): JSX.Element {
|
||||||
? defaultValueProp
|
? defaultValueProp
|
||||||
: defaultValueProp ??
|
: defaultValueProp ??
|
||||||
children.find((child) => child.props.default)?.props.value ??
|
children.find((child) => child.props.default)?.props.value ??
|
||||||
children[0]?.props.value;
|
children[0]!.props.value;
|
||||||
if (defaultValue !== null && !values.some((a) => a.value === defaultValue)) {
|
if (defaultValue !== null && !values.some((a) => a.value === defaultValue)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${values
|
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${values
|
||||||
|
@ -126,12 +126,12 @@ function TabsComponent(props: Props): JSX.Element {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'ArrowRight': {
|
case 'ArrowRight': {
|
||||||
const nextTab = tabRefs.indexOf(event.currentTarget) + 1;
|
const nextTab = tabRefs.indexOf(event.currentTarget) + 1;
|
||||||
focusElement = tabRefs[nextTab] || tabRefs[0]!;
|
focusElement = tabRefs[nextTab] ?? tabRefs[0]!;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'ArrowLeft': {
|
case 'ArrowLeft': {
|
||||||
const prevTab = tabRefs.indexOf(event.currentTarget) - 1;
|
const prevTab = tabRefs.indexOf(event.currentTarget) - 1;
|
||||||
focusElement = tabRefs[prevTab] || tabRefs[tabRefs.length - 1]!;
|
focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1]!;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -92,7 +92,7 @@ export function Details({
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{/* eslint-disable-next-line @docusaurus/no-untranslated-text */}
|
{/* eslint-disable-next-line @docusaurus/no-untranslated-text */}
|
||||||
{summary || <summary>Details</summary>}
|
{summary ?? <summary>Details</summary>}
|
||||||
|
|
||||||
<Collapsible
|
<Collapsible
|
||||||
lazy={false} // Content might matter for SEO in this case
|
lazy={false} // Content might matter for SEO in this case
|
||||||
|
|
|
@ -151,7 +151,7 @@ export function useTOCHighlight(config: TOCHighlightConfig | undefined): void {
|
||||||
function updateLinkActiveClass(link: HTMLAnchorElement, active: boolean) {
|
function updateLinkActiveClass(link: HTMLAnchorElement, active: boolean) {
|
||||||
if (active) {
|
if (active) {
|
||||||
if (lastActiveLinkRef.current && lastActiveLinkRef.current !== link) {
|
if (lastActiveLinkRef.current && lastActiveLinkRef.current !== link) {
|
||||||
lastActiveLinkRef.current?.classList.remove(linkActiveClassName);
|
lastActiveLinkRef.current.classList.remove(linkActiveClassName);
|
||||||
}
|
}
|
||||||
link.classList.add(linkActiveClassName);
|
link.classList.add(linkActiveClassName);
|
||||||
lastActiveLinkRef.current = link;
|
lastActiveLinkRef.current = link;
|
||||||
|
|
|
@ -97,7 +97,7 @@ export function parseCodeBlockTitle(metastring?: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function containsLineNumbers(metastring?: string): boolean {
|
export function containsLineNumbers(metastring?: string): boolean {
|
||||||
return metastring?.includes('showLineNumbers') || false;
|
return Boolean(metastring?.includes('showLineNumbers'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,7 +209,9 @@ export function parseLines(
|
||||||
lineNumber += 1;
|
lineNumber += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const directive = match.slice(1).find((item) => item !== undefined)!;
|
const directive = match
|
||||||
|
.slice(1)
|
||||||
|
.find((item: string | undefined) => item !== undefined)!;
|
||||||
if (lineToClassName[directive]) {
|
if (lineToClassName[directive]) {
|
||||||
blocks[lineToClassName[directive]!]!.range += `${lineNumber},`;
|
blocks[lineToClassName[directive]!]!.range += `${lineNumber},`;
|
||||||
} else if (blockStartToClassName[directive]) {
|
} else if (blockStartToClassName[directive]) {
|
||||||
|
|
|
@ -85,10 +85,10 @@ export type FooterBase = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MultiColumnFooter = FooterBase & {
|
export type MultiColumnFooter = FooterBase & {
|
||||||
links: Array<{
|
links: {
|
||||||
title: string | null;
|
title: string | null;
|
||||||
items: FooterLinkItem[];
|
items: FooterLinkItem[];
|
||||||
}>;
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SimpleFooter = FooterBase & {
|
export type SimpleFooter = FooterBase & {
|
||||||
|
@ -123,7 +123,7 @@ export type ThemeConfig = {
|
||||||
prism: PrismConfig;
|
prism: PrismConfig;
|
||||||
footer?: Footer;
|
footer?: Footer;
|
||||||
image?: string;
|
image?: string;
|
||||||
metadata: Array<{[key: string]: string}>;
|
metadata: {[key: string]: string}[];
|
||||||
tableOfContents: TableOfContents;
|
tableOfContents: TableOfContents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ export default function Playground({
|
||||||
{/* @ts-expect-error: type incompatibility with refs */}
|
{/* @ts-expect-error: type incompatibility with refs */}
|
||||||
<LiveProvider
|
<LiveProvider
|
||||||
code={children.replace(/\n$/, '')}
|
code={children.replace(/\n$/, '')}
|
||||||
transformCode={transformCode || ((code) => `${code};`)}
|
transformCode={transformCode ?? ((code) => `${code};`)}
|
||||||
theme={prismTheme}
|
theme={prismTheme}
|
||||||
{...props}>
|
{...props}>
|
||||||
{playgroundPosition === 'top' ? (
|
{playgroundPosition === 'top' ? (
|
||||||
|
|
|
@ -75,7 +75,7 @@ type FacetFilters = Required<
|
||||||
function mergeFacetFilters(f1: FacetFilters, f2: FacetFilters): FacetFilters {
|
function mergeFacetFilters(f1: FacetFilters, f2: FacetFilters): FacetFilters {
|
||||||
const normalize = (
|
const normalize = (
|
||||||
f: FacetFilters,
|
f: FacetFilters,
|
||||||
): readonly string[] | ReadonlyArray<string | readonly string[]> =>
|
): readonly string[] | readonly (string | readonly string[])[] =>
|
||||||
typeof f === 'string' ? [f] : f;
|
typeof f === 'string' ? [f] : f;
|
||||||
return [...normalize(f1), ...normalize(f2)] as FacetFilters;
|
return [...normalize(f1), ...normalize(f2)] as FacetFilters;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,10 @@ describe('theme translations', () => {
|
||||||
.then((files) =>
|
.then((files) =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
files.map(
|
files.map(
|
||||||
(baseMessagesFile): Promise<{[key: string]: string}> =>
|
(baseMessagesFile) =>
|
||||||
fs.readJSON(path.join(baseMessagesDirPath, baseMessagesFile)),
|
fs.readJSON(
|
||||||
|
path.join(baseMessagesDirPath, baseMessagesFile),
|
||||||
|
) as Promise<{[key: string]: string}>,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -51,7 +51,7 @@ export async function readDefaultCodeTranslationMessages({
|
||||||
const filePath = path.resolve(dirPath, localeToTry, `${name}.json`);
|
const filePath = path.resolve(dirPath, localeToTry, `${name}.json`);
|
||||||
|
|
||||||
if (await fs.pathExists(filePath)) {
|
if (await fs.pathExists(filePath)) {
|
||||||
return fs.readJSON(filePath);
|
return fs.readJSON(filePath) as Promise<CodeTranslations>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,21 +37,24 @@ describe('isNameTooLong', () => {
|
||||||
};
|
};
|
||||||
const oldProcessPlatform = process.platform;
|
const oldProcessPlatform = process.platform;
|
||||||
Object.defineProperty(process, 'platform', {value: 'darwin'});
|
Object.defineProperty(process, 'platform', {value: 'darwin'});
|
||||||
Object.keys(asserts).forEach((file) => {
|
(Object.keys(asserts) as (keyof typeof asserts)[]).forEach((file) => {
|
||||||
|
const expected = asserts[file];
|
||||||
expect(isNameTooLong(file)).toBe(
|
expect(isNameTooLong(file)).toBe(
|
||||||
typeof asserts[file] === 'boolean' ? asserts[file] : asserts[file].apfs,
|
typeof expected === 'boolean' ? expected : expected.apfs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Object.defineProperty(process, 'platform', {value: 'win32'});
|
Object.defineProperty(process, 'platform', {value: 'win32'});
|
||||||
Object.keys(asserts).forEach((file) => {
|
(Object.keys(asserts) as (keyof typeof asserts)[]).forEach((file) => {
|
||||||
|
const expected = asserts[file];
|
||||||
expect(isNameTooLong(file)).toBe(
|
expect(isNameTooLong(file)).toBe(
|
||||||
typeof asserts[file] === 'boolean' ? asserts[file] : asserts[file].apfs,
|
typeof expected === 'boolean' ? expected : expected.apfs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Object.defineProperty(process, 'platform', {value: 'android'});
|
Object.defineProperty(process, 'platform', {value: 'android'});
|
||||||
Object.keys(asserts).forEach((file) => {
|
(Object.keys(asserts) as (keyof typeof asserts)[]).forEach((file) => {
|
||||||
|
const expected = asserts[file];
|
||||||
expect(isNameTooLong(file)).toBe(
|
expect(isNameTooLong(file)).toBe(
|
||||||
typeof asserts[file] === 'boolean' ? asserts[file] : asserts[file].xfs,
|
typeof expected === 'boolean' ? expected : expected.xfs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Object.defineProperty(process, 'platform', {value: oldProcessPlatform});
|
Object.defineProperty(process, 'platform', {value: oldProcessPlatform});
|
||||||
|
@ -79,21 +82,24 @@ describe('shortName', () => {
|
||||||
};
|
};
|
||||||
const oldProcessPlatform = process.platform;
|
const oldProcessPlatform = process.platform;
|
||||||
Object.defineProperty(process, 'platform', {value: 'darwin'});
|
Object.defineProperty(process, 'platform', {value: 'darwin'});
|
||||||
Object.keys(asserts).forEach((file) => {
|
(Object.keys(asserts) as (keyof typeof asserts)[]).forEach((file) => {
|
||||||
|
const expected = asserts[file];
|
||||||
expect(shortName(file)).toBe(
|
expect(shortName(file)).toBe(
|
||||||
typeof asserts[file] === 'string' ? asserts[file] : asserts[file].apfs,
|
typeof expected === 'string' ? expected : expected.apfs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Object.defineProperty(process, 'platform', {value: 'win32'});
|
Object.defineProperty(process, 'platform', {value: 'win32'});
|
||||||
Object.keys(asserts).forEach((file) => {
|
(Object.keys(asserts) as (keyof typeof asserts)[]).forEach((file) => {
|
||||||
|
const expected = asserts[file];
|
||||||
expect(shortName(file)).toBe(
|
expect(shortName(file)).toBe(
|
||||||
typeof asserts[file] === 'string' ? asserts[file] : asserts[file].apfs,
|
typeof expected === 'string' ? expected : expected.apfs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Object.defineProperty(process, 'platform', {value: 'android'});
|
Object.defineProperty(process, 'platform', {value: 'android'});
|
||||||
Object.keys(asserts).forEach((file) => {
|
(Object.keys(asserts) as (keyof typeof asserts)[]).forEach((file) => {
|
||||||
|
const expected = asserts[file];
|
||||||
expect(shortName(file)).toBe(
|
expect(shortName(file)).toBe(
|
||||||
typeof asserts[file] === 'string' ? asserts[file] : asserts[file].xfs,
|
typeof expected === 'string' ? expected : expected.xfs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Object.defineProperty(process, 'platform', {value: oldProcessPlatform});
|
Object.defineProperty(process, 'platform', {value: oldProcessPlatform});
|
||||||
|
|
|
@ -16,7 +16,9 @@ import updateNotifier from 'update-notifier';
|
||||||
import boxen from 'boxen';
|
import boxen from 'boxen';
|
||||||
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
|
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
|
||||||
|
|
||||||
const packageJson = createRequire(import.meta.url)('../package.json');
|
const packageJson = /** @type {import("../package.json")} */ (
|
||||||
|
createRequire(import.meta.url)('../package.json')
|
||||||
|
);
|
||||||
/** @type {Record<string, any>} */
|
/** @type {Record<string, any>} */
|
||||||
let sitePkg;
|
let sitePkg;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -15,7 +15,7 @@ export function dispatchLifecycleAction<K extends keyof ClientModule>(
|
||||||
...args: Parameters<NonNullable<ClientModule[K]>>
|
...args: Parameters<NonNullable<ClientModule[K]>>
|
||||||
): () => void {
|
): () => void {
|
||||||
const callbacks = clientModules.map((clientModule) => {
|
const callbacks = clientModules.map((clientModule) => {
|
||||||
const lifecycleFunction = (clientModule?.default?.[lifecycleAction] ??
|
const lifecycleFunction = (clientModule.default?.[lifecycleAction] ??
|
||||||
clientModule[lifecycleAction]) as
|
clientModule[lifecycleAction]) as
|
||||||
| ((
|
| ((
|
||||||
...a: Parameters<NonNullable<ClientModule[K]>>
|
...a: Parameters<NonNullable<ClientModule[K]>>
|
||||||
|
|
|
@ -41,7 +41,7 @@ describe('flat', () => {
|
||||||
null: null,
|
null: null,
|
||||||
undefined,
|
undefined,
|
||||||
};
|
};
|
||||||
Object.keys(primitives).forEach((key) => {
|
(Object.keys(primitives) as (keyof typeof primitives)[]).forEach((key) => {
|
||||||
const value = primitives[key];
|
const value = primitives[key];
|
||||||
expect(
|
expect(
|
||||||
flat({
|
flat({
|
||||||
|
|
|
@ -70,7 +70,7 @@ const docusaurus = {
|
||||||
// In some cases, webpack might decide to optimize further, leading to
|
// In some cases, webpack might decide to optimize further, leading to
|
||||||
// the chunk assets being merged to another chunk. In this case, we can
|
// the chunk assets being merged to another chunk. In this case, we can
|
||||||
// safely filter it out and don't need to load it.
|
// safely filter it out and don't need to load it.
|
||||||
if (chunkAsset && !/undefined/.test(chunkAsset)) {
|
if (chunkAsset && !chunkAsset.includes('undefined')) {
|
||||||
return prefetchHelper(chunkAsset);
|
return prefetchHelper(chunkAsset);
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
|
@ -47,10 +47,7 @@ function Link(
|
||||||
const linksCollector = useLinksCollector();
|
const linksCollector = useLinksCollector();
|
||||||
const innerRef = useRef<HTMLAnchorElement | null>(null);
|
const innerRef = useRef<HTMLAnchorElement | null>(null);
|
||||||
|
|
||||||
useImperativeHandle(
|
useImperativeHandle(forwardedRef, () => innerRef.current!);
|
||||||
forwardedRef,
|
|
||||||
() => innerRef.current as HTMLAnchorElement,
|
|
||||||
);
|
|
||||||
|
|
||||||
// IMPORTANT: using to or href should not change anything
|
// IMPORTANT: using to or href should not change anything
|
||||||
// For example, MDX links will ALWAYS give us the href props
|
// For example, MDX links will ALWAYS give us the href props
|
||||||
|
|
|
@ -26,19 +26,6 @@ describe('useGlobalData', () => {
|
||||||
}).result.current,
|
}).result.current,
|
||||||
).toEqual({foo: 'bar'});
|
).toEqual({foo: 'bar'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws when global data not found', () => {
|
|
||||||
// Can it actually happen?
|
|
||||||
expect(
|
|
||||||
() =>
|
|
||||||
renderHook(() => useGlobalData(), {
|
|
||||||
wrapper: ({children}) => (
|
|
||||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
|
||||||
<Context.Provider value={{}}>{children}</Context.Provider>
|
|
||||||
),
|
|
||||||
}).result.current,
|
|
||||||
).toThrowErrorMatchingInlineSnapshot(`"Docusaurus global data not found."`);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('useAllPluginInstancesData', () => {
|
describe('useAllPluginInstancesData', () => {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function hasProtocol(url: string): boolean {
|
export function hasProtocol(url: string): boolean {
|
||||||
return /^(?:\w*:|\/\/)/.test(url) === true;
|
return /^(?:\w*:|\/\/)/.test(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function isInternalUrl(url?: string): boolean {
|
export default function isInternalUrl(url?: string): boolean {
|
||||||
|
|
|
@ -11,9 +11,6 @@ import type {GlobalData, UseDataOptions} from '@docusaurus/types';
|
||||||
|
|
||||||
export default function useGlobalData(): GlobalData {
|
export default function useGlobalData(): GlobalData {
|
||||||
const {globalData} = useDocusaurusContext();
|
const {globalData} = useDocusaurusContext();
|
||||||
if (!globalData) {
|
|
||||||
throw new Error('Docusaurus global data not found.');
|
|
||||||
}
|
|
||||||
return globalData;
|
return globalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type {ChunkNames} from '@docusaurus/types';
|
||||||
type Chunk = ChunkNames[string];
|
type Chunk = ChunkNames[string];
|
||||||
type Tree = Exclude<Chunk, string>;
|
type Tree = Exclude<Chunk, string>;
|
||||||
|
|
||||||
const isTree = (x: Chunk): x is Tree =>
|
const isTree = (x: unknown): x is Tree =>
|
||||||
typeof x === 'object' && !!x && Object.keys(x).length > 0;
|
typeof x === 'object' && !!x && Object.keys(x).length > 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
function supports(feature: string) {
|
function supports(feature: string) {
|
||||||
try {
|
try {
|
||||||
const fakeLink = document.createElement('link');
|
const fakeLink = document.createElement('link');
|
||||||
return fakeLink.relList?.supports?.(feature);
|
return fakeLink.relList.supports(feature);
|
||||||
} catch (err) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,9 +115,9 @@ async function doRender(locals: Locals & {path: string}) {
|
||||||
// Using readJSON seems to fail for users of some plugins, possibly because of
|
// Using readJSON seems to fail for users of some plugins, possibly because of
|
||||||
// the eval sandbox having a different `Buffer` instance (native one instead
|
// the eval sandbox having a different `Buffer` instance (native one instead
|
||||||
// of polyfilled one)
|
// of polyfilled one)
|
||||||
const manifest: Manifest = await fs
|
const manifest = (await fs
|
||||||
.readFile(manifestPath, 'utf-8')
|
.readFile(manifestPath, 'utf-8')
|
||||||
.then(JSON.parse);
|
.then(JSON.parse)) as Manifest;
|
||||||
|
|
||||||
// Get all required assets for this particular page based on client
|
// Get all required assets for this particular page based on client
|
||||||
// manifest information.
|
// manifest information.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if plugins is not a string and it's not an array #1 for the input of: [123] 1`] = `
|
exports[`normalizeConfig should throw error if plugins is not a string and it's not an array #1 for the input of: [123] 1`] = `
|
||||||
" => Bad Docusaurus plugin value as path [plugins,0].
|
" => Bad Docusaurus plugin value plugins[0].
|
||||||
Example valid plugin config:
|
Example valid plugin config:
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -17,7 +17,7 @@ Example valid plugin config:
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if plugins is not an array of [string, object][] #1 for the input of: [[Array]] 1`] = `
|
exports[`normalizeConfig should throw error if plugins is not an array of [string, object][] #1 for the input of: [[Array]] 1`] = `
|
||||||
" => Bad Docusaurus plugin value as path [plugins,0].
|
" => Bad Docusaurus plugin value plugins[0].
|
||||||
Example valid plugin config:
|
Example valid plugin config:
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -33,7 +33,7 @@ Example valid plugin config:
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if plugins is not an array of [string, object][] #2 for the input of: [[Array]] 1`] = `
|
exports[`normalizeConfig should throw error if plugins is not an array of [string, object][] #2 for the input of: [[Array]] 1`] = `
|
||||||
" => Bad Docusaurus plugin value as path [plugins,0].
|
" => Bad Docusaurus plugin value plugins[0].
|
||||||
Example valid plugin config:
|
Example valid plugin config:
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -49,7 +49,7 @@ Example valid plugin config:
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if plugins is not an array of [string, object][] #3 for the input of: [[Array]] 1`] = `
|
exports[`normalizeConfig should throw error if plugins is not an array of [string, object][] #3 for the input of: [[Array]] 1`] = `
|
||||||
" => Bad Docusaurus plugin value as path [plugins,0].
|
" => Bad Docusaurus plugin value plugins[0].
|
||||||
Example valid plugin config:
|
Example valid plugin config:
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -70,7 +70,7 @@ exports[`normalizeConfig should throw error if plugins is not array for the inpu
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if themes is not a string and it's not an array #1 for the input of: [123] 1`] = `
|
exports[`normalizeConfig should throw error if themes is not a string and it's not an array #1 for the input of: [123] 1`] = `
|
||||||
" => Bad Docusaurus theme value as path [themes,0].
|
" => Bad Docusaurus theme value themes[0].
|
||||||
Example valid theme config:
|
Example valid theme config:
|
||||||
{
|
{
|
||||||
themes: [
|
themes: [
|
||||||
|
@ -86,7 +86,7 @@ Example valid theme config:
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if themes is not an array of [string, object][] #1 for the input of: [[Array]] 1`] = `
|
exports[`normalizeConfig should throw error if themes is not an array of [string, object][] #1 for the input of: [[Array]] 1`] = `
|
||||||
" => Bad Docusaurus theme value as path [themes,0].
|
" => Bad Docusaurus theme value themes[0].
|
||||||
Example valid theme config:
|
Example valid theme config:
|
||||||
{
|
{
|
||||||
themes: [
|
themes: [
|
||||||
|
@ -102,7 +102,7 @@ Example valid theme config:
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if themes is not an array of [string, object][] #2 for the input of: [[Array]] 1`] = `
|
exports[`normalizeConfig should throw error if themes is not an array of [string, object][] #2 for the input of: [[Array]] 1`] = `
|
||||||
" => Bad Docusaurus theme value as path [themes,0].
|
" => Bad Docusaurus theme value themes[0].
|
||||||
Example valid theme config:
|
Example valid theme config:
|
||||||
{
|
{
|
||||||
themes: [
|
themes: [
|
||||||
|
@ -118,7 +118,7 @@ Example valid theme config:
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`normalizeConfig should throw error if themes is not an array of [string, object][] #3 for the input of: [[Array]] 1`] = `
|
exports[`normalizeConfig should throw error if themes is not an array of [string, object][] #3 for the input of: [[Array]] 1`] = `
|
||||||
" => Bad Docusaurus theme value as path [themes,0].
|
" => Bad Docusaurus theme value themes[0].
|
||||||
Example valid theme config:
|
Example valid theme config:
|
||||||
{
|
{
|
||||||
themes: [
|
themes: [
|
||||||
|
|
|
@ -100,7 +100,9 @@ function createPluginSchema(theme: boolean) {
|
||||||
|
|
||||||
error.message = ` => Bad Docusaurus ${
|
error.message = ` => Bad Docusaurus ${
|
||||||
theme ? 'theme' : 'plugin'
|
theme ? 'theme' : 'plugin'
|
||||||
} value as path [${error.path}].
|
} value ${error.path.reduce((acc, cur) =>
|
||||||
|
typeof cur === 'string' ? `${acc}.${cur}` : `${acc}[${cur}]`,
|
||||||
|
)}.
|
||||||
${validConfigExample}
|
${validConfigExample}
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
@ -247,7 +249,9 @@ export function validateConfig(config: unknown): DocusaurusConfig {
|
||||||
if (error) {
|
if (error) {
|
||||||
const unknownFields = error.details.reduce((formattedError, err) => {
|
const unknownFields = error.details.reduce((formattedError, err) => {
|
||||||
if (err.type === 'object.unknown') {
|
if (err.type === 'object.unknown') {
|
||||||
return `${formattedError}"${err.path}",`;
|
return `${formattedError}"${err.path.reduce((acc, cur) =>
|
||||||
|
typeof cur === 'string' ? `${acc}.${cur}` : `${acc}[${cur}]`,
|
||||||
|
)}",`;
|
||||||
}
|
}
|
||||||
return formattedError;
|
return formattedError;
|
||||||
}, '');
|
}, '');
|
||||||
|
|
|
@ -83,5 +83,5 @@ export function loadHtmlTags(
|
||||||
.trim(),
|
.trim(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
) as Pick<Props, 'headTags' | 'preBodyTags' | 'postBodyTags'>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`initPlugins throws user-friendly error message for plugins with bad values 1`] = `
|
exports[`initPlugins throws user-friendly error message for plugins with bad values 1`] = `
|
||||||
" => Bad Docusaurus plugin value as path [plugins,0].
|
" => Bad Docusaurus plugin value plugins[0].
|
||||||
Example valid plugin config:
|
Example valid plugin config:
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -13,7 +13,7 @@ Example valid plugin config:
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
=> Bad Docusaurus plugin value as path [plugins,1].
|
=> Bad Docusaurus plugin value plugins[1].
|
||||||
Example valid plugin config:
|
Example valid plugin config:
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe('initPlugins', () => {
|
||||||
|
|
||||||
it('parses plugins correctly and loads them in correct order', async () => {
|
it('parses plugins correctly and loads them in correct order', async () => {
|
||||||
const {context, plugins} = await loadSite();
|
const {context, plugins} = await loadSite();
|
||||||
expect(context.siteConfig.plugins?.length).toBe(4);
|
expect(context.siteConfig.plugins).toHaveLength(4);
|
||||||
expect(plugins).toHaveLength(8);
|
expect(plugins).toHaveLength(8);
|
||||||
|
|
||||||
expect(plugins[0].name).toBe('preset-plugin1');
|
expect(plugins[0].name).toBe('preset-plugin1');
|
||||||
|
|
|
@ -184,7 +184,7 @@ function extractSourceCodeAstTranslations(
|
||||||
sourceCodeFilePath: string,
|
sourceCodeFilePath: string,
|
||||||
): SourceCodeFileTranslations {
|
): SourceCodeFileTranslations {
|
||||||
function sourceWarningPart(node: Node) {
|
function sourceWarningPart(node: Node) {
|
||||||
return `File: ${sourceCodeFilePath} at line ${node.loc?.start.line}
|
return `File: ${sourceCodeFilePath} at line ${node.loc?.start.line ?? '?'}
|
||||||
Full code: ${generate(node).code}`;
|
Full code: ${generate(node).code}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +313,9 @@ ${sourceWarningPart(path.node)}`);
|
||||||
if (isJSXText || isJSXExpressionContainer) {
|
if (isJSXText || isJSXExpressionContainer) {
|
||||||
message = isJSXText
|
message = isJSXText
|
||||||
? singleChildren.node.value.trim().replace(/\s+/g, ' ')
|
? singleChildren.node.value.trim().replace(/\s+/g, ' ')
|
||||||
: (singleChildren.get('expression') as NodePath).evaluate().value;
|
: String(
|
||||||
|
(singleChildren.get('expression') as NodePath).evaluate().value,
|
||||||
|
);
|
||||||
|
|
||||||
translations[id ?? message] = {
|
translations[id ?? message] = {
|
||||||
message,
|
message,
|
||||||
|
|
|
@ -198,7 +198,7 @@ export function applyConfigurePostCss(
|
||||||
configurePostCss: NonNullable<Plugin['configurePostCss']>,
|
configurePostCss: NonNullable<Plugin['configurePostCss']>,
|
||||||
config: Configuration,
|
config: Configuration,
|
||||||
): Configuration {
|
): Configuration {
|
||||||
type LocalPostCSSLoader = unknown & {
|
type LocalPostCSSLoader = object & {
|
||||||
options: {postcssOptions: PostCssOptions};
|
options: {postcssOptions: PostCssOptions};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as lqip from './lqip';
|
import * as lqip from './lqip';
|
||||||
import type {LoaderContext} from 'webpack';
|
import type {LoaderContext, LoaderModule} from 'webpack';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
base64: boolean;
|
base64: boolean;
|
||||||
|
@ -36,8 +36,9 @@ export default async function lqipLoader(
|
||||||
} else {
|
} else {
|
||||||
if (!contentIsFileExport) {
|
if (!contentIsFileExport) {
|
||||||
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
|
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
|
||||||
const fileLoader = require('file-loader') as typeof import('file-loader');
|
const fileLoader = require('file-loader') as LoaderModule['default'];
|
||||||
content = fileLoader.call(this, contentBuffer);
|
// @ts-expect-error: type is a bit unwieldy...
|
||||||
|
content = fileLoader!.call(this, contentBuffer) as string;
|
||||||
}
|
}
|
||||||
source = content.match(
|
source = content.match(
|
||||||
/^(?:export default|module.exports =) (?<source>.*);/,
|
/^(?:export default|module.exports =) (?<source>.*);/,
|
||||||
|
|
|
@ -43,9 +43,8 @@ function getOutputCss(output: stylelint.LinterResult) {
|
||||||
function testStylelintRule(config: stylelint.Config, tests: TestSuite) {
|
function testStylelintRule(config: stylelint.Config, tests: TestSuite) {
|
||||||
describe(`${tests.ruleName}`, () => {
|
describe(`${tests.ruleName}`, () => {
|
||||||
const checkTestCaseContent = (testCase: TestCase) =>
|
const checkTestCaseContent = (testCase: TestCase) =>
|
||||||
testCase.description || testCase.code;
|
testCase.description ?? testCase.code;
|
||||||
|
|
||||||
if (tests.accept?.length) {
|
|
||||||
describe('accept cases', () => {
|
describe('accept cases', () => {
|
||||||
tests.accept.forEach((testCase) => {
|
tests.accept.forEach((testCase) => {
|
||||||
it(`${checkTestCaseContent(testCase)}`, async () => {
|
it(`${checkTestCaseContent(testCase)}`, async () => {
|
||||||
|
@ -65,9 +64,7 @@ function testStylelintRule(config: stylelint.Config, tests: TestSuite) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (tests.reject?.length) {
|
|
||||||
describe('reject cases', () => {
|
describe('reject cases', () => {
|
||||||
tests.reject.forEach((testCase) => {
|
tests.reject.forEach((testCase) => {
|
||||||
it(`${checkTestCaseContent(testCase)}`, async () => {
|
it(`${checkTestCaseContent(testCase)}`, async () => {
|
||||||
|
@ -104,7 +101,6 @@ function testStylelintRule(config: stylelint.Config, tests: TestSuite) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
toHaveMessage(testCase: TestCase) {
|
toHaveMessage(testCase: TestCase) {
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true // @types/webpack and webpack/types.d.ts are not the same thing
|
"skipLibCheck": true // @types/webpack and webpack/types.d.ts are not the same thing
|
||||||
},
|
},
|
||||||
|
"include": ["./**/*", "./**/.eslintrc.js"],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"coverage/**",
|
"coverage/**",
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default function Version(): JSX.Element {
|
||||||
const pastVersions = versions.filter(
|
const pastVersions = versions.filter(
|
||||||
(version) => version !== latestVersion && version.name !== 'current',
|
(version) => version !== latestVersion && version.name !== 'current',
|
||||||
);
|
);
|
||||||
const repoUrl = `https://github.com/${organizationName}/${projectName}`;
|
const repoUrl = `https://github.com/${organizationName!}/${projectName!}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue