lint-suite 1.6.0 → 1.6.1

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.
Files changed (68) hide show
  1. package/README.md +149 -16
  2. package/eslint.cjs +1137 -132
  3. package/eslint.cjs.map +4 -4
  4. package/eslint.js +1205 -132
  5. package/eslint.js.map +4 -4
  6. package/lib/angular.d.ts.map +1 -1
  7. package/lib/rules/explicit-accessibility/explicit-accessibility.d.ts +12 -0
  8. package/lib/rules/explicit-accessibility/explicit-accessibility.d.ts.map +1 -0
  9. package/lib/rules/explicit-accessibility/explicit-accessibility.spec.d.ts +2 -0
  10. package/lib/rules/explicit-accessibility/explicit-accessibility.spec.d.ts.map +1 -0
  11. package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts +6 -0
  12. package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts.map +1 -0
  13. package/lib/rules/no-inline-object-types/no-inline-object-types.spec.d.ts +2 -0
  14. package/lib/rules/no-inline-object-types/no-inline-object-types.spec.d.ts.map +1 -0
  15. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.fixtures.spec.d.ts +2 -0
  16. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.fixtures.spec.d.ts.map +1 -0
  17. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.d.ts +4 -0
  18. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.d.ts.map +1 -0
  19. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts +5 -0
  20. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts.map +1 -0
  21. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts +4 -0
  22. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts.map +1 -0
  23. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/angular-index.type.d.ts +33 -0
  24. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/angular-index.type.d.ts.map +1 -0
  25. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts +7 -0
  26. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts.map +1 -0
  27. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts +4 -0
  28. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts.map +1 -0
  29. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts +4 -0
  30. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts.map +1 -0
  31. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts +4 -0
  32. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts.map +1 -0
  33. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.d.ts +8 -0
  34. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.d.ts.map +1 -0
  35. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.d.ts +2 -0
  36. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.d.ts.map +1 -0
  37. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.d.ts +4 -0
  38. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.d.ts.map +1 -0
  39. package/lib/rules/no-unused-angular-instance-fields/{angular-class-fields.d.ts → rule/angular/angular-class-fields.d.ts} +3 -3
  40. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts.map +1 -0
  41. package/lib/rules/no-unused-angular-instance-fields/{utils/angular-expression-reads.util.d.ts → rule/angular/angular-expression-reads.d.ts} +1 -1
  42. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.d.ts.map +1 -0
  43. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.d.ts.map +1 -0
  44. package/lib/rules/no-unused-angular-instance-fields/{common → rule/common}/no-unused-angular-instance-fields.type.d.ts +5 -0
  45. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts.map +1 -0
  46. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts +9 -0
  47. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts.map +1 -0
  48. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-instance-fields.spec.d.ts.map +1 -0
  49. package/lib/rules/no-unused-angular-instance-fields/{utils/typescript-field-reads.util.d.ts → rule/typescript/typescript-field-reads.d.ts} +1 -1
  50. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.d.ts.map +1 -0
  51. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts +6 -0
  52. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts.map +1 -0
  53. package/lib/rules/readonly-type-properties/readonly-type-properties.spec.d.ts +2 -0
  54. package/lib/rules/readonly-type-properties/readonly-type-properties.spec.d.ts.map +1 -0
  55. package/lib/typescript.d.ts.map +1 -1
  56. package/package.json +8 -3
  57. package/lib/rules/explicit-accessibility.d.ts +0 -12
  58. package/lib/rules/explicit-accessibility.d.ts.map +0 -1
  59. package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts.map +0 -1
  60. package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts.map +0 -1
  61. package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts.map +0 -1
  62. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts +0 -7
  63. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts.map +0 -1
  64. package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts.map +0 -1
  65. package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts.map +0 -1
  66. package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts.map +0 -1
  67. /package/lib/rules/no-unused-angular-instance-fields/{angular-metadata-reads.d.ts → rule/angular/angular-metadata-reads.d.ts} +0 -0
  68. /package/lib/rules/no-unused-angular-instance-fields/{no-unused-instance-fields.spec.d.ts → rule/no-unused-instance-fields.spec.d.ts} +0 -0
package/eslint.cjs CHANGED
@@ -128,19 +128,19 @@ var import_globals = __toESM(require("globals"), 1);
128
128
  var import_angular_eslint2 = require("angular-eslint");
129
129
  var import_config2 = require("eslint/config");
130
130
 
131
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.ts
131
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.ts
132
132
  var import_utils4 = require("@typescript-eslint/utils");
133
133
 
134
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
134
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.ts
135
135
  var import_utils2 = require("@typescript-eslint/utils");
136
136
 
137
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.ts
137
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
138
138
  var import_node_fs = require("node:fs");
139
139
  var import_node_path = require("node:path");
140
140
  var import_compiler2 = require("@angular/compiler");
141
141
  var import_utils = require("@typescript-eslint/utils");
142
142
 
143
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.ts
143
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.ts
144
144
  var import_compiler = require("@angular/compiler");
145
145
  var ReadCollector = class extends import_compiler.CombinedRecursiveAstVisitor {
146
146
  constructor(boundTarget, action) {
@@ -190,7 +190,7 @@ var collectAngularExpressionReads = (nodes, boundTarget, action) => {
190
190
  return collector.reads;
191
191
  };
192
192
 
193
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.ts
193
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
194
194
  var externalTemplates = /* @__PURE__ */ new Map();
195
195
  var parser = new import_compiler2.Parser(new import_compiler2.Lexer());
196
196
  var templateBinder = new import_compiler2.R3TargetBinder(null);
@@ -328,11 +328,15 @@ var metadataReads = (metadata, component, filename, remainingNames) => {
328
328
  return null;
329
329
  };
330
330
 
331
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
331
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.ts
332
332
  var managedApis = {
333
333
  input: true,
334
334
  model: true,
335
- output: true
335
+ output: true,
336
+ viewChild: true,
337
+ viewChildren: true,
338
+ contentChild: true,
339
+ contentChildren: true
336
340
  };
337
341
  var angularName = (node, imports) => {
338
342
  if (node.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier) {
@@ -346,6 +350,28 @@ var angularName = (node, imports) => {
346
350
  }
347
351
  return angularName(node.object, imports);
348
352
  };
353
+ var calleeRoot = (node) => {
354
+ if (node.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier) {
355
+ return node;
356
+ }
357
+ if (node.type !== import_utils2.TSESTree.AST_NODE_TYPES.MemberExpression || node.object.type === import_utils2.TSESTree.AST_NODE_TYPES.Super) {
358
+ return void 0;
359
+ }
360
+ return calleeRoot(node.object);
361
+ };
362
+ var isImportBinding = (node, sourceCode) => {
363
+ const root = calleeRoot(node);
364
+ if (!root) {
365
+ return false;
366
+ }
367
+ for (let scope = sourceCode.getScope(root); scope; scope = scope.upper) {
368
+ const variable = scope.set.get(root.name);
369
+ if (variable) {
370
+ return variable.defs[0]?.type === "ImportBinding";
371
+ }
372
+ }
373
+ return false;
374
+ };
349
375
  var angularClass = (node, imports) => {
350
376
  for (const decorator of node.decorators) {
351
377
  if (decorator.expression.type !== import_utils2.TSESTree.AST_NODE_TYPES.CallExpression) {
@@ -373,24 +399,72 @@ var lifecycleHooks = {
373
399
  ngAfterViewChecked: true,
374
400
  ngOnDestroy: true
375
401
  };
402
+ var formsInterfaceMethods = {
403
+ AsyncValidator: ["validate", "registerOnValidatorChange"],
404
+ ControlValueAccessor: [
405
+ "writeValue",
406
+ "registerOnChange",
407
+ "registerOnTouched",
408
+ "setDisabledState"
409
+ ],
410
+ Validator: ["validate", "registerOnValidatorChange"]
411
+ };
412
+ var implementedFormsMethods = (node) => {
413
+ const methods = /* @__PURE__ */ new Set();
414
+ for (const heritage of node.implements) {
415
+ if (heritage.expression.type !== import_utils2.TSESTree.AST_NODE_TYPES.Identifier) {
416
+ continue;
417
+ }
418
+ for (const name of formsInterfaceMethods[heritage.expression.name] ?? []) {
419
+ methods.add(name);
420
+ }
421
+ }
422
+ return methods;
423
+ };
376
424
  var isInstanceMethod = (node) => node.type === import_utils2.TSESTree.AST_NODE_TYPES.MethodDefinition && node.key.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier;
377
- var isExcludedField = (node, component) => node.static || node.declare || node.override || node.decorators.length > 0 || !component && node.accessibility !== "private";
378
- var isManagedField = (node, imports) => {
425
+ var isAngularComponentRefField = (node, sourceCode) => {
426
+ const type = node.typeAnnotation?.typeAnnotation;
427
+ if (type?.type !== import_utils2.TSESTree.AST_NODE_TYPES.TSTypeReference || type.typeName.type !== import_utils2.TSESTree.AST_NODE_TYPES.Identifier) {
428
+ return false;
429
+ }
430
+ const reference = sourceCode.getScope(type.typeName).references.find(({ identifier }) => identifier === type.typeName);
431
+ const variable = reference?.resolved;
432
+ const definition = variable?.defs[0];
433
+ return definition?.type === "ImportBinding" && definition.node.type === import_utils2.TSESTree.AST_NODE_TYPES.ImportSpecifier && definition.parent?.type === import_utils2.TSESTree.AST_NODE_TYPES.ImportDeclaration && definition.parent.source.value === "@angular/core" && (definition.node.imported.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier && definition.node.imported.name === "ComponentRef" || definition.node.imported.type === import_utils2.TSESTree.AST_NODE_TYPES.Literal && definition.node.imported.value === "ComponentRef");
434
+ };
435
+ var isExcludedField = (node, localPrivateOnly, sourceCode) => node.static || node.declare || node.override || node.decorators.length > 0 || isAngularComponentRefField(node, sourceCode) || localPrivateOnly && node.accessibility !== "private";
436
+ var hasAutomaticEffectCleanup = (node) => {
437
+ const options = node.arguments[1];
438
+ if (options === void 0) {
439
+ return true;
440
+ }
441
+ if (options.type !== import_utils2.TSESTree.AST_NODE_TYPES.ObjectExpression) {
442
+ return false;
443
+ }
444
+ return options.properties.every((property) => {
445
+ if (property.type !== import_utils2.TSESTree.AST_NODE_TYPES.Property || property.computed) {
446
+ return false;
447
+ }
448
+ const manualCleanup = property.key.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier && property.key.name === "manualCleanup" || property.key.type === import_utils2.TSESTree.AST_NODE_TYPES.Literal && property.key.value === "manualCleanup";
449
+ return !manualCleanup || property.value.type === import_utils2.TSESTree.AST_NODE_TYPES.Literal && property.value.value === false;
450
+ });
451
+ };
452
+ var isManagedField = (node, imports, allowEffectFields, sourceCode) => {
379
453
  if (node.value?.type !== import_utils2.TSESTree.AST_NODE_TYPES.CallExpression) {
380
454
  return false;
381
455
  }
382
456
  const name = angularName(node.value.callee, imports);
383
- return typeof name === "string" && managedApis[name] === true;
457
+ return typeof name === "string" && managedApis[name] === true || allowEffectFields && name === "effect" && isImportBinding(node.value.callee, sourceCode) && hasAutomaticEffectCleanup(node.value);
384
458
  };
385
- var field = (node, imports, component) => {
386
- if (isInstanceField(node) && !isExcludedField(node, component) && !isManagedField(node, imports)) {
459
+ var field = (node, imports, localPrivateOnly, allowEffectFields, sourceCode) => {
460
+ if (isInstanceField(node) && !isExcludedField(node, localPrivateOnly, sourceCode) && !isManagedField(node, imports, allowEffectFields, sourceCode)) {
387
461
  return { messageId: "unusedField", name: node.key.name, node };
388
462
  }
389
463
  return null;
390
464
  };
391
- var isExcludedMethod = (node, component) => node.static || node.override || node.decorators.length > 0 || node.computed || node.kind !== "method" || node.value.body === null || lifecycleHooks[node.key.name] === true || !component && node.accessibility !== "private";
392
- var method = (node, component) => {
393
- if (isInstanceMethod(node) && !isExcludedMethod(node, component)) {
465
+ var isExcludedMethod = (node, localPrivateOnly, implementedMethods) => node.static || node.override || node.decorators.length > 0 || node.computed || node.kind !== "method" || node.value.body === null || lifecycleHooks[node.key.name] === true || implementedMethods.has(node.key.name) || localPrivateOnly && node.accessibility !== "private";
466
+ var method = (node, localPrivateOnly, implementedMethods) => {
467
+ if (isInstanceMethod(node) && !isExcludedMethod(node, localPrivateOnly, implementedMethods)) {
394
468
  return { messageId: "unusedMethod", name: node.key.name, node };
395
469
  }
396
470
  return null;
@@ -408,15 +482,24 @@ var addAngularImport = (node, imports) => {
408
482
  }
409
483
  }
410
484
  };
411
- var reportUnusedMembers = (context, imports, classes, dynamicClasses) => {
485
+ var reportUnusedMembers = (context, imports, classes, dynamicClasses, allowEffectFields, projectMemberUsed) => {
486
+ const projectAnalysis = projectMemberUsed !== void 0;
412
487
  for (const entry of classes) {
413
488
  const ngClass = angularClass(entry.node, imports);
414
489
  if (!ngClass || dynamicClasses.has(entry)) {
415
490
  continue;
416
491
  }
492
+ const localPrivateOnly = !projectAnalysis && (!ngClass.component || entry.node.abstract === true);
493
+ const implementedMethods = implementedFormsMethods(entry.node);
417
494
  const members = [];
418
495
  for (const node of entry.node.body.body) {
419
- const candidate = field(node, imports, ngClass.component) ?? method(node, ngClass.component);
496
+ const candidate = field(
497
+ node,
498
+ imports,
499
+ localPrivateOnly,
500
+ allowEffectFields,
501
+ context.sourceCode
502
+ ) ?? method(node, localPrivateOnly, implementedMethods);
420
503
  if (candidate) {
421
504
  members.push(candidate);
422
505
  }
@@ -440,18 +523,800 @@ var reportUnusedMembers = (context, imports, classes, dynamicClasses) => {
440
523
  continue;
441
524
  }
442
525
  for (const candidate of unreadMembers) {
443
- if (!reads.has(candidate.name)) {
444
- context.report({
445
- data: { name: candidate.name },
446
- messageId: candidate.messageId,
447
- node: candidate.node.key
448
- });
526
+ if (reads.has(candidate.name) || projectMemberUsed?.(candidate.node) === true) {
527
+ continue;
528
+ }
529
+ context.report({
530
+ data: { name: candidate.name },
531
+ messageId: candidate.messageId,
532
+ node: candidate.node.key
533
+ });
534
+ }
535
+ }
536
+ };
537
+
538
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
539
+ var import_node_fs3 = require("node:fs");
540
+ var import_node_path2 = require("node:path");
541
+
542
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
543
+ var compilerCli = __toESM(require("@angular/compiler-cli"), 1);
544
+ var import_typescript4 = require("typescript");
545
+
546
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.ts
547
+ var isSpecFile = (fileName) => /\.spec\.[cm]?[tj]sx?$/u.test(fileName);
548
+
549
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
550
+ var import_node_fs2 = require("node:fs");
551
+ var import_compiler3 = require("@angular/compiler");
552
+ var import_typescript3 = require("typescript");
553
+
554
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.ts
555
+ var import_typescript = require("typescript");
556
+ var symbolsForName = (checker, type, name) => {
557
+ const symbol = checker.getPropertyOfType(checker.getApparentType(type), name);
558
+ if (symbol) {
559
+ return [symbol];
560
+ }
561
+ if (!type.isUnionOrIntersection()) {
562
+ return [];
563
+ }
564
+ const symbols = /* @__PURE__ */ new Set();
565
+ for (const member of type.types) {
566
+ for (const memberSymbol of symbolsForName(checker, member, name)) {
567
+ symbols.add(memberSymbol);
568
+ }
569
+ }
570
+ return [...symbols];
571
+ };
572
+ var stringIndexTypes = (checker, type) => {
573
+ const indexedType = checker.getIndexTypeOfType(
574
+ checker.getApparentType(type),
575
+ import_typescript.IndexKind.String
576
+ );
577
+ if (indexedType) {
578
+ return [indexedType];
579
+ }
580
+ if (!type.isUnionOrIntersection()) {
581
+ return [];
582
+ }
583
+ const indexedTypes = /* @__PURE__ */ new Set();
584
+ for (const member of type.types) {
585
+ for (const memberIndexedType of stringIndexTypes(checker, member)) {
586
+ indexedTypes.add(memberIndexedType);
587
+ }
588
+ }
589
+ return [...indexedTypes];
590
+ };
591
+
592
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.ts
593
+ var import_typescript2 = require("typescript");
594
+ var importedFromAngularCore = (declaration) => {
595
+ let current = declaration;
596
+ while (current.parent) {
597
+ current = current.parent;
598
+ if ((0, import_typescript2.isImportDeclaration)(current)) {
599
+ return (0, import_typescript2.isStringLiteralLike)(current.moduleSpecifier) && current.moduleSpecifier.text === "@angular/core";
600
+ }
601
+ }
602
+ return false;
603
+ };
604
+ var isAngularComponentDecorator = (expression, checker) => {
605
+ const unresolved = checker.getSymbolAtLocation(expression);
606
+ if (!unresolved) {
607
+ return false;
608
+ }
609
+ const symbol = (unresolved.flags & import_typescript2.SymbolFlags.Alias) === 0 ? unresolved : checker.getAliasedSymbol(unresolved);
610
+ return symbol.getName() === "Component" && ((unresolved.declarations ?? []).some(importedFromAngularCore) || (symbol.declarations ?? []).some(
611
+ (declaration) => declaration.getSourceFile().fileName.replaceAll("\\", "/").includes("/node_modules/@angular/core/")
612
+ ));
613
+ };
614
+ var componentMetadata = (declaration, checker) => {
615
+ if (!(0, import_typescript2.canHaveDecorators)(declaration)) {
616
+ return void 0;
617
+ }
618
+ for (const decorator of (0, import_typescript2.getDecorators)(declaration) ?? []) {
619
+ if (!(0, import_typescript2.isCallExpression)(decorator.expression) || !isAngularComponentDecorator(decorator.expression.expression, checker)) {
620
+ continue;
621
+ }
622
+ const metadata = decorator.expression.arguments[0];
623
+ return metadata && (0, import_typescript2.isObjectLiteralExpression)(metadata) ? metadata : null;
624
+ }
625
+ return void 0;
626
+ };
627
+ var inlineTemplate = (declaration, checker) => {
628
+ const metadata = componentMetadata(declaration, checker);
629
+ if (!metadata) {
630
+ return null;
631
+ }
632
+ for (const property of metadata.properties) {
633
+ if ((0, import_typescript2.isPropertyAssignment)(property) && ((0, import_typescript2.isIdentifier)(property.name) || (0, import_typescript2.isStringLiteralLike)(property.name) || (0, import_typescript2.isNumericLiteral)(property.name)) && property.name.text === "template") {
634
+ return (0, import_typescript2.isStringLiteralLike)(property.initializer) ? property.initializer.text : null;
635
+ }
636
+ }
637
+ return null;
638
+ };
639
+ var projectComponents = (program, checker) => {
640
+ const declarations = [];
641
+ let valid = true;
642
+ const visit = (node) => {
643
+ if ((0, import_typescript2.isClassLike)(node)) {
644
+ const metadata = componentMetadata(node, checker);
645
+ if (metadata === null) {
646
+ valid = false;
647
+ } else if (metadata) {
648
+ declarations.push(node);
649
+ }
650
+ }
651
+ (0, import_typescript2.forEachChild)(node, visit);
652
+ };
653
+ for (const sourceFile of program.getSourceFiles()) {
654
+ if (!sourceFile.isDeclarationFile && !program.isSourceFileFromExternalLibrary(sourceFile) && !isSpecFile(sourceFile.fileName)) {
655
+ visit(sourceFile);
656
+ }
657
+ }
658
+ return valid ? declarations : null;
659
+ };
660
+
661
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
662
+ var propertyIdentifierKind = 0;
663
+ var referenceIdentifierKind = 5;
664
+ var RootCollector = class extends import_compiler3.CombinedRecursiveAstVisitor {
665
+ roots = [];
666
+ visitPropertyRead(node, context) {
667
+ if (node.receiver instanceof import_compiler3.ImplicitReceiver || node.receiver instanceof import_compiler3.ThisReceiver) {
668
+ this.roots.push(node);
669
+ }
670
+ return super.visitPropertyRead(node, context);
671
+ }
672
+ };
673
+ var readChain = (node) => {
674
+ const names = [];
675
+ let current = node;
676
+ let called = false;
677
+ while (true) {
678
+ names.unshift({ called, name: current.name });
679
+ if (current.receiver instanceof import_compiler3.PropertyRead || current.receiver instanceof import_compiler3.SafePropertyRead) {
680
+ current = current.receiver;
681
+ called = false;
682
+ continue;
683
+ }
684
+ if (current.receiver instanceof import_compiler3.Call) {
685
+ if (!(current.receiver.receiver instanceof import_compiler3.PropertyRead) && !(current.receiver.receiver instanceof import_compiler3.SafePropertyRead)) {
686
+ return null;
687
+ }
688
+ current = current.receiver.receiver;
689
+ called = true;
690
+ continue;
691
+ }
692
+ return (current.receiver instanceof import_compiler3.ImplicitReceiver || current.receiver instanceof import_compiler3.ThisReceiver) && current instanceof import_compiler3.PropertyRead ? { names, root: current } : null;
693
+ }
694
+ };
695
+ var ReadCollector2 = class extends import_compiler3.CombinedRecursiveAstVisitor {
696
+ reads = [];
697
+ record(node) {
698
+ const chain = readChain(node);
699
+ if (chain) {
700
+ this.reads.push(chain);
701
+ }
702
+ }
703
+ visitBinary(node, context) {
704
+ if (node.operation !== "=") {
705
+ return super.visitBinary(node, context);
706
+ }
707
+ if (node.left instanceof import_compiler3.PropertyRead || node.left instanceof import_compiler3.SafePropertyRead || node.left instanceof import_compiler3.KeyedRead || node.left instanceof import_compiler3.SafeKeyedRead) {
708
+ this.visit(node.left.receiver);
709
+ }
710
+ if (node.left instanceof import_compiler3.KeyedRead || node.left instanceof import_compiler3.SafeKeyedRead) {
711
+ this.visit(node.left.key);
712
+ }
713
+ this.visit(node.right);
714
+ return void 0;
715
+ }
716
+ visitPropertyRead(node, context) {
717
+ this.record(node);
718
+ return super.visitPropertyRead(node, context);
719
+ }
720
+ visitSafePropertyRead(node, context) {
721
+ this.record(node);
722
+ return super.visitSafePropertyRead(node, context);
723
+ }
724
+ };
725
+ var templateText = (declaration, component, checker) => {
726
+ if (component.template.fileUrl === component.fileUrl) {
727
+ return inlineTemplate(declaration, checker);
728
+ }
729
+ return (0, import_node_fs2.readFileSync)(component.template.fileUrl, "utf8");
730
+ };
731
+ var isPropertyIdentifier = (identifier) => identifier.kind === propertyIdentifierKind;
732
+ var addResolvedPath = (declaration, names, checker, addDeclaration, allowMissingRoot) => {
733
+ let types = [checker.getTypeAtLocation(declaration)];
734
+ for (const [index, segment] of names.entries()) {
735
+ const symbols = new Set(
736
+ types.flatMap((type) => symbolsForName(checker, type, segment.name))
737
+ );
738
+ if (symbols.size === 0) {
739
+ const indexedTypes = types.flatMap(
740
+ (type) => stringIndexTypes(checker, type)
741
+ );
742
+ if (indexedTypes.length > 0) {
743
+ types = indexedTypes;
744
+ continue;
745
+ }
746
+ if (types.some(
747
+ (type) => (type.flags & (import_typescript3.TypeFlags.Any | import_typescript3.TypeFlags.Unknown)) !== 0
748
+ )) {
749
+ return true;
750
+ }
751
+ return index === 0 && allowMissingRoot;
752
+ }
753
+ for (const symbol of symbols) {
754
+ for (const memberDeclaration of symbol.declarations ?? []) {
755
+ addDeclaration(memberDeclaration);
756
+ }
757
+ }
758
+ types = [...symbols].map(
759
+ (symbol) => checker.getTypeOfSymbolAtLocation(symbol, declaration)
760
+ );
761
+ if (segment.called) {
762
+ const returnTypes = types.flatMap(
763
+ (type) => checker.getSignaturesOfType(type, import_typescript3.SignatureKind.Call).map((signature) => signature.getReturnType())
764
+ );
765
+ if (returnTypes.length === 0) {
766
+ if (types.some(
767
+ (type) => (type.flags & (import_typescript3.TypeFlags.Any | import_typescript3.TypeFlags.Unknown)) !== 0
768
+ )) {
769
+ return true;
770
+ }
771
+ return false;
772
+ }
773
+ types = returnTypes;
774
+ }
775
+ }
776
+ return true;
777
+ };
778
+ var addTemplateReads = (declaration, component, checker, addDeclaration, missingRootNames) => {
779
+ const source = templateText(declaration, component, checker);
780
+ if (source === null) {
781
+ return false;
782
+ }
783
+ const parsed = (0, import_compiler3.parseTemplate)(source, component.template.fileUrl);
784
+ if (parsed.errors?.length) {
785
+ return false;
786
+ }
787
+ const roots = new RootCollector();
788
+ const reads = new ReadCollector2();
789
+ for (const node of parsed.nodes) {
790
+ roots.visit(node);
791
+ reads.visit(node);
792
+ }
793
+ roots.roots.sort((left, right) => left.nameSpan.start - right.nameSpan.start);
794
+ const indexedRoots = [...component.template.identifiers].filter(isPropertyIdentifier).sort((left, right) => left.span.start - right.span.start);
795
+ const indexedByRoot = /* @__PURE__ */ new Map();
796
+ let indexedRootIndex = 0;
797
+ for (const root of roots.roots) {
798
+ const identifier = indexedRoots[indexedRootIndex];
799
+ if (identifier?.name === root.name) {
800
+ indexedByRoot.set(root, identifier);
801
+ indexedRootIndex += 1;
802
+ } else if (!missingRootNames.has(root.name)) {
803
+ return false;
804
+ }
805
+ }
806
+ if (indexedRootIndex !== indexedRoots.length) {
807
+ return false;
808
+ }
809
+ for (const chain of reads.reads) {
810
+ const identifier = indexedByRoot.get(chain.root);
811
+ if (!identifier) {
812
+ if (missingRootNames.has(chain.root.name)) {
813
+ continue;
449
814
  }
815
+ return false;
816
+ }
817
+ if (identifier.target === null) {
818
+ if (!addResolvedPath(
819
+ declaration,
820
+ chain.names,
821
+ checker,
822
+ addDeclaration,
823
+ true
824
+ )) {
825
+ return false;
826
+ }
827
+ continue;
828
+ }
829
+ if (identifier.target.kind !== referenceIdentifierKind) {
830
+ continue;
831
+ }
832
+ const referenceTarget = identifier.target.target;
833
+ if (!referenceTarget) {
834
+ return false;
835
+ }
836
+ const targetDeclaration = referenceTarget.directive;
837
+ const targetNames = chain.names.slice(1);
838
+ const targetResolved = !targetDeclaration || addResolvedPath(
839
+ targetDeclaration,
840
+ targetNames,
841
+ checker,
842
+ addDeclaration,
843
+ false
844
+ );
845
+ if (!targetResolved) {
846
+ return false;
450
847
  }
451
848
  }
849
+ return true;
452
850
  };
453
851
 
454
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.ts
852
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
853
+ var hasReadConfiguration = (value) => "readConfiguration" in value && typeof value.readConfiguration === "function";
854
+ var contextIndexErrorPattern = /^Impossible state: "(.+)" not found in "/u;
855
+ var missingContextRootNames = (errors) => {
856
+ const names = /* @__PURE__ */ new Set();
857
+ for (const error of errors) {
858
+ const match = contextIndexErrorPattern.exec(error.message);
859
+ if (!match?.[1]) {
860
+ return null;
861
+ }
862
+ names.add(match[1]);
863
+ }
864
+ return names;
865
+ };
866
+ var reusingCompilerHost = (program, options) => {
867
+ const base2 = (0, import_typescript4.createCompilerHost)(options, true);
868
+ return {
869
+ ...base2,
870
+ getSourceFile: (fileName, ...rest) => program.getSourceFile(fileName) ?? base2.getSourceFile(fileName, ...rest)
871
+ };
872
+ };
873
+ var collectAngularTemplateReads = (program, configFilePath, addDeclaration) => {
874
+ if (!hasReadConfiguration(compilerCli)) {
875
+ return null;
876
+ }
877
+ const configuration = compilerCli.readConfiguration(configFilePath);
878
+ if (configuration.errors.some(
879
+ (diagnostic) => diagnostic.category === import_typescript4.DiagnosticCategory.Error
880
+ )) {
881
+ return null;
882
+ }
883
+ const angularProgram = new compilerCli.NgtscProgram(
884
+ configuration.rootNames,
885
+ configuration.options,
886
+ reusingCompilerHost(program, configuration.options)
887
+ );
888
+ if (angularProgram.getNgOptionDiagnostics().some(
889
+ (diagnostic) => diagnostic.category === import_typescript4.DiagnosticCategory.Error
890
+ )) {
891
+ return null;
892
+ }
893
+ const indexedComponents = angularProgram.getIndexedComponents();
894
+ const typescriptProgram = angularProgram.getTsProgram();
895
+ const checker = typescriptProgram.getTypeChecker();
896
+ const components = projectComponents(typescriptProgram, checker);
897
+ if (!components || components.some((declaration) => !indexedComponents.has(declaration))) {
898
+ return null;
899
+ }
900
+ const templateFiles = /* @__PURE__ */ new Set();
901
+ for (const [declaration, component] of indexedComponents) {
902
+ if (isSpecFile(declaration.getSourceFile().fileName)) {
903
+ continue;
904
+ }
905
+ const missingRootNames = missingContextRootNames(component.errors);
906
+ if (!missingRootNames) {
907
+ return null;
908
+ }
909
+ if (!addTemplateReads(
910
+ declaration,
911
+ component,
912
+ checker,
913
+ addDeclaration,
914
+ missingRootNames
915
+ )) {
916
+ return null;
917
+ }
918
+ if (component.template.fileUrl !== component.fileUrl) {
919
+ templateFiles.add(component.template.fileUrl);
920
+ }
921
+ }
922
+ return [...templateFiles];
923
+ };
924
+
925
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
926
+ var import_typescript7 = require("typescript");
927
+
928
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
929
+ var import_typescript6 = require("typescript");
930
+
931
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.ts
932
+ var import_typescript5 = require("typescript");
933
+ var addSymbolDeclarations = (checker, symbol, addDeclaration) => {
934
+ const resolved = (symbol.flags & import_typescript5.SymbolFlags.Alias) === 0 ? symbol : checker.getAliasedSymbol(symbol);
935
+ for (const declaration of resolved.declarations ?? []) {
936
+ addDeclaration(declaration);
937
+ }
938
+ };
939
+ var allPropertySymbols = (checker, type) => {
940
+ const symbols = new Set(
941
+ checker.getPropertiesOfType(checker.getApparentType(type))
942
+ );
943
+ if (type.isUnionOrIntersection()) {
944
+ for (const member of type.types) {
945
+ for (const symbol of allPropertySymbols(checker, member)) {
946
+ symbols.add(symbol);
947
+ }
948
+ }
949
+ }
950
+ return [...symbols];
951
+ };
952
+ var addNamedProperties = (checker, type, names, addDeclaration) => {
953
+ const symbols = names ? names.flatMap((name) => symbolsForName(checker, type, name)) : allPropertySymbols(checker, type);
954
+ for (const symbol of symbols) {
955
+ addSymbolDeclarations(checker, symbol, addDeclaration);
956
+ }
957
+ return symbols;
958
+ };
959
+ var literalPropertyNames = (type) => {
960
+ if (type.isStringLiteral() || type.isNumberLiteral()) {
961
+ return [String(type.value)];
962
+ }
963
+ if (!type.isUnion()) {
964
+ return null;
965
+ }
966
+ const names = [];
967
+ for (const member of type.types) {
968
+ const memberNames = literalPropertyNames(member);
969
+ if (!memberNames) {
970
+ return null;
971
+ }
972
+ names.push(...memberNames);
973
+ }
974
+ return names;
975
+ };
976
+ var propertyName = (checker, node) => {
977
+ if ((0, import_typescript5.isIdentifier)(node) || (0, import_typescript5.isPrivateIdentifier)(node) || (0, import_typescript5.isStringLiteralLike)(node) || (0, import_typescript5.isNumericLiteral)(node) || (0, import_typescript5.isBigIntLiteral)(node)) {
978
+ return [node.text];
979
+ }
980
+ return literalPropertyNames(checker.getTypeAtLocation(node.expression));
981
+ };
982
+
983
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
984
+ var isBindingPattern = (node) => (0, import_typescript6.isArrayBindingPattern)(node) || (0, import_typescript6.isObjectBindingPattern)(node);
985
+ var lazyType = (compute) => {
986
+ let type;
987
+ return () => type ??= compute();
988
+ };
989
+ var skippable = (names, nested, candidateNames) => !nested && names !== null && !names.some((name) => candidateNames.has(name));
990
+ var collectBindingPattern = (pattern, type, checker, addDeclaration, candidateNames) => {
991
+ if ((0, import_typescript6.isArrayBindingPattern)(pattern)) {
992
+ for (const [index, element] of pattern.elements.entries()) {
993
+ if ((0, import_typescript6.isOmittedExpression)(element)) {
994
+ continue;
995
+ }
996
+ const names = element.dotDotDotToken ? null : [String(index)];
997
+ const nested = isBindingPattern(element.name);
998
+ if (skippable(names, nested, candidateNames)) {
999
+ continue;
1000
+ }
1001
+ const symbols = addNamedProperties(
1002
+ checker,
1003
+ type(),
1004
+ names,
1005
+ addDeclaration
1006
+ );
1007
+ if (nested) {
1008
+ for (const symbol of symbols) {
1009
+ collectBindingPattern(
1010
+ element.name,
1011
+ lazyType(() => checker.getTypeOfSymbolAtLocation(symbol, element)),
1012
+ checker,
1013
+ addDeclaration,
1014
+ candidateNames
1015
+ );
1016
+ }
1017
+ }
1018
+ }
1019
+ return;
1020
+ }
1021
+ const consumed = /* @__PURE__ */ new Set();
1022
+ for (const element of pattern.elements) {
1023
+ if (element.dotDotDotToken) {
1024
+ for (const symbol of allPropertySymbols(checker, type())) {
1025
+ if (!consumed.has(symbol.name)) {
1026
+ addSymbolDeclarations(checker, symbol, addDeclaration);
1027
+ }
1028
+ }
1029
+ continue;
1030
+ }
1031
+ const names = element.propertyName ? propertyName(checker, element.propertyName) : (0, import_typescript6.isIdentifier)(element.name) ? [element.name.text] : null;
1032
+ for (const name of names ?? []) {
1033
+ consumed.add(name);
1034
+ }
1035
+ const nested = isBindingPattern(element.name);
1036
+ if (skippable(names, nested, candidateNames)) {
1037
+ continue;
1038
+ }
1039
+ const symbols = addNamedProperties(checker, type(), names, addDeclaration);
1040
+ if (nested) {
1041
+ for (const symbol of symbols) {
1042
+ collectBindingPattern(
1043
+ element.name,
1044
+ lazyType(() => checker.getTypeOfSymbolAtLocation(symbol, element)),
1045
+ checker,
1046
+ addDeclaration,
1047
+ candidateNames
1048
+ );
1049
+ }
1050
+ }
1051
+ }
1052
+ };
1053
+ var collectAssignmentPattern = (pattern, type, checker, addDeclaration, candidateNames) => {
1054
+ if ((0, import_typescript6.isArrayLiteralExpression)(pattern)) {
1055
+ for (const [index, element] of pattern.elements.entries()) {
1056
+ if ((0, import_typescript6.isOmittedExpression)(element)) {
1057
+ continue;
1058
+ }
1059
+ const names = (0, import_typescript6.isSpreadElement)(element) ? null : [String(index)];
1060
+ const nested = (0, import_typescript6.isArrayLiteralExpression)(element) || (0, import_typescript6.isObjectLiteralExpression)(element);
1061
+ if (skippable(names, nested, candidateNames)) {
1062
+ continue;
1063
+ }
1064
+ const symbols = addNamedProperties(
1065
+ checker,
1066
+ type(),
1067
+ names,
1068
+ addDeclaration
1069
+ );
1070
+ if (nested) {
1071
+ for (const symbol of symbols) {
1072
+ collectAssignmentPattern(
1073
+ element,
1074
+ lazyType(() => checker.getTypeOfSymbolAtLocation(symbol, element)),
1075
+ checker,
1076
+ addDeclaration,
1077
+ candidateNames
1078
+ );
1079
+ }
1080
+ }
1081
+ }
1082
+ return;
1083
+ }
1084
+ const consumed = /* @__PURE__ */ new Set();
1085
+ for (const property of pattern.properties) {
1086
+ if ((0, import_typescript6.isSpreadAssignment)(property)) {
1087
+ for (const symbol of allPropertySymbols(checker, type())) {
1088
+ if (!consumed.has(symbol.name)) {
1089
+ addSymbolDeclarations(checker, symbol, addDeclaration);
1090
+ }
1091
+ }
1092
+ continue;
1093
+ }
1094
+ const names = propertyName(checker, property.name);
1095
+ for (const name of names ?? []) {
1096
+ consumed.add(name);
1097
+ }
1098
+ const nested = (0, import_typescript6.isPropertyAssignment)(property) && ((0, import_typescript6.isArrayLiteralExpression)(property.initializer) || (0, import_typescript6.isObjectLiteralExpression)(property.initializer));
1099
+ if (skippable(names, nested, candidateNames)) {
1100
+ continue;
1101
+ }
1102
+ const symbols = addNamedProperties(checker, type(), names, addDeclaration);
1103
+ if (nested) {
1104
+ for (const symbol of symbols) {
1105
+ collectAssignmentPattern(
1106
+ property.initializer,
1107
+ lazyType(() => checker.getTypeOfSymbolAtLocation(symbol, property)),
1108
+ checker,
1109
+ addDeclaration,
1110
+ candidateNames
1111
+ );
1112
+ }
1113
+ }
1114
+ }
1115
+ };
1116
+ var collectDestructuringReads = (node, checker, addDeclaration, candidateNames) => {
1117
+ const isDestructuringBindingPattern = isBindingPattern(node) && !((0, import_typescript6.isBindingElement)(node.parent) && node.parent.name === node);
1118
+ if (isDestructuringBindingPattern) {
1119
+ const declaration = node.parent;
1120
+ const source = (0, import_typescript6.isVariableDeclaration)(declaration) && declaration.initializer ? declaration.initializer : declaration;
1121
+ collectBindingPattern(
1122
+ node,
1123
+ lazyType(() => checker.getTypeAtLocation(source)),
1124
+ checker,
1125
+ addDeclaration,
1126
+ candidateNames
1127
+ );
1128
+ return;
1129
+ }
1130
+ const isDestructuringAssignment = (0, import_typescript6.isBinaryExpression)(node) && node.operatorToken.kind === import_typescript6.SyntaxKind.EqualsToken && ((0, import_typescript6.isArrayLiteralExpression)(node.left) || (0, import_typescript6.isObjectLiteralExpression)(node.left));
1131
+ if (isDestructuringAssignment) {
1132
+ collectAssignmentPattern(
1133
+ node.left,
1134
+ lazyType(() => checker.getTypeAtLocation(node.right)),
1135
+ checker,
1136
+ addDeclaration,
1137
+ candidateNames
1138
+ );
1139
+ }
1140
+ };
1141
+
1142
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
1143
+ var isExpressionWrapper = (parent, child) => ((0, import_typescript7.isParenthesizedExpression)(parent) || (0, import_typescript7.isAsExpression)(parent) || (0, import_typescript7.isTypeAssertionExpression)(parent) || (0, import_typescript7.isNonNullExpression)(parent) || (0, import_typescript7.isSatisfiesExpression)(parent)) && parent.expression === child;
1144
+ var isPatternContainer = (parent, child) => (0, import_typescript7.isArrayLiteralExpression)(parent) && parent.elements.some((element) => element === child) || (0, import_typescript7.isObjectLiteralExpression)(parent) && parent.properties.some((property) => property === child) || (0, import_typescript7.isPropertyAssignment)(parent) && parent.initializer === child || (0, import_typescript7.isSpreadAssignment)(parent) && parent.expression === child;
1145
+ var isWriteOnly = (node) => {
1146
+ let current = node;
1147
+ let parent = node.parent;
1148
+ while (parent && (isExpressionWrapper(parent, current) || isPatternContainer(parent, current))) {
1149
+ current = parent;
1150
+ parent = parent.parent;
1151
+ }
1152
+ const isWriteOnlyBinaryAssignment = (0, import_typescript7.isBinaryExpression)(parent) && parent.left === current && parent.operatorToken.kind === import_typescript7.SyntaxKind.EqualsToken;
1153
+ const isWriteOnlyDelete = (0, import_typescript7.isDeleteExpression)(parent) && parent.expression === current;
1154
+ const isWriteOnlyIteration = ((0, import_typescript7.isForInStatement)(parent) || (0, import_typescript7.isForOfStatement)(parent)) && parent.initializer === current;
1155
+ return isWriteOnlyBinaryAssignment || isWriteOnlyDelete || isWriteOnlyIteration;
1156
+ };
1157
+ var addPropertyAccessRead = (node, checker, addDeclaration, candidateNames) => {
1158
+ if (!candidateNames.has(node.name.text) || isWriteOnly(node)) {
1159
+ return;
1160
+ }
1161
+ const symbol = checker.getSymbolAtLocation(node.name);
1162
+ if (symbol) {
1163
+ addSymbolDeclarations(checker, symbol, addDeclaration);
1164
+ }
1165
+ };
1166
+ var addElementAccessRead = (node, checker, addDeclaration, candidateNames) => {
1167
+ if (isWriteOnly(node) || !node.argumentExpression) {
1168
+ return;
1169
+ }
1170
+ const names = literalPropertyNames(
1171
+ checker.getTypeAtLocation(node.argumentExpression)
1172
+ );
1173
+ if (names && !names.some((name) => candidateNames.has(name))) {
1174
+ return;
1175
+ }
1176
+ addNamedProperties(
1177
+ checker,
1178
+ checker.getTypeAtLocation(node.expression),
1179
+ names,
1180
+ addDeclaration
1181
+ );
1182
+ };
1183
+ var isAngularInterfaceMethod = (symbol) => (symbol.declarations ?? []).some(
1184
+ (declaration) => (0, import_typescript7.isMethodSignature)(declaration) && declaration.getSourceFile().fileName.replaceAll("\\", "/").includes("/node_modules/@angular/")
1185
+ );
1186
+ var collectAngularInterfaceMethods = (node, checker, addDeclaration) => {
1187
+ const implementsClauses = (node.heritageClauses ?? []).filter(
1188
+ (clause) => clause.token === import_typescript7.SyntaxKind.ImplementsKeyword
1189
+ );
1190
+ if (implementsClauses.length === 0) {
1191
+ return;
1192
+ }
1193
+ const classType = checker.getTypeAtLocation(node);
1194
+ for (const clause of implementsClauses) {
1195
+ for (const heritageType of clause.types) {
1196
+ const type = checker.getTypeAtLocation(heritageType);
1197
+ for (const interfaceMethod of type.getProperties()) {
1198
+ if (!isAngularInterfaceMethod(interfaceMethod)) {
1199
+ continue;
1200
+ }
1201
+ const implementation = classType.getProperty(interfaceMethod.name);
1202
+ if (implementation) {
1203
+ addSymbolDeclarations(checker, implementation, addDeclaration);
1204
+ }
1205
+ }
1206
+ }
1207
+ }
1208
+ };
1209
+ var collectCandidateNames = (sourceFiles) => {
1210
+ const names = /* @__PURE__ */ new Set();
1211
+ const visit = (node) => {
1212
+ if ((0, import_typescript7.isClassLike)(node) && (0, import_typescript7.canHaveDecorators)(node) && ((0, import_typescript7.getDecorators)(node)?.length ?? 0) > 0) {
1213
+ for (const member of node.members) {
1214
+ if (member.name && ((0, import_typescript7.isIdentifier)(member.name) || (0, import_typescript7.isStringLiteralLike)(member.name))) {
1215
+ names.add(member.name.text);
1216
+ }
1217
+ }
1218
+ }
1219
+ (0, import_typescript7.forEachChild)(node, visit);
1220
+ };
1221
+ for (const sourceFile of sourceFiles) {
1222
+ visit(sourceFile);
1223
+ }
1224
+ return names;
1225
+ };
1226
+ var collectTypeScriptReads = (program, addDeclaration) => {
1227
+ const checker = program.getTypeChecker();
1228
+ const sourceFiles = program.getSourceFiles().filter(
1229
+ (sourceFile) => !sourceFile.isDeclarationFile && !program.isSourceFileFromExternalLibrary(sourceFile) && !isSpecFile(sourceFile.fileName) && /\.(?:[cm]?ts|tsx)$/u.test(sourceFile.fileName)
1230
+ );
1231
+ const candidateNames = collectCandidateNames(sourceFiles);
1232
+ const visit = (node) => {
1233
+ if ((0, import_typescript7.isPropertyAccessExpression)(node)) {
1234
+ addPropertyAccessRead(node, checker, addDeclaration, candidateNames);
1235
+ } else if ((0, import_typescript7.isElementAccessExpression)(node)) {
1236
+ addElementAccessRead(node, checker, addDeclaration, candidateNames);
1237
+ } else if ((0, import_typescript7.isClassLike)(node)) {
1238
+ collectAngularInterfaceMethods(node, checker, addDeclaration);
1239
+ }
1240
+ collectDestructuringReads(node, checker, addDeclaration, candidateNames);
1241
+ (0, import_typescript7.forEachChild)(node, visit);
1242
+ };
1243
+ for (const sourceFile of sourceFiles) {
1244
+ visit(sourceFile);
1245
+ }
1246
+ };
1247
+
1248
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
1249
+ var projectUsageCache = /* @__PURE__ */ new WeakMap();
1250
+ var templateFileVersion = (fileName) => {
1251
+ const { mtimeNs, size } = (0, import_node_fs3.statSync)(fileName, { bigint: true });
1252
+ return { fileName: (0, import_node_path2.resolve)(fileName), mtimeNs, size };
1253
+ };
1254
+ var templateFileVersionsAreCurrent = (versions) => {
1255
+ try {
1256
+ return versions.every(({ fileName, mtimeNs, size }) => {
1257
+ const current = (0, import_node_fs3.statSync)(fileName, { bigint: true });
1258
+ return current.mtimeNs === mtimeNs && current.size === size;
1259
+ });
1260
+ } catch {
1261
+ return false;
1262
+ }
1263
+ };
1264
+ var cacheEntryIsCurrent = (entry) => {
1265
+ const now = performance.now();
1266
+ if (now - entry.checkedAt < entry.checkDuration * 20) {
1267
+ return true;
1268
+ }
1269
+ const current = templateFileVersionsAreCurrent(entry.templateFileVersions);
1270
+ entry.checkedAt = now;
1271
+ entry.checkDuration = performance.now() - now;
1272
+ return current;
1273
+ };
1274
+ var buildProjectUsage = (program) => {
1275
+ try {
1276
+ const configFilePath = program.getCompilerOptions()["configFilePath"];
1277
+ if (typeof configFilePath !== "string") {
1278
+ return null;
1279
+ }
1280
+ const declarations = /* @__PURE__ */ new Set();
1281
+ const addDeclaration = (declaration) => {
1282
+ declarations.add(declaration);
1283
+ };
1284
+ collectTypeScriptReads(program, addDeclaration);
1285
+ const templateFiles = collectAngularTemplateReads(
1286
+ program,
1287
+ configFilePath,
1288
+ addDeclaration
1289
+ );
1290
+ if (!templateFiles) {
1291
+ return null;
1292
+ }
1293
+ return {
1294
+ checkDuration: 0,
1295
+ checkedAt: performance.now(),
1296
+ templateFileVersions: templateFiles.map(templateFileVersion),
1297
+ usage: {
1298
+ has: (node) => declarations.has(node)
1299
+ }
1300
+ };
1301
+ } catch {
1302
+ return null;
1303
+ }
1304
+ };
1305
+ var projectUsage = (program) => {
1306
+ const cached = projectUsageCache.get(program);
1307
+ if (cached && cacheEntryIsCurrent(cached)) {
1308
+ return cached.usage;
1309
+ }
1310
+ const built = buildProjectUsage(program);
1311
+ if (!built) {
1312
+ projectUsageCache.delete(program);
1313
+ return null;
1314
+ }
1315
+ projectUsageCache.set(program, built);
1316
+ return built.usage;
1317
+ };
1318
+
1319
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.ts
455
1320
  var import_utils3 = require("@typescript-eslint/utils");
456
1321
  var isTypeScriptExpressionWrapper = (node) => {
457
1322
  switch (node.type) {
@@ -518,7 +1383,7 @@ var isDestructuringOrLoopTarget = (node) => {
518
1383
  }
519
1384
  return patternTarget && isSimpleAssignmentTarget(parent, current) || isLoopTarget(parent, current);
520
1385
  };
521
- var isWriteOnly = (node) => isDirectWrite(node) || isDestructuringOrLoopTarget(node);
1386
+ var isWriteOnly2 = (node) => isDirectWrite(node) || isDestructuringOrLoopTarget(node);
522
1387
  var destructuringParts = (node) => {
523
1388
  if (node.type === import_utils3.TSESTree.AST_NODE_TYPES.VariableDeclarator) {
524
1389
  return { pattern: node.id, source: node.init };
@@ -528,7 +1393,7 @@ var destructuringParts = (node) => {
528
1393
  }
529
1394
  return null;
530
1395
  };
531
- var propertyName = (property) => {
1396
+ var propertyName2 = (property) => {
532
1397
  if (property.type !== import_utils3.TSESTree.AST_NODE_TYPES.Property || property.computed) {
533
1398
  return null;
534
1399
  }
@@ -544,7 +1409,7 @@ var destructuredThisReads = (node) => {
544
1409
  }
545
1410
  const reads = [];
546
1411
  for (const property of parts.pattern.properties) {
547
- const name = propertyName(property);
1412
+ const name = propertyName2(property);
548
1413
  if (name === null) {
549
1414
  return null;
550
1415
  }
@@ -553,7 +1418,11 @@ var destructuredThisReads = (node) => {
553
1418
  return reads;
554
1419
  };
555
1420
 
556
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.ts
1421
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.ts
1422
+ var defaultOptions = {
1423
+ allowEffectFields: false,
1424
+ analysis: "local"
1425
+ };
557
1426
  var createRule = import_utils4.ESLintUtils.RuleCreator(
558
1427
  () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
559
1428
  );
@@ -576,7 +1445,7 @@ var trackMemberRead = (node, stack, thisStack, dynamicClasses) => {
576
1445
  if (node.computed && receiverIsThis) {
577
1446
  dynamicClasses.add(current);
578
1447
  }
579
- if (receiverIsThis && !node.computed && node.property.type === import_utils4.TSESTree.AST_NODE_TYPES.Identifier && !isWriteOnly(node)) {
1448
+ if (receiverIsThis && !node.computed && node.property.type === import_utils4.TSESTree.AST_NODE_TYPES.Identifier && !isWriteOnly2(node)) {
580
1449
  current.reads.add(node.property.name);
581
1450
  }
582
1451
  };
@@ -633,7 +1502,7 @@ var lexicalThisVisitor = (thisStack) => ({
633
1502
  }
634
1503
  }
635
1504
  });
636
- var visitor = (context, imports, classes, stack, dynamicClasses) => {
1505
+ var visitor = (context, imports, classes, stack, dynamicClasses, allowEffectFields, projectMemberUsed) => {
637
1506
  const thisStack = [];
638
1507
  return {
639
1508
  ClassDeclaration(node) {
@@ -661,10 +1530,26 @@ var visitor = (context, imports, classes, stack, dynamicClasses) => {
661
1530
  trackDestructuringRead(node, stack, thisStack, dynamicClasses);
662
1531
  },
663
1532
  "Program:exit"() {
664
- reportUnusedMembers(context, imports, classes, dynamicClasses);
1533
+ reportUnusedMembers(
1534
+ context,
1535
+ imports,
1536
+ classes,
1537
+ dynamicClasses,
1538
+ allowEffectFields,
1539
+ projectMemberUsed
1540
+ );
665
1541
  }
666
1542
  };
667
1543
  };
1544
+ var projectParserServices = (context) => {
1545
+ try {
1546
+ return import_utils4.ESLintUtils.getParserServices(context);
1547
+ } catch {
1548
+ throw new Error(
1549
+ "Project analysis requires parser services with type information."
1550
+ );
1551
+ }
1552
+ };
668
1553
  var no_unused_angular_instance_fields_default = createRule({
669
1554
  name: "no-unused-instance-fields",
670
1555
  meta: {
@@ -676,23 +1561,70 @@ var no_unused_angular_instance_fields_default = createRule({
676
1561
  unusedField: "Angular instance field '{{name}}' is never read.",
677
1562
  unusedMethod: "Angular instance method '{{name}}' is never read."
678
1563
  },
679
- schema: []
1564
+ schema: [
1565
+ {
1566
+ type: "object",
1567
+ properties: {
1568
+ allowEffectFields: {
1569
+ type: "boolean",
1570
+ description: "Allow Angular effect() fields with automatic cleanup."
1571
+ },
1572
+ analysis: {
1573
+ type: "string",
1574
+ enum: ["local", "project"],
1575
+ description: "Choose local-file or typed whole-project analysis."
1576
+ }
1577
+ },
1578
+ additionalProperties: false
1579
+ }
1580
+ ]
680
1581
  },
681
- defaultOptions: [],
682
- create(context) {
1582
+ defaultOptions: [defaultOptions],
1583
+ create(context, [options]) {
1584
+ const analysis = options.analysis ?? "local";
1585
+ if (analysis === "project" && isSpecFile(context.filename)) {
1586
+ return {};
1587
+ }
1588
+ const parserServices = analysis === "project" ? projectParserServices(context) : void 0;
683
1589
  const imports = /* @__PURE__ */ new Map();
684
1590
  for (const node of context.sourceCode.ast.body) {
685
1591
  if (node.type === import_utils4.TSESTree.AST_NODE_TYPES.ImportDeclaration) {
686
1592
  addAngularImport(node, imports);
687
1593
  }
688
1594
  }
689
- if (imports.size === 0) {
1595
+ let hasAngularClassImport = false;
1596
+ for (const imported of imports.values()) {
1597
+ if (imported === null || imported === "Component" || imported === "Directive") {
1598
+ hasAngularClassImport = true;
1599
+ break;
1600
+ }
1601
+ }
1602
+ if (!hasAngularClassImport) {
690
1603
  return {};
691
1604
  }
1605
+ let projectMemberUsed;
1606
+ if (parserServices) {
1607
+ let usage;
1608
+ projectMemberUsed = (node) => {
1609
+ if (usage === void 0) {
1610
+ usage = projectUsage(parserServices.program);
1611
+ }
1612
+ return usage?.has(parserServices.esTreeNodeToTSNodeMap.get(node)) ?? true;
1613
+ };
1614
+ }
692
1615
  const classes = [];
693
1616
  const stack = [];
694
1617
  const dynamicClasses = /* @__PURE__ */ new Set();
695
- return visitor(context, imports, classes, stack, dynamicClasses);
1618
+ const allowEffectFields = options.allowEffectFields ?? false;
1619
+ return visitor(
1620
+ context,
1621
+ imports,
1622
+ classes,
1623
+ stack,
1624
+ dynamicClasses,
1625
+ allowEffectFields,
1626
+ projectMemberUsed
1627
+ );
696
1628
  }
697
1629
  });
698
1630
 
@@ -714,11 +1646,17 @@ var angular = (0, import_config2.defineConfig)([
714
1646
  ...import_globals.default.browser,
715
1647
  ...import_globals.default.es2025,
716
1648
  ...import_globals.default.node
1649
+ },
1650
+ parserOptions: {
1651
+ projectService: true
717
1652
  }
718
1653
  },
719
1654
  processor: import_angular_eslint2.processInlineTemplates,
720
1655
  rules: {
721
- "lint-suite-angular/no-unused-instance-fields": "error",
1656
+ "lint-suite-angular/no-unused-instance-fields": [
1657
+ "error",
1658
+ { analysis: "project" }
1659
+ ],
722
1660
  "@angular-eslint/prefer-on-push-component-change-detection": "error",
723
1661
  // v22: flags only explicit opt-out of OnPush (omission now means OnPush)
724
1662
  "@angular-eslint/prefer-signals": "error",
@@ -1102,44 +2040,38 @@ var import_eslint_import_resolver_typescript = require("eslint-import-resolver-t
1102
2040
  var import_eslint_plugin_import_x2 = __toESM(require("eslint-plugin-import-x"), 1);
1103
2041
  var import_config9 = require("eslint/config");
1104
2042
 
1105
- // packages/lint-suite/src/lib/rules/explicit-accessibility.ts
2043
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
1106
2044
  var import_utils5 = require("@typescript-eslint/utils");
2045
+ var accessibilities = [
2046
+ "public",
2047
+ "private",
2048
+ "protected"
2049
+ ];
1107
2050
  var createRule2 = import_utils5.ESLintUtils.RuleCreator(
1108
- () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
2051
+ () => "https://github.com/F0rty-Tw0/lint-suite#explicit-accessibility"
1109
2052
  );
1110
- var defaultOptions = {
1111
- defaultAccessibility: "private"
1112
- };
1113
- var getNodeName = (node) => {
1114
- if (!node) {
1115
- return "<unknown>";
1116
- }
1117
- if ("key" in node && node.key) {
1118
- if (node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
1119
- return node.key.name;
1120
- }
1121
- if (node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.PrivateIdentifier) {
1122
- return `#${node.key.name}`;
1123
- }
1124
- return "<computed/complex key>";
2053
+ var isPrivateMember = (node) => node.type !== import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty && node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.PrivateIdentifier;
2054
+ var isConstructor = (node) => node.type === import_utils5.TSESTree.AST_NODE_TYPES.MethodDefinition && node.kind === "constructor";
2055
+ var reportTarget = (node) => {
2056
+ if (node.type !== import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty) {
2057
+ return node.key;
1125
2058
  }
1126
- if ("kind" in node && node.kind === "constructor") {
1127
- return "constructor";
2059
+ const { parameter } = node;
2060
+ if (parameter.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern && parameter.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
2061
+ return parameter.left;
1128
2062
  }
1129
- if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty) {
1130
- const param = node.parameter;
1131
- if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
1132
- return param.name;
1133
- }
1134
- if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern) {
1135
- if (param.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
1136
- return param.left.name;
1137
- }
1138
- return "<destructured parameter>";
1139
- }
1140
- return "<complex parameter>";
1141
- }
1142
- return "<unknown>";
2063
+ return parameter;
2064
+ };
2065
+ var memberName = (target, sourceCode) => target.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier ? target.name : sourceCode.getText(target);
2066
+ var insertModifier = (node, accessibility, sourceCode) => {
2067
+ const lastDecorator = node.decorators.at(-1);
2068
+ const token = lastDecorator ? sourceCode.getTokenAfter(lastDecorator) : sourceCode.getFirstToken(node);
2069
+ if (!token) {
2070
+ return () => null;
2071
+ }
2072
+ const adjacentToDecorator = lastDecorator?.range[1] === token.range[0];
2073
+ const text2 = `${adjacentToDecorator ? " " : ""}${accessibility} `;
2074
+ return (fixer) => fixer.insertTextBefore(token, text2);
1143
2075
  };
1144
2076
  var explicit_accessibility_default = createRule2({
1145
2077
  name: "explicit-accessibility",
@@ -1149,87 +2081,157 @@ var explicit_accessibility_default = createRule2({
1149
2081
  description: "Require explicit accessibility modifiers on class members"
1150
2082
  },
1151
2083
  fixable: "code",
2084
+ hasSuggestions: true,
1152
2085
  schema: [
1153
2086
  {
1154
2087
  type: "object",
1155
2088
  properties: {
1156
2089
  defaultAccessibility: {
1157
2090
  type: "string",
1158
- enum: ["public", "private", "protected"],
1159
- description: "The accessibility modifier to add if one is missing."
2091
+ enum: ["public", "private", "protected", "none"],
2092
+ description: "The accessibility modifier inserted by the auto-fix; constructors always get public. 'none' reports without an auto-fix and offers all three levels as suggestions."
1160
2093
  }
1161
2094
  },
1162
2095
  additionalProperties: false
1163
2096
  }
1164
2097
  ],
1165
2098
  messages: {
1166
- missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected)."
2099
+ missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected).",
2100
+ setAccessibility: "Add the '{{ accessibility }}' modifier."
1167
2101
  }
1168
2102
  },
1169
- defaultOptions: [defaultOptions],
1170
- create(context, [options]) {
1171
- const { defaultAccessibility } = { ...defaultOptions, ...options };
1172
- const sourceCode = context.sourceCode;
1173
- function checkNode(node) {
1174
- if (node.accessibility) {
2103
+ defaultOptions: [{ defaultAccessibility: "public" }],
2104
+ create(context, [{ defaultAccessibility = "public" }]) {
2105
+ const { sourceCode } = context;
2106
+ const check = (node) => {
2107
+ if (node.accessibility || isPrivateMember(node)) {
1175
2108
  return;
1176
2109
  }
1177
- let reportNode = node;
1178
- const name = getNodeName(node);
1179
- if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.MethodDefinition || node.type === import_utils5.TSESTree.AST_NODE_TYPES.PropertyDefinition) {
1180
- reportNode = node.key;
1181
- } else {
1182
- const param = node.parameter;
1183
- if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
1184
- reportNode = param;
1185
- } else if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern && param.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
1186
- reportNode = param.left;
1187
- }
1188
- }
2110
+ const target = reportTarget(node);
2111
+ const fixAccessibility = isConstructor(node) && defaultAccessibility !== "none" ? "public" : defaultAccessibility;
1189
2112
  context.report({
1190
- node: reportNode ?? node,
2113
+ node: target,
1191
2114
  messageId: "missingAccessibility",
1192
- data: { name },
1193
- fix(fixer) {
1194
- let insertText = `${defaultAccessibility} `;
1195
- let tokenToInsertBefore;
1196
- if (node.decorators.length > 0) {
1197
- const lastDecorator = node.decorators[node.decorators.length - 1];
1198
- tokenToInsertBefore = sourceCode.getTokenAfter(lastDecorator);
1199
- } else {
1200
- tokenToInsertBefore = sourceCode.getFirstToken(node);
1201
- }
1202
- if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty && node.readonly) {
1203
- const firstToken = sourceCode.getFirstToken(node);
1204
- if (firstToken?.value === "readonly") {
1205
- tokenToInsertBefore = firstToken;
1206
- }
1207
- }
1208
- if (!tokenToInsertBefore) return null;
1209
- const tokenAfterInsertion = sourceCode.getTokenAfter(
1210
- tokenToInsertBefore,
1211
- { includeComments: false }
1212
- );
1213
- const textBetween = sourceCode.text.slice(
1214
- tokenToInsertBefore.range[1],
1215
- tokenAfterInsertion?.range[0]
1216
- );
1217
- if (!/^\s/.test(textBetween)) {
1218
- insertText = ` ${insertText}`;
1219
- }
1220
- return fixer.insertTextBefore(tokenToInsertBefore, insertText);
1221
- }
2115
+ data: { name: memberName(target, sourceCode) },
2116
+ ...fixAccessibility === "none" ? {} : { fix: insertModifier(node, fixAccessibility, sourceCode) },
2117
+ suggest: accessibilities.filter((accessibility) => accessibility !== fixAccessibility).map((accessibility) => ({
2118
+ messageId: "setAccessibility",
2119
+ data: { accessibility },
2120
+ fix: insertModifier(node, accessibility, sourceCode)
2121
+ }))
1222
2122
  });
2123
+ };
2124
+ return {
2125
+ AccessorProperty: check,
2126
+ MethodDefinition: check,
2127
+ PropertyDefinition: check,
2128
+ TSAbstractAccessorProperty: check,
2129
+ TSAbstractMethodDefinition: check,
2130
+ TSAbstractPropertyDefinition: check,
2131
+ TSParameterProperty: check
2132
+ };
2133
+ }
2134
+ });
2135
+
2136
+ // packages/lint-suite/src/lib/rules/no-inline-object-types/no-inline-object-types.ts
2137
+ var import_utils6 = require("@typescript-eslint/utils");
2138
+ var createRule3 = import_utils6.ESLintUtils.RuleCreator(
2139
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-inline-object-types"
2140
+ );
2141
+ var enclosingTypeAlias = (node) => {
2142
+ let current = node.parent;
2143
+ while (current && current.type !== import_utils6.TSESTree.AST_NODE_TYPES.Program) {
2144
+ if (current.type === import_utils6.TSESTree.AST_NODE_TYPES.TSTypeAliasDeclaration) {
2145
+ return current;
2146
+ }
2147
+ current = current.parent;
2148
+ }
2149
+ return void 0;
2150
+ };
2151
+ var no_inline_object_types_default = createRule3({
2152
+ name: "no-inline-object-types",
2153
+ meta: {
2154
+ type: "suggestion",
2155
+ docs: {
2156
+ description: "Require nested object types inside a type alias to be extracted to a named type alias"
2157
+ },
2158
+ schema: [],
2159
+ messages: {
2160
+ inlineObjectType: "Inline object type must be extracted to a named type alias."
1223
2161
  }
2162
+ },
2163
+ defaultOptions: [],
2164
+ create(context) {
1224
2165
  return {
1225
- MethodDefinition(node) {
1226
- checkNode(node);
1227
- },
1228
- PropertyDefinition(node) {
1229
- checkNode(node);
1230
- },
1231
- TSParameterProperty(node) {
1232
- checkNode(node);
2166
+ TSTypeLiteral(node) {
2167
+ if (node.parent.type !== import_utils6.TSESTree.AST_NODE_TYPES.TSTypeAliasDeclaration && enclosingTypeAlias(node)) {
2168
+ context.report({ node, messageId: "inlineObjectType" });
2169
+ }
2170
+ }
2171
+ };
2172
+ }
2173
+ });
2174
+
2175
+ // packages/lint-suite/src/lib/rules/readonly-type-properties/readonly-type-properties.ts
2176
+ var import_utils7 = require("@typescript-eslint/utils");
2177
+ var createRule4 = import_utils7.ESLintUtils.RuleCreator(
2178
+ () => "https://github.com/F0rty-Tw0/lint-suite#readonly-type-properties"
2179
+ );
2180
+ var propertyName3 = (node, sourceCode) => node.key.type === import_utils7.TSESTree.AST_NODE_TYPES.Identifier ? node.key.name : sourceCode.getText(node.key);
2181
+ var PRIMITIVE_KEYWORDS = /* @__PURE__ */ new Set([
2182
+ import_utils7.TSESTree.AST_NODE_TYPES.TSStringKeyword,
2183
+ import_utils7.TSESTree.AST_NODE_TYPES.TSNumberKeyword,
2184
+ import_utils7.TSESTree.AST_NODE_TYPES.TSBooleanKeyword,
2185
+ import_utils7.TSESTree.AST_NODE_TYPES.TSBigIntKeyword,
2186
+ import_utils7.TSESTree.AST_NODE_TYPES.TSSymbolKeyword,
2187
+ import_utils7.TSESTree.AST_NODE_TYPES.TSNullKeyword,
2188
+ import_utils7.TSESTree.AST_NODE_TYPES.TSUndefinedKeyword
2189
+ ]);
2190
+ var isPrimitive = (type) => {
2191
+ if (PRIMITIVE_KEYWORDS.has(type.type)) {
2192
+ return true;
2193
+ }
2194
+ if (type.type === import_utils7.TSESTree.AST_NODE_TYPES.TSLiteralType || type.type === import_utils7.TSESTree.AST_NODE_TYPES.TSTemplateLiteralType) {
2195
+ return true;
2196
+ }
2197
+ if (type.type === import_utils7.TSESTree.AST_NODE_TYPES.TSUnionType || type.type === import_utils7.TSESTree.AST_NODE_TYPES.TSIntersectionType) {
2198
+ return type.types.every(isPrimitive);
2199
+ }
2200
+ return false;
2201
+ };
2202
+ var readonly_type_properties_default = createRule4({
2203
+ name: "readonly-type-properties",
2204
+ meta: {
2205
+ type: "suggestion",
2206
+ docs: {
2207
+ description: "Require readonly on primitive-typed type and interface properties"
2208
+ },
2209
+ fixable: "code",
2210
+ schema: [],
2211
+ messages: {
2212
+ missingReadonly: "Type property '{{ name }}' should be readonly. Disable this rule on the line if mutation is required."
2213
+ }
2214
+ },
2215
+ defaultOptions: [],
2216
+ create(context) {
2217
+ const { sourceCode } = context;
2218
+ return {
2219
+ TSPropertySignature(node) {
2220
+ if (node.readonly || !node.typeAnnotation) {
2221
+ return;
2222
+ }
2223
+ if (!isPrimitive(node.typeAnnotation.typeAnnotation)) {
2224
+ return;
2225
+ }
2226
+ context.report({
2227
+ node: node.key,
2228
+ messageId: "missingReadonly",
2229
+ data: { name: propertyName3(node, sourceCode) },
2230
+ fix(fixer) {
2231
+ const firstToken = sourceCode.getFirstToken(node);
2232
+ return firstToken ? fixer.insertTextBefore(firstToken, "readonly ") : null;
2233
+ }
2234
+ });
1233
2235
  }
1234
2236
  };
1235
2237
  }
@@ -1238,7 +2240,9 @@ var explicit_accessibility_default = createRule2({
1238
2240
  // packages/lint-suite/src/lib/typescript.ts
1239
2241
  var localTypescriptRules = {
1240
2242
  rules: {
1241
- "explicit-accessibility": explicit_accessibility_default
2243
+ "explicit-accessibility": explicit_accessibility_default,
2244
+ "readonly-type-properties": readonly_type_properties_default,
2245
+ "no-inline-object-types": no_inline_object_types_default
1242
2246
  }
1243
2247
  };
1244
2248
  var typescript = (0, import_config9.defineConfig)([
@@ -1255,7 +2259,8 @@ var typescript = (0, import_config9.defineConfig)([
1255
2259
  plugins: { local: localTypescriptRules },
1256
2260
  rules: {
1257
2261
  "local/explicit-accessibility": "error",
1258
- "@typescript-eslint/explicit-member-accessibility": "error",
2262
+ "local/readonly-type-properties": "error",
2263
+ "local/no-inline-object-types": "error",
1259
2264
  "@typescript-eslint/explicit-function-return-type": "error",
1260
2265
  "@typescript-eslint/no-empty-function": "error",
1261
2266
  "@typescript-eslint/no-unused-vars": "error",