Creating custom eslint rules can often come handy. I enjoy using typescript, so I decided I'd share a way how to use its superpowers to create custom eslint rules.
Step one
Create a repo and initialise an npm package in it:
mkdir my-awesome-eslint-rule && cd my-awesome-eslint-rule && npm init -y
Make sure your package name starts with eslint-plugin-
, 'cos this is the way, i.e.:
{
"name": "eslint-plugin-my-awesome-rule",
...
Install the dependencies (we won't touch @types/estree in this tutorial, but it's quite handy for types when writing eslint rules, so I keep it here):
npm i -D @types/eslint @types/estree @types/node tsup typescript
Basically we're installing three packages with types definitions, typescript and a the simplest and fastest way to bundle your TypeScript libraries :)
Step two
We'll be seriously developing, so we'll have a src
and dist
folders, for the source and produced output respectively, so let's configure our package.json
accordingly:
{
"name": "eslint-plugin-my-awesome-rule",
"main": "./dist/index.js",
"files": [
"dist/**"
],
...
Now let's create the src
folder with an index.ts
file as the entry point to our future rule:
module.exports = {
rules: {
// our rules will go here
},
};
Step three
Let's create a silly simple rule in our src
folder. Note, that a rule is a function that accepts context
and returns rule listener. Both these types we can get from the eslint
.
For the sake of demonstration let's create a rule that questions every import:
// ./src/my-awesome-rule.ts
import { Rule } from "eslint";
export function myAwesomeRule(context: Rule.RuleContext): Rule.RuleListener {
return {
ImportDeclaration(node) {
context.report({
node,
message: 'Are you sure about this?'
})
}
}
}
Now let's import it into our entry file and plug it in:
// ./src/index.ts
import { myAwesomeRule } from './my-awesome-rule';
module.exports = {
rules: {
'my-silly-rule': {
create: myAwesomeRule,
},
},
};
Step four
Let's transpile it to js to make it usable. Add a build command to the scripts section in the package.json
file. We'll be using tsup, which is absolutely awesome for this:
...
"scripts": {
"build": "tsup src/index.ts --no-splitting --minify"
},
...
Run the command to get a minified tiny version of your rule in dist
folder.
Step five
Install and enjoy, you can install it directly from the dist
folder, run npm pack
to create an archive with your new rule or publish it to npm with npm publish
.
After installing, don't forget to plug it into your .eslintrc
, by adding it to the plugins section and activating its rule:
{
"plugins": ["my-awesome-rule"],
"rules": {
"my-awesome-rule/my-silly-rule": "warn"
}
...
Have fun :)
Top comments (4)
I also had to add meta object
Reviewing the article, I now think it'd be more neat to have the
module.exports
file a bit simpler, so the rules property looks like'my-silly-rule': myAwesomeRule
, so thatmyAwesomeRule
completely encapsulates everything related to the rule, not only the create property, while the exports file is just for mapping of string selectors to rules.But yea, the main point is, with typescript it is less cryptic what should be there :)
Very helpful, thanks.
Btw, there is a typo in
import { myAwesomeRule } from './my-aswesome-rule';
Fixed, thanks! :D