Neeraj Lagwankar
Posted on January 13, 2024
Background
At work (Codebuddy), we wanted to create shareable ESLint rules to be used across the projects instead of copy pasting the rules each time. This can be achieved by following the Official shareable docs, but I wanted to outline how to achieve the same using a monorepo and also mention some issues which I faced along the way.
Creating packages
This setup has been inspired by antfu's eslint-config where he too has used a monorepo. Let us first begin by creating a pnpm workspace by creating these files
pnpm-workspace.yaml
packages:
- "packages/*"
package.json
{
"name": "your-eslint-configs",
"version": "1.0.0",
"description": "Common ESLint configs",
"license": "MIT",
"keywords": [
"eslint",
"eslint-config"
]
}
.npmrc
auto-install-peers=true
use-lockfile-v6=true
Then create a packages directory with all the necessary packages. For now we will be creating 2 packages: base
and react
:
base
index.js
module.exports = {
env: {
es2021: true,
browser: true,
node: true,
},
extends: [
'eslint:recommended',
'airbnb',
'plugin:import/recommended',
'plugin:promise/recommended',
// This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.
// Make sure it's always the last config, so it gets the chance to override other configs.
'plugin:prettier/recommended',
'prettier',
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],
'max-len': 'off',
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'always'],
'object-curly-newline': 'off',
'arrow-parens': 'off',
'implicit-arrow-linebreak': 'off',
'no-nested-ternary': 'off',
'nonblock-statement-body-position': ['error', 'any'],
camelcase: 'error',
'consistent-return': 0,
'no-restricted-syntax': 'off',
'no-console': ['error', { allow: ['warn', 'error'] }],
'arrow-body-style': ['error', 'as-needed'],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'no-shadow': 'error',
'no-underscore-dangle': 'off',
},
};
package.json
{
"name": "@your-org/eslint-config-base",
"version": "1.0.0",
"description": "Common JS ESLint rules",
"license": "MIT",
"main": "index.js",
"keywords": [
"eslint",
"eslint-config"
],
"peerDependencies": {
"eslint": ">=8",
"eslint-config-airbnb": ">=19.0.4",
"eslint-plugin-import": ">=2.29.1",
"eslint-plugin-prettier": ">=5.1.2",
"eslint-plugin-promise": ">=6.1.1"
},
"engines": {
"node": ">=18"
}
}
If you check the name
field, we're scoping it to an org name.
After installing the packages by running pnpm i
from the root of the directory, below is the directory structure:
node_modules
packages/
└── base/
├── index.js
└── package.json
.npmrc
package.json
pnpm-lock.json
pnpm-workspace.yaml
Use the above base into react
package:
react
index.js
module.exports = {
extends: [
'plugin:react/recommended',
'plugin:jsx-a11y/recommended',
'@your-org/eslint-config-base',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react'],
rules: {
'react/function-component-definition': [
2,
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
'react/prop-types': 'off',
'react/no-unstable-nested-components': 'off',
'react/react-in-jsx-scope': 'off',
'react/require-default-props': 'off',
'react/jsx-props-no-spreading': 'off',
'import/no-extraneous-dependencies': ['error', { devDependencies: ['vite.config.js'] }],
},
};
In the extends array, put the config at the last so that it overrides the rules. If you have included a plugin in an earlier config, do not include it in the plugins
array else, it will cause issues while linting.
To use @your-org/eslint-config-base
, add it as a dependency. To do so, go into packages/react
and run pnpm add @your-org/eslint-config-base
. This will automatically install the package from the workspace. Below is the package.json
:
react/package.json
{
"name": "@your-org/eslint-config-react",
"version": "1.0.0",
"description": "Common ESLint rules for React with Vite",
"license": "MIT",
"main": "index.js",
"keywords": [
"eslint",
"eslint-config"
],
"peerDependencies": {
"eslint": ">=8",
"eslint-plugin-jsx-a11y": ">=6.8.0",
"eslint-plugin-react": ">=7.33.2"
},
"engines": {
"node": ">=18"
},
"dependencies": {
"@your-org/eslint-config-base": "workspace:^"
}
}
And the updated directory structure:
node_modules
packages/
├── base/
│ ├── index.js
│ └── package.json
└── react/
├── index.js
└── package.json
.npmrc
package.json
pnpm-lock.json
pnpm-workspace.yaml
Using the packages in a project to test it
Now that the configs are created, next step would be to use them in a project and see whether they are working or not. To do so, create a Vite project with React template. To link the package, check the commands here or follow the below commands.
- Go into the created project, and run
pnpm add -D <path-to-your-directory>/packages/react
to install it from the packages dir. - Add it to
.eslintrc.js
like so:
.eslintrc.js
module.exports = {
extends: ['@your-org/eslint-config-react'],
};
Publishing the packages
To publish the packages, go into each package and run pnpm publish --access public .
If everything is set up correctly, then it will be published 🥳. Below are some issues which I faced while publishing. Do check if you run into any of the issues
I tried running npm adduser
and npm login
, however I got these errors
> npm login
npm notice Log in on https://registry.npmjs.org/
Login at:
https://www.npmjs.com/login?next=/login/cli/c2db3009-70e8-41ea-9194-04abd03d7c5c
Press ENTER to open in the browser...
npm ERR! code ERR_INVALID_ARG_TYPE
npm ERR! The "file" argument must be of type string. Received undefined
npm ERR! Cannot read properties of undefined (reading 'stdin')
npm ERR! A complete log of this run can be found in:
npm ERR! /home/neeraj/.npm/_logs/2023-12-11T16_39_39_060Z-debug-0.log
Fortunately I found this Solution. Check if .npmrc
exists if it exsist, then add the below line to it else create it and add the below line to it:
//registry.npmjs.org/:_authToken=token_here
If you want to see how Codebuddy has implemented the same for Next.js and TypeScript, do check it out on GitHub
Posted on January 13, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.