refactor: fix a lot of errors in type-aware linting (#7477)

This commit is contained in:
Joshua Chen 2022-05-24 15:40:26 +08:00 committed by GitHub
parent 222bf3c091
commit bf1513a3e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
120 changed files with 407 additions and 364 deletions

View file

@ -70,7 +70,6 @@ describe('packages', () => {
packageJsonFile.content.name?.startsWith('@'), packageJsonFile.content.name?.startsWith('@'),
) )
.forEach((packageJsonFile) => { .forEach((packageJsonFile) => {
if (packageJsonFile) {
// Unfortunately jest custom message do not exist in loops, // Unfortunately jest custom message do not exist in loops,
// so using an exception instead to show failing package file // so using an exception instead to show failing package file
// (see https://github.com/facebook/jest/issues/3293) // (see https://github.com/facebook/jest/issues/3293)
@ -81,7 +80,6 @@ describe('packages', () => {
`Package ${packageJsonFile.file} does not have publishConfig.access: 'public'`, `Package ${packageJsonFile.file} does not have publishConfig.access: 'public'`,
); );
} }
}
}); });
}); });
}); });

View file

@ -8,6 +8,5 @@
import {createPlaygroundResponse} from '../functionUtils/playgroundUtils'; import {createPlaygroundResponse} from '../functionUtils/playgroundUtils';
import type {Handler} from '@netlify/functions'; import type {Handler} from '@netlify/functions';
export const handler: Handler = async function handler() { export const handler: Handler = () =>
return createPlaygroundResponse('codesandbox'); Promise.resolve(createPlaygroundResponse('codesandbox'));
};

View file

@ -8,6 +8,5 @@
import {createPlaygroundResponse} from '../functionUtils/playgroundUtils'; import {createPlaygroundResponse} from '../functionUtils/playgroundUtils';
import type {Handler} from '@netlify/functions'; import type {Handler} from '@netlify/functions';
export const handler: Handler = async function handler() { export const handler: Handler = () =>
return createPlaygroundResponse('stackblitz'); Promise.resolve(createPlaygroundResponse('stackblitz'));
};

View file

@ -65,6 +65,7 @@ async function askForPackageManagerChoice(): Promise<PackageManager> {
.map((p) => ({title: p, value: p})); .map((p) => ({title: p, value: p}));
return ( return (
(
(await prompts( (await prompts(
{ {
type: 'select', type: 'select',
@ -78,7 +79,8 @@ async function askForPackageManagerChoice(): Promise<PackageManager> {
}, },
}, },
)) as {packageManager: PackageManager} )) as {packageManager: PackageManager}
).packageManager; ).packageManager ?? defaultPackageManager
);
} }
async function getPackageManager( async function getPackageManager(
@ -101,8 +103,7 @@ async function getPackageManager(
(await findPackageManagerFromLockFile('.')) ?? (await findPackageManagerFromLockFile('.')) ??
findPackageManagerFromUserAgent() ?? findPackageManagerFromUserAgent() ??
// This only happens if the user has a global installation in PATH // This only happens if the user has a global installation in PATH
(skipInstall ? defaultPackageManager : askForPackageManagerChoice()) ?? (skipInstall ? defaultPackageManager : askForPackageManagerChoice())
defaultPackageManager
); );
} }
@ -215,7 +216,7 @@ async function getGitCommand(gitStrategy: GitStrategy): Promise<string> {
logger.info`Falling back to code=${'git clone'}`; logger.info`Falling back to code=${'git clone'}`;
}, },
}, },
)) as {command: string}; )) as {command?: string};
return command ?? 'git clone'; return command ?? 'git clone';
} }
case 'deep': case 'deep':
@ -362,7 +363,7 @@ async function getSource(
)) as {gitRepoUrl: string}; )) as {gitRepoUrl: string};
let strategy = cliOptions.gitStrategy; let strategy = cliOptions.gitStrategy;
if (!strategy) { if (!strategy) {
({strategy} = await prompts( ({strategy} = (await prompts(
{ {
type: 'select', type: 'select',
name: 'strategy', name: 'strategy',
@ -385,7 +386,7 @@ async function getSource(
logger.info`Falling back to name=${'deep'}`; logger.info`Falling back to name=${'deep'}`;
}, },
}, },
)); )) as {strategy?: GitStrategy});
} }
return { return {
type: 'git', type: 'git',
@ -426,13 +427,13 @@ async function getSource(
} }
let useTS = cliOptions.typescript; let useTS = cliOptions.typescript;
if (!useTS && template.tsVariantPath) { if (!useTS && template.tsVariantPath) {
({useTS} = await prompts({ ({useTS} = (await prompts({
type: 'confirm', type: 'confirm',
name: 'useTS', name: 'useTS',
message: message:
'This template is available in TypeScript. Do you want to use the TS variant?', 'This template is available in TypeScript. Do you want to use the TS variant?',
initial: false, initial: false,
})); })) as {useTS?: boolean});
} }
return { return {
type: 'template', type: 'template',

View file

@ -9,10 +9,10 @@ import chalk from 'chalk';
type InterpolatableValue = string | number | (string | number)[]; type InterpolatableValue = string | number | (string | number)[];
const path = (msg: unknown): string => chalk.cyan.underline(`"${msg}"`); const path = (msg: unknown): string => chalk.cyan.underline(`"${String(msg)}"`);
const url = (msg: unknown): string => chalk.cyan.underline(msg); const url = (msg: unknown): string => chalk.cyan.underline(msg);
const name = (msg: unknown): string => chalk.blue.bold(msg); const name = (msg: unknown): string => chalk.blue.bold(msg);
const code = (msg: unknown): string => chalk.cyan(`\`${msg}\``); const code = (msg: unknown): string => chalk.cyan(`\`${String(msg)}\``);
const subdue = (msg: unknown): string => chalk.gray(msg); const subdue = (msg: unknown): string => chalk.gray(msg);
const num = (msg: unknown): string => chalk.yellow(msg); const num = (msg: unknown): string => chalk.yellow(msg);

View file

@ -21,7 +21,7 @@ export type LoadedMDXContent<FrontMatter, Metadata, Assets = undefined> = {
/** As provided by the content plugin. */ /** As provided by the content plugin. */
readonly metadata: Metadata; readonly metadata: Metadata;
/** A list of TOC items (headings). */ /** A list of TOC items (headings). */
readonly toc: readonly TOCItem[]; readonly toc?: readonly TOCItem[];
/** First h1 title before any content. */ /** First h1 title before any content. */
readonly contentTitle: string | undefined; readonly contentTitle: string | undefined;
/** /**

View file

@ -55,7 +55,7 @@ export type MDXOptions = {
beforeDefaultRehypePlugins: MDXPlugin[]; beforeDefaultRehypePlugins: MDXPlugin[];
}; };
export type Options = MDXOptions & { export type Options = Partial<MDXOptions> & {
staticDirs: string[]; staticDirs: string[];
siteDir: string; siteDir: string;
isMDXPartial?: (filePath: string) => boolean; isMDXPartial?: (filePath: string) => boolean;
@ -138,7 +138,7 @@ export async function mdxLoader(
): Promise<void> { ): Promise<void> {
const callback = this.async(); const callback = this.async();
const filePath = this.resourcePath; const filePath = this.resourcePath;
const reqOptions = this.getOptions() ?? {}; const reqOptions = this.getOptions();
const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString); const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString);

View file

@ -17,7 +17,7 @@ export default function plugin(): Transformer {
return (root) => { return (root) => {
const slugs = createSlugger(); const slugs = createSlugger();
visit(root, 'heading', (headingNode: Heading) => { visit(root, 'heading', (headingNode: Heading) => {
const data = headingNode.data || (headingNode.data = {}); const data = headingNode.data ?? (headingNode.data = {});
const properties = (data.hProperties || (data.hProperties = {})) as { const properties = (data.hProperties || (data.hProperties = {})) as {
id: string; id: string;
}; };
@ -36,7 +36,7 @@ export default function plugin(): Transformer {
// Support explicit heading IDs // Support explicit heading IDs
const parsedHeading = parseMarkdownHeadingId(heading); const parsedHeading = parseMarkdownHeadingId(heading);
id = parsedHeading.id || slugs.slug(heading); id = parsedHeading.id ?? slugs.slug(heading);
if (parsedHeading.id) { if (parsedHeading.id) {
// When there's an id, it is always in the last child node // When there's an id, it is always in the last child node

View file

@ -110,8 +110,9 @@ async function processLinkNode(node: Link, context: Context) {
if (!node.url) { if (!node.url) {
// Try to improve error feedback // Try to improve error feedback
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675 // see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
const title = node.title || (node.children[0] as Literal)?.value || '?'; const title =
const line = node?.position?.start?.line || '?'; node.title ?? (node.children[0] as Literal | undefined)?.value ?? '?';
const line = node.position?.start.line ?? '?';
throw new Error( throw new Error(
`Markdown link URL is mandatory in "${toMessageRelativeFilePath( `Markdown link URL is mandatory in "${toMessageRelativeFilePath(
context.filePath, context.filePath,

View file

@ -15,7 +15,7 @@ export function stringifyContent(node: Parent): string {
} }
export function toValue(node: PhrasingContent | Heading): string { export function toValue(node: PhrasingContent | Heading): string {
switch (node?.type) { switch (node.type) {
case 'text': case 'text':
return escapeHtml(node.value); return escapeHtml(node.value);
case 'heading': case 'heading':

View file

@ -19,22 +19,22 @@ async function testMigration(siteDir: string, newDir: string) {
await migrateDocusaurusProject(siteDir, newDir, true, true); await migrateDocusaurusProject(siteDir, newDir, true, true);
expect( expect(
writeMock.mock.calls.sort((a, b) => writeMock.mock.calls.sort((a, b) =>
posixPath(a[0] as string).localeCompare(posixPath(b[0] as string)), posixPath(a[0]).localeCompare(posixPath(b[0])),
), ),
).toMatchSnapshot('write'); ).toMatchSnapshot('write');
expect( expect(
mkdirpMock.mock.calls.sort((a, b) => mkdirpMock.mock.calls.sort((a, b) =>
posixPath(a[0] as string).localeCompare(posixPath(b[0] as string)), posixPath(a[0]).localeCompare(posixPath(b[0])),
), ),
).toMatchSnapshot('mkdirp'); ).toMatchSnapshot('mkdirp');
expect( expect(
mkdirsMock.mock.calls.sort((a, b) => mkdirsMock.mock.calls.sort((a, b) =>
posixPath(a[0] as string).localeCompare(posixPath(b[0] as string)), posixPath(a[0]).localeCompare(posixPath(b[0])),
), ),
).toMatchSnapshot('mkdirs'); ).toMatchSnapshot('mkdirs');
expect( expect(
copyMock.mock.calls.sort((a, b) => copyMock.mock.calls.sort((a, b) =>
posixPath(a[0] as string).localeCompare(posixPath(b[0] as string)), posixPath(a[0]).localeCompare(posixPath(b[0])),
), ),
).toMatchSnapshot('copy'); ).toMatchSnapshot('copy');
writeMock.mockRestore(); writeMock.mockRestore();

View file

@ -40,12 +40,7 @@ export default function extractMetadata(content: string): Data {
lines.slice(0, -1).forEach((line) => { lines.slice(0, -1).forEach((line) => {
const keyValue = line.split(':') as [string, ...string[]]; const keyValue = line.split(':') as [string, ...string[]];
const key = keyValue[0].trim(); const key = keyValue[0].trim();
let value = keyValue.slice(1).join(':').trim(); const value = keyValue.slice(1).join(':').trim();
try {
value = JSON.parse(value);
} catch (err) {
// Ignore the error as it means it's not a JSON value.
}
metadata[key] = value; metadata[key] = value;
}); });
return {metadata, rawContent: both.content}; return {metadata, rawContent: both.content};

View file

@ -474,7 +474,7 @@ async function migrateVersionedDocs(
versions.reverse().map(async (version, index) => { versions.reverse().map(async (version, index) => {
if (index === 0) { if (index === 0) {
await fs.copy( await fs.copy(
path.join(siteDir, '..', context.v1Config.customDocsPath || 'docs'), path.join(siteDir, '..', context.v1Config.customDocsPath ?? 'docs'),
path.join(newDir, 'versioned_docs', `version-${version}`), path.join(newDir, 'versioned_docs', `version-${version}`),
); );
await fs.copy( await fs.copy(
@ -551,7 +551,9 @@ async function migrateVersionedSidebar(
const newSidebar = Object.entries(sidebarEntries).reduce( const newSidebar = Object.entries(sidebarEntries).reduce(
(topLevel: SidebarEntries, value) => { (topLevel: SidebarEntries, value) => {
const key = value[0].replace(versionRegex, ''); const key = value[0].replace(versionRegex, '');
topLevel[key] = Object.entries(value[1]).reduce((acc, val) => { topLevel[key] = Object.entries(value[1]).reduce<{
[key: string]: Array<string | {[key: string]: unknown}>;
}>((acc, val) => {
acc[val[0].replace(versionRegex, '')] = ( acc[val[0].replace(versionRegex, '')] = (
val[1] as SidebarEntry[] val[1] as SidebarEntry[]
).map((item) => { ).map((item) => {
@ -565,7 +567,7 @@ async function migrateVersionedSidebar(
}; };
}); });
return acc; return acc;
}, {} as {[key: string]: Array<string | {[key: string]: unknown}>}); }, {});
return topLevel; return topLevel;
}, },
{}, {},
@ -574,8 +576,9 @@ async function migrateVersionedSidebar(
} }
await Promise.all( await Promise.all(
sidebars.map(async (sidebar) => { sidebars.map(async (sidebar) => {
const newSidebar = Object.entries(sidebar.entries).reduce( const newSidebar = Object.entries(
(acc, val) => { sidebar.entries,
).reduce<SidebarEntries>((acc, val) => {
const key = `version-${sidebar.version}/${val[0]}`; const key = `version-${sidebar.version}/${val[0]}`;
acc[key] = Object.entries(val[1]).map((value) => ({ acc[key] = Object.entries(val[1]).map((value) => ({
type: 'category', type: 'category',
@ -598,9 +601,7 @@ async function migrateVersionedSidebar(
}), }),
})); }));
return acc; return acc;
}, }, {});
{} as SidebarEntries,
);
await fs.outputFile( await fs.outputFile(
path.join( path.join(
newDir, newDir,
@ -702,12 +703,12 @@ async function migrateLatestDocs(context: MigrationContext) {
async function migratePackageFile(context: MigrationContext): Promise<void> { async function migratePackageFile(context: MigrationContext): Promise<void> {
const {deps, siteDir, newDir} = context; const {deps, siteDir, newDir} = context;
const packageFile = importFresh(`${siteDir}/package.json`) as { const packageFile = importFresh<{
scripts?: {[key: string]: string}; scripts?: {[key: string]: string};
dependencies?: {[key: string]: string}; dependencies?: {[key: string]: string};
devDependencies?: {[key: string]: string}; devDependencies?: {[key: string]: string};
[otherKey: string]: unknown; [otherKey: string]: unknown;
}; }>(`${siteDir}/package.json`);
packageFile.scripts = { packageFile.scripts = {
...packageFile.scripts, ...packageFile.scripts,
start: 'docusaurus start', start: 'docusaurus start',

View file

@ -37,7 +37,7 @@ export default function sanitizeMD(code: string): string {
const htmlTree = unified().use(parse).parse(markdownString); const htmlTree = unified().use(parse).parse(markdownString);
visit(htmlTree, 'element', (node: Element) => { visit(htmlTree, 'element', (node: Element) => {
if (!tags[node.tagName as string]) { if (!tags[node.tagName]) {
(node as Element | Text).type = 'text'; (node as Element | Text).type = 'text';
(node as Element & Partial<Omit<Text, 'type'>>).value = (node as Element & Partial<Omit<Text, 'type'>>).value =
node.tagName + toText(node); node.tagName + toText(node);

View file

@ -8,7 +8,7 @@
declare module '@generated/client-modules' { declare module '@generated/client-modules' {
import type {ClientModule} from '@docusaurus/types'; import type {ClientModule} from '@docusaurus/types';
const clientModules: readonly (ClientModule & {default: ClientModule})[]; const clientModules: readonly (ClientModule & {default?: ClientModule})[];
export default clientModules; export default clientModules;
} }

View file

@ -17,7 +17,7 @@ import writeRedirectFiles, {
// - https://github.com/facebook/docusaurus/issues/3886 // - https://github.com/facebook/docusaurus/issues/3886
// - https://github.com/facebook/docusaurus/issues/3925 // - https://github.com/facebook/docusaurus/issues/3925
describe('createToUrl', () => { describe('createToUrl', () => {
it('creates appropriate redirect urls', async () => { it('creates appropriate redirect urls', () => {
expect(createToUrl('/', '/docs/something/else')).toBe( expect(createToUrl('/', '/docs/something/else')).toBe(
'/docs/something/else', '/docs/something/else',
); );
@ -29,7 +29,7 @@ describe('createToUrl', () => {
); );
}); });
it('creates appropriate redirect urls with baseUrl', async () => { it('creates appropriate redirect urls with baseUrl', () => {
expect(createToUrl('/baseUrl/', '/docs/something/else')).toBe( expect(createToUrl('/baseUrl/', '/docs/something/else')).toBe(
'/baseUrl/docs/something/else', '/baseUrl/docs/something/else',
); );
@ -43,7 +43,7 @@ describe('createToUrl', () => {
}); });
describe('toRedirectFilesMetadata', () => { describe('toRedirectFilesMetadata', () => {
it('creates appropriate metadata trailingSlash=undefined', async () => { it('creates appropriate metadata trailingSlash=undefined', () => {
const pluginContext = { const pluginContext = {
outDir: '/tmp/someFixedOutDir', outDir: '/tmp/someFixedOutDir',
baseUrl: 'https://docusaurus.io', baseUrl: 'https://docusaurus.io',
@ -70,7 +70,7 @@ describe('toRedirectFilesMetadata', () => {
); );
}); });
it('creates appropriate metadata trailingSlash=true', async () => { it('creates appropriate metadata trailingSlash=true', () => {
const pluginContext = { const pluginContext = {
outDir: '/tmp/someFixedOutDir', outDir: '/tmp/someFixedOutDir',
baseUrl: 'https://docusaurus.io', baseUrl: 'https://docusaurus.io',
@ -97,7 +97,7 @@ describe('toRedirectFilesMetadata', () => {
); );
}); });
it('creates appropriate metadata trailingSlash=false', async () => { it('creates appropriate metadata trailingSlash=false', () => {
const pluginContext = { const pluginContext = {
outDir: '/tmp/someFixedOutDir', outDir: '/tmp/someFixedOutDir',
baseUrl: 'https://docusaurus.io', baseUrl: 'https://docusaurus.io',
@ -127,7 +127,7 @@ describe('toRedirectFilesMetadata', () => {
); );
}); });
it('creates appropriate metadata for root baseUrl', async () => { it('creates appropriate metadata for root baseUrl', () => {
const pluginContext = { const pluginContext = {
outDir: '/tmp/someFixedOutDir', outDir: '/tmp/someFixedOutDir',
baseUrl: '/', baseUrl: '/',
@ -142,7 +142,7 @@ describe('toRedirectFilesMetadata', () => {
); );
}); });
it('creates appropriate metadata for empty baseUrl', async () => { it('creates appropriate metadata for empty baseUrl', () => {
const pluginContext = { const pluginContext = {
outDir: '/tmp/someFixedOutDir', outDir: '/tmp/someFixedOutDir',
baseUrl: '', baseUrl: '',

View file

@ -18,7 +18,7 @@ import type {PluginOptions, Options} from './options';
export default function pluginClientRedirectsPages( export default function pluginClientRedirectsPages(
context: LoadContext, context: LoadContext,
options: PluginOptions, options: PluginOptions,
): Plugin<unknown> { ): Plugin<void> {
const {trailingSlash} = context.siteConfig; const {trailingSlash} = context.siteConfig;
return { return {

View file

@ -89,6 +89,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
}, },
readingTime: ({content, defaultReadingTime}) => readingTime: ({content, defaultReadingTime}) =>
defaultReadingTime({content}), defaultReadingTime({content}),
truncateMarker: /<!--\s*truncate\s*-->/,
} as PluginOptions, } as PluginOptions,
); );
@ -128,6 +129,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
}, },
readingTime: ({content, defaultReadingTime}) => readingTime: ({content, defaultReadingTime}) =>
defaultReadingTime({content}), defaultReadingTime({content}),
truncateMarker: /<!--\s*truncate\s*-->/,
} as PluginOptions, } as PluginOptions,
); );

View file

@ -76,7 +76,7 @@ const getPlugin = async (
editUrl: BaseEditUrl, editUrl: BaseEditUrl,
...pluginOptions, ...pluginOptions,
}, },
}) as PluginOptions, }),
); );
}; };

View file

@ -74,7 +74,7 @@ function getSampleTranslationFilesTranslated() {
} }
describe('getContentTranslationFiles', () => { describe('getContentTranslationFiles', () => {
it('returns translation files matching snapshot', async () => { it('returns translation files matching snapshot', () => {
expect(getSampleTranslationFiles()).toMatchSnapshot(); expect(getSampleTranslationFiles()).toMatchSnapshot();
}); });
}); });

View file

@ -44,7 +44,11 @@ const AuthorsMapSchema = Joi.object<AuthorsMap>()
}); });
export function validateAuthorsMap(content: unknown): AuthorsMap { export function validateAuthorsMap(content: unknown): AuthorsMap {
return Joi.attempt(content, AuthorsMapSchema); const {error, value} = AuthorsMapSchema.validate(content);
if (error) {
throw error;
}
return value;
} }
export async function getAuthorsMap(params: { export async function getAuthorsMap(params: {

View file

@ -264,7 +264,7 @@ async function processBlogSourceFile(
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text; const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
const description = frontMatter.description ?? excerpt ?? ''; const description = frontMatter.description ?? excerpt ?? '';
const slug = frontMatter.slug || parsedBlogFileName.slug; const slug = frontMatter.slug ?? parsedBlogFileName.slug;
const permalink = normalizeUrl([baseUrl, routeBasePath, slug]); const permalink = normalizeUrl([baseUrl, routeBasePath, slug]);
@ -323,7 +323,7 @@ async function processBlogSourceFile(
defaultReadingTime, defaultReadingTime,
}) })
: undefined, : undefined,
truncated: truncateMarker?.test(content) || false, truncated: truncateMarker.test(content),
authors, authors,
frontMatter, frontMatter,
}, },

View file

@ -176,10 +176,6 @@ export default async function pluginContentBlog(
}, },
async contentLoaded({content: blogContents, actions}) { async contentLoaded({content: blogContents, actions}) {
if (!blogContents) {
return;
}
const { const {
blogListComponent, blogListComponent,
blogPostComponent, blogPostComponent,
@ -500,11 +496,7 @@ export default async function pluginContentBlog(
}, },
injectHtmlTags({content}) { injectHtmlTags({content}) {
if (!content.blogPosts.length) { if (!content.blogPosts.length || !options.feedOptions.type) {
return {};
}
if (!options.feedOptions?.type) {
return {}; return {};
} }

View file

@ -34,5 +34,5 @@ export default function markdownLoader(
finalContent = truncate(finalContent, markdownLoaderOptions.truncateMarker); finalContent = truncate(finalContent, markdownLoaderOptions.truncateMarker);
} }
return callback?.(null, finalContent); return callback(null, finalContent);
} }

View file

@ -136,9 +136,6 @@ export function validateOptions({
validate, validate,
options, options,
}: OptionValidationContext<Options, PluginOptions>): PluginOptions { }: OptionValidationContext<Options, PluginOptions>): PluginOptions {
const validatedOptions = validate( const validatedOptions = validate(PluginOptionSchema, options);
PluginOptionSchema,
options,
) as PluginOptions;
return validatedOptions; return validatedOptions;
} }

View file

@ -90,13 +90,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc1 description", "description": "doc1 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc1 title",
},
"id": "doc1", "id": "doc1",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc1 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc1 title", "title": "doc1 title",
@ -106,13 +108,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc2 description", "description": "doc2 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc2 title",
},
"id": "doc2", "id": "doc2",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc2 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc2 title", "title": "doc2 title",
@ -122,13 +126,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc3 description", "description": "doc3 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc3 title",
},
"id": "doc3", "id": "doc3",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc3 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc3 title", "title": "doc3 title",
@ -138,13 +144,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc4 description", "description": "doc4 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc4 title",
},
"id": "doc4", "id": "doc4",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc4 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc4 title", "title": "doc4 title",
@ -154,13 +162,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc5 description", "description": "doc5 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc5 title",
},
"id": "doc5", "id": "doc5",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc5 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc5 title", "title": "doc5 title",
@ -232,13 +242,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc1 description", "description": "doc1 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc1 title",
},
"id": "doc1", "id": "doc1",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc1 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc1 title", "title": "doc1 title",
@ -248,13 +260,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc2 description", "description": "doc2 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc2 title",
},
"id": "doc2", "id": "doc2",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc2 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc2 title", "title": "doc2 title",
@ -264,13 +278,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc3 description", "description": "doc3 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc3 title",
},
"id": "doc3", "id": "doc3",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc3 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc3 title", "title": "doc3 title",
@ -280,13 +296,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc4 description", "description": "doc4 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc4 title",
},
"id": "doc4", "id": "doc4",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc4 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc4 title", "title": "doc4 title",
@ -296,13 +314,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc5 description", "description": "doc5 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc5 title",
},
"id": "doc5", "id": "doc5",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc5 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc5 title", "title": "doc5 title",
@ -374,13 +394,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc1 description", "description": "doc1 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc1 title",
},
"id": "doc1", "id": "doc1",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc1 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc1 title", "title": "doc1 title",
@ -390,13 +412,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc2 description", "description": "doc2 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc2 title",
},
"id": "doc2", "id": "doc2",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc2 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc2 title", "title": "doc2 title",
@ -406,13 +430,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc3 description", "description": "doc3 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc3 title",
},
"id": "doc3", "id": "doc3",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc3 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc3 title", "title": "doc3 title",
@ -422,13 +448,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc4 description", "description": "doc4 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc4 title",
},
"id": "doc4", "id": "doc4",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc4 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc4 title", "title": "doc4 title",
@ -438,13 +466,15 @@ exports[`translateLoadedContent returns translated loaded content 1`] = `
{ {
"description": "doc5 description", "description": "doc5 description",
"editUrl": "any", "editUrl": "any",
"frontMatter": {
"sidebar_label": "doc5 title",
},
"id": "doc5", "id": "doc5",
"lastUpdatedAt": 0, "lastUpdatedAt": 0,
"lastUpdatedBy": "any", "lastUpdatedBy": "any",
"next": undefined, "next": undefined,
"permalink": "any", "permalink": "any",
"previous": undefined, "previous": undefined,
"sidebar_label": "doc5 title",
"slug": "any", "slug": "any",
"source": "any", "source": "any",
"title": "doc5 title", "title": "doc5 title",

View file

@ -119,7 +119,7 @@ function createTestUtils({
async function testSlug(docFileSource: string, expectedPermalink: string) { async function testSlug(docFileSource: string, expectedPermalink: string) {
const docFile = await readDoc(docFileSource); const docFile = await readDoc(docFileSource);
const metadata = await processDocMetadata({ const metadata = processDocMetadata({
docFile, docFile,
versionMetadata, versionMetadata,
context, context,

View file

@ -54,9 +54,9 @@ const createFakeActions = (contentDir: string) => {
addRoute: (config: RouteConfig) => { addRoute: (config: RouteConfig) => {
routeConfigs.push(config); routeConfigs.push(config);
}, },
createData: async (name: string, content: unknown) => { createData: (name: string, content: unknown) => {
dataContainer[name] = content; dataContainer[name] = content;
return path.join(contentDir, name); return Promise.resolve(path.join(contentDir, name));
}, },
setGlobalData: (data: unknown) => { setGlobalData: (data: unknown) => {
globalDataContainer.pluginName = {pluginId: data}; globalDataContainer.pluginName = {pluginId: data};

View file

@ -31,11 +31,11 @@ const defaultOptions = {
}; };
describe('normalizeDocsPluginOptions', () => { describe('normalizeDocsPluginOptions', () => {
it('returns default options for undefined user options', async () => { it('returns default options for undefined user options', () => {
expect(testValidate({})).toEqual(defaultOptions); expect(testValidate({})).toEqual(defaultOptions);
}); });
it('accepts correctly defined user options', async () => { it('accepts correctly defined user options', () => {
const userOptions = { const userOptions = {
path: 'my-docs', // Path to data on filesystem, relative to site dir. path: 'my-docs', // Path to data on filesystem, relative to site dir.
routeBasePath: 'my-docs', // URL Route. routeBasePath: 'my-docs', // URL Route.
@ -83,7 +83,7 @@ describe('normalizeDocsPluginOptions', () => {
}); });
}); });
it('accepts correctly defined remark and rehype plugin options', async () => { it('accepts correctly defined remark and rehype plugin options', () => {
const userOptions = { const userOptions = {
beforeDefaultRemarkPlugins: [], beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [markdownPluginsFunctionStub], beforeDefaultRehypePlugins: [markdownPluginsFunctionStub],
@ -100,7 +100,7 @@ describe('normalizeDocsPluginOptions', () => {
}); });
}); });
it('accepts admonitions false', async () => { it('accepts admonitions false', () => {
const admonitionsFalse = { const admonitionsFalse = {
admonitions: false, admonitions: false,
}; };
@ -110,7 +110,7 @@ describe('normalizeDocsPluginOptions', () => {
}); });
}); });
it('rejects admonitions true', async () => { it('rejects admonitions true', () => {
const admonitionsTrue = { const admonitionsTrue = {
admonitions: true, admonitions: true,
}; };

View file

@ -30,7 +30,9 @@ function createSampleDoc(doc: Pick<DocMetadata, 'id'>): DocMetadata {
unversionedId: 'any', unversionedId: 'any',
version: 'any', version: 'any',
title: `${doc.id} title`, title: `${doc.id} title`,
frontMatter: {
sidebar_label: `${doc.id} title`, sidebar_label: `${doc.id} title`,
},
description: `${doc.id} description`, description: `${doc.id} description`,
...doc, ...doc,
}; };
@ -136,7 +138,7 @@ function getSampleTranslationFilesTranslated() {
} }
describe('getLoadedContentTranslationFiles', () => { describe('getLoadedContentTranslationFiles', () => {
it('returns translation files', async () => { it('returns translation files', () => {
expect(getSampleTranslationFiles()).toMatchSnapshot(); expect(getSampleTranslationFiles()).toMatchSnapshot();
}); });
}); });

View file

@ -126,6 +126,6 @@ export function getDocVersionSuggestions(
const latestVersion = getLatestVersion(data); const latestVersion = getLatestVersion(data);
const activeDocContext = getActiveDocContext(data, pathname); const activeDocContext = getActiveDocContext(data, pathname);
const latestDocSuggestion: GlobalDoc | undefined = const latestDocSuggestion: GlobalDoc | undefined =
activeDocContext?.alternateDocVersions[latestVersion.name]; activeDocContext.alternateDocVersions[latestVersion.name];
return {latestDocSuggestion, latestVersionSuggestion: latestVersion}; return {latestDocSuggestion, latestVersionSuggestion: latestVersion};
} }

View file

@ -34,9 +34,11 @@ const StableEmptyObject = {};
// In blog-only mode, docs hooks are still used by the theme. We need a fail- // In blog-only mode, docs hooks are still used by the theme. We need a fail-
// safe fallback when the docs plugin is not in use // safe fallback when the docs plugin is not in use
export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} => export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} =>
(useAllPluginInstancesData('docusaurus-plugin-content-docs') as { (useAllPluginInstancesData('docusaurus-plugin-content-docs') as
| {
[pluginId: string]: GlobalPluginData; [pluginId: string]: GlobalPluginData;
}) ?? StableEmptyObject; }
| undefined) ?? StableEmptyObject;
export const useDocsData = (pluginId: string | undefined): GlobalPluginData => export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
usePluginData('docusaurus-plugin-content-docs', pluginId, { usePluginData('docusaurus-plugin-content-docs', pluginId, {

View file

@ -143,7 +143,7 @@ export default async function pluginContentDocs(
)}".`, )}".`,
); );
} }
async function processVersionDoc(docFile: DocFile) { function processVersionDoc(docFile: DocFile) {
return processDocMetadata({ return processDocMetadata({
docFile, docFile,
versionMetadata, versionMetadata,

View file

@ -16,5 +16,5 @@ export default function markdownLoader(
const fileString = source; const fileString = source;
const callback = this.async(); const callback = this.async();
const options = this.getOptions(); const options = this.getOptions();
return callback?.(null, linkify(fileString, this.resourcePath, options)); return callback(null, linkify(fileString, this.resourcePath, options));
} }

View file

@ -165,7 +165,7 @@ export function validateOptions({
} }
} }
const normalizedOptions = validate(OptionsSchema, options) as PluginOptions; const normalizedOptions = validate(OptionsSchema, options);
if (normalizedOptions.admonitions) { if (normalizedOptions.admonitions) {
normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([ normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([

View file

@ -51,7 +51,7 @@ Available document ids are:
} = docMetadata; } = docMetadata;
return { return {
type: 'link', type: 'link',
label: sidebarLabel || item.label || title, label: sidebarLabel ?? item.label ?? title,
href: permalink, href: permalink,
className: item.className, className: item.className,
customProps: customProps:

View file

@ -463,7 +463,7 @@ describe('DefaultSidebarItemsGenerator', () => {
expect(sidebarSlice).toMatchSnapshot(); expect(sidebarSlice).toMatchSnapshot();
}); });
it('throws for unknown index link', async () => { it('throws for unknown index link', () => {
const generateSidebar = () => const generateSidebar = () =>
DefaultSidebarItemsGenerator({ DefaultSidebarItemsGenerator({
numberPrefixParser: DefaultNumberPrefixParser, numberPrefixParser: DefaultNumberPrefixParser,
@ -499,7 +499,7 @@ describe('DefaultSidebarItemsGenerator', () => {
}, },
}); });
await expect(generateSidebar).rejects.toThrowErrorMatchingInlineSnapshot(` expect(() => generateSidebar()).toThrowErrorMatchingInlineSnapshot(`
"Can't find any doc with ID foo. "Can't find any doc with ID foo.
Available doc IDs: Available doc IDs:
- intro" - intro"

View file

@ -26,7 +26,7 @@ describe('processSidebars', () => {
function createStaticSidebarItemGenerator( function createStaticSidebarItemGenerator(
sidebarSlice: SidebarItem[], sidebarSlice: SidebarItem[],
): SidebarItemsGenerator { ): SidebarItemsGenerator {
return jest.fn(async () => sidebarSlice); return jest.fn(() => sidebarSlice);
} }
const StaticGeneratedSidebarSlice: NormalizedSidebar = [ const StaticGeneratedSidebarSlice: NormalizedSidebar = [
@ -40,7 +40,7 @@ describe('processSidebars', () => {
// @ts-expect-error: good enough for this test // @ts-expect-error: good enough for this test
const version: VersionMetadata = { const version: VersionMetadata = {
versionName: '1.0.0', versionName: '1.0.0',
versionPath: '/docs/1.0.0', path: '/docs/1.0.0',
}; };
const params: SidebarProcessorParams = { const params: SidebarProcessorParams = {

View file

@ -134,11 +134,11 @@ describe('createSidebarsUtils', () => {
getFirstLink, getFirstLink,
} = createSidebarsUtils(sidebars); } = createSidebarsUtils(sidebars);
it('getFirstDocIdOfFirstSidebar', async () => { it('getFirstDocIdOfFirstSidebar', () => {
expect(getFirstDocIdOfFirstSidebar()).toBe('doc1'); expect(getFirstDocIdOfFirstSidebar()).toBe('doc1');
}); });
it('getSidebarNameByDocId', async () => { it('getSidebarNameByDocId', () => {
expect(getSidebarNameByDocId('doc1')).toBe('sidebar1'); expect(getSidebarNameByDocId('doc1')).toBe('sidebar1');
expect(getSidebarNameByDocId('doc2')).toBe('sidebar1'); expect(getSidebarNameByDocId('doc2')).toBe('sidebar1');
expect(getSidebarNameByDocId('doc3')).toBe('sidebar2'); expect(getSidebarNameByDocId('doc3')).toBe('sidebar2');
@ -149,7 +149,7 @@ describe('createSidebarsUtils', () => {
expect(getSidebarNameByDocId('unknown_id')).toBeUndefined(); expect(getSidebarNameByDocId('unknown_id')).toBeUndefined();
}); });
it('getDocNavigation', async () => { it('getDocNavigation', () => {
expect(getDocNavigation('doc1', 'doc1', undefined)).toEqual({ expect(getDocNavigation('doc1', 'doc1', undefined)).toEqual({
sidebarName: 'sidebar1', sidebarName: 'sidebar1',
previous: undefined, previous: undefined,
@ -229,7 +229,7 @@ describe('createSidebarsUtils', () => {
}); });
}); });
it('getCategoryGeneratedIndexNavigation', async () => { it('getCategoryGeneratedIndexNavigation', () => {
expect( expect(
getCategoryGeneratedIndexNavigation('/s3-subcategory-index-permalink'), getCategoryGeneratedIndexNavigation('/s3-subcategory-index-permalink'),
).toMatchObject({ ).toMatchObject({
@ -259,7 +259,7 @@ describe('createSidebarsUtils', () => {
}); });
}); });
it('getCategoryGeneratedIndexList', async () => { it('getCategoryGeneratedIndexList', () => {
expect(getCategoryGeneratedIndexList()).toMatchObject([ expect(getCategoryGeneratedIndexList()).toMatchObject([
{ {
type: 'category', type: 'category',
@ -301,7 +301,7 @@ describe('createSidebarsUtils', () => {
}); });
describe('collectSidebarDocItems', () => { describe('collectSidebarDocItems', () => {
it('can collect docs', async () => { it('can collect docs', () => {
const sidebar: Sidebar = [ const sidebar: Sidebar = [
{ {
type: 'category', type: 'category',
@ -357,7 +357,7 @@ describe('collectSidebarDocItems', () => {
}); });
describe('collectSidebarCategories', () => { describe('collectSidebarCategories', () => {
it('can collect categories', async () => { it('can collect categories', () => {
const sidebar: Sidebar = [ const sidebar: Sidebar = [
{ {
type: 'category', type: 'category',
@ -415,7 +415,7 @@ describe('collectSidebarCategories', () => {
}); });
describe('collectSidebarLinks', () => { describe('collectSidebarLinks', () => {
it('can collect links', async () => { it('can collect links', () => {
const sidebar: Sidebar = [ const sidebar: Sidebar = [
{ {
type: 'category', type: 'category',
@ -453,7 +453,7 @@ describe('collectSidebarLinks', () => {
}); });
describe('collectSidebarsDocIds', () => { describe('collectSidebarsDocIds', () => {
it('can collect sidebars doc items', async () => { it('can collect sidebars doc items', () => {
const sidebar1: Sidebar = [ const sidebar1: Sidebar = [
{ {
type: 'category', type: 'category',
@ -499,7 +499,7 @@ describe('collectSidebarsDocIds', () => {
}); });
describe('transformSidebarItems', () => { describe('transformSidebarItems', () => {
it('can transform sidebar items', async () => { it('can transform sidebar items', () => {
const sidebar: Sidebar = [ const sidebar: Sidebar = [
{ {
type: 'category', type: 'category',

View file

@ -9,7 +9,7 @@ import {validateSidebars, validateCategoryMetadataFile} from '../validation';
import type {SidebarsConfig, CategoryMetadataFile} from '../types'; import type {SidebarsConfig, CategoryMetadataFile} from '../types';
describe('validateSidebars', () => { describe('validateSidebars', () => {
it('throw for bad value', async () => { it('throw for bad value', () => {
expect(() => validateSidebars({sidebar: [{type: 42}]})) expect(() => validateSidebars({sidebar: [{type: 42}]}))
.toThrowErrorMatchingInlineSnapshot(` .toThrowErrorMatchingInlineSnapshot(`
"{ "{
@ -21,12 +21,12 @@ describe('validateSidebars', () => {
`); `);
}); });
it('accept empty object', async () => { it('accept empty object', () => {
const sidebars: SidebarsConfig = {}; const sidebars: SidebarsConfig = {};
validateSidebars(sidebars); validateSidebars(sidebars);
}); });
it('accept valid values', async () => { it('accept valid values', () => {
const sidebars: SidebarsConfig = { const sidebars: SidebarsConfig = {
sidebar1: [ sidebar1: [
{type: 'doc', id: 'doc1'}, {type: 'doc', id: 'doc1'},
@ -207,7 +207,7 @@ describe('validateSidebars', () => {
).toThrowErrorMatchingInlineSnapshot(`"sidebar.forEach is not a function"`); ).toThrowErrorMatchingInlineSnapshot(`"sidebar.forEach is not a function"`);
}); });
it('sidebars item doc but id is not a string', async () => { it('sidebars item doc but id is not a string', () => {
expect(() => expect(() =>
validateSidebars({ validateSidebars({
docs: [ docs: [
@ -267,18 +267,18 @@ describe('validateSidebars', () => {
describe('validateCategoryMetadataFile', () => { describe('validateCategoryMetadataFile', () => {
// TODO add more tests // TODO add more tests
it('throw for bad value', async () => { it('throw for bad value', () => {
expect(() => expect(() =>
validateCategoryMetadataFile(42), validateCategoryMetadataFile(42),
).toThrowErrorMatchingInlineSnapshot(`""value" must be of type object"`); ).toThrowErrorMatchingInlineSnapshot(`""value" must be of type object"`);
}); });
it('accept empty object', async () => { it('accept empty object', () => {
const content: CategoryMetadataFile = {}; const content: CategoryMetadataFile = {};
expect(validateCategoryMetadataFile(content)).toEqual(content); expect(validateCategoryMetadataFile(content)).toEqual(content);
}); });
it('accept valid values', async () => { it('accept valid values', () => {
const content: CategoryMetadataFile = { const content: CategoryMetadataFile = {
className: 'className', className: 'className',
label: 'Category Label', label: 'Category Label',
@ -295,7 +295,7 @@ describe('validateCategoryMetadataFile', () => {
expect(validateCategoryMetadataFile(content)).toEqual(content); expect(validateCategoryMetadataFile(content)).toEqual(content);
}); });
it('rejects permalink', async () => { it('rejects permalink', () => {
const content: CategoryMetadataFile = { const content: CategoryMetadataFile = {
className: 'className', className: 'className',
label: 'Category Label', label: 'Category Label',

View file

@ -46,7 +46,7 @@ type Dir = {
}; };
// Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449 // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449
export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = ({
numberPrefixParser, numberPrefixParser,
isCategoryIndex, isCategoryIndex,
docs: allDocs, docs: allDocs,

View file

@ -84,7 +84,7 @@ function postProcessSidebarItem(
}; };
} }
// A non-collapsible category can't be collapsed! // A non-collapsible category can't be collapsed!
if (category.collapsible === false) { if (!category.collapsible) {
category.collapsed = false; category.collapsed = false;
} }
return category; return category;

View file

@ -251,7 +251,7 @@ export type SidebarItemsGeneratorArgs = {
}; };
export type SidebarItemsGenerator = ( export type SidebarItemsGenerator = (
generatorArgs: SidebarItemsGeneratorArgs, generatorArgs: SidebarItemsGeneratorArgs,
) => Promise<NormalizedSidebar>; ) => NormalizedSidebar | Promise<NormalizedSidebar>;
export type SidebarItemsGeneratorOption = ( export type SidebarItemsGeneratorOption = (
generatorArgs: { generatorArgs: {
@ -262,7 +262,7 @@ export type SidebarItemsGeneratorOption = (
*/ */
defaultSidebarItemsGenerator: SidebarItemsGenerator; defaultSidebarItemsGenerator: SidebarItemsGenerator;
} & SidebarItemsGeneratorArgs, } & SidebarItemsGeneratorArgs,
) => Promise<NormalizedSidebarItem[]>; ) => NormalizedSidebar | Promise<NormalizedSidebar>;
export type SidebarProcessorParams = { export type SidebarProcessorParams = {
sidebarItemsGenerator: SidebarItemsGeneratorOption; sidebarItemsGenerator: SidebarItemsGeneratorOption;

View file

@ -257,7 +257,7 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
): boolean { ): boolean {
return ( return (
item.type === 'category' && item.type === 'category' &&
item.link?.type === 'generated-index' && item.link.type === 'generated-index' &&
item.link.permalink === categoryGeneratedIndexPermalink item.link.permalink === categoryGeneratedIndexPermalink
); );
} }

View file

@ -144,7 +144,7 @@ function validateSidebarItem(
// manually // manually
Joi.assert(item, sidebarItemSchema); Joi.assert(item, sidebarItemSchema);
if ((item as NormalizedSidebarItemCategory).type === 'category') { if ((item as NormalizedSidebarItem).type === 'category') {
(item as NormalizedSidebarItemCategory).items.forEach(validateSidebarItem); (item as NormalizedSidebarItemCategory).items.forEach(validateSidebarItem);
} }
} }
@ -170,5 +170,9 @@ const categoryMetadataFileSchema = Joi.object<CategoryMetadataFile>({
export function validateCategoryMetadataFile( export function validateCategoryMetadataFile(
unsafeContent: unknown, unsafeContent: unknown,
): CategoryMetadataFile { ): CategoryMetadataFile {
return Joi.attempt(unsafeContent, categoryMetadataFileSchema); const {error, value} = categoryMetadataFileSchema.validate(unsafeContent);
if (error) {
throw error;
}
return value;
} }

View file

@ -58,7 +58,7 @@ export default function getSlug({
) { ) {
return dirNameSlug; return dirNameSlug;
} }
const baseSlug = frontMatterSlug || baseID; const baseSlug = frontMatterSlug ?? baseID;
return resolvePathname(baseSlug, getDirNameSlug()); return resolvePathname(baseSlug, getDirNameSlug());
} }

View file

@ -604,7 +604,7 @@ describe('readVersionsMetadata', () => {
context: defaultContext, context: defaultContext,
}), }),
).rejects.toThrowErrorMatchingInlineSnapshot( ).rejects.toThrowErrorMatchingInlineSnapshot(
`"Versions should be strings. Found type "number" for version "1.1"."`, `"Versions should be strings. Found type "number" for version 1.1."`,
); );
jsonMock.mockImplementationOnce(() => [' ']); jsonMock.mockImplementationOnce(() => [' ']);

View file

@ -11,7 +11,9 @@ import type {VersionsOptions} from '@docusaurus/plugin-content-docs';
export function validateVersionName(name: unknown): asserts name is string { export function validateVersionName(name: unknown): asserts name is string {
if (typeof name !== 'string') { if (typeof name !== 'string') {
throw new Error( throw new Error(
`Versions should be strings. Found type "${typeof name}" for version "${name}".`, `Versions should be strings. Found type "${typeof name}" for version ${JSON.stringify(
name,
)}.`,
); );
} }
if (!name.trim()) { if (!name.trim()) {

View file

@ -16,7 +16,7 @@ describe('docusaurus-plugin-content-pages', () => {
it('loads simple pages', async () => { it('loads simple pages', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website'); const siteDir = path.join(__dirname, '__fixtures__', 'website');
const context = await loadContext({siteDir}); const context = await loadContext({siteDir});
const plugin = await pluginContentPages( const plugin = pluginContentPages(
context, context,
validateOptions({ validateOptions({
validate: normalizePluginOptions, validate: normalizePluginOptions,
@ -33,7 +33,7 @@ describe('docusaurus-plugin-content-pages', () => {
it('loads simple pages with french translations', async () => { it('loads simple pages with french translations', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website'); const siteDir = path.join(__dirname, '__fixtures__', 'website');
const context = await loadContext({siteDir}); const context = await loadContext({siteDir});
const plugin = await pluginContentPages( const plugin = pluginContentPages(
{ {
...context, ...context,
i18n: { i18n: {

View file

@ -39,10 +39,10 @@ export function getContentPathList(contentPaths: PagesContentPaths): string[] {
const isMarkdownSource = (source: string) => const isMarkdownSource = (source: string) =>
source.endsWith('.md') || source.endsWith('.mdx'); source.endsWith('.md') || source.endsWith('.mdx');
export default async function pluginContentPages( export default function pluginContentPages(
context: LoadContext, context: LoadContext,
options: PluginOptions, options: PluginOptions,
): Promise<Plugin<LoadedContent | null>> { ): Plugin<LoadedContent | null> {
if (options.admonitions) { if (options.admonitions) {
options.remarkPlugins = options.remarkPlugins.concat([ options.remarkPlugins = options.remarkPlugins.concat([
[admonitions, options.admonitions], [admonitions, options.admonitions],

View file

@ -18,5 +18,5 @@ export default function markdownLoader(
// TODO provide additional md processing here? like interlinking pages? // TODO provide additional md processing here? like interlinking pages?
// fileString = linkify(fileString) // fileString = linkify(fileString)
return callback?.(null, fileString); return callback(null, fileString);
} }

View file

@ -28,7 +28,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
admonitions: {}, admonitions: {},
}; };
const PluginOptionSchema = Joi.object({ const PluginOptionSchema = Joi.object<PluginOptions>({
path: Joi.string().default(DEFAULT_OPTIONS.path), path: Joi.string().default(DEFAULT_OPTIONS.path),
routeBasePath: Joi.string().default(DEFAULT_OPTIONS.routeBasePath), routeBasePath: Joi.string().default(DEFAULT_OPTIONS.routeBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),

View file

@ -20,7 +20,7 @@ export default function DebugMetadata(): JSX.Element {
</div> </div>
<div> <div>
Site Version:{' '} Site Version:{' '}
<code>{siteMetadata.siteVersion || 'No version specified'}</code> <code>{siteMetadata.siteVersion ?? 'No version specified'}</code>
</div> </div>
<h3 className={styles.sectionTitle}>Plugins and themes</h3> <h3 className={styles.sectionTitle}>Plugins and themes</h3>
<ul className="clean-list"> <ul className="clean-list">

View file

@ -25,7 +25,7 @@ export default function pluginGoogleGtag(
return { return {
name: 'docusaurus-plugin-google-gtag', name: 'docusaurus-plugin-google-gtag',
async contentLoaded({actions}) { contentLoaded({actions}) {
actions.setGlobalData(options); actions.setGlobalData(options);
}, },

View file

@ -80,7 +80,7 @@ export function validateOptions({
validate, validate,
options, options,
}: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions { }: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions {
const pluginOptionsSchema = Joi.object({ const pluginOptionsSchema = Joi.object<PluginOptions>({
disableInDev: Joi.boolean().default(true), disableInDev: Joi.boolean().default(true),
}).unknown(); }).unknown();
return validate(pluginOptionsSchema, options); return validate(pluginOptionsSchema, options);

View file

@ -22,9 +22,9 @@ function bytesToSize(bytes: number) {
} }
const scale = Math.floor(Math.log(bytes) / Math.log(1024)); const scale = Math.floor(Math.log(bytes) / Math.log(1024));
if (scale === 0) { if (scale === 0) {
return `${bytes} ${sizes[scale]}`; return `${bytes} ${sizes[scale]!}`;
} }
return `${(bytes / 1024 ** scale).toFixed(1)} ${sizes[scale]}`; return `${(bytes / 1024 ** scale).toFixed(1)} ${sizes[scale]!}`;
} }
// Adopted from https://github.com/endiliey/react-ideal-image/blob/master/src/components/IdealImage/index.js#L43-L75 // Adopted from https://github.com/endiliey/react-ideal-image/blob/master/src/components/IdealImage/index.js#L43-L75
@ -100,8 +100,8 @@ export default function IdealImage(props: Props): JSX.Element {
{...props} {...props}
alt={alt} alt={alt}
className={className} className={className}
height={img.src.height || 100} height={img.src.height ?? 100}
width={img.src.width || 100} width={img.src.width ?? 100}
placeholder={{lqip: img.preSrc}} placeholder={{lqip: img.preSrc}}
src={img.src.src} src={img.src.src}
srcSet={img.src.images.map((image) => ({ srcSet={img.src.images.map((image) => ({

View file

@ -89,7 +89,7 @@ export default function pluginPWA(
new webpack.EnvironmentPlugin({ new webpack.EnvironmentPlugin({
PWA_DEBUG: debug, PWA_DEBUG: debug,
PWA_SERVICE_WORKER_URL: path.posix.resolve( PWA_SERVICE_WORKER_URL: path.posix.resolve(
`${config.output?.publicPath || '/'}`, `${(config.output?.publicPath as string) || '/'}`,
'sw.js', 'sw.js',
), ),
PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES: PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES:
@ -102,7 +102,7 @@ export default function pluginPWA(
injectHtmlTags() { injectHtmlTags() {
const headTags: HtmlTags = []; const headTags: HtmlTags = [];
if (isProd && pwaHead) { if (isProd) {
pwaHead.forEach(({tagName, ...attributes}) => { pwaHead.forEach(({tagName, ...attributes}) => {
(['href', 'content'] as const).forEach((attribute) => { (['href', 'content'] as const).forEach((attribute) => {
const attributeValue = attributes[attribute]; const attributeValue = attributes[attribute];
@ -160,7 +160,7 @@ export default function pluginPWA(
plugins: [ plugins: [
new webpack.EnvironmentPlugin({ new webpack.EnvironmentPlugin({
// Fallback value required with Webpack 5 // Fallback value required with Webpack 5
PWA_SW_CUSTOM: swCustom || '', PWA_SW_CUSTOM: swCustom ?? '',
}), }),
new LogPlugin({ new LogPlugin({
name: 'Service Worker', name: 'Service Worker',
@ -189,7 +189,7 @@ export default function pluginPWA(
'**/*.{png,jpg,jpeg,gif,svg,ico}', '**/*.{png,jpg,jpeg,gif,svg,ico}',
'**/*.{woff,woff2,eot,ttf,otf}', '**/*.{woff,woff2,eot,ttf,otf}',
// @ts-expect-error: internal API? // @ts-expect-error: internal API?
...(injectManifest.globPatterns ?? []), ...((injectManifest.globPatterns as string[] | undefined) ?? []),
], ],
// Those attributes are not overrideable // Those attributes are not overrideable
swDest, swDest,

View file

@ -23,7 +23,7 @@ const DEFAULT_OPTIONS = {
reloadPopup: '@theme/PwaReloadPopup', reloadPopup: '@theme/PwaReloadPopup',
}; };
const Schema = Joi.object({ const optionsSchema = Joi.object<PluginOptions>({
debug: Joi.bool().default(DEFAULT_OPTIONS.debug), debug: Joi.bool().default(DEFAULT_OPTIONS.debug),
offlineModeActivationStrategies: Joi.array() offlineModeActivationStrategies: Joi.array()
.items( .items(
@ -58,5 +58,5 @@ export function validateOptions({
validate, validate,
options, options,
}: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions { }: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions {
return validate(Schema, options); return validate(optionsSchema, options);
} }

View file

@ -9,7 +9,7 @@ import {createStorageSlot} from '@docusaurus/theme-common';
// First: read the env variables (provided by Webpack) // First: read the env variables (provided by Webpack)
/* eslint-disable prefer-destructuring */ /* eslint-disable prefer-destructuring */
const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL; const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL!;
const PWA_RELOAD_POPUP = process.env.PWA_RELOAD_POPUP; const PWA_RELOAD_POPUP = process.env.PWA_RELOAD_POPUP;
const PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES = process.env const PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES = process.env
.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES as unknown as (keyof typeof OfflineModeActivationStrategiesImplementations)[]; .PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES as unknown as (keyof typeof OfflineModeActivationStrategiesImplementations)[];
@ -34,7 +34,7 @@ async function clearRegistrations() {
} }
await Promise.all( await Promise.all(
registrations.map(async (registration) => { registrations.map(async (registration) => {
const result = await registration?.unregister(); const result = await registration.unregister();
if (debug) { if (debug) {
console.log( console.log(
`[Docusaurus-PWA][registerSw]: unregister() service worker registration`, `[Docusaurus-PWA][registerSw]: unregister() service worker registration`,
@ -69,7 +69,7 @@ async function isAppInstalledEventFired() {
declare global { declare global {
interface Navigator { interface Navigator {
getInstalledRelatedApps: () => Promise<{platform: string}[]>; getInstalledRelatedApps: () => Promise<{platform: string}[]>;
connection: {effectiveType: string; saveData: boolean}; connection?: {effectiveType: string; saveData: boolean};
} }
} }

View file

@ -29,7 +29,8 @@ export const DEFAULT_OPTIONS: PluginOptions = {
ignorePatterns: [], ignorePatterns: [],
}; };
const PluginOptionSchema = Joi.object({ const PluginOptionSchema = Joi.object<PluginOptions>({
// @ts-expect-error: forbidden
cacheTime: Joi.forbidden().messages({ cacheTime: Joi.forbidden().messages({
'any.unknown': 'any.unknown':
'Option `cacheTime` in sitemap config is deprecated. Please remove it.', 'Option `cacheTime` in sitemap config is deprecated. Please remove it.',

View file

@ -62,8 +62,8 @@ const nodeForImport: Literal = {
const plugin: Plugin<[PluginOptions?]> = (options = {}) => { const plugin: Plugin<[PluginOptions?]> = (options = {}) => {
const {sync = false} = options; const {sync = false} = options;
return (root) => { return (root) => {
let transformed = false; let transformed = false as boolean;
let alreadyImported = false; let alreadyImported = false as boolean;
visit(root, (node: Node) => { visit(root, (node: Node) => {
if (isImport(node) && node.value.includes('@theme/Tabs')) { if (isImport(node) && node.value.includes('@theme/Tabs')) {
alreadyImported = true; alreadyImported = true;

View file

@ -177,7 +177,7 @@ export default function themeClassic(
const plugin: PostCssPlugin = { const plugin: PostCssPlugin = {
postcssPlugin: 'RtlCssPlugin', postcssPlugin: 'RtlCssPlugin',
prepare: (result) => { prepare: (result) => {
const file = result.root?.source?.input?.file; const file = result.root.source?.input.file;
// Skip Infima as we are using the its RTL version. // Skip Infima as we are using the its RTL version.
if (file === resolvedInfimaFile) { if (file === resolvedInfimaFile) {
return {}; return {};

View file

@ -21,12 +21,14 @@
// in their tsconfig. // in their tsconfig.
declare module '@docusaurus/theme-classic' { declare module '@docusaurus/theme-classic' {
import type {LoadContext, Plugin} from '@docusaurus/types'; import type {LoadContext, Plugin, PluginModule} from '@docusaurus/types';
export type Options = { export type Options = {
customCss?: string | string[]; customCss?: string | string[];
}; };
export const getSwizzleConfig: PluginModule['getSwizzleConfig'];
export default function themeClassic( export default function themeClassic(
context: LoadContext, context: LoadContext,
options: Options, options: Options,

View file

@ -52,7 +52,7 @@ export default function CodeBlockString({
magicComments, magicComments,
}); });
const showLineNumbers = const showLineNumbers =
showLineNumbersProp || containsLineNumbers(metastring); showLineNumbersProp ?? containsLineNumbers(metastring);
return ( return (
<Container <Container

View file

@ -23,7 +23,7 @@ export default function DocSidebarItemHtml({
className={clsx( className={clsx(
ThemeClassNames.docs.docSidebarItemLink, ThemeClassNames.docs.docSidebarItemLink,
ThemeClassNames.docs.docSidebarItemLinkLevel(level), ThemeClassNames.docs.docSidebarItemLinkLevel(level),
defaultStyle && `${styles.menuHtmlItem} menu__list-item`, defaultStyle && [styles.menuHtmlItem, 'menu__list-item'],
className, className,
)} )}
key={index} key={index}

View file

@ -21,7 +21,7 @@ function ColumnLinkItem({item}: {item: ColumnItemType}) {
dangerouslySetInnerHTML={{__html: item.html}} dangerouslySetInnerHTML={{__html: item.html}}
/> />
) : ( ) : (
<li key={item.href || item.to} className="footer__item"> <li key={item.href ?? item.to} className="footer__item">
<LinkItem item={item} /> <LinkItem item={item} />
</li> </li>
); );

View file

@ -27,7 +27,7 @@ export function useCollapsible({
initialState, initialState,
}: { }: {
/** The initial state. Will be non-collapsed by default. */ /** The initial state. Will be non-collapsed by default. */
initialState: boolean | (() => boolean); initialState?: boolean | (() => boolean);
}): { }): {
collapsed: boolean; collapsed: boolean;
setCollapsed: Dispatch<SetStateAction<boolean>>; setCollapsed: Dispatch<SetStateAction<boolean>>;

View file

@ -40,7 +40,7 @@ export function useSkipToContent(): {
e.preventDefault(); e.preventDefault();
const targetElement: HTMLElement | null = const targetElement: HTMLElement | null =
document.querySelector('main:first-of-type') || document.querySelector('main:first-of-type') ??
document.querySelector(`.${ThemeClassNames.wrapper.main}`); document.querySelector(`.${ThemeClassNames.wrapper.main}`);
if (targetElement) { if (targetElement) {

View file

@ -38,15 +38,12 @@ function getAnchors({
}: { }: {
minHeadingLevel: number; minHeadingLevel: number;
maxHeadingLevel: number; maxHeadingLevel: number;
}) { }): HTMLElement[] {
const selectors = []; const selectors = [];
for (let i = minHeadingLevel; i <= maxHeadingLevel; i += 1) { for (let i = minHeadingLevel; i <= maxHeadingLevel; i += 1) {
selectors.push(`h${i}.anchor`); selectors.push(`h${i}.anchor`);
} }
return Array.from(document.querySelectorAll(selectors.join()));
return Array.from(
document.querySelectorAll(selectors.join()),
) as HTMLElement[];
} }
function getActiveAnchor( function getActiveAnchor(

View file

@ -171,7 +171,7 @@ export function parseLines(
const metastringRangeClassName = magicComments[0]!.className; const metastringRangeClassName = magicComments[0]!.className;
const lines = rangeParser(linesRange) const lines = rangeParser(linesRange)
.filter((n) => n > 0) .filter((n) => n > 0)
.map((n) => [n - 1, [metastringRangeClassName]]); .map((n) => [n - 1, [metastringRangeClassName]] as [number, string[]]);
return {lineClassNames: Object.fromEntries(lines), code}; return {lineClassNames: Object.fromEntries(lines), code};
} }
if (language === undefined) { if (language === undefined) {
@ -189,7 +189,7 @@ export function parseLines(
const lineToClassName: {[comment: string]: string} = Object.fromEntries( const lineToClassName: {[comment: string]: string} = Object.fromEntries(
magicComments magicComments
.filter((d) => d.line) .filter((d) => d.line)
.map(({className, line}) => [line, className]), .map(({className, line}) => [line!, className] as [string, string]),
); );
const blockStartToClassName: {[comment: string]: string} = Object.fromEntries( const blockStartToClassName: {[comment: string]: string} = Object.fromEntries(
magicComments magicComments

View file

@ -70,7 +70,7 @@ export class ReactContextError extends Error {
this.name = 'ReactContextError'; this.name = 'ReactContextError';
this.message = `Hook ${ this.message = `Hook ${
this.stack?.split('\n')[1]?.match(/at (?:\w+\.)?(?<name>\w+)/)?.groups! this.stack?.split('\n')[1]?.match(/at (?:\w+\.)?(?<name>\w+)/)?.groups!
.name .name ?? ''
} is called outside the <${providerName}>. ${additionalInfo || ''}`; } is called outside the <${providerName}>. ${additionalInfo || ''}`;
} }
} }

View file

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

View file

@ -112,11 +112,7 @@ export function useScrollPosition(
return; return;
} }
const currentPosition = getScrollPosition()!; const currentPosition = getScrollPosition()!;
if (dynamicEffect) {
dynamicEffect(currentPosition, lastPositionRef.current); dynamicEffect(currentPosition, lastPositionRef.current);
}
lastPositionRef.current = currentPosition; lastPositionRef.current = currentPosition;
}; };

View file

@ -39,7 +39,7 @@ export function useContextualSearchFilters(): {locale: string; tags: string[]} {
// plugin instances. // plugin instances.
function getDocPluginTags(pluginId: string) { function getDocPluginTags(pluginId: string) {
const activeVersion = const activeVersion =
activePluginAndVersion?.activePlugin?.pluginId === pluginId activePluginAndVersion?.activePlugin.pluginId === pluginId
? activePluginAndVersion.activeVersion ? activePluginAndVersion.activeVersion
: undefined; : undefined;

View file

@ -27,7 +27,7 @@ function treeifyTOC(flatTOC: readonly TOCItem[]): TOCTreeNode[] {
// level <i>. We will modify these indices as we iterate through all headings. // level <i>. We will modify these indices as we iterate through all headings.
// e.g. if an ### H3 was last seen at index 2, then prevIndexForLevel[3] === 2 // e.g. if an ### H3 was last seen at index 2, then prevIndexForLevel[3] === 2
// indices 0 and 1 will remain unused. // indices 0 and 1 will remain unused.
const prevIndexForLevel = Array(7).fill(-1); const prevIndexForLevel = Array<number>(7).fill(-1);
headings.forEach((curr, currIndex) => { headings.forEach((curr, currIndex) => {
// Take the last seen index for each ancestor level. the highest index will // Take the last seen index for each ancestor level. the highest index will

View file

@ -95,7 +95,11 @@ function selectPluralMessage(
} }
if (parts.length > localePluralForms.pluralForms.length) { if (parts.length > localePluralForms.pluralForms.length) {
console.error( console.error(
`For locale=${localePluralForms.locale}, a maximum of ${localePluralForms.pluralForms.length} plural forms are expected (${localePluralForms.pluralForms}), but the message contains ${parts.length}: ${pluralMessages}`, `For locale=${localePluralForms.locale}, a maximum of ${
localePluralForms.pluralForms.length
} plural forms are expected (${localePluralForms.pluralForms.join(
',',
)}), but the message contains ${parts.length}: ${pluralMessages}`,
); );
} }
const pluralForm = localePluralForms.select(count); const pluralForm = localePluralForms.select(count);

View file

@ -15,7 +15,7 @@ export const DEFAULT_CONFIG = {
playgroundPosition: 'bottom', playgroundPosition: 'bottom',
}; };
export const Schema = Joi.object({ export const Schema = Joi.object<ThemeConfig>({
liveCodeBlock: Joi.object({ liveCodeBlock: Joi.object({
playgroundPosition: Joi.string() playgroundPosition: Joi.string()
.equal('top', 'bottom') .equal('top', 'bottom')

View file

@ -60,7 +60,7 @@ export default function themeSearchAlgolia(context: LoadContext): Plugin<void> {
}); });
}, },
async contentLoaded({actions: {addRoute}}) { contentLoaded({actions: {addRoute}}) {
if (searchPagePath) { if (searchPagePath) {
addRoute({ addRoute({
path: normalizeUrl([baseUrl, searchPagePath]), path: normalizeUrl([baseUrl, searchPagePath]),

View file

@ -20,7 +20,7 @@ export const DEFAULT_CONFIG = {
searchPagePath: 'search', searchPagePath: 'search',
}; };
export const Schema = Joi.object({ export const Schema = Joi.object<ThemeConfig>({
algolia: Joi.object({ algolia: Joi.object({
// Docusaurus attributes // Docusaurus attributes
contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch), contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch),

View file

@ -25,9 +25,9 @@ export function codeTranslationLocalesToTry(locale: string): string[] {
// unresolved except for simply locales // unresolved except for simply locales
locale, locale,
// "zh-CN" / "pt-BR" // "zh-CN" / "pt-BR"
`${maximizedLocale.language}-${maximizedLocale.region}`, `${maximizedLocale.language!}-${maximizedLocale.region!}`,
// "zh-Hans" / "pt-Latn" // "zh-Hans" / "pt-Latn"
`${maximizedLocale.language}-${maximizedLocale.script}`, `${maximizedLocale.language!}-${maximizedLocale.script!}`,
// "zh" / "pt" // "zh" / "pt"
maximizedLocale.language!, maximizedLocale.language!,
]; ];

View file

@ -28,4 +28,4 @@ const JoiFrontMatterString: Joi.Extension = {
* @see https://github.com/facebook/docusaurus/issues/4642 * @see https://github.com/facebook/docusaurus/issues/4642
* @see https://github.com/sideway/joi/issues/1442#issuecomment-823997884 * @see https://github.com/sideway/joi/issues/1442#issuecomment-823997884
*/ */
export const JoiFrontMatter: typeof Joi = Joi.extend(JoiFrontMatterString); export const JoiFrontMatter = Joi.extend(JoiFrontMatterString) as typeof Joi;

View file

@ -56,7 +56,7 @@ export function shortName(str: string): string {
* Adopted from https://github.com/sindresorhus/slash/blob/main/index.js * Adopted from https://github.com/sindresorhus/slash/blob/main/index.js
*/ */
export function posixPath(str: string): string { export function posixPath(str: string): string {
const isExtendedLengthPath = /^\\\\\?\\/.test(str); const isExtendedLengthPath = str.startsWith('\\\\?\\');
// Forward slashes are only valid Windows paths when they don't contain non- // Forward slashes are only valid Windows paths when they don't contain non-
// ascii characters. // ascii characters.

View file

@ -72,11 +72,11 @@ export function normalizeUrl(rawUrls: string[]): string {
/^\/+/, /^\/+/,
// Special case where the first element of rawUrls is empty // Special case where the first element of rawUrls is empty
// ["", "/hello"] => /hello // ["", "/hello"] => /hello
component[0] === '/' && !hasStartingSlash ? '/' : '', component.startsWith('/') && !hasStartingSlash ? '/' : '',
); );
} }
hasEndingSlash = component[component.length - 1] === '/'; hasEndingSlash = component.endsWith('/');
// Removing the ending slashes for each component but the last. For the // Removing the ending slashes for each component but the last. For the
// last component we will combine multiple slashes to a single one. // last component we will combine multiple slashes to a single one.
component = component.replace(/\/+$/, i < urls.length - 1 ? '' : '/'); component = component.replace(/\/+$/, i < urls.length - 1 ? '' : '/');
@ -95,7 +95,7 @@ export function normalizeUrl(rawUrls: string[]): string {
// Replace ? in parameters with &. // Replace ? in parameters with &.
const parts = str.split('?'); const parts = str.split('?');
str = parts.shift() + (parts.length > 0 ? '?' : '') + parts.join('&'); str = parts.shift()! + (parts.length > 0 ? '?' : '') + parts.join('&');
// Dedupe forward slashes in the entire path, avoiding protocol slashes. // Dedupe forward slashes in the entire path, avoiding protocol slashes.
str = str.replace(/(?<textBefore>[^:/]\/)\/+/g, '$1'); str = str.replace(/(?<textBefore>[^:/]\/)\/+/g, '$1');

View file

@ -19,7 +19,7 @@ declare global {
// eslint-disable-next-line camelcase, no-underscore-dangle // eslint-disable-next-line camelcase, no-underscore-dangle
const __webpack_require__: {gca: (name: string) => string}; const __webpack_require__: {gca: (name: string) => string};
interface Navigator { interface Navigator {
connection: {effectiveType: string; saveData: boolean}; connection?: {effectiveType: string; saveData: boolean};
} }
} }

View file

@ -10,7 +10,7 @@ import {hasProtocol} from './isInternalUrl';
import type {BaseUrlOptions, BaseUrlUtils} from '@docusaurus/useBaseUrl'; import type {BaseUrlOptions, BaseUrlUtils} from '@docusaurus/useBaseUrl';
function addBaseUrl( function addBaseUrl(
siteUrl: string | undefined, siteUrl: string,
baseUrl: string, baseUrl: string,
url: string, url: string,
{forcePrependBaseUrl = false, absolute = false}: BaseUrlOptions = {}, {forcePrependBaseUrl = false, absolute = false}: BaseUrlOptions = {},

View file

@ -66,7 +66,7 @@ export async function serve(
// Remove baseUrl before calling serveHandler, because /baseUrl/ should // Remove baseUrl before calling serveHandler, because /baseUrl/ should
// serve /build/index.html, not /build/baseUrl/index.html (does not exist) // serve /build/index.html, not /build/baseUrl/index.html (does not exist)
req.url = req.url?.replace(baseUrl, '/'); req.url = req.url.replace(baseUrl, '/');
serveHandler(req, res, { serveHandler(req, res, {
cleanUrls: true, cleanUrls: true,

View file

@ -9,14 +9,14 @@ import {normalizeSwizzleConfig} from '../config';
import type {SwizzleConfig} from '@docusaurus/types'; import type {SwizzleConfig} from '@docusaurus/types';
describe('normalizeSwizzleConfig', () => { describe('normalizeSwizzleConfig', () => {
it(`validate no components config`, async () => { it(`validate no components config`, () => {
const config: SwizzleConfig = { const config: SwizzleConfig = {
components: {}, components: {},
}; };
expect(normalizeSwizzleConfig(config)).toEqual(config); expect(normalizeSwizzleConfig(config)).toEqual(config);
}); });
it(`validate complete config`, async () => { it(`validate complete config`, () => {
const config: SwizzleConfig = { const config: SwizzleConfig = {
components: { components: {
SomeComponent: { SomeComponent: {
@ -38,7 +38,7 @@ describe('normalizeSwizzleConfig', () => {
expect(normalizeSwizzleConfig(config)).toEqual(config); expect(normalizeSwizzleConfig(config)).toEqual(config);
}); });
it(`normalize partial config`, async () => { it(`normalize partial config`, () => {
const config: SwizzleConfig = { const config: SwizzleConfig = {
components: { components: {
SomeComponent: { SomeComponent: {
@ -59,7 +59,7 @@ describe('normalizeSwizzleConfig', () => {
expect(normalizeSwizzleConfig(config)).toMatchSnapshot(); expect(normalizeSwizzleConfig(config)).toMatchSnapshot();
}); });
it(`reject missing components`, async () => { it(`reject missing components`, () => {
// @ts-expect-error: incomplete actions map // @ts-expect-error: incomplete actions map
const config: SwizzleConfig = {}; const config: SwizzleConfig = {};
@ -70,7 +70,7 @@ describe('normalizeSwizzleConfig', () => {
); );
}); });
it(`reject invalid action name`, async () => { it(`reject invalid action name`, () => {
const config: SwizzleConfig = { const config: SwizzleConfig = {
components: { components: {
MyComponent: { MyComponent: {
@ -91,7 +91,7 @@ describe('normalizeSwizzleConfig', () => {
); );
}); });
it(`reject invalid action status`, async () => { it(`reject invalid action status`, () => {
const config: SwizzleConfig = { const config: SwizzleConfig = {
components: { components: {
MyComponent: { MyComponent: {

View file

@ -109,7 +109,7 @@ export async function wrap({
const isDirectory = await isDir(path.join(themePath, themeComponentName)); const isDirectory = await isDir(path.join(themePath, themeComponentName));
// Top/Parent/ComponentName => ComponentName // Top/Parent/ComponentName => ComponentName
const componentName = _.last(themeComponentName.split('/')); const componentName = _.last(themeComponentName.split('/'))!;
const wrapperComponentName = `${componentName}Wrapper`; const wrapperComponentName = `${componentName}Wrapper`;
const wrapperFileName = `${themeComponentName}${isDirectory ? '/index' : ''}${ const wrapperFileName = `${themeComponentName}${isDirectory ? '/index' : ''}${

View file

@ -16,9 +16,8 @@ function getModuleSwizzleConfig(
swizzlePlugin: SwizzlePlugin, swizzlePlugin: SwizzlePlugin,
): SwizzleConfig | undefined { ): SwizzleConfig | undefined {
const getSwizzleConfig = const getSwizzleConfig =
swizzlePlugin.plugin.plugin?.getSwizzleConfig ?? swizzlePlugin.plugin.plugin.getSwizzleConfig ??
swizzlePlugin.plugin.pluginModule?.module.getSwizzleConfig ?? swizzlePlugin.plugin.pluginModule?.module.getSwizzleConfig;
swizzlePlugin.plugin.pluginModule?.module?.getSwizzleConfig;
if (getSwizzleConfig) { if (getSwizzleConfig) {
return getSwizzleConfig(); return getSwizzleConfig();
@ -26,9 +25,8 @@ function getModuleSwizzleConfig(
// TODO deprecate getSwizzleComponentList later // TODO deprecate getSwizzleComponentList later
const getSwizzleComponentList = const getSwizzleComponentList =
swizzlePlugin.plugin.plugin?.getSwizzleComponentList ?? swizzlePlugin.plugin.plugin.getSwizzleComponentList ??
swizzlePlugin.plugin.pluginModule?.module.getSwizzleComponentList ?? swizzlePlugin.plugin.pluginModule?.module.getSwizzleComponentList;
swizzlePlugin.plugin.pluginModule?.module?.getSwizzleComponentList;
if (getSwizzleComponentList) { if (getSwizzleComponentList) {
const safeComponents = getSwizzleComponentList() ?? []; const safeComponents = getSwizzleComponentList() ?? [];

View file

@ -15,7 +15,7 @@ import {findStringIgnoringCase, type SwizzlePlugin} from './common';
export function pluginToThemeName(plugin: SwizzlePlugin): string | undefined { export function pluginToThemeName(plugin: SwizzlePlugin): string | undefined {
if (plugin.instance.getThemePath) { if (plugin.instance.getThemePath) {
return ( return (
(plugin.instance.version as {name: string}).name ?? plugin.instance.name (plugin.instance.version as {name?: string}).name ?? plugin.instance.name
); );
} }
return undefined; return undefined;
@ -76,9 +76,7 @@ function handleInvalidThemeName({
// TODO recover from short theme-names here: "classic" => "@docusaurus/theme-classic" // TODO recover from short theme-names here: "classic" => "@docusaurus/theme-classic"
// No recovery value is possible: print error // No recovery value is possible: print error
const suggestion = themeNames.find( const suggestion = themeNames.find((name) => leven(name, themeNameParam) < 4);
(name) => leven(name, themeNameParam!) < 4,
);
logger.error`Theme name=${themeNameParam} not found. ${ logger.error`Theme name=${themeNameParam} not found. ${
suggestion suggestion
? logger.interpolate`Did you mean name=${suggestion}?` ? logger.interpolate`Did you mean name=${suggestion}?`
@ -87,13 +85,13 @@ function handleInvalidThemeName({
return process.exit(1); return process.exit(1);
} }
async function validateThemeName({ function validateThemeName({
themeNameParam, themeNameParam,
themeNames, themeNames,
}: { }: {
themeNameParam: string; themeNameParam: string;
themeNames: string[]; themeNames: string[];
}): Promise<string> { }): string {
const isValidName = themeNames.includes(themeNameParam); const isValidName = themeNames.includes(themeNameParam);
if (!isValidName) { if (!isValidName) {
return handleInvalidThemeName({ return handleInvalidThemeName({

View file

@ -37,12 +37,12 @@ async function transformMarkdownFile(
async function getPathsToWatch(siteDir: string): Promise<string[]> { async function getPathsToWatch(siteDir: string): Promise<string[]> {
const context = await loadContext({siteDir}); const context = await loadContext({siteDir});
const plugins = await initPlugins(context); const plugins = await initPlugins(context);
return plugins.flatMap((plugin) => plugin?.getPathsToWatch?.() ?? []); return plugins.flatMap((plugin) => plugin.getPathsToWatch?.() ?? []);
} }
export async function writeHeadingIds( export async function writeHeadingIds(
siteDir: string, siteDir: string,
files: string[], files: string[] | undefined,
options: WriteHeadingIDOptions, options: WriteHeadingIDOptions,
): Promise<void> { ): Promise<void> {
const markdownFiles = await safeGlobby( const markdownFiles = await safeGlobby(

View file

@ -20,13 +20,13 @@ declare module 'react-loadable-ssr-addon-v5-slorber' {
export type Manifest = { export type Manifest = {
entrypoints: string[]; entrypoints: string[];
origins: {[key: string]: number[]}; origins: {[key: string]: number[]};
assets: Array<{[key: string]: Asset[]}>; assets: {[key: string]: Asset[]}[];
}; };
export function getBundles( export function getBundles(
manifest: Manifest, manifest: Manifest,
modulesToBeLoaded: string[], modulesToBeLoaded: string[],
): {js: Asset[]; css: Asset[]}; ): {js?: Asset[]; css?: Asset[]};
export default class ReactLoadableSSRAddon implements WebpackPluginInstance { export default class ReactLoadableSSRAddon implements WebpackPluginInstance {
constructor(props: {filename: string}); constructor(props: {filename: string});

View file

@ -63,7 +63,7 @@ async function normalizePluginConfig(
const pluginPath = pluginRequire.resolve(pluginModuleImport); const pluginPath = pluginRequire.resolve(pluginModuleImport);
const pluginModule = importFresh<ImportedPluginModule>(pluginPath); const pluginModule = importFresh<ImportedPluginModule>(pluginPath);
return { return {
plugin: pluginModule?.default ?? pluginModule, plugin: pluginModule.default ?? pluginModule,
options: {}, options: {},
pluginModule: { pluginModule: {
path: pluginModuleImport, path: pluginModuleImport,
@ -90,7 +90,7 @@ async function normalizePluginConfig(
const pluginPath = pluginRequire.resolve(pluginModuleImport); const pluginPath = pluginRequire.resolve(pluginModuleImport);
const pluginModule = importFresh<ImportedPluginModule>(pluginPath); const pluginModule = importFresh<ImportedPluginModule>(pluginPath);
return { return {
plugin: pluginModule?.default ?? pluginModule, plugin: pluginModule.default ?? pluginModule,
options: pluginConfig[1], options: pluginConfig[1],
pluginModule: { pluginModule: {
path: pluginModuleImport, path: pluginModuleImport,

View file

@ -52,7 +52,7 @@ export async function loadPlugins(context: LoadContext): Promise<{
plugins.map(async (plugin) => { plugins.map(async (plugin) => {
const content = await plugin.loadContent?.(); const content = await plugin.loadContent?.();
const rawTranslationFiles = const rawTranslationFiles =
(await plugin?.getTranslationFiles?.({content})) ?? []; (await plugin.getTranslationFiles?.({content})) ?? [];
const translationFiles = await Promise.all( const translationFiles = await Promise.all(
rawTranslationFiles.map((translationFile) => rawTranslationFiles.map((translationFile) =>
localizePluginTranslationFile({ localizePluginTranslationFile({

View file

@ -29,8 +29,8 @@ function getOptionValidationFunction(
if (normalizedPluginConfig.pluginModule) { if (normalizedPluginConfig.pluginModule) {
// Support both CommonJS and ES modules // Support both CommonJS and ES modules
return ( return (
normalizedPluginConfig.pluginModule.module?.default?.validateOptions ?? normalizedPluginConfig.pluginModule.module.default?.validateOptions ??
normalizedPluginConfig.pluginModule.module?.validateOptions normalizedPluginConfig.pluginModule.module.validateOptions
); );
} }
return normalizedPluginConfig.plugin.validateOptions; return normalizedPluginConfig.plugin.validateOptions;
@ -66,7 +66,7 @@ export async function initPlugins(
): Promise<PluginVersionInformation> { ): Promise<PluginVersionInformation> {
if (normalizedPluginConfig.pluginModule?.path) { if (normalizedPluginConfig.pluginModule?.path) {
const pluginPath = pluginRequire.resolve( const pluginPath = pluginRequire.resolve(
normalizedPluginConfig.pluginModule?.path, normalizedPluginConfig.pluginModule.path,
); );
return getPluginVersion(pluginPath, context.siteDir); return getPluginVersion(pluginPath, context.siteDir);
} }

View file

@ -14,7 +14,10 @@ export function getNamePatterns(
if (!moduleName.includes('/')) { if (!moduleName.includes('/')) {
return [`${moduleName}/docusaurus-${moduleType}`]; return [`${moduleName}/docusaurus-${moduleType}`];
} }
const [scope, packageName] = moduleName.split(/\/(?<rest>.*)/); const [scope, packageName] = moduleName.split(/\/(?<rest>.*)/) as [
string,
string,
];
return [ return [
`${scope}/${packageName}`, `${scope}/${packageName}`,
`${scope}/docusaurus-${moduleType}-${packageName}`, `${scope}/docusaurus-${moduleType}-${packageName}`,

View file

@ -50,8 +50,8 @@ export function sortConfig(
} }
// Higher priority get placed first. // Higher priority get placed first.
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 = priorityB - priorityA; const score = priorityB - priorityA;
if (score !== 0) { if (score !== 0) {

View file

@ -89,7 +89,7 @@ export function createMDXFallbackPlugin({
// processed by content plugins mdx loaders. This works, but a bit // processed by content plugins mdx loaders. This works, but a bit
// hacky... Not sure there's a way to handle that differently in webpack // hacky... Not sure there's a way to handle that differently in webpack
function getMDXFallbackExcludedPaths(): string[] { function getMDXFallbackExcludedPaths(): string[] {
const rules: RuleSetRule[] = config?.module?.rules as RuleSetRule[]; const rules: RuleSetRule[] = config.module?.rules as RuleSetRule[];
return rules.flatMap((rule) => { return rules.flatMap((rule) => {
const isMDXRule = const isMDXRule =
rule.test instanceof RegExp && rule.test.test('x.mdx'); rule.test instanceof RegExp && rule.test.test('x.mdx');

View file

@ -152,7 +152,7 @@ const isModule = (value: unknown): value is Module =>
typeof value === 'string' || typeof value === 'string' ||
(typeof value === 'object' && (typeof value === 'object' &&
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
!!(value as {[key: string]: unknown})?.__import); !!(value as {[key: string]: unknown} | null)?.__import);
/** /**
* Takes a {@link Module} (which is nothing more than a path plus some metadata * Takes a {@link Module} (which is nothing more than a path plus some metadata

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