Skip to content

Commit e5bbcf1

Browse files
nzakasmdjermanovic
andauthored
feat: Add Directive and DirectiveType (#112)
Co-authored-by: Milos Djermanovic <[email protected]>
1 parent 8d53243 commit e5bbcf1

File tree

5 files changed

+136
-2
lines changed

5 files changed

+136
-2
lines changed

packages/core/src/types.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,19 +419,28 @@ export interface CallTraversalStep {
419419

420420
export type TraversalStep = VisitTraversalStep | CallTraversalStep;
421421

422+
/**
423+
* The type of disable directive. This determines how ESLint will disable rules.
424+
*/
425+
export type DirectiveType =
426+
| "disable"
427+
| "enable"
428+
| "disable-line"
429+
| "disable-next-line";
430+
422431
/**
423432
* Represents a disable directive.
424433
*/
425434
export interface Directive {
426435
/**
427436
* The type of directive.
428437
*/
429-
type: "disable" | "enable" | "disable-line" | "disable-next-line";
438+
type: DirectiveType;
430439

431440
/**
432441
* The node of the directive. May be in the AST or a comment/token.
433442
*/
434-
node: object;
443+
node: unknown;
435444

436445
/**
437446
* The value of the directive.

packages/plugin-kit/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ This package exports the following utilities:
3030

3131
- `ConfigCommentParser` - used to parse ESLint configuration comments (i.e., `/* eslint-disable rule */`)
3232
- `VisitNodeStep` and `CallMethodStep` - used to help implement `SourceCode#traverse()`
33+
- `Directive` - used to help implement `SourceCode#getDisableDirectives()`
3334
- `TextSourceCodeBase` - base class to help implement the `SourceCode` interface
3435

3536
### `ConfigCommentParser`
@@ -151,6 +152,55 @@ class MySourceCode {
151152
}
152153
```
153154

155+
### `Directive`
156+
157+
The `Directive` class represents a disable directive in the source code and implements the `Directive` interface from `@eslint/core`. You can tell ESLint about disable directives using the `SourceCode#getDisableDirectives()` method, where part of the return value is an array of `Directive` objects. Here's an example:
158+
159+
```js
160+
import { Directive, ConfigCommentParser } from "@eslint/plugin-kit";
161+
162+
class MySourceCode {
163+
getDisableDirectives() {
164+
const directives = [];
165+
const problems = [];
166+
const commentParser = new ConfigCommentParser();
167+
168+
// read in the inline config nodes to check each one
169+
this.getInlineConfigNodes().forEach(comment => {
170+
// Step 1: Parse the directive
171+
const { label, value, justification } =
172+
commentParser.parseDirective(comment.value);
173+
174+
// Step 2: Extract the directive value and create the `Directive` object
175+
switch (label) {
176+
case "eslint-disable":
177+
case "eslint-enable":
178+
case "eslint-disable-next-line":
179+
case "eslint-disable-line": {
180+
const directiveType = label.slice("eslint-".length);
181+
182+
directives.push(
183+
new Directive({
184+
type: directiveType,
185+
node: comment,
186+
value,
187+
justification,
188+
}),
189+
);
190+
}
191+
192+
// ignore any comments that don't begin with known labels
193+
}
194+
});
195+
196+
return {
197+
directives,
198+
problems,
199+
};
200+
}
201+
}
202+
```
203+
154204
### `TextSourceCodeBase`
155205

156206
The `TextSourceCodeBase` class is intended to be a base class that has several of the common members found in `SourceCode` objects already implemented. Those members are:

packages/plugin-kit/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export {
88
CallMethodStep,
99
VisitNodeStep,
1010
TextSourceCodeBase,
11+
Directive,
1112
} from "./source-code.js";

packages/plugin-kit/src/source-code.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
/** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
1717
/** @typedef {import("@eslint/core").SourceLocationWithOffset} SourceLocationWithOffset */
1818
/** @typedef {import("@eslint/core").SourceRange} SourceRange */
19+
/** @typedef {import("@eslint/core").Directive} IDirective */
20+
/** @typedef {import("@eslint/core").DirectiveType} DirectiveType */
1921

2022
//-----------------------------------------------------------------------------
2123
// Helpers
@@ -158,6 +160,56 @@ export class CallMethodStep {
158160
}
159161
}
160162

163+
/**
164+
* A class to represent a directive comment.
165+
* @implements {IDirective}
166+
*/
167+
export class Directive {
168+
/**
169+
* The type of directive.
170+
* @type {DirectiveType}
171+
* @readonly
172+
*/
173+
type;
174+
175+
/**
176+
* The node representing the directive.
177+
* @type {unknown}
178+
* @readonly
179+
*/
180+
node;
181+
182+
/**
183+
* Everything after the "eslint-disable" portion of the directive,
184+
* but before the "--" that indicates the justification.
185+
* @type {string}
186+
* @readonly
187+
*/
188+
value;
189+
190+
/**
191+
* The justification for the directive.
192+
* @type {string}
193+
* @readonly
194+
*/
195+
justification;
196+
197+
/**
198+
* Creates a new instance.
199+
* @param {Object} options The options for the directive.
200+
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
201+
* @param {unknown} options.node The node representing the directive.
202+
* @param {string} options.value The value of the directive.
203+
* @param {string} options.justification The justification for the directive.
204+
*/
205+
constructor({ type, node, value, justification }) {
206+
this.type = type;
207+
this.node = node;
208+
this.value = value;
209+
this.justification = justification;
210+
}
211+
}
212+
161213
/**
162214
* Source Code Base Object
163215
* @implements {TextSourceCode}

packages/plugin-kit/tests/source-code.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import assert from "node:assert";
1111
import {
1212
CallMethodStep,
1313
VisitNodeStep,
14+
Directive,
1415
TextSourceCodeBase,
1516
} from "../src/source-code.js";
1617

@@ -63,6 +64,27 @@ describe("source-code", () => {
6364
});
6465
});
6566

67+
describe("Directive", () => {
68+
it("should create a new instance", () => {
69+
const type = "disable";
70+
const node = { foo: "bar" };
71+
const value = "baz";
72+
const justification = "qux";
73+
74+
const directive = new Directive({
75+
type,
76+
node,
77+
value,
78+
justification,
79+
});
80+
81+
assert.strictEqual(directive.type, type);
82+
assert.strictEqual(directive.node, node);
83+
assert.strictEqual(directive.value, value);
84+
assert.strictEqual(directive.justification, justification);
85+
});
86+
});
87+
6688
describe("TextSourceCodeBase", () => {
6789
describe("new TextSourceCodeBase", () => {
6890
it("should create a new instance", () => {

0 commit comments

Comments
 (0)