@putout/eslint 1.0.0 β†’ 1.2.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
@@ -49,7 +51,7 @@ const [source, places] = await eslint({
49
51
  });
50
52
  ```
51
53
 
52
- If you want to apply 🐊**Putout** transformations using `putout/putout` **ESLint** rule, enable 🐊**Putout** with the same called flag lowercased:
54
+ If you want to apply 🐊**Putout** transformations using [`putout/putout`](https://github.com/coderaiser/putout/tree/master/packages/eslint-plugin-putout#readme) **ESLint** rule, enable 🐊**Putout** with the same called flag lowercased:
53
55
 
54
56
  ```js
55
57
  const [source, places] = await eslint({
@@ -67,6 +69,90 @@ 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(require('./remove-duplicate-extensions')),
139
+ };
140
+ ```
141
+
142
+ ### `lint(code, {fix, plugins})`
143
+
144
+ ```js
145
+ const lint = require('@putout/eslint/lint');
146
+ const removeDebugger = require('./remove-debugger');
147
+
148
+ const [code, places] = lint('debugger', {
149
+ fix: true, // default
150
+ plugins: [
151
+ ['remove-debugger', createPlugin(removeDebugger)],
152
+ ],
153
+ });
154
+ ```
155
+
70
156
  ## License
71
157
 
72
158
  MIT
@@ -0,0 +1,129 @@
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
+ module.exports.createGetSpaceBeforeNode = createGetSpacesBeforeNode;
118
+
119
+ const createGetSpacesAfterNode = ({getText}) => (node, {text = getText(node)}) => {
120
+ let spaces = '';
121
+ let i = 0;
122
+
123
+ while (!spaces || /^[ \n;]+$/.test(spaces))
124
+ spaces = getText(node, 0, ++i)
125
+ .replace(text, '');
126
+
127
+ return spaces.slice(0, -1);
128
+ };
129
+ module.exports.createGetSpacesAfterNode = createGetSpacesAfterNode;
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ const {Linter} = require('eslint');
4
+ const {convertToPlace} = require('../eslint.js');
5
+
6
+ module.exports.lint = (source, {fix = true, plugins}) => {
7
+ const [name, plugin] = plugins[0];
8
+ const linter = new Linter({
9
+ configType: 'flat',
10
+ });
11
+
12
+ const options = {
13
+ rules: {
14
+ [`${name}/plugin`]: 'error',
15
+ },
16
+ plugins: {
17
+ [name]: {
18
+ rules: {
19
+ plugin,
20
+ },
21
+ },
22
+ },
23
+ };
24
+
25
+ if (!fix) {
26
+ const places = linter.verify(source, options).map(convertToPlace);
27
+ return [source, places];
28
+ }
29
+
30
+ const {output, messages} = linter.verifyAndFix(source, options);
31
+
32
+ return [output, messages.map(convertToPlace)];
33
+ };
34
+
package/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "@putout/eslint",
3
- "version": "1.0.0",
3
+ "version": "1.2.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
+ "./lint": "./lib/lint/index.js"
17
+ },
13
18
  "repository": {
14
19
  "type": "git",
15
20
  "url": "git://github.com/coderaiser/putout.git"