Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Directive and DirectiveType #112

Merged
merged 5 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,19 +419,28 @@ export interface CallTraversalStep {

export type TraversalStep = VisitTraversalStep | CallTraversalStep;

/**
* The type of disable directive. This determines how ESLint will disable rules.
*/
export type DirectiveType =
| "disable"
| "enable"
| "disable-line"
| "disable-next-line";

/**
* Represents a disable directive.
*/
export interface Directive {
/**
* The type of directive.
*/
type: "disable" | "enable" | "disable-line" | "disable-next-line";
type: DirectiveType;

/**
* The node of the directive. May be in the AST or a comment/token.
*/
node: object;
node: unknown;

/**
* The value of the directive.
Expand Down
54 changes: 54 additions & 0 deletions packages/plugin-kit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This package exports the following utilities:

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

### `ConfigCommentParser`
Expand Down Expand Up @@ -151,6 +152,59 @@ class MySourceCode {
}
```

### `Directive`

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:

```js
import {
VisitNodeStep,
CallMethodStep,
nzakas marked this conversation as resolved.
Show resolved Hide resolved
ConfigCommentParser,
} from "@eslint/plugin-kit";

class MySourceCode {
getDisableDirectives() {
const directives = [];
const problems = [];
const commentParser = new ConfigCommentParser();

// read in the inline config nodes to check each one
this.getInlineConfigNodes().forEach(comment => {
// Step 1: Parse the directive
const { label, value, justification } =
commentParser.parseDirective(comment.value);

// Step 2: Extract the directive value and create the `Directive` object
switch (label) {
case "eslint-disable":
case "eslint-enable":
case "eslint-disable-next-line":
case "eslint-disable-line": {
const directiveType = label.slice("eslint-".length);

directives.push(
new Directive({
type: directiveType,
node: comment,
value,
justification,
}),
);
}

// ignore any comments that don't begin with known labels
}
});

return {
directives,
problems,
};
}
}
```

### `TextSourceCodeBase`

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:
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-kit/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export {
CallMethodStep,
VisitNodeStep,
TextSourceCodeBase,
Directive,
} from "./source-code.js";
52 changes: 52 additions & 0 deletions packages/plugin-kit/src/source-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
/** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
/** @typedef {import("@eslint/core").SourceLocationWithOffset} SourceLocationWithOffset */
/** @typedef {import("@eslint/core").SourceRange} SourceRange */
/** @typedef {import("@eslint/core").Directive} IDirective */
/** @typedef {import("@eslint/core").DirectiveType} DirectiveType */

//-----------------------------------------------------------------------------
// Helpers
Expand Down Expand Up @@ -158,6 +160,56 @@ export class CallMethodStep {
}
}

/**
* A class to represent a directive comment.
* @implements {IDirective}
*/
export class Directive {
/**
* The type of directive.
* @type {DirectiveType}
* @readonly
*/
type;

/**
* The node representing the directive.
* @type {unknown}
* @readonly
*/
node;

/**
* Everything after the "eslint-disable" portion of the directive,
* but before the "--" that indicates the justification.
* @type {string}
* @readonly
*/
value;

/**
* The justification for the directive.
* @type {string}
* @readonly
*/
justification;

/**
* Creates a new instance.
* @param {Object} options The options for the directive.
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
* @param {unknown} options.node The node representing the directive.
* @param {string} options.value The value of the directive.
* @param {string} options.justification The justification for the directive.
*/
constructor({ type, node, value, justification }) {
this.type = type;
this.node = node;
this.value = value;
this.justification = justification;
}
}

/**
* Source Code Base Object
* @implements {TextSourceCode}
Expand Down
22 changes: 22 additions & 0 deletions packages/plugin-kit/tests/source-code.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import assert from "node:assert";
import {
CallMethodStep,
VisitNodeStep,
Directive,
TextSourceCodeBase,
} from "../src/source-code.js";

Expand Down Expand Up @@ -63,6 +64,27 @@ describe("source-code", () => {
});
});

describe("Directive", () => {
it("should create a new instance", () => {
const type = "disable";
const node = { foo: "bar" };
const value = "baz";
const justification = "qux";

const directive = new Directive({
type,
node,
value,
justification,
});

assert.strictEqual(directive.type, type);
assert.strictEqual(directive.node, node);
assert.strictEqual(directive.value, value);
assert.strictEqual(directive.justification, justification);
});
});

describe("TextSourceCodeBase", () => {
describe("new TextSourceCodeBase", () => {
it("should create a new instance", () => {
Expand Down