chore: upgrade TypeScript & other ESLint related deps (#5963)

* chore: upgrade ESLint related deps

* Upgrade TS

* Fix lock

* Bump Babel

* Update config
This commit is contained in:
Joshua Chen 2021-11-18 21:15:37 +08:00 committed by GitHub
parent 2f7d6fea1e
commit 0374426ce3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
104 changed files with 2662 additions and 2487 deletions

View file

@ -31,7 +31,6 @@ module.exports = {
'plugin:react-hooks/recommended', 'plugin:react-hooks/recommended',
'airbnb', 'airbnb',
'prettier', 'prettier',
'prettier/react',
], ],
settings: { settings: {
'import/resolver': { 'import/resolver': {
@ -61,6 +60,7 @@ module.exports = {
}, },
], ],
'import/extensions': OFF, 'import/extensions': OFF,
'no-restricted-exports': OFF,
'header/header': [ 'header/header': [
ERROR, ERROR,
'block', 'block',
@ -90,6 +90,14 @@ module.exports = {
'react/prefer-stateless-function': WARNING, 'react/prefer-stateless-function': WARNING,
'react/jsx-props-no-spreading': OFF, 'react/jsx-props-no-spreading': OFF,
'react/require-default-props': [ERROR, {ignoreFunctionalComponents: true}], 'react/require-default-props': [ERROR, {ignoreFunctionalComponents: true}],
'react/function-component-definition': [
WARNING,
{
namedComponents: 'function-declaration',
unnamedComponents: 'arrow-function',
},
],
'react/no-unstable-nested-components': [WARNING, {allowAsProps: true}],
'@typescript-eslint/no-inferrable-types': OFF, '@typescript-eslint/no-inferrable-types': OFF,
'import/first': OFF, 'import/first': OFF,
'import/order': OFF, 'import/order': OFF,
@ -104,19 +112,16 @@ module.exports = {
'no-unused-vars': OFF, 'no-unused-vars': OFF,
'no-nested-ternary': WARNING, 'no-nested-ternary': WARNING,
'@typescript-eslint/no-empty-function': OFF, '@typescript-eslint/no-empty-function': OFF,
'@typescript-eslint/no-non-null-assertion': OFF, // Have to use type assertion anyways '@typescript-eslint/no-non-null-assertion': OFF,
'@typescript-eslint/no-unused-vars': [ '@typescript-eslint/no-unused-vars': [
ERROR, ERROR,
{argsIgnorePattern: '^_', ignoreRestSiblings: true}, {argsIgnorePattern: '^_', ignoreRestSiblings: true},
], ],
'@typescript-eslint/explicit-module-boundary-types': WARNING,
'@typescript-eslint/ban-ts-comment': [ '@typescript-eslint/ban-ts-comment': [
ERROR, ERROR,
{'ts-expect-error': 'allow-with-description'}, {'ts-expect-error': 'allow-with-description'},
], ],
// TODO re-enable some these as errors
// context: https://github.com/facebook/docusaurus/pull/2949
'@typescript-eslint/ban-types': WARNING,
'import/no-extraneous-dependencies': ERROR, 'import/no-extraneous-dependencies': ERROR,
'no-useless-escape': WARNING, 'no-useless-escape': WARNING,
'prefer-template': WARNING, 'prefer-template': WARNING,
@ -189,6 +194,12 @@ module.exports = {
'import/no-duplicates': OFF, 'import/no-duplicates': OFF,
}, },
}, },
{
files: ['*.ts', '*.tsx'],
rules: {
'import/no-import-module-exports': OFF,
},
},
{ {
files: ['*.js'], files: ['*.js'],
rules: { rules: {

View file

@ -22,12 +22,10 @@ async function getPackagesJsonFiles(): Promise<PackageJsonFile[]> {
const files = await glob('packages/*/package.json'); const files = await glob('packages/*/package.json');
return Promise.all( return Promise.all(
files.map(async (file) => { files.map(async (file) => ({
return {
file, file,
content: JSON.parse(await readFile(file, 'utf8')), content: JSON.parse(await readFile(file, 'utf8')),
}; })),
}),
); );
} }

View file

@ -52,7 +52,7 @@ export function createPlaygroundResponse(
// Inspired by https://stackoverflow.com/a/3409200/82609 // Inspired by https://stackoverflow.com/a/3409200/82609
function parseCookieString(cookieString: string): Record<string, string> { function parseCookieString(cookieString: string): Record<string, string> {
const result: Record<string, string> = {}; const result: Record<string, string> = {};
cookieString.split(';').forEach(function (cookie) { cookieString.split(';').forEach((cookie) => {
const [name, value] = cookie.split('='); const [name, value] = cookie.split('=');
result[name.trim()] = decodeURI(value); result[name.trim()] = decodeURI(value);
}); });

View file

@ -58,13 +58,13 @@
"lock:update": "npx yarn-deduplicate" "lock:update": "npx yarn-deduplicate"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.15.7", "@babel/cli": "^7.16.0",
"@babel/core": "^7.12.16", "@babel/core": "^7.16.0",
"@babel/eslint-parser": "^7.15.7", "@babel/eslint-parser": "^7.16.3",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.13", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0",
"@babel/plugin-proposal-optional-chaining": "^7.12.16", "@babel/plugin-proposal-optional-chaining": "^7.16.0",
"@babel/plugin-transform-modules-commonjs": "^7.12.13", "@babel/plugin-transform-modules-commonjs": "^7.16.0",
"@babel/preset-typescript": "^7.12.16", "@babel/preset-typescript": "^7.16.0",
"@crowdin/cli": "^3.7.1", "@crowdin/cli": "^3.7.1",
"@formatjs/intl-datetimeformat": "^3.2.12", "@formatjs/intl-datetimeformat": "^3.2.12",
"@formatjs/intl-numberformat": "^6.2.2", "@formatjs/intl-numberformat": "^6.2.2",
@ -87,18 +87,18 @@
"@types/shelljs": "^0.8.6", "@types/shelljs": "^0.8.6",
"@types/wait-on": "^5.2.0", "@types/wait-on": "^5.2.0",
"@types/webpack-dev-server": "^4.1.0", "@types/webpack-dev-server": "^4.1.0",
"@typescript-eslint/eslint-plugin": "^4.18.0", "@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^4.18.0", "@typescript-eslint/parser": "^5.4.0",
"concurrently": "^6.2.1", "concurrently": "^6.2.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^7.20.0", "eslint": "^8.2.0",
"eslint-config-airbnb": "^18.2.1", "eslint-config-airbnb": "^19.0.0",
"eslint-config-prettier": "^6.15.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-header": "^3.0.0", "eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.21.5", "eslint-plugin-react": "^7.27.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.3.0",
"glob": "^7.1.6", "glob": "^7.1.6",
"husky": "^5.0.9", "husky": "^5.0.9",
"is-ci": "^3.0.0", "is-ci": "^3.0.0",
@ -117,7 +117,7 @@
"serve": "^12.0.1", "serve": "^12.0.1",
"stylelint": "^13.10.0", "stylelint": "^13.10.0",
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typescript": "^4.1.5" "typescript": "^4.5.2"
}, },
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": [ "*.{js,jsx,ts,tsx}": [

View file

@ -42,7 +42,12 @@ program
.option('--typescript') .option('--typescript')
.description('Initialize website.') .description('Initialize website.')
.action( .action(
(siteName, template, rootDir = '.', {useNpm, skipInstall, typescript}) => { (
siteName,
template,
rootDir = '.',
{useNpm, skipInstall, typescript} = {},
) => {
wrapCommand(init)(path.resolve(rootDir), siteName, template, { wrapCommand(init)(path.resolve(rootDir), siteName, template, {
useNpm, useNpm,
skipInstall, skipInstall,

View file

@ -29,7 +29,7 @@
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.9", "@docusaurus/module-type-aliases": "2.0.0-beta.9",
"@tsconfig/docusaurus": "^1.0.4", "@tsconfig/docusaurus": "^1.0.4",
"typescript": "^4.3.5" "typescript": "^4.5.2"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

View file

@ -23,7 +23,7 @@ module.exports = {
parserOptions: { parserOptions: {
allowImportExportEverywhere: true, allowImportExportEverywhere: true,
}, },
extends: ['airbnb', 'prettier', 'prettier/react'], extends: ['airbnb', 'prettier'],
plugins: ['react-hooks', 'header'], plugins: ['react-hooks', 'header'],
rules: { rules: {
// Ignore certain webpack alias because it can't be resolved // Ignore certain webpack alias because it can't be resolved
@ -56,5 +56,12 @@ module.exports = {
'react/jsx-filename-extension': OFF, 'react/jsx-filename-extension': OFF,
'react-hooks/rules-of-hooks': ERROR, 'react-hooks/rules-of-hooks': ERROR,
'react/prop-types': OFF, // PropTypes aren't used much these days. 'react/prop-types': OFF, // PropTypes aren't used much these days.
'react/function-component-definition': [
WARNING,
{
namedComponents: 'function-declaration',
unnamedComponents: 'arrow-function',
},
],
}, },
}; };

View file

@ -30,15 +30,15 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.15.7", "@babel/eslint-parser": "^7.15.7",
"eslint": "^7.20.0", "eslint": "^8.2.0",
"eslint-config-airbnb": "^18.2.1", "eslint-config-airbnb": "^19.0.0",
"eslint-config-prettier": "^6.15.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-header": "^3.0.0", "eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.21.5", "eslint-plugin-react": "^7.27.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.3.0",
"prettier": "^2.2.1", "prettier": "^2.4.1",
"stylelint": "^13.2.1" "stylelint": "^13.2.1"
}, },
"browserslist": { "browserslist": {

View file

@ -38,7 +38,7 @@ cli
.option('--mdx', 'try to migrate MD to MDX too') .option('--mdx', 'try to migrate MD to MDX too')
.option('--page', 'try to migrate pages too') .option('--page', 'try to migrate pages too')
.description('Migrate between versions of Docusaurus website.') .description('Migrate between versions of Docusaurus website.')
.action((siteDir = '.', newDir = '.', {mdx, page}) => { .action((siteDir = '.', newDir = '.', {mdx, page} = {}) => {
const sitePath = path.resolve(siteDir); const sitePath = path.resolve(siteDir);
const newSitePath = path.resolve(newDir); const newSitePath = path.resolve(newDir);
wrapCommand(migrateDocusaurusProject)(sitePath, newSitePath, mdx, page); wrapCommand(migrateDocusaurusProject)(sitePath, newSitePath, mdx, page);

View file

@ -47,11 +47,10 @@ function sanitizedFileContent(
): string { ): string {
const extractedData = extractMetadata(content); const extractedData = extractMetadata(content);
const extractedMetaData = Object.entries(extractedData.metadata).reduce( const extractedMetaData = Object.entries(extractedData.metadata).reduce(
(metaData, [key, value]) => { (metaData, [key, value]) =>
return `${metaData}\n${key}: ${ `${metaData}\n${key}: ${
shouldQuotifyFrontMatter([key, value]) ? `"${value}"` : value shouldQuotifyFrontMatter([key, value]) ? `"${value}"` : value
}`; }`,
},
'', '',
); );
const sanitizedData = `---${extractedMetaData}\n---\n${ const sanitizedData = `---${extractedMetaData}\n---\n${
@ -618,8 +617,7 @@ function migrateVersionedSidebar(
const newSidebar = Object.entries(sidebar.entries).reduce( const newSidebar = Object.entries(sidebar.entries).reduce(
(acc: SidebarEntries, val) => { (acc: SidebarEntries, val) => {
const key = `version-${sidebar.version}/${val[0]}`; const key = `version-${sidebar.version}/${val[0]}`;
acc[key] = Object.entries(val[1]).map((value) => { acc[key] = Object.entries(val[1]).map((value) => ({
return {
type: 'category', type: 'category',
label: value[0], label: value[0],
items: (value[1] as Array<SidebarEntry>).map((sidebarItem) => { items: (value[1] as Array<SidebarEntry>).map((sidebarItem) => {
@ -638,8 +636,7 @@ function migrateVersionedSidebar(
})), })),
}; };
}), }),
}; }));
});
return acc; return acc;
}, },
{}, {},

View file

@ -131,10 +131,8 @@ export default function transformer(file: string): string {
type: 'Identifier', type: 'Identifier',
}, },
}) })
.filter(function (p) { .filter((p) => p.parentPath.parentPath.name === 'body')
return p.parentPath.parentPath.name === 'body'; .forEach((p) => {
})
.forEach(function (p) {
const exportDecl = jscodeshift.exportDeclaration( const exportDecl = jscodeshift.exportDeclaration(
true, true,
jscodeshift.arrowFunctionExpression( jscodeshift.arrowFunctionExpression(
@ -177,9 +175,7 @@ function getDefaultImportDeclarators(rootAst: Collection) {
}, },
}, },
}) })
.filter((variableDeclarator) => { .filter((variableDeclarator) => !!variableDeclarator.value);
return !!variableDeclarator.value;
});
} }
function getNamedImportDeclarators(rootAst: Collection) { function getNamedImportDeclarators(rootAst: Collection) {

View file

@ -220,12 +220,10 @@ describe('collectRedirects', () => {
collectRedirects( collectRedirects(
createTestPluginContext( createTestPluginContext(
{ {
createRedirects: (routePath) => { createRedirects: (routePath) => [
return [
`${removeTrailingSlash(routePath)}/some/path/suffix1`, `${removeTrailingSlash(routePath)}/some/path/suffix1`,
`${removeTrailingSlash(routePath)}/some/other/path/suffix2`, `${removeTrailingSlash(routePath)}/some/other/path/suffix2`,
]; ],
},
}, },
['/', '/testpath', '/otherPath.html'], ['/', '/testpath', '/otherPath.html'],
), ),

View file

@ -28,9 +28,7 @@ describe('normalizePluginOptions', () => {
}); });
test('should override all default options with valid user options', () => { test('should override all default options with valid user options', () => {
const createRedirects: CreateRedirectsFnOption = (_routePath: string) => { const createRedirects: CreateRedirectsFnOption = (_routePath: string) => [];
return [];
};
expect( expect(
normalizePluginOptions({ normalizePluginOptions({
fromExtensions: ['exe', 'zip'], fromExtensions: ['exe', 'zip'],

View file

@ -47,12 +47,10 @@ function applyRedirectsTrailingSlash(
redirects: RedirectMetadata[], redirects: RedirectMetadata[],
params: ApplyTrailingSlashParams, params: ApplyTrailingSlashParams,
) { ) {
return redirects.map((redirect) => { return redirects.map((redirect) => ({
return {
...redirect, ...redirect,
to: applyTrailingSlash(redirect.to, params), to: applyTrailingSlash(redirect.to, params),
}; }));
});
} }
function validateCollectedRedirects( function validateCollectedRedirects(
@ -181,12 +179,10 @@ function createCreateRedirectsOptionRedirects(
const froms: string[] = const froms: string[] =
typeof fromsMixed === 'string' ? [fromsMixed] : fromsMixed; typeof fromsMixed === 'string' ? [fromsMixed] : fromsMixed;
return froms.map((from) => { return froms.map((from) => ({
return {
from, from,
to: path, to: path,
}; }));
});
} }
return paths.flatMap(createPathRedirects); return paths.flatMap(createPathRedirects);

View file

@ -13,9 +13,9 @@ type CreateRedirectPageOptions = {
toUrl: string; toUrl: string;
}; };
const getCompiledRedirectPageTemplate = memoize(() => { const getCompiledRedirectPageTemplate = memoize(() =>
return eta.compile(redirectPageTemplate.trim()); eta.compile(redirectPageTemplate.trim()),
}); );
function renderRedirectPageTemplate(data: Record<string, unknown>) { function renderRedirectPageTemplate(data: Record<string, unknown>) {
const compiled = getCompiledRedirectPageTemplate(); const compiled = getCompiledRedirectPageTemplate();

View file

@ -60,9 +60,9 @@ export function toRedirectFilesMetadata(
// Perf: avoid rendering the template twice with the exact same "props" // Perf: avoid rendering the template twice with the exact same "props"
// We might create multiple redirect pages for the same destination url // We might create multiple redirect pages for the same destination url
// note: the first fn arg is the cache key! // note: the first fn arg is the cache key!
const createPageContentMemoized = memoize((toUrl: string) => { const createPageContentMemoized = memoize((toUrl: string) =>
return createRedirectPageContent({toUrl}); createRedirectPageContent({toUrl}),
}); );
const createFileMetadata = (redirect: RedirectMetadata) => { const createFileMetadata = (redirect: RedirectMetadata) => {
const fileRelativePath = getRedirectFilePath(redirect.from, trailingSlash); const fileRelativePath = getRedirectFilePath(redirect.from, trailingSlash);

View file

@ -39,7 +39,6 @@ const BlogPostFrontMatterAuthorSchema = Joi.object({
.rename('image_url', 'imageURL', {alias: true}); .rename('image_url', 'imageURL', {alias: true});
export type BlogPostFrontMatter = { export type BlogPostFrontMatter = {
/* eslint-disable camelcase */
id?: string; id?: string;
title?: string; title?: string;
description?: string; description?: string;
@ -68,7 +67,6 @@ export type BlogPostFrontMatter = {
hide_table_of_contents?: boolean; hide_table_of_contents?: boolean;
toc_min_heading_level?: number; toc_min_heading_level?: number;
toc_max_heading_level?: number; toc_max_heading_level?: number;
/* eslint-enable camelcase */
}; };
const FrontMatterAuthorErrorMessage = const FrontMatterAuthorErrorMessage =

View file

@ -52,13 +52,11 @@ export function getBlogTags(blogPosts: BlogPost[]): BlogTags {
blogPosts, blogPosts,
(blogPost) => blogPost.metadata.tags, (blogPost) => blogPost.metadata.tags,
); );
return mapValues(groups, (group) => { return mapValues(groups, (group) => ({
return {
name: group.tag.label, name: group.tag.label,
items: group.items.map((item) => item.id), items: group.items.map((item) => item.id),
permalink: group.tag.permalink, permalink: group.tag.permalink,
}; }));
});
} }
const DATE_FILENAME_REGEX = const DATE_FILENAME_REGEX =
@ -112,9 +110,8 @@ async function parseBlogPostMarkdownFile(blogSourceAbsolute: string) {
}; };
} }
const defaultReadingTime: ReadingTimeFunction = ({content, options}) => { const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
return readingTime(content, options).minutes; readingTime(content, options).minutes;
};
async function processBlogSourceFile( async function processBlogSourceFile(
blogSourceRelative: string, blogSourceRelative: string,

View file

@ -118,7 +118,7 @@ export async function createBlogFeedFiles({
} }
await Promise.all( await Promise.all(
feedTypes.map(async function (feedType) { feedTypes.map(async (feedType) => {
await createBlogFeedFile({ await createBlogFeedFile({
feed, feed,
feedType, feedType,

View file

@ -322,9 +322,9 @@ export default function pluginContentBlog(
exact: true, exact: true,
modules: { modules: {
sidebar: aliasedSource(sidebarProp), sidebar: aliasedSource(sidebarProp),
items: items.map((postID) => { items: items.map((postID) =>
// To tell routes.js this is an import and not a nested object to recurse. // To tell routes.js this is an import and not a nested object to recurse.
return { ({
content: { content: {
__import: true, __import: true,
path: blogItemsToMetadata[postID].source, path: blogItemsToMetadata[postID].source,
@ -332,8 +332,8 @@ export default function pluginContentBlog(
truncated: true, truncated: true,
}, },
}, },
};
}), }),
),
metadata: aliasedSource(pageMetadataPath), metadata: aliasedSource(pageMetadataPath),
}, },
}); });
@ -490,14 +490,12 @@ export default function pluginContentBlog(
}: { }: {
frontMatter: BlogPostFrontMatter; frontMatter: BlogPostFrontMatter;
metadata: MetaData; metadata: MetaData;
}): Assets => { }): Assets => ({
return {
image: frontMatter.image, image: frontMatter.image,
authorsImageUrls: metadata.authors.map( authorsImageUrls: metadata.authors.map(
(author) => author.imageURL, (author) => author.imageURL,
), ),
}; }),
},
}, },
}, },
{ {

View file

@ -600,11 +600,9 @@ describe('versioned site, pluginId=default', () => {
test('readVersionsMetadata versioned site with invalid versions.json file', async () => { test('readVersionsMetadata versioned site with invalid versions.json file', async () => {
const {defaultOptions, defaultContext} = await loadSite(); const {defaultOptions, defaultContext} = await loadSite();
const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => { const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => ({
return {
invalid: 'json', invalid: 'json',
}; }));
});
expect(() => { expect(() => {
readVersionsMetadata({ readVersionsMetadata({

View file

@ -31,13 +31,12 @@ export function getActivePlugin(
options: GetActivePluginOptions = {}, options: GetActivePluginOptions = {},
): ActivePlugin | undefined { ): ActivePlugin | undefined {
const activeEntry = Object.entries(allPluginDatas).find( const activeEntry = Object.entries(allPluginDatas).find(
([_id, pluginData]) => { ([_id, pluginData]) =>
return !!matchPath(pathname, { !!matchPath(pathname, {
path: pluginData.path, path: pluginData.path,
exact: false, exact: false,
strict: false, strict: false,
}); }),
},
); );
const activePlugin: ActivePlugin | undefined = activeEntry const activePlugin: ActivePlugin | undefined = activeEntry
@ -63,9 +62,8 @@ export type ActiveDocContext = {
alternateDocVersions: Record<string, Doc>; alternateDocVersions: Record<string, Doc>;
}; };
export const getLatestVersion = (data: GlobalPluginData): Version => { export const getLatestVersion = (data: GlobalPluginData): Version =>
return data.versions.find((version) => version.isLast)!; data.versions.find((version) => version.isLast)!;
};
// Note: return undefined on doc-unrelated pages, // Note: return undefined on doc-unrelated pages,
// because there's no version currently considered as active // because there's no version currently considered as active
@ -80,13 +78,14 @@ export const getActiveVersion = (
...data.versions.filter((version) => version !== lastVersion), ...data.versions.filter((version) => version !== lastVersion),
lastVersion, lastVersion,
]; ];
return orderedVersionsMetadata.find((version) => { return orderedVersionsMetadata.find(
return !!matchPath(pathname, { (version) =>
!!matchPath(pathname, {
path: version.path, path: version.path,
exact: false, exact: false,
strict: false, strict: false,
}); }),
}); );
}; };
export const getActiveDocContext = ( export const getActiveDocContext = (

View file

@ -70,12 +70,10 @@ declare module '@theme/DocItem' {
readonly title: string; readonly title: string;
readonly image?: string; readonly image?: string;
readonly keywords?: readonly string[]; readonly keywords?: readonly string[];
/* eslint-disable camelcase */
readonly hide_title?: boolean; readonly hide_title?: boolean;
readonly hide_table_of_contents?: boolean; readonly hide_table_of_contents?: boolean;
readonly toc_min_heading_level?: number; readonly toc_min_heading_level?: number;
readonly toc_max_heading_level?: number; readonly toc_max_heading_level?: number;
/* eslint-enable camelcase */
}; };
export type Metadata = { export type Metadata = {

View file

@ -36,14 +36,16 @@ describe('DefaultSidebarItemsGenerator', () => {
function mockCategoryMetadataFiles( function mockCategoryMetadataFiles(
categoryMetadataFiles: Record<string, Partial<CategoryMetadataFile>>, categoryMetadataFiles: Record<string, Partial<CategoryMetadataFile>>,
) { ) {
jest.spyOn(fs, 'pathExists').mockImplementation((metadataFilePath) => { jest
return typeof categoryMetadataFiles[metadataFilePath] !== 'undefined'; .spyOn(fs, 'pathExists')
}); .mockImplementation(
(metadataFilePath) =>
typeof categoryMetadataFiles[metadataFilePath] !== 'undefined',
);
jest.spyOn(fs, 'readFile').mockImplementation( jest.spyOn(fs, 'readFile').mockImplementation(
// @ts-expect-error: annoying TS error due to overrides // @ts-expect-error: annoying TS error due to overrides
async (metadataFilePath: string) => { async (metadataFilePath: string) =>
return JSON.stringify(categoryMetadataFiles[metadataFilePath]); JSON.stringify(categoryMetadataFiles[metadataFilePath]),
},
); );
} }

View file

@ -21,9 +21,7 @@ describe('processSidebars', () => {
]; ];
const StaticSidebarItemsGenerator: SidebarItemsGenerator = jest.fn( const StaticSidebarItemsGenerator: SidebarItemsGenerator = jest.fn(
async () => { async () => StaticGeneratedSidebarSlice,
return StaticGeneratedSidebarSlice;
},
); );
async function testProcessSidebars(unprocessedSidebars: NormalizedSidebars) { async function testProcessSidebars(unprocessedSidebars: NormalizedSidebars) {

View file

@ -73,9 +73,9 @@ export function collectSidebarLinks(sidebar: Sidebar): SidebarItemLink[] {
export function collectSidebarsDocIds( export function collectSidebarsDocIds(
sidebars: Sidebars, sidebars: Sidebars,
): Record<string, string[]> { ): Record<string, string[]> {
return mapValues(sidebars, (sidebar) => { return mapValues(sidebars, (sidebar) =>
return collectSidebarDocItems(sidebar).map((docItem) => docItem.id); collectSidebarDocItems(sidebar).map((docItem) => docItem.id),
}); );
} }
export function createSidebarsUtils(sidebars: Sidebars): { export function createSidebarsUtils(sidebars: Sidebars): {

View file

@ -11,11 +11,9 @@ import {mapValues} from 'lodash';
export function getVersionTags(docs: DocMetadata[]): VersionTags { export function getVersionTags(docs: DocMetadata[]): VersionTags {
const groups = groupTaggedItems(docs, (doc) => doc.tags); const groups = groupTaggedItems(docs, (doc) => doc.tags);
return mapValues(groups, (group) => { return mapValues(groups, (group) => ({
return {
name: group.tag.label, name: group.tag.label,
docIds: group.items.map((item) => item.id), docIds: group.items.map((item) => item.id),
permalink: group.tag.permalink, permalink: group.tag.permalink,
}; }));
});
} }

View file

@ -164,16 +164,16 @@ function translateSidebars(
version: LoadedVersion, version: LoadedVersion,
sidebarsTranslations: TranslationFileContent, sidebarsTranslations: TranslationFileContent,
): Sidebars { ): Sidebars {
return mapValues(version.sidebars, (sidebar, sidebarName) => { return mapValues(version.sidebars, (sidebar, sidebarName) =>
return translateSidebar({ translateSidebar({
sidebar, sidebar,
sidebarName: getNormalizedSidebarName({ sidebarName: getNormalizedSidebarName({
sidebarName, sidebarName,
versionName: version.versionName, versionName: version.versionName,
}), }),
sidebarsTranslations, sidebarsTranslations,
}); }),
}); );
} }
function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles { function getVersionTranslationFiles(version: LoadedVersion): TranslationFiles {

View file

@ -113,7 +113,6 @@ export type LastUpdateData = {
export type DocFrontMatter = { export type DocFrontMatter = {
// Front matter uses snake case // Front matter uses snake case
/* eslint-disable camelcase */
id?: string; id?: string;
title?: string; title?: string;
tags?: FrontMatterTag[]; tags?: FrontMatterTag[];
@ -133,7 +132,6 @@ export type DocFrontMatter = {
toc_max_heading_level?: number; toc_max_heading_level?: number;
pagination_next?: string | null; pagination_next?: string | null;
pagination_prev?: string | null; pagination_prev?: string | null;
/* eslint-enable camelcase */
}; };
export type DocMetadataBase = LastUpdateData & { export type DocMetadataBase = LastUpdateData & {

View file

@ -18,11 +18,9 @@ declare module '@theme/MDXPage' {
readonly title: string; readonly title: string;
readonly description: string; readonly description: string;
readonly wrapperClassName?: string; readonly wrapperClassName?: string;
/* eslint-disable camelcase */
readonly hide_table_of_contents?: string; readonly hide_table_of_contents?: string;
readonly toc_min_heading_level?: number; readonly toc_min_heading_level?: number;
readonly toc_max_heading_level?: number; readonly toc_max_heading_level?: number;
/* eslint-enable camelcase */
}; };
readonly metadata: {readonly permalink: string}; readonly metadata: {readonly permalink: string};
readonly toc: readonly TOCItem[]; readonly toc: readonly TOCItem[];

View file

@ -11,26 +11,28 @@ import DebugLayout from '@theme/DebugLayout';
import DebugJsonView from '@theme/DebugJsonView'; import DebugJsonView from '@theme/DebugJsonView';
import type {Props} from '@theme/DebugContent'; import type {Props} from '@theme/DebugContent';
const PluginInstanceContent = ({ function PluginInstanceContent({
pluginId, pluginId,
pluginInstanceContent, pluginInstanceContent,
}: { }: {
pluginId: string; pluginId: string;
pluginInstanceContent: unknown; pluginInstanceContent: unknown;
}) => ( }) {
return (
<section style={{marginBottom: 30}}> <section style={{marginBottom: 30}}>
<code>{pluginId}</code> <code>{pluginId}</code>
<DebugJsonView src={pluginInstanceContent} collapseDepth={2} /> <DebugJsonView src={pluginInstanceContent} collapseDepth={2} />
</section> </section>
); );
}
const PluginContent = ({ function PluginContent({
pluginName, pluginName,
pluginContent, pluginContent,
}: { }: {
pluginName: string; pluginName: string;
pluginContent: Record<string, unknown>; pluginContent: Record<string, unknown>;
}) => { }) {
return ( return (
<section style={{marginBottom: 60}}> <section style={{marginBottom: 60}}>
<h3>{pluginName}</h3> <h3>{pluginName}</h3>
@ -40,19 +42,17 @@ const PluginContent = ({
.filter( .filter(
([_pluginId, pluginInstanceContent]) => !!pluginInstanceContent, ([_pluginId, pluginInstanceContent]) => !!pluginInstanceContent,
) )
.map(([pluginId, pluginInstanceContent]) => { .map(([pluginId, pluginInstanceContent]) => (
return (
<PluginInstanceContent <PluginInstanceContent
key={pluginId} key={pluginId}
pluginId={pluginId} pluginId={pluginId}
pluginInstanceContent={pluginInstanceContent} pluginInstanceContent={pluginInstanceContent}
/> />
); ))}
})}
</div> </div>
</section> </section>
); );
}; }
function DebugContent({allContent}: Props): JSX.Element { function DebugContent({allContent}: Props): JSX.Element {
return ( return (
@ -66,15 +66,13 @@ function DebugContent({allContent}: Props): JSX.Element {
(instanceContent) => !!instanceContent, (instanceContent) => !!instanceContent,
), ),
) )
.map(([pluginName, pluginContent]) => { .map(([pluginName, pluginContent]) => (
return (
<PluginContent <PluginContent
key={pluginName} key={pluginName}
pluginName={pluginName} pluginName={pluginName}
pluginContent={pluginContent} pluginContent={pluginContent}
/> />
); ))}
})}
</div> </div>
</DebugLayout> </DebugLayout>
); );

View file

@ -15,7 +15,7 @@ const RootName = null;
// Seems ReactJson does not work with SSR // Seems ReactJson does not work with SSR
// https://github.com/mac-s-g/react-json-view/issues/121 // https://github.com/mac-s-g/react-json-view/issues/121
const BrowserOnlyReactJson = (props: ReactJsonViewProps) => { function BrowserOnlyReactJson(props: ReactJsonViewProps) {
return ( return (
<BrowserOnly> <BrowserOnly>
{() => { {() => {
@ -25,13 +25,11 @@ const BrowserOnlyReactJson = (props: ReactJsonViewProps) => {
}} }}
</BrowserOnly> </BrowserOnly>
); );
}; }
function DebugJsonView({src, collapseDepth}: Props): JSX.Element { function DebugJsonView({src, collapseDepth}: Props): JSX.Element {
return ( return (
<BrowserOnlyReactJson <BrowserOnlyReactJson
// Prop type defined by react-json-view
// eslint-disable-next-line @typescript-eslint/ban-types
src={src as object} src={src as object}
style={{ style={{
marginTop: '10px', marginTop: '10px',
@ -41,12 +39,12 @@ function DebugJsonView({src, collapseDepth}: Props): JSX.Element {
}} }}
name={RootName} name={RootName}
theme="paraiso" theme="paraiso"
shouldCollapse={(field) => { shouldCollapse={(field) =>
// By default, we collapse the json for performance reasons // By default, we collapse the json for performance reasons
// See https://github.com/mac-s-g/react-json-view/issues/235 // See https://github.com/mac-s-g/react-json-view/issues/235
// Non-root elements that are larger than 50 fields are collapsed // Non-root elements that are larger than 50 fields are collapsed
return field.name !== RootName && Object.keys(field.src).length > 50; field.name !== RootName && Object.keys(field.src).length > 50
}} }
collapsed={collapseDepth} collapsed={collapseDepth}
groupArraysAfterLength={5} groupArraysAfterLength={5}
enableClipboard={false} enableClipboard={false}

View file

@ -10,7 +10,8 @@ import Head from '@docusaurus/Head';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';
import styles from './styles.module.css'; import styles from './styles.module.css';
const DebugNavLink = ({to, children}: {to: string; children: ReactNode}) => ( function DebugNavLink({to, children}: {to: string; children: ReactNode}) {
return (
<Link <Link
className={styles.navlink} className={styles.navlink}
isNavLink isNavLink
@ -21,7 +22,8 @@ const DebugNavLink = ({to, children}: {to: string; children: ReactNode}) => (
}}> }}>
{children} {children}
</Link> </Link>
); );
}
function DebugLayout({children}: {children: ReactNode}): JSX.Element { function DebugLayout({children}: {children: ReactNode}): JSX.Element {
return ( return (

View file

@ -8,7 +8,6 @@
/// <reference types="@docusaurus/module-type-aliases" /> /// <reference types="@docusaurus/module-type-aliases" />
interface Window { interface Window {
/* eslint-disable camelcase */
gtag: ( gtag: (
command: string, command: string,
fields: string, fields: string,
@ -18,5 +17,4 @@ interface Window {
page_path?: string; page_path?: string;
}, },
) => void; ) => void;
/* eslint-enable camelcase */
} }

View file

@ -27,13 +27,12 @@ describe('createSitemap', () => {
); );
}); });
test('empty site', () => { test('empty site', () =>
return expect(async () => { expect(async () => {
await createSitemap({} as DocusaurusConfig, [], {}); await createSitemap({} as DocusaurusConfig, [], {});
}).rejects.toThrow( }).rejects.toThrow(
'URL in docusaurus.config.js cannot be empty/undefined.', 'URL in docusaurus.config.js cannot be empty/undefined.',
); ));
});
test('exclusion of 404 page', async () => { test('exclusion of 404 page', async () => {
const sitemap = await createSitemap( const sitemap = await createSitemap(

View file

@ -27,8 +27,7 @@ const ThemeStorageKey = 'theme';
const noFlashColorMode = ({ const noFlashColorMode = ({
defaultMode, defaultMode,
respectPrefersColorScheme, respectPrefersColorScheme,
}: ThemeConfig['colorMode']) => { }: ThemeConfig['colorMode']) => `(function() {
return `(function() {
var defaultMode = '${defaultMode}'; var defaultMode = '${defaultMode}';
var respectPrefersColorScheme = ${respectPrefersColorScheme}; var respectPrefersColorScheme = ${respectPrefersColorScheme};
@ -63,7 +62,6 @@ const noFlashColorMode = ({
} }
} }
})();`; })();`;
};
// Duplicated constant. Unfortunately we can't import it from theme-common, as we need to support older nodejs versions without ESM support // Duplicated constant. Unfortunately we can't import it from theme-common, as we need to support older nodejs versions without ESM support
// TODO: import from theme-common once we only support Node.js with ESM support // TODO: import from theme-common once we only support Node.js with ESM support
@ -136,12 +134,11 @@ export default function docusaurusThemeClassic(
getTranslationFiles: async () => getTranslationFiles({themeConfig}), getTranslationFiles: async () => getTranslationFiles({themeConfig}),
translateThemeConfig, translateThemeConfig,
getDefaultCodeTranslationMessages: () => { getDefaultCodeTranslationMessages: () =>
return readDefaultCodeTranslationMessages({ readDefaultCodeTranslationMessages({
dirPath: path.resolve(__dirname, '..', 'codeTranslations'), dirPath: path.resolve(__dirname, '..', 'codeTranslations'),
locale: currentLocale, locale: currentLocale,
}); }),
},
getClientModules() { getClientModules() {
const modules = [ const modules = [

View file

@ -13,10 +13,13 @@ import BlogPostAuthor from '@theme/BlogPostAuthor';
import styles from './styles.module.css'; import styles from './styles.module.css';
// Component responsible for the authors layout // Component responsible for the authors layout
export default function BlogPostAuthors({authors, assets}: Props): JSX.Element { export default function BlogPostAuthors({
authors,
assets,
}: Props): JSX.Element | null {
const authorsCount = authors.length; const authorsCount = authors.length;
if (authorsCount === 0) { if (authorsCount === 0) {
return <></>; return null;
} }
return ( return (
<div className="row margin-top--md margin-bottom--sm"> <div className="row margin-top--md margin-bottom--sm">

View file

@ -28,8 +28,7 @@ export default function BlogSidebar({sidebar}: Props): JSX.Element | null {
{sidebar.title} {sidebar.title}
</div> </div>
<ul className={styles.sidebarItemList}> <ul className={styles.sidebarItemList}>
{sidebar.items.map((item) => { {sidebar.items.map((item) => (
return (
<li key={item.permalink} className={styles.sidebarItem}> <li key={item.permalink} className={styles.sidebarItem}>
<Link <Link
isNavLink isNavLink
@ -39,8 +38,7 @@ export default function BlogSidebar({sidebar}: Props): JSX.Element | null {
{item.title} {item.title}
</Link> </Link>
</li> </li>
); ))}
})}
</ul> </ul>
</nav> </nav>
); );

View file

@ -59,7 +59,7 @@ function EditMetaRow({
); );
} }
export default function DocItemFooter(props: Props): JSX.Element { export default function DocItemFooter(props: Props): JSX.Element | null {
const {content: DocContent} = props; const {content: DocContent} = props;
const {metadata} = DocContent; const {metadata} = DocContent;
const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} = const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
@ -71,7 +71,7 @@ export default function DocItemFooter(props: Props): JSX.Element {
const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow; const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;
if (!canDisplayFooter) { if (!canDisplayFooter) {
return <></>; return null;
} }
return ( return (

View file

@ -90,12 +90,12 @@ function DocSidebarDesktop({path, sidebar, onCollapse, isHidden}: Props) {
); );
} }
// eslint-disable-next-line react/function-component-definition
const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({ const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
toggleSidebar, toggleSidebar,
sidebar, sidebar,
path, path,
}) => { }) => (
return (
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}> <ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
<DocSidebarItems <DocSidebarItems
items={sidebar} items={sidebar}
@ -104,8 +104,7 @@ const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
level={1} level={1}
/> />
</ul> </ul>
); );
};
function DocSidebarMobile(props: Props) { function DocSidebarMobile(props: Props) {
return ( return (

View file

@ -44,11 +44,8 @@ const isActiveSidebarItem = (
// Optimize sidebar at each "level" // Optimize sidebar at each "level"
// TODO this item should probably not receive the "activePath" props // TODO this item should probably not receive the "activePath" props
// TODO this triggers whole sidebar re-renders on navigation // TODO this triggers whole sidebar re-renders on navigation
export const DocSidebarItems = memo(function DocSidebarItems({ export const DocSidebarItems = memo(
items, ({items, ...props}: DocSidebarItemsProps): JSX.Element => (
...props
}: DocSidebarItemsProps): JSX.Element {
return (
<> <>
{items.map((item, index) => ( {items.map((item, index) => (
<DocSidebarItem <DocSidebarItem
@ -58,8 +55,8 @@ export const DocSidebarItems = memo(function DocSidebarItems({
/> />
))} ))}
</> </>
); ),
}); );
export default function DocSidebarItem({ export default function DocSidebarItem({
item, item,

View file

@ -154,11 +154,11 @@ function DocVersionBannerEnabled({versionMetadata}: Props): JSX.Element {
); );
} }
function DocVersionBanner({versionMetadata}: Props): JSX.Element { function DocVersionBanner({versionMetadata}: Props): JSX.Element | null {
if (versionMetadata.banner) { if (versionMetadata.banner) {
return <DocVersionBannerEnabled versionMetadata={versionMetadata} />; return <DocVersionBannerEnabled versionMetadata={versionMetadata} />;
} }
return <></>; return null;
} }
export default DocVersionBanner; export default DocVersionBanner;

View file

@ -49,12 +49,13 @@ function FooterLink({
); );
} }
const FooterLogo = ({ function FooterLogo({
sources, sources,
alt, alt,
width, width,
height, height,
}: Pick<ThemedImageProps, 'sources' | 'alt' | 'width' | 'height'>) => ( }: Pick<ThemedImageProps, 'sources' | 'alt' | 'width' | 'height'>) {
return (
<ThemedImage <ThemedImage
className="footer__logo" className="footer__logo"
alt={alt} alt={alt}
@ -62,7 +63,8 @@ const FooterLogo = ({
width={width} width={width}
height={height} height={height}
/> />
); );
}
function Footer(): JSX.Element | null { function Footer(): JSX.Element | null {
const {footer} = useThemeConfig(); const {footer} = useThemeConfig();

View file

@ -16,8 +16,8 @@ import styles from './styles.module.css';
type HeadingComponent = (props: Props) => JSX.Element; type HeadingComponent = (props: Props) => JSX.Element;
export const MainHeading: HeadingComponent = function MainHeading({...props}) { // eslint-disable-next-line react/function-component-definition
return ( export const MainHeading: HeadingComponent = ({...props}) => (
<header> <header>
<h1 <h1
{...props} {...props}
@ -26,13 +26,11 @@ export const MainHeading: HeadingComponent = function MainHeading({...props}) {
{props.children} {props.children}
</h1> </h1>
</header> </header>
); );
};
const createAnchorHeading = ( const createAnchorHeading =
Tag: HeadingType, (Tag: HeadingType) =>
): ((props: Props) => JSX.Element) => ({id, ...props}: Props) => {
function TargetComponent({id, ...props}) {
const { const {
navbar: {hideOnScroll}, navbar: {hideOnScroll},
} = useThemeConfig(); } = useThemeConfig();
@ -65,8 +63,7 @@ const createAnchorHeading = (
); );
}; };
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) => { const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) =>
return headingType === 'h1' ? MainHeading : createAnchorHeading(headingType); headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
};
export default Heading; export default Heading;

View file

@ -8,7 +8,7 @@
import React from 'react'; import React from 'react';
import type {Props} from '@theme/IconArrow'; import type {Props} from '@theme/IconArrow';
const IconArrow = (props: Props): JSX.Element => { function IconArrow(props: Props): JSX.Element {
return ( return (
<svg width="20" height="20" aria-hidden="true" {...props}> <svg width="20" height="20" aria-hidden="true" {...props}>
<g fill="#7a7a7a"> <g fill="#7a7a7a">
@ -17,6 +17,6 @@ const IconArrow = (props: Props): JSX.Element => {
</g> </g>
</svg> </svg>
); );
}; }
export default IconArrow; export default IconArrow;

View file

@ -12,7 +12,7 @@ import type {Props} from '@theme/IconEdit';
import styles from './styles.module.css'; import styles from './styles.module.css';
const IconEdit = ({className, ...restProps}: Props): JSX.Element => { function IconEdit({className, ...restProps}: Props): JSX.Element {
return ( return (
<svg <svg
fill="currentColor" fill="currentColor"
@ -27,6 +27,6 @@ const IconEdit = ({className, ...restProps}: Props): JSX.Element => {
</g> </g>
</svg> </svg>
); );
}; }
export default IconEdit; export default IconEdit;

View file

@ -10,10 +10,7 @@ import type {Props} from '@theme/IconExternalLink';
import styles from './styles.module.css'; import styles from './styles.module.css';
const IconExternalLink = ({ function IconExternalLink({width = 13.5, height = 13.5}: Props): JSX.Element {
width = 13.5,
height = 13.5,
}: Props): JSX.Element => {
return ( return (
<svg <svg
width={width} width={width}
@ -27,6 +24,6 @@ const IconExternalLink = ({
/> />
</svg> </svg>
); );
}; }
export default IconExternalLink; export default IconExternalLink;

View file

@ -8,11 +8,7 @@
import React from 'react'; import React from 'react';
import type {Props} from '@theme/IconLanguage'; import type {Props} from '@theme/IconLanguage';
const IconLanguage = ({ function IconLanguage({width = 20, height = 20, ...props}: Props): JSX.Element {
width = 20,
height = 20,
...props
}: Props): JSX.Element => {
return ( return (
<svg <svg
viewBox="0 0 20 20" viewBox="0 0 20 20"
@ -26,6 +22,6 @@ const IconLanguage = ({
/> />
</svg> </svg>
); );
}; }
export default IconLanguage; export default IconLanguage;

View file

@ -8,12 +8,12 @@
import React from 'react'; import React from 'react';
import type {Props} from '@theme/IconMenu'; import type {Props} from '@theme/IconMenu';
const IconMenu = ({ function IconMenu({
width = 30, width = 30,
height = 30, height = 30,
className, className,
...restProps ...restProps
}: Props): JSX.Element => { }: Props): JSX.Element {
return ( return (
<svg <svg
className={className} className={className}
@ -31,6 +31,6 @@ const IconMenu = ({
/> />
</svg> </svg>
); );
}; }
export default IconMenu; export default IconMenu;

View file

@ -101,7 +101,7 @@ export default function LayoutHead(props: Props): JSX.Element {
<> <>
<Head> <Head>
<html lang={htmlLang} dir={htmlDir} /> <html lang={htmlLang} dir={htmlDir} />
{favicon && <link rel="shortcut icon" href={faviconUrl} />} {favicon && <link rel="icon" href={faviconUrl} />}
<title>{pageTitle}</title> <title>{pageTitle}</title>
<meta property="og:title" content={pageTitle} /> <meta property="og:title" content={pageTitle} />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />

View file

@ -14,7 +14,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useThemeConfig} from '@docusaurus/theme-common'; import {useThemeConfig} from '@docusaurus/theme-common';
const Logo = (props: Props): JSX.Element => { function Logo(props: Props): JSX.Element {
const { const {
siteConfig: {title}, siteConfig: {title},
} = useDocusaurusContext(); } = useDocusaurusContext();
@ -46,11 +46,11 @@ const Logo = (props: Props): JSX.Element => {
(imageClassName ? ( (imageClassName ? (
<div className={imageClassName}>{themedImage}</div> <div className={imageClassName}>{themedImage}</div>
) : ( ) : (
<>{themedImage}</> themedImage
))} ))}
{navbarTitle != null && <b className={titleClassName}>{navbarTitle}</b>} {navbarTitle != null && <b className={titleClassName}>{navbarTitle}</b>}
</Link> </Link>
); );
}; }
export default Logo; export default Logo;

View file

@ -105,12 +105,13 @@ function useSecondaryMenu({
}); });
const previousContent = usePrevious(content); const previousContent = usePrevious(content);
const [shown, setShown] = useState<boolean>(() => { const [shown, setShown] = useState<boolean>(
() =>
// /!\ content is set with useEffect, // /!\ content is set with useEffect,
// so it's not available on mount anyway // so it's not available on mount anyway
// "return !!content" => always returns false // "return !!content" => always returns false
return false; false,
}); );
// When content is become available for the first time (set in useEffect) // When content is become available for the first time (set in useEffect)
// we set this content to be shown! // we set this content to be shown!

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React from 'react'; import React, {useMemo} from 'react';
import useTheme from '@theme/hooks/useTheme'; import useTheme from '@theme/hooks/useTheme';
import ThemeContext from '@theme/ThemeContext'; import ThemeContext from '@theme/ThemeContext';
@ -13,9 +13,13 @@ import type {Props} from '@theme/ThemeProvider';
function ThemeProvider(props: Props): JSX.Element { function ThemeProvider(props: Props): JSX.Element {
const {isDarkTheme, setLightTheme, setDarkTheme} = useTheme(); const {isDarkTheme, setLightTheme, setDarkTheme} = useTheme();
const contextValue = useMemo(
() => ({isDarkTheme, setLightTheme, setDarkTheme}),
[isDarkTheme, setLightTheme, setDarkTheme],
);
return ( return (
<ThemeContext.Provider value={{isDarkTheme, setLightTheme, setDarkTheme}}> <ThemeContext.Provider value={contextValue}>
{props.children} {props.children}
</ThemeContext.Provider> </ThemeContext.Provider>
); );

View file

@ -14,7 +14,7 @@ import type {Props} from '@theme/ThemedImage';
import styles from './styles.module.css'; import styles from './styles.module.css';
const ThemedImage = (props: Props): JSX.Element => { function ThemedImage(props: Props): JSX.Element {
const isBrowser = useIsBrowser(); const isBrowser = useIsBrowser();
const {isDarkTheme} = useThemeContext(); const {isDarkTheme} = useThemeContext();
const {sources, className, alt = '', ...propsRest} = props; const {sources, className, alt = '', ...propsRest} = props;
@ -46,6 +46,6 @@ const ThemedImage = (props: Props): JSX.Element => {
))} ))}
</> </>
); );
}; }
export default ThemedImage; export default ThemedImage;

View file

@ -18,16 +18,20 @@ interface IconProps {
style: CSSProperties; style: CSSProperties;
} }
const Dark = ({icon, style}: IconProps): JSX.Element => ( function Dark({icon, style}: IconProps): JSX.Element {
return (
<span className={clsx(styles.toggleIcon, styles.dark)} style={style}> <span className={clsx(styles.toggleIcon, styles.dark)} style={style}>
{icon} {icon}
</span> </span>
); );
const Light = ({icon, style}: IconProps): JSX.Element => ( }
function Light({icon, style}: IconProps): JSX.Element {
return (
<span className={clsx(styles.toggleIcon, styles.light)} style={style}> <span className={clsx(styles.toggleIcon, styles.light)} style={style}>
{icon} {icon}
</span> </span>
); );
}
// Based on react-toggle (https://github.com/aaronshaf/react-toggle/). // Based on react-toggle (https://github.com/aaronshaf/react-toggle/).
const Toggle = memo( const Toggle = memo(

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React from 'react'; import React, {useMemo} from 'react';
import useTabGroupChoice from '@theme/hooks/useTabGroupChoice'; import useTabGroupChoice from '@theme/hooks/useTabGroupChoice';
import UserPreferencesContext from '@theme/UserPreferencesContext'; import UserPreferencesContext from '@theme/UserPreferencesContext';
@ -13,12 +13,15 @@ import type {Props} from '@theme/UserPreferencesProvider';
function UserPreferencesProvider(props: Props): JSX.Element { function UserPreferencesProvider(props: Props): JSX.Element {
const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice(); const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
return ( const contextValue = useMemo(
<UserPreferencesContext.Provider () => ({
value={{
tabGroupChoices, tabGroupChoices,
setTabGroupChoices, setTabGroupChoices,
}}> }),
[tabGroupChoices, setTabGroupChoices],
);
return (
<UserPreferencesContext.Provider value={contextValue}>
{props.children} {props.children}
</UserPreferencesContext.Provider> </UserPreferencesContext.Provider>
); );

View file

@ -21,9 +21,8 @@ const themes = {
type Themes = typeof themes[keyof typeof themes]; type Themes = typeof themes[keyof typeof themes];
// Ensure to always return a valid theme even if input is invalid // Ensure to always return a valid theme even if input is invalid
const coerceToTheme = (theme?: string | null): Themes => { const coerceToTheme = (theme?: string | null): Themes =>
return theme === themes.dark ? themes.dark : themes.light; theme === themes.dark ? themes.dark : themes.light;
};
const getInitialTheme = (defaultMode: Themes | undefined): Themes => { const getInitialTheme = (defaultMode: Themes | undefined): Themes => {
if (!ExecutionEnvironment.canUseDOM) { if (!ExecutionEnvironment.canUseDOM) {

View file

@ -80,9 +80,10 @@ ${warning}
}); });
const translations = filesExtractedTranslations.reduce( const translations = filesExtractedTranslations.reduce(
(acc, extractedTranslations) => { (acc, extractedTranslations) => ({
return {...acc, ...extractedTranslations.translations}; ...acc,
}, ...extractedTranslations.translations,
}),
{}, {},
); );
@ -193,9 +194,7 @@ ${logKeys(unknownMessages)}`),
}; };
const untranslatedKeys = Object.entries(newLocaleFileMessages) const untranslatedKeys = Object.entries(newLocaleFileMessages)
.filter(([key, value]) => { .filter(([key, value]) => value === baseFileMessages[key])
return value === baseFileMessages[key];
})
.map(([key]) => key); .map(([key]) => key);
if (untranslatedKeys.length) { if (untranslatedKeys.length) {

View file

@ -29,7 +29,7 @@ export type DetailsProps = {
summary?: ReactElement; summary?: ReactElement;
} & ComponentProps<'details'>; } & ComponentProps<'details'>;
const Details = ({summary, children, ...props}: DetailsProps): JSX.Element => { function Details({summary, children, ...props}: DetailsProps): JSX.Element {
const isBrowser = useIsBrowser(); const isBrowser = useIsBrowser();
const detailsRef = useRef<HTMLDetailsElement>(null); const detailsRef = useRef<HTMLDetailsElement>(null);
@ -89,6 +89,6 @@ const Details = ({summary, children, ...props}: DetailsProps): JSX.Element => {
</Collapsible> </Collapsible>
</details> </details>
); );
}; }
export default Details; export default Details;

View file

@ -41,13 +41,13 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
const {announcementBar} = useThemeConfig(); const {announcementBar} = useThemeConfig();
const isBrowser = useIsBrowser(); const isBrowser = useIsBrowser();
const [isClosed, setClosed] = useState(() => { const [isClosed, setClosed] = useState(() =>
return isBrowser isBrowser
? // On client navigation: init with localstorage value ? // On client navigation: init with localstorage value
isDismissedInStorage() isDismissedInStorage()
: // On server/hydration: always visible to prevent layout shifts (will be hidden with css if needed) : // On server/hydration: always visible to prevent layout shifts (will be hidden with css if needed)
false; false,
}); );
// Update state after hydration // Update state after hydration
useEffect(() => { useEffect(() => {
setClosed(isDismissedInStorage()); setClosed(isDismissedInStorage());
@ -85,28 +85,29 @@ const useAnnouncementBarContextValue = (): AnnouncementBarAPI => {
} }
}, [announcementBar]); }, [announcementBar]);
return useMemo(() => { return useMemo(
return { () => ({
isActive: !!announcementBar && !isClosed, isActive: !!announcementBar && !isClosed,
close: handleClose, close: handleClose,
}; }),
}, [announcementBar, isClosed, handleClose]); [announcementBar, isClosed, handleClose],
);
}; };
const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null); const AnnouncementBarContext = createContext<AnnouncementBarAPI | null>(null);
export const AnnouncementBarProvider = ({ export function AnnouncementBarProvider({
children, children,
}: { }: {
children: ReactNode; children: ReactNode;
}): JSX.Element => { }): JSX.Element {
const value = useAnnouncementBarContextValue(); const value = useAnnouncementBarContextValue();
return ( return (
<AnnouncementBarContext.Provider value={value}> <AnnouncementBarContext.Provider value={value}>
{children} {children}
</AnnouncementBarContext.Provider> </AnnouncementBarContext.Provider>
); );
}; }
export const useAnnouncementBar = (): AnnouncementBarAPI => { export const useAnnouncementBar = (): AnnouncementBarAPI => {
const api = useContext(AnnouncementBarContext); const api = useContext(AnnouncementBarContext);

View file

@ -132,7 +132,7 @@ const Context = createContext<DocsPreferredVersionContextValue | null>(null);
export function DocsPreferredVersionContextProvider({ export function DocsPreferredVersionContextProvider({
children, children,
}: { }: {
children: ReactNode; children: JSX.Element;
}): JSX.Element { }): JSX.Element {
if (isDocsPluginEnabled) { if (isDocsPluginEnabled) {
return ( return (
@ -141,7 +141,7 @@ export function DocsPreferredVersionContextProvider({
</DocsPreferredVersionContextProviderUnsafe> </DocsPreferredVersionContextProviderUnsafe>
); );
} else { } else {
return <>{children}</>; return children;
} }
} }

View file

@ -22,9 +22,8 @@ const DocsPreferredVersionStorage = {
read: ( read: (
pluginId: string, pluginId: string,
persistence: DocsVersionPersistence, persistence: DocsVersionPersistence,
): string | null => { ): string | null =>
return createStorageSlot(storageKey(pluginId), {persistence}).get(); createStorageSlot(storageKey(pluginId), {persistence}).get(),
},
clear: (pluginId: string, persistence: DocsVersionPersistence): void => { clear: (pluginId: string, persistence: DocsVersionPersistence): void => {
createStorageSlot(storageKey(pluginId), {persistence}).del(); createStorageSlot(storageKey(pluginId), {persistence}).del();

View file

@ -24,12 +24,12 @@ export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
lastHandlerRef.current = handler; lastHandlerRef.current = handler;
}, [handler]); }, [handler]);
useEffect(() => { useEffect(
() =>
// See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md // See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md
return block((location, action) => { block((location, action) => lastHandlerRef.current(location, action)),
return lastHandlerRef.current(location, action); [block, lastHandlerRef],
}); );
}, [block, lastHandlerRef]);
} }
/* /*

View file

@ -29,7 +29,7 @@ type ExtraProps = {
toggleSidebar: () => void; toggleSidebar: () => void;
}; };
export type MobileSecondaryMenuComponent<Props extends unknown> = ComponentType< export type MobileSecondaryMenuComponent<Props> = ComponentType<
Props & ExtraProps Props & ExtraProps
>; >;
@ -108,9 +108,7 @@ export function MobileSecondaryMenuFiller<
setState({component, props: memoizedProps}); setState({component, props: memoizedProps});
}, [setState, component, memoizedProps]); }, [setState, component, memoizedProps]);
useEffect(() => { useEffect(() => () => setState(null), [setState]);
return () => setState(null);
}, [setState]);
return null; return null;
} }

View file

@ -10,8 +10,7 @@ export const isSamePath = (
path1: string | undefined, path1: string | undefined,
path2: string | undefined, path2: string | undefined,
): boolean => { ): boolean => {
const normalize = (pathname: string | undefined) => { const normalize = (pathname: string | undefined) =>
return !pathname || pathname?.endsWith('/') ? pathname : `${pathname}/`; !pathname || pathname?.endsWith('/') ? pathname : `${pathname}/`;
};
return normalize(path1) === normalize(path2); return normalize(path1) === normalize(path2);
}; };

View file

@ -83,14 +83,13 @@ export function useScrollController(): ScrollController {
return context; return context;
} }
const getScrollPosition = (): ScrollPosition | null => { const getScrollPosition = (): ScrollPosition | null =>
return ExecutionEnvironment.canUseDOM ExecutionEnvironment.canUseDOM
? { ? {
scrollX: window.pageXOffset, scrollX: window.pageXOffset,
scrollY: window.pageYOffset, scrollY: window.pageYOffset,
} }
: null; : null;
};
type ScrollPosition = {scrollX: number; scrollY: number}; type ScrollPosition = {scrollX: number; scrollY: number};

View file

@ -48,7 +48,8 @@ export function useTOCFilter({
minHeadingLevel, minHeadingLevel,
maxHeadingLevel, maxHeadingLevel,
}: FilterTOCParam): readonly TOCItem[] { }: FilterTOCParam): readonly TOCItem[] {
return useMemo(() => { return useMemo(
return filterTOC({toc, minHeadingLevel, maxHeadingLevel}); () => filterTOC({toc, minHeadingLevel, maxHeadingLevel}),
}, [toc, minHeadingLevel, maxHeadingLevel]); [toc, minHeadingLevel, maxHeadingLevel],
);
} }

View file

@ -112,8 +112,7 @@ export function usePluralForm(): {
} { } {
const localePluralForm = useLocalePluralForms(); const localePluralForm = useLocalePluralForms();
return { return {
selectMessage: (count: number, pluralMessages: string): string => { selectMessage: (count: number, pluralMessages: string): string =>
return selectPluralMessage(pluralMessages, count, localePluralForm); selectPluralMessage(pluralMessages, count, localePluralForm),
},
}; };
} }

View file

@ -11,13 +11,13 @@ import ReactLiveScope from '@theme/ReactLiveScope';
import CodeBlock from '@theme-init/CodeBlock'; import CodeBlock from '@theme-init/CodeBlock';
const withLiveEditor = (Component) => { const withLiveEditor = (Component) => {
const WrappedComponent = (props) => { function WrappedComponent(props) {
if (props.live) { if (props.live) {
return <Playground scope={ReactLiveScope} {...props} />; return <Playground scope={ReactLiveScope} {...props} />;
} }
return <Component {...props} />; return <Component {...props} />;
}; }
return WrappedComponent; return WrappedComponent;
}; };

View file

@ -14,9 +14,9 @@ import {memoize} from 'lodash';
import type {DocusaurusContext, Plugin} from '@docusaurus/types'; import type {DocusaurusContext, Plugin} from '@docusaurus/types';
const getCompiledOpenSearchTemplate = memoize(() => { const getCompiledOpenSearchTemplate = memoize(() =>
return compile(openSearchTemplate.trim()); compile(openSearchTemplate.trim()),
}); );
function renderOpenSearchTemplate(data: { function renderOpenSearchTemplate(data: {
title: string; title: string;

View file

@ -152,8 +152,8 @@ function DocSearch({
}).current; }).current;
const transformItems = useRef<DocSearchModalProps['transformItems']>( const transformItems = useRef<DocSearchModalProps['transformItems']>(
(items) => { (items) =>
return items.map((item) => { items.map((item) => {
// If Algolia contains a external domain, we should navigate without relative URL // If Algolia contains a external domain, we should navigate without relative URL
if (isRegexpStringMatch(externalUrlRegex, item.url)) { if (isRegexpStringMatch(externalUrlRegex, item.url)) {
return item; return item;
@ -165,11 +165,11 @@ function DocSearch({
...item, ...item,
url: withBaseUrl(`${url.pathname}${url.hash}`), url: withBaseUrl(`${url.pathname}${url.hash}`),
}; };
}); }),
},
).current; ).current;
const resultsFooterComponent = useMemo( const resultsFooterComponent = useMemo(
// eslint-disable-next-line react/no-unstable-nested-components
() => (footerProps: ResultsFooterProps) => () => (footerProps: ResultsFooterProps) =>
<ResultsFooter {...footerProps} onClose={onClose} />, <ResultsFooter {...footerProps} onClose={onClose} />,
[onClose], [onClose],
@ -250,7 +250,7 @@ function DocSearch({
); );
} }
function SearchBar() { function SearchBar(): JSX.Element {
const {siteConfig} = useDocusaurusContext(); const {siteConfig} = useDocusaurusContext();
// @ts-ignore // @ts-ignore
return <DocSearch {...siteConfig.themeConfig.algolia} />; return <DocSearch {...siteConfig.themeConfig.algolia} />;

View file

@ -54,14 +54,14 @@ function useDocsSearchVersionsHelpers() {
// State of the version select menus / algolia facet filters // State of the version select menus / algolia facet filters
// docsPluginId -> versionName map // docsPluginId -> versionName map
const [searchVersions, setSearchVersions] = useState<Record<string, string>>( const [searchVersions, setSearchVersions] = useState<Record<string, string>>(
() => { () =>
return Object.entries(allDocsData).reduce( Object.entries(allDocsData).reduce(
(acc, [pluginId, pluginData]) => { (acc, [pluginId, pluginData]) => ({
return {...acc, [pluginId]: pluginData.versions[0].name}; ...acc,
}, [pluginId]: pluginData.versions[0].name,
}),
{}, {},
); ),
},
); );
// Set the value of a single select menu // Set the value of a single select menu
@ -81,11 +81,11 @@ function useDocsSearchVersionsHelpers() {
} }
// We want to display one select per versioned docs plugin instance // We want to display one select per versioned docs plugin instance
const SearchVersionSelectList = ({ function SearchVersionSelectList({
docsSearchVersionsHelpers, docsSearchVersionsHelpers,
}: { }: {
docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>; docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>;
}) => { }) {
const versionedPluginEntries = Object.entries( const versionedPluginEntries = Object.entries(
docsSearchVersionsHelpers.allDocsData, docsSearchVersionsHelpers.allDocsData,
) )
@ -126,7 +126,7 @@ const SearchVersionSelectList = ({
})} })}
</div> </div>
); );
}; }
type ResultDispatcherState = { type ResultDispatcherState = {
items: { items: {
@ -225,12 +225,11 @@ function SearchPage(): JSX.Element {
return; return;
} }
const sanitizeValue = (value: string) => { const sanitizeValue = (value: string) =>
return value.replace( value.replace(
/algolia-docsearch-suggestion--highlight/g, /algolia-docsearch-suggestion--highlight/g,
'search-result-match', 'search-result-match',
); );
};
const items = hits.map( const items = hits.map(
({ ({
@ -239,9 +238,9 @@ function SearchPage(): JSX.Element {
_snippetResult: snippet = {}, _snippetResult: snippet = {},
}) => { }) => {
const parsedURL = new URL(url); const parsedURL = new URL(url);
const titles = Object.keys(hierarchy).map((key) => { const titles = Object.keys(hierarchy).map((key) =>
return sanitizeValue(hierarchy[key].value); sanitizeValue(hierarchy[key].value),
}); );
return { return {
title: titles.pop()!, title: titles.pop()!,

View file

@ -47,10 +47,9 @@ function useSearchQuery(): SearchQuery {
); );
const generateSearchPageLink = useCallback( const generateSearchPageLink = useCallback(
(targetSearchQuery: string) => { (targetSearchQuery: string) =>
// Refer to https://github.com/facebook/docusaurus/pull/2838 // Refer to https://github.com/facebook/docusaurus/pull/2838
return `${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`; `${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`,
},
[baseUrl], [baseUrl],
); );

View file

@ -351,7 +351,9 @@ describe('mergeTranslations', () => {
describe('mapAsyncSequencial', () => { describe('mapAsyncSequencial', () => {
function sleep(timeout: number): Promise<void> { function sleep(timeout: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, timeout)); return new Promise((resolve) => {
setTimeout(resolve, timeout);
});
} }
test('map sequentially', async () => { test('map sequentially', async () => {
@ -390,7 +392,9 @@ describe('mapAsyncSequencial', () => {
describe('findAsyncSequencial', () => { describe('findAsyncSequencial', () => {
function sleep(timeout: number): Promise<void> { function sleep(timeout: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, timeout)); return new Promise((resolve) => {
setTimeout(resolve, timeout);
});
} }
test('find sequentially', async () => { test('find sequentially', async () => {

View file

@ -10,7 +10,6 @@ import fs from 'fs-extra';
// Return an ordered list of locales we should try // Return an ordered list of locales we should try
export function codeTranslationLocalesToTry(locale: string): string[] { export function codeTranslationLocalesToTry(locale: string): string[] {
// @ts-expect-error: TODO until available in TS, see https://github.com/microsoft/TypeScript/issues/37326
const intlLocale = Intl.Locale ? new Intl.Locale(locale) : undefined; const intlLocale = Intl.Locale ? new Intl.Locale(locale) : undefined;
if (!intlLocale) { if (!intlLocale) {
return [locale]; return [locale];
@ -24,7 +23,7 @@ export function codeTranslationLocalesToTry(locale: string): string[] {
} }
// if locale is like "pt-BR", we want to fallback to "pt" // if locale is like "pt-BR", we want to fallback to "pt"
else { else {
return [locale, intlLocale.language]; return [locale, intlLocale.language!];
} }
} }

View file

@ -259,7 +259,7 @@ export function removePrefix(str: string, prefix: string): string {
return str.startsWith(prefix) ? str.slice(prefix.length) : str; return str.startsWith(prefix) ? str.slice(prefix.length) : str;
} }
export function getElementsAround<T extends unknown>( export function getElementsAround<T>(
array: T[], array: T[],
aroundIndex: number, aroundIndex: number,
): { ): {
@ -306,7 +306,7 @@ export function getPluginI18nPath({
); );
} }
export async function mapAsyncSequencial<T extends unknown, R extends unknown>( export async function mapAsyncSequencial<T, R>(
array: T[], array: T[],
action: (t: T) => Promise<R>, action: (t: T) => Promise<R>,
): Promise<R[]> { ): Promise<R[]> {
@ -389,9 +389,7 @@ export function reportMessage(
export function mergeTranslations( export function mergeTranslations(
contents: TranslationFileContent[], contents: TranslationFileContent[],
): TranslationFileContent { ): TranslationFileContent {
return contents.reduce((acc, content) => { return contents.reduce((acc, content) => ({...acc, ...content}), {});
return {...acc, ...content};
}, {});
} }
export function getSwizzledComponent( export function getSwizzledComponent(

View file

@ -16,11 +16,10 @@ const SPACE_FOR_APPENDING = 10;
const isMacOs = process.platform === `darwin`; const isMacOs = process.platform === `darwin`;
const isWindows = process.platform === `win32`; const isWindows = process.platform === `win32`;
export const isNameTooLong = (str: string): boolean => { export const isNameTooLong = (str: string): boolean =>
return isMacOs || isWindows isMacOs || isWindows
? str.length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_CHARS // MacOS (APFS) and Windows (NTFS) filename length limit (255 chars) ? str.length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_CHARS // MacOS (APFS) and Windows (NTFS) filename length limit (255 chars)
: Buffer.from(str).length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_BYTES; // Other (255 bytes) : Buffer.from(str).length + SPACE_FOR_APPENDING > MAX_PATH_SEGMENT_BYTES; // Other (255 bytes)
};
export const shortName = (str: string): string => { export const shortName = (str: string): string => {
if (isMacOs || isWindows) { if (isMacOs || isWindows) {

View file

@ -91,7 +91,7 @@ export default async function choosePort(
(port) => (port) =>
new Promise((resolve) => { new Promise((resolve) => {
if (port === defaultPort) { if (port === defaultPort) {
return resolve(port); resolve(port);
} }
const message = const message =
process.platform !== 'win32' && defaultPort < 1024 && !isRoot() process.platform !== 'win32' && defaultPort < 1024 && !isRoot()
@ -121,7 +121,6 @@ export default async function choosePort(
console.log(chalk.red(message)); console.log(chalk.red(message));
resolve(null); resolve(null);
} }
return null;
}), }),
(err) => { (err) => {
throw new Error( throw new Error(

View file

@ -22,9 +22,7 @@ export const createStatefulLinksCollector = (): StatefulLinksCollector => {
collectLink: (link: string): void => { collectLink: (link: string): void => {
allLinks.add(link); allLinks.add(link);
}, },
getCollectedLinks: (): string[] => { getCollectedLinks: (): string[] => [...allLinks],
return [...allLinks];
},
}; };
}; };
@ -35,16 +33,14 @@ const Context = createContext<LinksCollector>({
}, },
}); });
export const useLinksCollector = (): LinksCollector => { export const useLinksCollector = (): LinksCollector => useContext(Context);
return useContext(Context);
};
export const ProvideLinksCollector = ({ export function ProvideLinksCollector({
children, children,
linksCollector, linksCollector,
}: { }: {
children: ReactNode; children: ReactNode;
linksCollector: LinksCollector; linksCollector: LinksCollector;
}): JSX.Element => { }): JSX.Element {
return <Context.Provider value={linksCollector}>{children}</Context.Provider>; return <Context.Provider value={linksCollector}>{children}</Context.Provider>;
}; }

View file

@ -49,19 +49,18 @@ const flatten = <T>(arrays: T[][]): T[] =>
// output: /blog/2018/12/14/Happy-First-Birthday-Slash // output: /blog/2018/12/14/Happy-First-Birthday-Slash
const removeRouteNameHash = (str: string) => str.replace(/(-[^-]+)$/, ''); const removeRouteNameHash = (str: string) => str.replace(/(-[^-]+)$/, '');
const getChunkNamesToLoad = (path: string): string[] => { const getChunkNamesToLoad = (path: string): string[] =>
return flatten( flatten(
Object.entries(routesChunkNames) Object.entries(routesChunkNames)
.filter( .filter(
([routeNameWithHash]) => ([routeNameWithHash]) =>
removeRouteNameHash(routeNameWithHash) === path, removeRouteNameHash(routeNameWithHash) === path,
) )
.map(([, routeChunks]) => { .map(([, routeChunks]) =>
// flat() is useful for nested chunk names, it's not like array.flat() // flat() is useful for nested chunk names, it's not like array.flat()
return Object.values(flat(routeChunks)); Object.values(flat(routeChunks)),
}), ),
); );
};
const docusaurus = { const docusaurus = {
prefetch: (routePath: string): boolean => { prefetch: (routePath: string): boolean => {
@ -82,6 +81,7 @@ const docusaurus = {
chunkNamesNeeded.forEach((chunkName) => { chunkNamesNeeded.forEach((chunkName) => {
// "__webpack_require__.gca" is a custom function provided by ChunkAssetPlugin. // "__webpack_require__.gca" is a custom function provided by ChunkAssetPlugin.
// Pass it the chunkName or chunkId you want to load and it will return the URL for that chunk. // Pass it the chunkName or chunkId you want to load and it will return the URL for that chunk.
// eslint-disable-next-line camelcase
const chunkAsset = __webpack_require__.gca(chunkName); const chunkAsset = __webpack_require__.gca(chunkName);
// In some cases, webpack might decide to optimize further & hence the chunk assets are merged to another chunk/previous chunk. // In some cases, webpack might decide to optimize further & hence the chunk assets are merged to another chunk/previous chunk.

View file

@ -68,23 +68,24 @@ export function interpolate<Str extends string, Value extends ReactNode>(
else if (elements.every((el) => typeof el === 'string')) { else if (elements.every((el) => typeof el === 'string')) {
return processedText return processedText
.split(ValueFoundMarker) .split(ValueFoundMarker)
.reduce<string>((str, value, index) => { .reduce<string>(
return str.concat(value).concat((elements[index] as string) ?? ''); (str, value, index) =>
}, ''); str.concat(value).concat((elements[index] as string) ?? ''),
'',
);
} }
// JSX interpolation: returns ReactNode // JSX interpolation: returns ReactNode
else { else {
return processedText return processedText.split(ValueFoundMarker).reduce<ReactNode[]>(
.split(ValueFoundMarker) (array, value, index) => [
.reduce<ReactNode[]>((array, value, index) => {
return [
...array, ...array,
<React.Fragment key={index}> <React.Fragment key={index}>
{value} {value}
{elements[index]} {elements[index]}
</React.Fragment>, </React.Fragment>,
]; ],
}, []); [],
);
} }
} }

View file

@ -45,9 +45,7 @@ export function useBaseUrlUtils(): BaseUrlUtils {
const {siteConfig: {baseUrl = '/', url: siteUrl} = {}} = const {siteConfig: {baseUrl = '/', url: siteUrl} = {}} =
useDocusaurusContext(); useDocusaurusContext();
return { return {
withBaseUrl: (url, options) => { withBaseUrl: (url, options) => addBaseUrl(siteUrl, baseUrl, url, options),
return addBaseUrl(siteUrl, baseUrl, url, options);
},
}; };
} }

View file

@ -28,11 +28,11 @@ import chalk from 'chalk';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import {memoize} from 'lodash'; import {memoize} from 'lodash';
const getCompiledSSRTemplate = memoize((template) => { const getCompiledSSRTemplate = memoize((template) =>
return eta.compile(template.trim(), { eta.compile(template.trim(), {
rmWhitespace: true, rmWhitespace: true,
}); }),
}); );
function renderSSRTemplate(ssrTemplate, data) { function renderSSRTemplate(ssrTemplate, data) {
const compiled = getCompiledSSRTemplate(ssrTemplate); const compiled = getCompiledSSRTemplate(ssrTemplate);

View file

@ -20,7 +20,7 @@ function Layout(props) {
<> <>
<Head defaultTitle={`${defaultTitle}${tagline ? ` · ${tagline}` : ''}`}> <Head defaultTitle={`${defaultTitle}${tagline ? ` · ${tagline}` : ''}`}>
{title && <title>{`${title} · ${tagline}`}</title>} {title && <title>{`${title} · ${tagline}`}</title>}
{favicon && <link rel="shortcut icon" href={faviconUrl} />} {favicon && <link rel="icon" href={faviconUrl} />}
{description && <meta name="description" content={description} />} {description && <meta name="description" content={description} />}
{description && ( {description && (
<meta property="og:description" content={description} /> <meta property="og:description" content={description} />

View file

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
export default ({error, retry, pastDelay}) => { export default function Loading({error, retry, pastDelay}) {
if (error) { if (error) {
return ( return (
<div <div
@ -133,4 +133,4 @@ export default ({error, retry, pastDelay}) => {
} }
return null; return null;
}; }

View file

@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React from 'react';
// Wrapper at the very top of the app, that is applied constantly // Wrapper at the very top of the app, that is applied constantly
// and does not depend on current route (unlike the layout) // and does not depend on current route (unlike the layout)
// //
@ -15,7 +13,7 @@ import React from 'react';
// //
// See https://github.com/facebook/docusaurus/issues/3919 // See https://github.com/facebook/docusaurus/issues/3919
function Root({children}) { function Root({children}) {
return <>{children}</>; return children;
} }
export default Root; export default Root;

View file

@ -75,9 +75,9 @@ export function getAllBrokenLinks({
}): Record<string, BrokenLink[]> { }): Record<string, BrokenLink[]> {
const filteredRoutes = filterIntermediateRoutes(routes); const filteredRoutes = filterIntermediateRoutes(routes);
const allBrokenLinks = mapValues(allCollectedLinks, (pageLinks, pagePath) => { const allBrokenLinks = mapValues(allCollectedLinks, (pageLinks, pagePath) =>
return getPageBrokenLinks({pageLinks, pagePath, routes: filteredRoutes}); getPageBrokenLinks({pageLinks, pagePath, routes: filteredRoutes}),
}); );
// remove pages without any broken link // remove pages without any broken link
return pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0); return pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0);
@ -186,9 +186,9 @@ export async function filterExistingFileLinks({
return filePathsToTry.some(isExistingFile); return filePathsToTry.some(isExistingFile);
} }
return mapValues(allCollectedLinks, (links) => { return mapValues(allCollectedLinks, (links) =>
return links.filter((link) => !linkFileExists(link)); links.filter((link) => !linkFileExists(link)),
}); );
} }
export async function handleBrokenLinks({ export async function handleBrokenLinks({

View file

@ -108,7 +108,7 @@ const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
.optional() .optional()
.default(DEFAULT_I18N_CONFIG); .default(DEFAULT_I18N_CONFIG);
const SiteUrlSchema = URISchema.required().custom(function (value, helpers) { const SiteUrlSchema = URISchema.required().custom((value, helpers) => {
try { try {
const {pathname} = new URL(value); const {pathname} = new URL(value);
if (pathname !== '/') { if (pathname !== '/') {
@ -124,7 +124,7 @@ const SiteUrlSchema = URISchema.required().custom(function (value, helpers) {
export const ConfigSchema = Joi.object({ export const ConfigSchema = Joi.object({
baseUrl: Joi.string() baseUrl: Joi.string()
.required() .required()
.regex(new RegExp('/$', 'm')) .regex(/\/$/m)
.message('{{#label}} must be a string with a trailing slash.'), .message('{{#label}} must be a string with a trailing slash.'),
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner), baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
favicon: Joi.string().optional(), favicon: Joi.string().optional(),

View file

@ -14,9 +14,7 @@ import chalk from 'chalk';
function getDefaultLocaleLabel(locale: string) { function getDefaultLocaleLabel(locale: string) {
// Intl.DisplayNames is ES2021 - Node14+ // Intl.DisplayNames is ES2021 - Node14+
// https://v8.dev/features/intl-displaynames // https://v8.dev/features/intl-displaynames
// @ts-expect-error: wait for TS support of ES2021 feature
if (typeof Intl.DisplayNames !== 'undefined') { if (typeof Intl.DisplayNames !== 'undefined') {
// @ts-expect-error: wait for TS support of ES2021 feature
return new Intl.DisplayNames([locale], {type: 'language'}).of(locale); return new Intl.DisplayNames([locale], {type: 'language'}).of(locale);
} }
return locale; return locale;
@ -76,9 +74,10 @@ Note: Docusaurus only support running one locale at a time.`,
}; };
} }
const localeConfigs = locales.reduce((acc, locale) => { const localeConfigs = locales.reduce(
return {...acc, [locale]: getLocaleConfig(locale)}; (acc, locale) => ({...acc, [locale]: getLocaleConfig(locale)}),
}, {}); {},
);
return { return {
defaultLocale: i18nConfig.defaultLocale, defaultLocale: i18nConfig.defaultLocale,

View file

@ -124,12 +124,12 @@ export async function loadPlugins({
const allContent: AllContent = chain(loadedPlugins) const allContent: AllContent = chain(loadedPlugins)
.groupBy((item) => item.name) .groupBy((item) => item.name)
.mapValues((nameItems) => { .mapValues((nameItems) =>
return chain(nameItems) chain(nameItems)
.groupBy((item) => item.options.id ?? DEFAULT_PLUGIN_ID) .groupBy((item) => item.options.id ?? DEFAULT_PLUGIN_ID)
.mapValues((idItems) => idItems[0].content) .mapValues((idItems) => idItems[0].content)
.value(); .value(),
}) )
.value(); .value();
// 3. Plugin Lifecycle - contentLoaded. // 3. Plugin Lifecycle - contentLoaded.

View file

@ -19,9 +19,9 @@ export function sortAliases(aliases: ThemeAliases): ThemeAliases {
// Alphabetical order by default // Alphabetical order by default
const entries = sortBy(Object.entries(aliases), ([alias]) => alias); const entries = sortBy(Object.entries(aliases), ([alias]) => alias);
// @theme/NavbarItem should be after @theme/NavbarItem/LocaleDropdown // @theme/NavbarItem should be after @theme/NavbarItem/LocaleDropdown
entries.sort(([alias1], [alias2]) => { entries.sort(([alias1], [alias2]) =>
return alias1.includes(`${alias2}/`) ? -1 : 0; alias1.includes(`${alias2}/`) ? -1 : 0,
}); );
return Object.fromEntries(entries); return Object.fromEntries(entries);
} }

View file

@ -271,9 +271,10 @@ export async function getPluginsDefaultCodeTranslationMessages(
plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}), plugins.map((plugin) => plugin.getDefaultCodeTranslationMessages?.() ?? {}),
); );
return pluginsMessages.reduce((allMessages, pluginMessages) => { return pluginsMessages.reduce(
return {...allMessages, ...pluginMessages}; (allMessages, pluginMessages) => ({...allMessages, ...pluginMessages}),
}, {}); {},
);
} }
export function applyDefaultCodeTranslations({ export function applyDefaultCodeTranslations({
@ -298,11 +299,9 @@ Please report this Docusaurus issue.
return mapValues( return mapValues(
extractedCodeTranslations, extractedCodeTranslations,
(messageTranslation, messageId) => { (messageTranslation, messageId) => ({
return {
...messageTranslation, ...messageTranslation,
message: defaultCodeMessages[messageId] ?? messageTranslation.message, message: defaultCodeMessages[messageId] ?? messageTranslation.message,
}; }),
},
); );
} }

View file

@ -85,9 +85,10 @@ export async function extractSiteSourceCodeTranslations(
function toTranslationFileContent( function toTranslationFileContent(
sourceCodeFileTranslations: SourceCodeFileTranslations[], sourceCodeFileTranslations: SourceCodeFileTranslations[],
): TranslationFileContent { ): TranslationFileContent {
return sourceCodeFileTranslations.reduce((acc, item) => { return sourceCodeFileTranslations.reduce(
return {...acc, ...item.translations}; (acc, item) => ({...acc, ...item.translations}),
}, {}); {},
);
} }
const sourceCodeFilePaths = await getSourceCodeFilePaths(siteDir, plugins); const sourceCodeFilePaths = await getSourceCodeFilePaths(siteDir, plugins);

View file

@ -250,35 +250,38 @@ describe('extending PostCSS', () => {
} }
// Run multiple times: ensure last run does not override previous runs // Run multiple times: ensure last run does not override previous runs
webpackConfig = applyConfigurePostCss((postCssOptions) => { webpackConfig = applyConfigurePostCss(
return { (postCssOptions) => ({
...postCssOptions, ...postCssOptions,
plugins: [ plugins: [
...postCssOptions.plugins, ...postCssOptions.plugins,
createFakePlugin('postcss-plugin-1'), createFakePlugin('postcss-plugin-1'),
], ],
}; }),
}, webpackConfig); webpackConfig,
);
webpackConfig = applyConfigurePostCss((postCssOptions) => { webpackConfig = applyConfigurePostCss(
return { (postCssOptions) => ({
...postCssOptions, ...postCssOptions,
plugins: [ plugins: [
createFakePlugin('postcss-plugin-2'), createFakePlugin('postcss-plugin-2'),
...postCssOptions.plugins, ...postCssOptions.plugins,
], ],
}; }),
}, webpackConfig); webpackConfig,
);
webpackConfig = applyConfigurePostCss((postCssOptions) => { webpackConfig = applyConfigurePostCss(
return { (postCssOptions) => ({
...postCssOptions, ...postCssOptions,
plugins: [ plugins: [
...postCssOptions.plugins, ...postCssOptions.plugins,
createFakePlugin('postcss-plugin-3'), createFakePlugin('postcss-plugin-3'),
], ],
}; }),
}, webpackConfig); webpackConfig,
);
// @ts-expect-error: relax type // @ts-expect-error: relax type
const postCssLoader1 = webpackConfig.module?.rules[0].use[2]; const postCssLoader1 = webpackConfig.module?.rules[0].use[2];

View file

@ -176,18 +176,16 @@ class CleanWebpackPlugin {
all: false, all: false,
assets: true, assets: true,
}).assets || []; }).assets || [];
const assets = statsAssets.map((asset: {name: string}) => { const assets = statsAssets.map((asset: {name: string}) => asset.name);
return asset.name;
});
/** /**
* Get all files that were in the previous build but not the current * Get all files that were in the previous build but not the current
* *
* (relies on del's cwd: outputPath option) * (relies on del's cwd: outputPath option)
*/ */
const staleFiles = this.currentAssets.filter((previousAsset) => { const staleFiles = this.currentAssets.filter(
return assets.includes(previousAsset) === false; (previousAsset) => assets.includes(previousAsset) === false,
}); );
/** /**
* Save assets for next compilation * Save assets for next compilation

View file

@ -164,7 +164,7 @@ export const getCustomizableJSLoader =
: jsLoader(isServer); : jsLoader(isServer);
// TODO remove this before end of 2021? // TODO remove this before end of 2021?
const warnBabelLoaderOnce = memoize(function () { const warnBabelLoaderOnce = memoize(() => {
console.warn( console.warn(
chalk.yellow( chalk.yellow(
'Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): "getBabelLoader(isServer)" is now deprecated in favor of "getJSLoader({isServer})".', 'Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): "getBabelLoader(isServer)" is now deprecated in favor of "getJSLoader({isServer})".',
@ -180,7 +180,7 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated(
}; };
// TODO remove this before end of 2021 ? // TODO remove this before end of 2021 ?
const warnCacheLoaderOnce = memoize(function () { const warnCacheLoaderOnce = memoize(() => {
console.warn( console.warn(
chalk.yellow( chalk.yellow(
'Docusaurus uses Webpack 5 and getCacheLoader() usage is now deprecated.', 'Docusaurus uses Webpack 5 and getCacheLoader() usage is now deprecated.',
@ -335,24 +335,20 @@ export function getFileLoaderUtils(): FileLoaderUtils {
`${OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[hash].[ext]`; `${OUTPUT_STATIC_ASSETS_DIR_NAME}/${folder}/[name]-[hash].[ext]`;
const loaders: FileLoaderUtils['loaders'] = { const loaders: FileLoaderUtils['loaders'] = {
file: (options: {folder: AssetFolder}) => { file: (options: {folder: AssetFolder}) => ({
return {
loader: require.resolve(`file-loader`), loader: require.resolve(`file-loader`),
options: { options: {
name: fileLoaderFileName(options.folder), name: fileLoaderFileName(options.folder),
}, },
}; }),
}, url: (options: {folder: AssetFolder}) => ({
url: (options: {folder: AssetFolder}) => {
return {
loader: require.resolve(`url-loader`), loader: require.resolve(`url-loader`),
options: { options: {
limit: urlLoaderLimit, limit: urlLoaderLimit,
name: fileLoaderFileName(options.folder), name: fileLoaderFileName(options.folder),
fallback: require.resolve(`file-loader`), fallback: require.resolve(`file-loader`),
}, },
}; }),
},
// TODO find a better solution to avoid conflicts with the ideal-image plugin // TODO find a better solution to avoid conflicts with the ideal-image plugin
// TODO this may require a little breaking change for ideal-image users? // TODO this may require a little breaking change for ideal-image users?
@ -372,33 +368,26 @@ export function getFileLoaderUtils(): FileLoaderUtils {
* Loads image assets, inlines images via a data URI if they are below * Loads image assets, inlines images via a data URI if they are below
* the size threshold * the size threshold
*/ */
images: () => { images: () => ({
return {
use: [loaders.url({folder: 'images'})], use: [loaders.url({folder: 'images'})],
test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/, test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/,
}; }),
},
fonts: () => { fonts: () => ({
return {
use: [loaders.url({folder: 'fonts'})], use: [loaders.url({folder: 'fonts'})],
test: /\.(woff|woff2|eot|ttf|otf)$/, test: /\.(woff|woff2|eot|ttf|otf)$/,
}; }),
},
/** /**
* Loads audio and video and inlines them via a data URI if they are below * Loads audio and video and inlines them via a data URI if they are below
* the size threshold * the size threshold
*/ */
media: () => { media: () => ({
return {
use: [loaders.url({folder: 'medias'})], use: [loaders.url({folder: 'medias'})],
test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/, test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/,
}; }),
},
svg: () => { svg: () => ({
return {
test: /\.svg?$/, test: /\.svg?$/,
oneOf: [ oneOf: [
{ {
@ -426,15 +415,12 @@ export function getFileLoaderUtils(): FileLoaderUtils {
use: [loaders.url({folder: 'images'})], use: [loaders.url({folder: 'images'})],
}, },
], ],
}; }),
},
otherAssets: () => { otherAssets: () => ({
return {
use: [loaders.file({folder: 'files'})], use: [loaders.file({folder: 'files'})],
test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/, test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/,
}; }),
},
}; };
return {loaders, rules}; return {loaders, rules};

View file

@ -12,9 +12,8 @@ import type {Palette} from 'node-vibrant/lib/color';
* it returns a Base64 image string with required formatting * it returns a Base64 image string with required formatting
* to work on the web (<img src=".." /> or in CSS url('..')) * to work on the web (<img src=".." /> or in CSS url('..'))
*/ */
const toBase64 = (extMimeType: string, data: Buffer): string => { const toBase64 = (extMimeType: string, data: Buffer): string =>
return `data:${extMimeType};base64,${data.toString('base64')}`; `data:${extMimeType};base64,${data.toString('base64')}`;
};
/** /**
* takes a color swatch object, converts it to an array & returns * takes a color swatch object, converts it to an array & returns

View file

@ -12,8 +12,9 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
rejected: 'Missing copyright in the header comment', rejected: 'Missing copyright in the header comment',
}); });
module.exports = stylelint.createPlugin(ruleName, (actual) => { module.exports = stylelint.createPlugin(
return (root, result) => { ruleName,
(actual) => (root, result) => {
const validOptions = stylelint.utils.validateOptions(result, ruleName, { const validOptions = stylelint.utils.validateOptions(result, ruleName, {
actual, actual,
}); });
@ -48,8 +49,8 @@ module.exports = stylelint.createPlugin(ruleName, (actual) => {
ruleName, ruleName,
}); });
}); });
}; },
}); );
module.exports.ruleName = ruleName; module.exports.ruleName = ruleName;
module.exports.messages = messages; module.exports.messages = messages;

View file

@ -14,7 +14,9 @@ Used in conjunction with waitForCrowdin.js (which is not enough)
*/ */
async function delay(ms) { async function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => {
setTimeout(resolve, ms);
});
} }
async function run() { async function run() {

View file

@ -43,8 +43,7 @@ function ShowcaseCardTagIcons({tags}: {tags: TagType[]}) {
); );
} }
const ShowcaseCard = memo(function ({user}: {user: User}) { const ShowcaseCard = memo(({user}: {user: User}) => (
return (
<div key={user.title} className="col col--4 margin-bottom--lg"> <div key={user.title} className="col col--4 margin-bottom--lg">
<div className={clsx('card', styles.showcaseCard)}> <div className={clsx('card', styles.showcaseCard)}>
<div className={clsx('card__image', styles.showcaseCardImage)}> <div className={clsx('card__image', styles.showcaseCardImage)}>
@ -91,7 +90,6 @@ const ShowcaseCard = memo(function ({user}: {user: User}) {
)} )}
</div> </div>
</div> </div>
); ));
});
export default ShowcaseCard; export default ShowcaseCard;

Some files were not shown because too many files have changed in this diff Show more