Skip to content

Commit

Permalink
refactor(v2): use correct plugin types (#4418)
Browse files Browse the repository at this point in the history
  • Loading branch information
armano2 authored Mar 15, 2021
1 parent 1078341 commit abae86f
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 80 deletions.
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

0 comments on commit abae86f

Please sign in to comment.