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',
'airbnb',
'prettier',
'prettier/react',
],
settings: {
'import/resolver': {
@ -61,6 +60,7 @@ module.exports = {
},
],
'import/extensions': OFF,
'no-restricted-exports': OFF,
'header/header': [
ERROR,
'block',
@ -90,6 +90,14 @@ module.exports = {
'react/prefer-stateless-function': WARNING,
'react/jsx-props-no-spreading': OFF,
'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,
'import/first': OFF,
'import/order': OFF,
@ -104,19 +112,16 @@ module.exports = {
'no-unused-vars': OFF,
'no-nested-ternary': WARNING,
'@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': [
ERROR,
{argsIgnorePattern: '^_', ignoreRestSiblings: true},
],
'@typescript-eslint/explicit-module-boundary-types': WARNING,
'@typescript-eslint/ban-ts-comment': [
ERROR,
{'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,
'no-useless-escape': WARNING,
'prefer-template': WARNING,
@ -189,6 +194,12 @@ module.exports = {
'import/no-duplicates': OFF,
},
},
{
files: ['*.ts', '*.tsx'],
rules: {
'import/no-import-module-exports': OFF,
},
},
{
files: ['*.js'],
rules: {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -23,7 +23,7 @@ module.exports = {
parserOptions: {
allowImportExportEverywhere: true,
},
extends: ['airbnb', 'prettier', 'prettier/react'],
extends: ['airbnb', 'prettier'],
plugins: ['react-hooks', 'header'],
rules: {
// Ignore certain webpack alias because it can't be resolved
@ -56,5 +56,12 @@ module.exports = {
'react/jsx-filename-extension': OFF,
'react-hooks/rules-of-hooks': ERROR,
'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": {
"@babel/eslint-parser": "^7.15.7",
"eslint": "^7.20.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-header": "^3.0.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"prettier": "^2.2.1",
"eslint": "^8.2.0",
"eslint-config-airbnb": "^19.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.27.0",
"eslint-plugin-react-hooks": "^4.3.0",
"prettier": "^2.4.1",
"stylelint": "^13.2.1"
},
"browserslist": {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -322,9 +322,9 @@ export default function pluginContentBlog(
exact: true,
modules: {
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.
return {
({
content: {
__import: true,
path: blogItemsToMetadata[postID].source,
@ -332,8 +332,8 @@ export default function pluginContentBlog(
truncated: true,
},
},
};
}),
}),
),
metadata: aliasedSource(pageMetadataPath),
},
});
@ -490,14 +490,12 @@ export default function pluginContentBlog(
}: {
frontMatter: BlogPostFrontMatter;
metadata: MetaData;
}): Assets => {
return {
image: frontMatter.image,
authorsImageUrls: metadata.authors.map(
(author) => author.imageURL,
),
};
},
}): Assets => ({
image: frontMatter.image,
authorsImageUrls: metadata.authors.map(
(author) => author.imageURL,
),
}),
},
},
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,18 +10,20 @@ import Head from '@docusaurus/Head';
import Link from '@docusaurus/Link';
import styles from './styles.module.css';
const DebugNavLink = ({to, children}: {to: string; children: ReactNode}) => (
<Link
className={styles.navlink}
isNavLink
to={to}
exact
activeStyle={{
backgroundColor: '#363739',
}}>
{children}
</Link>
);
function DebugNavLink({to, children}: {to: string; children: ReactNode}) {
return (
<Link
className={styles.navlink}
isNavLink
to={to}
exact
activeStyle={{
backgroundColor: '#363739',
}}>
{children}
</Link>
);
}
function DebugLayout({children}: {children: ReactNode}): JSX.Element {
return (

View file

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

View file

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

View file

@ -27,8 +27,7 @@ const ThemeStorageKey = 'theme';
const noFlashColorMode = ({
defaultMode,
respectPrefersColorScheme,
}: ThemeConfig['colorMode']) => {
return `(function() {
}: ThemeConfig['colorMode']) => `(function() {
var defaultMode = '${defaultMode}';
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
// 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}),
translateThemeConfig,
getDefaultCodeTranslationMessages: () => {
return readDefaultCodeTranslationMessages({
getDefaultCodeTranslationMessages: () =>
readDefaultCodeTranslationMessages({
dirPath: path.resolve(__dirname, '..', 'codeTranslations'),
locale: currentLocale,
});
},
}),
getClientModules() {
const modules = [

View file

@ -13,10 +13,13 @@ import BlogPostAuthor from '@theme/BlogPostAuthor';
import styles from './styles.module.css';
// 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;
if (authorsCount === 0) {
return <></>;
return null;
}
return (
<div className="row margin-top--md margin-bottom--sm">

View file

@ -28,19 +28,17 @@ export default function BlogSidebar({sidebar}: Props): JSX.Element | null {
{sidebar.title}
</div>
<ul className={styles.sidebarItemList}>
{sidebar.items.map((item) => {
return (
<li key={item.permalink} className={styles.sidebarItem}>
<Link
isNavLink
to={item.permalink}
className={styles.sidebarItemLink}
activeClassName={styles.sidebarItemLinkActive}>
{item.title}
</Link>
</li>
);
})}
{sidebar.items.map((item) => (
<li key={item.permalink} className={styles.sidebarItem}>
<Link
isNavLink
to={item.permalink}
className={styles.sidebarItemLink}
activeClassName={styles.sidebarItemLinkActive}>
{item.title}
</Link>
</li>
))}
</ul>
</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 {metadata} = DocContent;
const {editUrl, lastUpdatedAt, formattedLastUpdatedAt, lastUpdatedBy, tags} =
@ -71,7 +71,7 @@ export default function DocItemFooter(props: Props): JSX.Element {
const canDisplayFooter = canDisplayTagsRow || canDisplayEditMetaRow;
if (!canDisplayFooter) {
return <></>;
return null;
}
return (

View file

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

View file

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

View file

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

View file

@ -16,23 +16,21 @@ import styles from './styles.module.css';
type HeadingComponent = (props: Props) => JSX.Element;
export const MainHeading: HeadingComponent = function MainHeading({...props}) {
return (
<header>
<h1
{...props}
id={undefined} // h1 headings do not need an id because they don't appear in the TOC
>
{props.children}
</h1>
</header>
);
};
// eslint-disable-next-line react/function-component-definition
export const MainHeading: HeadingComponent = ({...props}) => (
<header>
<h1
{...props}
id={undefined} // h1 headings do not need an id because they don't appear in the TOC
>
{props.children}
</h1>
</header>
);
const createAnchorHeading = (
Tag: HeadingType,
): ((props: Props) => JSX.Element) =>
function TargetComponent({id, ...props}) {
const createAnchorHeading =
(Tag: HeadingType) =>
({id, ...props}: Props) => {
const {
navbar: {hideOnScroll},
} = useThemeConfig();
@ -65,8 +63,7 @@ const createAnchorHeading = (
);
};
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) => {
return headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
};
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) =>
headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
export default Heading;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -101,7 +101,7 @@ export default function LayoutHead(props: Props): JSX.Element {
<>
<Head>
<html lang={htmlLang} dir={htmlDir} />
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
{favicon && <link rel="icon" href={faviconUrl} />}
<title>{pageTitle}</title>
<meta property="og:title" content={pageTitle} />
<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 {useThemeConfig} from '@docusaurus/theme-common';
const Logo = (props: Props): JSX.Element => {
function Logo(props: Props): JSX.Element {
const {
siteConfig: {title},
} = useDocusaurusContext();
@ -46,11 +46,11 @@ const Logo = (props: Props): JSX.Element => {
(imageClassName ? (
<div className={imageClassName}>{themedImage}</div>
) : (
<>{themedImage}</>
themedImage
))}
{navbarTitle != null && <b className={titleClassName}>{navbarTitle}</b>}
</Link>
);
};
}
export default Logo;

View file

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

View file

@ -5,7 +5,7 @@
* 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 ThemeContext from '@theme/ThemeContext';
@ -13,9 +13,13 @@ import type {Props} from '@theme/ThemeProvider';
function ThemeProvider(props: Props): JSX.Element {
const {isDarkTheme, setLightTheme, setDarkTheme} = useTheme();
const contextValue = useMemo(
() => ({isDarkTheme, setLightTheme, setDarkTheme}),
[isDarkTheme, setLightTheme, setDarkTheme],
);
return (
<ThemeContext.Provider value={{isDarkTheme, setLightTheme, setDarkTheme}}>
<ThemeContext.Provider value={contextValue}>
{props.children}
</ThemeContext.Provider>
);

View file

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

View file

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

View file

@ -5,7 +5,7 @@
* 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 UserPreferencesContext from '@theme/UserPreferencesContext';
@ -13,12 +13,15 @@ import type {Props} from '@theme/UserPreferencesProvider';
function UserPreferencesProvider(props: Props): JSX.Element {
const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
const contextValue = useMemo(
() => ({
tabGroupChoices,
setTabGroupChoices,
}),
[tabGroupChoices, setTabGroupChoices],
);
return (
<UserPreferencesContext.Provider
value={{
tabGroupChoices,
setTabGroupChoices,
}}>
<UserPreferencesContext.Provider value={contextValue}>
{props.children}
</UserPreferencesContext.Provider>
);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,6 @@ import fs from 'fs-extra';
// Return an ordered list of locales we should try
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;
if (!intlLocale) {
return [locale];
@ -24,7 +23,7 @@ export function codeTranslationLocalesToTry(locale: string): string[] {
}
// if locale is like "pt-BR", we want to fallback to "pt"
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;
}
export function getElementsAround<T extends unknown>(
export function getElementsAround<T>(
array: T[],
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[],
action: (t: T) => Promise<R>,
): Promise<R[]> {
@ -389,9 +389,7 @@ export function reportMessage(
export function mergeTranslations(
contents: TranslationFileContent[],
): TranslationFileContent {
return contents.reduce((acc, content) => {
return {...acc, ...content};
}, {});
return contents.reduce((acc, content) => ({...acc, ...content}), {});
}
export function getSwizzledComponent(

View file

@ -16,11 +16,10 @@ const SPACE_FOR_APPENDING = 10;
const isMacOs = process.platform === `darwin`;
const isWindows = process.platform === `win32`;
export const isNameTooLong = (str: string): boolean => {
return isMacOs || isWindows
export const isNameTooLong = (str: string): boolean =>
isMacOs || isWindows
? 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)
};
export const shortName = (str: string): string => {
if (isMacOs || isWindows) {

View file

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

View file

@ -22,9 +22,7 @@ export const createStatefulLinksCollector = (): StatefulLinksCollector => {
collectLink: (link: string): void => {
allLinks.add(link);
},
getCollectedLinks: (): string[] => {
return [...allLinks];
},
getCollectedLinks: (): string[] => [...allLinks],
};
};
@ -35,16 +33,14 @@ const Context = createContext<LinksCollector>({
},
});
export const useLinksCollector = (): LinksCollector => {
return useContext(Context);
};
export const useLinksCollector = (): LinksCollector => useContext(Context);
export const ProvideLinksCollector = ({
export function ProvideLinksCollector({
children,
linksCollector,
}: {
children: ReactNode;
linksCollector: LinksCollector;
}): JSX.Element => {
}): JSX.Element {
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
const removeRouteNameHash = (str: string) => str.replace(/(-[^-]+)$/, '');
const getChunkNamesToLoad = (path: string): string[] => {
return flatten(
const getChunkNamesToLoad = (path: string): string[] =>
flatten(
Object.entries(routesChunkNames)
.filter(
([routeNameWithHash]) =>
removeRouteNameHash(routeNameWithHash) === path,
)
.map(([, routeChunks]) => {
.map(([, routeChunks]) =>
// flat() is useful for nested chunk names, it's not like array.flat()
return Object.values(flat(routeChunks));
}),
Object.values(flat(routeChunks)),
),
);
};
const docusaurus = {
prefetch: (routePath: string): boolean => {
@ -82,6 +81,7 @@ const docusaurus = {
chunkNamesNeeded.forEach((chunkName) => {
// "__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.
// eslint-disable-next-line camelcase
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.

View file

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

View file

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

View file

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

View file

@ -20,7 +20,7 @@ function Layout(props) {
<>
<Head defaultTitle={`${defaultTitle}${tagline ? ` · ${tagline}` : ''}`}>
{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 property="og:description" content={description} />

View file

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

View file

@ -5,8 +5,6 @@
* 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
// 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
function Root({children}) {
return <>{children}</>;
return children;
}
export default Root;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -85,9 +85,10 @@ export async function extractSiteSourceCodeTranslations(
function toTranslationFileContent(
sourceCodeFileTranslations: SourceCodeFileTranslations[],
): TranslationFileContent {
return sourceCodeFileTranslations.reduce((acc, item) => {
return {...acc, ...item.translations};
}, {});
return sourceCodeFileTranslations.reduce(
(acc, item) => ({...acc, ...item.translations}),
{},
);
}
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
webpackConfig = applyConfigurePostCss((postCssOptions) => {
return {
webpackConfig = applyConfigurePostCss(
(postCssOptions) => ({
...postCssOptions,
plugins: [
...postCssOptions.plugins,
createFakePlugin('postcss-plugin-1'),
],
};
}, webpackConfig);
}),
webpackConfig,
);
webpackConfig = applyConfigurePostCss((postCssOptions) => {
return {
webpackConfig = applyConfigurePostCss(
(postCssOptions) => ({
...postCssOptions,
plugins: [
createFakePlugin('postcss-plugin-2'),
...postCssOptions.plugins,
],
};
}, webpackConfig);
}),
webpackConfig,
);
webpackConfig = applyConfigurePostCss((postCssOptions) => {
return {
webpackConfig = applyConfigurePostCss(
(postCssOptions) => ({
...postCssOptions,
plugins: [
...postCssOptions.plugins,
createFakePlugin('postcss-plugin-3'),
],
};
}, webpackConfig);
}),
webpackConfig,
);
// @ts-expect-error: relax type
const postCssLoader1 = webpackConfig.module?.rules[0].use[2];

View file

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

View file

@ -164,7 +164,7 @@ export const getCustomizableJSLoader =
: jsLoader(isServer);
// TODO remove this before end of 2021?
const warnBabelLoaderOnce = memoize(function () {
const warnBabelLoaderOnce = memoize(() => {
console.warn(
chalk.yellow(
'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 ?
const warnCacheLoaderOnce = memoize(function () {
const warnCacheLoaderOnce = memoize(() => {
console.warn(
chalk.yellow(
'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]`;
const loaders: FileLoaderUtils['loaders'] = {
file: (options: {folder: AssetFolder}) => {
return {
loader: require.resolve(`file-loader`),
options: {
name: fileLoaderFileName(options.folder),
},
};
},
url: (options: {folder: AssetFolder}) => {
return {
loader: require.resolve(`url-loader`),
options: {
limit: urlLoaderLimit,
name: fileLoaderFileName(options.folder),
fallback: require.resolve(`file-loader`),
},
};
},
file: (options: {folder: AssetFolder}) => ({
loader: require.resolve(`file-loader`),
options: {
name: fileLoaderFileName(options.folder),
},
}),
url: (options: {folder: AssetFolder}) => ({
loader: require.resolve(`url-loader`),
options: {
limit: urlLoaderLimit,
name: fileLoaderFileName(options.folder),
fallback: require.resolve(`file-loader`),
},
}),
// 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?
@ -372,69 +368,59 @@ export function getFileLoaderUtils(): FileLoaderUtils {
* Loads image assets, inlines images via a data URI if they are below
* the size threshold
*/
images: () => {
return {
use: [loaders.url({folder: 'images'})],
test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/,
};
},
images: () => ({
use: [loaders.url({folder: 'images'})],
test: /\.(ico|jpg|jpeg|png|gif|webp)(\?.*)?$/,
}),
fonts: () => {
return {
use: [loaders.url({folder: 'fonts'})],
test: /\.(woff|woff2|eot|ttf|otf)$/,
};
},
fonts: () => ({
use: [loaders.url({folder: 'fonts'})],
test: /\.(woff|woff2|eot|ttf|otf)$/,
}),
/**
* Loads audio and video and inlines them via a data URI if they are below
* the size threshold
*/
media: () => {
return {
use: [loaders.url({folder: 'medias'})],
test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/,
};
},
media: () => ({
use: [loaders.url({folder: 'medias'})],
test: /\.(mp4|webm|ogv|wav|mp3|m4a|aac|oga|flac)$/,
}),
svg: () => {
return {
test: /\.svg?$/,
oneOf: [
{
use: [
{
loader: '@svgr/webpack',
options: {
prettier: false,
svgo: true,
svgoConfig: {
plugins: [{removeViewBox: false}],
},
titleProp: true,
ref: ![path],
svg: () => ({
test: /\.svg?$/,
oneOf: [
{
use: [
{
loader: '@svgr/webpack',
options: {
prettier: false,
svgo: true,
svgoConfig: {
plugins: [{removeViewBox: false}],
},
titleProp: true,
ref: ![path],
},
],
// We don't want to use SVGR loader for non-React source code
// ie we don't want to use SVGR for CSS files...
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
],
// We don't want to use SVGR loader for non-React source code
// ie we don't want to use SVGR for CSS files...
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
{
use: [loaders.url({folder: 'images'})],
},
],
};
},
},
{
use: [loaders.url({folder: 'images'})],
},
],
}),
otherAssets: () => {
return {
use: [loaders.file({folder: 'files'})],
test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/,
};
},
otherAssets: () => ({
use: [loaders.file({folder: 'files'})],
test: /\.(pdf|doc|docx|xls|xlsx|zip|rar)$/,
}),
};
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
* to work on the web (<img src=".." /> or in CSS url('..'))
*/
const toBase64 = (extMimeType: string, data: Buffer): string => {
return `data:${extMimeType};base64,${data.toString('base64')}`;
};
const toBase64 = (extMimeType: string, data: Buffer): string =>
`data:${extMimeType};base64,${data.toString('base64')}`;
/**
* 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',
});
module.exports = stylelint.createPlugin(ruleName, (actual) => {
return (root, result) => {
module.exports = stylelint.createPlugin(
ruleName,
(actual) => (root, result) => {
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
actual,
});
@ -48,8 +49,8 @@ module.exports = stylelint.createPlugin(ruleName, (actual) => {
ruleName,
});
});
};
});
},
);
module.exports.ruleName = ruleName;
module.exports.messages = messages;

View file

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

View file

@ -43,55 +43,53 @@ function ShowcaseCardTagIcons({tags}: {tags: TagType[]}) {
);
}
const ShowcaseCard = memo(function ({user}: {user: User}) {
return (
<div key={user.title} className="col col--4 margin-bottom--lg">
<div className={clsx('card', styles.showcaseCard)}>
<div className={clsx('card__image', styles.showcaseCardImage)}>
<Image img={user.preview} alt={user.title} />
</div>
<div className="card__body">
<div className="avatar">
<div className="avatar__intro margin-left--none">
<div className={styles.titleIconsRow}>
<div className={styles.titleIconsRowTitle}>
<div className="avatar__name">{user.title}</div>
</div>
<div className={styles.titleIconsRowIcons}>
<ShowcaseCardTagIcons tags={user.tags} />
</div>
</div>
<small className="avatar__subtitle">{user.description}</small>
</div>
</div>
</div>
{(user.website || user.source) && (
<div className="card__footer">
<div className="button-group button-group--block">
{user.website && (
<a
className="button button--small button--secondary button--block"
href={user.website}
target="_blank"
rel="noreferrer noopener">
Website
</a>
)}
{user.source && (
<a
className="button button--small button--secondary button--block"
href={user.source}
target="_blank"
rel="noreferrer noopener">
Source
</a>
)}
</div>
</div>
)}
const ShowcaseCard = memo(({user}: {user: User}) => (
<div key={user.title} className="col col--4 margin-bottom--lg">
<div className={clsx('card', styles.showcaseCard)}>
<div className={clsx('card__image', styles.showcaseCardImage)}>
<Image img={user.preview} alt={user.title} />
</div>
<div className="card__body">
<div className="avatar">
<div className="avatar__intro margin-left--none">
<div className={styles.titleIconsRow}>
<div className={styles.titleIconsRowTitle}>
<div className="avatar__name">{user.title}</div>
</div>
<div className={styles.titleIconsRowIcons}>
<ShowcaseCardTagIcons tags={user.tags} />
</div>
</div>
<small className="avatar__subtitle">{user.description}</small>
</div>
</div>
</div>
{(user.website || user.source) && (
<div className="card__footer">
<div className="button-group button-group--block">
{user.website && (
<a
className="button button--small button--secondary button--block"
href={user.website}
target="_blank"
rel="noreferrer noopener">
Website
</a>
)}
{user.source && (
<a
className="button button--small button--secondary button--block"
href={user.source}
target="_blank"
rel="noreferrer noopener">
Source
</a>
)}
</div>
</div>
)}
</div>
);
});
</div>
));
export default ShowcaseCard;

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