{"_id":"@elsikora/eslint-plugin-kit","name":"@elsikora/eslint-plugin-kit","dist-tags":{"latest":"0.2.7"},"versions":{"0.2.7":{"name":"@elsikora/eslint-plugin-kit","version":"0.2.7","description":"Utilities for building ESLint plugins.","author":{"name":"Nicholas C. Zakas"},"type":"module","main":"dist/esm/index.js","types":"dist/esm/index.d.ts","exports":{"require":{"types":"./dist/cjs/index.d.cts","default":"./dist/cjs/index.cjs"},"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"}},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/eslint/rewrite.git"},"bugs":{"url":"https://github.com/eslint/rewrite/issues"},"homepage":"https://github.com/eslint/rewrite#readme","scripts":{"build:dedupe-types":"node ../../tools/dedupe-types.js dist/cjs/index.cjs dist/esm/index.js","build:cts":"node ../../tools/build-cts.js dist/esm/index.d.ts dist/cjs/index.d.cts","build":"rollup -c && npm run build:dedupe-types && tsc -p tsconfig.esm.json && npm run build:cts","pretest":"npm run build","test":"mocha tests/","test:coverage":"c8 npm test","test:jsr":"npx jsr@latest publish --dry-run","test:types":"tsc -p tests/types/tsconfig.json"},"keywords":["eslint","eslintplugin","eslint-plugin"],"license":"Apache-2.0","dependencies":{"@eslint/core":"^0.12.0","levn":"^0.4.1"},"devDependencies":{"@types/levn":"^0.4.0","c8":"^9.1.0","mocha":"^10.4.0","rollup":"^4.16.2","rollup-plugin-copy":"^3.5.0","typescript":"^5.4.5"},"engines":{"node":"^18.18.0 || ^20.9.0 || >=21.1.0"},"_id":"@elsikora/eslint-plugin-kit@0.2.7","gitHead":"a48aa6ac192f1c2d1fd9a1ed396a57919b24a25e","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-aoFkTR/EO8pJpB9hgFlU9OcAFMxiBW9GjZKyksAIqPxREVJ1MG0Sq2bEOYHlnQMsqpStHLUTS+Jd40Y3LPkG2Q==","shasum":"e8c8e66939d8f356e532629213b60962212cc341","tarball":"https://registry.npmjs.org/@elsikora/eslint-plugin-kit/-/eslint-plugin-kit-0.2.7.tgz","fileCount":7,"unpackedSize":76033,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCW1AgyX9qKXBytoFQ7E6sVoJjiOVKgJaPFpohE095sHwIgALZWqNl76ACU6qQxIYyq0sY5qi222XkTPxE8OKwfRhU="}]},"_npmUser":{"name":"admin-elsikora","email":"admin@elsikora.com"},"directories":{},"maintainers":[{"name":"dmitryv_devops","email":"dmitry@elsikora.com"},{"name":"michail-elsikora","email":"michail.developer@elsikora.com"},{"name":"admin-elsikora","email":"admin@elsikora.com"},{"name":"dmitry-elsikora","email":"dmitry.developer@elsikora.com"},{"name":"aleksandr-elsikora","email":"alexander.frontend@elsikora.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/eslint-plugin-kit_0.2.7_1741117278673_0.9791437233339284"},"_hasShrinkwrap":false}},"time":{"created":"2025-03-04T19:41:18.593Z","0.2.7":"2025-03-04T19:41:18.890Z","modified":"2025-03-04T19:41:19.276Z"},"maintainers":[{"name":"dmitryv_devops","email":"dmitry@elsikora.com"},{"name":"michail-elsikora","email":"michail.developer@elsikora.com"},{"name":"admin-elsikora","email":"admin@elsikora.com"},{"name":"dmitry-elsikora","email":"dmitry.developer@elsikora.com"},{"name":"aleksandr-elsikora","email":"alexander.frontend@elsikora.com"}],"description":"Utilities for building ESLint plugins.","homepage":"https://github.com/eslint/rewrite#readme","keywords":["eslint","eslintplugin","eslint-plugin"],"repository":{"type":"git","url":"git+https://github.com/eslint/rewrite.git"},"author":{"name":"Nicholas C. Zakas"},"bugs":{"url":"https://github.com/eslint/rewrite/issues"},"license":"Apache-2.0","readme":"# ESLint Plugin Kit\n\n## Description\n\nA collection of utilities to help build ESLint plugins.\n\n## Installation\n\nFor Node.js and compatible runtimes:\n\n```shell\nnpm install @eslint/plugin-kit\n# or\nyarn add @eslint/plugin-kit\n# or\npnpm install @eslint/plugin-kit\n# or\nbun install @eslint/plugin-kit\n```\n\nFor Deno:\n\n```shell\ndeno add @eslint/plugin-kit\n```\n\n## Usage\n\nThis package exports the following utilities:\n\n- `ConfigCommentParser` - used to parse ESLint configuration comments (i.e., `/* eslint-disable rule */`)\n- `VisitNodeStep` and `CallMethodStep` - used to help implement `SourceCode#traverse()`\n- `Directive` - used to help implement `SourceCode#getDisableDirectives()`\n- `TextSourceCodeBase` - base class to help implement the `SourceCode` interface\n\n### `ConfigCommentParser`\n\nTo use the `ConfigCommentParser` class, import it from the package and create a new instance, such as:\n\n```js\nimport { ConfigCommentParser } from \"@eslint/plugin-kit\";\n\n// create a new instance\nconst commentParser = new ConfigCommentParser();\n\n// pass in a comment string without the comment delimiters\nconst directive = commentParser.parseDirective(\n\t\"eslint-disable prefer-const, semi -- I don't want to use these.\",\n);\n\n// will be undefined when a directive can't be parsed\nif (directive) {\n\tconsole.log(directive.label); // \"eslint-disable\"\n\tconsole.log(directive.value); // \"prefer-const, semi\"\n\tconsole.log(directive.justification); // \"I don't want to use these\"\n}\n```\n\nThere are different styles of directive values that you'll need to parse separately to get the correct format:\n\n```js\nimport { ConfigCommentParser } from \"@eslint/plugin-kit\";\n\n// create a new instance\nconst commentParser = new ConfigCommentParser();\n\n// list format\nconst list = commentParser.parseListConfig(\"prefer-const, semi\");\nconsole.log(Object.entries(list)); // [[\"prefer-const\", true], [\"semi\", true]]\n\n// string format\nconst strings = commentParser.parseStringConfig(\"foo:off, bar\");\nconsole.log(Object.entries(strings)); // [[\"foo\", \"off\"], [\"bar\", null]]\n\n// JSON-like config format\nconst jsonLike = commentParser.parseJSONLikeConfig(\n\t\"semi:[error, never], prefer-const: warn\",\n);\nconsole.log(Object.entries(jsonLike.config)); // [[\"semi\", [\"error\", \"never\"]], [\"prefer-const\", \"warn\"]]\n```\n\n### `VisitNodeStep` and `CallMethodStep`\n\nThe `VisitNodeStep` and `CallMethodStep` classes represent steps in the traversal of source code. They implement the correct interfaces to return from the `SourceCode#traverse()` method.\n\nThe `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:\n\n- `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).\n- `phase` - either 1 for enter or 2 for exit.\n- `args` - an array of arguments to pass into the visitor method of a rule.\n\nFor example:\n\n```js\nimport { VisitNodeStep } from \"@eslint/plugin-kit\";\n\nclass MySourceCode {\n\ttraverse() {\n\t\tconst steps = [];\n\n\t\tfor (const { node, parent, phase } of iterator(this.ast)) {\n\t\t\tsteps.push(\n\t\t\t\tnew VisitNodeStep({\n\t\t\t\t\ttarget: node,\n\t\t\t\t\tphase: phase === \"enter\" ? 1 : 2,\n\t\t\t\t\targs: [node, parent],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn steps;\n\t}\n}\n```\n\nThe `CallMethodStep` class is less common and is used to tell ESLint to call a specific method on the rule. The constructor accepts two arguments:\n\n- `target` - the name of the method to call, frequently beginning with `\"on\"` such as `\"onCodePathStart\"`.\n- `args` - an array of arguments to pass to the method.\n\nFor example:\n\n```js\nimport { VisitNodeStep, CallMethodStep } from \"@eslint/plugin-kit\";\n\nclass MySourceCode {\n\n    traverse() {\n\n        const steps = [];\n\n        for (const { node, parent, phase } of iterator(this.ast)) {\n            steps.push(\n                new VisitNodeStep({\n                    target: node,\n                    phase: phase === \"enter\" ? 1 : 2,\n                    args: [node, parent],\n                }),\n            );\n\n            // call a method indicating how many times we've been through the loop\n            steps.push(\n                new CallMethodStep({\n                    target: \"onIteration\",\n                    args: [steps.length]\n                });\n            )\n        }\n\n        return steps;\n    }\n}\n```\n\n### `Directive`\n\nThe `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:\n\n```js\nimport { Directive, ConfigCommentParser } from \"@eslint/plugin-kit\";\n\nclass MySourceCode {\n\tgetDisableDirectives() {\n\t\tconst directives = [];\n\t\tconst problems = [];\n\t\tconst commentParser = new ConfigCommentParser();\n\n\t\t// read in the inline config nodes to check each one\n\t\tthis.getInlineConfigNodes().forEach(comment => {\n\t\t\t// Step 1: Parse the directive\n\t\t\tconst { label, value, justification } =\n\t\t\t\tcommentParser.parseDirective(comment.value);\n\n\t\t\t// Step 2: Extract the directive value and create the `Directive` object\n\t\t\tswitch (label) {\n\t\t\t\tcase \"eslint-disable\":\n\t\t\t\tcase \"eslint-enable\":\n\t\t\t\tcase \"eslint-disable-next-line\":\n\t\t\t\tcase \"eslint-disable-line\": {\n\t\t\t\t\tconst directiveType = label.slice(\"eslint-\".length);\n\n\t\t\t\t\tdirectives.push(\n\t\t\t\t\t\tnew Directive({\n\t\t\t\t\t\t\ttype: directiveType,\n\t\t\t\t\t\t\tnode: comment,\n\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\tjustification,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// ignore any comments that don't begin with known labels\n\t\t\t}\n\t\t});\n\n\t\treturn {\n\t\t\tdirectives,\n\t\t\tproblems,\n\t\t};\n\t}\n}\n```\n\n### `TextSourceCodeBase`\n\nThe `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:\n\n- `lines` - an array of text lines that is created automatically when the constructor is called.\n- `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](https://github.com/syntax-tree/unist?tab=readme-ov-file#position). If you're using an AST with a different location format, you'll still need to implement this method yourself.\n- `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](https://github.com/syntax-tree/unist?tab=readme-ov-file#position). If you're using an AST with a different range format, you'll still need to implement this method yourself.\n- `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.\n- `getAncestors(node)` - returns the ancestry of the node. In order for this to work, you must implement the `getParent()` method yourself.\n\nHere's an example:\n\n```js\nimport { TextSourceCodeBase } from \"@eslint/plugin-kit\";\n\nexport class MySourceCode extends TextSourceCodeBase {\n\t#parents = new Map();\n\n\tconstructor({ ast, text }) {\n\t\tsuper({ ast, text });\n\t}\n\n\tgetParent(node) {\n\t\treturn this.#parents.get(node);\n\t}\n\n\ttraverse() {\n\t\tconst steps = [];\n\n\t\tfor (const { node, parent, phase } of iterator(this.ast)) {\n\t\t\t//save the parent information\n\t\t\tthis.#parent.set(node, parent);\n\n\t\t\tsteps.push(\n\t\t\t\tnew VisitNodeStep({\n\t\t\t\t\ttarget: node,\n\t\t\t\t\tphase: phase === \"enter\" ? 1 : 2,\n\t\t\t\t\targs: [node, parent],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn steps;\n\t}\n}\n```\n\nIn 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.\n\n## License\n\nApache 2.0\n\n<!-- NOTE: This section is autogenerated. Do not manually edit.-->\n<!--sponsorsstart-->\n\n## Sponsors\n\nThe following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)\nto get your logo on our READMEs and [website](https://eslint.org/sponsors).\n\n<h3>Platinum Sponsors</h3>\n<p><a href=\"https://automattic.com\"><img src=\"https://images.opencollective.com/automattic/d0ef3e1/logo.png\" alt=\"Automattic\" height=\"128\"></a> <a href=\"https://www.airbnb.com/\"><img src=\"https://images.opencollective.com/airbnb/d327d66/logo.png\" alt=\"Airbnb\" height=\"128\"></a></p><h3>Gold Sponsors</h3>\n<p><a href=\"https://qlty.sh/\"><img src=\"https://images.opencollective.com/qltysh/33d157d/logo.png\" alt=\"Qlty Software\" height=\"96\"></a> <a href=\"https://trunk.io/\"><img src=\"https://images.opencollective.com/trunkio/fb92d60/avatar.png\" alt=\"trunk.io\" height=\"96\"></a></p><h3>Silver Sponsors</h3>\n<p><a href=\"https://vite.dev/\"><img src=\"https://images.opencollective.com/vite/e6d15e1/logo.png\" alt=\"Vite\" height=\"64\"></a> <a href=\"https://www.jetbrains.com/\"><img src=\"https://images.opencollective.com/jetbrains/fe76f99/logo.png\" alt=\"JetBrains\" height=\"64\"></a> <a href=\"https://liftoff.io/\"><img src=\"https://images.opencollective.com/liftoff/5c4fa84/logo.png\" alt=\"Liftoff\" height=\"64\"></a> <a href=\"https://americanexpress.io\"><img src=\"https://avatars.githubusercontent.com/u/3853301\" alt=\"American Express\" height=\"64\"></a> <a href=\"https://stackblitz.com\"><img src=\"https://avatars.githubusercontent.com/u/28635252\" alt=\"StackBlitz\" height=\"64\"></a></p><h3>Bronze Sponsors</h3>\n<p><a href=\"https://cybozu.co.jp/\"><img src=\"https://images.opencollective.com/cybozu/933e46d/logo.png\" alt=\"Cybozu\" height=\"32\"></a> <a href=\"https://www.crosswordsolver.org/anagram-solver/\"><img src=\"https://images.opencollective.com/anagram-solver/2666271/logo.png\" alt=\"Anagram Solver\" height=\"32\"></a> <a href=\"https://icons8.com/\"><img src=\"https://images.opencollective.com/icons8/7fa1641/logo.png\" alt=\"Icons8\" height=\"32\"></a> <a href=\"https://discord.com\"><img src=\"https://images.opencollective.com/discordapp/f9645d9/logo.png\" alt=\"Discord\" height=\"32\"></a> <a href=\"https://www.gitbook.com\"><img src=\"https://avatars.githubusercontent.com/u/7111340\" alt=\"GitBook\" height=\"32\"></a> <a href=\"https://nolebase.ayaka.io\"><img src=\"https://avatars.githubusercontent.com/u/11081491\" alt=\"Neko\" height=\"32\"></a> <a href=\"https://nx.dev\"><img src=\"https://avatars.githubusercontent.com/u/23692104\" alt=\"Nx\" height=\"32\"></a> <a href=\"https://opensource.mercedes-benz.com/\"><img src=\"https://avatars.githubusercontent.com/u/34240465\" alt=\"Mercedes-Benz Group\" height=\"32\"></a> <a href=\"https://herocoders.com\"><img src=\"https://avatars.githubusercontent.com/u/37549774\" alt=\"HeroCoders\" height=\"32\"></a> <a href=\"https://www.lambdatest.com\"><img src=\"https://avatars.githubusercontent.com/u/171592363\" alt=\"LambdaTest\" height=\"32\"></a></p>\n<h3>Technology Sponsors</h3>\nTechnology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.\n<p><a href=\"https://netlify.com\"><img src=\"https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg\" alt=\"Netlify\" height=\"32\"></a> <a href=\"https://algolia.com\"><img src=\"https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg\" alt=\"Algolia\" height=\"32\"></a> <a href=\"https://1password.com\"><img src=\"https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg\" alt=\"1Password\" height=\"32\"></a></p>\n<!--sponsorsend-->\n","readmeFilename":"README.md"}