forked from babel/babel
/
plugins.ts
121 lines (109 loc) 路 3.25 KB
/
plugins.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import {
assertString,
assertFunction,
assertObject,
msg,
} from "./option-assertions";
import type {
ValidatorSet,
Validator,
OptionPath,
RootPath,
} from "./option-assertions";
import type { ParserOptions } from "@babel/parser";
import type { Visitor } from "@babel/traverse";
import type PluginPass from "../../transformation/plugin-pass";
import type { ValidatedOptions } from "./options";
// Note: The casts here are just meant to be static assertions to make sure
// that the assertion functions actually assert that the value's type matches
// the declared types.
const VALIDATORS: ValidatorSet = {
name: assertString as Validator<PluginObject["name"]>,
manipulateOptions: assertFunction as Validator<
PluginObject["manipulateOptions"]
>,
pre: assertFunction as Validator<PluginObject["pre"]>,
post: assertFunction as Validator<PluginObject["post"]>,
inherits: assertFunction as Validator<PluginObject["inherits"]>,
visitor: assertVisitorMap as Validator<PluginObject["visitor"]>,
parserOverride: assertFunction as Validator<PluginObject["parserOverride"]>,
generatorOverride: assertFunction as Validator<
PluginObject["generatorOverride"]
>,
};
function assertVisitorMap(loc: OptionPath, value: unknown): Visitor {
const obj = assertObject(loc, value);
if (obj) {
Object.keys(obj).forEach(prop => assertVisitorHandler(prop, obj[prop]));
// @ts-ignore
if (obj.enter || obj.exit) {
throw new Error(
`${msg(
loc,
)} cannot contain catch-all "enter" or "exit" handlers. Please target individual nodes.`,
);
}
}
return obj as Visitor;
}
function assertVisitorHandler(
key: string,
value: unknown,
): VisitorHandler | void {
if (value && typeof value === "object") {
Object.keys(value).forEach((handler: string) => {
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 as any;
}
type VisitorHandler =
| Function
| {
enter?: Function;
exit?: Function;
};
export type PluginObject<S = PluginPass> = {
name?: string;
manipulateOptions?: (
options: ValidatedOptions,
parserOpts: ParserOptions,
) => void;
pre?: Function;
post?: Function;
inherits?: Function;
visitor?: Visitor<S>;
parserOverride?: Function;
generatorOverride?: Function;
};
export function validatePluginObject(obj: {}): PluginObject {
const rootPath: RootPath = {
type: "root",
source: "plugin",
};
Object.keys(obj).forEach((key: string) => {
const validator = VALIDATORS[key];
if (validator) {
const optLoc: OptionPath = {
type: "option",
name: key,
parent: rootPath,
};
validator(optLoc, obj[key]);
} else {
const invalidPluginPropertyError = new Error(
`.${key} is not a valid Plugin property`,
);
// @ts-expect-error todo(flow->ts) consider additing BabelConfigError with code field
invalidPluginPropertyError.code = "BABEL_UNKNOWN_PLUGIN_PROPERTY";
throw invalidPluginPropertyError;
}
});
return obj as any;
}