rolldown-plugin-angularjs-annotate 0.1.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,1687 @@
1
+ 'use strict';
2
+
3
+ const REGISTRATION_METHODS = new Set([
4
+ 'animation', 'component', 'config', 'controller', 'decorator', 'directive', 'factory', 'filter',
5
+ 'invoke', 'provider', 'run', 'service', 'store'
6
+ ]);
7
+ const CHAIN_ONLY_METHODS = new Set(['bootstrap', 'constant', 'value']);
8
+ const STATE_PROPERTIES = new Set(['controller', 'controllerProvider', 'onEnter', 'onExit', 'templateProvider']);
9
+ const VIEW_PROPERTIES = new Set(['controller', 'controllerProvider', 'templateProvider']);
10
+ const COMPONENT_PROPERTIES = new Set(['controller', 'template', 'templateUrl']);
11
+ const STANDALONE_STATEMENTS = new Set([
12
+ 'ClassDeclaration', 'ExportDefaultDeclaration', 'ExportNamedDeclaration', 'ExpressionStatement',
13
+ 'FunctionDeclaration', 'VariableDeclaration'
14
+ ]);
15
+ const AMBIGUOUS_WRITE_ANCESTORS = new Set([
16
+ 'CatchClause', 'ConditionalExpression', 'DoWhileStatement', 'ForInStatement', 'ForOfStatement',
17
+ 'ForStatement', 'IfStatement', 'LogicalExpression', 'SwitchCase', 'TryStatement', 'WhileStatement'
18
+ ]);
19
+ const WRAPPER_TYPES = new Set([
20
+ 'ChainExpression', 'ParenthesizedExpression', 'TSAsExpression', 'TSInstantiationExpression', 'TSNonNullExpression',
21
+ 'TSSatisfiesExpression', 'TSTypeAssertion'
22
+ ]);
23
+ const AMD_LOADER_NAMES = new Set(['define', 'require', 'requirejs']);
24
+ const ANNOTATION_WRAPPER_NAMES = new Set(['ngInject', 'ngNoInject']);
25
+ const DEFAULT_MODULE_REGEXP = /^[a-zA-Z0-9_$\.\s]+$/;
26
+
27
+ module.exports = function annotate(program, code, magicString, options = {}) {
28
+ const annotator = new Annotator(program, code, magicString, options);
29
+ annotator.run();
30
+ };
31
+
32
+ class Annotator {
33
+ constructor(program, code, magicString, options) {
34
+ this.program = program;
35
+ this.code = code;
36
+ this.magicString = magicString;
37
+ this.options = options || {};
38
+ this.moduleRegexp = normalizeModuleRegexp(this.options.regexp);
39
+
40
+ this.nodes = [];
41
+ this.calls = [];
42
+ this.parents = new WeakMap();
43
+ this.methodProperties = new WeakMap();
44
+ this.nodeScopes = new WeakMap();
45
+ this.functionScopes = new WeakMap();
46
+ this.bindingByValue = new WeakMap();
47
+ this.bindingByDeclaration = new WeakMap();
48
+ this.regularInfoCache = new WeakMap();
49
+
50
+ this.explicitActions = [];
51
+ this.suppressedFunctionDirectives = new WeakSet();
52
+ this.blockedNodes = new WeakSet();
53
+ this.blockedBindings = new Set();
54
+ this.blockedWrites = new Set();
55
+ this.contextRoots = new WeakSet();
56
+ this.contextRootCount = 0;
57
+ this.processedHelpers = new WeakSet();
58
+ this.processedMethods = new WeakMap();
59
+ this.queue = [];
60
+
61
+ this.plannedNodes = new WeakSet();
62
+ this.before = new Map();
63
+ this.after = new Map();
64
+ this.generatedNames = new Set();
65
+ this.configSeen = new Map();
66
+
67
+ this.indexAst();
68
+ this.rootScope = this.buildScopes();
69
+ this.collectBindingWrites();
70
+ this.comments = normalizeComments(this.options.comments) || scanComments(this.code, this.nodes);
71
+ this.commentByStart = new Map(this.comments.map(comment => [comment.start, comment]));
72
+ }
73
+
74
+ run() {
75
+ this.collectExistingAnnotations();
76
+ this.collectExplicitActions();
77
+
78
+ for (const action of this.explicitActions) {
79
+ if (!action.annotate) this.blockTarget(action.node);
80
+ }
81
+ for (const action of this.explicitActions) {
82
+ if (action.annotate) this.enqueue(action.node, { explicit: true });
83
+ }
84
+
85
+ if (!this.options.explicitOnly) this.collectRegularTargets();
86
+ this.drainQueue();
87
+
88
+ if (!this.options.explicitOnly) {
89
+ let previousContextCount = -1;
90
+ while (previousContextCount !== this.contextRootCount) {
91
+ previousContextCount = this.contextRootCount;
92
+ this.collectContextualTargets();
93
+ this.drainQueue();
94
+ }
95
+ }
96
+
97
+ this.applyInsertions();
98
+ }
99
+
100
+ indexAst() {
101
+ walk(this.program, (node, parent) => {
102
+ this.nodes.push(node);
103
+ if (parent) {
104
+ this.parents.set(node, parent);
105
+ }
106
+ if (node.type === 'CallExpression') this.calls.push(node);
107
+ if (node.type === 'Property' && node.method && isFunction(node.value)) {
108
+ this.methodProperties.set(node.value, node);
109
+ }
110
+ });
111
+ }
112
+
113
+ buildScopes() {
114
+ const root = new Scope('program', null, this.program, this.program);
115
+ this.visitScope(this.program, root, true);
116
+ return root;
117
+ }
118
+
119
+ visitScope(node, scope, reuseBlock = false) {
120
+ if (!isNode(node)) return;
121
+ this.nodeScopes.set(node, scope);
122
+
123
+ if (node.type === 'Program') {
124
+ for (const statement of node.body) this.visitScope(statement, scope);
125
+ return;
126
+ }
127
+
128
+ if (isFunction(node)) {
129
+ if (node.type === 'FunctionDeclaration' && node.id) {
130
+ this.declare(scope, node.id.name, node, node, 'hoisted');
131
+ }
132
+
133
+ const functionScope = new Scope('function', scope, node, node.body?.type === 'BlockStatement' ? node.body : null);
134
+ this.functionScopes.set(node, functionScope);
135
+ if (node.type !== 'FunctionDeclaration' && node.id) {
136
+ this.declare(functionScope, node.id.name, node, node, 'function-name');
137
+ }
138
+ for (const parameter of node.params || []) {
139
+ this.declarePattern(functionScope, parameter, null, parameter, 'param');
140
+ this.visitScope(parameter, functionScope);
141
+ }
142
+ if (node.body?.type === 'BlockStatement') this.visitScope(node.body, functionScope, true);
143
+ else this.visitScope(node.body, functionScope);
144
+ return;
145
+ }
146
+
147
+ if (node.type === 'BlockStatement') {
148
+ const blockScope = reuseBlock ? scope : new Scope('block', scope, node, node);
149
+ this.nodeScopes.set(node, blockScope);
150
+ for (const statement of node.body) this.visitScope(statement, blockScope);
151
+ return;
152
+ }
153
+
154
+ if (node.type === 'StaticBlock') {
155
+ const blockScope = new Scope('static-block', scope, node, node);
156
+ this.nodeScopes.set(node, blockScope);
157
+ for (const statement of node.body || []) this.visitScope(statement, blockScope);
158
+ return;
159
+ }
160
+
161
+ if (node.type === 'SwitchStatement') {
162
+ this.visitScope(node.discriminant, scope);
163
+ const switchScope = new Scope('block', scope, node, null);
164
+ this.nodeScopes.set(node, switchScope);
165
+ for (const switchCase of node.cases || []) this.visitScope(switchCase, switchScope);
166
+ return;
167
+ }
168
+
169
+ if (node.type === 'ForStatement' || node.type === 'ForInStatement' || node.type === 'ForOfStatement') {
170
+ const loopScope = new Scope('block', scope, node, null);
171
+ this.nodeScopes.set(node, loopScope);
172
+ forEachChild(node, child => this.visitScope(child, loopScope));
173
+ return;
174
+ }
175
+
176
+ if (node.type === 'CatchClause') {
177
+ const catchScope = new Scope('block', scope, node, node.body);
178
+ this.nodeScopes.set(node, catchScope);
179
+ if (node.param) {
180
+ this.declarePattern(catchScope, node.param, null, node.param, 'caught');
181
+ this.visitScope(node.param, catchScope);
182
+ }
183
+ this.visitScope(node.body, catchScope, true);
184
+ return;
185
+ }
186
+
187
+ if (isClass(node)) {
188
+ if (node.type === 'ClassDeclaration' && node.id) {
189
+ this.declare(scope, node.id.name, node, node, 'class');
190
+ }
191
+ const classScope = new Scope('class', scope, node, null);
192
+ if (node.id) this.declare(classScope, node.id.name, node, node, 'class-name');
193
+ if (node.superClass) this.visitScope(node.superClass, scope);
194
+ this.visitScope(node.body, classScope);
195
+ return;
196
+ }
197
+
198
+ if (node.type === 'VariableDeclaration') {
199
+ for (const declarator of node.declarations) {
200
+ const targetScope = node.kind === 'var' ? nearestFunctionScope(scope) : scope;
201
+ this.declarePattern(targetScope, declarator.id, declarator.init, declarator, node.kind);
202
+ }
203
+ forEachChild(node, child => this.visitScope(child, scope));
204
+ return;
205
+ }
206
+
207
+ if (node.type === 'ImportDeclaration') {
208
+ for (const specifier of node.specifiers || []) {
209
+ if (specifier.local) this.declare(scope, specifier.local.name, null, specifier, 'import');
210
+ }
211
+ }
212
+
213
+ forEachChild(node, child => this.visitScope(child, scope));
214
+ }
215
+
216
+ declarePattern(scope, pattern, value, declaration, kind) {
217
+ pattern = unwrap(pattern);
218
+ if (!pattern) return;
219
+ if (pattern.type === 'Identifier') {
220
+ this.declare(scope, pattern.name, value, declaration, kind);
221
+ return;
222
+ }
223
+ if (pattern.type === 'AssignmentPattern') {
224
+ this.declarePattern(scope, pattern.left, value, declaration, kind);
225
+ return;
226
+ }
227
+ if (pattern.type === 'RestElement') {
228
+ this.declarePattern(scope, pattern.argument, value, declaration, kind);
229
+ return;
230
+ }
231
+ if (pattern.type === 'TSParameterProperty') {
232
+ this.declarePattern(scope, pattern.parameter, value, declaration, kind);
233
+ return;
234
+ }
235
+ if (pattern.type === 'ArrayPattern') {
236
+ for (const element of pattern.elements || []) this.declarePattern(scope, element, null, declaration, kind);
237
+ return;
238
+ }
239
+ if (pattern.type === 'ObjectPattern') {
240
+ for (const property of pattern.properties || []) {
241
+ this.declarePattern(scope, property.type === 'RestElement' ? property.argument : property.value, null, declaration, kind);
242
+ }
243
+ }
244
+ }
245
+
246
+ declare(scope, name, value, declaration, kind) {
247
+ if (!name) return null;
248
+ let binding = scope.bindings.get(name);
249
+ if (!binding || !['var', 'hoisted'].includes(kind)) {
250
+ binding = { name, value: null, declaration: null, kind, scope, statement: null, writes: [], unknownWrites: [] };
251
+ scope.bindings.set(name, binding);
252
+ }
253
+ if (declaration && !binding.declaration) binding.declaration = declaration;
254
+ if (value) this.addBindingWrite(binding, value, declaration, kind, kind === 'hoisted');
255
+ if (declaration) this.bindingByDeclaration.set(declaration, binding);
256
+ return binding;
257
+ }
258
+
259
+ addBindingWrite(binding, value, declaration, kind, hoisted = false, ambiguous = false) {
260
+ const write = {
261
+ value,
262
+ declaration,
263
+ kind,
264
+ statement: declaration && this.statementFor(declaration),
265
+ start: hoisted ? Number.NEGATIVE_INFINITY : (declaration?.start ?? value?.start ?? 0),
266
+ ambiguous,
267
+ };
268
+ binding.writes.push(write);
269
+ binding.value = value;
270
+ binding.declaration = declaration;
271
+ binding.statement = write.statement;
272
+ binding.kind = kind;
273
+ this.rememberBindingValue(value, binding);
274
+ return write;
275
+ }
276
+
277
+ rememberBindingValue(value, binding) {
278
+ for (const candidate of new Set([value, unwrap(value)])) {
279
+ if (!isNode(candidate)) continue;
280
+ const existing = this.bindingByValue.get(candidate);
281
+ if (!existing || existing.kind === 'function-name' || existing.kind === 'class-name') {
282
+ this.bindingByValue.set(candidate, binding);
283
+ }
284
+ }
285
+ }
286
+
287
+ collectBindingWrites() {
288
+ for (const node of this.nodes) {
289
+ if (node.type === 'AssignmentExpression') {
290
+ if (node.left?.type === 'Identifier' && node.operator === '=') {
291
+ const binding = this.bindingFor(node.left);
292
+ if (binding) {
293
+ this.addBindingWrite(binding, node.right, node, 'assignment', false, this.isAmbiguousWrite(node, binding));
294
+ }
295
+ } else {
296
+ for (const identifier of assignedIdentifiers(node.left)) this.markUnknownWrite(identifier, node);
297
+ }
298
+ } else if (node.type === 'UpdateExpression' && node.argument?.type === 'Identifier') {
299
+ this.markUnknownWrite(node.argument, node);
300
+ } else if (node.type === 'ForInStatement' || node.type === 'ForOfStatement') {
301
+ const patterns = node.left?.type === 'VariableDeclaration' ?
302
+ node.left.declarations.map(declaration => declaration.id) : [node.left];
303
+ for (const pattern of patterns) {
304
+ for (const identifier of assignedIdentifiers(pattern)) this.markUnknownWrite(identifier, node);
305
+ }
306
+ }
307
+ }
308
+ }
309
+
310
+ markUnknownWrite(identifier, node) {
311
+ const binding = this.bindingFor(identifier);
312
+ if (!binding) return;
313
+ binding.unknownWrites.push({ start: node.start, ambiguous: this.isAmbiguousWrite(node, binding) });
314
+ }
315
+
316
+ isAmbiguousWrite(node, binding) {
317
+ let current = this.parents.get(node);
318
+ while (current && current !== binding.scope.node) {
319
+ if (AMBIGUOUS_WRITE_ANCESTORS.has(current.type)) return true;
320
+ if (isFunction(current) && this.functionScopes.get(current) !== binding.scope) return true;
321
+ current = this.parents.get(current);
322
+ }
323
+ return false;
324
+ }
325
+
326
+ statementFor(node) {
327
+ let current = node;
328
+ let parent = this.parents.get(current);
329
+ while (parent && parent.type !== 'Program' && parent.type !== 'BlockStatement' &&
330
+ parent.type !== 'StaticBlock' && parent.type !== 'SwitchCase') {
331
+ current = parent;
332
+ parent = this.parents.get(current);
333
+ }
334
+ if (!parent) return null;
335
+ return STANDALONE_STATEMENTS.has(current.type) ? current : null;
336
+ }
337
+
338
+ bindingFor(identifier) {
339
+ if (identifier?.type !== 'Identifier') return null;
340
+ let scope = this.nodeScopes.get(identifier);
341
+ while (scope) {
342
+ const binding = scope.bindings.get(identifier.name);
343
+ if (binding) return binding;
344
+ scope = scope.parent;
345
+ }
346
+ return null;
347
+ }
348
+
349
+ bindingView(binding, write) {
350
+ if (!binding || !write) return null;
351
+ return {
352
+ ...binding,
353
+ baseBinding: binding.baseBinding || binding,
354
+ value: write.value,
355
+ declaration: write.declaration,
356
+ statement: write.statement,
357
+ kind: write.kind,
358
+ write,
359
+ };
360
+ }
361
+
362
+ bindingForValue(value) {
363
+ const binding = this.bindingByValue.get(value);
364
+ if (!binding) return null;
365
+ if (binding.kind === 'function-name' || binding.kind === 'class-name') return null;
366
+ const write = binding.writes.find(candidate => unwrap(candidate.value) === value);
367
+ return write ? this.bindingView(binding, write) : binding;
368
+ }
369
+
370
+ bindingForDeclaration(declaration) {
371
+ const binding = this.bindingByDeclaration.get(declaration);
372
+ if (!binding) return null;
373
+ const write = binding.writes.find(candidate => candidate.declaration === declaration);
374
+ return write ? this.bindingView(binding, write) : binding;
375
+ }
376
+
377
+ effectiveBinding(binding, reference) {
378
+ if (!binding) return null;
379
+ if (binding.kind === 'function-name' || binding.kind === 'class-name') {
380
+ const outer = this.bindingByValue.get(binding.value);
381
+ if (!outer || outer === binding) return null;
382
+ binding = outer;
383
+ }
384
+
385
+ const referenceFunction = nearestFunctionScope(this.nodeScopes.get(reference));
386
+ const bindingFunction = nearestFunctionScope(binding.scope);
387
+ if ((binding.unknownWrites.length || binding.writes.length > 1) && referenceFunction !== bindingFunction) return null;
388
+ if (binding.unknownWrites.some(write => write.start <= reference.start)) return null;
389
+
390
+ let effective = null;
391
+ for (const write of binding.writes) {
392
+ if (write.start <= reference.start && (!effective || write.start >= effective.start)) effective = write;
393
+ }
394
+ if (!effective && binding.writes.length === 1 && referenceFunction !== bindingFunction) {
395
+ const future = binding.writes[0];
396
+ if (['class', 'const', 'let', 'var'].includes(future.kind) && !future.ambiguous) effective = future;
397
+ }
398
+ if (!effective || effective.ambiguous) return null;
399
+ return this.bindingView(binding, effective);
400
+ }
401
+
402
+ collectExistingAnnotations() {
403
+ for (const node of this.nodes) {
404
+ if (node.type === 'AssignmentExpression' && isInjectMember(node.left)) {
405
+ const object = unwrap(node.left.object);
406
+ if (object?.type === 'Identifier') {
407
+ const binding = this.bindingFor(object);
408
+ const effective = this.effectiveBinding(binding, object);
409
+ if (effective?.write) this.blockedWrites.add(effective.write);
410
+ else if (binding) this.blockedBindings.add(binding);
411
+ }
412
+ }
413
+ if ((node.type === 'PropertyDefinition' || node.type === 'FieldDefinition' ||
414
+ node.type === 'AccessorProperty' || node.type === 'MethodDefinition') &&
415
+ node.static && staticPropertyName(node) === '$inject') {
416
+ const classNode = this.findAncestor(node, isClass);
417
+ if (classNode) {
418
+ this.blockedNodes.add(classNode);
419
+ }
420
+ }
421
+ }
422
+ }
423
+
424
+ collectExplicitActions() {
425
+ const candidates = this.explicitCandidates();
426
+ for (const comment of this.comments) {
427
+ const annotation = commentAnnotation(comment.value);
428
+ if (annotation == null) continue;
429
+ const node = this.nextAnnotatedNode(comment, candidates);
430
+ if (!annotation && node?.type === 'VariableDeclaration' && node.declarations.length !== 1) {
431
+ for (const declaration of node.declarations) {
432
+ const value = unwrap(declaration.init);
433
+ if (isFunction(value)) this.suppressedFunctionDirectives.add(value);
434
+ if (isClass(value)) {
435
+ for (const element of value.body?.body || []) {
436
+ if (element.type === 'MethodDefinition' && element.kind === 'constructor' && isFunction(element.value)) {
437
+ this.suppressedFunctionDirectives.add(element.value);
438
+ }
439
+ }
440
+ }
441
+ }
442
+ }
443
+ const targets = node ? this.normalizeExplicitNodes(node) : [];
444
+ for (const target of targets) this.explicitActions.push({ node: target, annotate: annotation });
445
+ }
446
+
447
+ for (const node of this.nodes) {
448
+ if (isFunction(node)) {
449
+ const directive = functionDirective(node);
450
+ if (directive != null && !this.suppressedFunctionDirectives.has(node)) {
451
+ let target = node;
452
+ const parent = this.parents.get(node);
453
+ if (parent?.type === 'Property' && (parent.kind === 'get' || parent.kind === 'set')) continue;
454
+ if (parent?.type === 'MethodDefinition') {
455
+ if (parent.kind !== 'constructor') continue;
456
+ target = this.findAncestor(parent, isClass) || node;
457
+ }
458
+ this.explicitActions.push({ node: target, annotate: directive });
459
+ }
460
+ }
461
+ const wrapper = annotationWrapperName(node);
462
+ if (wrapper) {
463
+ this.explicitActions.push({ node: node.arguments[0], annotate: wrapper === 'ngInject' });
464
+ }
465
+ }
466
+ }
467
+
468
+ explicitCandidates() {
469
+ const result = this.nodes.filter(node => explicitPriority(node) < 100);
470
+ result.sort((left, right) => explicitStart(left) - explicitStart(right) ||
471
+ explicitPriority(left) - explicitPriority(right) || right.end - left.end);
472
+ return result;
473
+ }
474
+
475
+ nextAnnotatedNode(comment, candidates) {
476
+ for (const candidate of candidates) {
477
+ const start = explicitStart(candidate);
478
+ if (start < comment.end) continue;
479
+ if (this.isTrivia(comment.end, start)) return candidate;
480
+ if (/\S/.test(this.code.slice(comment.end, start))) break;
481
+ }
482
+ return null;
483
+ }
484
+
485
+ isTrivia(start, end) {
486
+ let cursor = start;
487
+ while (cursor < end) {
488
+ const whitespace = /^[\s]*/.exec(this.code.slice(cursor, end))[0].length;
489
+ cursor += whitespace;
490
+ if (cursor >= end) return true;
491
+ const comment = this.commentByStart.get(cursor);
492
+ if (comment && comment.end > end) return false;
493
+ if (!comment) return false;
494
+ cursor = comment.end;
495
+ }
496
+ return true;
497
+ }
498
+
499
+ normalizeExplicitNodes(node) {
500
+ if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportNamedDeclaration') {
501
+ const declaration = node.declaration || node;
502
+ if (declaration.type === 'VariableDeclaration' && declaration.declarations.length !== 1) return [];
503
+ return [declaration];
504
+ }
505
+ if (node.type === 'VariableDeclaration' && node.declarations.length !== 1) {
506
+ return node.declarations.map(declaration => unwrap(declaration.init)).filter(value => value?.type === 'ObjectExpression');
507
+ }
508
+ if (node.type === 'ExpressionStatement') return [node.expression];
509
+ if (node.type === 'MethodDefinition' && node.kind === 'constructor') return [this.findAncestor(node, isClass) || node];
510
+ return [node];
511
+ }
512
+
513
+ blockTarget(input, seen = new WeakSet()) {
514
+ let node = unwrap(input);
515
+ if (!isNode(node) || seen.has(node)) return;
516
+ seen.add(node);
517
+
518
+ if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportNamedDeclaration') {
519
+ this.blockTarget(node.declaration, seen);
520
+ return;
521
+ }
522
+ if (node.type === 'VariableDeclaration') {
523
+ for (const declaration of node.declarations) this.blockTarget(declaration, seen);
524
+ return;
525
+ }
526
+ if (node.type === 'VariableDeclarator') {
527
+ const binding = this.bindingForDeclaration(node);
528
+ if (binding?.write) this.blockedWrites.add(binding.write);
529
+ else if (binding) this.blockedBindings.add(binding.baseBinding || binding);
530
+ this.blockTarget(node.init, seen);
531
+ return;
532
+ }
533
+ if (node.type === 'Property') {
534
+ this.blockTarget(node.value, seen);
535
+ return;
536
+ }
537
+ if (node.type === 'AssignmentExpression') {
538
+ this.blockTarget(node.right, seen);
539
+ return;
540
+ }
541
+ const branches = branchExpressions(node);
542
+ if (branches) {
543
+ for (const branch of branches) this.blockTarget(branch, seen);
544
+ return;
545
+ }
546
+ if (node.type === 'ArrayExpression') {
547
+ const last = unwrap(node.elements[node.elements.length - 1]);
548
+ if (last) this.blockTarget(last, seen);
549
+ return;
550
+ }
551
+ if (node.type === 'ObjectExpression') {
552
+ this.walkAnnotatedObject(node, value => this.blockTarget(value, seen));
553
+ return;
554
+ }
555
+ if (node.type === 'Identifier') {
556
+ const binding = this.bindingFor(node);
557
+ if (binding) {
558
+ const effective = this.effectiveBinding(binding, node);
559
+ if (effective?.write) this.blockedWrites.add(effective.write);
560
+ else this.blockedBindings.add(binding);
561
+ this.blockTarget(effective?.value, seen);
562
+ }
563
+ return;
564
+ }
565
+ if (node.type === 'CallExpression') {
566
+ if (annotationWrapperName(node)) {
567
+ this.blockTarget(node.arguments[0], seen);
568
+ return;
569
+ }
570
+ const regular = this.regularInfo(node);
571
+ if (regular?.target) this.blockTarget(regular.target, seen);
572
+ const returned = this.iifeReturn(node);
573
+ if (returned) this.blockTarget(returned, seen);
574
+ this.blockedNodes.add(node);
575
+ return;
576
+ }
577
+ if (isFunction(node) || isClass(node)) {
578
+ this.blockedNodes.add(node);
579
+ }
580
+ }
581
+
582
+ collectRegularTargets() {
583
+ for (const call of this.calls) {
584
+ const info = this.regularInfo(call);
585
+ if (!info?.target || this.blockedNodes.has(call)) continue;
586
+ if (info.method === 'component') this.processConfig(info.target, 'component');
587
+ else this.enqueue(info.target, { method: info.method });
588
+ }
589
+ }
590
+
591
+ regularInfo(call, stack = new WeakSet()) {
592
+ if (this.regularInfoCache.has(call)) return this.regularInfoCache.get(call);
593
+ if (!isNode(call) || call.type !== 'CallExpression' || stack.has(call)) return null;
594
+ stack.add(call);
595
+
596
+ const callee = unwrap(call.callee);
597
+ if (callee?.type !== 'MemberExpression' || callee.computed) {
598
+ this.regularInfoCache.set(call, null);
599
+ return null;
600
+ }
601
+ const method = staticPropertyName(callee);
602
+ const object = unwrap(callee.object);
603
+
604
+ if (method === 'module' && object?.type === 'Identifier' && this.isAngularReference(object)) {
605
+ const info = { chain: true, method: 'module', target: call.arguments.length >= 2 ? call.arguments[call.arguments.length - 1] : null };
606
+ this.regularInfoCache.set(call, info);
607
+ return info;
608
+ }
609
+
610
+ let isModule = false;
611
+ if (object?.type === 'CallExpression') {
612
+ isModule = Boolean(this.regularInfo(object, stack)?.chain) || this.matchesModuleExpression(object);
613
+ }
614
+ else if (object && this.matchesModuleExpression(object)) isModule = true;
615
+ if (!isModule || (!REGISTRATION_METHODS.has(method) && !CHAIN_ONLY_METHODS.has(method))) {
616
+ this.regularInfoCache.set(call, null);
617
+ return null;
618
+ }
619
+
620
+ if (method === 'decorator' && object?.type === 'Identifier' && object.name === '$stateProvider') {
621
+ this.regularInfoCache.set(call, null);
622
+ return null;
623
+ }
624
+ if (method === 'invoke' && object?.type === 'Identifier' && object.name === '$injector') {
625
+ this.regularInfoCache.set(call, null);
626
+ return null;
627
+ }
628
+ if (['decorator', 'factory', 'provider', 'service'].includes(method) && object?.type === 'Identifier' && object.name === '$provide') {
629
+ this.regularInfoCache.set(call, null);
630
+ return null;
631
+ }
632
+
633
+ let target = null;
634
+ if (REGISTRATION_METHODS.has(method)) {
635
+ if ((method === 'config' || method === 'run') && call.arguments.length === 1) target = call.arguments[0];
636
+ else if (method !== 'config' && method !== 'run' && call.arguments.length === 2 && isStringLiteral(call.arguments[0])) target = call.arguments[1];
637
+ }
638
+ const info = { chain: true, method, target };
639
+ this.regularInfoCache.set(call, info);
640
+ return info;
641
+ }
642
+
643
+ matchesModuleExpression(node) {
644
+ if (node.type === 'Identifier' && node.name === 'angular' && !this.isAngularReference(node)) return false;
645
+ const source = this.code.slice(node.start, node.end);
646
+ return this.moduleRegexp.test(source);
647
+ }
648
+
649
+ isAngularReference(identifier, trail = new Set()) {
650
+ const binding = this.bindingFor(identifier);
651
+ if (!binding) return identifier.name === 'angular';
652
+ const baseBinding = binding.baseBinding || binding;
653
+ if (trail.has(baseBinding)) return false;
654
+ trail.add(baseBinding);
655
+ if (binding.kind === 'import') {
656
+ const declaration = this.parents.get(binding.declaration);
657
+ if (declaration?.type !== 'ImportDeclaration' || declaration.source?.value !== 'angular') return false;
658
+ if (binding.declaration.type === 'ImportDefaultSpecifier' || binding.declaration.type === 'ImportNamespaceSpecifier') return true;
659
+ const imported = binding.declaration.imported;
660
+ return binding.declaration.type === 'ImportSpecifier' && (imported?.name === 'default' || imported?.value === 'default');
661
+ }
662
+ const declaration = this.parents.get(binding.declaration);
663
+ if (identifier.name === 'angular' && declaration?.type === 'VariableDeclaration' && declaration.declare) return true;
664
+ if (this.isDestructuredAngularBinding(binding)) return true;
665
+ if (this.isAmdAngularParameter(binding)) return true;
666
+ if (this.isIifeAngularParameter(binding, trail)) return true;
667
+ const effective = this.effectiveBinding(binding, identifier);
668
+ return this.isAngularValue(effective?.value, trail);
669
+ }
670
+
671
+ isAngularValue(input, trail) {
672
+ const value = unwrap(input);
673
+ if (!value) return false;
674
+ if (this.isDirectAngularRequire(value)) return true;
675
+ if (value.type === 'MemberExpression') {
676
+ const object = unwrap(value.object);
677
+ const property = staticPropertyName(value);
678
+ if (property === 'default' && (this.isDirectAngularRequire(object) ||
679
+ (object?.type === 'Identifier' && this.isAngularReference(object, trail)))) return true;
680
+ if (property === 'angular' && object?.type === 'Identifier' &&
681
+ ['window', 'globalThis'].includes(object.name) && !this.bindingFor(object)) return true;
682
+ }
683
+ if (value.type === 'Identifier') return this.isAngularReference(value, trail);
684
+ return false;
685
+ }
686
+
687
+ isDirectAngularRequire(value) {
688
+ value = unwrap(value);
689
+ return value?.type === 'CallExpression' && value.callee?.type === 'Identifier' &&
690
+ value.callee.name === 'require' && !this.bindingFor(value.callee) &&
691
+ value.arguments.length === 1 && isStringLiteral(value.arguments[0]) && value.arguments[0].value === 'angular';
692
+ }
693
+
694
+ isDestructuredAngularBinding(binding) {
695
+ const declaration = binding.declaration;
696
+ if (declaration?.type !== 'VariableDeclarator') return false;
697
+ const pattern = unwrap(declaration.id);
698
+ if (pattern?.type !== 'ObjectPattern') return false;
699
+ const source = unwrap(declaration.init);
700
+ const propertyName = this.isDirectAngularRequire(source) ? 'default' :
701
+ (source?.type === 'Identifier' && ['window', 'globalThis'].includes(source.name) && !this.bindingFor(source) ? 'angular' : null);
702
+ if (!propertyName) return false;
703
+ return pattern.properties.some(property => property.type === 'Property' && staticPropertyName(property) === propertyName &&
704
+ parameterName(property.value) === binding.name);
705
+ }
706
+
707
+ isIifeAngularParameter(binding, trail) {
708
+ if (binding.kind !== 'param') return false;
709
+ const callback = binding.scope?.node;
710
+ if (!isFunction(callback)) return false;
711
+ const runtimeIndex = runtimeParameterIndex(callback.params, binding.name);
712
+ if (runtimeIndex < 0) return false;
713
+ let parent = this.parents.get(callback);
714
+ let child = callback;
715
+ while (parent && WRAPPER_TYPES.has(parent.type) && parent.expression === child) {
716
+ child = parent;
717
+ parent = this.parents.get(parent);
718
+ }
719
+ if (parent?.type !== 'CallExpression' || unwrap(parent.callee) !== callback) return false;
720
+ return this.isAngularValue(parent.arguments[runtimeIndex], trail);
721
+ }
722
+
723
+ isAmdAngularParameter(binding) {
724
+ if (binding.kind !== 'param') return false;
725
+ const callback = binding.scope?.node;
726
+ if (!isFunction(callback)) return false;
727
+ const runtimeIndex = runtimeParameterIndex(callback.params, binding.name);
728
+ if (runtimeIndex < 0) return false;
729
+
730
+ let parent = this.parents.get(callback);
731
+ let child = callback;
732
+ while (parent && WRAPPER_TYPES.has(parent.type) && parent.expression === child) {
733
+ child = parent;
734
+ parent = this.parents.get(parent);
735
+ }
736
+ if (parent?.type === 'CallExpression') {
737
+ const callbackIndex = parent.arguments.findIndex(argument => unwrap(argument) === callback || argument === child);
738
+ if (this.isAmdAngularArgument(parent, callbackIndex, runtimeIndex)) return true;
739
+ }
740
+
741
+ for (const call of this.calls) {
742
+ const callee = unwrap(call.callee);
743
+ if (callee?.type !== 'Identifier' || !AMD_LOADER_NAMES.has(callee.name) || this.bindingFor(callee)) continue;
744
+ const callbackIndex = call.arguments.findIndex(argument => {
745
+ const reference = unwrap(argument);
746
+ if (reference?.type !== 'Identifier') return false;
747
+ const effective = this.effectiveBinding(this.bindingFor(reference), reference);
748
+ return unwrap(effective?.value) === callback;
749
+ });
750
+ if (this.isAmdAngularArgument(call, callbackIndex, runtimeIndex)) return true;
751
+ }
752
+ return false;
753
+ }
754
+
755
+ isAmdAngularArgument(call, callbackIndex, parameterIndex) {
756
+ if (callbackIndex < 0) return false;
757
+ const callee = unwrap(call.callee);
758
+ if (callee?.type !== 'Identifier' || !AMD_LOADER_NAMES.has(callee.name) || this.bindingFor(callee)) return false;
759
+ for (let index = callbackIndex - 1; index >= 0; index--) {
760
+ const dependencies = this.resolveValue(call.arguments[index], new WeakSet());
761
+ if (dependencies?.type === 'ArrayExpression') {
762
+ return dependencies.elements[parameterIndex]?.value === 'angular';
763
+ }
764
+ }
765
+ return false;
766
+ }
767
+
768
+ collectContextualTargets() {
769
+ for (const call of this.calls) {
770
+ if (this.processedHelpers.has(call) || !this.isInContext(call)) continue;
771
+ if (this.matchContextualCall(call)) this.processedHelpers.add(call);
772
+ }
773
+ }
774
+
775
+ matchContextualCall(call) {
776
+ const callee = unwrap(call.callee);
777
+ if (callee?.type !== 'MemberExpression' || callee.computed) return false;
778
+ const object = unwrap(callee.object);
779
+ const method = staticPropertyName(callee);
780
+
781
+ if (object?.type === 'Identifier' && object.name === '$injector' && method === 'invoke' && call.arguments.length >= 1) {
782
+ for (const argument of call.arguments) this.enqueue(argument, { method: 'invoke' });
783
+ return true;
784
+ }
785
+ if (object?.type === 'MemberExpression' && !object.computed && method === 'push' &&
786
+ object.object?.type === 'Identifier' && object.object.name === '$httpProvider' &&
787
+ ['interceptors', 'responseInterceptors'].includes(staticPropertyName(object)) && call.arguments.length >= 1) {
788
+ for (const argument of call.arguments) this.enqueue(argument, { method: 'push' });
789
+ return true;
790
+ }
791
+ if (object?.type === 'Identifier' && object.name === '$controllerProvider' && method === 'register' && call.arguments.length === 2) {
792
+ this.enqueue(call.arguments[1], { method: 'register' });
793
+ return true;
794
+ }
795
+ if (object?.type === 'Identifier' && object.name === '$provide' &&
796
+ ['decorator', 'factory', 'provider', 'service'].includes(method) && call.arguments.length === 2) {
797
+ this.enqueue(call.arguments[1], { method });
798
+ return true;
799
+ }
800
+ if (method === 'when' && this.chainStartsWith(object, '$routeProvider') && call.arguments.length === 2) {
801
+ this.processConfig(call.arguments[1], 'route');
802
+ return true;
803
+ }
804
+ if (method === 'when' && this.chainStartsWith(object, '$urlRouterProvider') && call.arguments.length >= 1) {
805
+ this.enqueue(call.arguments[call.arguments.length - 1], { method: 'when' });
806
+ return true;
807
+ }
808
+ if (method === 'state' && this.chainStartsWith(object, '$stateProvider') && call.arguments.length >= 1 && call.arguments.length <= 2) {
809
+ this.processConfig(call.arguments[call.arguments.length - 1], 'state');
810
+ return true;
811
+ }
812
+ if (method === 'setNestedState' && this.chainStartsWith(object, 'stateHelperProvider') && call.arguments.length >= 1 && call.arguments.length <= 2) {
813
+ this.processConfig(call.arguments[0], 'state');
814
+ return true;
815
+ }
816
+ if (object?.type === 'Identifier' && call.arguments.length === 1 &&
817
+ ((['$modal', '$uibModal'].includes(object.name) && method === 'open') ||
818
+ (['$mdBottomSheet', '$mdDialog', '$mdToast'].includes(object.name) && method === 'show'))) {
819
+ this.processConfig(call.arguments[0], 'modal');
820
+ return true;
821
+ }
822
+ return false;
823
+ }
824
+
825
+ chainStartsWith(node, name) {
826
+ node = unwrap(node);
827
+ if (node?.type === 'Identifier') return node.name === name;
828
+ if (node?.type !== 'CallExpression') return false;
829
+ const callee = unwrap(node.callee);
830
+ return callee?.type === 'MemberExpression' && !callee.computed && this.chainStartsWith(callee.object, name);
831
+ }
832
+
833
+ enqueue(node, info = {}) {
834
+ if (isNode(node)) this.queue.push({ node, info });
835
+ }
836
+
837
+ drainQueue() {
838
+ for (let index = 0; index < this.queue.length; index++) {
839
+ const { node, info } = this.queue[index];
840
+ this.processTarget(node, info, new WeakSet());
841
+ }
842
+ this.queue.length = 0;
843
+ }
844
+
845
+ processTarget(input, info, trail, bindingHint = null) {
846
+ let node = unwrap(input);
847
+ if (!isNode(node) || trail.has(node)) return;
848
+ trail.add(node);
849
+ this.markContext(node);
850
+
851
+ if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportNamedDeclaration') {
852
+ this.processTarget(node.declaration, info, trail, bindingHint);
853
+ return;
854
+ }
855
+ if (node.type === 'Property') {
856
+ if (node.kind === 'get' || node.kind === 'set') return;
857
+ this.processTarget(node.value, info, trail, bindingHint);
858
+ return;
859
+ }
860
+ if (node.type === 'VariableDeclaration') {
861
+ for (const declaration of node.declarations) this.processTarget(declaration, info, trail, bindingHint);
862
+ return;
863
+ }
864
+ if (node.type === 'VariableDeclarator') {
865
+ const binding = this.bindingForDeclaration(node) || bindingHint;
866
+ this.processTarget(node.init, info, trail, binding);
867
+ return;
868
+ }
869
+ if (node.type === 'Identifier') {
870
+ const binding = this.effectiveBinding(this.bindingFor(node), node);
871
+ if (!binding || !binding.value) return;
872
+ if (this.blockedBindings.has(binding.baseBinding || binding)) {
873
+ this.processBlockedMethodContext(binding.value, info.method);
874
+ return;
875
+ }
876
+ this.markContext(binding.value);
877
+ this.processTarget(binding.value, info, trail, binding);
878
+ return;
879
+ }
880
+ const branches = branchExpressions(node);
881
+ if (branches) {
882
+ if (bindingHint && unwrap(bindingHint.value) === node) {
883
+ this.processBlockedMethodContext(node, info.method);
884
+ return;
885
+ }
886
+ for (const branch of branches) this.processTarget(branch, info, trail, bindingHint);
887
+ return;
888
+ }
889
+ if (node.type === 'AssignmentExpression') {
890
+ const assignment = this.code.slice(node.left.start, node.left.end);
891
+ this.processTarget(node.right, { ...info, assignment }, trail, null);
892
+ return;
893
+ }
894
+ if (node.type === 'CallExpression') {
895
+ const wrapper = annotationWrapperName(node);
896
+ if (wrapper) {
897
+ if (wrapper === 'ngNoInject') {
898
+ this.processBlockedMethodContext(node.arguments[0], info.method);
899
+ return;
900
+ }
901
+ this.processTarget(node.arguments[0], { ...info, explicit: true }, trail, bindingHint);
902
+ return;
903
+ }
904
+ const returned = this.iifeReturn(node);
905
+ if (returned && bindingHint && (isFunction(unwrap(returned)) || isClass(unwrap(returned)))) {
906
+ const target = unwrap(returned);
907
+ const dependencies = isClass(target) ? classDependencies(target) : functionDependencies(target);
908
+ if (!this.isBlocked(target, bindingHint) && dependencies?.length && !this.plannedNodes.has(target)) {
909
+ this.plannedNodes.add(target);
910
+ this.planBindingInjection(bindingHint, dependencies);
911
+ }
912
+ this.markContext(target);
913
+ this.processMethodContext(target, info.method);
914
+ } else if (returned?.type === 'Identifier') {
915
+ this.processTarget(returned, info, trail, null);
916
+ } else if (returned) {
917
+ if (bindingHint) this.processBlockedMethodContext(returned, info.method);
918
+ else this.processTarget(returned, info, trail, null);
919
+ }
920
+ return;
921
+ }
922
+ if (node.type === 'ArrayExpression') {
923
+ this.validateAnnotatedArray(node, info);
924
+ this.processBlockedMethodContext(node.elements[node.elements.length - 1], info.method);
925
+ return;
926
+ }
927
+ if (node.type === 'ObjectExpression') {
928
+ if (info.explicit) {
929
+ const childInfo = { ...info };
930
+ delete childInfo.assignment;
931
+ this.walkAnnotatedObject(node, value => this.enqueue(value, childInfo));
932
+ }
933
+ this.processMethodContext(node, info.method);
934
+ return;
935
+ }
936
+ if (!isFunction(node) && !isClass(node)) return;
937
+
938
+ const binding = bindingHint || this.bindingForValue(node) || null;
939
+ const annotatedParent = this.annotatedArrayParent(node);
940
+ if (annotatedParent && info.explicit) this.validateAnnotatedArray(annotatedParent, info);
941
+ if (this.isBlocked(node, binding)) {
942
+ this.processBlockedMethodContext(node, info.method);
943
+ return;
944
+ }
945
+ this.markContext(node);
946
+ this.planAnnotation(node, binding, info);
947
+ this.processMethodContext(node, info.method);
948
+ }
949
+
950
+ processBlockedMethodContext(input, method, trail = new WeakSet()) {
951
+ let node = unwrap(input);
952
+ if (!method || !isNode(node) || trail.has(node)) return;
953
+ trail.add(node);
954
+
955
+ if (node.type === 'Identifier') {
956
+ const binding = this.effectiveBinding(this.bindingFor(node), node);
957
+ if (binding?.value) this.processBlockedMethodContext(binding.value, method, trail);
958
+ return;
959
+ }
960
+ if (node.type === 'ArrayExpression') {
961
+ this.processBlockedMethodContext(node.elements[node.elements.length - 1], method, trail);
962
+ return;
963
+ }
964
+ const branches = branchExpressions(node);
965
+ if (branches) {
966
+ for (const branch of branches) this.processBlockedMethodContext(branch, method, trail);
967
+ return;
968
+ }
969
+ if (node.type === 'AssignmentExpression') {
970
+ this.processBlockedMethodContext(node.right, method, trail);
971
+ return;
972
+ }
973
+ if (node.type === 'SequenceExpression') {
974
+ this.processBlockedMethodContext(node.expressions[node.expressions.length - 1], method, trail);
975
+ return;
976
+ }
977
+ if (node.type === 'CallExpression') {
978
+ if (annotationWrapperName(node)) {
979
+ this.processBlockedMethodContext(node.arguments[0], method, trail);
980
+ return;
981
+ }
982
+ const returned = this.iifeReturn(node);
983
+ if (returned) this.processBlockedMethodContext(returned, method, trail);
984
+ return;
985
+ }
986
+ if (!isFunction(node) && !isClass(node) && node.type !== 'ObjectExpression') return;
987
+ this.markContext(node);
988
+ this.processMethodContext(node, method);
989
+ }
990
+
991
+ processMethodContext(node, method) {
992
+ if (!method) return;
993
+ let methods = this.processedMethods.get(node);
994
+ if (!methods) this.processedMethods.set(node, methods = new Set());
995
+ if (methods.has(method)) return;
996
+ methods.add(method);
997
+ if (method === 'provider') this.scanProvider(node);
998
+ if (method === 'directive') this.scanDirective(node);
999
+ }
1000
+
1001
+ scanProvider(root) {
1002
+ walk(root, node => {
1003
+ if (node.type === 'AssignmentExpression' && node.left?.type === 'MemberExpression' &&
1004
+ staticPropertyName(node.left) === '$get') {
1005
+ const owner = unwrap(node.left.object);
1006
+ if (owner?.type === 'ThisExpression' || (owner?.type === 'Identifier' && ['self', 'that'].includes(owner.name))) {
1007
+ this.enqueue(node.right, { method: 'provider' });
1008
+ }
1009
+ }
1010
+ if (node.type === 'ObjectExpression') {
1011
+ for (const property of node.properties || []) {
1012
+ if (property.type === 'Property' && staticPropertyName(property) === '$get' && property.kind !== 'get' && property.kind !== 'set') {
1013
+ this.enqueue(property.value, { method: 'provider' });
1014
+ }
1015
+ }
1016
+ }
1017
+ });
1018
+ }
1019
+
1020
+ scanDirective(root) {
1021
+ this.scanDirectiveFunction(root, new WeakSet());
1022
+ }
1023
+
1024
+ scanDirectiveFunction(root, trail) {
1025
+ root = unwrap(root);
1026
+ if (!isFunction(root) || trail.has(root)) return;
1027
+ trail.add(root);
1028
+ if (root.type === 'ArrowFunctionExpression' && root.body?.type !== 'BlockStatement') {
1029
+ this.processDirectiveReturn(root.body, trail);
1030
+ return;
1031
+ }
1032
+ const visit = node => {
1033
+ if (!isNode(node)) return;
1034
+ if (node !== root && (isFunction(node) || isClass(node))) return;
1035
+ if (node.type === 'ReturnStatement' && node.argument) this.processDirectiveReturn(node.argument, trail);
1036
+ forEachChild(node, visit);
1037
+ };
1038
+ visit(root);
1039
+ }
1040
+
1041
+ processDirectiveReturn(input, trail) {
1042
+ const node = unwrap(input);
1043
+ if (!isNode(node)) return;
1044
+ const branches = branchExpressions(node);
1045
+ if (branches) {
1046
+ for (const branch of branches) this.processDirectiveReturn(branch, trail);
1047
+ return;
1048
+ }
1049
+ if (node.type === 'CallExpression') {
1050
+ const returned = this.iifeReturn(node);
1051
+ if (returned) {
1052
+ this.processDirectiveReturn(returned, trail);
1053
+ return;
1054
+ }
1055
+ const callee = lastSequenceExpression(node.callee);
1056
+ if (callee?.type === 'Identifier') {
1057
+ const binding = this.effectiveBinding(this.bindingFor(callee), callee);
1058
+ const helper = this.resolveValue(binding?.value, new WeakSet());
1059
+ if (isFunction(helper)) this.scanDirectiveFunction(helper, trail);
1060
+ }
1061
+ return;
1062
+ }
1063
+ this.processDirectiveObject(node);
1064
+ }
1065
+
1066
+ processDirectiveObject(input) {
1067
+ const node = this.resolveValue(input, new WeakSet());
1068
+ if (node?.type !== 'ObjectExpression') return;
1069
+ const controller = findProperty(node, 'controller');
1070
+ if (controller && controller.kind !== 'get' && controller.kind !== 'set') this.enqueue(controller.value, { method: 'controller' });
1071
+ }
1072
+
1073
+ processConfig(input, kind) {
1074
+ let seen = this.configSeen.get(kind);
1075
+ if (!seen) this.configSeen.set(kind, seen = new WeakSet());
1076
+ const node = unwrap(input);
1077
+ if (!isNode(node) || seen.has(node)) return;
1078
+ seen.add(node);
1079
+
1080
+ if (node.type === 'ConditionalExpression') {
1081
+ this.processConfig(node.consequent, kind);
1082
+ this.processConfig(node.alternate, kind);
1083
+ return;
1084
+ }
1085
+ if (isObjectAssign(node)) {
1086
+ for (const argument of node.arguments) this.processConfig(argument, kind);
1087
+ return;
1088
+ }
1089
+ const resolved = this.resolveValue(node, new WeakSet());
1090
+ if (resolved !== node) {
1091
+ this.processConfig(resolved, kind);
1092
+ return;
1093
+ }
1094
+ if (node.type !== 'ObjectExpression') return;
1095
+
1096
+ for (const property of node.properties || []) {
1097
+ if (property.type === 'SpreadElement') {
1098
+ this.processConfig(property.argument, kind);
1099
+ continue;
1100
+ }
1101
+ if (property.type !== 'Property' || property.kind === 'get' || property.kind === 'set') continue;
1102
+ const name = staticPropertyName(property);
1103
+ if (kind === 'component' && COMPONENT_PROPERTIES.has(name)) {
1104
+ this.enqueue(property.value, { method: name });
1105
+ } else if ((kind === 'route' || kind === 'modal') && name === 'controller') {
1106
+ this.enqueue(property.value, { method: 'controller' });
1107
+ } else if (kind === 'state' && STATE_PROPERTIES.has(name)) {
1108
+ this.enqueue(property.value, { method: name });
1109
+ } else if ((kind === 'route' || kind === 'modal' || kind === 'state') && name === 'resolve') {
1110
+ this.processValues(property.value, value => this.enqueue(value, { method: 'resolve' }));
1111
+ } else if (kind === 'state' && name === 'params') {
1112
+ this.processParams(property.value);
1113
+ } else if (kind === 'state' && name === 'views') {
1114
+ this.processViews(property.value);
1115
+ } else if (kind === 'state' && name === 'children') {
1116
+ const children = this.resolveValue(property.value, new WeakSet());
1117
+ if (children?.type === 'ArrayExpression') {
1118
+ for (const child of children.elements || []) this.processConfig(child, 'state');
1119
+ }
1120
+ }
1121
+ }
1122
+ }
1123
+
1124
+ processValues(input, callback) {
1125
+ const node = this.resolveValue(input, new WeakSet());
1126
+ if (node?.type !== 'ObjectExpression') return;
1127
+ for (const property of node.properties || []) {
1128
+ if (property.type === 'SpreadElement') this.processValues(property.argument, callback);
1129
+ else if (property.type === 'Property' && property.kind !== 'get' && property.kind !== 'set') callback(property.value);
1130
+ }
1131
+ }
1132
+
1133
+ processParams(input) {
1134
+ this.processValues(input, value => {
1135
+ const resolved = this.resolveValue(value, new WeakSet());
1136
+ if (resolved?.type === 'ObjectExpression') {
1137
+ const property = findProperty(resolved, 'value');
1138
+ if (property && property.kind !== 'get' && property.kind !== 'set') this.enqueue(property.value, { method: 'params' });
1139
+ } else {
1140
+ this.enqueue(value, { method: 'params' });
1141
+ }
1142
+ });
1143
+ }
1144
+
1145
+ processViews(input) {
1146
+ this.processValues(input, value => {
1147
+ const view = this.resolveValue(value, new WeakSet());
1148
+ if (view?.type !== 'ObjectExpression') return;
1149
+ for (const property of view.properties || []) {
1150
+ if (property.type !== 'Property' || property.kind === 'get' || property.kind === 'set') continue;
1151
+ const name = staticPropertyName(property);
1152
+ if (VIEW_PROPERTIES.has(name)) this.enqueue(property.value, { method: name });
1153
+ else if (name === 'resolve') this.processValues(property.value, item => this.enqueue(item, { method: 'resolve' }));
1154
+ }
1155
+ });
1156
+ }
1157
+
1158
+ resolveValue(input, trail) {
1159
+ let node = unwrap(input);
1160
+ if (!isNode(node) || trail.has(node)) return node;
1161
+ trail.add(node);
1162
+ if (node.type === 'Identifier') {
1163
+ const binding = this.effectiveBinding(this.bindingFor(node), node);
1164
+ if (binding?.value && !this.blockedBindings.has(binding.baseBinding || binding)) return this.resolveValue(binding.value, trail);
1165
+ }
1166
+ if (node.type === 'CallExpression') {
1167
+ const returned = this.iifeReturn(node);
1168
+ if (returned) return this.resolveValue(returned, trail);
1169
+ }
1170
+ return node;
1171
+ }
1172
+
1173
+ iifeReturn(node) {
1174
+ node = unwrap(node);
1175
+ if (node?.type !== 'CallExpression') return null;
1176
+ const callee = unwrap(node.callee);
1177
+ if (!isFunction(callee)) return null;
1178
+ if (callee.body?.type !== 'BlockStatement') return callee.body || null;
1179
+ const statement = callee.body.body.find(item => item.type === 'ReturnStatement');
1180
+ return statement?.argument || null;
1181
+ }
1182
+
1183
+ walkAnnotatedObject(object, callback) {
1184
+ for (const property of object.properties || []) {
1185
+ if (property.type !== 'Property' || property.kind === 'get' || property.kind === 'set') continue;
1186
+ const value = unwrap(property.value);
1187
+ if (isFunction(value) || isClass(value)) callback(value);
1188
+ else if (value?.type === 'ObjectExpression') this.walkAnnotatedObject(value, callback);
1189
+ else if (value?.type === 'ArrayExpression') this.validateAnnotatedArray(value, { explicit: true });
1190
+ }
1191
+ }
1192
+
1193
+ validateAnnotatedArray(node, info) {
1194
+ if (!info.explicit || node.elements.length === 0) return;
1195
+ const target = unwrap(node.elements[node.elements.length - 1]);
1196
+ if (!isFunction(target) && !isClass(target)) return;
1197
+ const annotations = node.elements.slice(0, -1);
1198
+ if (!annotations.every(isStringLiteral)) return;
1199
+ const dependencies = isClass(target) ? classDependencies(target) : functionDependencies(target);
1200
+ if (!dependencies) return;
1201
+ if (annotations.length < dependencies.length) {
1202
+ const error = new Error('[angularjs-annotate] Function parameters do not match existing annotations.');
1203
+ error.code = 'ANNOTATION_MISMATCH';
1204
+ error.start = node.start;
1205
+ error.end = node.end;
1206
+ throw error;
1207
+ }
1208
+ const mismatch = dependencies.findIndex((dependency, index) => annotations[index]?.value !== dependency);
1209
+ if (mismatch >= 0 && typeof this.options.onWarn === 'function') {
1210
+ const location = annotations[mismatch] || node;
1211
+ this.options.onWarn('[angularjs-annotate] Function parameters do not match existing annotations.', {
1212
+ code: 'ANNOTATION_MISMATCH',
1213
+ start: location.start,
1214
+ end: location.end,
1215
+ });
1216
+ }
1217
+ }
1218
+
1219
+ annotatedArrayParent(node) {
1220
+ let current = node;
1221
+ let parent = this.parents.get(current);
1222
+ while (parent && WRAPPER_TYPES.has(parent.type) && parent.expression === current) {
1223
+ current = parent;
1224
+ parent = this.parents.get(current);
1225
+ }
1226
+ if (parent?.type !== 'ArrayExpression' || unwrap(parent.elements[parent.elements.length - 1]) !== node) return null;
1227
+ return isAnnotatedArray(parent) ? parent : null;
1228
+ }
1229
+
1230
+ isBlocked(node, binding) {
1231
+ const baseBinding = binding?.baseBinding || binding;
1232
+ if (this.blockedNodes.has(node) || (baseBinding && this.blockedBindings.has(baseBinding))) return true;
1233
+ if (binding?.write && this.blockedWrites.has(binding.write)) return true;
1234
+ if (this.annotatedArrayParent(node)) return true;
1235
+ return false;
1236
+ }
1237
+
1238
+ markContext(node) {
1239
+ if (!isNode(node) || this.contextRoots.has(node)) return;
1240
+ this.contextRoots.add(node);
1241
+ this.contextRootCount++;
1242
+ }
1243
+
1244
+ isInContext(node) {
1245
+ let current = node;
1246
+ while (current) {
1247
+ if (this.contextRoots.has(current)) return true;
1248
+ current = this.parents.get(current);
1249
+ }
1250
+ return false;
1251
+ }
1252
+
1253
+ planAnnotation(node, binding, info) {
1254
+ if (this.plannedNodes.has(node)) return;
1255
+ const dependencies = isClass(node) ? classDependencies(node) : functionDependencies(node);
1256
+ if (!dependencies || dependencies.length === 0) return;
1257
+ if (binding?.write?.kind === 'hoisted') {
1258
+ const writes = (binding.baseBinding || binding).writes.filter(write => write.kind === 'hoisted');
1259
+ if (writes[writes.length - 1] !== binding.write) return;
1260
+ }
1261
+
1262
+ if (isClass(node) && !node.id && this.isAnonymousDefaultExport(node)) {
1263
+ const name = this.generateName('_ngInjectAnonymousClass');
1264
+ if (!this.nameAnonymousDeclaration(node, name, 'class')) return;
1265
+ this.plannedNodes.add(node);
1266
+ this.planAfter(this.statementFor(node)?.end || node.end, `${name}.$inject = ${dependencyArray(dependencies)};`);
1267
+ return;
1268
+ }
1269
+ if (node.type === 'FunctionDeclaration' && !node.id && this.isAnonymousDefaultExport(node)) {
1270
+ const name = this.generateName('_ngInjectExport');
1271
+ if (!this.nameAnonymousDeclaration(node, name, 'function')) return;
1272
+ this.plannedNodes.add(node);
1273
+ const statement = this.statementFor(node);
1274
+ this.planBefore(statement?.start ?? node.start, `${name}.$inject = ${dependencyArray(dependencies)};`);
1275
+ return;
1276
+ }
1277
+
1278
+ if (info.assignment) {
1279
+ const statement = this.statementFor(node);
1280
+ if (statement) {
1281
+ this.plannedNodes.add(node);
1282
+ this.planAfter(statement.end, `${info.assignment}.$inject = ${dependencyArray(dependencies)};`);
1283
+ }
1284
+ return;
1285
+ }
1286
+
1287
+ if (binding && unwrap(binding.value) === node) {
1288
+ if (binding.statement) {
1289
+ this.plannedNodes.add(node);
1290
+ this.planBindingInjection(binding, dependencies);
1291
+ }
1292
+ return;
1293
+ }
1294
+ if ((node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') && node.id) {
1295
+ const declarationBinding = this.bindingForValue(node) || binding;
1296
+ if (declarationBinding?.statement) {
1297
+ this.plannedNodes.add(node);
1298
+ this.planBindingInjection(declarationBinding, dependencies);
1299
+ }
1300
+ return;
1301
+ }
1302
+
1303
+ const property = this.methodProperties.get(node);
1304
+ if (property) {
1305
+ if (property.kind === 'get' || property.kind === 'set') return;
1306
+ if (containsSuper(node)) return;
1307
+ const key = this.code.slice(property.key.start, property.key.end);
1308
+ const prefix = `${property.computed ? `[${key}]` : key}: ${dependencyPrefix(dependencies)}, ${node.async ? 'async ' : ''}function${node.generator ? '*' : ''}`;
1309
+ this.magicString.overwrite(property.start, node.start, prefix);
1310
+ this.magicString.appendRight(node.end, ']');
1311
+ this.plannedNodes.add(node);
1312
+ return;
1313
+ }
1314
+
1315
+ if (isFunction(node) || isClass(node)) {
1316
+ if (this.isInvocationTarget(node)) return;
1317
+ this.magicString.appendLeft(node.start, `${dependencyPrefix(dependencies)}, `);
1318
+ this.magicString.appendRight(node.end, ']');
1319
+ this.plannedNodes.add(node);
1320
+ }
1321
+ }
1322
+
1323
+ planBindingInjection(binding, dependencies) {
1324
+ const text = `${binding.name}.$inject = ${dependencyArray(dependencies)};`;
1325
+ if (binding.value?.type === 'FunctionDeclaration') {
1326
+ let position = scopeInsertionPosition(binding.scope);
1327
+ const statements = binding.scope?.body?.body || [];
1328
+ if (statements.length === 0 || directiveValue(statements[0]) == null) {
1329
+ const marker = this.comments.find(comment => comment.end <= position &&
1330
+ commentAnnotation(comment.value) != null && this.isTrivia(comment.end, position));
1331
+ if (marker) {
1332
+ const leadingComment = this.comments.find(comment => comment.end <= position && this.isTrivia(comment.end, position));
1333
+ position = leadingComment?.start ?? marker.start;
1334
+ }
1335
+ }
1336
+ if (position != null) this.planBefore(position, text);
1337
+ else if (binding.statement) this.planAfter(binding.statement.end, text);
1338
+ return;
1339
+ }
1340
+ if (binding.statement) this.planAfter(binding.statement.end, text);
1341
+ }
1342
+
1343
+ planBefore(position, text) {
1344
+ let entries = this.before.get(position);
1345
+ if (!entries) this.before.set(position, entries = []);
1346
+ entries.unshift(text);
1347
+ }
1348
+
1349
+ planAfter(position, text) {
1350
+ let entries = this.after.get(position);
1351
+ if (!entries) this.after.set(position, entries = []);
1352
+ entries.unshift(text);
1353
+ }
1354
+
1355
+ applyInsertions() {
1356
+ for (const [position, entries] of this.before) {
1357
+ this.magicString.appendLeft(position, `${entries.join('\n')}\n`);
1358
+ }
1359
+ for (const [position, entries] of this.after) {
1360
+ this.magicString.appendRight(position, `\n${entries.join('\n')}`);
1361
+ }
1362
+ }
1363
+
1364
+ isAnonymousDefaultExport(node) {
1365
+ return this.parents.get(node)?.type === 'ExportDefaultDeclaration';
1366
+ }
1367
+
1368
+ isInvocationTarget(node) {
1369
+ let current = node;
1370
+ let parent = this.parents.get(current);
1371
+ while (parent && WRAPPER_TYPES.has(parent.type) && parent.expression === current) {
1372
+ current = parent;
1373
+ parent = this.parents.get(current);
1374
+ }
1375
+ return (parent?.type === 'CallExpression' && parent.callee === current) ||
1376
+ (parent?.type === 'NewExpression' && parent.callee === current) ||
1377
+ (parent?.type === 'TaggedTemplateExpression' && parent.tag === current);
1378
+ }
1379
+
1380
+ nameAnonymousDeclaration(node, name, kind) {
1381
+ let start = node.start;
1382
+ if (kind === 'class' && node.decorators?.length) {
1383
+ start = Math.max(start, ...node.decorators.map(decorator => decorator.end));
1384
+ }
1385
+ const source = this.code.slice(start, node.end);
1386
+ const match = kind === 'class' ? /\bclass\b/ : /^(?:async\s+)?function\*?/;
1387
+ const found = match.exec(source);
1388
+ if (!found) return false;
1389
+ this.magicString.appendLeft(start + found.index + found[0].length, ` ${name}`);
1390
+ return true;
1391
+ }
1392
+
1393
+ generateName(base) {
1394
+ let name = base;
1395
+ let suffix = 2;
1396
+ while (this.generatedNames.has(name) || this.rootScope.bindings.has(name)) name = `${base}${suffix++}`;
1397
+ this.generatedNames.add(name);
1398
+ return name;
1399
+ }
1400
+
1401
+ findAncestor(node, predicate) {
1402
+ let current = this.parents.get(node);
1403
+ while (current) {
1404
+ if (predicate(current)) return current;
1405
+ current = this.parents.get(current);
1406
+ }
1407
+ return null;
1408
+ }
1409
+ }
1410
+
1411
+ class Scope {
1412
+ constructor(type, parent, node, body) {
1413
+ this.type = type;
1414
+ this.parent = parent;
1415
+ this.node = node;
1416
+ this.body = body;
1417
+ this.bindings = new Map();
1418
+ }
1419
+ }
1420
+
1421
+ function normalizeModuleRegexp(value) {
1422
+ if (!value) return new RegExp(DEFAULT_MODULE_REGEXP.source);
1423
+ if (value instanceof RegExp) return new RegExp(value.source, value.flags.replace(/[gy]/g, ''));
1424
+ return new RegExp(value);
1425
+ }
1426
+
1427
+ function nearestFunctionScope(scope) {
1428
+ while (scope.parent && scope.type !== 'function' && scope.type !== 'program' && scope.type !== 'static-block') {
1429
+ scope = scope.parent;
1430
+ }
1431
+ return scope;
1432
+ }
1433
+
1434
+ function branchExpressions(node) {
1435
+ if (node?.type === 'ConditionalExpression') return [node.consequent, node.alternate];
1436
+ if (node?.type === 'LogicalExpression') return [node.left, node.right];
1437
+ return null;
1438
+ }
1439
+
1440
+ function annotationWrapperName(node) {
1441
+ if (node?.type !== 'CallExpression' || node.arguments.length !== 1) return null;
1442
+ const callee = unwrap(node.callee);
1443
+ return callee?.type === 'Identifier' && ANNOTATION_WRAPPER_NAMES.has(callee.name) ? callee.name : null;
1444
+ }
1445
+
1446
+ function lastSequenceExpression(input) {
1447
+ let node = unwrap(input);
1448
+ while (node?.type === 'SequenceExpression') node = unwrap(node.expressions[node.expressions.length - 1]);
1449
+ return node;
1450
+ }
1451
+
1452
+ function scopeInsertionPosition(scope) {
1453
+ const body = scope?.body;
1454
+ if (!body || (body.type !== 'Program' && body.type !== 'BlockStatement' && body.type !== 'StaticBlock')) return null;
1455
+ const statements = body.body || [];
1456
+ let index = 0;
1457
+ while (index < statements.length && directiveValue(statements[index]) != null) index++;
1458
+ if (index < statements.length) return statements[index].start;
1459
+ return body.type === 'BlockStatement' || body.type === 'StaticBlock' ? body.end - 1 : body.end;
1460
+ }
1461
+
1462
+ function normalizeComments(comments) {
1463
+ if (!Array.isArray(comments)) return null;
1464
+ return comments.map(comment => ({
1465
+ value: comment.value || '',
1466
+ start: comment.start,
1467
+ end: comment.end,
1468
+ })).sort((left, right) => left.start - right.start);
1469
+ }
1470
+
1471
+ function scanComments(code, nodes) {
1472
+ const protectedRanges = nodes.filter(node =>
1473
+ node.type === 'Literal' || node.type === 'TemplateElement' || node.type === 'JSXText'
1474
+ ).map(node => [node.start, node.end]).sort((left, right) => left[0] - right[0]);
1475
+ const comments = [];
1476
+ const regexp = /\/\*[\s\S]*?\*\/|\/\/[^\r\n]*/g;
1477
+ let rangeIndex = 0;
1478
+ for (const match of code.matchAll(regexp)) {
1479
+ const start = match.index;
1480
+ while (rangeIndex < protectedRanges.length && protectedRanges[rangeIndex][1] <= start) rangeIndex++;
1481
+ const range = protectedRanges[rangeIndex];
1482
+ if (range && start >= range[0] && start < range[1]) continue;
1483
+ const block = match[0].startsWith('/*');
1484
+ comments.push({
1485
+ value: block ? match[0].slice(2, -2) : match[0].slice(2),
1486
+ start,
1487
+ end: start + match[0].length,
1488
+ });
1489
+ }
1490
+ return comments;
1491
+ }
1492
+
1493
+ function commentAnnotation(value) {
1494
+ for (const line of String(value).split(/\r?\n/)) {
1495
+ const normalized = line.replace(/^[\s*]*/, '').replace(/[\s*]*$/, '').trim();
1496
+ if (normalized === '@ngInject') return true;
1497
+ if (normalized === '@ngNoInject') return false;
1498
+ }
1499
+ return null;
1500
+ }
1501
+
1502
+ function explicitPriority(node) {
1503
+ if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportNamedDeclaration') return 0;
1504
+ if (node.type === 'VariableDeclaration' || node.type === 'ExpressionStatement') return 1;
1505
+ if (node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') return 2;
1506
+ if (node.type === 'MethodDefinition' || node.type === 'Property') return 3;
1507
+ if (node.type === 'ObjectExpression') return 4;
1508
+ if (WRAPPER_TYPES.has(node.type)) return 5;
1509
+ if (node.type === 'CallExpression') return 5;
1510
+ if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' || node.type === 'ClassExpression') return 6;
1511
+ if (node.type === 'Identifier') return 7;
1512
+ return 100;
1513
+ }
1514
+
1515
+ function explicitStart(node) {
1516
+ const target = node.type === 'ExportDefaultDeclaration' || node.type === 'ExportNamedDeclaration' ?
1517
+ node.declaration : node;
1518
+ if (!target?.decorators?.length) return node.start;
1519
+ return Math.min(node.start, ...target.decorators.map(decorator => decorator.start));
1520
+ }
1521
+
1522
+ function functionDirective(node) {
1523
+ if (node.body?.type !== 'BlockStatement') return null;
1524
+ for (const statement of node.body.body) {
1525
+ const value = directiveValue(statement);
1526
+ if (value == null) break;
1527
+ if (value === 'ngInject') return true;
1528
+ if (value === 'ngNoInject') return false;
1529
+ }
1530
+ return null;
1531
+ }
1532
+
1533
+ function directiveValue(statement) {
1534
+ if (statement?.type !== 'ExpressionStatement') return null;
1535
+ const expression = statement.expression;
1536
+ return expression?.type === 'Literal' && typeof expression.value === 'string' ? expression.value : null;
1537
+ }
1538
+
1539
+ function functionDependencies(node) {
1540
+ if (!isFunction(node)) return null;
1541
+ const result = [];
1542
+ for (const [index, parameter] of (node.params || []).entries()) {
1543
+ if (index === 0 && isTypeScriptThisParameter(parameter)) continue;
1544
+ const name = parameterName(parameter);
1545
+ if (!name) return null;
1546
+ result.push(name);
1547
+ }
1548
+ return result;
1549
+ }
1550
+
1551
+ function classDependencies(node) {
1552
+ if (!isClass(node)) return null;
1553
+ const constructor = node.body?.body?.find(item => item.type === 'MethodDefinition' &&
1554
+ item.kind === 'constructor' && isFunction(item.value) && item.value.body?.type === 'BlockStatement');
1555
+ return constructor ? functionDependencies(constructor.value) : [];
1556
+ }
1557
+
1558
+ function unwrapParameter(parameter) {
1559
+ parameter = unwrap(parameter);
1560
+ while (parameter?.type === 'AssignmentPattern' || parameter?.type === 'RestElement' || parameter?.type === 'TSParameterProperty') {
1561
+ parameter = unwrap(parameter.left || parameter.argument || parameter.parameter);
1562
+ }
1563
+ return parameter;
1564
+ }
1565
+
1566
+ function isTypeScriptThisParameter(parameter) {
1567
+ parameter = unwrapParameter(parameter);
1568
+ return parameter?.type === 'Identifier' && parameter.name === 'this' && Boolean(parameter.typeAnnotation);
1569
+ }
1570
+
1571
+ function parameterName(parameter) {
1572
+ parameter = unwrapParameter(parameter);
1573
+ return parameter?.type === 'Identifier' ? parameter.name : null;
1574
+ }
1575
+
1576
+ function runtimeParameterIndex(parameters, bindingName) {
1577
+ let runtimeIndex = 0;
1578
+ for (const parameter of parameters || []) {
1579
+ if (parameterName(parameter) === bindingName) return runtimeIndex;
1580
+ if (!isTypeScriptThisParameter(parameter)) runtimeIndex++;
1581
+ }
1582
+ return -1;
1583
+ }
1584
+
1585
+ function dependencyArray(dependencies) {
1586
+ return `[${dependencies.map(JSON.stringify).join(', ')}]`;
1587
+ }
1588
+
1589
+ function dependencyPrefix(dependencies) {
1590
+ return `[${dependencies.map(JSON.stringify).join(', ')}`;
1591
+ }
1592
+
1593
+ function isAnnotatedArray(node) {
1594
+ if (node?.type !== 'ArrayExpression' || node.elements.length === 0) return false;
1595
+ const last = unwrap(node.elements[node.elements.length - 1]);
1596
+ return (isFunction(last) || isClass(last)) && node.elements.slice(0, -1).every(isStringLiteral);
1597
+ }
1598
+
1599
+ function isInjectMember(node) {
1600
+ return node?.type === 'MemberExpression' && staticPropertyName(node) === '$inject';
1601
+ }
1602
+
1603
+ function isStringLiteral(node) {
1604
+ node = unwrap(node);
1605
+ return node?.type === 'Literal' && typeof node.value === 'string';
1606
+ }
1607
+
1608
+ function isObjectAssign(node) {
1609
+ node = unwrap(node);
1610
+ const callee = unwrap(node?.callee);
1611
+ return node?.type === 'CallExpression' && callee?.type === 'MemberExpression' && !callee.computed &&
1612
+ callee.object?.type === 'Identifier' && callee.object.name === 'Object' && staticPropertyName(callee) === 'assign';
1613
+ }
1614
+
1615
+ function containsSuper(node) {
1616
+ let result = false;
1617
+ walk(node, candidate => {
1618
+ if (candidate.type === 'Super') result = true;
1619
+ });
1620
+ return result;
1621
+ }
1622
+
1623
+ function assignedIdentifiers(pattern, result = []) {
1624
+ pattern = unwrap(pattern);
1625
+ if (!pattern) return result;
1626
+ if (pattern.type === 'Identifier') {
1627
+ result.push(pattern);
1628
+ } else if (pattern.type === 'AssignmentPattern') {
1629
+ assignedIdentifiers(pattern.left, result);
1630
+ } else if (pattern.type === 'RestElement' || pattern.type === 'TSParameterProperty') {
1631
+ assignedIdentifiers(pattern.argument || pattern.parameter, result);
1632
+ } else if (pattern.type === 'ArrayPattern') {
1633
+ for (const element of pattern.elements || []) assignedIdentifiers(element, result);
1634
+ } else if (pattern.type === 'ObjectPattern') {
1635
+ for (const property of pattern.properties || []) {
1636
+ assignedIdentifiers(property.type === 'RestElement' ? property.argument : property.value, result);
1637
+ }
1638
+ }
1639
+ return result;
1640
+ }
1641
+
1642
+ function findProperty(object, name) {
1643
+ return object?.properties?.find(property => property.type === 'Property' && staticPropertyName(property) === name) || null;
1644
+ }
1645
+
1646
+ function staticPropertyName(node) {
1647
+ const key = node?.property || node?.key;
1648
+ if (!key) return null;
1649
+ if (!node.computed && key.type === 'Identifier') return key.name;
1650
+ if (key.type === 'Literal' && typeof key.value === 'string') return key.value;
1651
+ return null;
1652
+ }
1653
+
1654
+ function unwrap(node) {
1655
+ while (WRAPPER_TYPES.has(node?.type)) node = node.expression;
1656
+ return node;
1657
+ }
1658
+
1659
+ function isFunction(node) {
1660
+ return node?.type === 'ArrowFunctionExpression' || node?.type === 'FunctionDeclaration' || node?.type === 'FunctionExpression';
1661
+ }
1662
+
1663
+ function isClass(node) {
1664
+ return node?.type === 'ClassDeclaration' || node?.type === 'ClassExpression';
1665
+ }
1666
+
1667
+ function isNode(value) {
1668
+ return value && typeof value === 'object' && typeof value.type === 'string';
1669
+ }
1670
+
1671
+ function forEachChild(node, callback) {
1672
+ for (const [key, value] of Object.entries(node)) {
1673
+ if (key === 'loc' || key === 'start' || key === 'end') continue;
1674
+ if (Array.isArray(value)) {
1675
+ for (const child of value) if (isNode(child)) callback(child, key);
1676
+ } else if (isNode(value)) {
1677
+ callback(value, key);
1678
+ }
1679
+ }
1680
+ }
1681
+
1682
+ function walk(node, visitor, seen = new WeakSet(), parent = null, key = null) {
1683
+ if (!isNode(node) || seen.has(node)) return;
1684
+ seen.add(node);
1685
+ visitor(node, parent, key);
1686
+ forEachChild(node, (child, childKey) => walk(child, visitor, seen, node, childKey));
1687
+ }