@putout/plugin-destructuring 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) coderaiser
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # @putout/plugin-destructuring [![NPM version][NPMIMGURL]][NPMURL]
2
+
3
+ [NPMIMGURL]: https://img.shields.io/npm/v/@putout/plugin-destructuring.svg?style=flat&longCache=true
4
+ [NPMURL]: https://npmjs.org/package/@putout/plugin-destructuring"npm"
5
+
6
+ > The **destructuring** assignment syntax is a **JavaScript** expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
7
+ >
8
+ > (c) [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
9
+
10
+ 🐊[**Putout**](https://github.com/coderaiser/putout) plugin adds ability to use **destructuring** on variable declarations.
11
+
12
+ ## Install
13
+
14
+ ```
15
+ npm i @putout/plugin-destructuring
16
+ ```
17
+
18
+ ## Rules
19
+
20
+ - βœ… [apply-object](#apply-object);
21
+ - βœ… [apply-array](#apply-array);
22
+ - βœ… [remove-useless-object](#remove-useless-object);
23
+ - βœ… [convert-object-to-array](#convert-object-to-array);
24
+ - βœ… [split-nested](#split-nested);
25
+ - βœ… [split-call](#split-call);
26
+
27
+ ## Config
28
+
29
+ ```json
30
+ {
31
+ "rules": {
32
+ "destructuring/apply-object": "on",
33
+ "destructuring/apply-array": "on",
34
+ "destructuring/remove-useless-object": "on",
35
+ "destructuring/convert-object-to-array": "on"
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## apply-array
41
+
42
+ ### ❌ Example of incorrect code
43
+
44
+ ```js
45
+ const first = array[0];
46
+ ```
47
+
48
+ ## βœ… Example of correct code
49
+
50
+ ```js
51
+ const [first] = array;
52
+ ```
53
+
54
+ ## apply-object
55
+
56
+ ### ❌ Example of incorrect code
57
+
58
+ ```js
59
+ const name = user.name;
60
+
61
+ hello = world.hello;
62
+ ```
63
+
64
+ ### βœ… Example of correct code
65
+
66
+ ```js
67
+ const {name} = user;
68
+
69
+ ({hello} = world);
70
+ ```
71
+
72
+ ## remove-useless-object
73
+
74
+ Check out in 🐊[**Putout Editor**](https://putout.cloudcmd.io/#/gist/c9ed04b421d75ae39e58038fa6e14630/4c097e3173990ec7e5ebabbe2cedf8e952092ebf).
75
+
76
+ ### ❌ Example of incorrect code
77
+
78
+ ```js
79
+ const {maxElementsInOneLine} = {
80
+ options,
81
+ };
82
+ ```
83
+
84
+ ### βœ… Example of correct code
85
+
86
+ ```js
87
+ const {maxElementsInOneLine} = options;
88
+ ```
89
+
90
+ ## convert-object-to-array
91
+
92
+ Check out in 🐊[**Putout Editor**](https://putout.cloudcmd.io/#/gist/a1d26daf8bb83ee3ea1c0b62a6ad3afd/cef9b4d27c9dbb0d413a935b0359a6fe9b50364f).
93
+
94
+ ### ❌ Example of incorrect code
95
+
96
+ ```js
97
+ const {0: a, 1: b} = c;
98
+ ```
99
+
100
+ ### βœ… Example of correct code
101
+
102
+ ```js
103
+ const [a, b] = c;
104
+ ```
105
+
106
+ ## split-nested
107
+
108
+ > - Don't use nested destructuring on data that comes from any external data sources (such as `REST API`s, `GraphQL` endpoints or files).
109
+ > - Don't use nested destructuring on function arguments that have long or complicated signatures.
110
+ >
111
+ > (c) [Destructuring in JavaScript: the not so good parts](https://goodguydaniel.com/blog/destructuring-not-so-good-parts)
112
+
113
+ 🐊[**Putout**](https://github.com/coderaiser/putout) plugin adds ability to split nested destructuring.
114
+
115
+ ### ❌ Example of incorrect code
116
+
117
+ ```js
118
+ const {
119
+ a: {
120
+ b,
121
+ },
122
+ a: {
123
+ b: x,
124
+ },
125
+ } = c;
126
+
127
+ function f({a}) {
128
+ const {b} = a;
129
+ console.log(b);
130
+ }
131
+ ```
132
+
133
+ ### βœ… Example of correct code
134
+
135
+ ```js
136
+ const {a} = c;
137
+ const {b, b: x} = a;
138
+
139
+ function f({a}) {
140
+ const {b} = a;
141
+ console.log(b);
142
+ }
143
+ ```
144
+
145
+ ## split-call
146
+
147
+ ### ❌ Example of incorrect code
148
+
149
+ ```js
150
+ console.log('hello')({uid} = path.scope);
151
+ console.log('hello')[uid] = path.scope;
152
+ ```
153
+
154
+ ### βœ… Example of correct code
155
+
156
+ ```js
157
+ console.log('hello');
158
+ ({uid} = path.scope);
159
+
160
+ console.log('hello');
161
+ [uid] = path.scope;
162
+ ```
163
+
164
+ ## merge-properties
165
+
166
+ > The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from `arrays`, or `properties` from `objects`, into distinct `variables`.
167
+ >
168
+ > (c) [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
169
+
170
+ Checkout in 🐊[**Putout Editor**](https://putout.cloudcmd.io/#/gist/11c8cfa59f87e46238309b857448b9c5/688f10ad8fd7c0e4d9e9e0c74c399f1edb3ba29e).
171
+
172
+ ### ❌ Example of incorrect code
173
+
174
+ ```js
175
+ const {one} = require('numbers');
176
+ const {two} = require('numbers');
177
+
178
+ ({from} = data);
179
+ ({to} = data);
180
+ ({names} = data);
181
+ ```
182
+
183
+ ### βœ… Example of correct code
184
+
185
+ ```js
186
+ const {one, two} = require('numbers');
187
+
188
+ ({
189
+ from,
190
+ to,
191
+ names,
192
+ } = data);
193
+ ```
194
+
195
+ ## License
196
+
197
+ MIT
@@ -0,0 +1,22 @@
1
+ export const report = () => `Use array destructuring`;
2
+
3
+ export const exclude = () => [
4
+ 'const __object = __[0]',
5
+ 'const __array = __[0]',
6
+ '({__} = __[0])',
7
+ '[__] = __[0]',
8
+ '__a[0] = __b[0]',
9
+ ];
10
+
11
+ export const replace = () => ({
12
+ '__a = __b[0]': '[__a] = __b',
13
+ '__a = __b[1]': '[, __a] = __b',
14
+ 'const __a = __b[1]': 'const [, __a] = __b',
15
+ 'const __a = __b[0]': convertTo('const [__a] = __b'),
16
+ 'let __a = __b[0]': convertTo('let [__a] = __b'),
17
+ });
18
+
19
+ const convertTo = (template) => ({__a}) => {
20
+ delete __a.typeAnnotation;
21
+ return template;
22
+ };
@@ -0,0 +1,41 @@
1
+ import {types, operator} from 'putout';
2
+
3
+ const {replaceWith} = operator;
4
+ const {
5
+ isObjectProperty,
6
+ arrayPattern,
7
+ } = types;
8
+
9
+ export const report = () => `Use array destructuring instead of object destructuring`;
10
+
11
+ export const fix = (path) => {
12
+ const elements = [];
13
+
14
+ for (const {value} of path.node.properties) {
15
+ elements.push(value);
16
+ }
17
+
18
+ const array = arrayPattern(elements);
19
+ replaceWith(path, array);
20
+ };
21
+
22
+ export const traverse = ({push}) => ({
23
+ ObjectPattern(path) {
24
+ const properties = path.get('properties');
25
+
26
+ if (!properties.length)
27
+ return;
28
+
29
+ for (const [i, prop] of properties.entries()) {
30
+ if (!isObjectProperty(prop))
31
+ return;
32
+
33
+ const {key} = prop.node;
34
+
35
+ if (i !== key.value)
36
+ return;
37
+ }
38
+
39
+ push(path);
40
+ },
41
+ });
@@ -0,0 +1,17 @@
1
+ const FIRST_PROPERTY = 'declarations.0.id.properties.0';
2
+
3
+ export const report = (path) => {
4
+ const {name} = path.get(`${FIRST_PROPERTY}.key`).node;
5
+ return `Use destructuring instead of setting '${name}' to 'undefined'`;
6
+ };
7
+
8
+ export const match = () => ({
9
+ 'const {__a} = {__b}': (vars, path) => {
10
+ const prop = path.get(FIRST_PROPERTY);
11
+ return !prop.node.computed;
12
+ },
13
+ });
14
+
15
+ export const replace = () => ({
16
+ 'const {__a} = {__b}': 'const {__a} = __b',
17
+ });
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ import * as convertObjectToArray from './convert-object-to-array/index.js';
2
+ import * as object from './object/index.js';
3
+ import * as array from './array/index.js';
4
+ import * as falsy from './falsy/index.js';
5
+ import * as splitNested from './split-nested/index.js';
6
+ import * as splitCall from './split-call/index.js';
7
+ import * as mergeProperties from './merge-properties/index.js';
8
+
9
+ export const rules = {
10
+ 'apply-object': object,
11
+ 'apply-array': array,
12
+ 'remove-useless-object': falsy,
13
+ 'convert-object-to-array': convertObjectToArray,
14
+ 'split-nested': splitNested,
15
+ 'split-call': splitCall,
16
+ 'merge-properties': mergeProperties,
17
+ };
@@ -0,0 +1,152 @@
1
+ import {types, operator} from 'putout';
2
+
3
+ const {remove, compare} = operator;
4
+
5
+ const {
6
+ isObjectPattern,
7
+ isRestElement,
8
+ isAssignmentExpression,
9
+ isVariableDeclaration,
10
+ isExpressionStatement,
11
+ } = types;
12
+
13
+ const notEmptyPlaces = (a) => a.places.length;
14
+
15
+ const LEFT = {
16
+ VariableDeclarator: 'id',
17
+ AssignmentExpression: 'left',
18
+ };
19
+
20
+ const RIGHT = {
21
+ VariableDeclarator: 'init',
22
+ AssignmentExpression: 'right',
23
+ };
24
+
25
+ export const report = () => 'Merge object properties when destructuring';
26
+
27
+ export const fix = ({path, places}) => {
28
+ merge(path, places);
29
+ };
30
+
31
+ export const traverse = ({push, store}) => {
32
+ const add = addVariable({
33
+ store,
34
+ });
35
+
36
+ return {
37
+ 'VariableDeclarator|AssignmentExpression'(path) {
38
+ const {left, right} = split(path);
39
+
40
+ if (!right)
41
+ return;
42
+
43
+ if (!isObjectPattern(left))
44
+ return;
45
+
46
+ for (const property of left.properties) {
47
+ if (isRestElement(property))
48
+ return;
49
+ }
50
+
51
+ add(path, right);
52
+ },
53
+ Program: {
54
+ exit() {
55
+ store()
56
+ .filter(notEmptyPlaces)
57
+ .filter(checkPlaces)
58
+ .map(push);
59
+ },
60
+ },
61
+ };
62
+ };
63
+
64
+ function checkPlaces({places}) {
65
+ return places.filter(hasParentNode).length;
66
+ }
67
+
68
+ const hasParentNode = ({parentPath}) => {
69
+ if (!parentPath.node)
70
+ return false;
71
+
72
+ return isVariableDeclaration(parentPath) || isExpressionStatement(parentPath);
73
+ };
74
+
75
+ const createUID = (path) => {
76
+ const {uid} = path.scope;
77
+ const {type} = path;
78
+ const name = RIGHT[type];
79
+
80
+ const str = `${uid}-${path.get(name).toString()}`;
81
+
82
+ return str.replace(/['"`]/g, '*');
83
+ };
84
+
85
+ const addVariable = ({store}) => (path, node) => {
86
+ const str = createUID(path);
87
+ const currentVar = store(str);
88
+
89
+ if (!currentVar) {
90
+ store(str, {
91
+ path,
92
+ places: [],
93
+ });
94
+
95
+ return;
96
+ }
97
+
98
+ const currentPath = currentVar.path;
99
+
100
+ if (path === currentPath)
101
+ return;
102
+
103
+ const {parentPath, type} = currentPath;
104
+
105
+ if (currentPath.removed || !currentPath.node)
106
+ return;
107
+
108
+ if (parentPath.removed || !parentPath.node)
109
+ return;
110
+
111
+ const name = RIGHT[type];
112
+
113
+ if (isAssignmentExpression(currentPath) && compare(currentPath.node[name], node))
114
+ return currentVar.places.push(path);
115
+
116
+ if (sameKind(path, currentPath))
117
+ return currentVar.places.push(path);
118
+ };
119
+
120
+ function sameKind(path1, path2) {
121
+ const kind1 = path1.parentPath.node.kind;
122
+ const kind2 = path2.parentPath.node.kind;
123
+
124
+ return kind1 === kind2;
125
+ }
126
+
127
+ function split(path) {
128
+ const {type} = path;
129
+ const leftName = LEFT[type];
130
+ const rightName = RIGHT[type];
131
+ const left = path.node[leftName];
132
+ const right = path.node[rightName];
133
+
134
+ return {
135
+ left,
136
+ right,
137
+ };
138
+ }
139
+
140
+ function merge(path, places) {
141
+ const {node, type} = path;
142
+ const name = LEFT[type];
143
+
144
+ for (const place of places) {
145
+ node[name].properties = [
146
+ ...node[name].properties,
147
+ ...place.node[name].properties,
148
+ ];
149
+
150
+ remove(place);
151
+ }
152
+ }
@@ -0,0 +1,15 @@
1
+ import {types} from 'putout';
2
+
3
+ const {isExpressionStatement} = types;
4
+
5
+ export const report = () => 'Use object destructuring';
6
+
7
+ export const match = () => ({
8
+ '__a = __b.__a': (vars, {parentPath}) => isExpressionStatement(parentPath),
9
+ });
10
+
11
+ export const replace = () => ({
12
+ 'const __a = __b.__a': 'const {__a} = __b',
13
+ 'let __a = __b.__a': 'let {__a} = __b',
14
+ '__a = __b.__a': '({__a} = __b)',
15
+ });
@@ -0,0 +1,28 @@
1
+ import {types} from 'putout';
2
+
3
+ const {isIdentifier} = types;
4
+
5
+ export const report = () => `Split call with destructuring`;
6
+
7
+ export const replace = () => ({
8
+ '__a(__args)({__c} = __d)': `{
9
+ __a(__args);
10
+ ({__c} = __d);
11
+ }`,
12
+ '__a(__args)[__c] = __d': ({__c}) => {
13
+ if (isIdentifier(__c))
14
+ return `{
15
+ __a(__args);
16
+ [__c] = __d;
17
+ }`;
18
+
19
+ __c.elements = __c.expressions;
20
+ __c.type = 'ArrayPattern';
21
+ delete __c.expressions;
22
+
23
+ return `{
24
+ __a(__args);
25
+ __c = __d;
26
+ }`;
27
+ },
28
+ });
@@ -0,0 +1,54 @@
1
+ import {types, template} from 'putout';
2
+
3
+ const {
4
+ objectProperty,
5
+ isObjectPattern,
6
+ objectPattern,
7
+ } = types;
8
+
9
+ const buildDeclaration = template(`
10
+ const %%__b%% = %%__a%%
11
+ `);
12
+
13
+ export const report = () => 'Avoid nested destructuring';
14
+
15
+ export const match = () => ({
16
+ 'const {__a: {__b}} = __c': matchConst,
17
+ 'const {__a: {__b: __c}} = __d': matchConst,
18
+ 'const {__a: {__b = __c}} = __d': matchConst,
19
+ });
20
+
21
+ export const replace = () => ({
22
+ 'const {__a: {__b}} = __c': 'const {__a} = __c, {__b} = __a',
23
+ 'const {__a: {__b: __c}} = __d': 'const {__a} = __d, {__b: __c} = __a',
24
+ 'const {__a: {__b = __c}} = __d': 'const {__a} = __d, {__b = __c} = __a',
25
+ 'function f({ __a: { __b } }) {}': replaceArg,
26
+ 'function f({ __a: { __b: __c } }) {}': replaceArg,
27
+ });
28
+
29
+ function matchConst({__a, __c}, path) {
30
+ const bindings = path.scope.getAllBindings();
31
+
32
+ if (bindings[__a.name])
33
+ return false;
34
+
35
+ return !isObjectPattern(__c);
36
+ }
37
+
38
+ function replaceArg({__a, __b, __c}, path) {
39
+ const {properties} = path.node.params[0];
40
+ const [first] = properties;
41
+
42
+ first.value = first.key;
43
+
44
+ const declaration = buildDeclaration({
45
+ __a,
46
+ __b: objectPattern([
47
+ objectProperty(__b, __c || __b),
48
+ ]),
49
+ });
50
+
51
+ path.node.body.body.unshift(declaration);
52
+
53
+ return path;
54
+ }
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@putout/plugin-destructuring",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
+ "description": "🐊Putout plugin adds ability to transform destructuring",
7
+ "homepage": "https://github.com/coderaiser/putout/tree/master/packages/plugin-destructuring#readme",
8
+ "main": "lib/index.js",
9
+ "release": false,
10
+ "tag": false,
11
+ "changelog": false,
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/coderaiser/putout.git"
15
+ },
16
+ "scripts": {
17
+ "test": "madrun test",
18
+ "watch:test": "madrun watch:test",
19
+ "lint": "madrun lint",
20
+ "fresh:lint": "madrun fresh:lint",
21
+ "lint:fresh": "madrun lint:fresh",
22
+ "fix:lint": "madrun fix:lint",
23
+ "coverage": "madrun coverage",
24
+ "report": "madrun report"
25
+ },
26
+ "dependencies": {},
27
+ "keywords": [
28
+ "putout",
29
+ "putout-plugin",
30
+ "plugin",
31
+ "destructuring"
32
+ ],
33
+ "devDependencies": {
34
+ "@putout/plugin-declare": "*",
35
+ "@putout/plugin-declare-before-reference": "*",
36
+ "@putout/plugin-esm": "*",
37
+ "@putout/plugin-minify": "*",
38
+ "@putout/plugin-nodejs": "*",
39
+ "@putout/plugin-printer": "*",
40
+ "@putout/plugin-putout": "*",
41
+ "@putout/plugin-tape": "*",
42
+ "@putout/test": "^14.0.0",
43
+ "c8": "^10.0.0",
44
+ "eslint": "v10.0.0-alpha.0",
45
+ "eslint-plugin-n": "^17.0.0",
46
+ "eslint-plugin-putout": "^29.0.0",
47
+ "madrun": "^11.0.0",
48
+ "nodemon": "^3.0.1"
49
+ },
50
+ "peerDependencies": {
51
+ "putout": ">=41"
52
+ },
53
+ "license": "MIT",
54
+ "engines": {
55
+ "node": ">=20"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ }
60
+ }