eslint-plugin-wrapper
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -0,0 +0,0 @@ import type * as eslint from 'eslint' |
91
index.js
@@ -11,9 +11,86 @@ class EslintPluginWrapper { | ||
default: { | ||
configs: { | ||
recommended: { | ||
rules: { | ||
'default/eslint-comments': 'error', | ||
}, | ||
}, | ||
}, | ||
rules: { | ||
'not-setup': { | ||
'eslint-comments': { | ||
meta: { | ||
docs: { | ||
description: | ||
'Detect and fix incorrectly-prefixed wrapped rules', | ||
url: 'https://github.com/mmkal/eslint-plugin-wrapper#README', | ||
recommended: true, | ||
}, | ||
fixable: 'code', | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
directives: { | ||
type: 'array', | ||
items: {type: 'string'}, | ||
}, | ||
prefixReplacements: { | ||
type: 'object', | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
create: context => { | ||
context.report({ | ||
message: | ||
'You must call `wrapper.add(...)` before using this plugin', | ||
loc: {line: 1, column: 0}, | ||
const comments = context.getSourceCode().getAllComments() | ||
const directives = (context.options && | ||
context.options[0] && | ||
context.options[0].directives) || [ | ||
'eslint-disable', | ||
'eslint-enable', | ||
'eslint-disable-next-line', | ||
] | ||
comments.forEach(c => { | ||
const matchedDirective = directives.find(d => | ||
c.value.trim().startsWith(d + ' '), | ||
) | ||
if (!matchedDirective) { | ||
return {} | ||
} | ||
/** @type {Record<string, string>} */ | ||
const prefixReplacements = { | ||
...Object.fromEntries( | ||
Object.keys(this.plugins).map(pluginName => [ | ||
`${pluginName}/`, | ||
`${this.pluginName}/${pluginName}/`, | ||
]), | ||
), | ||
...(context.options && | ||
context.options[0] && | ||
context.options[0].prefixReplacements), | ||
} | ||
const updatedComment = Object.entries(prefixReplacements) | ||
.sort((a, b) => b[0].length - a[0].length) | ||
.reduce((comment, [withoutPrefix, withPrefix]) => { | ||
return comment | ||
.split(' ' + withoutPrefix) | ||
.join(' ' + withPrefix) | ||
}, c.value) | ||
if (updatedComment !== c.value) { | ||
context.report({ | ||
message: `eslint comment incorrectly prefixed. Replace "${c.value.trim()}" with "${updatedComment.trim()}"`, | ||
loc: c.loc, | ||
fix: fixer => | ||
fixer.replaceTextRange( | ||
[ | ||
c.range[0] + 2, | ||
c.type === 'Block' ? c.range[1] - 2 : c.range[1], | ||
], | ||
updatedComment, | ||
), | ||
}) | ||
} | ||
}) | ||
@@ -50,3 +127,2 @@ return {} | ||
addPlugins(plugins) { | ||
delete this.plugins.default | ||
Object.assign(this.plugins, plugins) | ||
@@ -184,4 +260,3 @@ } | ||
* | ||
* @param {Record<string, import('.').Plugin>} plugins | ||
* @param {(opts: {pluginName: string; plugin: import('.').Plugin; ruleName: string; rule: import('eslint').Rule.RuleModule}) => [string, any]} getEntry | ||
* @type {<T>(plugins: Record<string, import('.').Plugin>, getEntry: (opts: {pluginName: string; plugin: import('.').Plugin; ruleName: string; rule: import('eslint').Rule.RuleModule}) => [string, T]) => Record<string, T>} | ||
*/ | ||
@@ -188,0 +263,0 @@ function ruleDict(plugins, getEntry) { |
{ | ||
"name": "eslint-plugin-wrapper", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Write project-specific rules, in your own eslint config file", | ||
@@ -13,3 +13,3 @@ "main": "index.js", | ||
"lint": "eslint .", | ||
"test": "jest" | ||
"test": "npm run lint" | ||
}, | ||
@@ -16,0 +16,0 @@ "repository": { |
@@ -13,2 +13,4 @@ # eslint-plugin-wrapper | ||
### Project-specific rules | ||
In your `.eslintrc.js` file: | ||
@@ -45,1 +47,86 @@ | ||
Your codebase will now be linted with the `no-literals` rule. | ||
### Wrap external plugins and configs | ||
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](https://github.com/eslint/eslint/issues/3458), which is legal now, until [supported in eslint](https://github.com/eslint/eslint/issues/13481). | ||
For example, you could create a package internal to your company, say called `@yourcompany/eslint-plugin'`. Then, in its `main` module: | ||
```js | ||
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', | ||
'@hidrb/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: | ||
```js | ||
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 a `config:` 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. | ||
#### Alternatives | ||
Note that some other workarounds exist, but they require shimming, e.g. [@rushstack/eslint-config](https://www.npmjs.com/package/@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). | ||
### Multiple versions of a library | ||
You could use this to pick rules from different published versions of a single library. In package.json: | ||
```json | ||
"dependencies": { | ||
..., | ||
"eslint-plugin-unicorn_37": "npm:eslint-plugin-unicorn@37.0.0", | ||
"eslint-plugin-unicorn_39": "npm:eslint-plugin-unicorn@39.0.0" | ||
} | ||
``` | ||
Then in `.eslintrc.js`: | ||
```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'], | ||
}, | ||
}, | ||
}) | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25427
276
131