@putout/eslint 1.0.1 β†’ 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,8 @@ npm i @putout/eslint
13
13
 
14
14
  ## API
15
15
 
16
+ ### `eslint(options)`
17
+
16
18
  **ESLint** begins his work as a formatter when 🐊**Putout** done his transformations. That's why it used a lot in different parts of application, for testing purpose and using **API** in a simplest possible way. You can access it with:
17
19
 
18
20
  ```js
@@ -32,7 +34,7 @@ const [source, places] = await eslint({
32
34
  Isn't it looks similar to 🐊**Putout** way? It definitely is! But... It has a couple differences you should remember:
33
35
 
34
36
  - ☝️ *[🐊**Putout** returns object with `code` and `places` properties](https://github.com/coderaiser/putout#plugins).*
35
- - ☝️ ***ESLint** has a `name` property that is used to calculate configuration file.*
37
+ - ☝️ * **ESLint** has a `name` property that is used to calculate configuration file. *
36
38
 
37
39
  And you can even override any of **ESLint** βš™οΈ options with help of `config` property:
38
40
 
@@ -67,6 +69,76 @@ const [source, places] = await eslint({
67
69
 
68
70
  It is disabled by default, because **ESLint** always runs after 🐊**Putout** transformations, so there is no need to traverse tree again.
69
71
 
72
+ ### `createPlugin(options)`
73
+
74
+ You can also simplify creating of plugins for **ESLint** with help of `createPlugin`.
75
+ 🐊**Putout**-based **ESLint** plugin are highly inspired by [**Putout Plugins API**](https://github.com/coderaiser/putout/tree/master/packages/engine-runner#readme) of [**Includer**](https://github.com/coderaiser/putout/tree/master/packages/engine-runner#includer).
76
+
77
+ So it must contain classic `4` methods:
78
+
79
+ ```js
80
+ module.exports.report = () => 'debugger statement should not be used';
81
+
82
+ module.exports.fix = (path) => {
83
+ return '';
84
+ };
85
+
86
+ module.exports.include = () => [
87
+ 'DebuggerStatement',
88
+ ];
89
+
90
+ module.exports.filter = (path) => {
91
+ return true;
92
+ };
93
+ ```
94
+
95
+ Take a look at more sophisticated example, rule [`remove-duplicate-extensions`](https://github.com/coderaiser/putout/tree/master/packages/eslint-plugin-putout/lib/remove-duplicate-extensions#readme):
96
+
97
+ ```js
98
+ const getValue = ({source}) => source?.value;
99
+
100
+ module.exports.report = () => 'Avoid duplicate extensions in relative imports';
101
+ module.exports.include = () => [
102
+ 'ImportDeclaration',
103
+ 'ImportExpression',
104
+ 'ExportAllDeclaration',
105
+ 'ExportNamedDeclaration',
106
+ ];
107
+
108
+ module.exports.fix = ({text}) => {
109
+ return text.replace('.js.js', '.js');
110
+ };
111
+
112
+ module.exports.filter = ({node}) => {
113
+ const value = getValue(node);
114
+ return /\.js\.js/.test(value);
115
+ };
116
+ ```
117
+
118
+ To use it just add couple lines to your main plugin file:
119
+
120
+ ```js
121
+ const {createPlugin} = require('@putout/eslint/create-plugin');
122
+
123
+ const createRule = (a) => ({
124
+ [a]: createPlugin(require(`./${a}`)),
125
+ });
126
+
127
+ module.exports.rules = {
128
+ ...createRule('remove-duplicate-extensions'),
129
+ };
130
+ ```
131
+
132
+ Or just:
133
+
134
+ ```js
135
+ const {createPlugin} = require('@putout/eslint/create-plugin');
136
+
137
+ module.exports.rules = {
138
+ 'remove-duplicate-extensions': createPlugin('remove-duplicate-extensions'),
139
+ };
140
+ ```
141
+
70
142
  ## License
71
143
 
72
144
  MIT
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ const prepare = (plugin, context, options) => (node) => {
4
+ const {filter, report} = plugin;
5
+
6
+ const source = context.getSourceCode();
7
+ const filename = context.getFilename();
8
+ const getText = source.getText.bind(source);
9
+ const getCommentsBefore = source.getCommentsBefore.bind(source);
10
+ const getCommentsAfter = source.getCommentsAfter.bind(source);
11
+ const getCommentsInside = source.getCommentsInside.bind(source);
12
+
13
+ const getSpacesBeforeNode = createGetSpacesBeforeNode({
14
+ getText,
15
+ });
16
+
17
+ const getSpacesAfterNode = createGetSpacesAfterNode({
18
+ getText,
19
+ });
20
+
21
+ const text = getText(node);
22
+
23
+ const result = filter({
24
+ text,
25
+ node,
26
+ options,
27
+ getText,
28
+ getCommentsBefore,
29
+ getCommentsAfter,
30
+ getCommentsInside,
31
+ getSpacesBeforeNode,
32
+ getSpacesAfterNode,
33
+ filename,
34
+ });
35
+
36
+ if (!result)
37
+ return;
38
+
39
+ const fix = prepareFix(plugin.fix, {
40
+ filename,
41
+ node,
42
+ text,
43
+ getText,
44
+ });
45
+
46
+ context.report({
47
+ node,
48
+ message: report(node),
49
+ fix,
50
+ });
51
+ };
52
+
53
+ const prepareFix = (fix, {node, text, getText, filename}) => (fixer) => {
54
+ const fixed = fix({
55
+ node,
56
+ text,
57
+ getText,
58
+ filename,
59
+ });
60
+
61
+ return [
62
+ fixer.replaceText(node, fixed),
63
+ ];
64
+ };
65
+
66
+ module.exports.createPlugin = (plugin) => {
67
+ const meta = getMeta(plugin);
68
+
69
+ return {
70
+ meta,
71
+ create(context) {
72
+ const {options} = context;
73
+ const prepared = prepare(plugin, context, options);
74
+ const names = plugin.include({options});
75
+
76
+ return getTraversers(names, prepared);
77
+ },
78
+ };
79
+ };
80
+
81
+ function getMeta(plugin) {
82
+ const {
83
+ type = 'layout',
84
+ recommended = true,
85
+ fixable = 'whitespace',
86
+ } = plugin;
87
+
88
+ return {
89
+ type,
90
+ docs: {
91
+ recommended,
92
+ },
93
+ schema: {},
94
+ fixable,
95
+ };
96
+ }
97
+
98
+ function getTraversers(names, plugin) {
99
+ const traversers = {};
100
+
101
+ for (const name of names)
102
+ traversers[name] = plugin;
103
+
104
+ return traversers;
105
+ }
106
+
107
+ const createGetSpacesBeforeNode = ({getText}) => (node, text = getText(node)) => {
108
+ let spaces = '';
109
+ let i = 0;
110
+
111
+ while (!spaces || /^[ \n]+$/.test(spaces))
112
+ spaces = getText(node, ++i)
113
+ .replace(text, '');
114
+
115
+ return spaces.slice(1);
116
+ };
117
+
118
+ const createGetSpacesAfterNode = ({getText}) => (node, {text = getText(node)}) => {
119
+ let spaces = '';
120
+ let i = 0;
121
+
122
+ while (!spaces || /^[ \n;]+$/.test(spaces))
123
+ spaces = getText(node, 0, ++i)
124
+ .replace(text, '');
125
+
126
+ return spaces.slice(0, -1);
127
+ };
128
+
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "@putout/eslint",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "commonjs",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "Wrapper that simplifies ESLint API and makes it compatible with 🐊Putout",
7
7
  "homepage": "https://github.com/coderaiser/putout/tree/master/packages/eslint#readme",
8
- "main": "lib/eslint.js",
8
+ "main": "./lib/eslint.js",
9
9
  "commitType": "colon",
10
10
  "release": false,
11
11
  "tag": false,
12
12
  "changelog": false,
13
+ "exports": {
14
+ ".": "./lib/eslint.js",
15
+ "./create-plugin": "./lib/create-plugin/index.js"
16
+ },
13
17
  "repository": {
14
18
  "type": "git",
15
19
  "url": "git://github.com/coderaiser/putout.git"