@putout/plugin-variables 1.2.0 → 1.3.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
@@ -17,6 +17,8 @@ npm i @putout/plugin-variables -D
17
17
 
18
18
  ## Rules
19
19
 
20
+ - ✅ [convert-const-to-let](#convert-const-to-let');
21
+ - ✅ [extract-keywords](#extract-keywords');
20
22
  - ✅ [remove-useless-assignment](#remove-useless-assignmenn);
21
23
  - ✅ [remove-useless-declaration](#remove-useless-declaration);
22
24
  - ✅ [remove-useless-duplicate](#remove-useless-duplicate);
@@ -29,6 +31,8 @@ npm i @putout/plugin-variables -D
29
31
  ```json
30
32
  {
31
33
  "rules": {
34
+ "variables/convert-const-to-let": "on",
35
+ "variables/extract-keywords": "on",
32
36
  "variables/remove-useless-assignment": "on",
33
37
  "variables/remove-useless-declaration": ["on", {
34
38
  "maxLength": 20
@@ -240,6 +244,68 @@ let a = 5;
240
244
  a = 3;
241
245
  ```
242
246
 
247
+ ## remove-unused
248
+
249
+ > A variable is a container for a value, like a `number` we might use in a sum, or a `string` that we might use as part of a sentence.
250
+ >
251
+ > (c) [MDN](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Variables)
252
+
253
+ 🐊[**Putout**](https://github.com/coderaiser/putout) plugin adds ability to find and remove the variables that are declared, but:
254
+
255
+ - not passed as **argument** to a **function**;
256
+ - not used as **operand** in **expression**;
257
+
258
+ That is **unused variables**. Most likely it is a leftovers due to incomplete transforming of the code. Such variables take up space and gives no value so they must be removed.
259
+
260
+ ☝️*Remember, when you [writing a transform](https://github.com/coderaiser/putout/tree/master/packages/engine-runner#readme) you can skip all parts related to **removing unused variables** and just reuse current **plugin** it will make your code simpler and less error prone.*
261
+
262
+ ☝️*No, you cannot just look at [`referenced` and `constant` fields](https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#user-content-bindings) to determine if you can remove variable and [here is why](https://putout.cloudcmd.io/#/gist/4277392f74b56b74911b779c9624af8d/cfec476f857dfb4f4c7a6247bdcc6b521fed8e70) one of the biggest plugins exists*.
263
+
264
+ ### ❌ Example of incorrect code
265
+
266
+ ```js
267
+ const a = 'hello';
268
+ const b = 'world';
269
+
270
+ console.log(a);
271
+ ```
272
+
273
+ ### ✅ Example of correct code
274
+
275
+ ```js
276
+ const a = 'hello';
277
+ console.log(a);
278
+ ```
279
+
280
+ ## Comparison
281
+
282
+ Linter | Rule | Fix
283
+ --------|-------|------------|
284
+ 🐊 **Putout**| [`remove-unused-variables`](https://github.com/coderaiser/putout/tree/master/packages/plugin-remove-unused-variables#readme)| ✅
285
+ ⏣ **ESLint** | [`no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars) | ❌
286
+
287
+ ## extract-keywords
288
+
289
+ > The JavaScript exceptions "unexpected token" occur when the parser does not see a token it recognizes at the given position, so it cannot make sense of the structure of the program. This might be a simple typo.
290
+ >
291
+ > (c) [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Unexpected_token)
292
+
293
+ Extract `keywords` from variables. Check out in 🐊[**Putout Editor**](https://putout.cloudcmd.io/#/gist/fcaedaa9daf7f3a771274aca0da9ab1b/00850a5d28aec86b1b4083ba2ef9f81bd49aaaac).
294
+
295
+ ```diff
296
+ -export const isTemplateMiddle = (a) => a?.type === 'TemplateMiddle',
297
+ +export const isTemplateMiddle = (a) => a?.type === 'TemplateMiddle';
298
+ export const isTemplateTail = (a) => a?.type === 'TemplateTail';
299
+
300
+ -const a 5;
301
+ +const a = 5;
302
+
303
+ -export const packContent = (content) {
304
+ +export const packContent = (content) => {
305
+ console.log(a);
306
+ }
307
+ ```
308
+
243
309
  ## License
244
310
 
245
311
  MIT
@@ -0,0 +1,178 @@
1
+ import {types, operator} from 'putout';
2
+
3
+ const {
4
+ ifStatement,
5
+ importDefaultSpecifier,
6
+ importDeclaration,
7
+ exportNamedDeclaration,
8
+ variableDeclarator,
9
+ variableDeclaration,
10
+ isExportDeclaration,
11
+ isArrowFunctionExpression,
12
+ isLiteral,
13
+ isAssignmentExpression,
14
+ isExportNamedDeclaration,
15
+ isIdentifier,
16
+ isImportDeclaration,
17
+ isMemberExpression,
18
+ isBlockStatement,
19
+ arrowFunctionExpression,
20
+ } = types;
21
+
22
+ const {
23
+ removeParens,
24
+ remove,
25
+ replaceWith,
26
+ isDeclarationKeyword,
27
+ isConditionKeyword,
28
+ isModuleDeclarationKeyword,
29
+ } = operator;
30
+
31
+ const isInit = (a) => isIdentifier(a) || isLiteral(a) || isMemberExpression(a) || isArrowFunctionExpression(a);
32
+
33
+ const buildDeclaration = (type) => (nextPath, path) => {
34
+ const {expression} = nextPath.node;
35
+ let left;
36
+ let right;
37
+
38
+ if (isBlockStatement(nextPath)) {
39
+ left = path.node.id;
40
+ const {node: init} = removeParens(path.get('init'));
41
+ const params = [init];
42
+
43
+ right = arrowFunctionExpression(params, nextPath.node);
44
+ } else if (isAssignmentExpression(expression)) {
45
+ ({
46
+ left,
47
+ right,
48
+ } = expression);
49
+ } else {
50
+ left = path.node.id;
51
+ right = nextPath.node.expression;
52
+ }
53
+
54
+ replaceWith(nextPath, variableDeclaration(type, [variableDeclarator(left, right)]));
55
+
56
+ const {name} = path.node.id;
57
+
58
+ if (isDeclarationKeyword(name))
59
+ return;
60
+
61
+ if (isExportNamedDeclaration(path.parentPath.parentPath))
62
+ replaceWith(nextPath, exportNamedDeclaration(nextPath.node));
63
+ };
64
+
65
+ const builders = {
66
+ const: buildDeclaration('const'),
67
+ var: buildDeclaration('var'),
68
+ let: buildDeclaration('let'),
69
+ import: buildImport,
70
+ export: buildExport,
71
+ if: buildIf,
72
+ };
73
+
74
+ export const report = ({name}) => `Extract '${name}' from variable`;
75
+
76
+ export const fix = ({name, path, nextPath}) => {
77
+ builders[name](nextPath, path);
78
+ remove(path);
79
+ };
80
+
81
+ export const traverse = ({push}) => ({
82
+ VariableDeclarator(path) {
83
+ const {name} = path.node.id;
84
+
85
+ if (isDeclarationKeyword(name) || isConditionKeyword(name) || isModuleDeclarationKeyword(name)) {
86
+ const topPath = getTopPath(path);
87
+ const nextPath = topPath.getNextSibling();
88
+
89
+ if (nextPath.isVariableDeclaration())
90
+ push({
91
+ name,
92
+ path,
93
+ nextPath,
94
+ });
95
+
96
+ if (nextPath.isExpressionStatement())
97
+ push({
98
+ name,
99
+ path,
100
+ nextPath,
101
+ });
102
+
103
+ return;
104
+ }
105
+
106
+ const {kind} = path.parentPath.node;
107
+
108
+ if (!path.node.init) {
109
+ const topPath = getTopPath(path);
110
+ const nextPath = topPath.getNextSibling();
111
+
112
+ if (!nextPath.isExpressionStatement())
113
+ return;
114
+
115
+ const {expression} = nextPath.node;
116
+
117
+ if (isInit(expression))
118
+ push({
119
+ name: kind,
120
+ path,
121
+ nextPath,
122
+ });
123
+
124
+ return;
125
+ }
126
+
127
+ if (isExportDeclaration(path.parentPath.parentPath)) {
128
+ const nextPath = path.parentPath.parentPath.getNextSibling();
129
+
130
+ if (!isBlockStatement(nextPath))
131
+ return;
132
+
133
+ if (!nextPath.node.body.length)
134
+ return;
135
+
136
+ const count = nextPath.node.body.filter(isImportDeclaration).length;
137
+
138
+ if (!count)
139
+ push({
140
+ name: kind,
141
+ path,
142
+ nextPath,
143
+ });
144
+ }
145
+ },
146
+ });
147
+
148
+ function getTopPath(path) {
149
+ if (path.parentPath.parentPath.isExportDeclaration())
150
+ return path.parentPath.parentPath;
151
+
152
+ return path.parentPath;
153
+ }
154
+
155
+ function buildExport(path) {
156
+ replaceWith(path, exportNamedDeclaration(path.node));
157
+ }
158
+
159
+ function buildImport(path) {
160
+ const fromPath = path.getNextSibling();
161
+ const sourcePath = fromPath.getNextSibling();
162
+ const source = sourcePath.node.expression;
163
+ const local = path.node.expression;
164
+
165
+ replaceWith(path, importDeclaration([importDefaultSpecifier(local, local)], source));
166
+ remove(sourcePath);
167
+ remove(fromPath);
168
+ }
169
+
170
+ function buildIf(path) {
171
+ const {expression} = path.node;
172
+ delete expression.extra.parenthesized;
173
+ const nextPath = path.getNextSibling();
174
+ const next = nextPath.node;
175
+
176
+ remove(nextPath);
177
+ replaceWith(path, ifStatement(expression, next));
178
+ }
package/lib/index.js CHANGED
@@ -1,19 +1,23 @@
1
1
  import * as convertConstToLet from './convert-const-to-let/index.js';
2
+ import * as extractKeywords from './extract-keywords/index.js';
2
3
  import * as removeUseless from './remove-useless/index.js';
3
4
  import * as removeUselessAssignment from './remove-useless-assignment/index.js';
4
5
  import * as removeUselessDeclarations from './remove-useless-declarations/index.js';
5
6
  import * as removeUselessDuplicates from './remove-useless-duplicates/index.js';
6
7
  import * as removeUselessRename from './remove-useless-rename/index.js';
7
8
  import * as removeUnreferenced from './remove-unreferenced/index.js';
9
+ import * as removeUnused from './remove-unused/index.js';
8
10
  import * as splitDeclarations from './split-declarations/index.js';
9
11
 
10
12
  export const rules = {
11
13
  'convert-const-to-let': convertConstToLet,
14
+ 'extract-keywords': extractKeywords,
12
15
  'remove-useless': removeUseless,
13
16
  'remove-useless-assignment': removeUselessAssignment,
14
17
  'remove-useless-declarations': removeUselessDeclarations,
15
18
  'remove-useless-duplicates': removeUselessDuplicates,
16
19
  'remove-useless-rename': removeUselessRename,
17
20
  'remove-unreferenced': removeUnreferenced,
21
+ 'remove-unused': removeUnused,
18
22
  'split-declarations': splitDeclarations,
19
23
  };
@@ -0,0 +1,5 @@
1
+ const onlyDeclared = ({declared, used}) => declared && !used;
2
+
3
+ export default (items) => {
4
+ return items.filter(onlyDeclared);
5
+ };