refactor: ensure lodash is default-imported (#6716)

This commit is contained in:
Joshua Chen 2022-02-19 18:15:02 +08:00 committed by GitHub
parent 47c9a37c5f
commit ea6ceaa371
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 239 additions and 221 deletions

View file

@ -69,31 +69,44 @@ module.exports = {
'no-param-reassign': [WARNING, {props: false}],
'no-prototype-builtins': WARNING,
'no-restricted-exports': OFF,
'no-restricted-imports': [
'no-restricted-properties': [
ERROR,
{
paths: [
{
name: 'lodash',
importNames: [
// TODO: TS doesn't make Boolean a narrowing function yet,
// so filter(Boolean) is problematic type-wise
// 'compact',
'filter',
'flatten',
'flatMap',
'map',
'reduce',
'take',
'takeRight',
'head',
'tail',
'initial',
],
message: 'These APIs have their ES counterparts.',
},
],
},
...[
// TODO: TS doesn't make Boolean a narrowing function yet,
// so filter(Boolean) is problematic type-wise
// ['compact', 'Array#filter(Boolean)'],
['concat', 'Array#concat'],
['drop', 'Array#slice(n)'],
['dropRight', 'Array#slice(0, -n)'],
['fill', 'Array#fill'],
['filter', 'Array#filter'],
['find', 'Array#find'],
['findIndex', 'Array#findIndex'],
['first', 'foo[0]'],
['flatten', 'Array#flat'],
['flattenDeep', 'Array#flat(Infinity)'],
['flatMap', 'Array#flatMap'],
['fromPairs', 'Object.fromEntries'],
['head', 'foo[0]'],
['indexOf', 'Array#indexOf'],
['initial', 'Array#slice(0, -1)'],
['join', 'Array#join'],
// Unfortunately there's no great alternative to _.last yet
// Candidates: foo.slice(-1)[0]; foo[foo.length - 1]
// Array#at is ES2022; could replace _.nth as well
// ['last'],
['map', 'Array#map'],
['reduce', 'Array#reduce'],
['reverse', 'Array#reverse'],
['slice', 'Array#slice'],
['take', 'Array#slice(0, n)'],
['takeRight', 'Array#slice(-n)'],
['tail', 'Array#slice(1)'],
].map(([property, alternative]) => ({
object: '_',
property,
message: `Use ${alternative} instead.`,
})),
],
'no-restricted-syntax': [
WARNING,

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {uniqBy, difference, groupBy} from 'lodash';
import _ from 'lodash';
import type {
PluginOptions,
RedirectOption,
@ -79,7 +79,7 @@ function validateCollectedRedirects(
const allowedToPaths = pluginContext.relativeRoutesPaths;
const toPaths = redirects.map((redirect) => redirect.to);
const illegalToPaths = difference(toPaths, allowedToPaths);
const illegalToPaths = _.difference(toPaths, allowedToPaths);
if (illegalToPaths.length > 0) {
throw new Error(
`You are trying to create client-side redirections to paths that do not exist:
@ -98,7 +98,7 @@ function filterUnwantedRedirects(
): RedirectMetadata[] {
// we don't want to create twice the same redirect
// that would lead to writing twice the same html redirection file
Object.entries(groupBy(redirects, (redirect) => redirect.from)).forEach(
Object.entries(_.groupBy(redirects, (redirect) => redirect.from)).forEach(
([from, groupedFromRedirects]) => {
if (groupedFromRedirects.length > 1) {
logger.error`name=${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: path=${from}
@ -108,7 +108,7 @@ It is not possible to redirect the same pathname to multiple destinations: ${gro
}
},
);
const collectedRedirects = uniqBy(redirects, (redirect) => redirect.from);
const collectedRedirects = _.uniqBy(redirects, (redirect) => redirect.from);
// We don't want to override an already existing route with a redirect file!
const redirectsOverridingExistingPath = collectedRedirects.filter(

View file

@ -7,13 +7,13 @@
import * as eta from 'eta';
import redirectPageTemplate from './templates/redirectPage.template.html';
import {memoize} from 'lodash';
import _ from 'lodash';
type CreateRedirectPageOptions = {
toUrl: string;
};
const getCompiledRedirectPageTemplate = memoize(() =>
const getCompiledRedirectPageTemplate = _.memoize(() =>
eta.compile(redirectPageTemplate.trim()),
);

View file

@ -7,7 +7,7 @@
import fs from 'fs-extra';
import path from 'path';
import {memoize} from 'lodash';
import _ from 'lodash';
import type {PluginContext, RedirectMetadata} from './types';
import createRedirectPageContent from './createRedirectPageContent';
@ -63,7 +63,7 @@ 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) =>
const createPageContentMemoized = _.memoize((toUrl: string) =>
createRedirectPageContent({toUrl}),
);

View file

@ -8,7 +8,7 @@
import fs from 'fs-extra';
import path from 'path';
import readingTime from 'reading-time';
import {keyBy, mapValues} from 'lodash';
import _ from 'lodash';
import type {
BlogPost,
BlogContentPaths,
@ -46,9 +46,8 @@ export function truncate(fileString: string, truncateMarker: RegExp): string {
export function getSourceToPermalink(
blogPosts: BlogPost[],
): Record<string, string> {
return mapValues(
keyBy(blogPosts, (item) => item.metadata.source),
(v) => v.metadata.permalink,
return Object.fromEntries(
blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]),
);
}
@ -112,19 +111,16 @@ export function getBlogTags({
(blogPost) => blogPost.metadata.tags,
);
return mapValues(groups, (group) => {
const {tag, items: tagBlogPosts} = group;
return {
name: tag.label,
items: tagBlogPosts.map((item) => item.id),
permalink: tag.permalink,
pages: paginateBlogPosts({
blogPosts: tagBlogPosts,
basePageUrl: group.tag.permalink,
...params,
}),
};
});
return _.mapValues(groups, ({tag, items: tagBlogPosts}) => ({
name: tag.label,
items: tagBlogPosts.map((item) => item.id),
permalink: tag.permalink,
pages: paginateBlogPosts({
blogPosts: tagBlogPosts,
basePageUrl: tag.permalink,
...params,
}),
}));
}
const DATE_FILENAME_REGEX =

View file

@ -8,7 +8,7 @@
import path from 'path';
import {isMatch} from 'picomatch';
import commander from 'commander';
import {kebabCase, orderBy} from 'lodash';
import _ from 'lodash';
import fs from 'fs-extra';
import pluginContentDocs from '../index';
@ -89,7 +89,7 @@ Entries created:
checkVersionMetadataPropCreated: (version: LoadedVersion) => {
const versionMetadataProp = getCreatedDataByPrefix(
`version-${kebabCase(version.versionName)}-metadata-prop`,
`version-${_.kebabCase(version.versionName)}-metadata-prop`,
);
expect(versionMetadataProp.docsSidebars).toEqual(toSidebarsProp(version));
},
@ -815,7 +815,7 @@ describe('site with custom sidebar items generator', () => {
): SidebarItemsGeneratorOptionArgs {
return {
...arg,
docs: orderBy(arg.docs, 'id'),
docs: _.orderBy(arg.docs, 'id'),
version: {
...arg.version,
contentPath: path.relative(siteDir, arg.version.contentPath),

View file

@ -17,7 +17,7 @@ import type {
GlobalVersion,
ActivePlugin,
} from '@docusaurus/plugin-content-docs/client';
import {shuffle} from 'lodash';
import _ from 'lodash';
describe('docsClientUtils', () => {
test('getActivePlugin', () => {
@ -227,7 +227,7 @@ describe('docsClientUtils', () => {
};
// shuffle, because order shouldn't matter
const versions: GlobalVersion[] = shuffle([
const versions: GlobalVersion[] = _.shuffle([
versionNext,
version2,
version1,
@ -356,7 +356,7 @@ describe('docsClientUtils', () => {
};
// shuffle, because order shouldn't matter
const versions: GlobalVersion[] = shuffle([
const versions: GlobalVersion[] = _.shuffle([
versionNext,
version2,
version1,

View file

@ -8,7 +8,6 @@
import path from 'path';
import fs from 'fs-extra';
import logger from '@docusaurus/logger';
import {keyBy} from 'lodash';
import {
aliasedSitePath,
getEditUrl,
@ -444,8 +443,10 @@ export function getDocIds(doc: DocMetadataBase): [string, string] {
export function createDocsByIdIndex<
Doc extends {id: string; unversionedId: string},
>(docs: Doc[]): Record<string, Doc> {
return {
...keyBy(docs, (doc) => doc.unversionedId),
...keyBy(docs, (doc) => doc.id),
};
return Object.fromEntries(
docs.flatMap((doc) => [
[doc.unversionedId, doc],
[doc.id, doc],
]),
);
}

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {mapValues} from 'lodash';
import _ from 'lodash';
import {normalizeUrl} from '@docusaurus/utils';
import type {Sidebars} from './sidebars/types';
import {createSidebarsUtils} from './sidebars/utils';
@ -43,7 +43,7 @@ export function toGlobalSidebars(
version: LoadedVersion,
): Record<string, GlobalSidebar> {
const {getFirstLink} = createSidebarsUtils(sidebars);
return mapValues(sidebars, (sidebar, sidebarId) => {
return _.mapValues(sidebars, (sidebar, sidebarId) => {
const firstLink = getFirstLink(sidebarId);
if (!firstLink) {
return {};

View file

@ -42,7 +42,6 @@ import type {
import type {RuleSetRule} from 'webpack';
import {cliDocsVersionCommand} from './cli';
import {VERSIONS_JSON_FILE} from './constants';
import {keyBy, mapValues} from 'lodash';
import {toGlobalDataVersion} from './globalData';
import {toTagDocListProp} from './props';
import {
@ -311,9 +310,8 @@ export default async function pluginContentDocs(
function getSourceToPermalink(): SourceToPermalink {
const allDocs = content.loadedVersions.flatMap((v) => v.docs);
return mapValues(
keyBy(allDocs, (d) => d.source),
(d) => d.permalink,
return Object.fromEntries(
allDocs.map(({source, permalink}) => [source, permalink]),
);
}

View file

@ -22,7 +22,7 @@ import type {
PropTagDocListDoc,
PropSidebarItemLink,
} from '@docusaurus/plugin-content-docs';
import {compact, keyBy, mapValues} from 'lodash';
import _ from 'lodash';
import {createDocsByIdIndex} from './docs';
export function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars {
@ -93,18 +93,22 @@ Available document ids are:
// Transform the sidebar so that all sidebar item will be in the
// form of 'link' or 'category' only.
// This is what will be passed as props to the UI component.
return mapValues(loadedVersion.sidebars, (items) => items.map(normalizeItem));
return _.mapValues(loadedVersion.sidebars, (items) =>
items.map(normalizeItem),
);
}
function toVersionDocsProp(loadedVersion: LoadedVersion): PropVersionDocs {
return mapValues(
keyBy(loadedVersion.docs, (doc) => doc.unversionedId),
(doc) => ({
id: doc.unversionedId,
title: doc.title,
description: doc.description,
sidebar: doc.sidebar,
}),
return Object.fromEntries(
loadedVersion.docs.map((doc) => [
doc.unversionedId,
{
id: doc.unversionedId,
title: doc.title,
description: doc.description,
sidebar: doc.sidebar,
},
]),
);
}
@ -135,7 +139,7 @@ export function toTagDocListProp({
docs: Pick<DocMetadata, 'id' | 'title' | 'description' | 'permalink'>[];
}): PropTagDocList {
function toDocListProp(): PropTagDocListDoc[] {
const list = compact(
const list = _.compact(
tag.docIds.map((id) => docs.find((doc) => doc.id === id)),
);
// Sort docs by title

View file

@ -13,7 +13,7 @@ import type {
NormalizedSidebarItem,
SidebarItemCategoryLinkConfig,
} from './types';
import {sortBy, last} from 'lodash';
import _ from 'lodash';
import {addTrailingSlash, posixPath} from '@docusaurus/utils';
import logger from '@docusaurus/logger';
import path from 'path';
@ -25,7 +25,7 @@ const docIdPrefix = '$doc$/';
// Just an alias to the make code more explicit
function getLocalDocId(docId: string): string {
return last(docId.split('/'))!;
return _.last(docId.split('/'))!;
}
export const CategoryMetadataFilenameBase = '_category_';
@ -248,7 +248,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
}
return item;
});
const sortedSidebarItems = sortBy(
const sortedSidebarItems = _.sortBy(
processedSidebarItems,
(item) => item.position,
);

View file

@ -17,7 +17,7 @@ import {Globby} from '@docusaurus/utils';
import logger from '@docusaurus/logger';
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
import Yaml from 'js-yaml';
import {groupBy, mapValues} from 'lodash';
import _ from 'lodash';
import combinePromises from 'combine-promises';
export const DefaultSidebars: SidebarsConfig = {
@ -46,9 +46,9 @@ async function readCategoriesMetadata(contentPath: string) {
const categoryFiles = await Globby('**/_category_.{json,yml,yaml}', {
cwd: contentPath,
});
const categoryToFile = groupBy(categoryFiles, path.dirname);
const categoryToFile = _.groupBy(categoryFiles, path.dirname);
return combinePromises(
mapValues(categoryToFile, async (files, folder) => {
_.mapValues(categoryToFile, async (files, folder) => {
const [filePath] = files;
if (files.length > 1) {
logger.warn`There are more than one category metadata files for path=${folder}: ${files.join(

View file

@ -17,7 +17,7 @@ import type {
NormalizedSidebarItemCategory,
} from './types';
import {isCategoriesShorthand} from './utils';
import {mapValues} from 'lodash';
import _ from 'lodash';
function normalizeCategoriesShorthand(
sidebar: SidebarCategoriesShorthand,
@ -81,5 +81,5 @@ function normalizeSidebar(sidebar: SidebarConfig): NormalizedSidebar {
export function normalizeSidebars(
sidebars: SidebarsConfig,
): NormalizedSidebars {
return mapValues(sidebars, normalizeSidebar);
return _.mapValues(sidebars, normalizeSidebar);
}

View file

@ -15,7 +15,7 @@ import type {
ProcessedSidebars,
SidebarItemCategoryLink,
} from './types';
import {mapValues} from 'lodash';
import _ from 'lodash';
function normalizeCategoryLink(
category: ProcessedSidebarItemCategory,
@ -88,7 +88,7 @@ export function postProcessSidebars(
sidebars: ProcessedSidebars,
params: SidebarProcessorParams,
): Sidebars {
return mapValues(sidebars, (sidebar) =>
return _.mapValues(sidebars, (sidebar) =>
sidebar.map((item) => postProcessSidebarItem(item, params)),
);
}

View file

@ -21,14 +21,14 @@ import type {
} from './types';
import {DefaultSidebarItemsGenerator} from './generator';
import {validateSidebars} from './validation';
import {mapValues, memoize, pick} from 'lodash';
import _ from 'lodash';
import combinePromises from 'combine-promises';
import {isCategoryIndex} from '../docs';
function toSidebarItemsGeneratorDoc(
doc: DocMetadataBase,
): SidebarItemsGeneratorDoc {
return pick(doc, [
return _.pick(doc, [
'id',
'unversionedId',
'frontMatter',
@ -41,7 +41,7 @@ function toSidebarItemsGeneratorDoc(
function toSidebarItemsGeneratorVersion(
version: VersionMetadata,
): SidebarItemsGeneratorVersion {
return pick(version, ['versionName', 'contentPath']);
return _.pick(version, ['versionName', 'contentPath']);
}
// Handle the generation of autogenerated sidebar items and other
@ -60,7 +60,7 @@ async function processSidebar(
} = params;
// Just a minor lazy transformation optimization
const getSidebarItemsGeneratorDocsAndVersion = memoize(() => ({
const getSidebarItemsGeneratorDocsAndVersion = _.memoize(() => ({
docs: docs.map(toSidebarItemsGeneratorDoc),
version: toSidebarItemsGeneratorVersion(version),
}));
@ -117,7 +117,7 @@ export async function processSidebars(
params: SidebarProcessorParams,
): Promise<ProcessedSidebars> {
const processedSidebars = await combinePromises(
mapValues(unprocessedSidebars, (unprocessedSidebar) =>
_.mapValues(unprocessedSidebars, (unprocessedSidebar) =>
processSidebar(unprocessedSidebar, categoriesMetadata, params),
),
);

View file

@ -19,7 +19,7 @@ import type {
SidebarNavigationItem,
} from './types';
import {mapValues, difference, uniq} from 'lodash';
import _ from 'lodash';
import {getElementsAround, toMessageRelativeFilePath} from '@docusaurus/utils';
import type {DocMetadataBase, DocNavLink} from '../types';
@ -110,13 +110,13 @@ export function collectSidebarNavigation(
export function collectSidebarsDocIds(
sidebars: Sidebars,
): Record<string, string[]> {
return mapValues(sidebars, collectSidebarDocIds);
return _.mapValues(sidebars, collectSidebarDocIds);
}
export function collectSidebarsNavigations(
sidebars: Sidebars,
): Record<string, SidebarNavigationItem[]> {
return mapValues(sidebars, collectSidebarNavigation);
return _.mapValues(sidebars, collectSidebarNavigation);
}
export type SidebarNavigation = {
@ -276,7 +276,7 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
function checkSidebarsDocIds(validDocIds: string[], sidebarFilePath: string) {
const allSidebarDocIds = Object.values(sidebarNameToDocIds).flat();
const invalidSidebarDocIds = difference(allSidebarDocIds, validDocIds);
const invalidSidebarDocIds = _.difference(allSidebarDocIds, validDocIds);
if (invalidSidebarDocIds.length > 0) {
throw new Error(
`Invalid sidebar file at "${toMessageRelativeFilePath(
@ -286,7 +286,7 @@ These sidebar document ids do not exist:
- ${invalidSidebarDocIds.sort().join('\n- ')}
Available document ids are:
- ${uniq(validDocIds).sort().join('\n- ')}`,
- ${_.uniq(validDocIds).sort().join('\n- ')}`,
);
}
}

View file

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

View file

@ -13,7 +13,7 @@ import type {
Sidebars,
} from './sidebars/types';
import {chain, mapValues, keyBy} from 'lodash';
import _ from 'lodash';
import {
collectSidebarCategories,
transformSidebarItems,
@ -146,13 +146,15 @@ function getSidebarTranslationFileContent(
);
const links = collectSidebarLinks(sidebar);
const linksContent: TranslationFileContent = chain(links)
.keyBy((link) => `sidebar.${sidebarName}.link.${link.label}`)
.mapValues((link) => ({
message: link.label,
description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
}))
.value();
const linksContent: TranslationFileContent = Object.fromEntries(
links.map((link) => [
`sidebar.${sidebarName}.link.${link.label}`,
{
message: link.label,
description: `The label for link ${link.label} in sidebar ${sidebarName}, linking to ${link.href}`,
},
]),
);
return mergeTranslations([categoryContent, linksContent]);
}
@ -230,7 +232,7 @@ function translateSidebars(
version: LoadedVersion,
sidebarsTranslations: TranslationFileContent,
): Sidebars {
return mapValues(version.sidebars, (sidebar, sidebarName) =>
return _.mapValues(version.sidebars, (sidebar, sidebarName) =>
translateSidebar({
sidebar,
sidebarName: getNormalizedSidebarName({
@ -302,7 +304,7 @@ export function translateLoadedContent(
loadedContent: LoadedContent,
translationFiles: TranslationFile[],
): LoadedContent {
const translationFilesMap: Record<string, TranslationFile> = keyBy(
const translationFilesMap: Record<string, TranslationFile> = _.keyBy(
translationFiles,
(f) => f.path,
);

View file

@ -28,7 +28,7 @@ import {
posixPath,
DEFAULT_PLUGIN_ID,
} from '@docusaurus/utils';
import {difference} from 'lodash';
import _ from 'lodash';
import {resolveSidebarPathOption} from './sidebars';
// retro-compatibility: no prefix for the default plugin id
@ -486,7 +486,7 @@ function checkVersionsOptions(
`Docs option lastVersion: ${options.lastVersion} is invalid. ${availableVersionNamesMsg}`,
);
}
const unknownVersionConfigNames = difference(
const unknownVersionConfigNames = _.difference(
Object.keys(options.versions),
availableVersionNames,
);
@ -504,7 +504,7 @@ function checkVersionsOptions(
`Invalid docs option "onlyIncludeVersions": an empty array is not allowed, at least one version is needed.`,
);
}
const unknownOnlyIncludeVersionNames = difference(
const unknownOnlyIncludeVersionNames = _.difference(
options.onlyIncludeVersions,
availableVersionNames,
);

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {merge} from 'lodash';
import _ from 'lodash';
import {ThemeConfigSchema, DEFAULT_CONFIG} from '../validateThemeConfig';
import {normalizeThemeConfig} from '@docusaurus/utils-validation';
@ -507,7 +507,7 @@ describe('themeConfig', () => {
describe('color mode config', () => {
const withDefaultValues = (colorMode) =>
merge({}, DEFAULT_CONFIG.colorMode, colorMode);
_.merge({}, DEFAULT_CONFIG.colorMode, colorMode);
test('minimal config', () => {
const colorMode = {

View file

@ -15,7 +15,7 @@ import type {
SimpleFooter,
} from '@docusaurus/theme-common';
import {keyBy, chain} from 'lodash';
import _ from 'lodash';
import {mergeTranslations} from '@docusaurus/utils';
function getNavbarTranslationFile(navbar: Navbar): TranslationFileContent {
@ -30,15 +30,17 @@ function getNavbarTranslationFile(navbar: Navbar): TranslationFileContent {
const allNavbarItems = flattenNavbarItems(navbar.items);
const navbarItemsTranslations: TranslationFileContent = chain(
allNavbarItems.filter((navbarItem) => !!navbarItem.label),
)
.keyBy((navbarItem) => `item.label.${navbarItem.label}`)
.mapValues((navbarItem) => ({
message: navbarItem.label!,
description: `Navbar item with label ${navbarItem.label}`,
}))
.value();
const navbarItemsTranslations: TranslationFileContent = Object.fromEntries(
allNavbarItems
.filter((navbarItem) => navbarItem.label)
.map((navbarItem) => [
`item.label.${navbarItem.label}`,
{
message: navbarItem.label!,
description: `Navbar item with label ${navbarItem.label}`,
},
]),
);
const titleTranslations: TranslationFileContent = navbar.title
? {title: {message: navbar.title, description: 'The title in the navbar'}}
@ -78,33 +80,33 @@ function isMultiColumnFooterLinks(
}
function getFooterTranslationFile(footer: Footer): TranslationFileContent {
const footerLinkTitles: TranslationFileContent = chain(
isMultiColumnFooterLinks(footer.links)
? footer.links.filter((link) => !!link.title)
: [],
)
.keyBy((link) => `link.title.${link.title}`)
.mapValues((link) => ({
message: link.title!,
description: `The title of the footer links column with title=${link.title} in the footer`,
}))
.value();
const footerLinkTitles: TranslationFileContent = Object.fromEntries(
(isMultiColumnFooterLinks(footer.links)
? footer.links.filter((link) => link.title)
: []
).map((link) => [
`link.title.${link.title}`,
{
message: link.title!,
description: `The title of the footer links column with title=${link.title} in the footer`,
},
]),
);
const footerLinkLabels: TranslationFileContent = chain(
isMultiColumnFooterLinks(footer.links)
? footer.links
.flatMap((link) => link.items)
.filter((link) => !!link.label)
: footer.links.filter((link) => !!link.label),
)
.keyBy((linkItem) => `link.item.label.${linkItem.label}`)
.mapValues((linkItem) => ({
message: linkItem.label!,
description: `The label of footer link with label=${
linkItem.label
} linking to ${linkItem.to ?? linkItem.href}`,
}))
.value();
const footerLinkLabels: TranslationFileContent = Object.fromEntries(
(isMultiColumnFooterLinks(footer.links)
? footer.links.flatMap((link) => link.items).filter((link) => link.label)
: footer.links.filter((link) => link.label)
).map((link) => [
`link.item.label.${link.label}`,
{
message: link.label!,
description: `The label of footer link with label=${
link.label
} linking to ${link.to ?? link.href}`,
},
]),
);
const copyright: TranslationFileContent = footer.copyright
? {
@ -176,7 +178,7 @@ export function translateThemeConfig({
themeConfig: ThemeConfig;
translationFiles: TranslationFile[];
}): ThemeConfig {
const translationFilesMap: Record<string, TranslationFile> = keyBy(
const translationFilesMap: Record<string, TranslationFile> = _.keyBy(
translationFiles,
(f) => f.path,
);

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {shuffle} from 'lodash';
import _ from 'lodash';
import {listTagsByLetters} from '../tagsUtils';
describe('listTagsByLetters', () => {
@ -60,7 +60,9 @@ describe('listTagsByLetters', () => {
listTagsByLetters([tagzxy, tagdef, tagaaa, tag2, tagAbc, tag1]),
).toEqual(expectedResult);
expect(
listTagsByLetters(shuffle([tagzxy, tagdef, tagaaa, tag2, tagAbc, tag1])),
listTagsByLetters(
_.shuffle([tagzxy, tagdef, tagaaa, tag2, tagAbc, tag1]),
),
).toEqual(expectedResult);
});
});

View file

@ -12,12 +12,12 @@ import {normalizeUrl} from '@docusaurus/utils';
import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
import logger from '@docusaurus/logger';
import openSearchTemplate from './templates/opensearch';
import {memoize} from 'lodash';
import _ from 'lodash';
import type {LoadContext, Plugin} from '@docusaurus/types';
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
const getCompiledOpenSearchTemplate = memoize(() =>
const getCompiledOpenSearchTemplate = _.memoize(() =>
compile(openSearchTemplate.trim()),
);

View file

@ -8,7 +8,7 @@
import {extractThemeCodeMessages} from '../update';
import path from 'path';
import fs from 'fs-extra';
import {mapValues} from 'lodash';
import _ from 'lodash';
// Seems the 5s default timeout fails sometimes
jest.setTimeout(15000);
@ -37,7 +37,7 @@ describe('theme-translations package', () => {
.flat()
.filter(([key]) => !key.endsWith('___DESCRIPTION')),
);
const codeMessages = mapValues(
const codeMessages = _.mapValues(
await extractThemeCodeMessages(),
(translation) => translation.message,
);

View file

@ -11,7 +11,7 @@
const logger = require('@docusaurus/logger').default;
const path = require('path');
const fs = require('fs-extra');
const {mapValues, pickBy, difference, orderBy} = require('lodash');
const _ = require('lodash');
const LocalesDirPath = path.join(__dirname, 'locales');
const Themes = [
@ -80,7 +80,7 @@ function removeDescriptionSuffix(key) {
*/
function sortObjectKeys(obj) {
let keys = Object.keys(obj);
keys = orderBy(keys, [(k) => removeDescriptionSuffix(k)]);
keys = _.orderBy(keys, [(k) => removeDescriptionSuffix(k)]);
return keys.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
@ -179,18 +179,18 @@ const DescriptionSuffix = '___DESCRIPTION';
*/
async function updateBaseFile(baseFile, targetDirs) {
const baseMessagesWithDescriptions = await readMessagesFile(baseFile);
const baseMessages = pickBy(
const baseMessages = _.pickBy(
baseMessagesWithDescriptions,
(_, key) => !key.endsWith(DescriptionSuffix),
(v, key) => !key.endsWith(DescriptionSuffix),
);
const codeExtractedTranslations = await extractThemeCodeMessages(targetDirs);
const codeMessages = mapValues(
const codeMessages = _.mapValues(
codeExtractedTranslations,
(translation) => translation.message,
);
const unknownMessages = difference(
const unknownMessages = _.difference(
Object.keys(baseMessages),
Object.keys(codeMessages),
);
@ -236,7 +236,7 @@ They won't be removed automatically, so do the cleanup manually if necessary! co
async function updateLocaleCodeTranslations(localeFile, baseFileMessages) {
const localeFileMessages = await readMessagesFile(localeFile);
const unknownMessages = difference(
const unknownMessages = _.difference(
Object.keys(localeFileMessages),
Object.keys(baseFileMessages),
);

View file

@ -28,7 +28,7 @@ import {
posixPath,
readOutputHTMLFile,
} from '../index';
import {sum} from 'lodash';
import _ from 'lodash';
import fs from 'fs-extra';
import path from 'path';
@ -322,7 +322,7 @@ describe('mapAsyncSequential', () => {
const timeTotal = timeAfter - timeBefore;
const totalTimeouts = sum(Object.values(itemToTimeout));
const totalTimeouts = _.sum(Object.values(itemToTimeout));
expect(timeTotal).toBeGreaterThanOrEqual(totalTimeouts - 20);
expect(itemMapStartsAt['1']).toBeGreaterThanOrEqual(0);

View file

@ -6,7 +6,7 @@
*/
import {createHash} from 'crypto';
import {kebabCase} from 'lodash';
import _ from 'lodash';
import {shortName, isNameTooLong} from './pathUtils';
export function md5Hash(str: string): string {
@ -29,9 +29,9 @@ export function docuHash(str: string): string {
return 'index';
}
const shortHash = simpleHash(str, 3);
const parsedPath = `${kebabCase(str)}-${shortHash}`;
const parsedPath = `${_.kebabCase(str)}-${shortHash}`;
if (isNameTooLong(parsedPath)) {
return `${shortName(kebabCase(str))}-${shortHash}`;
return `${shortName(_.kebabCase(str))}-${shortHash}`;
}
return parsedPath;
}

View file

@ -8,7 +8,7 @@
import logger from '@docusaurus/logger';
import path from 'path';
import {createHash} from 'crypto';
import {mapValues} from 'lodash';
import _ from 'lodash';
import fs from 'fs-extra';
import {URL} from 'url';
import type {
@ -361,7 +361,7 @@ export function updateTranslationFileMessages(
): TranslationFile {
return {
...translationFile,
content: mapValues(translationFile.content, (translation) => ({
content: _.mapValues(translationFile.content, (translation) => ({
...translation,
message: updateMessage(translation.message),
})),

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {kebabCase, uniq, uniqBy} from 'lodash';
import _ from 'lodash';
import {normalizeUrl} from './urlUtils';
export type Tag = {
@ -22,7 +22,7 @@ export function normalizeFrontMatterTag(
function toTagObject(tagString: string): Tag {
return {
label: tagString,
permalink: kebabCase(tagString),
permalink: _.kebabCase(tagString),
};
}
@ -53,7 +53,7 @@ export function normalizeFrontMatterTags(
normalizeFrontMatterTag(tagsPath, tag),
);
return uniqBy(tags, (tag) => tag.permalink);
return _.uniqBy(tags, (tag) => tag.permalink);
}
export type TaggedItemGroup<Item> = {
@ -98,7 +98,7 @@ export function groupTaggedItems<Item>(
// If user add twice the same tag to a md doc (weird but possible),
// we don't want the item to appear twice in the list...
Object.values(result).forEach((group) => {
group.items = uniq(group.items);
group.items = _.uniq(group.items);
});
return result;

View file

@ -25,13 +25,13 @@ import {
} from './LinksCollector';
import logger from '@docusaurus/logger';
// eslint-disable-next-line no-restricted-imports
import {memoize} from 'lodash';
import _ from 'lodash';
import type {Locals} from '@slorber/static-site-generator-webpack-plugin';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../../package.json');
const getCompiledSSRTemplate = memoize((template: string) =>
const getCompiledSSRTemplate = _.memoize((template: string) =>
eta.compile(template.trim(), {
rmWhitespace: true,
}),

View file

@ -10,7 +10,7 @@ import logger from '@docusaurus/logger';
import chokidar from 'chokidar';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import path from 'path';
import {debounce} from 'lodash';
import _ from 'lodash';
import openBrowser from 'react-dev-utils/openBrowser';
import {prepareUrls} from 'react-dev-utils/WebpackDevServerUtils';
import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware';
@ -63,7 +63,7 @@ export default async function start(
logger.success`Docusaurus website is running at path=${openUrl}.`;
// Reload files processing.
const reload = debounce(() => {
const reload = _.debounce(() => {
loadSite()
.then(({baseUrl: newBaseUrl}) => {
const newOpenUrl = normalizeUrl([urls.localUrlForBrowser, newBaseUrl]);

View file

@ -13,14 +13,12 @@ import {
} from '../i18n';
import {DEFAULT_I18N_CONFIG} from '../configValidation';
import path from 'path';
import {chain, identity} from 'lodash';
import type {I18nConfig} from '@docusaurus/types';
function testLocaleConfigsFor(locales: string[]) {
return chain(locales)
.keyBy(identity)
.mapValues(getDefaultLocaleConfig)
.value();
return Object.fromEntries(
locales.map((locale) => [locale, getDefaultLocaleConfig(locale)]),
);
}
function loadI18nTest(i18nConfig: I18nConfig, locale?: string) {

View file

@ -10,7 +10,7 @@ import {
type RouteConfig as RRRouteConfig,
} from 'react-router-config';
import fs from 'fs-extra';
import {mapValues, pickBy, countBy} from 'lodash';
import _ from 'lodash';
import type {RouteConfig, ReportingSeverity} from '@docusaurus/types';
import {
removePrefix,
@ -84,12 +84,12 @@ export function getAllBrokenLinks({
}): Record<string, BrokenLink[]> {
const filteredRoutes = filterIntermediateRoutes(routes);
const allBrokenLinks = mapValues(allCollectedLinks, (pageLinks, pagePath) =>
const allBrokenLinks = _.mapValues(allCollectedLinks, (pageLinks, pagePath) =>
getPageBrokenLinks({pageLinks, pagePath, routes: filteredRoutes}),
);
// remove pages without any broken link
return pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0);
return _.pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0);
}
export function getBrokenLinksErrorMessage(
@ -126,7 +126,7 @@ export function getBrokenLinksErrorMessage(
brokenLinks.map((brokenLink) => ({pagePage, brokenLink})),
);
const countedBrokenLinks = countBy(
const countedBrokenLinks = _.countBy(
flatList,
(item) => item.brokenLink.link,
);
@ -197,7 +197,7 @@ export async function filterExistingFileLinks({
return filePathsToTry.some(isExistingFile);
}
return mapValues(allCollectedLinks, (links) =>
return _.mapValues(allCollectedLinks, (links) =>
links.filter((link) => !linkFileExists(link)),
);
}

View file

@ -5,14 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/
import {isPlainObject} from 'lodash';
import type {HtmlTagObject} from '@docusaurus/types';
import htmlTags from 'html-tags';
import voidHtmlTags from 'html-tags/void';
import escapeHTML from 'escape-html';
function assertIsHtmlTagObject(val: unknown): asserts val is HtmlTagObject {
if (!isPlainObject(val)) {
if (typeof val !== 'object' || !val) {
throw new Error(`"${val}" is not a valid HTML tag object.`);
}
if (typeof (val as HtmlTagObject).tagName !== 'string') {

View file

@ -37,7 +37,7 @@ import {
readCodeTranslationFileContent,
getPluginsDefaultCodeTranslationMessages,
} from './translations/translations';
import {mapValues} from 'lodash';
import _ from 'lodash';
import type {RuleSetRule} from 'webpack';
import admonitions from 'remark-admonitions';
import {createRequire} from 'module';
@ -111,7 +111,7 @@ export async function loadContext(
})) ?? {};
// We only need key->message for code translations
const codeTranslations = mapValues(
const codeTranslations = _.mapValues(
codeTranslationFileContent,
(value) => value.message,
);

View file

@ -21,7 +21,7 @@ import type {
} from '@docusaurus/types';
import initPlugins from './init';
import logger from '@docusaurus/logger';
import {chain} from 'lodash';
import _ from 'lodash';
import {localizePluginTranslationFile} from '../translations/translations';
import applyRouteTrailingSlash from './applyRouteTrailingSlash';
@ -121,10 +121,10 @@ export async function loadPlugins({
}),
);
const allContent: AllContent = chain(loadedPlugins)
const allContent: AllContent = _.chain(loadedPlugins)
.groupBy((item) => item.name)
.mapValues((nameItems) =>
chain(nameItems)
_.chain(nameItems)
.groupBy((item) => item.options.id ?? DEFAULT_PLUGIN_ID)
.mapValues((idItems) => idItems[0].content)
.value(),

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {groupBy} from 'lodash';
import _ from 'lodash';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
import type {InitializedPlugin} from '@docusaurus/types';
@ -14,9 +14,9 @@ import type {InitializedPlugin} from '@docusaurus/types';
export function ensureUniquePluginInstanceIds(
plugins: InitializedPlugin[],
): void {
const pluginsByName = groupBy(plugins, (p) => p.name);
const pluginsByName = _.groupBy(plugins, (p) => p.name);
Object.entries(pluginsByName).forEach(([pluginName, pluginInstances]) => {
const pluginInstancesById = groupBy(
const pluginInstancesById = _.groupBy(
pluginInstances,
(p) => p.options.id ?? DEFAULT_PLUGIN_ID,
);

View file

@ -12,7 +12,6 @@ import {
simpleHash,
escapePath,
} from '@docusaurus/utils';
import {has, isPlainObject, isString} from 'lodash';
import {stringify} from 'querystring';
import type {
ChunkRegistry,
@ -85,10 +84,15 @@ const RoutesImportsCode = [
].join('\n');
function isModule(value: unknown): value is Module {
if (isString(value)) {
if (typeof value === 'string') {
return true;
}
if (isPlainObject(value) && has(value, '__import') && has(value, 'path')) {
if (
typeof value === 'object' &&
// eslint-disable-next-line no-underscore-dangle
(value as Record<string, unknown>)?.__import &&
(value as Record<string, unknown>)?.path
) {
return true;
}
return false;
@ -137,11 +141,10 @@ export default async function loadRoutes(
...props
} = routeConfig;
if (!isString(routePath) || !component) {
if (typeof routePath !== 'string' || !component) {
throw new Error(
`Invalid route config: path must be a string and component is required.\n${JSON.stringify(
routeConfig,
)}`,
`Invalid route config: path must be a string and component is required.
${JSON.stringify(routeConfig)}`,
);
}

View file

@ -9,7 +9,7 @@ import fs from 'fs-extra';
import path from 'path';
import {fileToPath, posixPath, normalizeUrl, Globby} from '@docusaurus/utils';
import type {ThemeAliases} from '@docusaurus/types';
import {sortBy} from 'lodash';
import _ from 'lodash';
// Order of Webpack aliases is important because one alias can shadow another
// This ensure @theme/NavbarItem alias is after @theme/NavbarItem/LocaleDropdown
@ -17,7 +17,7 @@ import {sortBy} from 'lodash';
// See https://github.com/facebook/docusaurus/issues/5382
export function sortAliases(aliases: ThemeAliases): ThemeAliases {
// Alphabetical order by default
const entries = sortBy(Object.entries(aliases), ([alias]) => alias);
const entries = _.sortBy(Object.entries(aliases), ([alias]) => alias);
// @theme/NavbarItem should be after @theme/NavbarItem/LocaleDropdown
entries.sort(([alias1], [alias2]) =>
alias1.includes(`${alias2}/`) ? -1 : 0,

View file

@ -7,7 +7,7 @@
import path from 'path';
import fs from 'fs-extra';
import {mapValues, difference} from 'lodash';
import _ from 'lodash';
import type {
TranslationFileContent,
TranslationFile,
@ -75,7 +75,7 @@ function mergeTranslationFileContent({
options: WriteTranslationsOptions;
}): TranslationFileContent {
// Apply messagePrefix to all messages
const newContentTransformed = mapValues(newContent, (value) => ({
const newContentTransformed = _.mapValues(newContent, (value) => ({
...value,
message: `${options.messagePrefix ?? ''}${value.message}`,
}));
@ -110,7 +110,7 @@ export async function writeTranslationFileContent({
const existingContent = await readTranslationFileContent(filePath);
// Warn about potential legacy keys
const unknownKeys = difference(
const unknownKeys = _.difference(
Object.keys(existingContent ?? {}),
Object.keys(newContent),
);
@ -276,7 +276,7 @@ export function applyDefaultCodeTranslations({
extractedCodeTranslations: Record<string, TranslationMessage>;
defaultCodeMessages: Record<string, string>;
}): Record<string, TranslationMessage> {
const unusedDefaultCodeMessages = difference(
const unusedDefaultCodeMessages = _.difference(
Object.keys(defaultCodeMessages),
Object.keys(extractedCodeTranslations),
);
@ -285,7 +285,7 @@ export function applyDefaultCodeTranslations({
Please report this Docusaurus issue. name=${unusedDefaultCodeMessages}`;
}
return mapValues(
return _.mapValues(
extractedCodeTranslations,
(messageTranslation, messageId) => ({
...messageTranslation,

View file

@ -15,7 +15,7 @@ import {
} from '../base';
import * as utils from '@docusaurus/utils/lib/webpackUtils';
import {posixPath} from '@docusaurus/utils';
import {mapValues} from 'lodash';
import _ from 'lodash';
import type {Props, ThemeAliases} from '@docusaurus/types';
describe('babel transpilation exclude logic', () => {
@ -70,7 +70,7 @@ describe('babel transpilation exclude logic', () => {
describe('getDocusaurusAliases()', () => {
test('return appropriate webpack aliases', () => {
// using relative paths makes tests work everywhere
const relativeDocusaurusAliases = mapValues(
const relativeDocusaurusAliases = _.mapValues(
getDocusaurusAliases(),
(aliasValue) => posixPath(path.relative(__dirname, aliasValue)),
);
@ -125,7 +125,7 @@ describe('base webpack config', () => {
const aliases: ThemeAliases =
createBaseConfig(props, true).resolve?.alias ?? {};
// Make aliases relative so that test work on all computers
const relativeAliases = mapValues(aliases, (a) =>
const relativeAliases = _.mapValues(aliases, (a) =>
posixPath(path.relative(props.siteDir, a)),
);
expect(relativeAliases).toMatchSnapshot();

View file

@ -31,7 +31,7 @@ import type {
ConfigureWebpackUtils,
} from '@docusaurus/types';
import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils';
import {memoize} from 'lodash';
import _ from 'lodash';
// Utility method to get style loaders
export function getStyleLoaders(
@ -164,7 +164,7 @@ export const getCustomizableJSLoader =
: jsLoader(isServer);
// TODO remove this before end of 2021?
const warnBabelLoaderOnce = memoize(() => {
const warnBabelLoaderOnce = _.memoize(() => {
logger.warn`Docusaurus plans to support multiple JS loader strategies (Babel, esbuild...): code=${'getBabelLoader(isServer)'} is now deprecated in favor of code=${'getJSLoader(isServer)'}.`;
});
const getBabelLoaderDeprecated = function getBabelLoaderDeprecated(
@ -176,7 +176,7 @@ const getBabelLoaderDeprecated = function getBabelLoaderDeprecated(
};
// TODO remove this before end of 2021 ?
const warnCacheLoaderOnce = memoize(() => {
const warnCacheLoaderOnce = _.memoize(() => {
logger.warn`Docusaurus uses Webpack 5 and code=${'getCacheLoader()'} usage is now deprecated.`;
});
function getCacheLoaderDeprecated() {

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {sortBy} from 'lodash';
import _ from 'lodash';
import type {Palette} from 'node-vibrant/lib/color';
/**
@ -29,7 +29,7 @@ const toPalette = (swatch: Palette): string[] => {
}
return result;
}, [] as {popularity: number; hex: string}[]);
palette = sortBy(palette, ['popularity']);
palette = _.sortBy(palette, ['popularity']);
return palette.map((color) => color.hex).reverse();
};