Skip to content

Commit 22a405c

Browse files
committed
feat(linter/plugins): add defineRule API
1 parent e16f1ee commit 22a405c

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

apps/oxlint/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "oxlint",
33
"version": "1.16.0",
4+
"main": "dist/index.js",
45
"bin": "dist/cli.js",
56
"type": "module",
67
"scripts": {

apps/oxlint/src-js/index.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { Context } from './plugins/context.ts';
2+
import type { CreateOnceRule, Rule } from './plugins/load.ts';
3+
4+
const { defineProperty, getPrototypeOf, setPrototypeOf } = Object;
5+
6+
const dummyOptions: unknown[] = [],
7+
dummyReport = () => {};
8+
9+
// Define a rule.
10+
// If rule has `createOnce` method, add an ESLint-compatible `create` method which delegates to `createOnce`.
11+
export function defineRule(rule: Rule): Rule {
12+
if (!('createOnce' in rule)) return rule;
13+
if ('create' in rule) throw new Error('Rules must define only `create` or `createOnce` methods, not both');
14+
15+
// Run `createOnce` with empty context object.
16+
// Really, `context` should be an instance of `Context`, which would throw error on accessing e.g. `id`
17+
// in body of `createOnce`. But any such bugs should have been caught when testing the rule in Oxlint,
18+
// so should be OK to take this shortcut.
19+
const context = Object.create(null, {
20+
id: { value: '', enumerable: true, configurable: true },
21+
options: { value: dummyOptions, enumerable: true, configurable: true },
22+
report: { value: dummyReport, enumerable: true, configurable: true },
23+
});
24+
25+
const { before: beforeHook, after: afterHook, ...visitor } = rule.createOnce(context as Context);
26+
27+
// Add `after` hook to `Program:exit` visit fn
28+
if (afterHook !== null) {
29+
const programExit = visitor['Program:exit'];
30+
visitor['Program:exit'] = programExit
31+
? (node) => {
32+
programExit(node);
33+
afterHook();
34+
}
35+
: (_node) => afterHook();
36+
}
37+
38+
// Create `create` function
39+
rule.create = (eslintContext) => {
40+
// Copy properties from ESLint's context object to `context`.
41+
// ESLint's context object is an object of form `{ id, options, report }`, with all other properties
42+
// and methods on another object which is its prototype.
43+
defineProperty(context, 'id', { value: eslintContext.id });
44+
defineProperty(context, 'options', { value: eslintContext.options });
45+
defineProperty(context, 'report', { value: eslintContext.report });
46+
setPrototypeOf(context, getPrototypeOf(eslintContext));
47+
48+
// If `before` hook returns `false`, skip rest of traversal by returning an empty object as visitor
49+
if (beforeHook !== null) {
50+
const shouldRun = beforeHook();
51+
if (shouldRun === false) return {};
52+
}
53+
54+
// Return same visitor each time
55+
return visitor;
56+
};
57+
58+
return rule;
59+
}

apps/oxlint/src-js/plugins/load.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ interface Plugin {
1616
// Linter rule.
1717
// `Rule` can have either `create` method, or `createOnce` method.
1818
// If `createOnce` method is present, `create` is ignored.
19-
type Rule = CreateRule | CreateOnceRule;
19+
export type Rule = CreateRule | CreateOnceRule;
2020

2121
interface CreateRule {
2222
create: (context: Context) => Visitor;
2323
}
2424

25-
interface CreateOnceRule {
25+
export interface CreateOnceRule {
2626
create?: (context: Context) => Visitor;
2727
createOnce: (context: Context) => VisitorWithHooks;
2828
}

apps/oxlint/tsdown.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineConfig } from 'tsdown';
22

33
export default defineConfig({
4-
entry: ['src-js/cli.ts', 'src-js/plugins/index.ts'],
4+
entry: ['src-js/index.ts', 'src-js/cli.ts', 'src-js/plugins/index.ts'],
55
format: ['esm'],
66
platform: 'node',
77
target: 'node20',

npm/oxlint/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"url": "git+https://github.com/oxc-project/oxc",
1414
"directory": "npm/oxlint"
1515
},
16+
"main": "dist/index.js",
1617
"bin": {
1718
"oxlint": "bin/oxlint",
1819
"oxc_language_server": "bin/oxc_language_server"

0 commit comments

Comments
 (0)