chore(v2): Fix more eslint errors (#2976)

This commit is contained in:
Sam Zhou 2020-06-21 03:09:00 -04:00 committed by GitHub
parent 3611c96f90
commit 6e43c9bd34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 375 additions and 237 deletions

View file

@ -85,27 +85,23 @@ module.exports = {
], ],
'no-unused-vars': OFF, 'no-unused-vars': OFF,
'@typescript-eslint/no-unused-vars': [ERROR, {argsIgnorePattern: '^_'}], '@typescript-eslint/no-unused-vars': [ERROR, {argsIgnorePattern: '^_'}],
'@typescript-eslint/ban-ts-comment': [
ERROR,
{'ts-expect-error': 'allow-with-description'},
],
// TODO re-enable some these as errors // TODO re-enable some these as errors
// context: https://github.com/facebook/docusaurus/pull/2949 // context: https://github.com/facebook/docusaurus/pull/2949
'@typescript-eslint/ban-ts-comment': WARNING,
'@typescript-eslint/ban-types': WARNING, '@typescript-eslint/ban-types': WARNING,
'import/prefer-default-export': WARNING,
'import/no-extraneous-dependencies': WARNING, 'import/no-extraneous-dependencies': WARNING,
'no-useless-escape': WARNING, 'no-useless-escape': WARNING,
'prefer-template': WARNING, 'prefer-template': WARNING,
'no-shadow': WARNING,
'no-param-reassign': WARNING, 'no-param-reassign': WARNING,
'no-else-return': WARNING,
'no-template-curly-in-string': WARNING, 'no-template-curly-in-string': WARNING,
'array-callback-return': WARNING, 'array-callback-return': WARNING,
camelcase: WARNING, camelcase: WARNING,
'no-nested-ternary': WARNING,
'object-shorthand': WARNING,
'no-restricted-syntax': WARNING, 'no-restricted-syntax': WARNING,
'no-unused-expressions': WARNING, 'no-unused-expressions': WARNING,
'consistent-return': WARNING,
'no-useless-return': WARNING,
'@typescript-eslint/no-empty-function': WARNING, '@typescript-eslint/no-empty-function': WARNING,
'global-require': WARNING, 'global-require': WARNING,
'prefer-destructuring': WARNING, 'prefer-destructuring': WARNING,
@ -114,8 +110,6 @@ module.exports = {
'no-empty': WARNING, 'no-empty': WARNING,
'no-prototype-builtins': WARNING, 'no-prototype-builtins': WARNING,
'no-case-declarations': WARNING, 'no-case-declarations': WARNING,
'default-case': WARNING,
'dot-notation': WARNING,
}, },
overrides: [ overrides: [
{ {

View file

@ -37,8 +37,6 @@
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.9.0", "@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/preset-typescript": "^7.9.0", "@babel/preset-typescript": "^7.9.0",
"@typescript-eslint/eslint-plugin": "^3.3.0",
"@typescript-eslint/parser": "^3.3.0",
"@types/express": "^4.17.2", "@types/express": "^4.17.2",
"@types/fs-extra": "^8.0.1", "@types/fs-extra": "^8.0.1",
"@types/inquirer": "^6.5.0", "@types/inquirer": "^6.5.0",
@ -54,13 +52,18 @@
"@types/lodash.pick": "^4.4.6", "@types/lodash.pick": "^4.4.6",
"@types/lodash.pickby": "^4.6.6", "@types/lodash.pickby": "^4.6.6",
"@types/node": "^13.11.0", "@types/node": "^13.11.0",
"@types/react": "^16.9.13", "@types/react": "^16.9.38",
"@types/react-dev-utils": "^9.0.1", "@types/react-dev-utils": "^9.0.1",
"@types/react-helmet": "^6.0.0",
"@types/react-loadable": "^5.5.3",
"@types/react-router-config": "^5.0.1",
"@types/semver": "^7.1.0", "@types/semver": "^7.1.0",
"@types/shelljs": "^0.8.6", "@types/shelljs": "^0.8.6",
"@types/webpack": "^4.41.0", "@types/webpack": "^4.41.0",
"@types/webpack-dev-server": "^3.9.0", "@types/webpack-dev-server": "^3.9.0",
"@types/webpack-merge": "^4.1.5", "@types/webpack-merge": "^4.1.5",
"@typescript-eslint/eslint-plugin": "^3.3.0",
"@typescript-eslint/parser": "^3.3.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"concurrently": "^5.2.0", "concurrently": "^5.2.0",
"enzyme": "^3.10.0", "enzyme": "^3.10.0",
@ -85,7 +88,7 @@
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"serve": "^11.3.2", "serve": "^11.3.2",
"stylelint": "^13.2.1", "stylelint": "^13.2.1",
"typescript": "^3.7.2" "typescript": "^3.9.5"
}, },
"peerDependencies": { "peerDependencies": {
"stylelint-copyright": "^2.0.0" "stylelint-copyright": "^2.0.0"

View file

@ -11,7 +11,7 @@ const chalk = require('chalk');
const semver = require('semver'); const semver = require('semver');
const path = require('path'); const path = require('path');
const program = require('commander'); const program = require('commander');
const {init} = require('../lib'); const {default: init} = require('../lib');
const requiredVersion = require('../package.json').engines.node; const requiredVersion = require('../package.json').engines.node;
if (!semver.satisfies(process.version, requiredVersion)) { if (!semver.satisfies(process.version, requiredVersion)) {

View file

@ -26,7 +26,10 @@ function isValidGitRepoUrl(gitRepoUrl: string): boolean {
return ['https://', 'git@'].some((item) => gitRepoUrl.startsWith(item)); return ['https://', 'git@'].some((item) => gitRepoUrl.startsWith(item));
} }
async function updatePkg(pkgPath: string, obj: any): Promise<void> { async function updatePkg(
pkgPath: string,
obj: Record<string, unknown>,
): Promise<void> {
const content = await fs.readFile(pkgPath, 'utf-8'); const content = await fs.readFile(pkgPath, 'utf-8');
const pkg = JSON.parse(content); const pkg = JSON.parse(content);
const newPkg = Object.assign(pkg, obj); const newPkg = Object.assign(pkg, obj);
@ -34,7 +37,7 @@ async function updatePkg(pkgPath: string, obj: any): Promise<void> {
await fs.outputFile(pkgPath, JSON.stringify(newPkg, null, 2)); await fs.outputFile(pkgPath, JSON.stringify(newPkg, null, 2));
} }
export async function init( export default async function init(
rootDir: string, rootDir: string,
siteName?: string, siteName?: string,
reqTemplate?: string, reqTemplate?: string,

View file

@ -14,8 +14,11 @@
"dependencies": { "dependencies": {
"@docusaurus/types": "^2.0.0-alpha.58", "@docusaurus/types": "^2.0.0-alpha.58",
"@docusaurus/utils": "^2.0.0-alpha.58", "@docusaurus/utils": "^2.0.0-alpha.58",
"chalk": "^3.0.0",
"eta": "^1.1.1", "eta": "^1.1.1",
"fs-extra": "^8.1.0",
"globby": "^10.0.1", "globby": "^10.0.1",
"lodash": "^4.17.15",
"yup": "^0.29.0" "yup": "^0.29.0"
}, },
"peerDependencies": { "peerDependencies": {

View file

@ -208,7 +208,7 @@ describe('collectRedirects', () => {
{ {
createRedirects: (routePath) => { createRedirects: (routePath) => {
if (routePath === '/') { if (routePath === '/') {
return [[`/fromPath`]] as any; return ([[`/fromPath`]] as unknown) as string;
} }
return undefined; return undefined;
}, },

View file

@ -49,7 +49,7 @@ describe('normalizePluginOptions', () => {
test('should reject bad fromExtensions user inputs', () => { test('should reject bad fromExtensions user inputs', () => {
expect(() => expect(() =>
normalizePluginOptions({ normalizePluginOptions({
fromExtensions: [null, undefined, 123, true] as any, fromExtensions: ([null, undefined, 123, true] as unknown) as string[],
}), }),
).toThrowErrorMatchingSnapshot(); ).toThrowErrorMatchingSnapshot();
}); });
@ -57,7 +57,7 @@ describe('normalizePluginOptions', () => {
test('should reject bad toExtensions user inputs', () => { test('should reject bad toExtensions user inputs', () => {
expect(() => expect(() =>
normalizePluginOptions({ normalizePluginOptions({
toExtensions: [null, undefined, 123, true] as any, toExtensions: ([null, undefined, 123, true] as unknown) as string[],
}), }),
).toThrowErrorMatchingSnapshot(); ).toThrowErrorMatchingSnapshot();
}); });
@ -65,7 +65,10 @@ describe('normalizePluginOptions', () => {
test('should reject bad createRedirects user inputs', () => { test('should reject bad createRedirects user inputs', () => {
expect(() => expect(() =>
normalizePluginOptions({ normalizePluginOptions({
createRedirects: ['bad', 'value'] as any, createRedirects: ([
'bad',
'value',
] as unknown) as CreateRedirectsFnOption,
}), }),
).toThrowErrorMatchingSnapshot(); ).toThrowErrorMatchingSnapshot();
}); });

View file

@ -36,6 +36,7 @@ function validateCollectedRedirects(
.map((redirect) => { .map((redirect) => {
try { try {
validateRedirect(redirect); validateRedirect(redirect);
return undefined;
} catch (e) { } catch (e) {
return e.message; return e.message;
} }
@ -132,13 +133,12 @@ function createRedirectsOptionRedirects(
function optionToRedirects(option: RedirectOption): RedirectMetadata[] { function optionToRedirects(option: RedirectOption): RedirectMetadata[] {
if (typeof option.from === 'string') { if (typeof option.from === 'string') {
return [{from: option.from, to: option.to}]; return [{from: option.from, to: option.to}];
} else { }
return option.from.map((from) => ({ return option.from.map((from) => ({
from, from,
to: option.to, to: option.to,
})); }));
} }
}
return flatten(redirectsOption.map(optionToRedirects)); return flatten(redirectsOption.map(optionToRedirects));
} }

View file

@ -12,6 +12,8 @@ import {RedirectMetadata} from './types';
export const PathnameValidator = Yup.string().test({ export const PathnameValidator = Yup.string().test({
name: 'isValidPathname', name: 'isValidPathname',
message: message:
// Yup requires this format.
// eslint-disable-next-line no-template-curly-in-string
'${path} is not a valid pathname. Pathname should start with / and not contain any domain or query string', '${path} is not a valid pathname. Pathname should start with / and not contain any domain or query string',
test: isValidPathname, test: isValidPathname,
}); });

View file

@ -31,7 +31,7 @@ describe('blogFeed', () => {
routeBasePath: 'blog', routeBasePath: 'blog',
include: ['*.md', '*.mdx'], include: ['*.md', '*.mdx'],
feedOptions: { feedOptions: {
type: feedType as any, type: feedType,
copyright: 'Copyright', copyright: 'Copyright',
}, },
} as PluginOptions, } as PluginOptions,
@ -62,7 +62,7 @@ describe('blogFeed', () => {
routeBasePath: 'blog', routeBasePath: 'blog',
include: ['*r*.md', '*.mdx'], // skip no-date.md - it won't play nice with snapshots include: ['*r*.md', '*.mdx'], // skip no-date.md - it won't play nice with snapshots
feedOptions: { feedOptions: {
type: feedType as any, type: feedType,
copyright: 'Copyright', copyright: 'Copyright',
}, },
} as PluginOptions, } as PluginOptions,

View file

@ -72,10 +72,10 @@ export async function generateBlogFeed(
blogPosts.forEach((post) => { blogPosts.forEach((post) => {
const { const {
id, id,
metadata: {title, permalink, date, description}, metadata: {title: metadataTitle, permalink, date, description},
} = post; } = post;
feed.addItem({ feed.addItem({
title, title: metadataTitle,
id, id,
link: normalizeUrl([siteUrl, permalink]), link: normalizeUrl([siteUrl, permalink]),
date, date,

View file

@ -205,9 +205,8 @@ export default function pluginContentBlog(
label: tag, label: tag,
permalink, permalink,
}; };
} else {
return tag;
} }
return tag;
}); });
}); });
@ -244,7 +243,7 @@ export default function pluginContentBlog(
`~blog/${path.relative(dataDir, source)}`; `~blog/${path.relative(dataDir, source)}`;
const {addRoute, createData} = actions; const {addRoute, createData} = actions;
const { const {
blogPosts, blogPosts: loadedBlogPosts,
blogListPaginated, blogListPaginated,
blogTags, blogTags,
blogTagsListPath, blogTagsListPath,
@ -254,7 +253,7 @@ export default function pluginContentBlog(
// Create routes for blog entries. // Create routes for blog entries.
await Promise.all( await Promise.all(
blogPosts.map(async (blogPost) => { loadedBlogPosts.map(async (blogPost) => {
const {id, metadata} = blogPost; const {id, metadata} = blogPost;
await createData( await createData(
// Note that this created data path must be in sync with // Note that this created data path must be in sync with
@ -292,12 +291,11 @@ export default function pluginContentBlog(
exact: true, exact: true,
modules: { modules: {
items: items.map((postID) => { items: items.map((postID) => {
const metadata = blogItemsToMetadata[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 { return {
content: { content: {
__import: true, __import: true,
path: metadata.source, path: blogItemsToMetadata[postID].source,
query: { query: {
truncated: true, truncated: true,
}, },
@ -481,22 +479,26 @@ export default function pluginContentBlog(
}; };
const headTags: HtmlTags = []; const headTags: HtmlTags = [];
feedTypes.map((feedType) => { feedTypes.forEach((feedType) => {
const feedConfig = feedsConfig[feedType] || {}; const feedConfig = feedsConfig[feedType] || {};
if (!feedsConfig) { if (!feedsConfig) {
return; return;
} }
const {type, path, title} = feedConfig; const {type, path: feedConfigPath, title: feedConfigTitle} = feedConfig;
headTags.push({ headTags.push({
tagName: 'link', tagName: 'link',
attributes: { attributes: {
rel: 'alternate', rel: 'alternate',
type, type,
href: normalizeUrl([baseUrl, options.routeBasePath, path]), href: normalizeUrl([
title, baseUrl,
options.routeBasePath,
feedConfigPath,
]),
title: feedConfigTitle,
}, },
}); });
}); });

View file

@ -170,7 +170,8 @@ describe('simple site', () => {
}); });
// unrelated frontmatter is not part of metadata // unrelated frontmatter is not part of metadata
expect(data['unrelated_frontmatter']).toBeUndefined(); // @ts-expect-error: It doesn't exist, so the test will show it's undefined.
expect(data.unrelated_frontmatter).toBeUndefined();
}); });
test('docs with last update time and author', async () => { test('docs with last update time and author', async () => {

View file

@ -14,15 +14,15 @@ import {
VERSIONED_SIDEBARS_DIR, VERSIONED_SIDEBARS_DIR,
} from './constants'; } from './constants';
export function getVersionedDocsDir(siteDir: string) { export function getVersionedDocsDir(siteDir: string): string {
return path.join(siteDir, VERSIONED_DOCS_DIR); return path.join(siteDir, VERSIONED_DOCS_DIR);
} }
export function getVersionedSidebarsDir(siteDir: string) { export function getVersionedSidebarsDir(siteDir: string): string {
return path.join(siteDir, VERSIONED_SIDEBARS_DIR); return path.join(siteDir, VERSIONED_SIDEBARS_DIR);
} }
export function getVersionsJSONFile(siteDir: string) { export function getVersionsJSONFile(siteDir: string): string {
return path.join(siteDir, VERSIONS_JSON_FILE); return path.join(siteDir, VERSIONS_JSON_FILE);
} }
@ -41,6 +41,7 @@ export default function (siteDir: string): Env {
fs.readFileSync(versionsJSONFile, 'utf8'), fs.readFileSync(versionsJSONFile, 'utf8'),
); );
if (parsedVersions && parsedVersions.length > 0) { if (parsedVersions && parsedVersions.length > 0) {
// eslint-disable-next-line prefer-destructuring
versioning.latestVersion = parsedVersions[0]; versioning.latestVersion = parsedVersions[0];
versioning.enabled = true; versioning.enabled = true;
versioning.versions = parsedVersions; versioning.versions = parsedVersions;

View file

@ -378,9 +378,7 @@ Available document ids=
}), }),
); );
return routes.sort((a, b) => return routes.sort((a, b) => a.path.localeCompare(b.path));
a.path > b.path ? 1 : b.path > a.path ? -1 : 0,
);
}; };
// This is the base route of the document root (for a doc given version) // This is the base route of the document root (for a doc given version)
@ -400,10 +398,10 @@ Available document ids=
// Important: the layout component should not end with /, // Important: the layout component should not end with /,
// as it conflicts with the home doc // as it conflicts with the home doc
// Workaround fix for https://github.com/facebook/docusaurus/issues/2917 // Workaround fix for https://github.com/facebook/docusaurus/issues/2917
const path = docsBaseRoute === '/' ? '' : docsBaseRoute; const docsPath = docsBaseRoute === '/' ? '' : docsBaseRoute;
addRoute({ addRoute({
path, path: docsPath,
exact: false, // allow matching /docs/* as well exact: false, // allow matching /docs/* as well
component: docLayoutComponent, // main docs component (DocPage) component: docLayoutComponent, // main docs component (DocPage)
routes, // subroute for each doc routes, // subroute for each doc
@ -466,7 +464,7 @@ Available document ids=
const routes = await genRoutes(Object.values(content.docsMetadata)); const routes = await genRoutes(Object.values(content.docsMetadata));
const docsBaseMetadata = createDocsBaseMetadata(); const docsBaseMetadata = createDocsBaseMetadata();
const docsBaseRoute = normalizeUrl([baseUrl, routeBasePath]); const docsBaseRoute = normalizeUrl([baseUrl, routeBasePath]);
return addBaseRoute(docsBaseRoute, docsBaseMetadata, routes); await addBaseRoute(docsBaseRoute, docsBaseMetadata, routes);
} }
}, },

View file

@ -41,13 +41,12 @@ function inferVersion(
.replace(/^version-/, ''); .replace(/^version-/, '');
if (inferredVersion && versioning.versions.includes(inferredVersion)) { if (inferredVersion && versioning.versions.includes(inferredVersion)) {
return inferredVersion; return inferredVersion;
} else { }
throw new Error( throw new Error(
`Can't infer version from folder=${dirName} `Can't infer version from folder=${dirName}
Expected versions: Expected versions:
- ${versioning.versions.join('- ')}`, - ${versioning.versions.join('- ')}`,
); );
}
} else { } else {
return 'next'; return 'next';
} }

View file

@ -30,6 +30,7 @@ export default function createOrder(allSidebars: Sidebar = {}): Order {
case 'doc': case 'doc':
ids.push(item.id); ids.push(item.id);
break; break;
default:
} }
}); });
}; };

View file

@ -45,8 +45,12 @@ function normalizeCategoryShorthand(
/** /**
* Check that item contains only allowed keys. * Check that item contains only allowed keys.
*/ */
function assertItem(item: Object, keys: string[]): void { function assertItem<K extends string>(
item: any,
keys: K[],
): asserts item is Record<K, any> {
const unknownKeys = Object.keys(item).filter( const unknownKeys = Object.keys(item).filter(
// @ts-expect-error: key is always string
(key) => !keys.includes(key) && key !== 'type', (key) => !keys.includes(key) && key !== 'type',
); );
@ -59,7 +63,9 @@ function assertItem(item: Object, keys: string[]): void {
} }
} }
function assertIsCategory(item: any): asserts item is SidebarItemCategoryRaw { function assertIsCategory(
item: unknown,
): asserts item is SidebarItemCategoryRaw {
assertItem(item, ['items', 'label', 'collapsed']); assertItem(item, ['items', 'label', 'collapsed']);
if (typeof item.label !== 'string') { if (typeof item.label !== 'string') {
throw new Error( throw new Error(
@ -79,7 +85,7 @@ function assertIsCategory(item: any): asserts item is SidebarItemCategoryRaw {
} }
} }
function assertIsDoc(item: any): asserts item is SidebarItemDoc { function assertIsDoc(item: unknown): asserts item is SidebarItemDoc {
assertItem(item, ['id']); assertItem(item, ['id']);
if (typeof item.id !== 'string') { if (typeof item.id !== 'string') {
throw new Error( throw new Error(
@ -88,7 +94,7 @@ function assertIsDoc(item: any): asserts item is SidebarItemDoc {
} }
} }
function assertIsLink(item: any): asserts item is SidebarItemLink { function assertIsLink(item: unknown): asserts item is SidebarItemLink {
assertItem(item, ['href', 'label']); assertItem(item, ['href', 'label']);
if (typeof item.href !== 'string') { if (typeof item.href !== 'string') {
throw new Error( throw new Error(
@ -175,7 +181,7 @@ export default function loadSidebars(sidebarPaths?: string[]): Sidebar {
return {} as Sidebar; return {} as Sidebar;
} }
sidebarPaths.map((sidebarPath) => { sidebarPaths.forEach((sidebarPath) => {
if (sidebarPath && fs.existsSync(sidebarPath)) { if (sidebarPath && fs.existsSync(sidebarPath)) {
const sidebar = importFresh(sidebarPath) as SidebarRaw; const sidebar = importFresh(sidebarPath) as SidebarRaw;
Object.assign(allSidebars, sidebar); Object.assign(allSidebars, sidebar);

View file

@ -5,13 +5,20 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
let versions: string[] = []; let versions: string[];
try { try {
// eslint-disable-next-line global-require
versions = require('@site/versions.json'); versions = require('@site/versions.json');
} catch (e) {} } catch {
versions = [];
}
function useVersioning() { function useVersioning(): {
versioningEnabled: boolean;
versions: string[];
latestVersion: string;
} {
return { return {
versioningEnabled: versions.length > 0, versioningEnabled: versions.length > 0,
versions, versions,

View file

@ -65,7 +65,7 @@ export type SidebarItemRaw =
| SidebarItemCategoryRaw | SidebarItemCategoryRaw
| { | {
type: string; type: string;
[key: string]: any; [key: string]: unknown;
}; };
export interface SidebarCategoryShorthandRaw { export interface SidebarCategoryShorthandRaw {

View file

@ -15,6 +15,8 @@ import path from 'path';
import {Sidebar, PathOptions, SidebarItem} from './types'; import {Sidebar, PathOptions, SidebarItem} from './types';
import loadSidebars from './sidebars'; import loadSidebars from './sidebars';
// Tests depend on non-default export for mocking.
// eslint-disable-next-line import/prefer-default-export
export function docsVersion( export function docsVersion(
version: string | null | undefined, version: string | null | undefined,
siteDir: string, siteDir: string,

View file

@ -4,13 +4,16 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {LoadContext} from '@docusaurus/types'; import {LoadContext, Plugin} from '@docusaurus/types';
import {PluginOptions} from './types'; import {PluginOptions} from './types';
import {Configuration} from 'webpack'; import {Configuration} from 'webpack';
import path from 'path'; import path from 'path';
export default function (_context: LoadContext, options: PluginOptions) { export default function (
_context: LoadContext,
options: PluginOptions,
): Plugin<void> {
const isProd = process.env.NODE_ENV === 'production'; const isProd = process.env.NODE_ENV === 'production';
return { return {

View file

@ -13,6 +13,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@docusaurus/types": "^2.0.0-alpha.58", "@docusaurus/types": "^2.0.0-alpha.58",
"fs-extra": "^8.1.0",
"sitemap": "^3.2.2" "sitemap": "^3.2.2"
}, },
"peerDependencies": { "peerDependencies": {

View file

@ -28,7 +28,7 @@ describe('createSitemap', () => {
test('empty site', () => { test('empty site', () => {
expect(() => { expect(() => {
createSitemap({} as DocusaurusConfig, [], {} as any); createSitemap({} as DocusaurusConfig, [], {});
}).toThrowErrorMatchingInlineSnapshot( }).toThrowErrorMatchingInlineSnapshot(
`"url in docusaurus.config.js cannot be empty/undefined"`, `"url in docusaurus.config.js cannot be empty/undefined"`,
); );

View file

@ -13,7 +13,7 @@ export default function createSitemap(
siteConfig: DocusaurusConfig, siteConfig: DocusaurusConfig,
routesPaths: string[], routesPaths: string[],
options: PluginOptions, options: PluginOptions,
) { ): sitemap.Sitemap {
const {url: hostname} = siteConfig; const {url: hostname} = siteConfig;
if (!hostname) { if (!hostname) {
throw new Error('url in docusaurus.config.js cannot be empty/undefined'); throw new Error('url in docusaurus.config.js cannot be empty/undefined');

View file

@ -11,7 +11,7 @@ import {PluginOptions} from './types';
import createSitemap from './createSitemap'; import createSitemap from './createSitemap';
import {LoadContext, Props, Plugin} from '@docusaurus/types'; import {LoadContext, Props, Plugin} from '@docusaurus/types';
const DEFAULT_OPTIONS: PluginOptions = { const DEFAULT_OPTIONS: Required<PluginOptions> = {
cacheTime: 600 * 1000, // 600 sec - cache purge period. cacheTime: 600 * 1000, // 600 sec - cache purge period.
changefreq: 'weekly', changefreq: 'weekly',
priority: 0.5, priority: 0.5,

View file

@ -6,7 +6,7 @@
*/ */
export interface PluginOptions { export interface PluginOptions {
cacheTime: number; cacheTime?: number;
changefreq: string; changefreq?: string;
priority: number; priority?: number;
} }

View file

@ -52,6 +52,8 @@ function Footer() {
<li <li
key={key} key={key}
className="mb-2" className="mb-2"
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: item.html, __html: item.html,
}} }}

View file

@ -32,6 +32,8 @@ function AnnouncementBar() {
role="banner"> role="banner">
<div <div
className={styles.announcementBarContent} className={styles.announcementBarContent}
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: content}} dangerouslySetInnerHTML={{__html: content}}
/> />

View file

@ -47,6 +47,8 @@ function Headings({headings, isChild}) {
<a <a
href={`#${heading.id}`} href={`#${heading.id}`}
className={LINK_CLASS_NAME} className={LINK_CLASS_NAME}
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: heading.value}} dangerouslySetInnerHTML={{__html: heading.value}}
/> />
<Headings isChild headings={heading.children} /> <Headings isChild headings={heading.children} />

View file

@ -74,6 +74,8 @@ function Footer() {
<li <li
key={key} key={key}
className="footer__item" className="footer__item"
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: item.html, __html: item.html,
}} }}
@ -109,6 +111,8 @@ function Footer() {
)} )}
<div <div
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: copyright, __html: copyright,
}} }}

View file

@ -323,6 +323,8 @@ function Search() {
{breadcrumbs.length > 0 && ( {breadcrumbs.length > 0 && (
<span <span
className={styles.searchResultItemPath} className={styles.searchResultItemPath}
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: breadcrumbs.join(' '), __html: breadcrumbs.join(' '),
}} }}
@ -332,6 +334,8 @@ function Search() {
{summary && ( {summary && (
<p <p
className={styles.searchResultItemSummary} className={styles.searchResultItemSummary}
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: summary}} dangerouslySetInnerHTML={{__html: summary}}
/> />
)} )}

View file

@ -22,23 +22,23 @@ export interface DocusaurusConfig {
themes?: PluginConfig[]; themes?: PluginConfig[];
presets?: PresetConfig[]; presets?: PresetConfig[];
themeConfig?: { themeConfig?: {
[key: string]: any; [key: string]: unknown;
}; };
customFields?: { customFields?: {
[key: string]: any; [key: string]: unknown;
}; };
scripts?: ( scripts?: (
| string | string
| { | {
src: string; src: string;
[key: string]: any; [key: string]: unknown;
} }
)[]; )[];
stylesheets?: ( stylesheets?: (
| string | string
| { | {
href: string; href: string;
[key: string]: any; [key: string]: unknown;
} }
)[]; )[];
} }
@ -53,7 +53,10 @@ export interface Preset {
themes?: PluginConfig[]; themes?: PluginConfig[];
} }
export type PresetConfig = [string, Object] | [string] | string; export type PresetConfig =
| [string, Record<string, unknown>]
| [string]
| string;
export interface StartCLIOptions { export interface StartCLIOptions {
port: string; port: string;
@ -88,7 +91,7 @@ export type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
export interface Props extends LoadContext, InjectedHtmlTags { export interface Props extends LoadContext, InjectedHtmlTags {
routesPaths: string[]; routesPaths: string[];
plugins: Plugin<any>[]; plugins: Plugin<unknown>[];
} }
export interface PluginContentLoadedActions { export interface PluginContentLoadedActions {
@ -125,7 +128,10 @@ export interface Plugin<T> {
}; };
} }
export type PluginConfig = [string, Object] | [string] | string; export type PluginConfig =
| [string, Record<string, unknown>]
| [string]
| string;
export interface ChunkRegistry { export interface ChunkRegistry {
loader: string; loader: string;
@ -165,11 +171,17 @@ export interface ConfigureWebpackUtils {
getStyleLoaders: ( getStyleLoaders: (
isServer: boolean, isServer: boolean,
cssOptions: { cssOptions: {
[key: string]: any; [key: string]: unknown;
}, },
) => Loader[]; ) => Loader[];
getCacheLoader: (isServer: boolean, cacheOptions?: {}) => Loader | null; getCacheLoader: (
getBabelLoader: (isServer: boolean, babelOptions?: {}) => Loader; isServer: boolean,
cacheOptions?: Record<string, unknown>,
) => Loader | null;
getBabelLoader: (
isServer: boolean,
babelOptions?: Record<string, unknown>,
) => Loader;
} }
interface HtmlTagObject { interface HtmlTagObject {

View file

@ -160,6 +160,8 @@ export function genChunkName(
return chunkName; return chunkName;
} }
// Too dynamic
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
export function idx(target: any, keyPaths?: string | (string | number)[]): any { export function idx(target: any, keyPaths?: string | (string | number)[]): any {
return ( return (
target && target &&
@ -231,13 +233,15 @@ export function createExcerpt(fileString: string): string | undefined {
type ParsedMarkdown = { type ParsedMarkdown = {
frontMatter: { frontMatter: {
// Returned by gray-matter
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any; [key: string]: any;
}; };
content: string; content: string;
excerpt: string | undefined; excerpt: string | undefined;
}; };
export function parseMarkdownString(markdownString: string): ParsedMarkdown { export function parseMarkdownString(markdownString: string): ParsedMarkdown {
const options: {} = { const options: Record<string, unknown> = {
excerpt: (file: matter.GrayMatterFile<string>): void => { excerpt: (file: matter.GrayMatterFile<string>): void => {
// Hacky way of stripping out import statements from the excerpt // Hacky way of stripping out import statements from the excerpt
// TODO: Find a better way to do so, possibly by compiling the Markdown content, // TODO: Find a better way to do so, possibly by compiling the Markdown content,

View file

@ -50,7 +50,7 @@ function getTransformOptions(isServer: boolean): TransformOptions {
// By default, it assumes @babel/runtime@7.0.0. Since we use >7.0.0, better to // By default, it assumes @babel/runtime@7.0.0. Since we use >7.0.0, better to
// explicitly specify the version so that it can reuse the helper better // explicitly specify the version so that it can reuse the helper better
// See https://github.com/babel/babel/issues/10261 // See https://github.com/babel/babel/issues/10261
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
version: require('@babel/runtime/package.json').version, version: require('@babel/runtime/package.json').version,
regenerator: true, regenerator: true,
useESModules: true, useESModules: true,

View file

@ -28,7 +28,7 @@ interface State {
class PendingNavigation extends React.Component<Props, State> { class PendingNavigation extends React.Component<Props, State> {
previousLocation: any; previousLocation: any;
progressBarTimeout: any; progressBarTimeout: NodeJS.Timeout | null;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
@ -43,7 +43,7 @@ class PendingNavigation extends React.Component<Props, State> {
// Intercept location update and still show current route until next route // Intercept location update and still show current route until next route
// is done loading. // is done loading.
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps: Props, nextState: State) {
const routeDidChange = nextProps.location !== this.props.location; const routeDidChange = nextProps.location !== this.props.location;
const {routes, delay = 1000} = this.props; const {routes, delay = 1000} = this.props;

View file

@ -5,9 +5,20 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// too dynamic
/* eslint-disable @typescript-eslint/no-explicit-any */
import clientModules from '@generated/client-modules'; import clientModules from '@generated/client-modules';
function dispatchLifecycleAction(lifecycleAction, ...args) { interface Dispatchers {
onRouteUpdate: (...args: any) => void;
onRouteUpdateDelayed: (...args: any) => void;
}
function dispatchLifecycleAction(
lifecycleAction: keyof Dispatchers,
...args: any[]
) {
clientModules.forEach((clientModule) => { clientModules.forEach((clientModule) => {
const mod = clientModule.__esModule ? clientModule.default : clientModule; const mod = clientModule.__esModule ? clientModule.default : clientModule;
if (mod && mod[lifecycleAction]) { if (mod && mod[lifecycleAction]) {
@ -16,26 +27,13 @@ function dispatchLifecycleAction(lifecycleAction, ...args) {
}); });
} }
interface Dispatchers { const clientLifecyclesDispatchers: Dispatchers = {
onRouteUpdate: Function; onRouteUpdate(...args) {
onRouteUpdateDelayed: Function; dispatchLifecycleAction('onRouteUpdate', ...args);
}
function createLifecyclesDispatcher(): Dispatchers {
// TODO: Not sure whether it's better to declare an explicit object
// with all the lifecycles. It's better for typing but quite verbose.
// On the other hand, there's some runtime cost generating this object
// on initial load.
return ['onRouteUpdate', 'onRouteUpdateDelayed'].reduce(
(lifecycles, lifecycleAction) => {
// eslint-disable-next-line no-param-reassign
lifecycles[lifecycleAction] = function (...args) {
dispatchLifecycleAction(lifecycleAction, ...args);
};
return lifecycles;
}, },
{}, onRouteUpdateDelayed(...args) {
) as Dispatchers; dispatchLifecycleAction('onRouteUpdateDelayed', ...args);
} },
};
export default createLifecyclesDispatcher(); export default clientLifecyclesDispatchers;

View file

@ -17,7 +17,7 @@ import docusaurus from './docusaurus';
declare global { declare global {
interface NodeModule { interface NodeModule {
hot?: any; hot?: {accept: () => void};
} }
} }

View file

@ -15,8 +15,8 @@ const fetched = {};
const loaded = {}; const loaded = {};
declare global { declare global {
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase, @typescript-eslint/no-explicit-any
const __webpack_require__: any; const __webpack_require__: {gca: (name: string) => string};
interface Navigator { interface Navigator {
connection: any; connection: any;
} }
@ -35,13 +35,14 @@ const isSlowConnection = () => {
return false; return false;
}; };
const canPrefetch = (routePath) => const canPrefetch = (routePath: string) =>
!isSlowConnection() && !loaded[routePath] && !fetched[routePath]; !isSlowConnection() && !loaded[routePath] && !fetched[routePath];
const canPreload = (routePath) => !isSlowConnection() && !loaded[routePath]; const canPreload = (routePath: string) =>
!isSlowConnection() && !loaded[routePath];
const docusaurus = { const docusaurus = {
prefetch: (routePath) => { prefetch: (routePath: string): boolean => {
if (!canPrefetch(routePath)) { if (!canPrefetch(routePath)) {
return false; return false;
} }
@ -50,13 +51,13 @@ const docusaurus = {
// Find all webpack chunk names needed. // Find all webpack chunk names needed.
const matches = matchRoutes(routes, routePath); const matches = matchRoutes(routes, routePath);
const chunkNamesNeeded = matches.reduce((arr, match) => { const chunkNamesNeeded = matches.reduce((arr: string[], match) => {
const chunk = routesChunkNames[match.route.path]; const chunk = routesChunkNames[match.route.path as string];
if (!chunk) { if (!chunk) {
return arr; return arr;
} }
const chunkNames = Object.values(flat(chunk)); const chunkNames = Object.values(flat(chunk)) as string[];
return arr.concat(chunkNames); return arr.concat(chunkNames);
}, []); }, []);
@ -77,7 +78,7 @@ const docusaurus = {
return true; return true;
}, },
preload: (routePath) => { preload: (routePath: string): boolean => {
if (!canPreload(routePath)) { if (!canPreload(routePath)) {
return false; return false;
} }

View file

@ -8,9 +8,15 @@
import React from 'react'; import React from 'react';
import ExecutionEnvironment from './ExecutionEnvironment'; import ExecutionEnvironment from './ExecutionEnvironment';
function BrowserOnly({children, fallback}) { function BrowserOnly({
children,
fallback,
}: {
children?: () => JSX.Element;
fallback?: JSX.Element;
}): JSX.Element | undefined {
if (!ExecutionEnvironment.canUseDOM || children == null) { if (!ExecutionEnvironment.canUseDOM || children == null) {
return fallback || null; return fallback || undefined;
} }
return <>{children()}</>; return <>{children()}</>;

View file

@ -12,7 +12,7 @@ import routesChunkNames from '@generated/routesChunkNames';
import registry from '@generated/registry'; import registry from '@generated/registry';
import flat from '../flat'; import flat from '../flat';
function ComponentCreator(path: string) { function ComponentCreator(path: string): ReturnType<typeof Loadable> {
// 404 page // 404 page
if (path === '*') { if (path === '*') {
return Loadable({ return Loadable({

View file

@ -15,7 +15,8 @@ const ExecutionEnvironment = {
canUseDOM, canUseDOM,
canUseEventListeners: canUseEventListeners:
// @ts-ignore // @ts-expect-error: window.attachEvent is IE specific.
// See https://github.com/Microsoft/TypeScript/issues/3953#issuecomment-123396830
canUseDOM && !!(window.addEventListener || window.attachEvent), canUseDOM && !!(window.addEventListener || window.attachEvent),
canUseIntersectionObserver: canUseDOM && 'IntersectionObserver' in window, canUseIntersectionObserver: canUseDOM && 'IntersectionObserver' in window,

View file

@ -6,9 +6,9 @@
*/ */
import React from 'react'; import React from 'react';
import {Helmet} from 'react-helmet'; import {Helmet, HelmetProps} from 'react-helmet';
function Head(props): JSX.Element { function Head(props: HelmetProps): JSX.Element {
return <Helmet {...props} />; return <Helmet {...props} />;
} }

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, {useEffect, useRef} from 'react'; import React, {ReactNode, useEffect, useRef} from 'react';
import {NavLink, Link as RRLink} from 'react-router-dom'; import {NavLink, Link as RRLink} from 'react-router-dom';
import isInternalUrl from './isInternalUrl'; import isInternalUrl from './isInternalUrl';
@ -21,6 +21,7 @@ interface Props {
readonly isNavLink?: boolean; readonly isNavLink?: boolean;
readonly to?: string; readonly to?: string;
readonly href: string; readonly href: string;
readonly children?: ReactNode;
} }
function Link({isNavLink, ...props}: Props): JSX.Element { function Link({isNavLink, ...props}: Props): JSX.Element {
@ -85,6 +86,7 @@ function Link({isNavLink, ...props}: Props): JSX.Element {
return !targetLink || !isInternal || targetLink.startsWith('#') ? ( return !targetLink || !isInternal || targetLink.startsWith('#') ? (
// eslint-disable-next-line jsx-a11y/anchor-has-content // eslint-disable-next-line jsx-a11y/anchor-has-content
<a <a
// @ts-expect-error: href specified twice needed to pass children and other user specified props
href={targetLink} href={targetLink}
{...(!isInternal && {target: '_blank', rel: 'noopener noreferrer'})} {...(!isInternal && {target: '_blank', rel: 'noopener noreferrer'})}
{...props} {...props}

View file

@ -5,7 +5,9 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
function flat(target) { // Too dynamic
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function flat(target: unknown): any {
const delimiter = '.'; const delimiter = '.';
const output = {}; const output = {};

View file

@ -5,10 +5,12 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// Memoize previously normalized pathnames. type Location = {pathname: string};
const pathnames = {};
function normalizeLocation(location) { // Memoize previously normalized pathnames.
const pathnames: Record<string, string> = {};
function normalizeLocation<T extends Location>(location: T): T {
if (pathnames[location.pathname]) { if (pathnames[location.pathname]) {
return { return {
...location, ...location,

View file

@ -5,24 +5,29 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {matchRoutes} from 'react-router-config'; import {matchRoutes, RouteConfig} from 'react-router-config';
/** /**
* Helper function to make sure all async components for that particular route * Helper function to make sure all async components for that particular route
* is preloaded before rendering. This is especially useful to avoid loading screens. * is preloaded before rendering. This is especially useful to avoid loading screens.
* *
* @param {Array<RouteConfig>} routes react-router-config * @param routes react-router-config
* @param {string} pathname the route pathname, example: /docs/installation * @param pathname the route pathname, example: /docs/installation
* @returns {Promise} Promise object represents whether pathname has been preloaded * @returns Promise object represents whether pathname has been preloaded
*/ */
export default function preload(routes, pathname: string) { export default function preload(
routes: RouteConfig[],
pathname: string,
): Promise<void[]> {
const matches = matchRoutes(routes, pathname); const matches = matchRoutes(routes, pathname);
return Promise.all( return Promise.all(
matches.map((match) => { matches.map((match) => {
const {component} = match.route; const {component} = match.route;
// @ts-expect-error: ComponentCreator injected this method.
if (component && component.preload) { if (component && component.preload) {
// @ts-expect-error: checked above.
return component.preload(); return component.preload();
} }

View file

@ -16,12 +16,12 @@ import merge from 'webpack-merge';
import {STATIC_DIR_NAME} from '../constants'; import {STATIC_DIR_NAME} from '../constants';
import {load} from '../server'; import {load} from '../server';
import {BuildCLIOptions, Props} from '@docusaurus/types'; import {BuildCLIOptions, Props} from '@docusaurus/types';
import {createClientConfig} from '../webpack/client'; import createClientConfig from '../webpack/client';
import {createServerConfig} from '../webpack/server'; import createServerConfig from '../webpack/server';
import {applyConfigureWebpack} from '../webpack/utils'; import {applyConfigureWebpack} from '../webpack/utils';
import CleanWebpackPlugin from '../webpack/plugins/CleanWebpackPlugin'; import CleanWebpackPlugin from '../webpack/plugins/CleanWebpackPlugin';
function compile(config: Configuration[]): Promise<any> { function compile(config: Configuration[]): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const compiler = webpack(config); const compiler = webpack(config);
compiler.run((err, stats) => { compiler.run((err, stats) => {
@ -55,7 +55,7 @@ function compile(config: Configuration[]): Promise<any> {
}); });
} }
export async function build( export default async function build(
siteDir: string, siteDir: string,
cliOptions: Partial<BuildCLIOptions> = {}, cliOptions: Partial<BuildCLIOptions> = {},
forceTerminate: boolean = true, forceTerminate: boolean = true,

View file

@ -10,11 +10,11 @@ import path from 'path';
import shell from 'shelljs'; import shell from 'shelljs';
import {CONFIG_FILE_NAME, GENERATED_FILES_DIR_NAME} from '../constants'; import {CONFIG_FILE_NAME, GENERATED_FILES_DIR_NAME} from '../constants';
import {loadContext} from '../server'; import {loadContext} from '../server';
import {loadConfig} from '../server/config'; import loadConfig from '../server/config';
import {build} from './build'; import build from './build';
import {BuildCLIOptions} from '@docusaurus/types'; import {BuildCLIOptions} from '@docusaurus/types';
export async function deploy( export default async function deploy(
siteDir: string, siteDir: string,
cliOptions: Partial<BuildCLIOptions> = {}, cliOptions: Partial<BuildCLIOptions> = {},
): Promise<void> { ): Promise<void> {
@ -98,7 +98,7 @@ export async function deploy(
// out to deployment branch. // out to deployment branch.
const currentCommit = shell.exec('git rev-parse HEAD').stdout.trim(); const currentCommit = shell.exec('git rev-parse HEAD').stdout.trim();
const runDeploy = (outDir) => { const runDeploy = (outputDirectory) => {
if (shell.cd(tempDir).code !== 0) { if (shell.cd(tempDir).code !== 0) {
throw new Error( throw new Error(
`Temp dir ${GENERATED_FILES_DIR_NAME} does not exists. Run build website first.`, `Temp dir ${GENERATED_FILES_DIR_NAME} does not exists. Run build website first.`,
@ -141,7 +141,7 @@ export async function deploy(
shell.cd('../..'); shell.cd('../..');
const fromPath = outDir; const fromPath = outputDirectory;
const toPath = path.join( const toPath = path.join(
GENERATED_FILES_DIR_NAME, GENERATED_FILES_DIR_NAME,
`${projectName}-${deploymentBranch}`, `${projectName}-${deploymentBranch}`,

View file

@ -7,9 +7,12 @@
import {CommanderStatic} from 'commander'; import {CommanderStatic} from 'commander';
import {loadContext, loadPluginConfigs} from '../server'; import {loadContext, loadPluginConfigs} from '../server';
import {initPlugins} from '../server/plugins/init'; import initPlugins from '../server/plugins/init';
export function externalCommand(cli: CommanderStatic, siteDir: string): void { export default function externalCommand(
cli: CommanderStatic,
siteDir: string,
): void {
const context = loadContext(siteDir); const context = loadContext(siteDir);
const pluginConfigs = loadPluginConfigs(context); const pluginConfigs = loadPluginConfigs(context);
const plugins = initPlugins({pluginConfigs, context}); const plugins = initPlugins({pluginConfigs, context});

View file

@ -23,7 +23,7 @@ import HotModuleReplacementPlugin from 'webpack/lib/HotModuleReplacementPlugin';
import {load} from '../server'; import {load} from '../server';
import {StartCLIOptions} from '@docusaurus/types'; import {StartCLIOptions} from '@docusaurus/types';
import {CONFIG_FILE_NAME, STATIC_DIR_NAME, DEFAULT_PORT} from '../constants'; import {CONFIG_FILE_NAME, STATIC_DIR_NAME, DEFAULT_PORT} from '../constants';
import {createClientConfig} from '../webpack/client'; import createClientConfig from '../webpack/client';
import {applyConfigureWebpack} from '../webpack/utils'; import {applyConfigureWebpack} from '../webpack/utils';
function getHost(reqHost: string | undefined): string { function getHost(reqHost: string | undefined): string {
@ -36,7 +36,7 @@ async function getPort(reqPort: string | undefined): Promise<number> {
return port; return port;
} }
export async function start( export default async function start(
siteDir: string, siteDir: string,
cliOptions: Partial<StartCLIOptions> = {}, cliOptions: Partial<StartCLIOptions> = {},
): Promise<void> { ): Promise<void> {
@ -65,9 +65,7 @@ export async function start(
const pluginPaths: string[] = ([] as string[]) const pluginPaths: string[] = ([] as string[])
.concat( .concat(
...plugins ...plugins
.map<any>( .map((plugin) => plugin.getPathsToWatch?.() ?? [])
(plugin) => plugin.getPathsToWatch && plugin.getPathsToWatch(),
)
.filter(Boolean), .filter(Boolean),
) )
.map(normalizeToSiteDir); .map(normalizeToSiteDir);
@ -172,7 +170,9 @@ export async function start(
if (err) { if (err) {
console.log(err); console.log(err);
} }
cliOptions.open && openBrowser(openUrl); if (cliOptions.open) {
openBrowser(openUrl);
}
}); });
['SIGINT', 'SIGTERM'].forEach((sig) => { ['SIGINT', 'SIGTERM'].forEach((sig) => {
process.on(sig as NodeJS.Signals, () => { process.on(sig as NodeJS.Signals, () => {

View file

@ -9,19 +9,22 @@ import chalk = require('chalk');
import fs from 'fs-extra'; import fs from 'fs-extra';
import importFresh from 'import-fresh'; import importFresh from 'import-fresh';
import path from 'path'; import path from 'path';
import {Plugin, LoadContext} from '@docusaurus/types';
import {THEME_PATH} from '../constants'; import {THEME_PATH} from '../constants';
import {loadContext} from '../server'; import {loadContext} from '../server';
export async function swizzle( export default async function swizzle(
siteDir: string, siteDir: string,
themeName: string, themeName: string,
componentName?: string, componentName?: string,
): Promise<void> { ): Promise<void> {
const plugin: any = importFresh(themeName); const plugin = importFresh(themeName) as (
context: LoadContext,
) => Plugin<unknown>;
const context = loadContext(siteDir); const context = loadContext(siteDir);
const pluginInstance = plugin(context); const pluginInstance = plugin(context);
let fromPath = pluginInstance.getThemePath(); let fromPath = pluginInstance.getThemePath?.();
if (fromPath) { if (fromPath) {
let toPath = path.resolve(siteDir, THEME_PATH); let toPath = path.resolve(siteDir, THEME_PATH);

View file

@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
export {build} from './commands/build'; export {default as build} from './commands/build';
export {start} from './commands/start'; export {default as start} from './commands/start';
export {swizzle} from './commands/swizzle'; export {default as swizzle} from './commands/swizzle';
export {deploy} from './commands/deploy'; export {default as deploy} from './commands/deploy';
export {externalCommand} from './commands/external'; export {default as externalCommand} from './commands/external';

View file

@ -6,7 +6,7 @@
*/ */
import path from 'path'; import path from 'path';
import {loadConfig} from '../config'; import loadConfig from '../config';
describe('loadConfig', () => { describe('loadConfig', () => {
test('website with valid siteConfig', async () => { test('website with valid siteConfig', async () => {

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 {loadRoutes} from '../routes'; import loadRoutes from '../routes';
import {RouteConfig} from '@docusaurus/types'; import {RouteConfig} from '@docusaurus/types';
describe('loadRoutes', () => { describe('loadRoutes', () => {

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 {loadClientModules} from '../index'; import loadClientModules from '../index';
import pluginEmpty from './__fixtures__/plugin-empty'; import pluginEmpty from './__fixtures__/plugin-empty';
import pluginFooBar from './__fixtures__/plugin-foo-bar'; import pluginFooBar from './__fixtures__/plugin-foo-bar';

View file

@ -7,12 +7,12 @@
import {Plugin} from '@docusaurus/types'; import {Plugin} from '@docusaurus/types';
export function loadClientModules(plugins: Plugin<unknown>[]): string[] { export default function loadClientModules(
plugins: Plugin<unknown>[],
): string[] {
return ([] as string[]).concat( return ([] as string[]).concat(
...plugins ...plugins
.map<any>( .map((plugin) => plugin.getClientModules?.() ?? [])
(plugin) => plugin.getClientModules && plugin.getClientModules(),
)
.filter(Boolean), .filter(Boolean),
); );
} }

View file

@ -48,7 +48,7 @@ function formatFields(fields: string[]): string {
return fields.map((field) => `'${field}'`).join(', '); return fields.map((field) => `'${field}'`).join(', ');
} }
export function loadConfig(siteDir: string): DocusaurusConfig { export default function loadConfig(siteDir: string): DocusaurusConfig {
const configPath = path.resolve(siteDir, CONFIG_FILE_NAME); const configPath = path.resolve(siteDir, CONFIG_FILE_NAME);
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath)) {

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 {htmlTagObjectToString} from '../htmlTags'; import htmlTagObjectToString from '../htmlTags';
describe('htmlTagObjectToString', () => { describe('htmlTagObjectToString', () => {
test('simple html tag', () => { test('simple html tag', () => {

View file

@ -10,10 +10,11 @@ import {HtmlTagObject} from '@docusaurus/types';
import htmlTags from 'html-tags'; import htmlTags from 'html-tags';
import voidHtmlTags from 'html-tags/void'; import voidHtmlTags from 'html-tags/void';
function assertIsHtmlTagObject(val: any): asserts val is HtmlTagObject { function assertIsHtmlTagObject(val: unknown): asserts val is HtmlTagObject {
if (!isPlainObject(val)) { if (!isPlainObject(val)) {
throw new Error(`"${val}" is not a valid HTML tag object`); throw new Error(`"${val}" is not a valid HTML tag object`);
} }
// @ts-expect-error: If tagName doesn't exist, it will throw.
if (typeof val.tagName !== 'string') { if (typeof val.tagName !== 'string') {
throw new Error( throw new Error(
`${JSON.stringify( `${JSON.stringify(
@ -23,7 +24,7 @@ function assertIsHtmlTagObject(val: any): asserts val is HtmlTagObject {
} }
} }
export function htmlTagObjectToString(tagDefinition: unknown): string { export default function htmlTagObjectToString(tagDefinition: unknown): string {
assertIsHtmlTagObject(tagDefinition); assertIsHtmlTagObject(tagDefinition);
if (htmlTags.indexOf(tagDefinition.tagName) === -1) { if (htmlTags.indexOf(tagDefinition.tagName) === -1) {
throw new Error( throw new Error(

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 {htmlTagObjectToString} from './htmlTags'; import htmlTagObjectToString from './htmlTags';
import { import {
Plugin, Plugin,
InjectedHtmlTags, InjectedHtmlTags,
@ -21,7 +21,7 @@ export function createHtmlTagsString(tags: HtmlTags): string {
return Array.isArray(tags) ? tags.map(toString).join('\n') : toString(tags); return Array.isArray(tags) ? tags.map(toString).join('\n') : toString(tags);
} }
export function loadHtmlTags(plugins: Plugin<any>[]): InjectedHtmlTags { export function loadHtmlTags(plugins: Plugin<unknown>[]): InjectedHtmlTags {
const htmlTags = plugins.reduce( const htmlTags = plugins.reduce(
(acc, plugin) => { (acc, plugin) => {
if (!plugin.injectHtmlTags) { if (!plugin.injectHtmlTags) {

View file

@ -13,12 +13,12 @@ import {
GENERATED_FILES_DIR_NAME, GENERATED_FILES_DIR_NAME,
THEME_PATH, THEME_PATH,
} from '../constants'; } from '../constants';
import {loadClientModules} from './client-modules'; import loadClientModules from './client-modules';
import {loadConfig} from './config'; import loadConfig from './config';
import {loadPlugins} from './plugins'; import {loadPlugins} from './plugins';
import {loadPresets} from './presets'; import loadPresets from './presets';
import {loadRoutes} from './routes'; import loadRoutes from './routes';
import {loadThemeAlias} from './themes'; import loadThemeAlias from './themes';
import { import {
DocusaurusConfig, DocusaurusConfig,
LoadContext, LoadContext,
@ -84,11 +84,9 @@ export async function load(
// Themes. // Themes.
const fallbackTheme = path.resolve(__dirname, '../client/theme-fallback'); const fallbackTheme = path.resolve(__dirname, '../client/theme-fallback');
const pluginThemes = ([] as string[]).concat( const pluginThemes: string[] = plugins
...plugins .map((plugin) => plugin.getThemePath && plugin.getThemePath())
.map<any>((plugin) => plugin.getThemePath && plugin.getThemePath()) .filter((x): x is string => Boolean(x));
.filter(Boolean),
);
const userTheme = path.resolve(siteDir, THEME_PATH); const userTheme = path.resolve(siteDir, THEME_PATH);
const alias = loadThemeAlias([fallbackTheme, ...pluginThemes], [userTheme]); const alias = loadThemeAlias([fallbackTheme, ...pluginThemes], [userTheme]);

View file

@ -10,7 +10,7 @@ import {load} from './index';
import {Props} from '@docusaurus/types'; import {Props} from '@docusaurus/types';
// Helper methods to setup dummy/fake projects. // Helper methods to setup dummy/fake projects.
export const loadSetup = async (name: string): Promise<Props> => { const loadSetup = async (name: string): Promise<Props> => {
const fixtures = path.join(__dirname, '__tests__', '__fixtures__'); const fixtures = path.join(__dirname, '__tests__', '__fixtures__');
const simpleSite = path.join(fixtures, 'simple-site'); const simpleSite = path.join(fixtures, 'simple-site');
const customSite = path.join(fixtures, 'custom-site'); const customSite = path.join(fixtures, 'custom-site');
@ -23,3 +23,5 @@ export const loadSetup = async (name: string): Promise<Props> => {
return load(simpleSite); return load(simpleSite);
} }
}; };
export default loadSetup;

View file

@ -15,7 +15,7 @@ import {
PluginContentLoadedActions, PluginContentLoadedActions,
RouteConfig, RouteConfig,
} from '@docusaurus/types'; } from '@docusaurus/types';
import {initPlugins} from './init'; import initPlugins from './init';
export function sortConfig(routeConfigs: RouteConfig[]): void { export function sortConfig(routeConfigs: RouteConfig[]): void {
// Sort the route config. This ensures that route with nested // Sort the route config. This ensures that route with nested
@ -31,20 +31,18 @@ export function sortConfig(routeConfigs: RouteConfig[]): void {
if (a.priority || b.priority) { if (a.priority || b.priority) {
const priorityA = a.priority || 0; const priorityA = a.priority || 0;
const priorityB = b.priority || 0; const priorityB = b.priority || 0;
const score = priorityA > priorityB ? -1 : priorityB > priorityA ? 1 : 0; const score = priorityB - priorityA;
if (score !== 0) { if (score !== 0) {
return score; return score;
} }
} }
return a.path > b.path ? 1 : b.path > a.path ? -1 : 0; return a.path.localeCompare(b.path);
}); });
routeConfigs.forEach((routeConfig) => { routeConfigs.forEach((routeConfig) => {
routeConfig.routes?.sort((a, b) => { routeConfig.routes?.sort((a, b) => a.path.localeCompare(b.path));
return a.path > b.path ? 1 : b.path > a.path ? -1 : 0;
});
}); });
} }

View file

@ -11,13 +11,13 @@ import importFresh from 'import-fresh';
import {LoadContext, Plugin, PluginConfig} from '@docusaurus/types'; import {LoadContext, Plugin, PluginConfig} from '@docusaurus/types';
import {CONFIG_FILE_NAME} from '../../constants'; import {CONFIG_FILE_NAME} from '../../constants';
export function initPlugins({ export default function initPlugins({
pluginConfigs, pluginConfigs,
context, context,
}: { }: {
pluginConfigs: PluginConfig[]; pluginConfigs: PluginConfig[];
context: LoadContext; context: LoadContext;
}): Plugin<any>[] { }): Plugin<unknown>[] {
// We need to resolve plugins from the perspective of the siteDir, since the siteDir's package.json // We need to resolve plugins from the perspective of the siteDir, since the siteDir's package.json
// declares the dependency on these plugins. // declares the dependency on these plugins.
// We need to fallback to createRequireFromPath since createRequire is only available in node v12. // We need to fallback to createRequireFromPath since createRequire is only available in node v12.
@ -25,7 +25,7 @@ export function initPlugins({
const createRequire = Module.createRequire || Module.createRequireFromPath; const createRequire = Module.createRequire || Module.createRequireFromPath;
const pluginRequire = createRequire(join(context.siteDir, CONFIG_FILE_NAME)); const pluginRequire = createRequire(join(context.siteDir, CONFIG_FILE_NAME));
const plugins: Plugin<any>[] = pluginConfigs const plugins: Plugin<unknown>[] = pluginConfigs
.map((pluginItem) => { .map((pluginItem) => {
let pluginModuleImport: string | undefined; let pluginModuleImport: string | undefined;
let pluginOptions = {}; let pluginOptions = {};
@ -37,8 +37,7 @@ export function initPlugins({
if (typeof pluginItem === 'string') { if (typeof pluginItem === 'string') {
pluginModuleImport = pluginItem; pluginModuleImport = pluginItem;
} else if (Array.isArray(pluginItem)) { } else if (Array.isArray(pluginItem)) {
pluginModuleImport = pluginItem[0]; [pluginModuleImport, pluginOptions = {}] = pluginItem;
pluginOptions = pluginItem[1] || {};
} }
if (!pluginModuleImport) { if (!pluginModuleImport) {

View file

@ -7,7 +7,7 @@
import path from 'path'; import path from 'path';
import {loadPresets} from '../index'; import loadPresets from '../index';
import {LoadContext} from '@docusaurus/types'; import {LoadContext} from '@docusaurus/types';
describe('loadPresets', () => { describe('loadPresets', () => {

View file

@ -13,7 +13,7 @@ import {
PresetConfig, PresetConfig,
} from '@docusaurus/types'; } from '@docusaurus/types';
export function loadPresets( export default function loadPresets(
context: LoadContext, context: LoadContext,
): { ): {
plugins: PluginConfig[]; plugins: PluginConfig[];

View file

@ -36,10 +36,21 @@ function getModulePath(target: Module): string {
return `${target.path}${queryStr}`; return `${target.path}${queryStr}`;
} }
export async function loadRoutes( type LoadedRoutes = {
registry: {
[chunkName: string]: ChunkRegistry;
};
routesConfig: string;
routesChunkNames: {
[routePath: string]: ChunkNames;
};
routesPaths: string[];
};
export default async function loadRoutes(
pluginsRouteConfigs: RouteConfig[], pluginsRouteConfigs: RouteConfig[],
baseUrl: string, baseUrl: string,
) { ): Promise<LoadedRoutes> {
const routesImports = [ const routesImports = [
`import React from 'react';`, `import React from 'react';`,
`import ComponentCreator from '@docusaurus/ComponentCreator';`, `import ComponentCreator from '@docusaurus/ComponentCreator';`,

View file

@ -7,7 +7,7 @@
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs-extra';
import {themeAlias} from '../alias'; import themeAlias from '../alias';
describe('themeAlias', () => { describe('themeAlias', () => {
test('valid themePath 1 with components', () => { test('valid themePath 1 with components', () => {

View file

@ -6,7 +6,7 @@
*/ */
import path from 'path'; import path from 'path';
import {loadThemeAlias} from '../index'; import loadThemeAlias from '../index';
describe('loadThemeAlias', () => { describe('loadThemeAlias', () => {
test('next alias can override the previous alias', () => { test('next alias can override the previous alias', () => {

View file

@ -11,7 +11,7 @@ import path from 'path';
import {fileToPath, posixPath, normalizeUrl} from '@docusaurus/utils'; import {fileToPath, posixPath, normalizeUrl} from '@docusaurus/utils';
import {ThemeAlias} from '@docusaurus/types'; import {ThemeAlias} from '@docusaurus/types';
export function themeAlias( export default function themeAlias(
themePath: string, themePath: string,
addOriginalAlias: boolean = true, addOriginalAlias: boolean = true,
): ThemeAlias { ): ThemeAlias {

View file

@ -6,9 +6,9 @@
*/ */
import {ThemeAlias} from '@docusaurus/types'; import {ThemeAlias} from '@docusaurus/types';
import {themeAlias} from './alias'; import themeAlias from './alias';
export function loadThemeAlias( export default function loadThemeAlias(
themePaths: string[], themePaths: string[],
userThemePaths: string[] = [], userThemePaths: string[] = [],
): ThemeAlias { ): ThemeAlias {

View file

@ -7,8 +7,8 @@
import {validate} from 'webpack'; import {validate} from 'webpack';
import {createClientConfig} from '../client'; import createClientConfig from '../client';
import {loadSetup} from '../../server/loadSetup'; import loadSetup from '../../server/loadSetup';
describe('webpack dev config', () => { describe('webpack dev config', () => {
test('simple', async () => { test('simple', async () => {

View file

@ -7,8 +7,8 @@
import {validate} from 'webpack'; import {validate} from 'webpack';
import {createServerConfig} from '../server'; import createServerConfig from '../server';
import {loadSetup} from '../../server/loadSetup'; import loadSetup from '../../server/loadSetup';
describe('webpack production config', () => { describe('webpack production config', () => {
test('simple', async () => { test('simple', async () => {

View file

@ -15,7 +15,7 @@ import {createBaseConfig} from './base';
import ChunkAssetPlugin from './plugins/ChunkAssetPlugin'; import ChunkAssetPlugin from './plugins/ChunkAssetPlugin';
import LogPlugin from './plugins/LogPlugin'; import LogPlugin from './plugins/LogPlugin';
export function createClientConfig( export default function createClientConfig(
props: Props, props: Props,
minify: boolean = true, minify: boolean = true,
): Configuration { ): Configuration {
@ -46,7 +46,7 @@ export function createClientConfig(
// When building include the plugin to force terminate building if errors happened in the client bundle. // When building include the plugin to force terminate building if errors happened in the client bundle.
if (isBuilding) { if (isBuilding) {
clientConfig.plugins!.push({ clientConfig.plugins?.push({
apply: (compiler) => { apply: (compiler) => {
compiler.hooks.done.tap('client:done', (stats) => { compiler.hooks.done.tap('client:done', (stats) => {
if (stats.hasErrors()) { if (stats.hasErrors()) {

View file

@ -33,6 +33,9 @@ import path from 'path';
import {sync as delSync} from 'del'; import {sync as delSync} from 'del';
export interface Options { export interface Options {
/** @deprecated */
allowExternal?: unknown;
/** /**
* Simulate the removal of files * Simulate the removal of files
* *
@ -110,7 +113,6 @@ class CleanWebpackPlugin {
https://github.com/johnagan/clean-webpack-plugin#options-and-defaults-optional`); https://github.com/johnagan/clean-webpack-plugin#options-and-defaults-optional`);
} }
// @ts-ignore
if (options.allowExternal) { if (options.allowExternal) {
throw new Error( throw new Error(
'clean-webpack-plugin: `allowExternal` option no longer supported. Use `dangerouslyAllowCleanPatternsOutsideProject`', 'clean-webpack-plugin: `allowExternal` option no longer supported. Use `dangerouslyAllowCleanPatternsOutsideProject`',

View file

@ -15,7 +15,7 @@ import {createBaseConfig} from './base';
import WaitPlugin from './plugins/WaitPlugin'; import WaitPlugin from './plugins/WaitPlugin';
import LogPlugin from './plugins/LogPlugin'; import LogPlugin from './plugins/LogPlugin';
export function createServerConfig( export default function createServerConfig(
props: Props, props: Props,
minify: boolean = true, minify: boolean = true,
): Configuration { ): Configuration {

View file

@ -10,6 +10,7 @@ import env from 'std-env';
import merge from 'webpack-merge'; import merge from 'webpack-merge';
import {Configuration, Loader} from 'webpack'; import {Configuration, Loader} from 'webpack';
import {TransformOptions} from '@babel/core'; import {TransformOptions} from '@babel/core';
import {ConfigureWebpackUtils} from '@docusaurus/types';
import {version as cacheLoaderVersion} from 'cache-loader/package.json'; import {version as cacheLoaderVersion} from 'cache-loader/package.json';
@ -17,7 +18,7 @@ import {version as cacheLoaderVersion} from 'cache-loader/package.json';
export function getStyleLoaders( export function getStyleLoaders(
isServer: boolean, isServer: boolean,
cssOptions: { cssOptions: {
[key: string]: any; [key: string]: unknown;
} = {}, } = {},
): Loader[] { ): Loader[] {
if (isServer) { if (isServer) {
@ -53,7 +54,7 @@ export function getStyleLoaders(
// https://github.com/facebook/create-react-app/issues/2677 // https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss', ident: 'postcss',
plugins: () => [ plugins: () => [
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
require('postcss-preset-env')({ require('postcss-preset-env')({
autoprefixer: { autoprefixer: {
flexbox: 'no-2009', flexbox: 'no-2009',
@ -69,7 +70,7 @@ export function getStyleLoaders(
export function getCacheLoader( export function getCacheLoader(
isServer: boolean, isServer: boolean,
cacheOptions?: {}, cacheOptions?: {[key: string]: unknown},
): Loader | null { ): Loader | null {
if (env.ci || env.test) { if (env.ci || env.test) {
return null; return null;
@ -113,13 +114,19 @@ export function getBabelLoader(
/** /**
* Helper function to modify webpack config * Helper function to modify webpack config
* @param {Object | Function} configureWebpack a webpack config or a function to modify config * @param configureWebpack a webpack config or a function to modify config
* @param {Object} config initial webpack config * @param config initial webpack config
* @param {boolean} isServer indicates if this is a server webpack configuration * @param isServer indicates if this is a server webpack configuration
* @returns {Object} final/ modified webpack config * @returns final/ modified webpack config
*/ */
export function applyConfigureWebpack( export function applyConfigureWebpack(
configureWebpack: any, configureWebpack:
| Configuration
| ((
config: Configuration,
isServer: boolean,
utils: ConfigureWebpackUtils,
) => Configuration),
config: Configuration, config: Configuration,
isServer: boolean, isServer: boolean,
): Configuration { ): Configuration {

View file

@ -7,10 +7,9 @@
import path from 'path'; import path from 'path';
import Vibrant from 'node-vibrant'; import Vibrant from 'node-vibrant';
import {Palette} from 'node-vibrant/lib/color';
// @ts-ignore
import {toPalette, toBase64} from '../utils'; import {toPalette, toBase64} from '../utils';
// @ts-ignore
import lqip from '../lqip'; import lqip from '../lqip';
describe('lqip-loader', () => { describe('lqip-loader', () => {
@ -24,8 +23,8 @@ describe('lqip-loader', () => {
}); });
describe('toPalette', () => { describe('toPalette', () => {
let correctTestSwatch: object = {}; let correctTestSwatch: Palette = {};
let testSwatchWithNull: object = {}; let testSwatchWithNull: Palette & {Vibrant?: null} = {};
beforeAll(() => { beforeAll(() => {
const imgPath = path.join(__dirname, '__fixtures__', 'endi.jpg'); const imgPath = path.join(__dirname, '__fixtures__', 'endi.jpg');

View file

@ -5,6 +5,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
// @ts-check
const sortBy = require('lodash.sortby'); const sortBy = require('lodash.sortby');
/** /**
@ -12,7 +14,7 @@ const sortBy = require('lodash.sortby');
* @description it returns a Base64 image string with required formatting * @description 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('..'))
* *
* @param extMimeType: image mime type string * @param {string} extMimeType: image mime type string
* @param data: base64 string * @param data: base64 string
* @returns {string} * @returns {string}
*/ */
@ -25,10 +27,11 @@ const toBase64 = (extMimeType, data) => {
* @description takes a color swatch object, converts it to an array & returns * @description takes a color swatch object, converts it to an array & returns
* only hex color * only hex color
* *
* @param swatch * @param {import("node-vibrant/lib/color").Palette} swatch
* @returns {{palette: Array}} * @returns {string[]}
*/ */
const toPalette = (swatch) => { const toPalette = (swatch) => {
/** @type {Array<{popularity: number, hex: string}>} */
let palette = Object.keys(swatch).reduce((result, key) => { let palette = Object.keys(swatch).reduce((result, key) => {
if (swatch[key] !== null) { if (swatch[key] !== null) {
result.push({ result.push({
@ -39,8 +42,7 @@ const toPalette = (swatch) => {
return result; return result;
}, []); }, []);
palette = sortBy(palette, ['popularity']); palette = sortBy(palette, ['popularity']);
palette = palette.map((color) => color.hex).reverse(); return palette.map((color) => color.hex).reverse();
return palette;
}; };
module.exports = { module.exports = {

View file

@ -2795,6 +2795,11 @@
"@types/minimatch" "*" "@types/minimatch" "*"
"@types/node" "*" "@types/node" "*"
"@types/history@*":
version "4.7.6"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356"
integrity sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w==
"@types/html-minifier-terser@^5.0.0": "@types/html-minifier-terser@^5.0.0":
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz#7532440c138605ced1b555935c3115ddd20e8bef" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.0.0.tgz#7532440c138605ced1b555935c3115ddd20e8bef"
@ -3032,10 +3037,42 @@
"@types/webpack" "*" "@types/webpack" "*"
"@types/webpack-dev-server" "*" "@types/webpack-dev-server" "*"
"@types/react@^16.9.13": "@types/react-helmet@^6.0.0":
version "16.9.32" version "6.0.0"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383" resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.0.0.tgz#5b74e44a12662ffb12d1c97ee702cf4e220958cf"
integrity sha512-fmejdp0CTH00mOJmxUPPbWCEBWPvRIL4m8r0qD+BSDUqmutPyGQCHifzMpMzdvZwROdEdL78IuZItntFWgPXHQ== integrity sha512-NBMPAxgjpaMooXa51cU1BTgrX6T+hQbMiLm77JhBbfOzPQea3RB5rNpPOD5xGWHIVpGXHd59cltEzIq0qglGcQ==
dependencies:
"@types/react" "*"
"@types/react-loadable@^5.5.3":
version "5.5.3"
resolved "https://registry.yarnpkg.com/@types/react-loadable/-/react-loadable-5.5.3.tgz#65d50a6f9f7ff62513010bd6a460ed60ba58ca7d"
integrity sha512-BRzQhbMo5CjfxFU2tmmBNh16QqKUwNiaX0vflCwIVPVG8g/pCOyJ3rOdSPo4m+TPS7C9q/TupaqYXXTMtFoyng==
dependencies:
"@types/react" "*"
"@types/webpack" "*"
"@types/react-router-config@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.1.tgz#54da8418190ee47484dee279975e2b8038fb8b5d"
integrity sha512-D4srFL4XP21GjWWnM7mL8j+Nrrw13pc2TYr57WTHJxU9YTxnrXL7p1iqGtwecgwhyeXJSm4WrGwq0SOgSALVbA==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.7"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.7.tgz#e9d12ed7dcfc79187e4d36667745b69a5aa11556"
integrity sha512-2ouP76VQafKjtuc0ShpwUebhHwJo0G6rhahW9Pb8au3tQTjYXd2jta4wv6U2tGLR/I42yuG00+UXjNYY0dTzbg==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^16.9.38":
version "16.9.38"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.38.tgz#868405dace93a4095d3e054f4c4a1de7a1ac0680"
integrity sha512-pHAeZbjjNRa/hxyNuLrvbxhhnKyKNiLC6I5fRF2Zr/t/S6zS41MiyzH4+c+1I9vVfvuRt1VS2Lodjr4ZWnxrdA==
dependencies: dependencies:
"@types/prop-types" "*" "@types/prop-types" "*"
csstype "^2.2.0" csstype "^2.2.0"
@ -17815,10 +17852,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.7.2: typescript@^3.9.5:
version "3.8.3" version "3.9.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
ua-parser-js@^0.7.18: ua-parser-js@^0.7.18:
version "0.7.21" version "0.7.21"