Write project-specific rules, in your own eslint config file.
npm install eslint-plugin-wrapper
In your .eslintrc.js
file:
const wrapper = require('eslint-plugin-wrapper')
wrapper.addPlugins({
'my-project': {
rules: {
'no-literals': {
create: context => {
return {
Literal: node => {
context.report({
message: `Don't use literals for some reason!`,
node,
})
}
}
}
}
}
}
})
module.exports = {
plugins: ['wrapper'],
extends: ['plugin:wrapper/all'],
}
Your codebase will now be linted with the wrapper/no-literals
rule.
You can also use this to wrap external eslint plugins and configs. This is essentially a workaround to eslint imposing awkward peer dependency requirements on plugins and configs, which is legal now, until supported in eslint.
For example, you could create a package internal to your company, say called @yourcompany/eslint-plugin'
. Then, in its main
module:
const {EslintPluginWrapper} = require('eslint-plugin-wrapper')
const wrapper = new EslintPluginWrapper({pluginName: '@yourcompany'})
wrapper.addPlugins({
unicorn: require('eslint-plugin-unicorn'),
})
wrapper.addPlugins({
'config:xo': {configs: {recommended: require('eslint-config-xo')}},
})
wrapper.addPlugins({
default: {
rules: {
'no-literals': ...,
},
configs: {
recommended: {
plugins: ['@yourcompany'],
extends: ['plugin:@yourcompany/recommended'],
rules: {
'@yourcompany/default/no-literals': 'error',
'@yourcompany/unicorn/no-nested-ternary': 'off',
}
}
}
}
})
module.exports = wrapper
Then in a downstream project, you only need one eslint plugin dependency, @yourcompany/eslint-plugin
, which in this case will give you all of the eslint-plugin-unicorn
rules, and all of the eslint-config-xo
recommendations:
module.exports = require('@yourcompany/eslint-plugin').plugins.default.configs.recommended
Some notes on the above config:
- the "recommended" config will merge the "recommended" rules in all internal plugins - so in this case, the recommended rules for eslint-plugin-unicorn.
wrapper.addPlugins({ ... })
is also being used to add configs. Since plugins are allowed to contain configs, we can just use a convention of aconfig:
prefix and use the same method.- the
default
plugin defines project-specific rules, and overrides for recommended configs for external libraries. You can customise this to your heart's content.
Note that some other workarounds exist, but they require shimming, e.g. @rushstack/eslint-config. It gets unclear where the patch to eslint's weird module resolution should happen, or how it works. There is a bit less magic in this library. Basically, you only need one plugin, the wrapper. And the wrapper just-so-happens to rely on some other node libraries to implement its rules (and those node libraries just-so-happen to be eslint plugins themselves).
You could use this to pick rules from different published versions of a single library. In package.json:
"dependencies": {
...,
"eslint-plugin-unicorn_37": "npm:[email protected]",
"eslint-plugin-unicorn_39": "npm:[email protected]"
}
Then in .eslintrc.js
:
const unicorn37 = require('eslint-plugin-unicorn_37')
const unicorn39 = require('eslint-plugin-unicorn_39')
wrapper.addPlugin({
unicorn: {
...unicorn39,
rules: {
...unicorn39.rules,
'template-indent': unicorn37.rules['template-indent'],
},
},
})