npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@eslint/plugin-kit

v0.2.3

Published

Utilities for building ESLint plugins.

Downloads

20,550,083

Readme

ESLint Plugin Kit

Description

A collection of utilities to help build ESLint plugins.

Installation

For Node.js and compatible runtimes:

npm install @eslint/plugin-kit
# or
yarn add @eslint/plugin-kit
# or
pnpm install @eslint/plugin-kit
# or
bun install @eslint/plugin-kit

For Deno:

deno add @eslint/plugin-kit

Usage

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

To use the ConfigCommentParser class, import it from the package and create a new instance, such as:

import { ConfigCommentParser } from "@eslint/plugin-kit";

// create a new instance
const commentParser = new ConfigCommentParser();

// pass in a comment string without the comment delimiters
const directive = commentParser.parseDirective(
	"eslint-disable prefer-const, semi -- I don't want to use these.",
);

// will be undefined when a directive can't be parsed
if (directive) {
	console.log(directive.label); // "eslint-disable"
	console.log(directive.value); // "prefer-const, semi"
	console.log(directive.justification); // "I don't want to use these"
}

There are different styles of directive values that you'll need to parse separately to get the correct format:

import { ConfigCommentParser } from "@eslint/plugin-kit";

// create a new instance
const commentParser = new ConfigCommentParser();

// list format
const list = commentParser.parseListConfig("prefer-const, semi");
console.log(Object.entries(list)); // [["prefer-const", true], ["semi", true]]

// string format
const strings = commentParser.parseStringConfig("foo:off, bar");
console.log(Object.entries(strings)); // [["foo", "off"], ["bar", null]]

// JSON-like config format
const jsonLike = commentParser.parseJSONLikeConfig(
	"semi:[error, never], prefer-const: warn",
);
console.log(Object.entries(jsonLike.config)); // [["semi", ["error", "never"]], ["prefer-const", "warn"]]

VisitNodeStep and CallMethodStep

The VisitNodeStep and CallMethodStep classes represent steps in the traversal of source code. They implement the correct interfaces to return from the SourceCode#traverse() method.

The VisitNodeStep class is the more common of the two, where you are describing a visit to a particular node during the traversal. The constructor accepts three arguments:

  • target - the node being visited. This is used to determine the method to call inside of a rule. For instance, if the node's type is Literal then ESLint will call a method named Literal() on the rule (if present).
  • phase - either 1 for enter or 2 for exit.
  • args - an array of arguments to pass into the visitor method of a rule.

For example:

import { VisitNodeStep } from "@eslint/plugin-kit";

class MySourceCode {
	traverse() {
		const steps = [];

		for (const { node, parent, phase } of iterator(this.ast)) {
			steps.push(
				new VisitNodeStep({
					target: node,
					phase: phase === "enter" ? 1 : 2,
					args: [node, parent],
				}),
			);
		}

		return steps;
	}
}

The CallMethodStep class is less common and is used to tell ESLint to call a specific method on the rule. The constructor accepts two arguments:

  • target - the name of the method to call, frequently beginning with "on" such as "onCodePathStart".
  • args - an array of arguments to pass to the method.

For example:

import { VisitNodeStep, CallMethodStep } from "@eslint/plugin-kit";

class MySourceCode {

    traverse() {

        const steps = [];

        for (const { node, parent, phase } of iterator(this.ast)) {
            steps.push(
                new VisitNodeStep({
                    target: node,
                    phase: phase === "enter" ? 1 : 2,
                    args: [node, parent],
                }),
            );

            // call a method indicating how many times we've been through the loop
            steps.push(
                new CallMethodStep({
                    target: "onIteration",
                    args: [steps.length]
                });
            )
        }

        return steps;
    }
}

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:

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:

  • lines - an array of text lines that is created automatically when the constructor is called.
  • getLoc(node) - gets the location of a node. Works for nodes that have the ESLint-style loc property and nodes that have the Unist-style position property. If you're using an AST with a different location format, you'll still need to implement this method yourself.
  • getRange(node) - gets the range of a node within the source text. Works for nodes that have the ESLint-style range property and nodes that have the Unist-style position property. If you're using an AST with a different range format, you'll still need to implement this method yourself.
  • getText(nodeOrToken, charsBefore, charsAfter) - gets the source text for the given node or token that has range information attached. Optionally, can return additional characters before and after the given node or token. As long as getRange() is properly implemented, this method will just work.
  • getAncestors(node) - returns the ancestry of the node. In order for this to work, you must implement the getParent() method yourself.

Here's an example:

import { TextSourceCodeBase } from "@eslint/plugin-kit";

export class MySourceCode extends TextSourceCodeBase {
	#parents = new Map();

	constructor({ ast, text }) {
		super({ ast, text });
	}

	getParent(node) {
		return this.#parents.get(node);
	}

	traverse() {
		const steps = [];

		for (const { node, parent, phase } of iterator(this.ast)) {
			//save the parent information
			this.#parent.set(node, parent);

			steps.push(
				new VisitNodeStep({
					target: node,
					phase: phase === "enter" ? 1 : 2,
					args: [node, parent],
				}),
			);
		}

		return steps;
	}
}

In general, it's safe to collect the parent information during the traverse() method as getParent() and getAncestor() will only be called from rules once the AST has been traversed at least once.

License

Apache 2.0

Sponsors

The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. Become a Sponsor to get your logo on our READMEs and website.