@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.
@@ -0,0 +1,148 @@
1
+ import {types} from 'putout';
2
+ import getVars from './get-vars.js';
3
+ import {
4
+ useParamsBeforeLastUsed,
5
+ useParamsBeforeAssign,
6
+ usePropertiesBeforeRest,
7
+ } from './use-params.js';
8
+
9
+ const {
10
+ isClassDeclaration,
11
+ isFunctionDeclaration,
12
+ } = types;
13
+
14
+ export default (ast, opts) => {
15
+ const vars = {};
16
+ const allParams = [];
17
+
18
+ const {setPath, traverse} = opts;
19
+ const use = useVariable({
20
+ vars,
21
+ });
22
+
23
+ const declare = declareVariable({
24
+ vars,
25
+ setPath,
26
+ });
27
+
28
+ const isUsed = isUsedVariable({
29
+ vars,
30
+ });
31
+
32
+ const addParams = addParamsVariable(allParams);
33
+
34
+ traverse(ast, getVars({
35
+ use,
36
+ declare,
37
+ addParams,
38
+ }));
39
+
40
+ allParams
41
+ .map(useParamsBeforeLastUsed({
42
+ use,
43
+ isUsed,
44
+ }))
45
+ .map(usePropertiesBeforeRest({
46
+ use,
47
+ }))
48
+ .map(useParamsBeforeAssign({
49
+ use,
50
+ isUsed,
51
+ }));
52
+
53
+ return Object.values(vars);
54
+ };
55
+
56
+ const addParamsVariable = (allParams) => ({path, params}) => {
57
+ allParams.push({
58
+ path,
59
+ params,
60
+ });
61
+ };
62
+
63
+ function getScopeUID({name, scope}) {
64
+ if (scope.hasOwnBinding(name))
65
+ return scope.uid;
66
+
67
+ while (scope.parent) {
68
+ scope = scope.parent;
69
+
70
+ if (scope.hasOwnBinding(name))
71
+ return scope.uid;
72
+ }
73
+
74
+ return scope.uid;
75
+ }
76
+
77
+ const isUsedVariable = ({vars}) => (path, name) => {
78
+ const {scope} = path;
79
+
80
+ const uid = getScopeUID({
81
+ name,
82
+ scope,
83
+ });
84
+
85
+ const current = vars[uid];
86
+ const {used} = current[name];
87
+
88
+ return used;
89
+ };
90
+
91
+ function getScope(path) {
92
+ const {node, scope} = path;
93
+
94
+ if (isFunctionDeclaration(node))
95
+ return path.parentPath.scope;
96
+
97
+ if (isClassDeclaration(node))
98
+ return path.parentPath.scope;
99
+
100
+ return scope;
101
+ }
102
+
103
+ const declareVariable = ({vars, setPath}) => (path, name) => {
104
+ const scope = getScope(path);
105
+
106
+ const uid = getScopeUID({
107
+ name,
108
+ scope,
109
+ });
110
+
111
+ if (!vars[uid])
112
+ vars[uid] = {};
113
+
114
+ const current = vars[uid];
115
+
116
+ if (current[name])
117
+ current[name].declared = true;
118
+ else
119
+ current[name] = {
120
+ declared: true,
121
+ used: false,
122
+ };
123
+
124
+ if (setPath)
125
+ current[name].path = path;
126
+ };
127
+
128
+ const useVariable = ({vars}) => (path, name) => {
129
+ const {scope} = path;
130
+
131
+ const uid = getScopeUID({
132
+ name,
133
+ scope,
134
+ });
135
+
136
+ if (!vars[uid])
137
+ vars[uid] = {};
138
+
139
+ const current = vars[uid];
140
+
141
+ if (current[name])
142
+ current[name].used = true;
143
+ else
144
+ current[name] = {
145
+ declared: false,
146
+ used: true,
147
+ };
148
+ };
@@ -0,0 +1,45 @@
1
+ import {types} from 'putout';
2
+
3
+ const {
4
+ isIdentifier,
5
+ isJSXIdentifier,
6
+ } = types;
7
+
8
+ export default (use) => ({
9
+ JSXOpeningElement(path) {
10
+ const {node} = path;
11
+ const {name} = node;
12
+
13
+ if (/^[A-Z]/.test(name.name))
14
+ use(path, name.name);
15
+
16
+ use(path, 'React');
17
+ },
18
+
19
+ JSXFragment(path) {
20
+ use(path, 'React');
21
+ },
22
+
23
+ JSXSpreadAttribute(path) {
24
+ const argPath = path.get('argument');
25
+
26
+ if (argPath.isIdentifier())
27
+ return use(path, argPath.node.name);
28
+ },
29
+
30
+ JSXMemberExpression(path) {
31
+ const {node} = path;
32
+ const {object} = node;
33
+
34
+ if (isJSXIdentifier(object))
35
+ use(path, object.name);
36
+ },
37
+
38
+ JSXExpressionContainer(path) {
39
+ const {node} = path;
40
+ const {expression} = node;
41
+
42
+ if (isIdentifier(expression))
43
+ use(path, expression.name);
44
+ },
45
+ });
@@ -0,0 +1,179 @@
1
+ import {types} from 'putout';
2
+
3
+ const {
4
+ isIdentifier,
5
+ isObjectExpression,
6
+ isObjectPattern,
7
+ isTemplateLiteral,
8
+ isAssignmentPattern,
9
+ } = types;
10
+
11
+ export const traverseObjectPattern = ({use, declare}) => {
12
+ const traverseAssign = traverseAssignmentPattern({
13
+ use,
14
+ });
15
+
16
+ return (propertiesPaths) => {
17
+ for (const path of propertiesPaths) {
18
+ if (path.isRestElement())
19
+ continue;
20
+
21
+ const {key, value} = path.node;
22
+ const valuePath = path.get('value');
23
+
24
+ switch(value.type) {
25
+ case 'Identifier':
26
+ declare(path, value.name);
27
+ break;
28
+
29
+ case 'AssignmentPattern':
30
+ if (path.node.shorthand)
31
+ declare(path, key.name);
32
+ else
33
+ declare(path, valuePath.node.left.name);
34
+
35
+ traverseAssign(valuePath);
36
+ break;
37
+ }
38
+ }
39
+ };
40
+ };
41
+
42
+ export const processObjectPattern = ({use, declare}) => (propertiesPaths) => {
43
+ for (const path of propertiesPaths) {
44
+ const {
45
+ key,
46
+ value,
47
+ computed,
48
+ } = path.node;
49
+
50
+ if (computed && isIdentifier(key))
51
+ use(path, key.name);
52
+
53
+ if (isIdentifier(value)) {
54
+ declare(path, value.name);
55
+ continue;
56
+ }
57
+
58
+ if (isObjectPattern(value)) {
59
+ const process = processObjectPattern({
60
+ use,
61
+ declare,
62
+ });
63
+
64
+ process(path.get('value.properties'));
65
+ continue;
66
+ }
67
+
68
+ if (isAssignmentPattern(value)) {
69
+ const useAssignment = traverseAssignmentPattern({
70
+ use,
71
+ });
72
+
73
+ useAssignment(path.get('value.right'));
74
+
75
+ const leftPath = path.get('value.left');
76
+
77
+ switch(leftPath.type) {
78
+ case 'Identifier':
79
+ declare(path, leftPath.node.name);
80
+ continue;
81
+ }
82
+ }
83
+ }
84
+ };
85
+
86
+ export const traverseObjectExpression = (use) => {
87
+ const traverseTmpl = traverseTemplateLiteral(use);
88
+
89
+ return (propertiesPaths) => {
90
+ for (const path of propertiesPaths) {
91
+ const {node} = path;
92
+
93
+ const {
94
+ key,
95
+ value,
96
+ computed,
97
+ } = node;
98
+
99
+ if (computed && isIdentifier(key))
100
+ use(path, key.name);
101
+
102
+ if (isIdentifier(value)) {
103
+ use(path, value.name);
104
+ continue;
105
+ }
106
+
107
+ if (isTemplateLiteral(value)) {
108
+ traverseTmpl(path, value.expressions);
109
+ continue;
110
+ }
111
+
112
+ if (isObjectExpression(value)) {
113
+ const traverseObj = traverseObjectExpression(use);
114
+ traverseObj(path.get('value.properties'));
115
+ continue;
116
+ }
117
+ }
118
+ };
119
+ };
120
+
121
+ export const traverseArrayExpression = (use) => {
122
+ const traverseObjExpression = traverseObjectExpression(use);
123
+
124
+ return (elementsPaths) => {
125
+ for (const elementPath of elementsPaths) {
126
+ const {node} = elementPath;
127
+
128
+ if (isObjectExpression(node))
129
+ traverseObjExpression(elementPath.get('properties'));
130
+ }
131
+ };
132
+ };
133
+
134
+ export const traverseAssignmentExpression = ({use, declare}) => {
135
+ const traverseObjPattern = traverseObjectPattern({
136
+ use,
137
+ declare,
138
+ });
139
+
140
+ return (path) => {
141
+ const leftPath = path.get('left');
142
+ const rightPath = path.get('right');
143
+
144
+ if (leftPath.isIdentifier()) {
145
+ const {parentPath} = leftPath.parentPath;
146
+ const {name} = leftPath.node;
147
+
148
+ if (parentPath.isObjectProperty())
149
+ declare(parentPath, name);
150
+
151
+ if (parentPath.isFunction())
152
+ declare(leftPath.parentPath, name);
153
+ }
154
+
155
+ if (rightPath.isIdentifier())
156
+ use(rightPath, rightPath.node.name);
157
+
158
+ if (leftPath.isObjectPattern())
159
+ traverseObjPattern(leftPath.get('properties'));
160
+ };
161
+ };
162
+
163
+ export const traverseTemplateLiteral = (use) => (path, expressions) => {
164
+ for (const exp of expressions) {
165
+ if (isIdentifier(exp))
166
+ use(path, exp.name);
167
+ }
168
+ };
169
+
170
+ export const traverseAssignmentPattern = ({use}) => (path) => {
171
+ const {node} = path;
172
+ const {right} = node;
173
+
174
+ if (isIdentifier(node))
175
+ use(path.parentPath, node.name);
176
+
177
+ if (isIdentifier(right))
178
+ use(path, right.name);
179
+ };
@@ -0,0 +1,134 @@
1
+ import {types} from 'putout';
2
+
3
+ const {
4
+ isIdentifier,
5
+ isTSModuleDeclaration,
6
+ } = types;
7
+
8
+ export default ({use, declare}) => ({
9
+ 'TSClassImplements|TSInterfaceHeritage'(path) {
10
+ const {expression} = path.node;
11
+ const {type} = expression;
12
+
13
+ switch(type) {
14
+ case 'Identifier':
15
+ use(path, expression.name);
16
+ }
17
+ },
18
+
19
+ TSFunctionType(path) {
20
+ const {node} = path;
21
+ const {params} = node;
22
+
23
+ for (const param of params) {
24
+ const {type} = param;
25
+
26
+ switch(type) {
27
+ case 'Identifier':
28
+ declare(path, param.name);
29
+ use(path, param.name);
30
+ break;
31
+
32
+ case 'RestElement':
33
+ if (isIdentifier(param.argument)) {
34
+ declare(path, param.argument.name);
35
+ use(path, param.argument.name);
36
+ }
37
+ }
38
+ }
39
+ },
40
+
41
+ TSTypeReference(path) {
42
+ const {node} = path;
43
+ const {typeName} = node;
44
+ const {type} = typeName;
45
+
46
+ switch(type) {
47
+ case 'Identifier':
48
+ use(path, typeName.name);
49
+ }
50
+ },
51
+
52
+ TSTypeAliasDeclaration(path) {
53
+ const {node} = path;
54
+ const {id} = node;
55
+ const {type} = id;
56
+
57
+ switch(type) {
58
+ case 'Identifier':
59
+ declare(path, id.name);
60
+ }
61
+ },
62
+
63
+ TSTypeQuery(path) {
64
+ const {node} = path;
65
+ const {exprName} = node;
66
+ const {type} = exprName;
67
+
68
+ switch(type) {
69
+ case 'Identifier':
70
+ use(path, exprName.name);
71
+ }
72
+ },
73
+
74
+ TSAsExpression(path) {
75
+ const {node} = path;
76
+ const {expression} = node;
77
+ const {type} = expression;
78
+
79
+ switch(type) {
80
+ case 'Identifier':
81
+ use(path, expression.name);
82
+ }
83
+ },
84
+
85
+ TSQualifiedName(path) {
86
+ const {node} = path;
87
+ const {left} = node;
88
+ const {type} = left;
89
+
90
+ switch(type) {
91
+ case 'Identifier':
92
+ use(path, left.name);
93
+ }
94
+ },
95
+
96
+ TSInterfaceDeclaration(path) {
97
+ declare(path, path.node.id.name);
98
+
99
+ if (path.findParent(isTSModuleDeclaration))
100
+ use(path, path.node.id.name);
101
+ },
102
+
103
+ TSMethodSignature(path) {
104
+ const params = path.get('params');
105
+
106
+ for (const paramPath of params) {
107
+ if (paramPath.isIdentifier()) {
108
+ declare(paramPath, paramPath.node.name);
109
+ use(paramPath, paramPath.node.name);
110
+ continue;
111
+ }
112
+
113
+ const {type} = paramPath;
114
+
115
+ switch(type) {
116
+ case 'RestElement':
117
+ declare(paramPath, paramPath.node.argument.name);
118
+ use(paramPath, paramPath.node.argument.name);
119
+ continue;
120
+ }
121
+ }
122
+ },
123
+ TSDeclareFunction(path) {
124
+ const params = path.get('params');
125
+
126
+ if (!params.length)
127
+ return;
128
+
129
+ const [firstPath] = path.get('params');
130
+
131
+ if (firstPath.isRestElement())
132
+ use(firstPath, firstPath.node.argument.name);
133
+ },
134
+ });
@@ -0,0 +1,123 @@
1
+ import {types} from 'putout';
2
+
3
+ const {
4
+ isIdentifier,
5
+ isObjectPattern,
6
+ isRestElement,
7
+ isAssignmentPattern,
8
+ } = types;
9
+
10
+ export const usePropertiesBeforeRest = ({use}) => ({path, params}) => {
11
+ for (const param of params) {
12
+ if (!isObjectPattern(param))
13
+ continue;
14
+
15
+ traverseProperties(use, path, param.properties);
16
+ }
17
+
18
+ return {
19
+ path,
20
+ params,
21
+ };
22
+ };
23
+
24
+ export const useParamsBeforeAssign = ({use}) => ({path, params}) => {
25
+ const last = params.at(-1);
26
+ const usedParams = params.slice(0, -1);
27
+
28
+ if (!isAssignmentPattern(last))
29
+ return;
30
+
31
+ for (const param of usedParams) {
32
+ if (isIdentifier(param))
33
+ use(path, param.name);
34
+ }
35
+
36
+ return {
37
+ path,
38
+ params,
39
+ };
40
+ };
41
+
42
+ function traverseProperties(use, path, properties) {
43
+ const {length} = properties;
44
+ const last = properties.at(-1);
45
+
46
+ if (!isRestElement(last))
47
+ return;
48
+
49
+ for (let i = 0; i < length - 1; i++) {
50
+ const {value} = properties[i];
51
+ const {type} = value;
52
+
53
+ switch(type) {
54
+ case 'Identifier':
55
+ use(path, value.name);
56
+ continue;
57
+ }
58
+ }
59
+ }
60
+
61
+ export const useParamsBeforeLastUsed = ({use, isUsed}) => ({path, params}) => {
62
+ let i = params.length;
63
+
64
+ while (--i > 0) {
65
+ const param = params[i];
66
+
67
+ if (traverseIsUsed(isUsed, path, param))
68
+ break;
69
+ }
70
+
71
+ while (--i >= 0) {
72
+ if (!isIdentifier(params[i]))
73
+ continue;
74
+
75
+ use(path, params[i].name);
76
+ }
77
+
78
+ return {
79
+ path,
80
+ params,
81
+ };
82
+ };
83
+
84
+ const traverseIsUsed = (isUsed, path, node) => {
85
+ const {type} = node;
86
+
87
+ switch(type) {
88
+ case 'Identifier':
89
+ return isUsed(path, node.name);
90
+
91
+ case 'ObjectPattern':
92
+ return isUsedObjectPattern(isUsed, path, node);
93
+
94
+ case 'AssignmentPattern':
95
+ return isUsedAssignmentPattern(isUsed, path, node);
96
+ }
97
+ };
98
+
99
+ const isUsedAssignmentPattern = (isUsed, path, node) => {
100
+ const {left} = node;
101
+ const {type} = left;
102
+
103
+ switch(type) {
104
+ case 'Identifier':
105
+ return isUsed(path, left.name);
106
+
107
+ case 'ObjectPattern':
108
+ return isUsedObjectPattern(isUsed, path, left);
109
+ }
110
+ };
111
+
112
+ const isUsedObjectPattern = (isUsed, path, node) => {
113
+ for (const {value} of node.properties) {
114
+ if (isIdentifier(value)) {
115
+ if (isUsed(path, value.name))
116
+ return true;
117
+
118
+ continue;
119
+ }
120
+ }
121
+
122
+ return false;
123
+ };
@@ -0,0 +1,61 @@
1
+ import {types} from 'putout';
2
+
3
+ const {
4
+ isArrayPattern,
5
+ isRestElement,
6
+ isObjectPattern,
7
+ isIdentifier,
8
+ isVariableDeclaration,
9
+ } = types;
10
+
11
+ export const createExportNamedDeclaration = ({use}) => (path) => {
12
+ const declarationPath = path.get('declaration');
13
+ const {declaration, specifiers} = path.node;
14
+
15
+ if (declarationPath.isFunctionDeclaration())
16
+ return use(path, declaration.id.name);
17
+
18
+ if (declarationPath.isClassDeclaration())
19
+ return use(path, declaration.id.name);
20
+
21
+ // typescript
22
+ if (declarationPath.isTSInterfaceDeclaration())
23
+ return use(path, declaration.id.name);
24
+
25
+ if (declarationPath.isTSTypeAliasDeclaration())
26
+ return use(path, declaration.id.name);
27
+
28
+ if (isVariableDeclaration(declaration)) {
29
+ const {declarations} = declaration;
30
+
31
+ for (const {id} of declarations) {
32
+ /* istanbul ignore else */
33
+ if (isIdentifier(id))
34
+ use(path, id.name);
35
+
36
+ if (isObjectPattern(id))
37
+ for (const property of id.properties) {
38
+ if (isRestElement(property) && isIdentifier(property.argument)) {
39
+ use(path, property.argument.name);
40
+ continue;
41
+ }
42
+
43
+ if (isIdentifier(property.value))
44
+ use(path, property.value.name);
45
+ }
46
+
47
+ if (isArrayPattern(id))
48
+ for (const element of id.elements)
49
+ if (isIdentifier(element))
50
+ use(path, element.name);
51
+ }
52
+
53
+ return;
54
+ }
55
+
56
+ for (const {local} of specifiers) {
57
+ /* istanbul ignore else */
58
+ if (isIdentifier(local))
59
+ use(path, local.name);
60
+ }
61
+ };