DEV Community

Cover image for Publishing ESLint rules to npm using pnpm monorepo
Neeraj Lagwankar
Neeraj Lagwankar

Posted on • Originally published at flashblaze.xyz

Publishing ESLint rules to npm using pnpm monorepo

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/*" 
Enter fullscreen mode Exit fullscreen mode

package.json

{ "name": "your-eslint-configs", "version": "1.0.0", "description": "Common ESLint configs", "license": "MIT", "keywords": [ "eslint", "eslint-config" ] } 
Enter fullscreen mode Exit fullscreen mode

.npmrc

auto-install-peers=true use-lockfile-v6=true 
Enter fullscreen mode Exit fullscreen mode

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', }, }; 
Enter fullscreen mode Exit fullscreen mode

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" } } 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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'] }], }, }; 
Enter fullscreen mode Exit fullscreen mode

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:^" } } 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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.

  1. Go into the created project, and run pnpm add -D <path-to-your-directory>/packages/react to install it from the packages dir.
  2. Add it to .eslintrc.js like so:

.eslintrc.js

module.exports = { extends: ['@your-org/eslint-config-react'], }; 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

If you want to see how Codebuddy has implemented the same for Next.js and TypeScript, do check it out on GitHub

Top comments (0)