How to create a custom ESLint configuration and shareable plugin? PART-1

Mohammad Nadeem
Geek Culture
Published in
8 min readJul 5, 2021

My first article on Medium. Suggestions, appreciations, criticism are welcome, ‘coz that’s how we learn. 😇

Image Source: https://timesofindia.indiatimes.com/

Developers do write a lot of codes, and managing large codes is easier when the codes are formatted in a good way. A well-formatted code is easy to read, maintain and debug. We use plugins that make our formatting jobs easier. One such plugin is ESLint which helps Javascript developers to format the code easily.

ESLint comes with a lot of built-in configurations which can be used hassle-free. There are times we may need to have our own custom rules. In this article, I’ll help you understand how we can write custom ESLint rules/configurations.

Let’s consider a rule where we restrict the use of color literals within Javascript files(or React components) and let’s name the rule as:

no-color-literals

We added this rule to avoid writing color properties at the component level stylings in our React application. So, we’ll consider adding the rule for files having extensions .js and .jsx

This is PART-1, where we will create our custom ESLint configuration. Stay tuned for PART-2, where we’ll see how to make this configuration a shareable ESLint plugin and install across JS projects.

See to PART-2 here.

STEP 1:

Using Abstract Syntax Tree

Before we write our custom ESLint rules, we must understand how our code is structured. For this, we’ll take the help of ASTExplorer. AST Explorer helps us to analyze the structure of our code. We’ll paste a small code snippet containing our style object which will cover both React’s JSX syntaxes as well as VanillaJS files.

const pageStyles = {
textColor: {
color:'#123',
},
};
const MyComp = ({})=>{
return (
<div
style={{background:'black'}}
>
</div>
);
};

The above code has two parts, one containing a style object pageStyles object which has another object, textColor containing key-value pair.

The next part is a simple React component, having an inline style object, again containing key-value pair.

The key is a Javascript-style property, and the value is a hex-color-code/color value.

Let’s paste the above code snippet in the ASTExplorer and check how our code is structured.

Image 1: Style property node in AST

Notice here, at the right, texts in the blue color, Property, Identifier, Literal are all nodes.

Hovering over Property node(at right) highlights color: ‘#123'. Here, color is an Identifier, and ‘#123’ is a Literal.

This is the same for the inline style object as well. Check the below image:

Image 2: Style property node in AST

We’ll be needing these details later. Now let’s set up our environment to write and test the configuration.

STEP 2:

Setting up the environment to write and test the configuration

  1. Create a folder eslint-custom-config and open with your favorite code editor.
  2. Inside the eslint-custom-config folder, run the command
    npm init -y to create a package.jsonfile
  3. Install ESLint locally using the command npm i eslint
    Additionally, you can install ESLlint globally using npm i -g eslint
  4. In the same folder, create a file with the name .eslintrc.js and paste the following configurations:
module.exports = {
'env': {
'browser': true,
'commonjs': true,
'es2021': true
},
'extends': 'eslint:recommended',
'parserOptions': {
'ecmaVersion': 12
},
'rules': {
'indent': ['error', 4],
'linebreak-style': ['error','unix'],
'quotes': ['error','single'],
'semi': ['error','always']
}
};

5. To create an ESLint manual rule, create a folder rules and inside the rules folder, create a file named no-color-literals.js
6. Let’s create another folder src which will be holding the files we need to test.
7. Create a file test.js inside the src folder, and paste the below contents:

const pageStyles = {
textColor: {
color: '#333'
}
};
console.log(pageStyles);

So far our folder structure should look like this:

Image 3: Folder structure

8. To run the lint check, let’s set up the script. Navigate to package.json and add "lint":"node_modules/.bin/eslint src --ruledir rules" under the scripts. The package.json should look something like this:

Image 4: package.json

This will check for linting errors in the files inside thesrc folder with no-color-literals rule.

9. In the no-color-literals.js file, paste the following code:

module.exports = {
create(context){
return {
// our logic goes here

Property(node){
/* node refers to the Property node from Abstract Syntax Tree that we saw on ASTExplorer.
node.key refers to Identifier and node.value refers to Literals
*/
if(!node){ // if node doesn't exist, no need to check
return;
}

if(
node.key.name == 'color' && node.value.value.toString().includes('#')
){
/* if key is 'color' and value specified includes '#', report the error */

context.report({
node: node.value,
data: {
name: node.value.value
},
message: "Unexpected: '{{name}}'. Color literals are not allowed",
});
}
}
}
}
}

The context.report method takes an object with few properties and reports the error whenever the lint rule fails.

node is the exact node(location) where the lint error has occurred.

data is used to pass some data. Here, we are having name property, which holds the value of the color property

message is used to pass some information so that the developer knows exactly what is expected to be done.

We have successfully created our configuration to check if the color property is taking any hex color code as color literal. We’ll modify our logic later to cover more scenarios, but first, let’s implement this rule in our .eslintrc.js file.

12. Now that we have created our custom configuration, let’s plug it in with the ESLint configuration file. In the .eslintrc.js file, under the rules add our custom rules as "no-color-literals": "error" as below:

Image 5: .eslintrc.js

11. Time to test:

Run the command npm run lint in the root directory, you should see the error in the terminal.

Image 6: Trial 1

As of now, we have restricted the use of hex-color codes for the color property. The logic we have written is very crude, and it needs to be refined.

STEP 3:
Modifying our logic to restrict the usage of all color literals (hex, rgb, hsl, white, black, etc) in all Javascript style properties accepting colors(ex: border, background, color, etc)

  1. Collecting all the style properties:
const PROPERTIES = ['background', 'backgroundColor', 'border', 'borderColor', 'borderBottom', 'borderBottomColor', 'borderLeft', 'borderLeftColor', 'borderRightColor', 'borderRight', 'borderTop', 'borderTopColor', 'color' ];

2. Collecting all the possible color literals:

const COLORS = ['black', 'silver', 'gray', 'white', 'maroon', 'red', 'purple', 'fuchsia', 'green', 'lime', 'olive', 'aqua', 'teal', 'blue', 'navy', 'yellow', 'orange', 'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque', 'blanchedalmond', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen', 'linen', 'magenta', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown','salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet','wheat', 'whitesmoke', 'yellowgreen', 'rebeccapurple','#', 'rgb', 'rgba', 'hsl', 'hsla'
]

3. Creating a utility method to check:

a. If the property used resides in the PROPERTIES array.

function checkProperties(propertyName) {
if (propertyName) {
return PROPERTIES.some((property) => property === propertyName);
}
return false;
}

b. if the value is a hex-code(containing #), or rgb, rgba, hsl, hsla, or any other color value.

function checkValues(value) {
if (value) {
return COLORS.some((colorFormat) => value.toString().includes(colorFormat));
}
return false;
}

4. Re-writing our logic:

Property(node) {
if (!node || !node.key || !node.value) {
return;
}
const colorPropertyMatched = checkProperties(node.key.name);
const valueHasColorLiterals = checkValues(node.value.value);

if (colorPropertyMatched && valueHasColorLiterals) {
context.report({
node: node.value,
message: "Unexpected: '{{name}}'. Color literals are not allowed",
data: {
name: node.value.value
}
});
}
}

colorPropertyMatched : When the style property exists in the PROPERTIES array, it will hold the value true

valueHasColorLiterals : When the value specified to the style object is one of a kind mentioned in the COLORS array, it will hold the value true

In case both colorPropertyMatched and valueHasColorLiterals are true, we have a Property node, having key as Javascript style property with a value containing color literals. In this case, ESLint will report us the error with the message that we added already.

As of now, the no-color-lierals.js file would look like this:

Image 7: a. no-color-literals.js
Image 8: b. no-color-literals.js

Let’s modify the style object in the test.js file to test for few more scenarios:

const pageStyles = {
textColor: {
color: '#333',
background:'white',
borderTop:'1px solid rgb(255, 0, 0)',
borderBottom:'1px solid hsl(0, 100%, 50%)',
}
};
console.log(pageStyles);

Now run the command npm run lint to see the magic!

Image 9: Trial 2

Voila! We have successfully added our lint rule to restrict color literals in our Javascript style properties. This will not affect your css or scss files.

Final Step (optional)

To add this rule for files with jsx extension, we need to tweak our lint script a bit in the pacakge.json file.

"lint": "node_modules/.bin/eslint src --rulesdir rules --ext js,jsx"

Now we can add a dummy React component having inline style, and test the lint rule.

How to create a custom ESLint configuration and shareable plugin? PART-2

--

--