Skip to content

Commit

Permalink
feat: Add Directive and DirectiveType (#112)
Browse files Browse the repository at this point in the history
Co-authored-by: Milos Djermanovic <[email protected]>
  • Loading branch information
nzakas and mdjermanovic committed Aug 30, 2024
1 parent 8d53243 commit e5bbcf1
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 2 deletions.
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
50 changes: 50 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,55 @@ 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 { Directive, 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

0 comments on commit e5bbcf1

Please sign in to comment.