diff --git a/packages/docusaurus-plugin-client-redirects/src/__tests__/options.test.ts b/packages/docusaurus-plugin-client-redirects/src/__tests__/options.test.ts index 8b7fb91423..b5d9e24c36 100644 --- a/packages/docusaurus-plugin-client-redirects/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-client-redirects/src/__tests__/options.test.ts @@ -7,10 +7,7 @@ import {validateOptions, DEFAULT_OPTIONS} from '../options'; import {normalizePluginOptions} from '@docusaurus/utils-validation'; -import type { - CreateRedirectsFnOption, - Options, -} from '@docusaurus/plugin-client-redirects'; +import type {Options} from '@docusaurus/plugin-client-redirects'; function testValidate(options: Options) { return validateOptions({validate: normalizePluginOptions, options}); @@ -34,7 +31,9 @@ describe('normalizePluginOptions', () => { }); it('overrides all default options with valid user options', () => { - const createRedirects: CreateRedirectsFnOption = (_routePath: string) => []; + const createRedirects: Options['createRedirects'] = ( + _routePath: string, + ) => []; expect( testValidate({ fromExtensions: ['exe', 'zip'], @@ -54,7 +53,8 @@ describe('normalizePluginOptions', () => { it('rejects bad fromExtensions user inputs', () => { expect(() => testValidate({ - fromExtensions: [null, undefined, 123, true] as unknown as string[], + // @ts-expect-error: for test + fromExtensions: [null, undefined, 123, true], }), ).toThrowErrorMatchingSnapshot(); }); @@ -62,7 +62,8 @@ describe('normalizePluginOptions', () => { it('rejects bad toExtensions user inputs', () => { expect(() => testValidate({ - toExtensions: [null, undefined, 123, true] as unknown as string[], + // @ts-expect-error: for test + toExtensions: [null, undefined, 123, true], }), ).toThrowErrorMatchingSnapshot(); }); @@ -70,7 +71,8 @@ describe('normalizePluginOptions', () => { it('rejects bad createRedirects user inputs', () => { expect(() => testValidate({ - createRedirects: ['bad', 'value'] as unknown as CreateRedirectsFnOption, + // @ts-expect-error: for test + createRedirects: ['bad', 'value'], }), ).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/docusaurus-plugin-client-redirects/src/createRedirectPageContent.ts b/packages/docusaurus-plugin-client-redirects/src/createRedirectPageContent.ts index 68d2c5a40d..557ea9c381 100644 --- a/packages/docusaurus-plugin-client-redirects/src/createRedirectPageContent.ts +++ b/packages/docusaurus-plugin-client-redirects/src/createRedirectPageContent.ts @@ -9,10 +9,6 @@ import * as eta from 'eta'; import redirectPageTemplate from './templates/redirectPage.template.html'; import _ from 'lodash'; -type CreateRedirectPageOptions = { - toUrl: string; -}; - const getCompiledRedirectPageTemplate = _.memoize(() => eta.compile(redirectPageTemplate.trim()), ); @@ -24,7 +20,9 @@ function renderRedirectPageTemplate(data: Record) { export default function createRedirectPageContent({ toUrl, -}: CreateRedirectPageOptions): string { +}: { + toUrl: string; +}): string { return renderRedirectPageTemplate({ toUrl: encodeURI(toUrl), }); diff --git a/packages/docusaurus-plugin-client-redirects/src/plugin-client-redirects.d.ts b/packages/docusaurus-plugin-client-redirects/src/plugin-client-redirects.d.ts index fb580bae6a..a943eace5e 100644 --- a/packages/docusaurus-plugin-client-redirects/src/plugin-client-redirects.d.ts +++ b/packages/docusaurus-plugin-client-redirects/src/plugin-client-redirects.d.ts @@ -10,18 +10,23 @@ export type RedirectOption = { from: string | string[]; }; -// For a given existing route path, -// return all the paths from which we should redirect from -export type CreateRedirectsFnOption = ( - path: string, -) => string[] | string | null | undefined; - export type PluginOptions = { + /** Plugin ID. */ id: string; + /** The extensions to be removed from the route after redirecting. */ fromExtensions: string[]; + /** The extensions to be appended to the route after redirecting. */ toExtensions: string[]; + /** The list of redirect rules, each one with multiple `from`s → one `to`. */ redirects: RedirectOption[]; - createRedirects?: CreateRedirectsFnOption; + /** + * A callback to create a redirect rule. + * @returns All the paths from which we should redirect to `path` + */ + createRedirects?: ( + /** An existing Docusaurus route path */ + path: string, + ) => string[] | string | null | undefined; }; export type Options = Partial; diff --git a/packages/docusaurus-plugin-client-redirects/src/types.ts b/packages/docusaurus-plugin-client-redirects/src/types.ts index 2affd0c6c5..ca0f61a6fb 100644 --- a/packages/docusaurus-plugin-client-redirects/src/types.ts +++ b/packages/docusaurus-plugin-client-redirects/src/types.ts @@ -8,16 +8,22 @@ import type {Props} from '@docusaurus/types'; import type {PluginOptions} from '@docusaurus/plugin-client-redirects'; -// The minimal infos the plugin needs to work +/** + * The minimal infos the plugin needs to work + */ export type PluginContext = Pick & { options: PluginOptions; relativeRoutesPaths: string[]; }; -// In-memory representation of redirects we want: easier to test -// /!\ easy to be confused: "from" is the new page we should create, -// that redirects to "to": the existing Docusaurus page +/** + * In-memory representation of redirects we want: easier to test + * /!\ easy to be confused: "from" is the new page we should create, + * that redirects to "to": the existing Docusaurus page + */ export type RedirectMetadata = { - from: string; // pathname - to: string; // pathname + /** Pathname of the new page we should create */ + from: string; + /** Pathname of an existing Docusaurus page */ + to: string; }; diff --git a/packages/docusaurus-plugin-ideal-image/src/plugin-ideal-image.d.ts b/packages/docusaurus-plugin-ideal-image/src/plugin-ideal-image.d.ts index 1435d0b1d4..0c151f88cb 100644 --- a/packages/docusaurus-plugin-ideal-image/src/plugin-ideal-image.d.ts +++ b/packages/docusaurus-plugin-ideal-image/src/plugin-ideal-image.d.ts @@ -14,8 +14,7 @@ declare module '@docusaurus/plugin-ideal-image' { /** * Specify all widths you want to use; if a specified size exceeds the * original image's width, the latter will be used (i.e. images won't be - * scaled up). You may also declare a default sizes array in the loader - * options in your webpack.config.js. + * scaled up). */ sizes?: number[]; /** @@ -25,16 +24,17 @@ declare module '@docusaurus/plugin-ideal-image' { */ size?: number; /** - * As an alternative to manually specifying sizes, you can specify min, max - * and steps, and the sizes will be generated for you. + * As an alternative to manually specifying `sizes`, you can specify `min`, + * `max` and `steps`, and the `sizes` will be generated for you. */ min?: number; /** - * See min above + * @see {@link PluginOptions.min} */ max?: number; /** - * Configure the number of images generated between min and max (inclusive) + * Configure the number of images generated between `min` and `max` + * (inclusive) */ steps?: number; /** @@ -42,7 +42,8 @@ declare module '@docusaurus/plugin-ideal-image' { */ quality?: number; /** - * Just use regular images in dev mode + * You can test ideal image behavior in dev mode by setting this to `false`. + * Tip: use network throttling in your browser to simulate slow networks. */ disableInDev?: boolean; }; diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImage/index.tsx b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImage/index.tsx index 11f7da58f8..6bc259b2b1 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImage/index.tsx +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImage/index.tsx @@ -15,7 +15,7 @@ import {translate} from '@docusaurus/Translate'; import type {Props} from '@theme/IdealImage'; // Adopted from https://github.com/endiliey/react-ideal-image/blob/master/src/components/helpers.js#L59-L65 -const bytesToSize = (bytes: number) => { +function bytesToSize(bytes: number) { const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; if (bytes === 0) { return 'n/a'; @@ -25,10 +25,10 @@ const bytesToSize = (bytes: number) => { return `${bytes} ${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 -const getMessage = (icon: IconKey, state: State) => { +function getMessage(icon: IconKey, state: State) { switch (icon) { case 'noicon': case 'loaded': @@ -78,7 +78,7 @@ const getMessage = (icon: IconKey, state: State) => { default: throw new Error(`Wrong icon: ${icon}`); } -}; +} export default function IdealImage(props: Props): JSX.Element { const {alt, className, img} = props; diff --git a/packages/docusaurus-plugin-pwa/src/index.ts b/packages/docusaurus-plugin-pwa/src/index.ts index e43af904e0..044f05c549 100644 --- a/packages/docusaurus-plugin-pwa/src/index.ts +++ b/packages/docusaurus-plugin-pwa/src/index.ts @@ -71,7 +71,7 @@ export default function pluginPWA( }, getClientModules() { - return isProd ? [swRegister] : []; + return isProd && swRegister ? [swRegister] : []; }, getDefaultCodeTranslationMessages() { diff --git a/packages/docusaurus-plugin-pwa/src/plugin-pwa.d.ts b/packages/docusaurus-plugin-pwa/src/plugin-pwa.d.ts index 94fe49d350..1e01283310 100644 --- a/packages/docusaurus-plugin-pwa/src/plugin-pwa.d.ts +++ b/packages/docusaurus-plugin-pwa/src/plugin-pwa.d.ts @@ -6,26 +6,90 @@ */ declare module '@docusaurus/plugin-pwa' { - export type pwaHead = { - tagName: string; - href?: string; - content?: string; - [attributeName: string]: string | boolean; - }; + import type {InjectManifestOptions} from 'workbox-build'; export type PluginOptions = { + /** + * Turn debug mode on: + * + * - Workbox logs + * - Additional Docusaurus logs + * - Unoptimized SW file output + * - Source maps + */ debug?: boolean; - offlineModeActivationStrategies; - injectManifestConfig; - reloadPopup; - pwaHead: pwaHead[]; - swCustom; - swRegister; + /** + * Strategies used to turn the offline mode on: + * + * - `appInstalled`: activates for users having installed the site as an app + * (not 100% reliable) + * - `standalone`: activates for users running the app as standalone (often + * the case once a PWA is installed) + * - `queryString`: activates if queryString contains `offlineMode=true` + * (convenient for PWA debugging) + * - `mobile`: activates for mobile users (width <= 940px) + * - `saveData`: activates for users with `navigator.connection.saveData === + * true` + * - `always`: activates for all users + */ + offlineModeActivationStrategies: ( + | 'appInstalled' + | 'queryString' + | 'standalone' + | 'mobile' + | 'saveData' + | 'always' + )[]; + /** + * Workbox options to pass to `workbox.injectManifest()`. This gives you + * control over which assets will be precached, and be available offline. + * @see https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.injectManifest + */ + injectManifestConfig: InjectManifestOptions; + /** + * Module path to reload popup component. This popup is rendered when a new + * service worker is waiting to be installed, and we suggest a reload to + * the user. + * + * Passing `false` will disable the popup, but this is not recommended: + * users won't have a way to get up-to-date content. + * @see {@link @theme/PwaReloadPopup} + */ + reloadPopup: string | false; + /** + * Array of objects containing `tagName` and key-value pairs for attributes + * to inject into the `` tag. Technically you can inject any head tag + * through this, but it's ideally used for tags to make your site PWA- + * compliant. + */ + pwaHead: { + tagName: string; + href?: string; + content?: string; + [attributeName: string]: string | boolean; + }[]; + /** + * Useful for additional Workbox rules. You can do whatever a service worker + * can do here, and use the full power of workbox libraries. The code is + * transpiled, so you can use modern ES6+ syntax here. + */ + swCustom?: string; + /** + * Adds an entry before the Docusaurus app so that registration can happen + * before the app runs. The default `registerSW.js` file is enough for + * simple registration. Passing `false` will disable registration entirely. + */ + swRegister: string | false; }; } declare module '@theme/PwaReloadPopup' { export interface Props { + /** + * The popup should call this callback when the `reload` button is clicked. + * This will tell the service worker to install the waiting service worker + * and reload the page. + */ readonly onReload: () => void; } export default function PwaReloadPopup(props: Props): JSX.Element; diff --git a/packages/docusaurus-plugin-sitemap/src/index.ts b/packages/docusaurus-plugin-sitemap/src/index.ts index 7533713aca..e394a9a192 100644 --- a/packages/docusaurus-plugin-sitemap/src/index.ts +++ b/packages/docusaurus-plugin-sitemap/src/index.ts @@ -19,7 +19,7 @@ import type { import {PluginOptionSchema} from './pluginOptionSchema'; export default function pluginSitemap( - _context: LoadContext, + context: LoadContext, options: Options, ): Plugin { return { diff --git a/packages/docusaurus-plugin-sitemap/src/plugin-sitemap.d.ts b/packages/docusaurus-plugin-sitemap/src/plugin-sitemap.d.ts index 0c9b4a826c..7706a0cdd2 100644 --- a/packages/docusaurus-plugin-sitemap/src/plugin-sitemap.d.ts +++ b/packages/docusaurus-plugin-sitemap/src/plugin-sitemap.d.ts @@ -8,6 +8,8 @@ import type {EnumChangefreq} from 'sitemap'; export type Options = { + /** @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions */ changefreq?: EnumChangefreq; + /** @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions */ priority?: number; }; diff --git a/packages/docusaurus-plugin-sitemap/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-sitemap/src/pluginOptionSchema.ts index 77df104d6c..7ead9b2e80 100644 --- a/packages/docusaurus-plugin-sitemap/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-sitemap/src/pluginOptionSchema.ts @@ -15,7 +15,6 @@ export const DEFAULT_OPTIONS: Required = { }; export const PluginOptionSchema = Joi.object({ - // TODO temporary (@alpha-71) cacheTime: Joi.forbidden().messages({ 'any.unknown': 'Option `cacheTime` in sitemap config is deprecated. Please remove it.',