From de36444b18c33e85b61e5fb7d7909f2825dee234 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 12 Mar 2021 23:12:21 +0100 Subject: [PATCH] fix(v2): correct plugin types --- .../src/index.ts | 8 +-- .../src/index.ts | 3 +- .../src/options.ts | 11 +--- .../src/index.ts | 8 +-- .../docusaurus-plugin-sitemap/src/index.ts | 6 +- .../docusaurus-theme-classic/src/index.ts | 10 +++- packages/docusaurus-types/package.json | 3 +- packages/docusaurus-types/src/index.d.ts | 56 +++++++++---------- .../src/validationUtils.ts | 4 +- packages/docusaurus/src/commands/swizzle.ts | 35 +++++++----- .../docusaurus/src/server/plugins/init.ts | 5 +- 11 files changed, 69 insertions(+), 80 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index f7a7cd4209e6..0aeddaaa2395 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -21,7 +21,6 @@ import { STATIC_DIR_NAME, DEFAULT_PLUGIN_ID, } from '@docusaurus/core/lib/constants'; -import {ValidationError} from 'joi'; import {flatten, take, kebabCase} from 'lodash'; import { @@ -56,7 +55,7 @@ import { export default function pluginContentBlog( context: LoadContext, options: PluginOptions, -): Plugin { +): Plugin { if (options.admonitions) { options.remarkPlugins = options.remarkPlugins.concat([ [admonitions, options.admonitions], @@ -561,10 +560,7 @@ export default function pluginContentBlog( export function validateOptions({ validate, options, -}: OptionValidationContext): ValidationResult< - PluginOptions, - ValidationError -> { +}: OptionValidationContext): ValidationResult { const validatedOptions = validate(PluginOptionSchema, options); return validatedOptions; } diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 2f09210ce70a..846a72e827a1 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -42,7 +42,6 @@ import {PermalinkToSidebar} from '@docusaurus/plugin-content-docs-types'; import {RuleSetRule} from 'webpack'; import {cliDocsVersionCommand} from './cli'; import {VERSIONS_JSON_FILE} from './constants'; -import {OptionsSchema} from './options'; import {flatten, keyBy, compact} from 'lodash'; import {toGlobalDataVersion} from './globalData'; import {toVersionMetadataProp} from './props'; @@ -54,7 +53,7 @@ import { export default function pluginContentDocs( context: LoadContext, options: PluginOptions, -): Plugin { +): Plugin { const {siteDir, generatedFilesDir, baseUrl, siteConfig} = context; const versionsMetadata = readVersionsMetadata({context, options}); diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index f53ded796d3a..7e896d59191c 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -13,7 +13,6 @@ import { URISchema, } from '@docusaurus/utils-validation'; import {OptionValidationContext, ValidationResult} from '@docusaurus/types'; -import {ValidationError} from 'joi'; import chalk from 'chalk'; import admonitions from 'remark-admonitions'; @@ -89,14 +88,10 @@ export const OptionsSchema = Joi.object({ versions: VersionsOptionsSchema, }); -// TODO bad validation function types export function validateOptions({ validate, options, -}: OptionValidationContext): ValidationResult< - PluginOptions, - ValidationError -> { +}: OptionValidationContext): ValidationResult { // TODO remove homePageId before end of 2020 // "slug: /" is better because the home doc can be different across versions if (options.homePageId) { @@ -118,8 +113,7 @@ export function validateOptions({ options.includeCurrentVersion = !options.excludeNextVersionDocs; } - // @ts-expect-error: TODO bad OptionValidationContext, need refactor - const normalizedOptions: PluginOptions = validate(OptionsSchema, options); + const normalizedOptions = validate(OptionsSchema, options); if (normalizedOptions.admonitions) { normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([ @@ -127,6 +121,5 @@ export function validateOptions({ ]); } - // @ts-expect-error: TODO bad OptionValidationContext, need refactor return normalizedOptions; } diff --git a/packages/docusaurus-plugin-content-pages/src/index.ts b/packages/docusaurus-plugin-content-pages/src/index.ts index a147674a334c..1d8eafebafb6 100644 --- a/packages/docusaurus-plugin-content-pages/src/index.ts +++ b/packages/docusaurus-plugin-content-pages/src/index.ts @@ -29,7 +29,6 @@ import { import {Configuration, Loader} from 'webpack'; import admonitions from 'remark-admonitions'; import {PluginOptionSchema} from './pluginOptionSchema'; -import {ValidationError} from 'joi'; import { DEFAULT_PLUGIN_ID, STATIC_DIR_NAME, @@ -53,7 +52,7 @@ const isMarkdownSource = (source: string) => export default function pluginContentPages( context: LoadContext, options: PluginOptions, -): Plugin { +): Plugin { if (options.admonitions) { options.remarkPlugins = options.remarkPlugins.concat([ [admonitions, options.admonitions || {}], @@ -260,10 +259,7 @@ export default function pluginContentPages( export function validateOptions({ validate, options, -}: OptionValidationContext): ValidationResult< - PluginOptions, - ValidationError -> { +}: OptionValidationContext): ValidationResult { const validatedOptions = validate(PluginOptionSchema, options); return validatedOptions; } diff --git a/packages/docusaurus-plugin-sitemap/src/index.ts b/packages/docusaurus-plugin-sitemap/src/index.ts index f094e942268a..8fd84f975bc6 100644 --- a/packages/docusaurus-plugin-sitemap/src/index.ts +++ b/packages/docusaurus-plugin-sitemap/src/index.ts @@ -17,7 +17,6 @@ import { Plugin, } from '@docusaurus/types'; import {PluginOptionSchema} from './pluginOptionSchema'; -import {ValidationError} from 'joi'; export default function pluginSitemap( _context: LoadContext, @@ -48,10 +47,7 @@ export default function pluginSitemap( export function validateOptions({ validate, options, -}: OptionValidationContext): ValidationResult< - PluginOptions, - ValidationError -> { +}: OptionValidationContext): ValidationResult { const validatedOptions = validate(PluginOptionSchema, options); return validatedOptions; } diff --git a/packages/docusaurus-theme-classic/src/index.ts b/packages/docusaurus-theme-classic/src/index.ts index 69fa1e80a73a..123c0bb5d96d 100644 --- a/packages/docusaurus-theme-classic/src/index.ts +++ b/packages/docusaurus-theme-classic/src/index.ts @@ -68,10 +68,14 @@ function getInfimaCSSFile(direction) { }.css`; } +type PluginOptions = { + customCss?: string; +}; + export default function docusaurusThemeClassic( - context, - options, -): Plugin { + context: any, // TODO: LoadContext is missing some of properties + options: PluginOptions, +): Plugin { const { siteConfig: {themeConfig}, i18n: {currentLocale, localeConfigs}, diff --git a/packages/docusaurus-types/package.json b/packages/docusaurus-types/package.json index fb28ead5987a..dc8979474788 100644 --- a/packages/docusaurus-types/package.json +++ b/packages/docusaurus-types/package.json @@ -17,6 +17,7 @@ "@types/webpack": "^4.41.0", "commander": "^5.1.0", "querystring": "0.2.0", - "webpack-merge": "^4.2.2" + "webpack-merge": "^4.2.2", + "joi": "^17.4.0" } } diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index b1f3a424342e..cec7c1ee4b90 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -7,10 +7,11 @@ // ESLint doesn't understand types dependencies in d.ts // eslint-disable-next-line import/no-extraneous-dependencies -import {Loader, Configuration, Stats} from 'webpack'; -import {Command} from 'commander'; -import {ParsedUrlQueryInput} from 'querystring'; -import {MergeStrategy} from 'webpack-merge'; +import type {Loader, Configuration, Stats} from 'webpack'; +import type {Command} from 'commander'; +import type {ParsedUrlQueryInput} from 'querystring'; +import type {MergeStrategy} from 'webpack-merge'; +import type Joi from 'joi'; export type ReportingSeverity = 'ignore' | 'log' | 'warn' | 'error' | 'throw'; @@ -183,7 +184,7 @@ export type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[]; export interface Props extends LoadContext, InjectedHtmlTags { routes: RouteConfig[]; routesPaths: string[]; - plugins: Plugin[]; + plugins: Plugin[]; } /** @@ -210,11 +211,9 @@ export type AllContent = Record< // TODO improve type (not exposed by postcss-loader) export type PostCssOptions = Record & {plugins: any[]}; -export interface Plugin { +export interface Plugin { name: string; loadContent?(): Promise; - validateOptions?(): ValidationResult; - validateThemeConfig?(): ValidationResult; contentLoaded?({ content, actions, @@ -242,7 +241,6 @@ export interface Plugin { preBodyTags?: HtmlTags; postBodyTags?: HtmlTags; }; - getSwizzleComponentList?(): string[]; // TODO before/afterDevServer implementation // translations @@ -269,6 +267,17 @@ export interface Plugin { }): ThemeConfig; } +export type PluginModule = { + (context: LoadContext, options: T): Plugin; + validateOptions?(data: OptionValidationContext): T; + validateThemeConfig?(data: ThemeConfigValidationContext): T; + getSwizzleComponentList?(): string[]; +}; + +export type ImportedPluginModule = PluginModule & { + default?: PluginModule; +}; + export type ConfigureWebpackFn = Plugin['configureWebpack']; export type ConfigureWebpackFnMergeStrategy = Record; export type ConfigurePostCssFn = Plugin['configurePostCss']; @@ -346,36 +355,25 @@ interface HtmlTagObject { innerHTML?: string; } -export interface ValidationResult { - error?: E; - value: T; -} +export type ValidationResult = T; + +export type ValidationSchema = Joi.ObjectSchema; -export type Validate = ( +export type Validate = ( validationSchema: ValidationSchema, options: Partial, -) => ValidationResult; +) => ValidationResult; -export interface OptionValidationContext { - validate: Validate; +export interface OptionValidationContext { + validate: Validate; options: Partial; } -export interface ThemeConfigValidationContext { - validate: Validate; +export interface ThemeConfigValidationContext { + validate: Validate; themeConfig: Partial; } -// TODO we should use a Joi type here -export interface ValidationSchema { - validate( - options: Partial, - opt: Record, - ): ValidationResult; - unknown(): ValidationSchema; - append(data: any): ValidationSchema; -} - export interface TOCItem { readonly value: string; readonly id: string; diff --git a/packages/docusaurus-utils-validation/src/validationUtils.ts b/packages/docusaurus-utils-validation/src/validationUtils.ts index 9a919a4e6a97..578c6e0d7a53 100644 --- a/packages/docusaurus-utils-validation/src/validationUtils.ts +++ b/packages/docusaurus-utils-validation/src/validationUtils.ts @@ -37,7 +37,7 @@ export const logValidationBugReportHint = (): void => { export function normalizePluginOptions( schema: Joi.ObjectSchema, - options: unknown, + options: Partial, ): T { // All plugins can be provided an "id" option (multi-instance support) // we add schema validation automatically @@ -61,7 +61,7 @@ export function normalizePluginOptions( export function normalizeThemeConfig( schema: Joi.ObjectSchema, - themeConfig: unknown, + themeConfig: Partial, ): T { // A theme should only validate his "slice" of the full themeConfig, // not the whole object, so we allow unknown attributes diff --git a/packages/docusaurus/src/commands/swizzle.ts b/packages/docusaurus/src/commands/swizzle.ts index e8cc32e533d1..7b2026388acc 100644 --- a/packages/docusaurus/src/commands/swizzle.ts +++ b/packages/docusaurus/src/commands/swizzle.ts @@ -9,7 +9,7 @@ import chalk = require('chalk'); import fs from 'fs-extra'; import importFresh from 'import-fresh'; import path from 'path'; -import {Plugin, LoadContext, PluginConfig} from '@docusaurus/types'; +import {ImportedPluginModule, PluginConfig} from '@docusaurus/types'; import leven from 'leven'; import {partition} from 'lodash'; import {THEME_PATH} from '../constants'; @@ -31,9 +31,8 @@ export function getPluginNames(plugins: PluginConfig[]): string[] { if (packagePath === '.') { return pluginPath; } - return (importFresh(path.join(packagePath, 'package.json')) as { - name: string; - }).name as string; + return importFresh<{name: string}>(path.join(packagePath, 'package.json')) + .name; }); } @@ -66,7 +65,7 @@ function readComponent(themePath: string) { // load components from theme based on configurations function getComponentName( themePath: string, - plugin: any, + plugin: ImportedPluginModule, danger: boolean, ): Array { // support both commonjs and ES style exports @@ -82,7 +81,10 @@ function getComponentName( return readComponent(themePath); } -function themeComponents(themePath: string, plugin: Plugin): string { +function themeComponents( + themePath: string, + plugin: ImportedPluginModule, +): string { const components = colorCode(themePath, plugin); if (components.length === 0) { @@ -103,7 +105,10 @@ function formattedThemeNames(themeNames: string[]): string { return `Themes available for swizzle:\n${themeNames.join('\n')}`; } -function colorCode(themePath: string, plugin: any): Array { +function colorCode( + themePath: string, + plugin: ImportedPluginModule, +): Array { // support both commonjs and ES style exports const getSwizzleComponentList = plugin.default?.getSwizzleComponentList ?? plugin.getSwizzleComponentList; @@ -148,11 +153,9 @@ export default async function swizzle( process.exit(1); } - let pluginModule; + let pluginModule: ImportedPluginModule; try { - pluginModule = importFresh(themeName) as ( - context: LoadContext, - ) => Plugin; + pluginModule = importFresh(themeName); } catch { let suggestion; themeNames.forEach((name) => { @@ -170,10 +173,7 @@ export default async function swizzle( process.exit(1); } - const plugin = pluginModule.default ?? pluginModule; - const validateOptions = - pluginModule.default?.validateOptions ?? pluginModule.validateOptions; - let pluginOptions; + let pluginOptions = {}; const resolvedThemeName = require.resolve(themeName); // find the plugin from list of plugin and get options if specified pluginConfigs.forEach((pluginConfig) => { @@ -188,6 +188,9 @@ export default async function swizzle( } }); + // support both commonjs and ES style exports + const validateOptions = + pluginModule.default?.validateOptions ?? pluginModule.validateOptions; if (validateOptions) { pluginOptions = validateOptions({ validate: normalizePluginOptions, @@ -195,6 +198,8 @@ export default async function swizzle( }); } + // support both commonjs and ES style exports + const plugin = pluginModule.default ?? pluginModule; const pluginInstance = plugin(context, pluginOptions); const themePath = typescript ? pluginInstance.getTypeScriptThemePath?.() diff --git a/packages/docusaurus/src/server/plugins/init.ts b/packages/docusaurus/src/server/plugins/init.ts index 7fe92bbfb091..992649f7a64c 100644 --- a/packages/docusaurus/src/server/plugins/init.ts +++ b/packages/docusaurus/src/server/plugins/init.ts @@ -9,6 +9,7 @@ import Module from 'module'; import importFresh from 'import-fresh'; import { DocusaurusPluginVersionInformation, + ImportedPluginModule, LoadContext, Plugin, PluginConfig, @@ -68,7 +69,7 @@ For more information, visit https://v2.docusaurus.io/docs/using-plugins.`); // The pluginModuleImport value is any valid // module identifier - npm package or locally-resolved path. const pluginPath = pluginRequire.resolve(pluginModuleImport); - const pluginModule: any = importFresh(pluginPath); + const pluginModule: ImportedPluginModule = importFresh(pluginPath); const pluginVersion = getPluginVersion(pluginPath, context.siteDir); const plugin = pluginModule.default || pluginModule; @@ -114,7 +115,7 @@ For more information, visit https://v2.docusaurus.io/docs/using-plugins.`); version: pluginVersion, }; }) - .filter(Boolean); + .filter((item: T): item is Exclude => Boolean(item)); ensureUniquePluginInstanceIds(plugins);