11import type { Context } from './plugins/context.ts' ;
22import type { CreateOnceRule , Plugin , Rule } from './plugins/load.ts' ;
3- import type { BeforeHook , Visitor } from './plugins/types.ts' ;
3+ import type { BeforeHook , Visitor , VisitorWithHooks } from './plugins/types.ts' ;
44
5- const { defineProperty, getPrototypeOf, setPrototypeOf } = Object ;
5+ const { defineProperty, getPrototypeOf, hasOwn , setPrototypeOf } = Object ;
66
77const dummyOptions : unknown [ ] = [ ] ,
88 dummyReport = ( ) => { } ;
99
10- // Define a plugin.
10+ /**
11+ * Define a plugin.
12+ *
13+ * Converts any rules with `createOnce` method to have an ESLint-compatible `create` method.
14+ *
15+ * The `plugin` object passed in is mutated in-place.
16+ *
17+ * @param plugin - Plugin to define
18+ * @returns Plugin with all rules having `create` method
19+ * @throws {Error } If `plugin` is not an object, or `plugin.rules` is not an object
20+ */
1121export function definePlugin ( plugin : Plugin ) : Plugin {
22+ // Validate type of `plugin`
23+ if ( plugin === null || typeof plugin !== 'object' ) throw new Error ( 'Plugin must be an object' ) ;
24+
25+ const { rules } = plugin ;
26+ if ( rules === null || typeof rules !== 'object' ) throw new Error ( 'Plugin must have an object as `rules` property' ) ;
27+
28+ // Make each rule in the plugin ESLint-compatible by calling `defineRule` on it
29+ for ( const ruleName in rules ) {
30+ if ( hasOwn ( rules , ruleName ) ) {
31+ rules [ ruleName ] = defineRule ( rules [ ruleName ] ) ;
32+ }
33+ }
34+
1235 return plugin ;
1336}
1437
15- // Define a rule.
16- // If rule has `createOnce` method, add an ESLint-compatible `create` method which delegates to `createOnce`.
38+ /**
39+ * Define a rule.
40+ *
41+ * If rules does not already have a `create` method, create an ESLint-compatible `create` method
42+ * which delegates to `createOnce`.
43+ *
44+ * The `rule` object passed in is mutated in-place.
45+ *
46+ * @param rule - Rule to define
47+ * @returns Rule with `create` method
48+ * @throws {Error } If `rule` is not an object
49+ */
1750export function defineRule ( rule : Rule ) : Rule {
18- if ( ! ( 'createOnce' in rule ) ) return rule ;
19- if ( 'create' in rule ) throw new Error ( 'Rules must define only `create` or `createOnce` methods, not both' ) ;
51+ // Validate type of `rule`
52+ if ( rule === null || typeof rule !== 'object' ) throw new Error ( 'Rule must be an object' ) ;
53+
54+ // If rule already has `create` method, return it as is
55+ if ( 'create' in rule ) return rule ;
2056
2157 // Add `create` function to `rule`
2258 let context : Context = null , visitor : Visitor , beforeHook : BeforeHook | null ;
@@ -59,6 +95,11 @@ function createContextAndVisitor(rule: CreateOnceRule): {
5995 visitor : Visitor ;
6096 beforeHook : BeforeHook | null ;
6197} {
98+ // Validate type of `createOnce`
99+ const { createOnce } = rule ;
100+ if ( createOnce == null ) throw new Error ( 'Rules must define either a `create` or `createOnce` method' ) ;
101+ if ( typeof createOnce !== 'function' ) throw new Error ( 'Rule `createOnce` property must be a function' ) ;
102+
62103 // Call `createOnce` with empty context object.
63104 // Really, `context` should be an instance of `Context`, which would throw error on accessing e.g. `id`
64105 // in body of `createOnce`. But any such bugs should have been caught when testing the rule in Oxlint,
@@ -69,7 +110,7 @@ function createContextAndVisitor(rule: CreateOnceRule): {
69110 report : { value : dummyReport , enumerable : true , configurable : true } ,
70111 } ) ;
71112
72- let { before : beforeHook , after : afterHook , ...visitor } = rule . createOnce ( context ) ;
113+ let { before : beforeHook , after : afterHook , ...visitor } = createOnce . call ( rule , context ) as VisitorWithHooks ;
73114
74115 if ( beforeHook === void 0 ) {
75116 beforeHook = null ;
0 commit comments