@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,589 @@
1
+ import {operator, types} from 'putout';
2
+ import jsx from './jsx.js';
3
+ import typescript from './typescript.js';
4
+ import {createExportNamedDeclaration} from './visitors/export-named-declaration.js';
5
+ import {
6
+ traverseObjectExpression,
7
+ processObjectPattern,
8
+ traverseArrayExpression,
9
+ traverseAssignmentExpression,
10
+ traverseTemplateLiteral,
11
+ } from './traverse.js';
12
+
13
+ const {assign} = Object;
14
+ const {isKeyword} = operator;
15
+
16
+ const {
17
+ isAssignmentPattern,
18
+ isClassDeclaration,
19
+ isIdentifier,
20
+ isSpreadElement,
21
+ isObjectPattern,
22
+ isObjectExpression,
23
+ isFunctionDeclaration,
24
+ isArrayExpression,
25
+ isRestElement,
26
+ } = types;
27
+
28
+ export default ({use, declare, addParams}) => {
29
+ const traverseObj = traverseObjectExpression(use);
30
+
31
+ const processObj = processObjectPattern({
32
+ use,
33
+ declare,
34
+ });
35
+
36
+ const traverseAssign = traverseAssignmentExpression({
37
+ use,
38
+ declare,
39
+ });
40
+
41
+ const traverseTmpl = traverseTemplateLiteral(use);
42
+ const traverseArray = traverseArrayExpression(use);
43
+
44
+ return {
45
+ 'ObjectExpression'(path) {
46
+ traverseObj(path.get('properties'));
47
+ },
48
+ SequenceExpression(path) {
49
+ for (const exprPath of path.get('expressions')) {
50
+ if (exprPath.isIdentifier())
51
+ use(exprPath, exprPath.node.name);
52
+ }
53
+ },
54
+
55
+ 'AwaitExpression|YieldExpression|SpreadElement'(path) {
56
+ const {argument} = path.node;
57
+
58
+ if (isIdentifier(argument))
59
+ use(path, argument.name);
60
+ },
61
+
62
+ CatchClause(path) {
63
+ const param = path.get('param');
64
+
65
+ if (param.isObjectPattern())
66
+ return processObj(param.get('properties'));
67
+
68
+ if (!param.isIdentifier())
69
+ return;
70
+
71
+ const {name} = param.node;
72
+
73
+ declare(param, name);
74
+
75
+ const binding = path.scope.getOwnBinding(name);
76
+
77
+ if (binding.referenced)
78
+ use(param, name);
79
+ },
80
+
81
+ Decorator(path) {
82
+ const expressionPath = path.get('expression');
83
+
84
+ if (!expressionPath.isIdentifier())
85
+ return;
86
+
87
+ const {name} = expressionPath.node;
88
+
89
+ use(expressionPath, name);
90
+ },
91
+
92
+ RestElement(path) {
93
+ const {argument} = path.node;
94
+
95
+ /* istanbul ignore else */
96
+ if (isIdentifier(argument))
97
+ declare(path, argument.name);
98
+ },
99
+
100
+ VariableDeclarator(path) {
101
+ const {node} = path;
102
+ const {init} = node;
103
+ const idPath = path.get('id');
104
+ const isForIn = path.parentPath.parentPath.isForInStatement();
105
+ const isTSModule = path.parentPath.parentPath.isTSModuleBlock();
106
+
107
+ /* istanbul ignore else */
108
+ if (isIdentifier(node.id)) {
109
+ if (!isKeyword(node.id.name)) {
110
+ declare(path, node.id.name);
111
+
112
+ if (isForIn || isTSModule)
113
+ use(path, node.id.name);
114
+ }
115
+ } else if (isObjectPattern(node.id)) {
116
+ idPath.traverse({
117
+ ObjectProperty(propPath) {
118
+ if (isAssignmentPattern(propPath.node.value))
119
+ traverseAssign(propPath.get('value'));
120
+
121
+ if (propPath.node.computed && isIdentifier(propPath.node.key))
122
+ use(propPath.get('key'), propPath.node.key.name);
123
+
124
+ if (!isIdentifier(propPath.node.value))
125
+ return;
126
+
127
+ const {properties} = node.id;
128
+ const isOne = properties.length === 1;
129
+ const nodePath = isOne ? path : propPath;
130
+ const {name} = propPath.node.value;
131
+
132
+ declare(nodePath, name);
133
+
134
+ if (isRestElement(propPath.parentPath.node.properties.at(-1)))
135
+ use(nodePath, name);
136
+ },
137
+ });
138
+ } else if (idPath.isArrayPattern()) {
139
+ const elements = idPath.get('elements');
140
+
141
+ for (const elPath of elements) {
142
+ if (elPath.isObjectPattern()) {
143
+ processObj(elPath.get('properties'));
144
+ continue;
145
+ }
146
+
147
+ if (elPath.isAssignmentPattern()) {
148
+ const leftPath = elPath.get('left');
149
+ const rightPath = elPath.get('right');
150
+
151
+ declare(leftPath, elPath.node.left.name);
152
+ use(rightPath, elPath.node.right.name);
153
+
154
+ assign(leftPath, {
155
+ remove: removeArrayPatternElement(elPath),
156
+ });
157
+ }
158
+
159
+ if (elPath.isIdentifier()) {
160
+ assign(elPath, {
161
+ remove: removeArrayPatternElement(elPath),
162
+ });
163
+
164
+ declare(elPath, elPath.node.name);
165
+ }
166
+ }
167
+ }
168
+
169
+ const initPath = path.get('init');
170
+
171
+ if (isIdentifier(init))
172
+ use(path, init.name);
173
+ else if (isArrayExpression(init))
174
+ traverseArray(initPath.get('elements'));
175
+ },
176
+
177
+ 'ClassProperty'(path) {
178
+ const valuePath = path.get('value');
179
+
180
+ if (valuePath.isIdentifier())
181
+ use(valuePath, valuePath.node.name);
182
+ },
183
+
184
+ 'ClassDeclaration|ClassExpression'(path) {
185
+ const {node} = path;
186
+
187
+ const {id, superClass} = node;
188
+
189
+ if (superClass)
190
+ use(path, superClass.name);
191
+
192
+ if (id)
193
+ declare(path, id.name);
194
+
195
+ if (id && path.isClassExpression())
196
+ use(path, id.name);
197
+ },
198
+
199
+ AssignmentExpression(path) {
200
+ const leftPath = path.get('left');
201
+ const rightPath = path.get('right');
202
+
203
+ if (leftPath.isIdentifier())
204
+ use(leftPath, leftPath.node.name);
205
+
206
+ if (rightPath.isIdentifier())
207
+ use(rightPath, rightPath.node.name);
208
+ },
209
+
210
+ 'ArrayExpression'(path) {
211
+ const {elements} = path.node;
212
+
213
+ for (const el of elements) {
214
+ if (isIdentifier(el)) {
215
+ use(path, el.name);
216
+ continue;
217
+ }
218
+
219
+ if (isSpreadElement(el)) {
220
+ use(path, el.argument.name);
221
+ continue;
222
+ }
223
+ }
224
+ },
225
+
226
+ ConditionalExpression(path) {
227
+ const {
228
+ test,
229
+ consequent,
230
+ alternate,
231
+ } = path.node;
232
+
233
+ const alternatePath = path.get('alternate');
234
+ const consequentPath = path.get('consequent');
235
+
236
+ if (isIdentifier(test))
237
+ use(path, test.name);
238
+
239
+ if (isIdentifier(consequent))
240
+ use(path, consequent.name);
241
+
242
+ if (isIdentifier(alternate))
243
+ use(path, alternate.name);
244
+
245
+ if (alternatePath.isFunction() && alternate.id)
246
+ use(alternatePath, alternate.id.name);
247
+
248
+ if (consequentPath.isFunction() && consequent.id)
249
+ use(consequentPath, consequent.id.name);
250
+ },
251
+ DoWhileStatement(path) {
252
+ const testPath = path.get('test');
253
+
254
+ if (testPath.isIdentifier())
255
+ use(testPath, testPath.node.name);
256
+ },
257
+
258
+ TemplateLiteral(path) {
259
+ traverseTmpl(path, path.node.expressions);
260
+ },
261
+
262
+ LogicalExpression(path) {
263
+ const {left, right} = path.node;
264
+
265
+ if (isIdentifier(left))
266
+ use(path, left.name);
267
+
268
+ if (isIdentifier(right))
269
+ use(path, right.name);
270
+ },
271
+
272
+ MemberExpression(path) {
273
+ const {
274
+ property,
275
+ object,
276
+ computed,
277
+ } = path.node;
278
+
279
+ if (isIdentifier(object))
280
+ use(path, object.name);
281
+
282
+ if (computed && isIdentifier(property))
283
+ use(path, property.name);
284
+ },
285
+
286
+ OptionalMemberExpression(path) {
287
+ const {
288
+ object,
289
+ property,
290
+ computed,
291
+ } = path.node;
292
+
293
+ if (isIdentifier(object))
294
+ use(path, object.name);
295
+
296
+ if (computed && isIdentifier(property))
297
+ use(path, property.name);
298
+ },
299
+
300
+ NewExpression(path) {
301
+ const calleePath = path.get('callee');
302
+ const {node} = path;
303
+
304
+ if (calleePath.isIdentifier())
305
+ use(path, node.callee.name);
306
+ else if (calleePath.isFunction())
307
+ use(calleePath, calleePath.node.id.name);
308
+
309
+ const argPaths = path.get('arguments');
310
+
311
+ for (const {node} of argPaths) {
312
+ if (isIdentifier(node)) {
313
+ use(path, node.name);
314
+ continue;
315
+ }
316
+ }
317
+ },
318
+
319
+ TaggedTemplateExpression(path) {
320
+ const {tag} = path.node;
321
+
322
+ /* istanbul ignore else */
323
+ if (isIdentifier(tag))
324
+ use(path, tag.name);
325
+ },
326
+
327
+ UnaryExpression(path) {
328
+ const {argument} = path.node;
329
+
330
+ /* istanbul ignore else */
331
+ if (isIdentifier(argument))
332
+ use(path, argument.name);
333
+ },
334
+
335
+ UpdateExpression(path) {
336
+ const {argument} = path.node;
337
+
338
+ /* istanbul ignore else */
339
+ if (isIdentifier(argument))
340
+ use(path, argument.name);
341
+ },
342
+
343
+ ThrowStatement(path) {
344
+ const {argument} = path.node;
345
+
346
+ if (isIdentifier(argument))
347
+ use(path, argument.name);
348
+ },
349
+
350
+ IfStatement(path) {
351
+ const {node} = path;
352
+ const {test} = node;
353
+
354
+ if (isIdentifier(test))
355
+ return use(path, test.name);
356
+ },
357
+
358
+ ForInStatement(path) {
359
+ const {node} = path;
360
+
361
+ const {left, right} = node;
362
+
363
+ if (isIdentifier(left))
364
+ use(path, left.name);
365
+
366
+ /* istanbul ignore else */
367
+ if (isIdentifier(right))
368
+ use(path, right.name);
369
+ },
370
+
371
+ ForOfStatement(path) {
372
+ const {node} = path;
373
+
374
+ const {left, right} = node;
375
+
376
+ if (isIdentifier(right))
377
+ use(path, right.name);
378
+
379
+ if (isIdentifier(left))
380
+ use(path, left.name);
381
+ else
382
+ path.get('left').traverse({
383
+ Identifier(leftPath) {
384
+ declare(path, leftPath.node.name);
385
+ },
386
+ });
387
+
388
+ path.get('left').traverse({
389
+ Identifier(path) {
390
+ use(path, path.node.name);
391
+ },
392
+ });
393
+ },
394
+
395
+ ExpressionStatement(path) {
396
+ const {node} = path;
397
+
398
+ if (isIdentifier(node.expression))
399
+ use(path, node.expression.name);
400
+ },
401
+
402
+ SwitchStatement(path) {
403
+ const {node} = path;
404
+
405
+ if (isIdentifier(node.discriminant))
406
+ use(path, node.discriminant.name);
407
+
408
+ for (const {test} of node.cases) {
409
+ if (isIdentifier(test))
410
+ use(path, test.name);
411
+ }
412
+ },
413
+
414
+ ReturnStatement(path) {
415
+ const {node} = path;
416
+ const {argument} = node;
417
+ const argumentPath = path.get('argument');
418
+
419
+ if (argumentPath.isIdentifier())
420
+ return use(path, argument.name);
421
+
422
+ if (argumentPath.isFunction() && argument.id)
423
+ return use(argumentPath, argument.id.name);
424
+ },
425
+
426
+ ObjectMethod(path) {
427
+ const {params} = path.node;
428
+ const paramsPaths = path.get('params');
429
+
430
+ for (const paramPath of paramsPaths) {
431
+ const {node} = paramPath;
432
+
433
+ /* istanbul ignore else */
434
+ if (isIdentifier(node)) {
435
+ declare(paramPath, node.name);
436
+ continue;
437
+ }
438
+ }
439
+
440
+ addParams({
441
+ path,
442
+ params,
443
+ });
444
+ },
445
+
446
+ 'CallExpression|OptionalCallExpression'(path) {
447
+ const {node} = path;
448
+ const {callee} = node;
449
+
450
+ if (isIdentifier(callee))
451
+ use(path, node.callee.name);
452
+
453
+ path.traverse({
454
+ SpreadElement(path) {
455
+ const {argument} = path.node;
456
+
457
+ if (isIdentifier(argument))
458
+ use(path, argument.name);
459
+ },
460
+ Identifier(path) {
461
+ if (node.arguments.includes(path.node))
462
+ use(path, path.node.name);
463
+ },
464
+ });
465
+ },
466
+
467
+ BinaryExpression(path) {
468
+ const {left, right} = path.node;
469
+
470
+ if (isIdentifier(left))
471
+ use(path, left.name);
472
+
473
+ if (isIdentifier(right))
474
+ use(path, right.name);
475
+ },
476
+
477
+ ImportExpression(path) {
478
+ const {source} = path.node;
479
+ const {name} = source;
480
+
481
+ if (isIdentifier(source, {name}))
482
+ use(path, name);
483
+ },
484
+
485
+ ImportDeclaration(path) {
486
+ const specifierPaths = path.get('specifiers');
487
+
488
+ for (const specPath of specifierPaths) {
489
+ const {local} = specPath.node;
490
+
491
+ if (isIdentifier(local))
492
+ declare(specPath, local.name);
493
+ }
494
+ },
495
+
496
+ ExportDefaultDeclaration(path) {
497
+ const declarationPath = path.get('declaration');
498
+ const {declaration} = path.node;
499
+ const {id} = declaration;
500
+
501
+ if (id && isFunctionDeclaration(declaration))
502
+ return use(path, declaration.id.name);
503
+
504
+ if (isIdentifier(declaration))
505
+ return use(path, declaration.name);
506
+
507
+ if (id && isClassDeclaration(declaration))
508
+ return use(path, declaration.id.name);
509
+
510
+ if (isObjectExpression(declaration))
511
+ return traverseObj(declarationPath.get('properties'));
512
+ },
513
+ ExportNamedDeclaration: createExportNamedDeclaration({
514
+ use,
515
+ }),
516
+ Function(path) {
517
+ const {node, parentPath} = path;
518
+ const {
519
+ id,
520
+ body,
521
+ params,
522
+ } = node;
523
+
524
+ const paramsPaths = path.get('params');
525
+
526
+ if (id) {
527
+ declare(path, node.id.name);
528
+
529
+ if (!parentPath.isBlock() && !parentPath.isProgram())
530
+ use(path, node.id.name);
531
+ }
532
+
533
+ const paramsCount = paramsPaths.length;
534
+
535
+ for (const [i, paramPath] of paramsPaths.entries()) {
536
+ const {node} = paramPath;
537
+
538
+ if (isIdentifier(node)) {
539
+ declare(paramPath, node.name);
540
+
541
+ if (!i && paramsCount > 1)
542
+ use(paramPath, node.name);
543
+
544
+ continue;
545
+ }
546
+
547
+ if (isAssignmentPattern(node)) {
548
+ traverseAssign(paramPath);
549
+ continue;
550
+ }
551
+
552
+ /* istanbul ignore else */
553
+ if (isObjectPattern(node)) {
554
+ processObj(paramPath.get('properties'));
555
+ continue;
556
+ }
557
+ }
558
+
559
+ // ArrowFunction only
560
+ if (isIdentifier(body))
561
+ use(path, body.name);
562
+
563
+ addParams({
564
+ path,
565
+ params,
566
+ });
567
+ },
568
+ ...jsx(use),
569
+ ...typescript({
570
+ use,
571
+ declare,
572
+ }),
573
+ };
574
+ };
575
+
576
+ const removeArrayPatternElement = (elPath) => {
577
+ const {remove} = elPath;
578
+
579
+ return () => {
580
+ const el = elPath.node;
581
+ const {elements} = elPath.parentPath.node;
582
+
583
+ const n = elements.length - 1;
584
+ const i = elements.indexOf(el);
585
+
586
+ if (i === n)
587
+ return remove.call(elPath);
588
+ };
589
+ };