Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(v2): correct plugin types #4418

Merged
merged 1 commit into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions packages/docusaurus-plugin-content-blog/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -56,7 +55,7 @@ import {
export default function pluginContentBlog(
context: LoadContext,
options: PluginOptions,
): Plugin<BlogContent | null, typeof PluginOptionSchema> {
): Plugin<BlogContent | null> {
if (options.admonitions) {
options.remarkPlugins = options.remarkPlugins.concat([
[admonitions, options.admonitions],
Expand Down Expand Up @@ -561,10 +560,7 @@ export default function pluginContentBlog(
export function validateOptions({
validate,
options,
}: OptionValidationContext<PluginOptions, ValidationError>): ValidationResult<
PluginOptions,
ValidationError
> {
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
const validatedOptions = validate(PluginOptionSchema, options);
return validatedOptions;
}
3 changes: 1 addition & 2 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -54,7 +53,7 @@ import {
export default function pluginContentDocs(
context: LoadContext,
options: PluginOptions,
): Plugin<LoadedContent, typeof OptionsSchema> {
): Plugin<LoadedContent> {
const {siteDir, generatedFilesDir, baseUrl, siteConfig} = context;

const versionsMetadata = readVersionsMetadata({context, options});
Expand Down
11 changes: 2 additions & 9 deletions packages/docusaurus-plugin-content-docs/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -89,14 +88,10 @@ export const OptionsSchema = Joi.object({
versions: VersionsOptionsSchema,
});

// TODO bad validation function types
export function validateOptions({
validate,
options,
}: OptionValidationContext<PluginOptions, ValidationError>): ValidationResult<
PluginOptions,
ValidationError
> {
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
// TODO remove homePageId before end of 2020
// "slug: /" is better because the home doc can be different across versions
if (options.homePageId) {
Expand All @@ -118,15 +113,13 @@ 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([
[admonitions, normalizedOptions.admonitions],
]);
}

// @ts-expect-error: TODO bad OptionValidationContext, need refactor
return normalizedOptions;
}
8 changes: 2 additions & 6 deletions packages/docusaurus-plugin-content-pages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -53,7 +52,7 @@ const isMarkdownSource = (source: string) =>
export default function pluginContentPages(
context: LoadContext,
options: PluginOptions,
): Plugin<LoadedContent | null, typeof PluginOptionSchema> {
): Plugin<LoadedContent | null> {
if (options.admonitions) {
options.remarkPlugins = options.remarkPlugins.concat([
[admonitions, options.admonitions || {}],
Expand Down Expand Up @@ -260,10 +259,7 @@ export default function pluginContentPages(
export function validateOptions({
validate,
options,
}: OptionValidationContext<PluginOptions, ValidationError>): ValidationResult<
PluginOptions,
ValidationError
> {
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
const validatedOptions = validate(PluginOptionSchema, options);
return validatedOptions;
}
6 changes: 1 addition & 5 deletions packages/docusaurus-plugin-sitemap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
Plugin,
} from '@docusaurus/types';
import {PluginOptionSchema} from './pluginOptionSchema';
import {ValidationError} from 'joi';

export default function pluginSitemap(
_context: LoadContext,
Expand Down Expand Up @@ -48,10 +47,7 @@ export default function pluginSitemap(
export function validateOptions({
validate,
options,
}: OptionValidationContext<PluginOptions, ValidationError>): ValidationResult<
PluginOptions,
ValidationError
> {
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
const validatedOptions = validate(PluginOptionSchema, options);
return validatedOptions;
}
10 changes: 7 additions & 3 deletions packages/docusaurus-theme-classic/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,14 @@ function getInfimaCSSFile(direction) {
}.css`;
}

type PluginOptions = {
customCss?: string;
};

export default function docusaurusThemeClassic(
context,
options,
): Plugin<null, unknown> {
context: any, // TODO: LoadContext is missing some of properties
options: PluginOptions,
): Plugin<void> {
const {
siteConfig: {themeConfig},
i18n: {currentLocale, localeConfigs},
Expand Down
3 changes: 2 additions & 1 deletion packages/docusaurus-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
56 changes: 27 additions & 29 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -183,7 +184,7 @@ export type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
export interface Props extends LoadContext, InjectedHtmlTags {
routes: RouteConfig[];
routesPaths: string[];
plugins: Plugin<any, unknown>[];
plugins: Plugin<any>[];
}

/**
Expand All @@ -210,11 +211,9 @@ export type AllContent = Record<
// TODO improve type (not exposed by postcss-loader)
export type PostCssOptions = Record<string, any> & {plugins: any[]};

export interface Plugin<T, U = unknown> {
export interface Plugin<T> {
name: string;
loadContent?(): Promise<T>;
validateOptions?(): ValidationResult<U>;
validateThemeConfig?(): ValidationResult<any>;
contentLoaded?({
content,
actions,
Expand Down Expand Up @@ -242,7 +241,6 @@ export interface Plugin<T, U = unknown> {
preBodyTags?: HtmlTags;
postBodyTags?: HtmlTags;
};
getSwizzleComponentList?(): string[];
// TODO before/afterDevServer implementation

// translations
Expand All @@ -269,6 +267,17 @@ export interface Plugin<T, U = unknown> {
}): ThemeConfig;
}

export type PluginModule = {
<T, X>(context: LoadContext, options: T): Plugin<X>;
validateOptions?<T>(data: OptionValidationContext<T>): T;
validateThemeConfig?<T>(data: ThemeConfigValidationContext<T>): T;
getSwizzleComponentList?(): string[];
};

export type ImportedPluginModule = PluginModule & {
default?: PluginModule;
};

export type ConfigureWebpackFn = Plugin<unknown>['configureWebpack'];
export type ConfigureWebpackFnMergeStrategy = Record<string, MergeStrategy>;
export type ConfigurePostCssFn = Plugin<unknown>['configurePostCss'];
Expand Down Expand Up @@ -346,36 +355,25 @@ interface HtmlTagObject {
innerHTML?: string;
}

export interface ValidationResult<T, E extends Error = Error> {
error?: E;
value: T;
}
export type ValidationResult<T> = T;

export type ValidationSchema<T> = Joi.ObjectSchema<T>;

export type Validate<T, E extends Error = Error> = (
export type Validate<T> = (
validationSchema: ValidationSchema<T>,
options: Partial<T>,
) => ValidationResult<T, E>;
) => ValidationResult<T>;

export interface OptionValidationContext<T, E extends Error = Error> {
validate: Validate<T, E>;
export interface OptionValidationContext<T> {
validate: Validate<T>;
options: Partial<T>;
}

export interface ThemeConfigValidationContext<T, E extends Error = Error> {
validate: Validate<T, E>;
export interface ThemeConfigValidationContext<T> {
validate: Validate<T>;
themeConfig: Partial<T>;
}

// TODO we should use a Joi type here
export interface ValidationSchema<T> {
validate(
options: Partial<T>,
opt: Record<string, unknown>,
): ValidationResult<T>;
unknown(): ValidationSchema<T>;
append(data: any): ValidationSchema<T>;
}

export interface TOCItem {
readonly value: string;
readonly id: string;
Expand Down
4 changes: 2 additions & 2 deletions packages/docusaurus-utils-validation/src/validationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const logValidationBugReportHint = (): void => {

export function normalizePluginOptions<T extends {id?: string}>(
schema: Joi.ObjectSchema<T>,
options: unknown,
options: Partial<T>,
): T {
// All plugins can be provided an "id" option (multi-instance support)
// we add schema validation automatically
Expand All @@ -61,7 +61,7 @@ export function normalizePluginOptions<T extends {id?: string}>(

export function normalizeThemeConfig<T>(
schema: Joi.ObjectSchema<T>,
themeConfig: unknown,
themeConfig: Partial<T>,
): T {
// A theme should only validate his "slice" of the full themeConfig,
// not the whole object, so we allow unknown attributes
Expand Down
35 changes: 20 additions & 15 deletions packages/docusaurus/src/commands/swizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
});
}

Expand Down Expand Up @@ -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<string> {
// support both commonjs and ES style exports
Expand All @@ -82,7 +81,10 @@ function getComponentName(
return readComponent(themePath);
}

function themeComponents(themePath: string, plugin: Plugin<unknown>): string {
function themeComponents(
themePath: string,
plugin: ImportedPluginModule,
): string {
const components = colorCode(themePath, plugin);

if (components.length === 0) {
Expand All @@ -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<string> {
function colorCode(
themePath: string,
plugin: ImportedPluginModule,
): Array<string> {
// support both commonjs and ES style exports
const getSwizzleComponentList =
plugin.default?.getSwizzleComponentList ?? plugin.getSwizzleComponentList;
Expand Down Expand Up @@ -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<unknown>;
pluginModule = importFresh(themeName);
} catch {
let suggestion;
themeNames.forEach((name) => {
Expand All @@ -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) => {
Expand All @@ -188,13 +188,18 @@ 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,
options: pluginOptions,
});
}

// support both commonjs and ES style exports
const plugin = pluginModule.default ?? pluginModule;
const pluginInstance = plugin(context, pluginOptions);
const themePath = typescript
? pluginInstance.getTypeScriptThemePath?.()
Expand Down
Loading