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,17 +70,15 @@ describe('packages', () => {
packageJsonFile.content.name?.startsWith('@'),
)
.forEach((packageJsonFile) => {
if (packageJsonFile) {
// Unfortunately jest custom message do not exist in loops,
// so using an exception instead to show failing package file
// (see https://github.com/facebook/jest/issues/3293)
// expect(packageJsonFile.content.publishConfig?.access)
// .toEqual('public');
if (packageJsonFile.content.publishConfig?.access !== 'public') {
throw new Error(
`Package ${packageJsonFile.file} does not have publishConfig.access: 'public'`,
);
}
// Unfortunately jest custom message do not exist in loops,
// so using an exception instead to show failing package file
// (see https://github.com/facebook/jest/issues/3293)
// expect(packageJsonFile.content.publishConfig?.access)
// .toEqual('public');
if (packageJsonFile.content.publishConfig?.access !== 'public') {
throw new Error(
`Package ${packageJsonFile.file} does not have publishConfig.access: 'public'`,
);
}
});
});

View file

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

View file

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

View file

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

View file

@ -9,10 +9,10 @@ import chalk from 'chalk';
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 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 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. */
readonly metadata: Metadata;
/** A list of TOC items (headings). */
readonly toc: readonly TOCItem[];
readonly toc?: readonly TOCItem[];
/** First h1 title before any content. */
readonly contentTitle: string | undefined;
/**

View file

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

View file

@ -17,7 +17,7 @@ export default function plugin(): Transformer {
return (root) => {
const slugs = createSlugger();
visit(root, 'heading', (headingNode: Heading) => {
const data = headingNode.data || (headingNode.data = {});
const data = headingNode.data ?? (headingNode.data = {});
const properties = (data.hProperties || (data.hProperties = {})) as {
id: string;
};
@ -36,7 +36,7 @@ export default function plugin(): Transformer {
// Support explicit heading IDs
const parsedHeading = parseMarkdownHeadingId(heading);
id = parsedHeading.id || slugs.slug(heading);
id = parsedHeading.id ?? slugs.slug(heading);
if (parsedHeading.id) {
// 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) {
// Try to improve error feedback
// see https://github.com/facebook/docusaurus/issues/3309#issuecomment-690371675
const title = node.title || (node.children[0] as Literal)?.value || '?';
const line = node?.position?.start?.line || '?';
const title =
node.title ?? (node.children[0] as Literal | undefined)?.value ?? '?';
const line = node.position?.start.line ?? '?';
throw new Error(
`Markdown link URL is mandatory in "${toMessageRelativeFilePath(
context.filePath,

View file

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

View file

@ -19,22 +19,22 @@ async function testMigration(siteDir: string, newDir: string) {
await migrateDocusaurusProject(siteDir, newDir, true, true);
expect(
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');
expect(
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');
expect(
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');
expect(
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');
writeMock.mockRestore();

View file

@ -40,12 +40,7 @@ export default function extractMetadata(content: string): Data {
lines.slice(0, -1).forEach((line) => {
const keyValue = line.split(':') as [string, ...string[]];
const key = keyValue[0].trim();
let 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.
}
const value = keyValue.slice(1).join(':').trim();
metadata[key] = value;
});
return {metadata, rawContent: both.content};

View file

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

View file

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

View file

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

View file

@ -17,7 +17,7 @@ import writeRedirectFiles, {
// - https://github.com/facebook/docusaurus/issues/3886
// - https://github.com/facebook/docusaurus/issues/3925
describe('createToUrl', () => {
it('creates appropriate redirect urls', async () => {
it('creates appropriate redirect urls', () => {
expect(createToUrl('/', '/docs/something/else')).toBe(
'/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(
'/baseUrl/docs/something/else',
);
@ -43,7 +43,7 @@ describe('createToUrl', () => {
});
describe('toRedirectFilesMetadata', () => {
it('creates appropriate metadata trailingSlash=undefined', async () => {
it('creates appropriate metadata trailingSlash=undefined', () => {
const pluginContext = {
outDir: '/tmp/someFixedOutDir',
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 = {
outDir: '/tmp/someFixedOutDir',
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 = {
outDir: '/tmp/someFixedOutDir',
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 = {
outDir: '/tmp/someFixedOutDir',
baseUrl: '/',
@ -142,7 +142,7 @@ describe('toRedirectFilesMetadata', () => {
);
});
it('creates appropriate metadata for empty baseUrl', async () => {
it('creates appropriate metadata for empty baseUrl', () => {
const pluginContext = {
outDir: '/tmp/someFixedOutDir',
baseUrl: '',

View file

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

View file

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

View file

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

View file

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

View file

@ -44,7 +44,11 @@ const AuthorsMapSchema = Joi.object<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: {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -126,6 +126,6 @@ export function getDocVersionSuggestions(
const latestVersion = getLatestVersion(data);
const activeDocContext = getActiveDocContext(data, pathname);
const latestDocSuggestion: GlobalDoc | undefined =
activeDocContext?.alternateDocVersions[latestVersion.name];
activeDocContext.alternateDocVersions[latestVersion.name];
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-
// safe fallback when the docs plugin is not in use
export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} =>
(useAllPluginInstancesData('docusaurus-plugin-content-docs') as {
[pluginId: string]: GlobalPluginData;
}) ?? StableEmptyObject;
(useAllPluginInstancesData('docusaurus-plugin-content-docs') as
| {
[pluginId: string]: GlobalPluginData;
}
| undefined) ?? StableEmptyObject;
export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
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({
docFile,
versionMetadata,

View file

@ -16,5 +16,5 @@ export default function markdownLoader(
const fileString = source;
const callback = this.async();
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) {
normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -144,7 +144,7 @@ function validateSidebarItem(
// manually
Joi.assert(item, sidebarItemSchema);
if ((item as NormalizedSidebarItemCategory).type === 'category') {
if ((item as NormalizedSidebarItem).type === 'category') {
(item as NormalizedSidebarItemCategory).items.forEach(validateSidebarItem);
}
}
@ -170,5 +170,9 @@ const categoryMetadataFileSchema = Joi.object<CategoryMetadataFile>({
export function validateCategoryMetadataFile(
unsafeContent: unknown,
): 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;
}
const baseSlug = frontMatterSlug || baseID;
const baseSlug = frontMatterSlug ?? baseID;
return resolvePathname(baseSlug, getDirNameSlug());
}

View file

@ -604,7 +604,7 @@ describe('readVersionsMetadata', () => {
context: defaultContext,
}),
).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(() => [' ']);

View file

@ -11,7 +11,9 @@ import type {VersionsOptions} from '@docusaurus/plugin-content-docs';
export function validateVersionName(name: unknown): asserts name is string {
if (typeof name !== 'string') {
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()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,9 +22,9 @@ function bytesToSize(bytes: number) {
}
const scale = Math.floor(Math.log(bytes) / Math.log(1024));
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
@ -100,8 +100,8 @@ export default function IdealImage(props: Props): JSX.Element {
{...props}
alt={alt}
className={className}
height={img.src.height || 100}
width={img.src.width || 100}
height={img.src.height ?? 100}
width={img.src.width ?? 100}
placeholder={{lqip: img.preSrc}}
src={img.src.src}
srcSet={img.src.images.map((image) => ({

View file

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

View file

@ -23,7 +23,7 @@ const DEFAULT_OPTIONS = {
reloadPopup: '@theme/PwaReloadPopup',
};
const Schema = Joi.object({
const optionsSchema = Joi.object<PluginOptions>({
debug: Joi.bool().default(DEFAULT_OPTIONS.debug),
offlineModeActivationStrategies: Joi.array()
.items(
@ -58,5 +58,5 @@ export function validateOptions({
validate,
options,
}: 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)
/* 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_OFFLINE_MODE_ACTIVATION_STRATEGIES = process.env
.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES as unknown as (keyof typeof OfflineModeActivationStrategiesImplementations)[];
@ -34,7 +34,7 @@ async function clearRegistrations() {
}
await Promise.all(
registrations.map(async (registration) => {
const result = await registration?.unregister();
const result = await registration.unregister();
if (debug) {
console.log(
`[Docusaurus-PWA][registerSw]: unregister() service worker registration`,
@ -69,7 +69,7 @@ async function isAppInstalledEventFired() {
declare global {
interface Navigator {
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: [],
};
const PluginOptionSchema = Joi.object({
const PluginOptionSchema = Joi.object<PluginOptions>({
// @ts-expect-error: forbidden
cacheTime: Joi.forbidden().messages({
'any.unknown':
'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 {sync = false} = options;
return (root) => {
let transformed = false;
let alreadyImported = false;
let transformed = false as boolean;
let alreadyImported = false as boolean;
visit(root, (node: Node) => {
if (isImport(node) && node.value.includes('@theme/Tabs')) {
alreadyImported = true;

View file

@ -177,7 +177,7 @@ export default function themeClassic(
const plugin: PostCssPlugin = {
postcssPlugin: 'RtlCssPlugin',
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.
if (file === resolvedInfimaFile) {
return {};

View file

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

View file

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

View file

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

View file

@ -21,7 +21,7 @@ function ColumnLinkItem({item}: {item: ColumnItemType}) {
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} />
</li>
);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -39,7 +39,7 @@ export function useContextualSearchFilters(): {locale: string; tags: string[]} {
// plugin instances.
function getDocPluginTags(pluginId: string) {
const activeVersion =
activePluginAndVersion?.activePlugin?.pluginId === pluginId
activePluginAndVersion?.activePlugin.pluginId === pluginId
? activePluginAndVersion.activeVersion
: 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.
// e.g. if an ### H3 was last seen at index 2, then prevIndexForLevel[3] === 2
// indices 0 and 1 will remain unused.
const prevIndexForLevel = Array(7).fill(-1);
const prevIndexForLevel = Array<number>(7).fill(-1);
headings.forEach((curr, currIndex) => {
// 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) {
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);

View file

@ -15,7 +15,7 @@ export const DEFAULT_CONFIG = {
playgroundPosition: 'bottom',
};
export const Schema = Joi.object({
export const Schema = Joi.object<ThemeConfig>({
liveCodeBlock: Joi.object({
playgroundPosition: Joi.string()
.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) {
addRoute({
path: normalizeUrl([baseUrl, searchPagePath]),

View file

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

View file

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

View file

@ -28,4 +28,4 @@ const JoiFrontMatterString: Joi.Extension = {
* @see https://github.com/facebook/docusaurus/issues/4642
* @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
*/
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-
// ascii characters.

View file

@ -72,11 +72,11 @@ export function normalizeUrl(rawUrls: string[]): string {
/^\/+/,
// Special case where the first element of rawUrls is empty
// ["", "/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
// last component we will combine multiple slashes to a single one.
component = component.replace(/\/+$/, i < urls.length - 1 ? '' : '/');
@ -95,7 +95,7 @@ export function normalizeUrl(rawUrls: string[]): string {
// Replace ? in parameters with &.
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.
str = str.replace(/(?<textBefore>[^:/]\/)\/+/g, '$1');

View file

@ -19,7 +19,7 @@ declare global {
// eslint-disable-next-line camelcase, no-underscore-dangle
const __webpack_require__: {gca: (name: string) => string};
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';
function addBaseUrl(
siteUrl: string | undefined,
siteUrl: string,
baseUrl: string,
url: string,
{forcePrependBaseUrl = false, absolute = false}: BaseUrlOptions = {},

View file

@ -66,7 +66,7 @@ export async function serve(
// Remove baseUrl before calling serveHandler, because /baseUrl/ should
// 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, {
cleanUrls: true,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -50,8 +50,8 @@ export function sortConfig(
}
// Higher priority get placed first.
if (a.priority || b.priority) {
const priorityA = a.priority || 0;
const priorityB = b.priority || 0;
const priorityA = a.priority ?? 0;
const priorityB = b.priority ?? 0;
const score = priorityB - priorityA;
if (score !== 0) {

View file

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

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