Move plugin object validation into plugin file.
This commit is contained in:
parent
98969b8a73
commit
52d337e4d9
@ -12,6 +12,12 @@ import type {
|
||||
RootInputSourceMapOption,
|
||||
} from "./options";
|
||||
|
||||
export type ValidatorSet = {
|
||||
[string]: Validator<any>,
|
||||
};
|
||||
|
||||
export type Validator<T> = (string, mixed) => T;
|
||||
|
||||
export function assertSourceMaps(
|
||||
key: string,
|
||||
value: mixed,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import * as context from "../index";
|
||||
import Plugin from "./plugin";
|
||||
import Plugin, { validatePluginObject } from "./plugin";
|
||||
import defaults from "lodash/defaults";
|
||||
import merge from "lodash/merge";
|
||||
import buildConfigChain, { type ConfigItem } from "./build-config-chain";
|
||||
@ -28,15 +28,6 @@ type MergeOptions =
|
||||
dirname: string,
|
||||
};
|
||||
|
||||
const ALLOWED_PLUGIN_KEYS = new Set([
|
||||
"name",
|
||||
"manipulateOptions",
|
||||
"pre",
|
||||
"post",
|
||||
"visitor",
|
||||
"inherits",
|
||||
]);
|
||||
|
||||
export default function manageOptions(opts: {}): {
|
||||
options: Object,
|
||||
passes: Array<Array<Plugin>>,
|
||||
@ -275,37 +266,16 @@ function loadPluginDescriptor(descriptor: BasicDescriptor): Plugin {
|
||||
}
|
||||
|
||||
const instantiatePlugin = makeWeakCache(
|
||||
(
|
||||
{ value: pluginObj, options, dirname, alias }: LoadedDescriptor,
|
||||
cache,
|
||||
): Plugin => {
|
||||
Object.keys(pluginObj).forEach(key => {
|
||||
if (!ALLOWED_PLUGIN_KEYS.has(key)) {
|
||||
throw new Error(
|
||||
`Plugin ${alias} provided an invalid property of ${key}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
if (
|
||||
pluginObj.visitor &&
|
||||
(pluginObj.visitor.enter || pluginObj.visitor.exit)
|
||||
) {
|
||||
throw new Error(
|
||||
"Plugins aren't allowed to specify catch-all enter/exit handlers. " +
|
||||
"Please target individual nodes.",
|
||||
);
|
||||
({ value, options, dirname, alias }: LoadedDescriptor, cache): Plugin => {
|
||||
const pluginObj = validatePluginObject(value);
|
||||
|
||||
const plugin = Object.assign({}, pluginObj);
|
||||
if (plugin.visitor) {
|
||||
plugin.visitor = traverse.explode(clone(plugin.visitor));
|
||||
}
|
||||
|
||||
const plugin = Object.assign({}, pluginObj, {
|
||||
visitor: clone(pluginObj.visitor || {}),
|
||||
});
|
||||
|
||||
traverse.explode(plugin.visitor);
|
||||
|
||||
let inheritsDescriptor;
|
||||
let inherits;
|
||||
if (plugin.inherits) {
|
||||
inheritsDescriptor = {
|
||||
const inheritsDescriptor = {
|
||||
alias: `${alias}$inherits`,
|
||||
value: plugin.inherits,
|
||||
options,
|
||||
@ -313,7 +283,7 @@ const instantiatePlugin = makeWeakCache(
|
||||
};
|
||||
|
||||
// If the inherited plugin changes, reinstantiate this plugin.
|
||||
inherits = cache.invalidate(() =>
|
||||
const inherits = cache.invalidate(() =>
|
||||
loadPluginDescriptor(inheritsDescriptor),
|
||||
);
|
||||
|
||||
@ -324,8 +294,8 @@ const instantiatePlugin = makeWeakCache(
|
||||
plugin.manipulateOptions,
|
||||
);
|
||||
plugin.visitor = traverse.visitors.merge([
|
||||
inherits.visitor,
|
||||
plugin.visitor,
|
||||
inherits.visitor || {},
|
||||
plugin.visitor || {},
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -12,14 +12,10 @@ import {
|
||||
assertSourceMaps,
|
||||
assertCompact,
|
||||
assertSourceType,
|
||||
type ValidatorSet,
|
||||
type Validator,
|
||||
} from "./option-assertions";
|
||||
|
||||
type ValidatorSet = {
|
||||
[string]: Validator<any>,
|
||||
};
|
||||
|
||||
type Validator<T> = (string, mixed) => T;
|
||||
|
||||
const ROOT_VALIDATORS: ValidatorSet = {
|
||||
filename: (assertString: Validator<
|
||||
$PropertyType<ValidatedOptions, "filename">,
|
||||
|
||||
@ -1,43 +1,104 @@
|
||||
// @flow
|
||||
|
||||
// $FlowIssue recursion?
|
||||
import {
|
||||
assertString,
|
||||
assertFunction,
|
||||
assertObject,
|
||||
type ValidatorSet,
|
||||
type Validator,
|
||||
} from "./option-assertions";
|
||||
|
||||
const VALIDATORS: ValidatorSet = {
|
||||
name: (assertString: Validator<$PropertyType<PluginObject, "name">>),
|
||||
manipulateOptions: (assertFunction: Validator<
|
||||
$PropertyType<PluginObject, "manipulateOptions">,
|
||||
>),
|
||||
pre: (assertFunction: Validator<$PropertyType<PluginObject, "pre">>),
|
||||
post: (assertFunction: Validator<$PropertyType<PluginObject, "post">>),
|
||||
inherits: (assertFunction: Validator<
|
||||
$PropertyType<PluginObject, "inherits">,
|
||||
>),
|
||||
visitor: (assertVisitorMap: Validator<
|
||||
$PropertyType<PluginObject, "visitor">,
|
||||
>),
|
||||
};
|
||||
|
||||
function assertVisitorMap(key: string, value: mixed): VisitorMap {
|
||||
const obj = assertObject(key, value);
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach(prop => assertVisitorHandler(prop, obj[prop]));
|
||||
|
||||
if (obj.enter || obj.exit) {
|
||||
throw new Error(
|
||||
`.${key} cannot contain catch-all "enter" or "exit" handlers. Please target individual nodes.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return (obj: any);
|
||||
}
|
||||
|
||||
function assertVisitorHandler(
|
||||
key: string,
|
||||
value: mixed,
|
||||
): VisitorHandler | void {
|
||||
if (value && typeof value === "object") {
|
||||
Object.keys(value).forEach(handler => {
|
||||
if (handler !== "enter" && handler !== "exit") {
|
||||
throw new Error(
|
||||
`.visitor["${key}"] may only have .enter and/or .exit handlers.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (typeof value !== "function") {
|
||||
throw new Error(`.visitor["${key}"] must be a function`);
|
||||
}
|
||||
|
||||
return (value: any);
|
||||
}
|
||||
|
||||
type VisitorHandler = Function | { enter?: Function, exit?: Function };
|
||||
export type VisitorMap = {
|
||||
[string]: VisitorHandler,
|
||||
};
|
||||
|
||||
export type PluginObject = {
|
||||
name?: string,
|
||||
manipulateOptions?: Function,
|
||||
|
||||
pre?: Function,
|
||||
post?: Function,
|
||||
|
||||
inherits?: Function,
|
||||
visitor?: VisitorMap,
|
||||
};
|
||||
|
||||
export function validatePluginObject(obj: {}): PluginObject {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const validator = VALIDATORS[key];
|
||||
|
||||
if (validator) validator(key, obj[key]);
|
||||
else throw new Error(`.${key} is not a valid Plugin property`);
|
||||
});
|
||||
|
||||
return (obj: any);
|
||||
}
|
||||
|
||||
export default class Plugin {
|
||||
key: ?string;
|
||||
manipulateOptions: ?Function;
|
||||
post: ?Function;
|
||||
pre: ?Function;
|
||||
visitor: ?{};
|
||||
manipulateOptions: Function | void;
|
||||
post: Function | void;
|
||||
pre: Function | void;
|
||||
visitor: {};
|
||||
|
||||
options: {};
|
||||
|
||||
constructor(plugin: {}, options: {}, key?: string) {
|
||||
if (plugin.name != null && typeof plugin.name !== "string") {
|
||||
throw new Error("Plugin .name must be a string, null, or undefined");
|
||||
}
|
||||
if (
|
||||
plugin.manipulateOptions != null &&
|
||||
typeof plugin.manipulateOptions !== "function"
|
||||
) {
|
||||
throw new Error(
|
||||
"Plugin .manipulateOptions must be a function, null, or undefined",
|
||||
);
|
||||
}
|
||||
if (plugin.post != null && typeof plugin.post !== "function") {
|
||||
throw new Error("Plugin .post must be a function, null, or undefined");
|
||||
}
|
||||
if (plugin.pre != null && typeof plugin.pre !== "function") {
|
||||
throw new Error("Plugin .pre must be a function, null, or undefined");
|
||||
}
|
||||
if (plugin.visitor != null && typeof plugin.visitor !== "object") {
|
||||
throw new Error("Plugin .visitor must be an object, null, or undefined");
|
||||
}
|
||||
|
||||
constructor(plugin: PluginObject, options: {}, key?: string) {
|
||||
this.key = plugin.name || key;
|
||||
|
||||
this.manipulateOptions = plugin.manipulateOptions;
|
||||
this.post = plugin.post;
|
||||
this.pre = plugin.pre;
|
||||
this.visitor = plugin.visitor;
|
||||
this.visitor = plugin.visitor || {};
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user