lint-suite 1.6.0 → 1.6.2

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 (148) hide show
  1. package/README.md +161 -16
  2. package/eslint.cjs +3085 -998
  3. package/eslint.cjs.map +4 -4
  4. package/eslint.d.ts +13 -13
  5. package/eslint.d.ts.map +1 -1
  6. package/eslint.js +3173 -990
  7. package/eslint.js.map +4 -4
  8. package/lib/angular-template.d.ts.map +1 -1
  9. package/lib/angular.d.ts +1 -0
  10. package/lib/angular.d.ts.map +1 -1
  11. package/lib/base-limits.d.ts +2 -0
  12. package/lib/base-limits.d.ts.map +1 -0
  13. package/lib/base-practices.d.ts +2 -0
  14. package/lib/base-practices.d.ts.map +1 -0
  15. package/lib/base-recommended.d.ts +2 -0
  16. package/lib/base-recommended.d.ts.map +1 -0
  17. package/lib/base.d.ts.map +1 -1
  18. package/lib/javascript.d.ts.map +1 -1
  19. package/lib/rules/define-plugin.util.d.ts +6 -0
  20. package/lib/rules/define-plugin.util.d.ts.map +1 -0
  21. package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts +6 -0
  22. package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts.map +1 -0
  23. package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts +14 -0
  24. package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts.map +1 -0
  25. package/lib/rules/explicit-accessibility/explicit-accessibility.d.ts +7 -0
  26. package/lib/rules/explicit-accessibility/explicit-accessibility.d.ts.map +1 -0
  27. package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts +6 -0
  28. package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts.map +1 -0
  29. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.d.ts +5 -0
  30. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.d.ts.map +1 -0
  31. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.d.ts +9 -0
  32. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.d.ts.map +1 -0
  33. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-decorator-kind.d.ts +10 -0
  34. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-decorator-kind.d.ts.map +1 -0
  35. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts +4 -0
  36. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts.map +1 -0
  37. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.d.ts +12 -0
  38. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.d.ts.map +1 -0
  39. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.d.ts +3 -0
  40. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.d.ts.map +1 -0
  41. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-chains.d.ts +17 -0
  42. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-chains.d.ts.map +1 -0
  43. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts +8 -0
  44. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts.map +1 -0
  45. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts +6 -0
  46. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts.map +1 -0
  47. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.d.ts +5 -0
  48. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.d.ts.map +1 -0
  49. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts +54 -0
  50. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts.map +1 -0
  51. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts +116 -0
  52. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts.map +1 -0
  53. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.d.ts +4 -0
  54. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.d.ts.map +1 -0
  55. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts +4 -0
  56. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts.map +1 -0
  57. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts +4 -0
  58. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts.map +1 -0
  59. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts +10 -0
  60. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts.map +1 -0
  61. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts +17 -0
  62. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts.map +1 -0
  63. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.d.ts +8 -0
  64. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.d.ts.map +1 -0
  65. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.d.ts +5 -0
  66. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.d.ts.map +1 -0
  67. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.d.ts +6 -0
  68. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.d.ts.map +1 -0
  69. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-candidate-names.d.ts +4 -0
  70. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-candidate-names.d.ts.map +1 -0
  71. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts +4 -0
  72. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts.map +1 -0
  73. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts +4 -0
  74. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts.map +1 -0
  75. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.d.ts +8 -0
  76. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.d.ts.map +1 -0
  77. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-type-dependencies.d.ts +3 -0
  78. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-type-dependencies.d.ts.map +1 -0
  79. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.d.ts +7 -0
  80. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.d.ts.map +1 -0
  81. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/read-chain-text.util.d.ts +4 -0
  82. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/read-chain-text.util.d.ts.map +1 -0
  83. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.util.d.ts +2 -0
  84. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.util.d.ts.map +1 -0
  85. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.d.ts +4 -0
  86. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.d.ts.map +1 -0
  87. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts +3 -0
  88. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts.map +1 -0
  89. package/lib/rules/no-unused-angular-instance-fields/{utils/angular-expression-reads.util.d.ts → rule/angular/angular-expression-reads.d.ts} +1 -1
  90. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.d.ts.map +1 -0
  91. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.d.ts +9 -0
  92. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.d.ts.map +1 -0
  93. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.d.ts +5 -0
  94. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.d.ts.map +1 -0
  95. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.d.ts +6 -0
  96. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.d.ts.map +1 -0
  97. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.d.ts +5 -0
  98. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.d.ts.map +1 -0
  99. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.d.ts +9 -0
  100. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.d.ts.map +1 -0
  101. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.d.ts +4 -0
  102. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.d.ts.map +1 -0
  103. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts +54 -0
  104. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts.map +1 -0
  105. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts +8 -0
  106. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts.map +1 -0
  107. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.d.ts +4 -0
  108. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.d.ts.map +1 -0
  109. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.d.ts +8 -0
  110. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.d.ts.map +1 -0
  111. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-lexical-this.d.ts +3 -0
  112. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-lexical-this.d.ts.map +1 -0
  113. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-write-targets.d.ts +3 -0
  114. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-write-targets.d.ts.map +1 -0
  115. package/lib/rules/no-unused-angular-instance-fields/utils/angular-read-target.util.d.ts +4 -0
  116. package/lib/rules/no-unused-angular-instance-fields/utils/angular-read-target.util.d.ts.map +1 -0
  117. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts +6 -0
  118. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts.map +1 -0
  119. package/lib/typescript-consistency.d.ts +2 -0
  120. package/lib/typescript-consistency.d.ts.map +1 -0
  121. package/lib/typescript-imports.d.ts +2 -0
  122. package/lib/typescript-imports.d.ts.map +1 -0
  123. package/lib/typescript-local-plugin.d.ts +2 -0
  124. package/lib/typescript-local-plugin.d.ts.map +1 -0
  125. package/lib/typescript-overrides.d.ts +2 -0
  126. package/lib/typescript-overrides.d.ts.map +1 -0
  127. package/lib/typescript-recommended.d.ts +2 -0
  128. package/lib/typescript-recommended.d.ts.map +1 -0
  129. package/lib/typescript-safety.d.ts +2 -0
  130. package/lib/typescript-safety.d.ts.map +1 -0
  131. package/lib/typescript.d.ts +1 -0
  132. package/lib/typescript.d.ts.map +1 -1
  133. package/package.json +18 -14
  134. package/lib/rules/explicit-accessibility.d.ts +0 -12
  135. package/lib/rules/explicit-accessibility.d.ts.map +0 -1
  136. package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts +0 -8
  137. package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts.map +0 -1
  138. package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts +0 -3
  139. package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts.map +0 -1
  140. package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts +0 -10
  141. package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts.map +0 -1
  142. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts +0 -7
  143. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts.map +0 -1
  144. package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts +0 -2
  145. package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts.map +0 -1
  146. package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts.map +0 -1
  147. package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts +0 -7
  148. package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts.map +0 -1
package/eslint.js CHANGED
@@ -4,10 +4,7 @@ import { defineConfig } from "eslint/config";
4
4
  var angularTemplate = defineConfig([
5
5
  {
6
6
  files: ["**/*.html"],
7
- extends: [
8
- ...configs.templateAccessibility,
9
- ...configs.templateRecommended
10
- ],
7
+ extends: [...configs.templateAccessibility, ...configs.templateRecommended],
11
8
  rules: {
12
9
  "@angular-eslint/template/prefer-template-literal": "error",
13
10
  "@angular-eslint/template/no-any": "error",
@@ -76,19 +73,299 @@ var angularTemplate = defineConfig([
76
73
  ]);
77
74
 
78
75
  // packages/lint-suite/src/lib/angular.ts
79
- import globals from "globals";
80
76
  import { configs as configs2, processInlineTemplates } from "angular-eslint";
81
77
  import { defineConfig as defineConfig2 } from "eslint/config";
78
+ import globals from "globals";
79
+
80
+ // packages/lint-suite/src/lib/rules/define-plugin.util.ts
81
+ var definePlugin = (name, rules) => {
82
+ const meta5 = { name };
83
+ const plugin = { meta: meta5, rules };
84
+ return plugin;
85
+ };
86
+
87
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.ts
88
+ import { ESLintUtils } from "@typescript-eslint/utils";
89
+
90
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.ts
91
+ import { TSESTree } from "@typescript-eslint/utils";
92
+ var calleeRoot = (node) => {
93
+ if (node.type === TSESTree.AST_NODE_TYPES.Identifier) return node;
94
+ if (node.type !== TSESTree.AST_NODE_TYPES.MemberExpression || node.object.type === TSESTree.AST_NODE_TYPES.Super) {
95
+ return void 0;
96
+ }
97
+ return calleeRoot(node.object);
98
+ };
99
+ var angularName = (node, imports) => {
100
+ if (node.type === TSESTree.AST_NODE_TYPES.Identifier) {
101
+ return imports.get(node.name);
102
+ }
103
+ if (node.type !== TSESTree.AST_NODE_TYPES.MemberExpression || node.computed || node.property.type !== TSESTree.AST_NODE_TYPES.Identifier) {
104
+ return null;
105
+ }
106
+ if (node.object.type === TSESTree.AST_NODE_TYPES.Identifier) {
107
+ const namespaceAlias = imports.get(node.object.name);
108
+ if (namespaceAlias === null) return node.property.name;
109
+ }
110
+ return angularName(node.object, imports);
111
+ };
112
+ var isImportBinding = (node, sourceCode) => {
113
+ const root = calleeRoot(node);
114
+ if (!root) return false;
115
+ for (let scope = sourceCode.getScope(root); scope; scope = scope.upper) {
116
+ const variable = scope.set.get(root.name);
117
+ if (variable) return variable.defs[0]?.type === "ImportBinding";
118
+ }
119
+ return false;
120
+ };
121
+ var addAngularImport = (node, imports) => {
122
+ if (node.source.value !== "@angular/core" || node.importKind === "type") {
123
+ return;
124
+ }
125
+ for (const specifier of node.specifiers) {
126
+ if (specifier.type === TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier) {
127
+ imports.set(specifier.local.name, null);
128
+ } else if (specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type") {
129
+ const { imported } = specifier;
130
+ const isIdentifier8 = imported.type === TSESTree.AST_NODE_TYPES.Identifier;
131
+ const importedName = isIdentifier8 ? imported.name : imported.value;
132
+ imports.set(specifier.local.name, importedName);
133
+ }
134
+ }
135
+ };
136
+ var angularClassMetadata = (node, imports) => {
137
+ for (const decorator of node.decorators) {
138
+ if (decorator.expression.type !== TSESTree.AST_NODE_TYPES.CallExpression) {
139
+ continue;
140
+ }
141
+ const kind = angularName(decorator.expression.callee, imports);
142
+ const metadata = decorator.expression.arguments.at(0);
143
+ const isComponentOrDirective = kind === "Component" || kind === "Directive";
144
+ if (isComponentOrDirective && metadata?.type === TSESTree.AST_NODE_TYPES.ObjectExpression) {
145
+ const classMetadata = {
146
+ component: kind === "Component",
147
+ metadata
148
+ };
149
+ return classMetadata;
150
+ }
151
+ }
152
+ return null;
153
+ };
154
+ var angularClassImports = (ast) => {
155
+ const imports = /* @__PURE__ */ new Map();
156
+ for (const node of ast.body) {
157
+ if (node.type === TSESTree.AST_NODE_TYPES.ImportDeclaration) {
158
+ addAngularImport(node, imports);
159
+ }
160
+ }
161
+ for (const imported of imports.values()) {
162
+ const isNamespaceImport = imported === null;
163
+ const isComponent = imported === "Component";
164
+ const isDirective = imported === "Directive";
165
+ const isAngularClassImport = isNamespaceImport || isComponent || isDirective;
166
+ if (isAngularClassImport) return imports;
167
+ }
168
+ return null;
169
+ };
82
170
 
83
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.ts
84
- import { ESLintUtils, TSESTree as TSESTree4 } from "@typescript-eslint/utils";
171
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.ts
172
+ import { TSESTree as TSESTree3 } from "@typescript-eslint/utils";
85
173
 
86
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
174
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.ts
87
175
  import { TSESTree as TSESTree2 } from "@typescript-eslint/utils";
176
+ var managedApis = /* @__PURE__ */ new Set([
177
+ "input",
178
+ "model",
179
+ "output",
180
+ "viewChild",
181
+ "viewChildren",
182
+ "contentChild",
183
+ "contentChildren"
184
+ ]);
185
+ var hasAutomaticEffectCleanup = (node) => {
186
+ const options = node.arguments.at(1);
187
+ if (options === void 0) return true;
188
+ if (options.type !== TSESTree2.AST_NODE_TYPES.ObjectExpression) return false;
189
+ return options.properties.every((property) => {
190
+ if (property.type !== TSESTree2.AST_NODE_TYPES.Property || property.computed) {
191
+ return false;
192
+ }
193
+ const manualCleanup = property.key.type === TSESTree2.AST_NODE_TYPES.Identifier && property.key.name === "manualCleanup" || property.key.type === TSESTree2.AST_NODE_TYPES.Literal && property.key.value === "manualCleanup";
194
+ return !manualCleanup || property.value.type === TSESTree2.AST_NODE_TYPES.Literal && property.value.value === false;
195
+ });
196
+ };
197
+ var angularCoreImportedName = (definition) => {
198
+ if (definition?.type !== "ImportBinding") return null;
199
+ if (definition.node.type !== TSESTree2.AST_NODE_TYPES.ImportSpecifier) {
200
+ return null;
201
+ }
202
+ if (definition.parent.type !== TSESTree2.AST_NODE_TYPES.ImportDeclaration) {
203
+ return null;
204
+ }
205
+ if (definition.parent.source.value !== "@angular/core") return null;
206
+ const { imported } = definition.node;
207
+ if (imported.type === TSESTree2.AST_NODE_TYPES.Identifier) {
208
+ return imported.name;
209
+ }
210
+ return imported.value;
211
+ };
212
+ var isAngularComponentRefField = (node, sourceCode) => {
213
+ const type = node.typeAnnotation?.typeAnnotation;
214
+ if (type?.type !== TSESTree2.AST_NODE_TYPES.TSTypeReference || type.typeName.type !== TSESTree2.AST_NODE_TYPES.Identifier) {
215
+ return false;
216
+ }
217
+ const reference = sourceCode.getScope(type.typeName).references.find(({ identifier }) => identifier === type.typeName);
218
+ const definition = reference?.resolved?.defs[0];
219
+ return angularCoreImportedName(definition) === "ComponentRef";
220
+ };
221
+ var isManagedField = (node, imports, allowEffectFields, sourceCode) => {
222
+ if (node.value?.type !== TSESTree2.AST_NODE_TYPES.CallExpression) return false;
223
+ const name = angularName(node.value.callee, imports);
224
+ const isString = typeof name === "string";
225
+ if (!isString) return false;
226
+ const isManagedApi = managedApis.has(name);
227
+ if (isManagedApi) return true;
228
+ if (!allowEffectFields) return false;
229
+ if (name !== "effect") return false;
230
+ const isImported = isImportBinding(node.value.callee, sourceCode);
231
+ if (!isImported) return false;
232
+ return hasAutomaticEffectCleanup(node.value);
233
+ };
88
234
 
89
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.ts
90
- import { readFileSync, statSync } from "node:fs";
235
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.ts
236
+ var lifecycleHooks = /* @__PURE__ */ new Set([
237
+ "ngOnChanges",
238
+ "ngOnInit",
239
+ "ngDoCheck",
240
+ "ngAfterContentInit",
241
+ "ngAfterContentChecked",
242
+ "ngAfterViewInit",
243
+ "ngAfterViewChecked",
244
+ "ngOnDestroy"
245
+ ]);
246
+ var formsInterfaceMethods = {
247
+ AsyncValidator: ["validate", "registerOnValidatorChange"],
248
+ ControlValueAccessor: [
249
+ "writeValue",
250
+ "registerOnChange",
251
+ "registerOnTouched",
252
+ "setDisabledState"
253
+ ],
254
+ Validator: ["validate", "registerOnValidatorChange"]
255
+ };
256
+ var isInstanceField = (node) => {
257
+ if (node.type !== TSESTree3.AST_NODE_TYPES.PropertyDefinition) return false;
258
+ return node.key.type === TSESTree3.AST_NODE_TYPES.Identifier;
259
+ };
260
+ var isInstanceMethod = (node) => {
261
+ if (node.type !== TSESTree3.AST_NODE_TYPES.MethodDefinition) return false;
262
+ return node.key.type === TSESTree3.AST_NODE_TYPES.Identifier;
263
+ };
264
+ var isExcludedField = (node, localPrivateOnly, sourceCode) => {
265
+ const isModified = node.static || node.declare || node.override;
266
+ const isDecorated = node.decorators.length > 0;
267
+ if (isModified || isDecorated) return true;
268
+ const isComponentRef = isAngularComponentRefField(node, sourceCode);
269
+ if (isComponentRef) return true;
270
+ const isNonPrivate = node.accessibility !== "private";
271
+ return localPrivateOnly && isNonPrivate;
272
+ };
273
+ var isExcludedMethod = (node, localPrivateOnly, implementedMethods) => {
274
+ const isModified = node.static || node.override || node.computed;
275
+ const isDecorated = node.decorators.length > 0;
276
+ const isPlainMethod = node.kind === "method";
277
+ if (isModified || isDecorated || !isPlainMethod) return true;
278
+ const hasNoBody = node.value.body === null;
279
+ const isLifecycleHook = lifecycleHooks.has(node.key.name);
280
+ if (hasNoBody || isLifecycleHook) return true;
281
+ const isImplemented = implementedMethods.has(node.key.name);
282
+ if (isImplemented) return true;
283
+ const isNonPrivate = node.accessibility !== "private";
284
+ return localPrivateOnly && isNonPrivate;
285
+ };
286
+ var implementedFormsMethods = (node) => {
287
+ const methods = /* @__PURE__ */ new Set();
288
+ for (const heritage of node.implements) {
289
+ if (heritage.expression.type !== TSESTree3.AST_NODE_TYPES.Identifier) {
290
+ continue;
291
+ }
292
+ for (const name of formsInterfaceMethods[heritage.expression.name] ?? []) {
293
+ methods.add(name);
294
+ }
295
+ }
296
+ return methods;
297
+ };
298
+ var fieldCandidate = (node, options) => {
299
+ const isField = isInstanceField(node);
300
+ if (!isField) return null;
301
+ const { localPrivateOnly, sourceCode } = options;
302
+ const isExcluded = isExcludedField(node, localPrivateOnly, sourceCode);
303
+ if (isExcluded) return null;
304
+ const isManaged = isManagedField(
305
+ node,
306
+ options.imports,
307
+ options.allowEffectFields,
308
+ sourceCode
309
+ );
310
+ if (isManaged) return null;
311
+ const unusedFieldCandidate = {
312
+ messageId: "unusedField",
313
+ name: node.key.name,
314
+ node
315
+ };
316
+ return unusedFieldCandidate;
317
+ };
318
+ var methodCandidate = (node, localPrivateOnly, implementedMethods) => {
319
+ const isMethod = isInstanceMethod(node);
320
+ if (!isMethod) return null;
321
+ const isExcluded = isExcludedMethod(
322
+ node,
323
+ localPrivateOnly,
324
+ implementedMethods
325
+ );
326
+ if (isExcluded) return null;
327
+ const unusedMethodCandidate = {
328
+ messageId: "unusedMethod",
329
+ name: node.key.name,
330
+ node
331
+ };
332
+ return unusedMethodCandidate;
333
+ };
334
+
335
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
91
336
  import { dirname, resolve } from "node:path";
337
+ import { TSESTree as TSESTree5 } from "@typescript-eslint/utils";
338
+
339
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.ts
340
+ import { TSESTree as TSESTree4 } from "@typescript-eslint/utils";
341
+ var text = (node) => {
342
+ if (node?.type === TSESTree4.AST_NODE_TYPES.Literal && typeof node.value === "string") {
343
+ return node.value;
344
+ }
345
+ if (node?.type !== TSESTree4.AST_NODE_TYPES.TemplateLiteral) return null;
346
+ const hasExpressions = node.expressions.length > 0;
347
+ if (hasExpressions) return null;
348
+ return node.quasis[0].value.cooked;
349
+ };
350
+ var key = (property) => {
351
+ if (property.type !== TSESTree4.AST_NODE_TYPES.Property || property.computed) {
352
+ return null;
353
+ }
354
+ if (property.key.type === TSESTree4.AST_NODE_TYPES.Identifier) {
355
+ return property.key.name;
356
+ }
357
+ return text(property.key);
358
+ };
359
+ var metadataValue = (metadata, name) => {
360
+ for (const property of metadata.properties) {
361
+ if (property.type !== TSESTree4.AST_NODE_TYPES.Property) continue;
362
+ const propertyKey = key(property);
363
+ if (propertyKey === name) return property.value;
364
+ }
365
+ return void 0;
366
+ };
367
+
368
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.ts
92
369
  import {
93
370
  Lexer,
94
371
  ParseLocation,
@@ -98,44 +375,60 @@ import {
98
375
  R3TargetBinder,
99
376
  parseTemplate
100
377
  } from "@angular/compiler";
101
- import { TSESTree } from "@typescript-eslint/utils";
378
+ import { readFileSync, statSync } from "node:fs";
102
379
 
103
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.ts
380
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.ts
104
381
  import {
105
382
  CombinedRecursiveAstVisitor,
106
383
  ImplicitReceiver,
384
+ KeyedRead as KeyedRead2,
385
+ SafeKeyedRead as SafeKeyedRead2,
386
+ ThisReceiver
387
+ } from "@angular/compiler";
388
+
389
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/angular-read-target.util.ts
390
+ import {
107
391
  KeyedRead,
108
392
  PropertyRead,
109
393
  SafeKeyedRead,
110
- SafePropertyRead,
111
- ThisReceiver
394
+ SafePropertyRead
112
395
  } from "@angular/compiler";
396
+ var isReadTarget = (node) => {
397
+ const isPropertyRead = node instanceof PropertyRead;
398
+ const isSafePropertyRead = node instanceof SafePropertyRead;
399
+ const isKeyedRead = node instanceof KeyedRead;
400
+ const isSafeKeyedRead = node instanceof SafeKeyedRead;
401
+ return isPropertyRead || isSafePropertyRead || isKeyedRead || isSafeKeyedRead;
402
+ };
403
+
404
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.ts
113
405
  var ReadCollector = class extends CombinedRecursiveAstVisitor {
406
+ reads = /* @__PURE__ */ new Set();
407
+ boundTarget;
408
+ action;
114
409
  constructor(boundTarget, action) {
115
410
  super();
116
411
  this.boundTarget = boundTarget;
117
412
  this.action = action;
118
413
  }
119
- boundTarget;
120
- action;
121
- reads = /* @__PURE__ */ new Set();
122
414
  record(node) {
123
415
  if (node.receiver instanceof ThisReceiver) {
124
416
  this.reads.add(node.name);
125
417
  return;
126
418
  }
127
- if (node.receiver instanceof ImplicitReceiver && (!this.action || node.name !== "$event") && !this.boundTarget?.getExpressionTarget(node)) {
128
- this.reads.add(node.name);
129
- }
419
+ if (!(node.receiver instanceof ImplicitReceiver)) return;
420
+ const isBindableName = !this.action || node.name !== "$event";
421
+ if (!isBindableName) return;
422
+ const expressionTarget = this.boundTarget?.getExpressionTarget(node);
423
+ if (expressionTarget) return;
424
+ this.reads.add(node.name);
130
425
  }
131
426
  visitBinary(node, context) {
132
- if (node.operation !== "=") {
133
- return super.visitBinary(node, context);
134
- }
135
- if (node.left instanceof PropertyRead || node.left instanceof SafePropertyRead || node.left instanceof KeyedRead || node.left instanceof SafeKeyedRead) {
427
+ if (node.operation !== "=") return super.visitBinary(node, context);
428
+ if (isReadTarget(node.left)) {
136
429
  this.visit(node.left.receiver);
137
430
  }
138
- if (node.left instanceof KeyedRead || node.left instanceof SafeKeyedRead) {
431
+ if (node.left instanceof KeyedRead2 || node.left instanceof SafeKeyedRead2) {
139
432
  this.visit(node.left.key);
140
433
  }
141
434
  this.visit(node.right);
@@ -158,22 +451,14 @@ var collectAngularExpressionReads = (nodes, boundTarget, action) => {
158
451
  return collector.reads;
159
452
  };
160
453
 
161
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.ts
454
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.ts
162
455
  var externalTemplates = /* @__PURE__ */ new Map();
163
456
  var parser = new Parser(new Lexer());
164
457
  var templateBinder = new R3TargetBinder(null);
165
- var text = (node) => {
166
- if (node?.type === TSESTree.AST_NODE_TYPES.Literal && typeof node.value === "string") {
167
- return node.value;
168
- }
169
- return node?.type === TSESTree.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0 ? node.quasis[0].value.cooked : null;
170
- };
171
458
  var templateReads = (template, filename) => {
172
459
  try {
173
460
  const result = parseTemplate(template, filename);
174
- if (result.errors?.length) {
175
- return null;
176
- }
461
+ if (result.errors?.length) return null;
177
462
  return collectAngularExpressionReads(
178
463
  result.nodes,
179
464
  templateBinder.bind({ template: result.nodes }),
@@ -188,9 +473,7 @@ var externalTemplateReads = (filename) => {
188
473
  const stats = statSync(filename, { bigint: true });
189
474
  const version = `${stats.mtimeNs}:${stats.size}`;
190
475
  const cached = externalTemplates.get(filename);
191
- if (cached?.version === version) {
192
- return cached.reads;
193
- }
476
+ if (cached?.version === version) return cached.reads;
194
477
  const reads = templateReads(readFileSync(filename, "utf8"), filename);
195
478
  externalTemplates.set(filename, { reads, version });
196
479
  return reads;
@@ -198,82 +481,66 @@ var externalTemplateReads = (filename) => {
198
481
  return null;
199
482
  }
200
483
  };
484
+ var parseExpression = (expression, span, action) => {
485
+ if (action) return parser.parseAction(expression, span, 0);
486
+ return parser.parseBinding(expression, span, 0);
487
+ };
201
488
  var expressionReads = (expression, filename, action) => {
202
489
  try {
203
490
  const file = new ParseSourceFile(expression, filename);
204
491
  const start = new ParseLocation(file, 0, 0, 0);
205
492
  const span = new ParseSourceSpan(start, start.moveBy(expression.length));
206
- const result = action ? parser.parseAction(expression, span, 0) : parser.parseBinding(expression, span, 0);
207
- if (result.errors.length > 0) {
208
- return null;
209
- }
493
+ const result = parseExpression(expression, span, action);
494
+ if (result.errors.length > 0) return null;
210
495
  return collectAngularExpressionReads([result], void 0, action);
211
496
  } catch {
212
497
  return null;
213
498
  }
214
499
  };
500
+
501
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
215
502
  var addReads = (target, source) => {
216
- if (!source) {
217
- return false;
218
- }
503
+ if (!source) return false;
219
504
  for (const name of source) {
220
505
  target.add(name);
221
506
  }
222
507
  return true;
223
508
  };
224
- var key = (property) => {
225
- if (property.type !== TSESTree.AST_NODE_TYPES.Property || property.computed) {
226
- return null;
227
- }
228
- return property.key.type === TSESTree.AST_NODE_TYPES.Identifier ? property.key.name : text(property.key);
229
- };
230
- var metadataValue = (metadata, name) => {
231
- for (const property of metadata.properties) {
232
- if (property.type === TSESTree.AST_NODE_TYPES.Property && key(property) === name) {
233
- return property.value;
234
- }
235
- }
236
- return void 0;
237
- };
238
509
  var hostPropertyReads = (property, reads, filename) => {
239
510
  const name = key(property);
240
511
  const expression = text(property.value);
241
- if (name === null || expression === null) {
242
- return false;
243
- }
244
- if (name.startsWith("[")) {
512
+ if (name === null || expression === null) return false;
513
+ const isPropertyBinding = name.startsWith("[");
514
+ if (isPropertyBinding) {
245
515
  return addReads(reads, expressionReads(expression, filename, false));
246
516
  }
247
- if (name.startsWith("(")) {
517
+ const isEventBinding = name.startsWith("(");
518
+ if (isEventBinding) {
248
519
  return addReads(reads, expressionReads(expression, filename, true));
249
520
  }
250
521
  return true;
251
522
  };
252
523
  var hostReads = (host, reads, filename) => {
253
- if (!host) {
254
- return true;
255
- }
256
- if (host.type !== TSESTree.AST_NODE_TYPES.ObjectExpression || host.properties.some((property) => key(property) === null)) {
257
- return false;
258
- }
524
+ if (!host) return true;
525
+ if (host.type !== TSESTree5.AST_NODE_TYPES.ObjectExpression) return false;
526
+ const hasUnreadableKey = host.properties.some(
527
+ (property) => key(property) === null
528
+ );
529
+ if (hasUnreadableKey) return false;
259
530
  for (const property of host.properties) {
260
- if (property.type !== TSESTree.AST_NODE_TYPES.Property || !hostPropertyReads(property, reads, filename)) {
261
- return false;
262
- }
531
+ if (property.type !== TSESTree5.AST_NODE_TYPES.Property) return false;
532
+ const hasPropertyReads = hostPropertyReads(property, reads, filename);
533
+ if (!hasPropertyReads) return false;
263
534
  }
264
535
  return true;
265
536
  };
266
537
  var inlineTemplateReads = (template, reads, filename) => {
267
- if (!template) {
268
- return true;
269
- }
538
+ if (!template) return true;
270
539
  const source = text(template);
271
540
  return source !== null && addReads(reads, templateReads(source, filename));
272
541
  };
273
542
  var externalTemplatePropertyReads = (templateUrl, reads, filename) => {
274
- if (!templateUrl) {
275
- return true;
276
- }
543
+ if (!templateUrl) return true;
277
544
  const url = text(templateUrl);
278
545
  return url !== null && addReads(reads, externalTemplateReads(resolve(dirname(filename), url)));
279
546
  };
@@ -282,156 +549,113 @@ var componentTemplateReads = (metadata, reads, filename) => {
282
549
  const templateUrl = metadataValue(metadata, "templateUrl");
283
550
  return inlineTemplateReads(template, reads, filename) && externalTemplatePropertyReads(templateUrl, reads, filename);
284
551
  };
285
- var metadataReads = (metadata, component, filename, remainingNames) => {
286
- if (metadata.properties.some((property) => key(property) === null)) {
287
- return null;
288
- }
552
+ var metadataReads = (options) => {
553
+ const { filename, metadata } = options;
554
+ const isUnreadableKey = (property) => {
555
+ return key(property) === null;
556
+ };
557
+ const hasUnreadableKey = metadata.properties.some(isUnreadableKey);
558
+ if (hasUnreadableKey) return null;
289
559
  const reads = /* @__PURE__ */ new Set();
290
- if (!hostReads(metadataValue(metadata, "host"), reads, filename)) {
291
- return null;
292
- }
293
- if (component && remainingNames.every((name) => reads.has(name)) || !component || componentTemplateReads(metadata, reads, filename)) {
294
- return reads;
295
- }
560
+ const hostValue = metadataValue(metadata, "host");
561
+ const hasHostReads = hostReads(hostValue, reads, filename);
562
+ if (!hasHostReads) return null;
563
+ if (!options.component) return reads;
564
+ const isRead = (name) => reads.has(name);
565
+ const hasRemainingReads = options.remainingNames.every(isRead);
566
+ if (hasRemainingReads) return reads;
567
+ const hasTemplateReads = componentTemplateReads(metadata, reads, filename);
568
+ if (hasTemplateReads) return reads;
569
+ if (!options.requireTemplate) return reads;
296
570
  return null;
297
571
  };
298
572
 
299
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
300
- var managedApis = {
301
- input: true,
302
- model: true,
303
- output: true
304
- };
305
- var angularName = (node, imports) => {
306
- if (node.type === TSESTree2.AST_NODE_TYPES.Identifier) {
307
- return imports.get(node.name);
308
- }
309
- if (node.type !== TSESTree2.AST_NODE_TYPES.MemberExpression || node.computed || node.property.type !== TSESTree2.AST_NODE_TYPES.Identifier) {
310
- return null;
311
- }
312
- if (node.object.type === TSESTree2.AST_NODE_TYPES.Identifier && imports.get(node.object.name) === null) {
313
- return node.property.name;
314
- }
315
- return angularName(node.object, imports);
316
- };
317
- var angularClass = (node, imports) => {
318
- for (const decorator of node.decorators) {
319
- if (decorator.expression.type !== TSESTree2.AST_NODE_TYPES.CallExpression) {
320
- continue;
321
- }
322
- const kind = angularName(decorator.expression.callee, imports);
323
- const metadata = decorator.expression.arguments[0];
324
- if ((kind === "Component" || kind === "Directive") && metadata?.type === TSESTree2.AST_NODE_TYPES.ObjectExpression) {
325
- return {
326
- component: kind === "Component",
327
- metadata
328
- };
573
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.ts
574
+ var memberCandidates = (node, options) => {
575
+ const implementedMethods = implementedFormsMethods(node);
576
+ const members = [];
577
+ for (const element of node.body.body) {
578
+ const candidate = fieldCandidate(element, options) ?? methodCandidate(element, options.localPrivateOnly, implementedMethods);
579
+ if (candidate) {
580
+ members.push(candidate);
329
581
  }
330
582
  }
331
- return null;
332
- };
333
- var isInstanceField = (node) => node.type === TSESTree2.AST_NODE_TYPES.PropertyDefinition && node.key.type === TSESTree2.AST_NODE_TYPES.Identifier;
334
- var lifecycleHooks = {
335
- ngOnChanges: true,
336
- ngOnInit: true,
337
- ngDoCheck: true,
338
- ngAfterContentInit: true,
339
- ngAfterContentChecked: true,
340
- ngAfterViewInit: true,
341
- ngAfterViewChecked: true,
342
- ngOnDestroy: true
343
- };
344
- var isInstanceMethod = (node) => node.type === TSESTree2.AST_NODE_TYPES.MethodDefinition && node.key.type === TSESTree2.AST_NODE_TYPES.Identifier;
345
- var isExcludedField = (node, component) => node.static || node.declare || node.override || node.decorators.length > 0 || !component && node.accessibility !== "private";
346
- var isManagedField = (node, imports) => {
347
- if (node.value?.type !== TSESTree2.AST_NODE_TYPES.CallExpression) {
348
- return false;
349
- }
350
- const name = angularName(node.value.callee, imports);
351
- return typeof name === "string" && managedApis[name] === true;
583
+ return members;
352
584
  };
353
- var field = (node, imports, component) => {
354
- if (isInstanceField(node) && !isExcludedField(node, component) && !isManagedField(node, imports)) {
355
- return { messageId: "unusedField", name: node.key.name, node };
356
- }
357
- return null;
585
+ var reportMember = (context, candidate) => {
586
+ const data = { name: candidate.name };
587
+ const report = {
588
+ data,
589
+ messageId: candidate.messageId,
590
+ node: candidate.node.key
591
+ };
592
+ context.report(report);
358
593
  };
359
- 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";
360
- var method = (node, component) => {
361
- if (isInstanceMethod(node) && !isExcludedMethod(node, component)) {
362
- return { messageId: "unusedMethod", name: node.key.name, node };
594
+ var reportUnreadMembers = (members, reads, options) => {
595
+ for (const candidate of members) {
596
+ const isTemplateRead = reads.has(candidate.name);
597
+ if (isTemplateRead) continue;
598
+ const isProjectUsed = options.projectMemberUsed?.(candidate.node) === true;
599
+ if (isProjectUsed) continue;
600
+ reportMember(options.context, candidate);
363
601
  }
364
- return null;
365
602
  };
366
- var addAngularImport = (node, imports) => {
367
- if (node.source.value !== "@angular/core" || node.importKind === "type") {
368
- return;
369
- }
370
- for (const specifier of node.specifiers) {
371
- if (specifier.type === TSESTree2.AST_NODE_TYPES.ImportNamespaceSpecifier) {
372
- imports.set(specifier.local.name, null);
373
- } else if (specifier.type === TSESTree2.AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type") {
374
- const importedName = specifier.imported.type === TSESTree2.AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value;
375
- imports.set(specifier.local.name, importedName);
376
- }
377
- }
603
+ var reportClassMembers = (entry, ngClass, report) => {
604
+ const { options, projectAnalysis } = report;
605
+ const isLocalOnlyClass = !ngClass.component || entry.node.abstract === true;
606
+ const candidateOptions = {
607
+ allowEffectFields: options.allowEffectFields,
608
+ imports: options.imports,
609
+ localPrivateOnly: !projectAnalysis && isLocalOnlyClass,
610
+ sourceCode: options.context.sourceCode
611
+ };
612
+ const members = memberCandidates(entry.node, candidateOptions);
613
+ const unreadMembers = members.filter(
614
+ (candidate) => !entry.reads.has(candidate.name)
615
+ );
616
+ if (unreadMembers.length === 0) return;
617
+ const remainingNames = unreadMembers.map((candidate) => candidate.name);
618
+ const readsOptions = {
619
+ component: ngClass.component && report.localTemplates,
620
+ filename: options.context.filename,
621
+ metadata: ngClass.metadata,
622
+ remainingNames,
623
+ requireTemplate: !projectAnalysis
624
+ };
625
+ const reads = metadataReads(readsOptions);
626
+ if (!reads) return;
627
+ reportUnreadMembers(unreadMembers, reads, options);
378
628
  };
379
- var reportUnusedMembers = (context, imports, classes, dynamicClasses) => {
380
- for (const entry of classes) {
381
- const ngClass = angularClass(entry.node, imports);
382
- if (!ngClass || dynamicClasses.has(entry)) {
383
- continue;
384
- }
385
- const members = [];
386
- for (const node of entry.node.body.body) {
387
- const candidate = field(node, imports, ngClass.component) ?? method(node, ngClass.component);
388
- if (candidate) {
389
- members.push(candidate);
390
- }
391
- }
392
- if (members.length === 0) {
393
- continue;
394
- }
395
- const unreadMembers = members.filter(
396
- (candidate) => !entry.reads.has(candidate.name)
397
- );
398
- if (unreadMembers.length === 0) {
399
- continue;
400
- }
401
- const reads = metadataReads(
402
- ngClass.metadata,
403
- ngClass.component,
404
- context.filename,
405
- unreadMembers.map((candidate) => candidate.name)
406
- );
407
- if (!reads) {
408
- continue;
409
- }
410
- for (const candidate of unreadMembers) {
411
- if (!reads.has(candidate.name)) {
412
- context.report({
413
- data: { name: candidate.name },
414
- messageId: candidate.messageId,
415
- node: candidate.node.key
416
- });
417
- }
418
- }
629
+ var reportUnusedMembers = (options) => {
630
+ const projectAnalysis = options.projectMemberUsed !== void 0;
631
+ const localTemplates = !projectAnalysis || !options.projectIndexed();
632
+ const report = {
633
+ localTemplates,
634
+ options,
635
+ projectAnalysis
636
+ };
637
+ for (const entry of options.classes) {
638
+ const ngClass = angularClassMetadata(entry.node, options.imports);
639
+ const isDynamicClass = options.dynamicClasses.has(entry);
640
+ if (!ngClass || isDynamicClass) continue;
641
+ reportClassMembers(entry, ngClass, report);
419
642
  }
420
643
  };
421
644
 
422
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.ts
423
- import { TSESTree as TSESTree3 } from "@typescript-eslint/utils";
645
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.ts
646
+ import { TSESTree as TSESTree9 } from "@typescript-eslint/utils";
647
+
648
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.ts
649
+ import { TSESTree as TSESTree6 } from "@typescript-eslint/utils";
650
+ var expressionWrapperTypes = /* @__PURE__ */ new Set([
651
+ TSESTree6.AST_NODE_TYPES.TSAsExpression,
652
+ TSESTree6.AST_NODE_TYPES.TSInstantiationExpression,
653
+ TSESTree6.AST_NODE_TYPES.TSNonNullExpression,
654
+ TSESTree6.AST_NODE_TYPES.TSSatisfiesExpression,
655
+ TSESTree6.AST_NODE_TYPES.TSTypeAssertion
656
+ ]);
424
657
  var isTypeScriptExpressionWrapper = (node) => {
425
- switch (node.type) {
426
- case TSESTree3.AST_NODE_TYPES.TSAsExpression:
427
- case TSESTree3.AST_NODE_TYPES.TSInstantiationExpression:
428
- case TSESTree3.AST_NODE_TYPES.TSNonNullExpression:
429
- case TSESTree3.AST_NODE_TYPES.TSSatisfiesExpression:
430
- case TSESTree3.AST_NODE_TYPES.TSTypeAssertion:
431
- return true;
432
- default:
433
- return false;
434
- }
658
+ return expressionWrapperTypes.has(node.type);
435
659
  };
436
660
  var unwrapTypeScriptExpression = (node) => {
437
661
  let current = node;
@@ -440,123 +664,215 @@ var unwrapTypeScriptExpression = (node) => {
440
664
  }
441
665
  return current;
442
666
  };
443
- var isThisExpression = (node) => unwrapTypeScriptExpression(node)?.type === TSESTree3.AST_NODE_TYPES.ThisExpression;
444
- var isPatternTarget = (parent, child) => {
445
- switch (parent?.type) {
446
- case TSESTree3.AST_NODE_TYPES.ArrayPattern:
447
- return parent.elements.some((element) => element === child);
448
- case TSESTree3.AST_NODE_TYPES.AssignmentPattern:
449
- return parent.left === child;
450
- case TSESTree3.AST_NODE_TYPES.ObjectPattern:
451
- return parent.properties.some((property) => property === child);
452
- case TSESTree3.AST_NODE_TYPES.Property:
453
- return parent.value === child && parent.parent.type === TSESTree3.AST_NODE_TYPES.ObjectPattern;
454
- case TSESTree3.AST_NODE_TYPES.RestElement:
455
- return parent.argument === child;
456
- default:
457
- return false;
458
- }
459
- };
460
- var isSimpleAssignmentTarget = (parent, current) => parent?.type === TSESTree3.AST_NODE_TYPES.AssignmentExpression && parent.left === current && parent.operator === "=";
461
- var isLoopTarget = (parent, current) => (parent?.type === TSESTree3.AST_NODE_TYPES.ForInStatement || parent?.type === TSESTree3.AST_NODE_TYPES.ForOfStatement) && parent.left === current;
462
- var isDirectWrite = (node) => {
463
- let current = node;
464
- let parent = node.parent;
465
- while (parent && isTypeScriptExpressionWrapper(parent) && parent.expression === current) {
466
- current = parent;
467
- parent = parent.parent;
468
- }
469
- return isSimpleAssignmentTarget(parent, current) || parent?.type === TSESTree3.AST_NODE_TYPES.UnaryExpression && parent.operator === "delete";
470
- };
471
- var isDestructuringOrLoopTarget = (node) => {
472
- let current = node;
473
- let parent = node.parent;
474
- let patternTarget = false;
475
- while (parent) {
476
- const wrapper = isTypeScriptExpressionWrapper(parent) && parent.expression === current;
477
- const pattern = isPatternTarget(parent, current);
478
- if (!wrapper && !pattern) {
479
- break;
480
- }
481
- if (pattern) {
482
- patternTarget = true;
483
- }
484
- current = parent;
485
- parent = parent.parent;
486
- }
487
- return patternTarget && isSimpleAssignmentTarget(parent, current) || isLoopTarget(parent, current);
667
+ var isThisExpression = (node) => {
668
+ const unwrapped = unwrapTypeScriptExpression(node);
669
+ return unwrapped?.type === TSESTree6.AST_NODE_TYPES.ThisExpression;
488
670
  };
489
- var isWriteOnly = (node) => isDirectWrite(node) || isDestructuringOrLoopTarget(node);
490
671
  var destructuringParts = (node) => {
491
- if (node.type === TSESTree3.AST_NODE_TYPES.VariableDeclarator) {
492
- return { pattern: node.id, source: node.init };
672
+ if (node.type === TSESTree6.AST_NODE_TYPES.VariableDeclarator) {
673
+ const variableParts = {
674
+ pattern: node.id,
675
+ source: node.init
676
+ };
677
+ return variableParts;
493
678
  }
494
679
  if (node.operator === "=") {
495
- return { pattern: node.left, source: node.right };
680
+ const assignmentParts = {
681
+ pattern: node.left,
682
+ source: node.right
683
+ };
684
+ return assignmentParts;
496
685
  }
497
686
  return null;
498
687
  };
499
688
  var propertyName = (property) => {
500
- if (property.type !== TSESTree3.AST_NODE_TYPES.Property || property.computed) {
689
+ if (property.type !== TSESTree6.AST_NODE_TYPES.Property || property.computed) {
501
690
  return null;
502
691
  }
503
- if (property.key.type === TSESTree3.AST_NODE_TYPES.Identifier) {
692
+ if (property.key.type === TSESTree6.AST_NODE_TYPES.Identifier) {
504
693
  return property.key.name;
505
694
  }
506
- return property.key.type === TSESTree3.AST_NODE_TYPES.Literal && typeof property.key.value === "string" ? property.key.value : null;
695
+ const { value } = property.key;
696
+ if (typeof value !== "string") return null;
697
+ return value;
507
698
  };
508
699
  var destructuredThisReads = (node) => {
509
700
  const parts = destructuringParts(node);
510
- if (!parts || parts.pattern.type !== TSESTree3.AST_NODE_TYPES.ObjectPattern || !isThisExpression(parts.source)) {
701
+ if (!parts) return void 0;
702
+ if (parts.pattern.type !== TSESTree6.AST_NODE_TYPES.ObjectPattern) {
511
703
  return void 0;
512
704
  }
705
+ const isThisSource = isThisExpression(parts.source);
706
+ if (!isThisSource) return void 0;
513
707
  const reads = [];
514
708
  for (const property of parts.pattern.properties) {
515
709
  const name = propertyName(property);
516
- if (name === null) {
517
- return null;
518
- }
710
+ if (name === null) return null;
519
711
  reads.push(name);
520
712
  }
521
713
  return reads;
522
714
  };
523
715
 
524
- // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.ts
525
- var createRule = ESLintUtils.RuleCreator(
526
- () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
527
- );
528
- var enterClass = (node, classes, stack, thisStack) => {
529
- const entry = {
530
- node,
531
- reads: /* @__PURE__ */ new Set()
532
- };
533
- classes.push(entry);
534
- stack.push(entry);
535
- thisStack.push(true);
716
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-lexical-this.ts
717
+ import { TSESTree as TSESTree7 } from "@typescript-eslint/utils";
718
+ var componentThis = (node, thisStack) => {
719
+ if (node.type === TSESTree7.AST_NODE_TYPES.ArrowFunctionExpression) {
720
+ return thisStack.at(-1) ?? false;
721
+ }
722
+ const { parent } = node;
723
+ if (parent.type !== TSESTree7.AST_NODE_TYPES.MethodDefinition) return false;
724
+ return !parent.static;
536
725
  };
537
- var componentThis = (node, thisStack) => node.type === TSESTree4.AST_NODE_TYPES.ArrowFunctionExpression ? thisStack.at(-1) ?? false : node.parent.type === TSESTree4.AST_NODE_TYPES.MethodDefinition && !node.parent.static;
538
- var trackMemberRead = (node, stack, thisStack, dynamicClasses) => {
539
- const current = stack.at(-1);
540
- if (!current || !thisStack.at(-1)) {
541
- return;
542
- }
543
- const receiverIsThis = isThisExpression(node.object);
726
+ var lexicalThisVisitor = (thisStack) => {
727
+ const listeners = {
728
+ FunctionDeclaration(node) {
729
+ thisStack.push(componentThis(node, thisStack));
730
+ },
731
+ FunctionExpression(node) {
732
+ thisStack.push(componentThis(node, thisStack));
733
+ },
734
+ ArrowFunctionExpression(node) {
735
+ thisStack.push(componentThis(node, thisStack));
736
+ },
737
+ "FunctionDeclaration:exit"() {
738
+ thisStack.pop();
739
+ },
740
+ "FunctionExpression:exit"() {
741
+ thisStack.pop();
742
+ },
743
+ "ArrowFunctionExpression:exit"() {
744
+ thisStack.pop();
745
+ },
746
+ StaticBlock() {
747
+ thisStack.push(false);
748
+ },
749
+ "StaticBlock:exit"() {
750
+ thisStack.pop();
751
+ },
752
+ PropertyDefinition(node) {
753
+ if (node.static) {
754
+ thisStack.push(false);
755
+ }
756
+ },
757
+ "PropertyDefinition:exit"(node) {
758
+ if (node.static) {
759
+ thisStack.pop();
760
+ }
761
+ }
762
+ };
763
+ return listeners;
764
+ };
765
+
766
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-write-targets.ts
767
+ import { TSESTree as TSESTree8 } from "@typescript-eslint/utils";
768
+ var parentOf = (node) => {
769
+ if (node.type === TSESTree8.AST_NODE_TYPES.Program) return void 0;
770
+ return node.parent;
771
+ };
772
+ var isPatternTarget = (parent, child) => {
773
+ if (parent === void 0) return false;
774
+ if (parent.type === TSESTree8.AST_NODE_TYPES.ArrayPattern) {
775
+ return parent.elements.some((element) => element === child);
776
+ }
777
+ if (parent.type === TSESTree8.AST_NODE_TYPES.AssignmentPattern) {
778
+ return parent.left === child;
779
+ }
780
+ if (parent.type === TSESTree8.AST_NODE_TYPES.ObjectPattern) {
781
+ return parent.properties.some((property) => property === child);
782
+ }
783
+ if (parent.type === TSESTree8.AST_NODE_TYPES.Property) {
784
+ const isObjectPattern = parent.parent.type === TSESTree8.AST_NODE_TYPES.ObjectPattern;
785
+ return parent.value === child && isObjectPattern;
786
+ }
787
+ if (parent.type === TSESTree8.AST_NODE_TYPES.RestElement) {
788
+ return parent.argument === child;
789
+ }
790
+ return false;
791
+ };
792
+ var isSimpleAssignmentTarget = (parent, current) => {
793
+ if (parent?.type !== TSESTree8.AST_NODE_TYPES.AssignmentExpression) {
794
+ return false;
795
+ }
796
+ return parent.left === current && parent.operator === "=";
797
+ };
798
+ var isLoopTarget = (parent, current) => {
799
+ const isForIn = parent?.type === TSESTree8.AST_NODE_TYPES.ForInStatement;
800
+ const isForOf = parent?.type === TSESTree8.AST_NODE_TYPES.ForOfStatement;
801
+ const isLoop = isForIn || isForOf;
802
+ if (!isLoop) return false;
803
+ return parent.left === current;
804
+ };
805
+ var isWrapping = (parent, child) => {
806
+ const isWrapper = isTypeScriptExpressionWrapper(parent);
807
+ if (!isWrapper) return false;
808
+ return parent.expression === child;
809
+ };
810
+ var isDirectWrite = (node) => {
811
+ let current = node;
812
+ let parent = parentOf(node);
813
+ while (parent && isTypeScriptExpressionWrapper(parent) && parent.expression === current) {
814
+ current = parent;
815
+ parent = parentOf(parent);
816
+ }
817
+ const isDeleteExpression2 = parent?.type === TSESTree8.AST_NODE_TYPES.UnaryExpression && parent.operator === "delete";
818
+ const isAssignment = isSimpleAssignmentTarget(parent, current);
819
+ return isAssignment || isDeleteExpression2;
820
+ };
821
+ var isDestructuringOrLoopTarget = (node) => {
822
+ let current = node;
823
+ let parent = parentOf(node);
824
+ let patternTarget = false;
825
+ while (parent) {
826
+ const wrapper = isWrapping(parent, current);
827
+ const pattern = isPatternTarget(parent, current);
828
+ if (!wrapper && !pattern) break;
829
+ if (pattern) {
830
+ patternTarget = true;
831
+ }
832
+ current = parent;
833
+ parent = parentOf(parent);
834
+ }
835
+ const isSimpleTarget = isSimpleAssignmentTarget(parent, current);
836
+ const isAssignedPattern = patternTarget && isSimpleTarget;
837
+ return isAssignedPattern || isLoopTarget(parent, current);
838
+ };
839
+ var isWriteOnly = (node) => {
840
+ const isWrite = isDirectWrite(node);
841
+ if (isWrite) return true;
842
+ return isDestructuringOrLoopTarget(node);
843
+ };
844
+
845
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.ts
846
+ var enterClass = (node, classes, stack, thisStack) => {
847
+ const entry = {
848
+ node,
849
+ reads: /* @__PURE__ */ new Set()
850
+ };
851
+ classes.push(entry);
852
+ stack.push(entry);
853
+ thisStack.push(true);
854
+ };
855
+ var trackMemberRead = (node, stack, thisStack, dynamicClasses) => {
856
+ const current = stack.at(-1);
857
+ const isComponentThis = thisStack.at(-1);
858
+ if (!current || !isComponentThis) return;
859
+ const receiverIsThis = isThisExpression(node.object);
544
860
  if (node.computed && receiverIsThis) {
545
861
  dynamicClasses.add(current);
546
862
  }
547
- if (receiverIsThis && !node.computed && node.property.type === TSESTree4.AST_NODE_TYPES.Identifier && !isWriteOnly(node)) {
863
+ if (!receiverIsThis || node.computed) return;
864
+ const isWriteOnlyTarget = isWriteOnly(node);
865
+ if (isWriteOnlyTarget) return;
866
+ if (node.property.type === TSESTree9.AST_NODE_TYPES.Identifier) {
548
867
  current.reads.add(node.property.name);
549
868
  }
550
869
  };
551
870
  var trackDestructuringRead = (node, stack, thisStack, dynamicClasses) => {
552
871
  const current = stack.at(-1);
553
- if (!current || !thisStack.at(-1)) {
554
- return;
555
- }
872
+ const isComponentThis = thisStack.at(-1);
873
+ if (!current || !isComponentThis) return;
556
874
  const reads = destructuredThisReads(node);
557
- if (reads === void 0) {
558
- return;
559
- }
875
+ if (reads === void 0) return;
560
876
  if (reads === null) {
561
877
  dynamicClasses.add(current);
562
878
  return;
@@ -565,45 +881,9 @@ var trackDestructuringRead = (node, stack, thisStack, dynamicClasses) => {
565
881
  current.reads.add(name);
566
882
  }
567
883
  };
568
- var lexicalThisVisitor = (thisStack) => ({
569
- FunctionDeclaration(node) {
570
- thisStack.push(componentThis(node, thisStack));
571
- },
572
- FunctionExpression(node) {
573
- thisStack.push(componentThis(node, thisStack));
574
- },
575
- ArrowFunctionExpression(node) {
576
- thisStack.push(componentThis(node, thisStack));
577
- },
578
- "FunctionDeclaration:exit"() {
579
- thisStack.pop();
580
- },
581
- "FunctionExpression:exit"() {
582
- thisStack.pop();
583
- },
584
- "ArrowFunctionExpression:exit"() {
585
- thisStack.pop();
586
- },
587
- StaticBlock() {
588
- thisStack.push(false);
589
- },
590
- "StaticBlock:exit"() {
591
- thisStack.pop();
592
- },
593
- PropertyDefinition(node) {
594
- if (node.static) {
595
- thisStack.push(false);
596
- }
597
- },
598
- "PropertyDefinition:exit"(node) {
599
- if (node.static) {
600
- thisStack.pop();
601
- }
602
- }
603
- });
604
- var visitor = (context, imports, classes, stack, dynamicClasses) => {
884
+ var classReadVisitor = (classes, stack, dynamicClasses) => {
605
885
  const thisStack = [];
606
- return {
886
+ const listeners = {
607
887
  ClassDeclaration(node) {
608
888
  enterClass(node, classes, stack, thisStack);
609
889
  },
@@ -627,183 +907,1980 @@ var visitor = (context, imports, classes, stack, dynamicClasses) => {
627
907
  },
628
908
  VariableDeclarator(node) {
629
909
  trackDestructuringRead(node, stack, thisStack, dynamicClasses);
630
- },
631
- "Program:exit"() {
632
- reportUnusedMembers(context, imports, classes, dynamicClasses);
633
910
  }
634
911
  };
912
+ return listeners;
635
913
  };
636
- var no_unused_angular_instance_fields_default = createRule({
637
- name: "no-unused-instance-fields",
638
- meta: {
639
- type: "problem",
640
- docs: {
641
- description: "Disallow unread instance fields and methods in Angular components and directives"
642
- },
643
- messages: {
644
- unusedField: "Angular instance field '{{name}}' is never read.",
645
- unusedMethod: "Angular instance method '{{name}}' is never read."
646
- },
647
- schema: []
648
- },
649
- defaultOptions: [],
650
- create(context) {
651
- const imports = /* @__PURE__ */ new Map();
652
- for (const node of context.sourceCode.ast.body) {
653
- if (node.type === TSESTree4.AST_NODE_TYPES.ImportDeclaration) {
654
- addAngularImport(node, imports);
914
+
915
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
916
+ import { isClassElement, isIdentifier as isIdentifier7, isStringLiteralLike as isStringLiteralLike6 } from "typescript";
917
+
918
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.ts
919
+ import { CssSelector, SelectorMatcher } from "@angular/compiler";
920
+ var classShape = ({
921
+ component,
922
+ declaration,
923
+ exportAs,
924
+ hostDirectives,
925
+ name,
926
+ selector
927
+ }) => {
928
+ const fields = [
929
+ declaration.getSourceFile().fileName,
930
+ name,
931
+ String(component),
932
+ String(hostDirectives),
933
+ selector ?? "",
934
+ exportAs.join(",")
935
+ ];
936
+ return fields.join("\0");
937
+ };
938
+ var directiveShape = (classes) => {
939
+ const shapes = [...classes].map(classShape);
940
+ return shapes.sort().join("\n");
941
+ };
942
+ var parseSelector = (selector) => {
943
+ try {
944
+ return CssSelector.parse(selector);
945
+ } catch {
946
+ return null;
947
+ }
948
+ };
949
+ var buildDirectiveIndex = (classes) => {
950
+ const byDeclaration = /* @__PURE__ */ new Map();
951
+ const byExportAs = /* @__PURE__ */ new Map();
952
+ const componentMatcher = new SelectorMatcher();
953
+ for (const angularClass2 of classes) {
954
+ const { component, declaration, exportAs, selector } = angularClass2;
955
+ byDeclaration.set(declaration, angularClass2);
956
+ for (const name of exportAs) {
957
+ const declarations = byExportAs.get(name) ?? [];
958
+ declarations.push(declaration);
959
+ byExportAs.set(name, declarations);
960
+ }
961
+ if (component && selector !== null) {
962
+ const selectors = parseSelector(selector);
963
+ if (selectors) {
964
+ componentMatcher.addSelectables(selectors, [declaration]);
655
965
  }
656
966
  }
657
- if (imports.size === 0) {
658
- return {};
967
+ }
968
+ const directiveIndex = {
969
+ byDeclaration,
970
+ byExportAs,
971
+ componentMatcher
972
+ };
973
+ return directiveIndex;
974
+ };
975
+
976
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
977
+ import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
978
+ import { isIdentifier, isStringLiteralLike } from "typescript";
979
+
980
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
981
+ import {
982
+ R3TargetBinder as R3TargetBinder2,
983
+ ThisReceiver as ThisReceiver3,
984
+ TmplAstReference,
985
+ parseTemplate as parseTemplate2
986
+ } from "@angular/compiler";
987
+
988
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.ts
989
+ import { SignatureKind, TypeFlags } from "typescript";
990
+
991
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.ts
992
+ import { IndexKind } from "typescript";
993
+ var symbolsForName = (checker, type, name) => {
994
+ const symbol = checker.getPropertyOfType(checker.getApparentType(type), name);
995
+ if (symbol) return [symbol];
996
+ const isUnionOrIntersection = type.isUnionOrIntersection();
997
+ if (!isUnionOrIntersection) return [];
998
+ const symbols = /* @__PURE__ */ new Set();
999
+ for (const member of type.types) {
1000
+ for (const memberSymbol of symbolsForName(checker, member, name)) {
1001
+ symbols.add(memberSymbol);
659
1002
  }
660
- const classes = [];
661
- const stack = [];
662
- const dynamicClasses = /* @__PURE__ */ new Set();
663
- return visitor(context, imports, classes, stack, dynamicClasses);
664
1003
  }
665
- });
1004
+ return [...symbols];
1005
+ };
1006
+ var stringIndexTypes = (checker, type) => {
1007
+ const indexedType = checker.getIndexTypeOfType(
1008
+ checker.getApparentType(type),
1009
+ IndexKind.String
1010
+ );
1011
+ if (indexedType) return [indexedType];
1012
+ const isUnionOrIntersection = type.isUnionOrIntersection();
1013
+ if (!isUnionOrIntersection) return [];
1014
+ const indexedTypes = /* @__PURE__ */ new Set();
1015
+ for (const member of type.types) {
1016
+ for (const memberIndexedType of stringIndexTypes(checker, member)) {
1017
+ indexedTypes.add(memberIndexedType);
1018
+ }
1019
+ }
1020
+ return [...indexedTypes];
1021
+ };
666
1022
 
667
- // packages/lint-suite/src/lib/angular.ts
668
- var lintSuiteAngularPlugin = {
669
- rules: {
670
- "no-unused-instance-fields": no_unused_angular_instance_fields_default
1023
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.ts
1024
+ var hasAnyOrUnknownFlag = (type) => {
1025
+ const anyOrUnknown = TypeFlags.Any | TypeFlags.Unknown;
1026
+ return (type.flags & anyOrUnknown) !== 0;
1027
+ };
1028
+ var isAnyOrUnknown = (types) => {
1029
+ return types.some(hasAnyOrUnknownFlag);
1030
+ };
1031
+ var addTypes = (sink, types) => {
1032
+ for (const type of types) {
1033
+ sink.addType(type);
671
1034
  }
672
1035
  };
673
- var angular = defineConfig2([
674
- {
675
- files: ["**/*.ts"],
676
- extends: [...configs2.tsRecommended],
677
- plugins: {
678
- "lint-suite-angular": lintSuiteAngularPlugin
679
- },
680
- languageOptions: {
681
- globals: {
682
- ...globals.browser,
683
- ...globals.es2025,
684
- ...globals.node
1036
+ var callReturnTypes = (checker, type) => {
1037
+ const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
1038
+ return signatures.map((signature) => signature.getReturnType());
1039
+ };
1040
+ var memberTypes = (symbols, declaration, checker, sink) => {
1041
+ const types = [];
1042
+ for (const symbol of symbols) {
1043
+ for (const memberDeclaration of symbol.declarations ?? []) {
1044
+ sink.addDeclaration(memberDeclaration);
1045
+ }
1046
+ types.push(checker.getTypeOfSymbolAtLocation(symbol, declaration));
1047
+ }
1048
+ return types;
1049
+ };
1050
+ var addResolvedPath = ({
1051
+ allowMissingRoot,
1052
+ checker,
1053
+ declaration,
1054
+ names,
1055
+ sink
1056
+ }) => {
1057
+ let types = [checker.getTypeAtLocation(declaration)];
1058
+ const indexTypesOf = (type) => stringIndexTypes(checker, type);
1059
+ const returnTypesOf = (type) => callReturnTypes(checker, type);
1060
+ for (const [index, segment] of names.entries()) {
1061
+ addTypes(sink, types);
1062
+ const symbolsOf = (type) => {
1063
+ return symbolsForName(checker, type, segment.name);
1064
+ };
1065
+ const symbols = new Set(types.flatMap(symbolsOf));
1066
+ if (symbols.size === 0) {
1067
+ const indexedTypes = types.flatMap(indexTypesOf);
1068
+ if (indexedTypes.length > 0) {
1069
+ types = indexedTypes;
1070
+ continue;
685
1071
  }
686
- },
687
- processor: processInlineTemplates,
688
- rules: {
689
- "lint-suite-angular/no-unused-instance-fields": "error",
690
- "@angular-eslint/prefer-on-push-component-change-detection": "error",
691
- // v22: flags only explicit opt-out of OnPush (omission now means OnPush)
692
- "@angular-eslint/prefer-signals": "error",
693
- "@angular-eslint/use-injectable-provided-in": "error",
694
- "@angular-eslint/prefer-signal-model": "error",
695
- "@angular-eslint/no-uncalled-signals": "error",
696
- "@angular-eslint/prefer-host-metadata-property": "error",
697
- "@angular-eslint/no-implicit-take-until-destroyed": "error",
698
- // v21.2.0: Enforce explicit DestroyRef
699
- "@angular-eslint/no-async-lifecycle-method": "error",
700
- // Prevent async lifecycle hooks
701
- "@angular-eslint/prefer-output-readonly": "warn",
702
- // Prevent accidental reassignment
703
- "@angular-eslint/prefer-output-emitter-ref": "warn",
704
- // Modern output() function over @Output()
705
- "@angular-eslint/sort-lifecycle-methods": "warn",
706
- // Consistent lifecycle method ordering
707
- "@angular-eslint/computed-must-return": "error",
708
- // Catch silent computed() bugs missing a return
709
- "@angular-eslint/no-duplicates-in-metadata-arrays": "error",
710
- // Catch duplicate imports/providers in @Component/@NgModule
711
- "@angular-eslint/no-lifecycle-call": "error",
712
- // Forbid manual this.ngOnInit() calls
713
- "@angular-eslint/require-lifecycle-on-prototype": "error",
714
- // Lifecycle hooks must be methods, not arrow-fn fields
715
- "@angular-eslint/relative-url-prefix": "error",
716
- // Require ./ in templateUrl/styleUrl for portability
717
- "@angular-eslint/use-component-selector": "error",
718
- // All components must declare a selector
719
- "@angular-eslint/no-developer-preview": "warn",
720
- // Flag APIs marked @developerPreview (no breaking-change policy)
721
- "@angular-eslint/no-experimental": "warn"
722
- // Flag APIs marked @experimental
1072
+ const isAnyOrUnknownType = isAnyOrUnknown(types);
1073
+ if (isAnyOrUnknownType) return true;
1074
+ return index === 0 && allowMissingRoot;
1075
+ }
1076
+ types = memberTypes(symbols, declaration, checker, sink);
1077
+ if (segment.called) {
1078
+ const returnTypes = types.flatMap(returnTypesOf);
1079
+ if (returnTypes.length === 0) return isAnyOrUnknown(types);
1080
+ types = returnTypes;
723
1081
  }
724
1082
  }
725
- ]);
1083
+ return true;
1084
+ };
726
1085
 
727
- // packages/lint-suite/src/lib/base.ts
728
- import { configs as configs3 } from "@nx/eslint-plugin";
729
- import stylistic from "@stylistic/eslint-plugin";
730
- import importPluginX from "eslint-plugin-import-x";
731
- import { defineConfig as defineConfig3 } from "eslint/config";
732
- var base = defineConfig3([
733
- { extends: [...configs3["flat/base"]] },
734
- {
735
- files: [
736
- "**/*.js",
737
- "**/*.jsx",
738
- "**/*.cjs",
739
- "**/*.mjs",
740
- "**/*.ts",
741
- "**/*.tsx",
742
- "**/*.cts",
743
- "**/*.mts"
744
- ],
745
- extends: [importPluginX.flatConfigs.recommended]
746
- },
747
- {
748
- files: [
749
- "**/*.js",
750
- "**/*.jsx",
751
- "**/*.cjs",
752
- "**/*.mjs",
753
- "**/*.ts",
754
- "**/*.tsx",
755
- "**/*.cts",
756
- "**/*.mts"
757
- ],
758
- extends: [stylistic.configs.recommended]
759
- },
760
- {
761
- files: [
762
- "**/*.js",
763
- "**/*.jsx",
764
- "**/*.cjs",
765
- "**/*.mjs",
766
- "**/*.ts",
767
- "**/*.tsx",
768
- "**/*.cts",
769
- "**/*.mts"
770
- ],
771
- rules: {
772
- "no-param-reassign": "error",
773
- "no-alert": "error",
774
- "no-console": ["warn", { allow: ["warn", "error"] }],
775
- "no-debugger": "error",
776
- eqeqeq: ["error", "always", { null: "ignore" }],
777
- curly: ["error", "multi"],
778
- "prefer-const": ["error", { destructuring: "all" }],
779
- "no-var": "error",
780
- "object-shorthand": "error",
781
- "prefer-arrow-callback": "error",
782
- "no-useless-concat": "error",
783
- "prefer-template": "error",
784
- "no-nested-ternary": "error",
785
- "no-useless-assignment": "error",
786
- // NEW: Catch unused assignments (ESLint v9)
787
- "no-object-constructor": "error",
788
- // Disallow new Object() without arguments
789
- "no-else-return": ["error", { allowElseIf: false }],
790
- // Prefer early returns
791
- "no-multi-assign": "error",
792
- // Disallow chaining assignments
793
- radix: "error",
794
- // Require radix parameter for parseInt()
795
- yoda: "error",
796
- // Disallow yoda conditions (`if ("red" === color)`)
797
- complexity: ["warn", 10],
798
- // Warn if function complexity is high
799
- "max-depth": ["warn", 4],
800
- // Warn on deeply nested blocks
801
- "max-lines-per-function": [
802
- "warn",
803
- { max: 50, skipBlankLines: true, skipComments: true }
804
- ],
805
- // Warn on long functions
806
- "max-params": ["warn", 4],
1086
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-chains.ts
1087
+ import {
1088
+ Call,
1089
+ CombinedRecursiveAstVisitor as CombinedRecursiveAstVisitor2,
1090
+ ImplicitReceiver as ImplicitReceiver2,
1091
+ KeyedRead as KeyedRead3,
1092
+ PropertyRead as PropertyRead2,
1093
+ SafeKeyedRead as SafeKeyedRead3,
1094
+ SafePropertyRead as SafePropertyRead2,
1095
+ ThisReceiver as ThisReceiver2
1096
+ } from "@angular/compiler";
1097
+ var isPropertyReadLike = (node) => {
1098
+ const isPlainRead = node instanceof PropertyRead2;
1099
+ const isSafeRead = node instanceof SafePropertyRead2;
1100
+ return isPlainRead || isSafeRead;
1101
+ };
1102
+ var isRootRead = (read) => {
1103
+ const isImplicitReceiver = read.receiver instanceof ImplicitReceiver2;
1104
+ const isThisReceiver = read.receiver instanceof ThisReceiver2;
1105
+ return isImplicitReceiver || isThisReceiver;
1106
+ };
1107
+ var chainStep = (read) => {
1108
+ const receiver = read.receiver;
1109
+ const isReadReceiver = isPropertyReadLike(receiver);
1110
+ if (isReadReceiver) {
1111
+ const readStep = { called: false, next: receiver };
1112
+ return readStep;
1113
+ }
1114
+ const isCallReceiver = receiver instanceof Call;
1115
+ if (!isCallReceiver) return null;
1116
+ const target = receiver.receiver;
1117
+ const isReadLikeTarget = isPropertyReadLike(target);
1118
+ if (!isReadLikeTarget) return null;
1119
+ const calledStep = { called: true, next: target };
1120
+ return calledStep;
1121
+ };
1122
+ var readChain = (node) => {
1123
+ const leaf = { called: false, name: node.name };
1124
+ const names = [leaf];
1125
+ let current = node;
1126
+ while (!isRootRead(current)) {
1127
+ const step = chainStep(current);
1128
+ if (step === null) return null;
1129
+ current = step.next;
1130
+ names.unshift({ called: step.called, name: current.name });
1131
+ }
1132
+ if (!(current instanceof PropertyRead2)) return null;
1133
+ const chain = { names, root: current };
1134
+ return chain;
1135
+ };
1136
+ var ReadCollector2 = class extends CombinedRecursiveAstVisitor2 {
1137
+ reads = [];
1138
+ referenceOwners = /* @__PURE__ */ new Map();
1139
+ record(node) {
1140
+ const chain = readChain(node);
1141
+ if (chain) {
1142
+ this.reads.push(chain);
1143
+ }
1144
+ }
1145
+ recordReferences(owner) {
1146
+ for (const reference of owner.references) {
1147
+ this.referenceOwners.set(reference, owner);
1148
+ }
1149
+ }
1150
+ visitElement(element) {
1151
+ this.recordReferences(element);
1152
+ super.visitElement(element);
1153
+ }
1154
+ visitTemplate(template) {
1155
+ this.recordReferences(template);
1156
+ super.visitTemplate(template);
1157
+ }
1158
+ visitComponent(component) {
1159
+ this.recordReferences(component);
1160
+ super.visitComponent(component);
1161
+ }
1162
+ visitDirective(directive) {
1163
+ this.recordReferences(directive);
1164
+ super.visitDirective(directive);
1165
+ }
1166
+ visitBinary(node, context) {
1167
+ if (node.operation !== "=") return super.visitBinary(node, context);
1168
+ if (isReadTarget(node.left)) {
1169
+ this.visit(node.left.receiver);
1170
+ }
1171
+ if (node.left instanceof KeyedRead3 || node.left instanceof SafeKeyedRead3) {
1172
+ this.visit(node.left.key);
1173
+ }
1174
+ this.visit(node.right);
1175
+ return void 0;
1176
+ }
1177
+ visitPropertyRead(node, context) {
1178
+ this.record(node);
1179
+ return super.visitPropertyRead(node, context);
1180
+ }
1181
+ visitSafePropertyRead(node, context) {
1182
+ this.record(node);
1183
+ return super.visitSafePropertyRead(node, context);
1184
+ }
1185
+ };
1186
+
1187
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.ts
1188
+ import { TmplAstElement, createCssSelectorFromNode } from "@angular/compiler";
1189
+
1190
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/read-chain-text.util.ts
1191
+ var segmentText = (segment) => {
1192
+ const call = segment.called ? "()" : "";
1193
+ return `${segment.name}${call}`;
1194
+ };
1195
+ var chainText = (names) => {
1196
+ const texts = names.map(segmentText);
1197
+ return texts.join(".");
1198
+ };
1199
+
1200
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.ts
1201
+ var referenceTargets = (reference, owner, directives, scope) => {
1202
+ const exportAs = reference.value.trim();
1203
+ let targets = [];
1204
+ if (exportAs !== "") {
1205
+ const exportedTargets = directives.byExportAs.get(exportAs) ?? [];
1206
+ targets = [...exportedTargets];
1207
+ } else if (owner instanceof TmplAstElement) {
1208
+ directives.componentMatcher.match(
1209
+ createCssSelectorFromNode(owner),
1210
+ (_selector, declarations) => {
1211
+ targets.push(...declarations);
1212
+ }
1213
+ );
1214
+ }
1215
+ const scoped = scope !== null && !scope.some(
1216
+ (declaration) => directives.byDeclaration.get(declaration)?.hostDirectives
1217
+ );
1218
+ if (!scoped) return targets;
1219
+ const scopedTargets = targets.filter((target) => scope.includes(target));
1220
+ return scopedTargets;
1221
+ };
1222
+ var addReferenceRead = (context, reference, owner, names) => {
1223
+ const { checker, className, directives, fileName, scope, sink } = context;
1224
+ const targets = referenceTargets(reference, owner, directives, scope);
1225
+ const isTargetResolved = (target) => {
1226
+ const options = {
1227
+ allowMissingRoot: false,
1228
+ checker,
1229
+ declaration: target,
1230
+ names,
1231
+ sink
1232
+ };
1233
+ return addResolvedPath(options);
1234
+ };
1235
+ const resolved = targets.map(isTargetResolved);
1236
+ const hasResolvedTarget = resolved.includes(true);
1237
+ if (resolved.length === 0) return;
1238
+ if (hasResolvedTarget) return;
1239
+ const fallbackNames = names.map((segment) => segment.name);
1240
+ const reason = `${fileName}: cannot resolve '#${reference.name}.${chainText(names)}' in ${className}`;
1241
+ sink.addFallbackNames(fallbackNames, reason);
1242
+ };
1243
+
1244
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
1245
+ var identifierNames = (text2) => {
1246
+ const matches = text2.match(/[A-Za-z_$][\w$]*/gu) ?? [];
1247
+ return new Set(matches);
1248
+ };
1249
+ var templateBinder2 = new R3TargetBinder2(null);
1250
+ var unparsedTemplateReads = (context, source, error) => {
1251
+ const names = identifierNames(source);
1252
+ const message = error ?? "unknown error";
1253
+ const reason = `${context.fileName}: template of ${context.className} does not parse (${message})`;
1254
+ context.sink.addFallbackNames(names, reason);
1255
+ const unparsedReads = { usedDirectiveIndex: false };
1256
+ return unparsedReads;
1257
+ };
1258
+ var addComponentRead = (context, names) => {
1259
+ const { checker, className, declaration, fileName, sink } = context;
1260
+ const options = {
1261
+ allowMissingRoot: true,
1262
+ checker,
1263
+ declaration,
1264
+ names,
1265
+ sink
1266
+ };
1267
+ const isResolved = addResolvedPath(options);
1268
+ if (isResolved) return;
1269
+ const fallbackNames = names.map((segment) => segment.name);
1270
+ const reason = `${fileName}: cannot resolve '${chainText(names)}' on ${className}`;
1271
+ sink.addFallbackNames(fallbackNames, reason);
1272
+ };
1273
+ var chainEntity = (boundTarget, chain) => {
1274
+ const isThisRead = chain.root.receiver instanceof ThisReceiver3;
1275
+ if (isThisRead) return null;
1276
+ return boundTarget.getExpressionTarget(chain.root);
1277
+ };
1278
+ var addChainRead = (context, reads, boundTarget, chain) => {
1279
+ const entity = chainEntity(boundTarget, chain);
1280
+ if (entity === null) {
1281
+ addComponentRead(context, chain.names);
1282
+ return false;
1283
+ }
1284
+ if (!(entity instanceof TmplAstReference)) return false;
1285
+ const owner = reads.referenceOwners.get(entity);
1286
+ const names = chain.names.slice(1);
1287
+ addReferenceRead(context, entity, owner, names);
1288
+ return true;
1289
+ };
1290
+ var addTemplateReads = ({
1291
+ angularClass: angularClass2,
1292
+ checker,
1293
+ directives,
1294
+ fileName,
1295
+ sink,
1296
+ source
1297
+ }) => {
1298
+ const { declaration, scope } = angularClass2;
1299
+ const className = declaration.name?.text ?? "(anonymous)";
1300
+ const context = {
1301
+ checker,
1302
+ className,
1303
+ declaration,
1304
+ directives,
1305
+ fileName,
1306
+ scope,
1307
+ sink
1308
+ };
1309
+ const parsed = parseTemplate2(source, fileName);
1310
+ if (parsed.errors?.length) {
1311
+ const error = parsed.errors[0]?.msg;
1312
+ return unparsedTemplateReads(context, source, error);
1313
+ }
1314
+ const boundTarget = templateBinder2.bind({ template: parsed.nodes });
1315
+ const reads = new ReadCollector2();
1316
+ for (const node of parsed.nodes) {
1317
+ reads.visit(node);
1318
+ }
1319
+ let usedDirectiveIndex = false;
1320
+ for (const chain of reads.reads) {
1321
+ const usedIndex = addChainRead(context, reads, boundTarget, chain);
1322
+ usedDirectiveIndex ||= usedIndex;
1323
+ }
1324
+ const templateReads2 = { usedDirectiveIndex };
1325
+ return templateReads2;
1326
+ };
1327
+
1328
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
1329
+ var memberNameOf = (member) => {
1330
+ const name = member.name;
1331
+ if (!name) return [];
1332
+ const isIdentifierName = isIdentifier(name);
1333
+ const isStringName = isStringLiteralLike(name);
1334
+ const isTextName = isIdentifierName || isStringName;
1335
+ if (!isTextName) return [];
1336
+ const names = [name.text];
1337
+ return names;
1338
+ };
1339
+ var memberNames = (declaration) => {
1340
+ return declaration.members.flatMap(memberNameOf);
1341
+ };
1342
+ var unknownTemplateNames = ({ declaration, scope }, allNames) => {
1343
+ if (scope === null) return allNames;
1344
+ const scopeNames = scope.flatMap(memberNames);
1345
+ const names = [...memberNames(declaration), ...scopeNames];
1346
+ return names;
1347
+ };
1348
+ var templateFileVersion = (fileName) => {
1349
+ const { mtimeNs, size } = statSync2(fileName, { bigint: true });
1350
+ const version = { fileName, mtimeNs, size };
1351
+ return version;
1352
+ };
1353
+ var templateFileIsCurrent = (version) => {
1354
+ try {
1355
+ const current = statSync2(version.fileName, { bigint: true });
1356
+ return current.mtimeNs === version.mtimeNs && current.size === version.size;
1357
+ } catch {
1358
+ return false;
1359
+ }
1360
+ };
1361
+ var templateSourceOf = (angularClass2, allNames, sink, versions) => {
1362
+ const { declaration, template, valid } = angularClass2;
1363
+ const fileName = declaration.getSourceFile().fileName;
1364
+ const className = declaration.name?.text ?? "(anonymous)";
1365
+ if (!valid) {
1366
+ const names = unknownTemplateNames(angularClass2, allNames);
1367
+ const reason = `${fileName}: metadata of ${className} is not static (template, templateUrl)`;
1368
+ sink.addFallbackNames(names, reason);
1369
+ return null;
1370
+ }
1371
+ if (!template) return null;
1372
+ if (template.kind === "inline") {
1373
+ const inlineSource = { fileName, source: template.source };
1374
+ return inlineSource;
1375
+ }
1376
+ const templateFileName = template.fileName;
1377
+ try {
1378
+ versions.push(templateFileVersion(templateFileName));
1379
+ const source = readFileSync2(templateFileName, "utf8");
1380
+ const externalSource = {
1381
+ fileName: templateFileName,
1382
+ source
1383
+ };
1384
+ return externalSource;
1385
+ } catch {
1386
+ const names = unknownTemplateNames(angularClass2, allNames);
1387
+ const reason = `${fileName}: templateUrl of ${className} cannot be read (${templateFileName})`;
1388
+ sink.addFallbackNames(names, reason);
1389
+ return null;
1390
+ }
1391
+ };
1392
+ var collectAngularTemplateReads = ({
1393
+ allNames,
1394
+ checker,
1395
+ classes,
1396
+ directives,
1397
+ sink
1398
+ }) => {
1399
+ const templateVersions = [];
1400
+ let usedDirectiveIndex = false;
1401
+ for (const angularClass2 of classes) {
1402
+ const template = templateSourceOf(
1403
+ angularClass2,
1404
+ allNames,
1405
+ sink,
1406
+ templateVersions
1407
+ );
1408
+ if (template === null) continue;
1409
+ const result = addTemplateReads({
1410
+ angularClass: angularClass2,
1411
+ checker,
1412
+ directives,
1413
+ fileName: template.fileName,
1414
+ sink,
1415
+ source: template.source
1416
+ });
1417
+ usedDirectiveIndex ||= result.usedDirectiveIndex;
1418
+ }
1419
+ const templateReads2 = {
1420
+ templateVersions,
1421
+ usedDirectiveIndex
1422
+ };
1423
+ return templateReads2;
1424
+ };
1425
+
1426
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
1427
+ import {
1428
+ forEachChild,
1429
+ isClassLike,
1430
+ isElementAccessExpression,
1431
+ isPropertyAccessExpression
1432
+ } from "typescript";
1433
+
1434
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.ts
1435
+ import { SyntaxKind, isMethodSignature } from "typescript";
1436
+
1437
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.ts
1438
+ import {
1439
+ SymbolFlags,
1440
+ isBigIntLiteral,
1441
+ isIdentifier as isIdentifier2,
1442
+ isNumericLiteral,
1443
+ isPrivateIdentifier,
1444
+ isStringLiteralLike as isStringLiteralLike2
1445
+ } from "typescript";
1446
+ var aliasTarget = (checker, symbol) => {
1447
+ const isAlias = (symbol.flags & SymbolFlags.Alias) !== 0;
1448
+ if (!isAlias) return symbol;
1449
+ return checker.getAliasedSymbol(symbol);
1450
+ };
1451
+ var addSymbolDeclarations = (checker, symbol, sink) => {
1452
+ const resolved = aliasTarget(checker, symbol);
1453
+ for (const declaration of resolved.declarations ?? []) {
1454
+ sink.addDeclaration(declaration);
1455
+ }
1456
+ };
1457
+ var allPropertySymbols = (checker, type) => {
1458
+ const symbols = new Set(
1459
+ checker.getPropertiesOfType(checker.getApparentType(type))
1460
+ );
1461
+ const isUnionOrIntersection = type.isUnionOrIntersection();
1462
+ if (isUnionOrIntersection) {
1463
+ for (const member of type.types) {
1464
+ for (const symbol of allPropertySymbols(checker, member)) {
1465
+ symbols.add(symbol);
1466
+ }
1467
+ }
1468
+ }
1469
+ return [...symbols];
1470
+ };
1471
+ var namedPropertySymbols = (checker, type, names) => {
1472
+ if (names === null) return allPropertySymbols(checker, type);
1473
+ return names.flatMap((name) => symbolsForName(checker, type, name));
1474
+ };
1475
+ var addNamedProperties = (checker, type, names, sink) => {
1476
+ sink.addType(type);
1477
+ const symbols = namedPropertySymbols(checker, type, names);
1478
+ for (const symbol of symbols) {
1479
+ addSymbolDeclarations(checker, symbol, sink);
1480
+ }
1481
+ return symbols;
1482
+ };
1483
+ var literalPropertyNames = (type) => {
1484
+ const isStringLiteral = type.isStringLiteral();
1485
+ const isNumberLiteral = type.isNumberLiteral();
1486
+ const isLiteralType = isStringLiteral || isNumberLiteral;
1487
+ if (isLiteralType) return [String(type.value)];
1488
+ const isUnion = type.isUnion();
1489
+ if (!isUnion) return null;
1490
+ const names = [];
1491
+ for (const member of type.types) {
1492
+ const memberNames2 = literalPropertyNames(member);
1493
+ if (!memberNames2) return null;
1494
+ names.push(...memberNames2);
1495
+ }
1496
+ return names;
1497
+ };
1498
+ var propertyName2 = (checker, node) => {
1499
+ const isIdentifierName = isIdentifier2(node);
1500
+ const isPrivateName = isPrivateIdentifier(node);
1501
+ const isStringName = isStringLiteralLike2(node);
1502
+ const isNumericName = isNumericLiteral(node);
1503
+ const isBigIntName = isBigIntLiteral(node);
1504
+ const isLiteralName = isIdentifierName || isPrivateName || isStringName || isNumericName || isBigIntName;
1505
+ if (isLiteralName) return [node.text];
1506
+ return literalPropertyNames(checker.getTypeAtLocation(node.expression));
1507
+ };
1508
+
1509
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.ts
1510
+ var isAngularMethodSignature = (declaration) => {
1511
+ const isMethod = isMethodSignature(declaration);
1512
+ if (!isMethod) return false;
1513
+ const fileName = declaration.getSourceFile().fileName.replaceAll("\\", "/");
1514
+ return fileName.includes("/node_modules/@angular/");
1515
+ };
1516
+ var isAngularInterfaceMethod = (symbol) => {
1517
+ const declarations = symbol.declarations ?? [];
1518
+ return declarations.some(isAngularMethodSignature);
1519
+ };
1520
+ var addImplementations = (heritageType, classType, checker, sink) => {
1521
+ for (const interfaceMethod of heritageType.getProperties()) {
1522
+ const isInterfaceMethod = isAngularInterfaceMethod(interfaceMethod);
1523
+ if (!isInterfaceMethod) continue;
1524
+ const implementation = classType.getProperty(interfaceMethod.name);
1525
+ if (implementation) {
1526
+ addSymbolDeclarations(checker, implementation, sink);
1527
+ }
1528
+ }
1529
+ };
1530
+ var collectAngularInterfaceMethods = (node, checker, sink) => {
1531
+ const heritageClauses = node.heritageClauses ?? [];
1532
+ const implementsClauses = heritageClauses.filter(
1533
+ (clause) => clause.token === SyntaxKind.ImplementsKeyword
1534
+ );
1535
+ if (implementsClauses.length === 0) return;
1536
+ const classType = checker.getTypeAtLocation(node);
1537
+ for (const clause of implementsClauses) {
1538
+ for (const heritageType of clause.types) {
1539
+ const type = checker.getTypeAtLocation(heritageType);
1540
+ sink.addType(type);
1541
+ addImplementations(type, classType, checker, sink);
1542
+ }
1543
+ }
1544
+ };
1545
+
1546
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
1547
+ import {
1548
+ SyntaxKind as SyntaxKind2,
1549
+ isArrayLiteralExpression as isArrayLiteralExpression2,
1550
+ isBinaryExpression,
1551
+ isBindingElement,
1552
+ isObjectLiteralExpression as isObjectLiteralExpression2,
1553
+ isVariableDeclaration
1554
+ } from "typescript";
1555
+
1556
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.ts
1557
+ import {
1558
+ isArrayLiteralExpression,
1559
+ isObjectLiteralExpression,
1560
+ isOmittedExpression,
1561
+ isPropertyAssignment,
1562
+ isSpreadAssignment,
1563
+ isSpreadElement
1564
+ } from "typescript";
1565
+ var nestedPattern = (expression) => {
1566
+ const isArrayLiteral = isArrayLiteralExpression(expression);
1567
+ const isObjectLiteral = isObjectLiteralExpression(expression);
1568
+ const isNested = isArrayLiteral || isObjectLiteral;
1569
+ if (!isNested) return null;
1570
+ return expression;
1571
+ };
1572
+ var nestedPropertyPattern = (property) => {
1573
+ const isAssignment = isPropertyAssignment(property);
1574
+ if (!isAssignment) return null;
1575
+ return nestedPattern(property.initializer);
1576
+ };
1577
+ var arrayElementRead = (element, index) => {
1578
+ const isOmitted = isOmittedExpression(element);
1579
+ if (isOmitted) return null;
1580
+ const isRestElement = isSpreadElement(element);
1581
+ const indexNames = [String(index)];
1582
+ const names = isRestElement ? null : indexNames;
1583
+ const nested = nestedPattern(element);
1584
+ const read = {
1585
+ location: element,
1586
+ names,
1587
+ nested,
1588
+ rest: false
1589
+ };
1590
+ return read;
1591
+ };
1592
+ var propertyRead = (property, checker) => {
1593
+ const isRestProperty = isSpreadAssignment(property);
1594
+ if (isRestProperty) {
1595
+ const restRead = {
1596
+ location: property,
1597
+ names: null,
1598
+ nested: null,
1599
+ rest: true
1600
+ };
1601
+ return restRead;
1602
+ }
1603
+ const read = {
1604
+ location: property,
1605
+ names: propertyName2(checker, property.name),
1606
+ nested: nestedPropertyPattern(property),
1607
+ rest: false
1608
+ };
1609
+ return read;
1610
+ };
1611
+ var assignmentPatternElements = (pattern, checker) => {
1612
+ const isArrayPattern = isArrayLiteralExpression(pattern);
1613
+ if (isArrayPattern) {
1614
+ const reads = [];
1615
+ for (const [index, element] of pattern.elements.entries()) {
1616
+ const read = arrayElementRead(element, index);
1617
+ if (read !== null) {
1618
+ reads.push(read);
1619
+ }
1620
+ }
1621
+ return reads;
1622
+ }
1623
+ return pattern.properties.map((property) => propertyRead(property, checker));
1624
+ };
1625
+
1626
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.ts
1627
+ import {
1628
+ isArrayBindingPattern,
1629
+ isIdentifier as isIdentifier3,
1630
+ isObjectBindingPattern,
1631
+ isOmittedExpression as isOmittedExpression2
1632
+ } from "typescript";
1633
+ var isBindingPattern = (node) => {
1634
+ const isArrayPattern = isArrayBindingPattern(node);
1635
+ const isObjectPattern = isObjectBindingPattern(node);
1636
+ return isArrayPattern || isObjectPattern;
1637
+ };
1638
+ var nestedPattern2 = (element) => {
1639
+ const isNested = isBindingPattern(element.name);
1640
+ if (!isNested) return null;
1641
+ return element.name;
1642
+ };
1643
+ var bindingNames = (checker, element) => {
1644
+ if (element.propertyName) return propertyName2(checker, element.propertyName);
1645
+ const isIdentifierName = isIdentifier3(element.name);
1646
+ if (!isIdentifierName) return null;
1647
+ return [element.name.text];
1648
+ };
1649
+ var arrayElementRead2 = (element, index) => {
1650
+ const isOmitted = isOmittedExpression2(element);
1651
+ if (isOmitted) return null;
1652
+ const isRestElement = Boolean(element.dotDotDotToken);
1653
+ const indexNames = [String(index)];
1654
+ const names = isRestElement ? null : indexNames;
1655
+ const nested = nestedPattern2(element);
1656
+ const read = {
1657
+ location: element,
1658
+ names,
1659
+ nested,
1660
+ rest: false
1661
+ };
1662
+ return read;
1663
+ };
1664
+ var objectElementRead = (element, checker) => {
1665
+ const isRestElement = Boolean(element.dotDotDotToken);
1666
+ if (isRestElement) {
1667
+ const restRead = {
1668
+ location: element,
1669
+ names: null,
1670
+ nested: null,
1671
+ rest: true
1672
+ };
1673
+ return restRead;
1674
+ }
1675
+ const read = {
1676
+ location: element,
1677
+ names: bindingNames(checker, element),
1678
+ nested: nestedPattern2(element),
1679
+ rest: false
1680
+ };
1681
+ return read;
1682
+ };
1683
+ var bindingPatternElements = (pattern, checker) => {
1684
+ const isArrayPattern = isArrayBindingPattern(pattern);
1685
+ if (isArrayPattern) {
1686
+ const reads = [];
1687
+ for (const [index, element] of pattern.elements.entries()) {
1688
+ const read = arrayElementRead2(element, index);
1689
+ if (read !== null) {
1690
+ reads.push(read);
1691
+ }
1692
+ }
1693
+ return reads;
1694
+ }
1695
+ return pattern.elements.map((element) => objectElementRead(element, checker));
1696
+ };
1697
+
1698
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
1699
+ var lazyType = (compute) => {
1700
+ let type;
1701
+ return () => type ??= compute();
1702
+ };
1703
+ var isSkippable = (read, candidateNames) => {
1704
+ if (read.nested !== null) return false;
1705
+ if (read.names === null) return false;
1706
+ const hasCandidateName = read.names.some((name) => candidateNames.has(name));
1707
+ return !hasCandidateName;
1708
+ };
1709
+ var patternElements = (pattern, checker) => {
1710
+ const isBinding = isBindingPattern(pattern);
1711
+ if (isBinding) return bindingPatternElements(pattern, checker);
1712
+ return assignmentPatternElements(pattern, checker);
1713
+ };
1714
+ var addRestReads = (type, consumed, context) => {
1715
+ context.sink.addType(type);
1716
+ for (const symbol of allPropertySymbols(context.checker, type)) {
1717
+ const isConsumed = consumed.has(symbol.name);
1718
+ if (isConsumed) continue;
1719
+ addSymbolDeclarations(context.checker, symbol, context.sink);
1720
+ }
1721
+ };
1722
+ var collectPatternReads = (pattern, type, context) => {
1723
+ const { checker, sink, candidateNames } = context;
1724
+ const consumed = /* @__PURE__ */ new Set();
1725
+ const reads = patternElements(pattern, checker);
1726
+ for (const read of reads) {
1727
+ if (read.rest) {
1728
+ addRestReads(type(), consumed, context);
1729
+ continue;
1730
+ }
1731
+ for (const name of read.names ?? []) {
1732
+ consumed.add(name);
1733
+ }
1734
+ const skip = isSkippable(read, candidateNames);
1735
+ if (skip) continue;
1736
+ const symbols = addNamedProperties(checker, type(), read.names, sink);
1737
+ const nested = read.nested;
1738
+ if (nested === null) continue;
1739
+ for (const symbol of symbols) {
1740
+ const nestedType = lazyType(() => {
1741
+ return checker.getTypeOfSymbolAtLocation(symbol, read.location);
1742
+ });
1743
+ collectPatternReads(nested, nestedType, context);
1744
+ }
1745
+ }
1746
+ };
1747
+ var collectBindingReads = (node, context) => {
1748
+ const isBinding = isBindingPattern(node);
1749
+ if (!isBinding) return;
1750
+ const isBindingElementParent = isBindingElement(node.parent);
1751
+ const isNestedBindingName = isBindingElementParent && node.parent.name === node;
1752
+ if (isNestedBindingName) return;
1753
+ const declaration = node.parent;
1754
+ const isDeclaration = isVariableDeclaration(declaration);
1755
+ const isInitializedDeclaration = isDeclaration && declaration.initializer !== void 0;
1756
+ const source = isInitializedDeclaration ? declaration.initializer : declaration;
1757
+ const sourceTypeOf = () => context.checker.getTypeAtLocation(source);
1758
+ const sourceType = lazyType(sourceTypeOf);
1759
+ collectPatternReads(node, sourceType, context);
1760
+ };
1761
+ var collectAssignmentReads = (node, context) => {
1762
+ const isBinary = isBinaryExpression(node);
1763
+ if (!isBinary) return;
1764
+ const isEqualsAssignment = node.operatorToken.kind === SyntaxKind2.EqualsToken;
1765
+ if (!isEqualsAssignment) return;
1766
+ const isArrayTarget = isArrayLiteralExpression2(node.left);
1767
+ const isObjectTarget = isObjectLiteralExpression2(node.left);
1768
+ const isDestructuringAssignment = isArrayTarget || isObjectTarget;
1769
+ if (!isDestructuringAssignment) return;
1770
+ const rightTypeOf = () => context.checker.getTypeAtLocation(node.right);
1771
+ const sourceType = lazyType(rightTypeOf);
1772
+ collectPatternReads(node.left, sourceType, context);
1773
+ };
1774
+ var collectDestructuringReads = (node, checker, sink, candidateNames) => {
1775
+ const context = { checker, sink, candidateNames };
1776
+ collectBindingReads(node, context);
1777
+ collectAssignmentReads(node, context);
1778
+ };
1779
+
1780
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.ts
1781
+ import {
1782
+ SyntaxKind as SyntaxKind3,
1783
+ isArrayLiteralExpression as isArrayLiteralExpression3,
1784
+ isAsExpression,
1785
+ isBinaryExpression as isBinaryExpression2,
1786
+ isDeleteExpression,
1787
+ isForInStatement,
1788
+ isForOfStatement,
1789
+ isNonNullExpression,
1790
+ isObjectLiteralExpression as isObjectLiteralExpression3,
1791
+ isParenthesizedExpression,
1792
+ isPropertyAssignment as isPropertyAssignment2,
1793
+ isSatisfiesExpression,
1794
+ isSpreadAssignment as isSpreadAssignment2,
1795
+ isTypeAssertionExpression
1796
+ } from "typescript";
1797
+ var isExpressionWrapper = (parent, child) => {
1798
+ const isParenthesized = isParenthesizedExpression(parent);
1799
+ const isAs = isAsExpression(parent);
1800
+ const isTypeAssertion = isTypeAssertionExpression(parent);
1801
+ const isNonNull = isNonNullExpression(parent);
1802
+ const isSatisfies = isSatisfiesExpression(parent);
1803
+ const isCast = isAs || isTypeAssertion || isSatisfies;
1804
+ const isWrapper = isParenthesized || isNonNull || isCast;
1805
+ if (!isWrapper) return false;
1806
+ return parent.expression === child;
1807
+ };
1808
+ var isPatternContainer = (parent, child) => {
1809
+ const isArrayLiteral = isArrayLiteralExpression3(parent);
1810
+ if (isArrayLiteral) {
1811
+ return parent.elements.some((element) => element === child);
1812
+ }
1813
+ const isObjectLiteral = isObjectLiteralExpression3(parent);
1814
+ if (isObjectLiteral) {
1815
+ return parent.properties.some((property) => property === child);
1816
+ }
1817
+ const isAssignment = isPropertyAssignment2(parent);
1818
+ if (isAssignment) return parent.initializer === child;
1819
+ const isSpread = isSpreadAssignment2(parent);
1820
+ if (!isSpread) return false;
1821
+ return parent.expression === child;
1822
+ };
1823
+ var isWriteTarget = (parent, current) => {
1824
+ const isBinary = isBinaryExpression2(parent);
1825
+ const isBinaryTarget = isBinary && parent.left === current && parent.operatorToken.kind === SyntaxKind3.EqualsToken;
1826
+ if (isBinaryTarget) return true;
1827
+ const isDelete = isDeleteExpression(parent);
1828
+ const isDeleteTarget = isDelete && parent.expression === current;
1829
+ if (isDeleteTarget) return true;
1830
+ const isForIn = isForInStatement(parent);
1831
+ const isForOf = isForOfStatement(parent);
1832
+ const isIteration = isForIn || isForOf;
1833
+ return isIteration && parent.initializer === current;
1834
+ };
1835
+ var isWriteOnly2 = (node) => {
1836
+ let current = node;
1837
+ let parent = node.parent;
1838
+ while (isExpressionWrapper(parent, current) || isPatternContainer(parent, current)) {
1839
+ current = parent;
1840
+ parent = parent.parent;
1841
+ }
1842
+ return isWriteTarget(parent, current);
1843
+ };
1844
+
1845
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
1846
+ var addPropertyAccessRead = (node, checker, sink, candidateNames) => {
1847
+ const isCandidateName = candidateNames.has(node.name.text);
1848
+ if (!isCandidateName) return;
1849
+ const isWriteTarget2 = isWriteOnly2(node);
1850
+ if (isWriteTarget2) return;
1851
+ sink.addType(checker.getTypeAtLocation(node.expression));
1852
+ const symbol = checker.getSymbolAtLocation(node.name);
1853
+ if (symbol) {
1854
+ addSymbolDeclarations(checker, symbol, sink);
1855
+ }
1856
+ };
1857
+ var addElementAccessRead = (node, checker, sink, candidateNames) => {
1858
+ const isWriteTarget2 = isWriteOnly2(node);
1859
+ if (isWriteTarget2) return;
1860
+ const argumentType = checker.getTypeAtLocation(node.argumentExpression);
1861
+ const names = literalPropertyNames(argumentType);
1862
+ if (names) {
1863
+ const hasCandidateName = names.some((name) => candidateNames.has(name));
1864
+ if (!hasCandidateName) return;
1865
+ }
1866
+ const targetType = checker.getTypeAtLocation(node.expression);
1867
+ addNamedProperties(checker, targetType, names, sink);
1868
+ };
1869
+ var collectTypeScriptReads = (sourceFile, checker, sink, candidateNames) => {
1870
+ const visit = (node) => {
1871
+ const isPropertyAccess = isPropertyAccessExpression(node);
1872
+ const isElementAccess = isElementAccessExpression(node);
1873
+ const isClass = isClassLike(node);
1874
+ if (isPropertyAccess) {
1875
+ addPropertyAccessRead(node, checker, sink, candidateNames);
1876
+ } else if (isElementAccess) {
1877
+ addElementAccessRead(node, checker, sink, candidateNames);
1878
+ } else if (isClass) {
1879
+ collectAngularInterfaceMethods(node, checker, sink);
1880
+ }
1881
+ collectDestructuringReads(node, checker, sink, candidateNames);
1882
+ forEachChild(node, visit);
1883
+ };
1884
+ visit(sourceFile);
1885
+ };
1886
+
1887
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-type-dependencies.ts
1888
+ import { ObjectFlags, TypeFlags as TypeFlags2 } from "typescript";
1889
+ var isObjectType = (type) => {
1890
+ return (type.flags & TypeFlags2.Object) !== 0;
1891
+ };
1892
+ var isTypeReference = (type) => {
1893
+ return (type.objectFlags & ObjectFlags.Reference) !== 0;
1894
+ };
1895
+ var isInterfaceType = (type) => {
1896
+ return (type.objectFlags & ObjectFlags.ClassOrInterface) !== 0;
1897
+ };
1898
+ var addDeclarationFiles = (type, dependencies) => {
1899
+ for (const symbol of [type.getSymbol(), type.aliasSymbol]) {
1900
+ const declarations = symbol?.declarations ?? [];
1901
+ for (const declaration of declarations) {
1902
+ dependencies.add(declaration.getSourceFile());
1903
+ }
1904
+ }
1905
+ };
1906
+ var baseTypesOf = (checker, type) => {
1907
+ const isObject = isObjectType(type);
1908
+ if (!isObject) return [];
1909
+ const isReference = isTypeReference(type);
1910
+ const target = isReference ? type.target : type;
1911
+ const isClassOrInterface = isInterfaceType(target);
1912
+ if (!isClassOrInterface) return [];
1913
+ return checker.getBaseTypes(target);
1914
+ };
1915
+ var addTypeDependencies = (checker, type, dependencies, seen) => {
1916
+ const isSeen = seen.has(type);
1917
+ if (isSeen) return;
1918
+ seen.add(type);
1919
+ const isUnionOrIntersection = type.isUnionOrIntersection();
1920
+ if (isUnionOrIntersection) {
1921
+ for (const member of type.types) {
1922
+ addTypeDependencies(checker, member, dependencies, seen);
1923
+ }
1924
+ return;
1925
+ }
1926
+ addDeclarationFiles(type, dependencies);
1927
+ const isTypeParameter = (type.flags & TypeFlags2.TypeParameter) !== 0;
1928
+ if (isTypeParameter) {
1929
+ const constraint = checker.getBaseConstraintOfType(type);
1930
+ if (constraint) {
1931
+ addTypeDependencies(checker, constraint, dependencies, seen);
1932
+ }
1933
+ return;
1934
+ }
1935
+ for (const base2 of baseTypesOf(checker, type)) {
1936
+ addTypeDependencies(checker, base2, dependencies, seen);
1937
+ }
1938
+ };
1939
+
1940
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.ts
1941
+ var failureText = (error) => {
1942
+ if (error instanceof Error) return error.message;
1943
+ return String(error);
1944
+ };
1945
+ var collectFileReads = (index, sourceFile, checker, sink) => {
1946
+ try {
1947
+ collectTypeScriptReads(sourceFile, checker, sink, index.candidateNames);
1948
+ const classes = index.classes.get(sourceFile.fileName)?.classes ?? [];
1949
+ return collectAngularTemplateReads({
1950
+ allNames: index.candidateNames,
1951
+ checker,
1952
+ classes,
1953
+ directives: index.directives,
1954
+ sink
1955
+ });
1956
+ } catch (error) {
1957
+ const errorMessage = failureText(error);
1958
+ sink.addFallbackNames(
1959
+ index.candidateNames,
1960
+ `${sourceFile.fileName}: indexing failed (${errorMessage})`
1961
+ );
1962
+ const noTemplateReads = {
1963
+ templateVersions: [],
1964
+ usedDirectiveIndex: false
1965
+ };
1966
+ return noTemplateReads;
1967
+ }
1968
+ };
1969
+ var computeEntry = (index, sourceFile, checker) => {
1970
+ const declarations = /* @__PURE__ */ new Set();
1971
+ const dependencies = /* @__PURE__ */ new Set();
1972
+ const fallbackNames = /* @__PURE__ */ new Set();
1973
+ const seenTypes = /* @__PURE__ */ new Set();
1974
+ const sink = {
1975
+ addDeclaration: (declaration) => {
1976
+ declarations.add(declaration);
1977
+ dependencies.add(declaration.getSourceFile());
1978
+ },
1979
+ addFallbackNames: (names) => {
1980
+ for (const name of names) {
1981
+ fallbackNames.add(name);
1982
+ }
1983
+ },
1984
+ addType: (type) => {
1985
+ addTypeDependencies(checker, type, dependencies, seenTypes);
1986
+ }
1987
+ };
1988
+ const reads = collectFileReads(index, sourceFile, checker, sink);
1989
+ dependencies.delete(sourceFile);
1990
+ const fileEntry = {
1991
+ ...reads,
1992
+ declarations,
1993
+ dependencies,
1994
+ fallbackNames,
1995
+ sourceFile
1996
+ };
1997
+ return fileEntry;
1998
+ };
1999
+
2000
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.ts
2001
+ import {
2002
+ canHaveDecorators as canHaveDecorators2,
2003
+ forEachChild as forEachChild2,
2004
+ getDecorators as getDecorators2,
2005
+ isCallExpression as isCallExpression2,
2006
+ isClassLike as isClassLike3,
2007
+ isObjectLiteralExpression as isObjectLiteralExpression4
2008
+ } from "typescript";
2009
+
2010
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.ts
2011
+ import {
2012
+ SyntaxKind as SyntaxKind4,
2013
+ isArrayLiteralExpression as isArrayLiteralExpression4,
2014
+ isClassLike as isClassLike2,
2015
+ isIdentifier as isIdentifier5,
2016
+ isPropertyAccessExpression as isPropertyAccessExpression3
2017
+ } from "typescript";
2018
+
2019
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-decorator-kind.ts
2020
+ import {
2021
+ SymbolFlags as SymbolFlags2,
2022
+ canHaveDecorators,
2023
+ getDecorators,
2024
+ isCallExpression,
2025
+ isImportDeclaration,
2026
+ isStringLiteralLike as isStringLiteralLike3
2027
+ } from "typescript";
2028
+ var decoratorKinds = [
2029
+ "Component",
2030
+ "Directive",
2031
+ "NgModule",
2032
+ "Pipe"
2033
+ ];
2034
+ var isDecoratorKind = (name) => {
2035
+ return decoratorKinds.some((kind) => kind === name);
2036
+ };
2037
+ var isAngularCoreFile = (declaration) => {
2038
+ const fileName = declaration.getSourceFile().fileName.replaceAll("\\", "/");
2039
+ return fileName.includes("/node_modules/@angular/core/");
2040
+ };
2041
+ var parentOf2 = (node) => {
2042
+ return node.parent;
2043
+ };
2044
+ var importedFromAngularCore = (declaration) => {
2045
+ let current = parentOf2(declaration);
2046
+ while (current) {
2047
+ if (isImportDeclaration(current)) {
2048
+ return isStringLiteralLike3(current.moduleSpecifier) && current.moduleSpecifier.text === "@angular/core";
2049
+ }
2050
+ current = parentOf2(current);
2051
+ }
2052
+ return false;
2053
+ };
2054
+ var resolveAlias = (symbol, discovery) => {
2055
+ let current = symbol;
2056
+ for (let hop = 0; hop < 32; hop += 1) {
2057
+ for (const declaration of current.declarations ?? []) {
2058
+ discovery.dependencies.add(declaration.getSourceFile());
2059
+ }
2060
+ if ((current.flags & SymbolFlags2.Alias) === 0) return current;
2061
+ const next = discovery.checker.getImmediateAliasedSymbol(current);
2062
+ if (!next || next === current) return current;
2063
+ current = next;
2064
+ }
2065
+ return current;
2066
+ };
2067
+ var angularDecoratorKind = (expression, discovery) => {
2068
+ const unresolved = discovery.checker.getSymbolAtLocation(expression);
2069
+ if (!unresolved) return null;
2070
+ const symbol = resolveAlias(unresolved, discovery);
2071
+ const name = symbol.getName();
2072
+ const isKind = isDecoratorKind(name);
2073
+ if (!isKind) return null;
2074
+ const unresolvedDeclarations = unresolved.declarations ?? [];
2075
+ const symbolDeclarations = symbol.declarations ?? [];
2076
+ const isCoreImport = unresolvedDeclarations.some(importedFromAngularCore);
2077
+ const isCoreDeclaration = symbolDeclarations.some(isAngularCoreFile);
2078
+ const fromAngularCore = isCoreImport || isCoreDeclaration;
2079
+ if (!fromAngularCore) return null;
2080
+ return name;
2081
+ };
2082
+ var classDecoratorKind = (declaration, discovery) => {
2083
+ const hasDecorators = canHaveDecorators(declaration);
2084
+ if (!hasDecorators) return null;
2085
+ for (const decorator of getDecorators(declaration) ?? []) {
2086
+ const isDecoratorCall = isCallExpression(decorator.expression);
2087
+ if (!isDecoratorCall) continue;
2088
+ const kind = angularDecoratorKind(
2089
+ decorator.expression.expression,
2090
+ discovery
2091
+ );
2092
+ if (kind !== null) return kind;
2093
+ }
2094
+ return null;
2095
+ };
2096
+
2097
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.ts
2098
+ import { dirname as dirname2, resolve as resolve2 } from "node:path";
2099
+ import {
2100
+ isIdentifier as isIdentifier4,
2101
+ isNumericLiteral as isNumericLiteral2,
2102
+ isPropertyAccessExpression as isPropertyAccessExpression2,
2103
+ isPropertyAssignment as isPropertyAssignment3,
2104
+ isStringLiteralLike as isStringLiteralLike4
2105
+ } from "typescript";
2106
+ var stringValue = (node, discovery) => {
2107
+ if (!node) return null;
2108
+ const isStringLiteral = isStringLiteralLike4(node);
2109
+ if (isStringLiteral) return node.text;
2110
+ const isIdentifierNode = isIdentifier4(node);
2111
+ const isPropertyAccessNode = isPropertyAccessExpression2(node);
2112
+ if (isIdentifierNode || isPropertyAccessNode) {
2113
+ const symbol = discovery.checker.getSymbolAtLocation(
2114
+ isIdentifier4(node) ? node : node.name
2115
+ );
2116
+ if (symbol) {
2117
+ resolveAlias(symbol, discovery);
2118
+ }
2119
+ }
2120
+ const type = discovery.checker.getTypeAtLocation(node);
2121
+ return type.isStringLiteral() ? type.value : null;
2122
+ };
2123
+ var metadataProperty = (metadata, name) => {
2124
+ for (const property of metadata.properties) {
2125
+ const isAssignment = isPropertyAssignment3(property);
2126
+ if (!isAssignment) continue;
2127
+ const isIdentifierName = isIdentifier4(property.name);
2128
+ const isStringName = isStringLiteralLike4(property.name);
2129
+ const isNumericName = isNumericLiteral2(property.name);
2130
+ const isLiteralName = isIdentifierName || isStringName || isNumericName;
2131
+ const isPropertyNamed = isLiteralName && property.name.text === name;
2132
+ if (isPropertyNamed) return property.initializer;
2133
+ }
2134
+ return void 0;
2135
+ };
2136
+ var templateOf = (metadata, fileName, discovery) => {
2137
+ const inline = metadataProperty(metadata, "template");
2138
+ if (inline) {
2139
+ const source = stringValue(inline, discovery);
2140
+ if (source === null) return void 0;
2141
+ const inlineTemplate = { kind: "inline", source };
2142
+ return inlineTemplate;
2143
+ }
2144
+ const url = metadataProperty(metadata, "templateUrl");
2145
+ if (url) {
2146
+ const text2 = stringValue(url, discovery);
2147
+ if (text2 === null) return void 0;
2148
+ const externalFileName = resolve2(dirname2(fileName), text2);
2149
+ const externalTemplate = {
2150
+ fileName: externalFileName,
2151
+ kind: "external"
2152
+ };
2153
+ return externalTemplate;
2154
+ }
2155
+ return null;
2156
+ };
2157
+ var exportAsOf = (metadata, discovery) => {
2158
+ const exportAsProperty = metadataProperty(metadata, "exportAs");
2159
+ const exportAs = stringValue(exportAsProperty, discovery) ?? "";
2160
+ const names = exportAs.split(",").map((name) => name.trim());
2161
+ return names.filter(Boolean);
2162
+ };
2163
+
2164
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.ts
2165
+ var booleanValue = (node) => {
2166
+ if (!node) return void 0;
2167
+ if (node.kind === SyntaxKind4.TrueKeyword) return true;
2168
+ if (node.kind === SyntaxKind4.FalseKeyword) return false;
2169
+ return void 0;
2170
+ };
2171
+ var classesOf = (symbol, discovery) => {
2172
+ const resolved = resolveAlias(symbol, discovery);
2173
+ const declarations = resolved.declarations ?? [];
2174
+ return declarations.filter(isClassLike2);
2175
+ };
2176
+ var scopedClasses = (classes, discovery) => {
2177
+ const scoped = [];
2178
+ for (const declaration of classes) {
2179
+ if (declaration.getSourceFile().isDeclarationFile) continue;
2180
+ const kind = classDecoratorKind(declaration, discovery);
2181
+ if (kind === "Pipe") continue;
2182
+ if (kind !== "Component" && kind !== "Directive") return null;
2183
+ scoped.push(declaration);
2184
+ }
2185
+ return scoped;
2186
+ };
2187
+ var importedClasses = (element, discovery) => {
2188
+ const isIdentifierElement = isIdentifier5(element);
2189
+ const isPropertyAccessElement = isPropertyAccessExpression3(element);
2190
+ if (!isIdentifierElement && !isPropertyAccessElement) return null;
2191
+ const target = isIdentifierElement ? element : element.name;
2192
+ const symbol = discovery.checker.getSymbolAtLocation(target);
2193
+ if (!symbol) return null;
2194
+ const classes = classesOf(symbol, discovery);
2195
+ if (classes.length === 0) return null;
2196
+ return scopedClasses(classes, discovery);
2197
+ };
2198
+ var scopeOf = (metadata, discovery) => {
2199
+ const standalone = booleanValue(metadataProperty(metadata, "standalone"));
2200
+ const imports = metadataProperty(metadata, "imports");
2201
+ const isModuleDeclared = standalone === void 0 && !imports;
2202
+ if (standalone === false || isModuleDeclared) return null;
2203
+ if (!imports) return [];
2204
+ const isImportsArray = isArrayLiteralExpression4(imports);
2205
+ if (!isImportsArray) return null;
2206
+ const scope = [];
2207
+ for (const element of imports.elements) {
2208
+ const classes = importedClasses(element, discovery);
2209
+ if (classes === null) return null;
2210
+ scope.push(...classes);
2211
+ }
2212
+ return scope;
2213
+ };
2214
+
2215
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.ts
2216
+ var describedClass = (base2, metadata, discovery) => {
2217
+ const selector = metadataProperty(metadata, "selector");
2218
+ const described = {
2219
+ ...base2,
2220
+ exportAs: exportAsOf(metadata, discovery),
2221
+ hostDirectives: metadataProperty(metadata, "hostDirectives") !== void 0,
2222
+ selector: stringValue(selector, discovery)
2223
+ };
2224
+ if (!base2.component) {
2225
+ const directiveDiscovery = { ...described, valid: true };
2226
+ return directiveDiscovery;
2227
+ }
2228
+ const fileName = base2.declaration.getSourceFile().fileName;
2229
+ const template = templateOf(metadata, fileName, discovery);
2230
+ const componentDiscovery = {
2231
+ ...described,
2232
+ scope: scopeOf(metadata, discovery),
2233
+ template: template ?? null,
2234
+ valid: template !== void 0
2235
+ };
2236
+ return componentDiscovery;
2237
+ };
2238
+ var decoratedClass = (declaration, call, discovery) => {
2239
+ const kind = angularDecoratorKind(call.expression, discovery);
2240
+ if (kind !== "Component" && kind !== "Directive") return null;
2241
+ const component = kind === "Component";
2242
+ const base2 = {
2243
+ component,
2244
+ declaration,
2245
+ exportAs: [],
2246
+ hostDirectives: false,
2247
+ name: declaration.name?.text ?? "",
2248
+ scope: null,
2249
+ selector: null,
2250
+ template: null
2251
+ };
2252
+ const metadata = call.arguments.at(0);
2253
+ if (metadata === void 0) {
2254
+ const bareDiscovery = { ...base2, valid: !component };
2255
+ return bareDiscovery;
2256
+ }
2257
+ const isMetadataObject = isObjectLiteralExpression4(metadata);
2258
+ if (!isMetadataObject) {
2259
+ const invalidDiscovery = { ...base2, valid: false };
2260
+ return invalidDiscovery;
2261
+ }
2262
+ return describedClass(base2, metadata, discovery);
2263
+ };
2264
+ var angularClass = (declaration, discovery) => {
2265
+ const hasDecorators = canHaveDecorators2(declaration);
2266
+ if (!hasDecorators) return null;
2267
+ for (const decorator of getDecorators2(declaration) ?? []) {
2268
+ const isDecoratorCall = isCallExpression2(decorator.expression);
2269
+ if (!isDecoratorCall) continue;
2270
+ const found = decoratedClass(declaration, decorator.expression, discovery);
2271
+ if (found) return found;
2272
+ }
2273
+ return null;
2274
+ };
2275
+ var angularClasses = (sourceFile, checker) => {
2276
+ const classes = [];
2277
+ const discovery = { checker, dependencies: /* @__PURE__ */ new Set() };
2278
+ const visit = (node) => {
2279
+ const isClass = isClassLike3(node);
2280
+ if (isClass) {
2281
+ const found = angularClass(node, discovery);
2282
+ if (found) {
2283
+ classes.push(found);
2284
+ }
2285
+ }
2286
+ forEachChild2(node, visit);
2287
+ };
2288
+ visit(sourceFile);
2289
+ discovery.dependencies.delete(sourceFile);
2290
+ const discovered = {
2291
+ classes,
2292
+ dependencies: discovery.dependencies
2293
+ };
2294
+ return discovered;
2295
+ };
2296
+
2297
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.util.ts
2298
+ var specFilePattern = /\.spec\.[cm]?[tj]sx?$/u;
2299
+ var isSpecFile = (fileName) => {
2300
+ return specFilePattern.test(fileName);
2301
+ };
2302
+
2303
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.ts
2304
+ var typeScriptFilePattern = /\.(?:[cm]?ts|tsx)$/u;
2305
+ var isIndexable = (program, sourceFile) => {
2306
+ if (sourceFile.isDeclarationFile) return false;
2307
+ const isExternal = program.isSourceFileFromExternalLibrary(sourceFile);
2308
+ if (isExternal) return false;
2309
+ const isSpec = isSpecFile(sourceFile.fileName);
2310
+ if (isSpec) return false;
2311
+ return typeScriptFilePattern.test(sourceFile.fileName);
2312
+ };
2313
+ var indexableSourceFiles = (program) => {
2314
+ const sourceFiles = program.getSourceFiles();
2315
+ return sourceFiles.filter((sourceFile) => isIndexable(program, sourceFile));
2316
+ };
2317
+ var sourceFileMaps = (program) => {
2318
+ const all = /* @__PURE__ */ new Map();
2319
+ const current = /* @__PURE__ */ new Map();
2320
+ for (const sourceFile of program.getSourceFiles()) {
2321
+ all.set(sourceFile.fileName, sourceFile);
2322
+ }
2323
+ for (const sourceFile of indexableSourceFiles(program)) {
2324
+ current.set(sourceFile.fileName, sourceFile);
2325
+ }
2326
+ const maps = { all, current };
2327
+ return maps;
2328
+ };
2329
+ var dropEntries = (index, stale) => {
2330
+ for (const [fileName, entry] of index.entries) {
2331
+ const isStale = stale(entry);
2332
+ if (isStale) {
2333
+ index.entries.delete(fileName);
2334
+ }
2335
+ }
2336
+ };
2337
+ var isReplaced = (sourceFile, dependencies, maps) => {
2338
+ const indexedSourceFile = maps.current.get(sourceFile.fileName);
2339
+ if (indexedSourceFile !== sourceFile) return true;
2340
+ for (const dependency of dependencies) {
2341
+ const indexedDependency = maps.all.get(dependency.fileName);
2342
+ if (indexedDependency !== dependency) return true;
2343
+ }
2344
+ return false;
2345
+ };
2346
+ var dropReplacedEntries = (index, maps) => {
2347
+ const isEntryReplaced = (entry) => {
2348
+ return isReplaced(entry.sourceFile, entry.dependencies, maps);
2349
+ };
2350
+ dropEntries(index, isEntryReplaced);
2351
+ };
2352
+ var dropEntriesMentioning = (index, names) => {
2353
+ if (names.size === 0) return;
2354
+ if (index.entries.size === 0) return;
2355
+ const nameList = [...names];
2356
+ const mentionsName = (entry) => {
2357
+ return nameList.some((name) => entry.sourceFile.text.includes(name));
2358
+ };
2359
+ dropEntries(index, mentionsName);
2360
+ };
2361
+ var templateVersionChecker = () => {
2362
+ const checked = /* @__PURE__ */ new Map();
2363
+ return (version) => {
2364
+ const key2 = `${version.fileName}\0${version.mtimeNs}\0${version.size}`;
2365
+ const known = checked.get(key2);
2366
+ if (known !== void 0) return known;
2367
+ const current = templateFileIsCurrent(version);
2368
+ checked.set(key2, current);
2369
+ return current;
2370
+ };
2371
+ };
2372
+ var dropStaleTemplateEntries = (index, force) => {
2373
+ const now = performance.now();
2374
+ const sinceLastCheck = now - index.templateCheckedAt;
2375
+ const isThrottled = !force && sinceLastCheck < index.templateCheckDuration * 100;
2376
+ if (isThrottled) return;
2377
+ const isCurrent = templateVersionChecker();
2378
+ const hasStaleTemplate = (entry) => {
2379
+ return !entry.templateVersions.every(isCurrent);
2380
+ };
2381
+ dropEntries(index, hasStaleTemplate);
2382
+ index.templateCheckedAt = now;
2383
+ index.templateCheckDuration = performance.now() - now;
2384
+ };
2385
+
2386
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-candidate-names.ts
2387
+ import {
2388
+ canHaveDecorators as canHaveDecorators3,
2389
+ forEachChild as forEachChild3,
2390
+ getDecorators as getDecorators3,
2391
+ isClassLike as isClassLike4,
2392
+ isIdentifier as isIdentifier6,
2393
+ isStringLiteralLike as isStringLiteralLike5
2394
+ } from "typescript";
2395
+ var isDecoratedClass = (node) => {
2396
+ const isDecoratable = canHaveDecorators3(node);
2397
+ if (!isDecoratable) return false;
2398
+ const decorators = getDecorators3(node);
2399
+ const decoratorCount = decorators?.length ?? 0;
2400
+ return decoratorCount > 0;
2401
+ };
2402
+ var addMemberNames = (node, names) => {
2403
+ const isClass = isClassLike4(node);
2404
+ if (!isClass) return;
2405
+ const isDecorated = isDecoratedClass(node);
2406
+ if (!isDecorated) return;
2407
+ for (const member of node.members) {
2408
+ if (!member.name) continue;
2409
+ const isIdentifierName = isIdentifier6(member.name);
2410
+ const isStringName = isStringLiteralLike5(member.name);
2411
+ const isNamedMember = isIdentifierName || isStringName;
2412
+ if (isNamedMember) {
2413
+ names.add(member.name.text);
2414
+ }
2415
+ }
2416
+ };
2417
+ var collectCandidateNames = (sourceFile) => {
2418
+ const names = /* @__PURE__ */ new Set();
2419
+ const visit = (node) => {
2420
+ addMemberNames(node, names);
2421
+ forEachChild3(node, visit);
2422
+ };
2423
+ visit(sourceFile);
2424
+ return names;
2425
+ };
2426
+
2427
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.ts
2428
+ var dropReplacedClasses = (index, maps) => {
2429
+ let changed = false;
2430
+ for (const [fileName, fileClasses] of index.classes) {
2431
+ const replaced = isReplaced(
2432
+ fileClasses.sourceFile,
2433
+ fileClasses.dependencies,
2434
+ maps
2435
+ );
2436
+ if (!replaced) continue;
2437
+ index.classes.delete(fileName);
2438
+ changed = true;
2439
+ }
2440
+ return changed;
2441
+ };
2442
+ var addCandidateNames = (index, candidateNames, newNames) => {
2443
+ for (const name of candidateNames) {
2444
+ const isKnownName = index.candidateNames.has(name);
2445
+ if (isKnownName) continue;
2446
+ index.candidateNames.add(name);
2447
+ newNames.add(name);
2448
+ }
2449
+ };
2450
+ var indexFileClasses = (index, sourceFile, checker, newNames) => {
2451
+ const candidateNames = collectCandidateNames(sourceFile);
2452
+ addCandidateNames(index, candidateNames, newNames);
2453
+ const { classes, dependencies } = angularClasses(sourceFile, checker());
2454
+ const fileClasses = {
2455
+ candidateNames,
2456
+ classes,
2457
+ dependencies,
2458
+ sourceFile
2459
+ };
2460
+ index.classes.set(sourceFile.fileName, fileClasses);
2461
+ index.entries.delete(sourceFile.fileName);
2462
+ };
2463
+ var indexNewClasses = (index, current, checker, newNames) => {
2464
+ let changed = false;
2465
+ for (const [fileName, sourceFile] of current) {
2466
+ const isIndexed = index.classes.has(fileName);
2467
+ if (isIndexed) continue;
2468
+ indexFileClasses(index, sourceFile, checker, newNames);
2469
+ changed = true;
2470
+ }
2471
+ return changed;
2472
+ };
2473
+ var rebuildDirectives = (index) => {
2474
+ const fileClasses = [...index.classes.values()];
2475
+ const classes = fileClasses.flatMap((entry) => entry.classes);
2476
+ const shape = directiveShape(classes);
2477
+ index.directives = buildDirectiveIndex(classes);
2478
+ if (shape === index.directiveShape) return;
2479
+ index.directiveShape = shape;
2480
+ dropEntries(index, (entry) => entry.usedDirectiveIndex);
2481
+ };
2482
+ var reconcileClasses = (index, maps, checker) => {
2483
+ const newNames = /* @__PURE__ */ new Set();
2484
+ const droppedClasses = dropReplacedClasses(index, maps);
2485
+ const addedClasses = indexNewClasses(index, maps.current, checker, newNames);
2486
+ const changed = droppedClasses || addedClasses;
2487
+ if (changed) {
2488
+ rebuildDirectives(index);
2489
+ }
2490
+ return newNames;
2491
+ };
2492
+
2493
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.ts
2494
+ var lazyChecker = (program) => {
2495
+ let typeChecker;
2496
+ return () => typeChecker ??= program.getTypeChecker();
2497
+ };
2498
+ var indexMissingEntries = (index, sourceFiles, checker) => {
2499
+ for (const sourceFile of sourceFiles) {
2500
+ const isEntryIndexed = index.entries.has(sourceFile.fileName);
2501
+ if (isEntryIndexed) continue;
2502
+ const entry = computeEntry(index, sourceFile, checker());
2503
+ index.entries.set(sourceFile.fileName, entry);
2504
+ }
2505
+ };
2506
+ var reindexProgram = (index, program, checker) => {
2507
+ const maps = sourceFileMaps(program);
2508
+ dropReplacedEntries(index, maps);
2509
+ const newNames = reconcileClasses(index, maps, checker);
2510
+ dropEntriesMentioning(index, newNames);
2511
+ index.program = program;
2512
+ index.usage = void 0;
2513
+ dropStaleTemplateEntries(index, true);
2514
+ indexMissingEntries(index, maps.current.values(), checker);
2515
+ };
2516
+ var refreshTemplateEntries = (index, program, checker) => {
2517
+ const entryCount = index.entries.size;
2518
+ dropStaleTemplateEntries(index, false);
2519
+ const isUnchanged = index.entries.size === entryCount;
2520
+ if (isUnchanged) return;
2521
+ index.usage = void 0;
2522
+ indexMissingEntries(index, indexableSourceFiles(program), checker);
2523
+ };
2524
+ var reconcile = (index, program) => {
2525
+ const checker = lazyChecker(program);
2526
+ const isNewProgram = index.program !== program;
2527
+ if (isNewProgram) {
2528
+ reindexProgram(index, program, checker);
2529
+ return;
2530
+ }
2531
+ refreshTemplateEntries(index, program, checker);
2532
+ };
2533
+
2534
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
2535
+ var indexes = /* @__PURE__ */ new Map();
2536
+ var memberName = (node) => {
2537
+ const isMember = isClassElement(node);
2538
+ if (!isMember) return null;
2539
+ if (!node.name) return null;
2540
+ const isIdentifierName = isIdentifier7(node.name);
2541
+ const isStringName = isStringLiteralLike6(node.name);
2542
+ const isNamedMember = isIdentifierName || isStringName;
2543
+ if (!isNamedMember) return null;
2544
+ return node.name.text;
2545
+ };
2546
+ var usageOf = (index) => {
2547
+ if (index.usage !== void 0) return index.usage;
2548
+ const declarations = /* @__PURE__ */ new Set();
2549
+ const fallbackNames = /* @__PURE__ */ new Set();
2550
+ for (const entry of index.entries.values()) {
2551
+ for (const declaration of entry.declarations) {
2552
+ declarations.add(declaration);
2553
+ }
2554
+ for (const name of entry.fallbackNames) {
2555
+ fallbackNames.add(name);
2556
+ }
2557
+ }
2558
+ index.usage = {
2559
+ has: (node) => {
2560
+ const hasDeclaration = declarations.has(node);
2561
+ if (hasDeclaration) return true;
2562
+ if (fallbackNames.size === 0) return false;
2563
+ const name = memberName(node);
2564
+ return name !== null && fallbackNames.has(name);
2565
+ }
2566
+ };
2567
+ return index.usage;
2568
+ };
2569
+ var createIndex = () => {
2570
+ const emptyIndex = {
2571
+ candidateNames: /* @__PURE__ */ new Set(),
2572
+ classes: /* @__PURE__ */ new Map(),
2573
+ directives: buildDirectiveIndex([]),
2574
+ directiveShape: "",
2575
+ entries: /* @__PURE__ */ new Map(),
2576
+ program: null,
2577
+ templateCheckDuration: 0,
2578
+ templateCheckedAt: 0,
2579
+ usage: void 0
2580
+ };
2581
+ return emptyIndex;
2582
+ };
2583
+ var projectUsageIsCurrent = (program) => {
2584
+ const configFilePath = program.getCompilerOptions()["configFilePath"];
2585
+ return typeof configFilePath === "string" && indexes.get(configFilePath)?.program === program;
2586
+ };
2587
+ var projectUsage = (program) => {
2588
+ const configFilePath = program.getCompilerOptions()["configFilePath"];
2589
+ if (typeof configFilePath !== "string") return null;
2590
+ let index = indexes.get(configFilePath);
2591
+ if (!index) {
2592
+ index = createIndex();
2593
+ indexes.set(configFilePath, index);
2594
+ }
2595
+ try {
2596
+ reconcile(index, program);
2597
+ return usageOf(index);
2598
+ } catch {
2599
+ indexes.delete(configFilePath);
2600
+ return null;
2601
+ }
2602
+ };
2603
+
2604
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.ts
2605
+ var docs = {
2606
+ description: "Disallow unread instance fields and methods in Angular components and directives"
2607
+ };
2608
+ var messages = {
2609
+ unusedField: "Angular instance field '{{name}}' is never read.",
2610
+ unusedMethod: "Angular instance method '{{name}}' is never read."
2611
+ };
2612
+ var allowEffectFieldsSchema = {
2613
+ type: "boolean",
2614
+ description: "Allow Angular effect() fields with automatic cleanup."
2615
+ };
2616
+ var analysisSchema = {
2617
+ type: "string",
2618
+ enum: ["local", "project"],
2619
+ description: "Choose local-file or typed whole-project analysis."
2620
+ };
2621
+ var properties = {
2622
+ allowEffectFields: allowEffectFieldsSchema,
2623
+ analysis: analysisSchema
2624
+ };
2625
+ var optionsSchema = {
2626
+ type: "object",
2627
+ properties,
2628
+ additionalProperties: false
2629
+ };
2630
+ var schema = [optionsSchema];
2631
+ var meta = {
2632
+ type: "problem",
2633
+ docs,
2634
+ schema,
2635
+ messages
2636
+ };
2637
+ var ruleDefaults = {
2638
+ allowEffectFields: false,
2639
+ analysis: "local"
2640
+ };
2641
+ var defaultOptions = [ruleDefaults];
2642
+ var createRule = ESLintUtils.RuleCreator(
2643
+ () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
2644
+ );
2645
+ var projectParserServices = (context, analysis) => {
2646
+ if (analysis !== "project") return void 0;
2647
+ try {
2648
+ return ESLintUtils.getParserServices(context);
2649
+ } catch {
2650
+ throw new Error(
2651
+ "Project analysis requires parser services with type information."
2652
+ );
2653
+ }
2654
+ };
2655
+ var projectMemberUsedFor = (parserServices) => {
2656
+ if (parserServices === void 0) return void 0;
2657
+ let usage;
2658
+ const memberUsed = (node) => {
2659
+ if (usage === void 0) {
2660
+ usage = projectUsage(parserServices.program);
2661
+ }
2662
+ return usage?.has(parserServices.esTreeNodeToTSNodeMap.get(node)) ?? true;
2663
+ };
2664
+ return memberUsed;
2665
+ };
2666
+ var no_unused_angular_instance_fields_default = createRule({
2667
+ name: "no-unused-instance-fields",
2668
+ meta,
2669
+ defaultOptions,
2670
+ create(context, [options]) {
2671
+ const analysis = options.analysis ?? "local";
2672
+ const isSpec = isSpecFile(context.filename);
2673
+ const noListeners = {};
2674
+ if (analysis === "project" && isSpec) return noListeners;
2675
+ const parserServices = projectParserServices(context, analysis);
2676
+ const imports = angularClassImports(context.sourceCode.ast);
2677
+ if (!imports) return noListeners;
2678
+ const classes = [];
2679
+ const stack = [];
2680
+ const dynamicClasses = /* @__PURE__ */ new Set();
2681
+ const projectIndexed = () => {
2682
+ if (parserServices === void 0) return false;
2683
+ return projectUsageIsCurrent(parserServices.program);
2684
+ };
2685
+ const reportOptions = {
2686
+ allowEffectFields: options.allowEffectFields ?? false,
2687
+ classes,
2688
+ context,
2689
+ dynamicClasses,
2690
+ imports,
2691
+ projectIndexed,
2692
+ projectMemberUsed: projectMemberUsedFor(parserServices)
2693
+ };
2694
+ const readListeners = classReadVisitor(classes, stack, dynamicClasses);
2695
+ const listeners = {
2696
+ ...readListeners,
2697
+ "Program:exit"() {
2698
+ reportUnusedMembers(reportOptions);
2699
+ }
2700
+ };
2701
+ return listeners;
2702
+ }
2703
+ });
2704
+
2705
+ // packages/lint-suite/src/lib/angular.ts
2706
+ var lintSuiteAngularPlugin = definePlugin("lint-suite-angular", {
2707
+ "no-unused-instance-fields": no_unused_angular_instance_fields_default
2708
+ });
2709
+ var angular = defineConfig2([
2710
+ {
2711
+ files: ["**/*.ts"],
2712
+ extends: [...configs2.tsRecommended],
2713
+ plugins: {
2714
+ "lint-suite-angular": lintSuiteAngularPlugin
2715
+ },
2716
+ languageOptions: {
2717
+ globals: {
2718
+ ...globals.browser,
2719
+ ...globals.es2025,
2720
+ ...globals.node
2721
+ },
2722
+ parserOptions: {
2723
+ projectService: true
2724
+ }
2725
+ },
2726
+ processor: processInlineTemplates,
2727
+ rules: {
2728
+ "lint-suite-angular/no-unused-instance-fields": [
2729
+ "error",
2730
+ { analysis: "project" }
2731
+ ],
2732
+ "@angular-eslint/prefer-on-push-component-change-detection": "error",
2733
+ // v22: flags only explicit opt-out of OnPush (omission now means OnPush)
2734
+ "@angular-eslint/prefer-signals": "error",
2735
+ "@angular-eslint/use-injectable-provided-in": "error",
2736
+ "@angular-eslint/prefer-signal-model": "error",
2737
+ "@angular-eslint/no-uncalled-signals": "error",
2738
+ "@angular-eslint/prefer-host-metadata-property": "error",
2739
+ "@angular-eslint/no-implicit-take-until-destroyed": "error",
2740
+ // v21.2.0: Enforce explicit DestroyRef
2741
+ "@angular-eslint/no-async-lifecycle-method": "error",
2742
+ // Prevent async lifecycle hooks
2743
+ "@angular-eslint/prefer-output-readonly": "warn",
2744
+ // Prevent accidental reassignment
2745
+ "@angular-eslint/prefer-output-emitter-ref": "warn",
2746
+ // Modern output() function over @Output()
2747
+ "@angular-eslint/sort-lifecycle-methods": "warn",
2748
+ // Consistent lifecycle method ordering
2749
+ "@angular-eslint/computed-must-return": "error",
2750
+ // Catch silent computed() bugs missing a return
2751
+ "@angular-eslint/no-duplicates-in-metadata-arrays": "error",
2752
+ // Catch duplicate imports/providers in @Component/@NgModule
2753
+ "@angular-eslint/no-lifecycle-call": "error",
2754
+ // Forbid manual this.ngOnInit() calls
2755
+ "@angular-eslint/require-lifecycle-on-prototype": "error",
2756
+ // Lifecycle hooks must be methods, not arrow-fn fields
2757
+ "@angular-eslint/relative-url-prefix": "error",
2758
+ // Require ./ in templateUrl/styleUrl for portability
2759
+ "@angular-eslint/use-component-selector": "error",
2760
+ // All components must declare a selector
2761
+ "@angular-eslint/no-developer-preview": "warn",
2762
+ // Flag APIs marked @developerPreview (no breaking-change policy)
2763
+ "@angular-eslint/no-experimental": "warn"
2764
+ // Flag APIs marked @experimental
2765
+ }
2766
+ }
2767
+ ]);
2768
+
2769
+ // packages/lint-suite/src/lib/base.ts
2770
+ import { defineConfig as defineConfig6 } from "eslint/config";
2771
+
2772
+ // packages/lint-suite/src/lib/base-limits.ts
2773
+ import { defineConfig as defineConfig3 } from "eslint/config";
2774
+ var baseLimits = defineConfig3([
2775
+ {
2776
+ files: [
2777
+ "**/*.js",
2778
+ "**/*.jsx",
2779
+ "**/*.cjs",
2780
+ "**/*.mjs",
2781
+ "**/*.ts",
2782
+ "**/*.tsx",
2783
+ "**/*.cts",
2784
+ "**/*.mts",
2785
+ "**/*.html"
2786
+ ],
2787
+ rules: {
2788
+ "max-lines": [
2789
+ "error",
2790
+ {
2791
+ max: 150,
2792
+ skipBlankLines: true,
2793
+ skipComments: true
2794
+ }
2795
+ ]
2796
+ }
2797
+ },
2798
+ {
2799
+ files: [
2800
+ "**/*.spec.ts",
2801
+ "**/*.test.ts",
2802
+ "**/*.spec.js",
2803
+ "**/*.test.js",
2804
+ "**/*.e2e.ts",
2805
+ "**/*.e2e.js"
2806
+ ],
2807
+ rules: {
2808
+ "max-lines-per-function": "off",
2809
+ complexity: ["warn", { max: 3 }],
2810
+ "max-lines": [
2811
+ "error",
2812
+ {
2813
+ max: 300,
2814
+ skipBlankLines: true,
2815
+ skipComments: true
2816
+ }
2817
+ ]
2818
+ }
2819
+ },
2820
+ {
2821
+ files: ["**/*.action.ts"],
2822
+ rules: {
2823
+ "max-classes-per-file": "off"
2824
+ }
2825
+ },
2826
+ {
2827
+ files: ["**/*.state.ts"],
2828
+ rules: {
2829
+ "class-methods-use-this": "off"
2830
+ }
2831
+ }
2832
+ ]);
2833
+
2834
+ // packages/lint-suite/src/lib/base-practices.ts
2835
+ import { defineConfig as defineConfig4 } from "eslint/config";
2836
+ var basePractices = defineConfig4([
2837
+ {
2838
+ files: [
2839
+ "**/*.js",
2840
+ "**/*.jsx",
2841
+ "**/*.cjs",
2842
+ "**/*.mjs",
2843
+ "**/*.ts",
2844
+ "**/*.tsx",
2845
+ "**/*.cts",
2846
+ "**/*.mts"
2847
+ ],
2848
+ rules: {
2849
+ "no-param-reassign": "error",
2850
+ "no-alert": "error",
2851
+ "no-console": ["warn", { allow: ["warn", "error"] }],
2852
+ "no-debugger": "error",
2853
+ eqeqeq: ["error", "always", { null: "ignore" }],
2854
+ curly: ["error", "multi-line"],
2855
+ "prefer-const": ["error", { destructuring: "all" }],
2856
+ "no-var": "error",
2857
+ "object-shorthand": "error",
2858
+ "prefer-arrow-callback": "error",
2859
+ "no-useless-concat": "error",
2860
+ "prefer-template": "error",
2861
+ "no-nested-ternary": "error",
2862
+ "no-useless-assignment": "error",
2863
+ // NEW: Catch unused assignments (ESLint v9)
2864
+ "no-object-constructor": "error",
2865
+ // Disallow new Object() without arguments
2866
+ "no-else-return": ["error", { allowElseIf: false }],
2867
+ // Prefer early returns
2868
+ "no-multi-assign": "error",
2869
+ // Disallow chaining assignments
2870
+ radix: "error",
2871
+ // Require radix parameter for parseInt()
2872
+ yoda: "error",
2873
+ // Disallow yoda conditions (`if ("red" === color)`)
2874
+ complexity: ["warn", 10],
2875
+ // Warn if function complexity is high
2876
+ "max-depth": ["warn", 4],
2877
+ // Warn on deeply nested blocks
2878
+ "max-lines-per-function": [
2879
+ "warn",
2880
+ { max: 50, skipBlankLines: true, skipComments: true }
2881
+ ],
2882
+ // Warn on long functions
2883
+ "max-params": ["warn", 4],
807
2884
  // Warn on functions with many parameters
808
2885
  "no-lonely-if": "error",
809
2886
  // Disallow if statements as the only statement in an else block
@@ -883,7 +2960,16 @@ var base = defineConfig3([
883
2960
  }
884
2961
  ]
885
2962
  }
886
- },
2963
+ }
2964
+ ]);
2965
+
2966
+ // packages/lint-suite/src/lib/base-recommended.ts
2967
+ import { configs as configs3 } from "@nx/eslint-plugin";
2968
+ import stylistic from "@stylistic/eslint-plugin";
2969
+ import { defineConfig as defineConfig5 } from "eslint/config";
2970
+ import importPluginX from "eslint-plugin-import-x";
2971
+ var baseRecommended = defineConfig5([
2972
+ { extends: [...configs3["flat/base"]] },
887
2973
  {
888
2974
  files: [
889
2975
  "**/*.js",
@@ -893,68 +2979,161 @@ var base = defineConfig3([
893
2979
  "**/*.ts",
894
2980
  "**/*.tsx",
895
2981
  "**/*.cts",
896
- "**/*.mts",
897
- "**/*.html"
2982
+ "**/*.mts"
898
2983
  ],
899
- rules: {
900
- "max-lines": [
901
- "error",
902
- {
903
- max: 150,
904
- skipBlankLines: true,
905
- skipComments: true
906
- }
907
- ]
908
- }
2984
+ extends: [importPluginX.flatConfigs.recommended]
909
2985
  },
910
2986
  {
911
2987
  files: [
912
- "**/*.spec.ts",
913
- "**/*.test.ts",
914
- "**/*.spec.js",
915
- "**/*.test.js",
916
- "**/*.e2e.ts",
917
- "**/*.e2e.js"
2988
+ "**/*.js",
2989
+ "**/*.jsx",
2990
+ "**/*.cjs",
2991
+ "**/*.mjs",
2992
+ "**/*.ts",
2993
+ "**/*.tsx",
2994
+ "**/*.cts",
2995
+ "**/*.mts"
2996
+ ],
2997
+ extends: [stylistic.configs.recommended]
2998
+ }
2999
+ ]);
3000
+
3001
+ // packages/lint-suite/src/lib/base.ts
3002
+ var base = defineConfig6([
3003
+ ...baseRecommended,
3004
+ ...basePractices,
3005
+ ...baseLimits
3006
+ ]);
3007
+
3008
+ // packages/lint-suite/src/lib/boundaries.ts
3009
+ import { defineConfig as defineConfig7 } from "eslint/config";
3010
+ import boundariesPlugin from "eslint-plugin-boundaries";
3011
+ var boundaries = defineConfig7([
3012
+ {
3013
+ files: [
3014
+ "**/*.js",
3015
+ "**/*.jsx",
3016
+ "**/*.ts",
3017
+ "**/*.tsx",
3018
+ "**/*.cts",
3019
+ "**/*.mts"
918
3020
  ],
3021
+ plugins: {
3022
+ boundaries: boundariesPlugin
3023
+ },
3024
+ settings: {
3025
+ "boundaries/elements": [
3026
+ {
3027
+ type: "feature",
3028
+ pattern: "**/feature/**"
3029
+ },
3030
+ {
3031
+ type: "data-access",
3032
+ pattern: "**/data-access/**"
3033
+ },
3034
+ {
3035
+ type: "domain-logic",
3036
+ pattern: "**/domain-logic/**"
3037
+ },
3038
+ {
3039
+ type: "ui",
3040
+ pattern: "**/ui/**"
3041
+ },
3042
+ {
3043
+ type: "common",
3044
+ pattern: "**/common/**"
3045
+ },
3046
+ {
3047
+ type: "utils",
3048
+ pattern: "**/utils/**"
3049
+ }
3050
+ ],
3051
+ "import/resolver": {
3052
+ typescript: {
3053
+ alwaysTryTypes: true
3054
+ }
3055
+ }
3056
+ },
919
3057
  rules: {
920
- "max-lines-per-function": "off",
921
- complexity: ["warn", { max: 3 }],
922
- "max-lines": [
3058
+ ...boundariesPlugin.configs.recommended.rules,
3059
+ "boundaries/dependencies": [
923
3060
  "error",
924
3061
  {
925
- max: 300,
926
- skipBlankLines: true,
927
- skipComments: true
3062
+ default: "disallow",
3063
+ message: "{{to.element.types}} is not allowed to be imported in {{from.element.types}}",
3064
+ policies: [
3065
+ {
3066
+ from: { element: { type: "feature" } },
3067
+ allow: [
3068
+ { to: { element: { type: "feature" } } },
3069
+ { to: { element: { type: "ui" } } },
3070
+ { to: { element: { type: "domain-logic" } } },
3071
+ { to: { element: { type: "utils" } } },
3072
+ { to: { element: { type: "common" } } }
3073
+ ]
3074
+ },
3075
+ {
3076
+ from: { element: { type: "domain-logic" } },
3077
+ allow: [
3078
+ { to: { element: { type: "domain-logic" } } },
3079
+ { to: { element: { type: "data-access" } } },
3080
+ { to: { element: { type: "utils" } } },
3081
+ { to: { element: { type: "common" } } }
3082
+ ]
3083
+ },
3084
+ {
3085
+ from: { element: { type: "data-access" } },
3086
+ allow: [
3087
+ { to: { element: { type: "data-access" } } },
3088
+ { to: { element: { type: "utils" } } },
3089
+ { to: { element: { type: "common" } } }
3090
+ ]
3091
+ },
3092
+ {
3093
+ from: { element: { type: "ui" } },
3094
+ allow: [
3095
+ { to: { element: { type: "ui" } } },
3096
+ { to: { element: { type: "utils" } } },
3097
+ { to: { element: { type: "common" } } }
3098
+ ]
3099
+ },
3100
+ {
3101
+ from: { element: { type: "utils" } },
3102
+ allow: [
3103
+ { to: { element: { type: "utils" } } },
3104
+ { to: { element: { type: "common" } } }
3105
+ ]
3106
+ },
3107
+ {
3108
+ from: { element: { type: "common" } },
3109
+ allow: [{ to: { element: { type: "common" } } }]
3110
+ }
3111
+ ]
928
3112
  }
929
3113
  ]
930
3114
  }
931
3115
  },
932
3116
  {
933
- files: ["**/*.action.ts"],
934
- rules: {
935
- "max-classes-per-file": "off"
936
- }
937
- },
938
- {
939
- files: ["**/*.state.ts"],
3117
+ files: ["**/*.stories.ts"],
940
3118
  rules: {
941
- "class-methods-use-this": "off"
3119
+ "boundaries/dependencies": "off"
942
3120
  }
943
3121
  }
944
3122
  ]);
945
3123
 
946
3124
  // packages/lint-suite/src/lib/javascript.ts
947
3125
  import { configs as configs4 } from "@nx/eslint-plugin";
948
- import { defineConfig as defineConfig4 } from "eslint/config";
949
- var javascript = defineConfig4({
3126
+ import { defineConfig as defineConfig8 } from "eslint/config";
3127
+ var nxJavascript = configs4["flat/javascript"];
3128
+ var javascript = defineConfig8({
950
3129
  files: ["**/*.js", "**/*.jsx", "**/*.cjs", "**/*.mjs"],
951
- extends: [...configs4["flat/javascript"]]
3130
+ extends: nxJavascript
952
3131
  });
953
3132
 
954
3133
  // packages/lint-suite/src/lib/json.ts
955
3134
  import jsonPlugin from "@eslint/json";
956
- import { defineConfig as defineConfig5 } from "eslint/config";
957
- var json = defineConfig5([
3135
+ import { defineConfig as defineConfig9 } from "eslint/config";
3136
+ var json = defineConfig9([
958
3137
  {
959
3138
  plugins: { json: jsonPlugin }
960
3139
  },
@@ -965,15 +3144,40 @@ var json = defineConfig5([
965
3144
  }
966
3145
  ]);
967
3146
 
3147
+ // packages/lint-suite/src/lib/playwright.ts
3148
+ import { defineConfig as defineConfig10 } from "eslint/config";
3149
+ import playwrightEslint from "eslint-plugin-playwright";
3150
+ var playwright = defineConfig10([
3151
+ {
3152
+ ...playwrightEslint.configs["flat/recommended"],
3153
+ files: ["**/*.e2e.ts", "**/*.e2e.js"],
3154
+ rules: {
3155
+ ...playwrightEslint.configs["flat/recommended"].rules,
3156
+ // Locator best practices
3157
+ "playwright/prefer-locator": "error",
3158
+ "playwright/prefer-native-locators": "error",
3159
+ "playwright/no-nth-methods": "warn",
3160
+ // Matcher improvements
3161
+ "playwright/prefer-to-be": "error",
3162
+ "playwright/prefer-to-have-count": "error",
3163
+ "playwright/prefer-to-have-length": "error",
3164
+ "playwright/prefer-comparison-matcher": "error",
3165
+ // Test structure
3166
+ "playwright/prefer-hooks-on-top": "error",
3167
+ "playwright/require-top-level-describe": "warn"
3168
+ }
3169
+ }
3170
+ ]);
3171
+
968
3172
  // packages/lint-suite/src/lib/prettier.ts
3173
+ import { defineConfig as defineConfig11 } from "eslint/config";
969
3174
  import eslintConfigPrettier from "eslint-config-prettier";
970
- import { defineConfig as defineConfig6 } from "eslint/config";
971
- var prettier = defineConfig6([eslintConfigPrettier]);
3175
+ var prettier = defineConfig11([eslintConfigPrettier]);
972
3176
 
973
3177
  // packages/lint-suite/src/lib/rxjs.ts
974
3178
  import rxjsEslint from "@smarttools/eslint-plugin-rxjs";
975
- import { defineConfig as defineConfig7 } from "eslint/config";
976
- var rxjs = defineConfig7([
3179
+ import { defineConfig as defineConfig12 } from "eslint/config";
3180
+ var rxjs = defineConfig12([
977
3181
  {
978
3182
  files: [
979
3183
  "**/*.js",
@@ -1048,9 +3252,9 @@ var rxjs = defineConfig7([
1048
3252
  ]);
1049
3253
 
1050
3254
  // packages/lint-suite/src/lib/storybook.ts
3255
+ import { defineConfig as defineConfig13 } from "eslint/config";
1051
3256
  import storybookEslint from "eslint-plugin-storybook";
1052
- import { defineConfig as defineConfig8 } from "eslint/config";
1053
- var storybook = defineConfig8([
3257
+ var storybook = defineConfig13([
1054
3258
  {
1055
3259
  ignores: ["!.storybook"],
1056
3260
  files: ["**/*.stories.ts"],
@@ -1065,263 +3269,14 @@ var storybook = defineConfig8([
1065
3269
  ]);
1066
3270
 
1067
3271
  // packages/lint-suite/src/lib/typescript.ts
1068
- import { configs as configs5 } from "@nx/eslint-plugin";
1069
- import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
1070
- import importPluginX2 from "eslint-plugin-import-x";
1071
- import { defineConfig as defineConfig9 } from "eslint/config";
1072
-
1073
- // packages/lint-suite/src/lib/rules/explicit-accessibility.ts
1074
- import { ESLintUtils as ESLintUtils2, TSESTree as TSESTree5 } from "@typescript-eslint/utils";
1075
- var createRule2 = ESLintUtils2.RuleCreator(
1076
- () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
1077
- );
1078
- var defaultOptions = {
1079
- defaultAccessibility: "private"
1080
- };
1081
- var getNodeName = (node) => {
1082
- if (!node) {
1083
- return "<unknown>";
1084
- }
1085
- if ("key" in node && node.key) {
1086
- if (node.key.type === TSESTree5.AST_NODE_TYPES.Identifier) {
1087
- return node.key.name;
1088
- }
1089
- if (node.key.type === TSESTree5.AST_NODE_TYPES.PrivateIdentifier) {
1090
- return `#${node.key.name}`;
1091
- }
1092
- return "<computed/complex key>";
1093
- }
1094
- if ("kind" in node && node.kind === "constructor") {
1095
- return "constructor";
1096
- }
1097
- if (node.type === TSESTree5.AST_NODE_TYPES.TSParameterProperty) {
1098
- const param = node.parameter;
1099
- if (param.type === TSESTree5.AST_NODE_TYPES.Identifier) {
1100
- return param.name;
1101
- }
1102
- if (param.type === TSESTree5.AST_NODE_TYPES.AssignmentPattern) {
1103
- if (param.left.type === TSESTree5.AST_NODE_TYPES.Identifier) {
1104
- return param.left.name;
1105
- }
1106
- return "<destructured parameter>";
1107
- }
1108
- return "<complex parameter>";
1109
- }
1110
- return "<unknown>";
1111
- };
1112
- var explicit_accessibility_default = createRule2({
1113
- name: "explicit-accessibility",
1114
- meta: {
1115
- type: "suggestion",
1116
- docs: {
1117
- description: "Require explicit accessibility modifiers on class members"
1118
- },
1119
- fixable: "code",
1120
- schema: [
1121
- {
1122
- type: "object",
1123
- properties: {
1124
- defaultAccessibility: {
1125
- type: "string",
1126
- enum: ["public", "private", "protected"],
1127
- description: "The accessibility modifier to add if one is missing."
1128
- }
1129
- },
1130
- additionalProperties: false
1131
- }
1132
- ],
1133
- messages: {
1134
- missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected)."
1135
- }
1136
- },
1137
- defaultOptions: [defaultOptions],
1138
- create(context, [options]) {
1139
- const { defaultAccessibility } = { ...defaultOptions, ...options };
1140
- const sourceCode = context.sourceCode;
1141
- function checkNode(node) {
1142
- if (node.accessibility) {
1143
- return;
1144
- }
1145
- let reportNode = node;
1146
- const name = getNodeName(node);
1147
- if (node.type === TSESTree5.AST_NODE_TYPES.MethodDefinition || node.type === TSESTree5.AST_NODE_TYPES.PropertyDefinition) {
1148
- reportNode = node.key;
1149
- } else {
1150
- const param = node.parameter;
1151
- if (param.type === TSESTree5.AST_NODE_TYPES.Identifier) {
1152
- reportNode = param;
1153
- } else if (param.type === TSESTree5.AST_NODE_TYPES.AssignmentPattern && param.left.type === TSESTree5.AST_NODE_TYPES.Identifier) {
1154
- reportNode = param.left;
1155
- }
1156
- }
1157
- context.report({
1158
- node: reportNode ?? node,
1159
- messageId: "missingAccessibility",
1160
- data: { name },
1161
- fix(fixer) {
1162
- let insertText = `${defaultAccessibility} `;
1163
- let tokenToInsertBefore;
1164
- if (node.decorators.length > 0) {
1165
- const lastDecorator = node.decorators[node.decorators.length - 1];
1166
- tokenToInsertBefore = sourceCode.getTokenAfter(lastDecorator);
1167
- } else {
1168
- tokenToInsertBefore = sourceCode.getFirstToken(node);
1169
- }
1170
- if (node.type === TSESTree5.AST_NODE_TYPES.TSParameterProperty && node.readonly) {
1171
- const firstToken = sourceCode.getFirstToken(node);
1172
- if (firstToken?.value === "readonly") {
1173
- tokenToInsertBefore = firstToken;
1174
- }
1175
- }
1176
- if (!tokenToInsertBefore) return null;
1177
- const tokenAfterInsertion = sourceCode.getTokenAfter(
1178
- tokenToInsertBefore,
1179
- { includeComments: false }
1180
- );
1181
- const textBetween = sourceCode.text.slice(
1182
- tokenToInsertBefore.range[1],
1183
- tokenAfterInsertion?.range[0]
1184
- );
1185
- if (!/^\s/.test(textBetween)) {
1186
- insertText = ` ${insertText}`;
1187
- }
1188
- return fixer.insertTextBefore(tokenToInsertBefore, insertText);
1189
- }
1190
- });
1191
- }
1192
- return {
1193
- MethodDefinition(node) {
1194
- checkNode(node);
1195
- },
1196
- PropertyDefinition(node) {
1197
- checkNode(node);
1198
- },
1199
- TSParameterProperty(node) {
1200
- checkNode(node);
1201
- }
1202
- };
1203
- }
1204
- });
3272
+ import { defineConfig as defineConfig19 } from "eslint/config";
1205
3273
 
1206
- // packages/lint-suite/src/lib/typescript.ts
1207
- var localTypescriptRules = {
1208
- rules: {
1209
- "explicit-accessibility": explicit_accessibility_default
1210
- }
1211
- };
1212
- var typescript = defineConfig9([
1213
- // NOTE: Checks for 'prettier' and 'eslint-plugin-prettier'
1214
- {
1215
- files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
1216
- extends: [
1217
- importPluginX2.flatConfigs.typescript,
1218
- ...configs5["flat/typescript"]
1219
- ]
1220
- },
3274
+ // packages/lint-suite/src/lib/typescript-consistency.ts
3275
+ import { defineConfig as defineConfig14 } from "eslint/config";
3276
+ var typescriptConsistency = defineConfig14([
1221
3277
  {
1222
3278
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
1223
- plugins: { local: localTypescriptRules },
1224
3279
  rules: {
1225
- "local/explicit-accessibility": "error",
1226
- "@typescript-eslint/explicit-member-accessibility": "error",
1227
- "@typescript-eslint/explicit-function-return-type": "error",
1228
- "@typescript-eslint/no-empty-function": "error",
1229
- "@typescript-eslint/no-unused-vars": "error",
1230
- "@typescript-eslint/no-use-before-define": "error",
1231
- // A. Eliminating `any` (already covered by strict-type-checked, but explicit for clarity)
1232
- "@typescript-eslint/no-explicit-any": "error",
1233
- "@typescript-eslint/no-unsafe-assignment": "error",
1234
- "@typescript-eslint/no-unsafe-call": "error",
1235
- "@typescript-eslint/no-unsafe-member-access": "error",
1236
- "@typescript-eslint/no-unsafe-return": "error",
1237
- "@typescript-eslint/no-unsafe-argument": "error",
1238
- // New v8 type-safety rules (replacements for deprecated ban-types)
1239
- "@typescript-eslint/no-empty-object-type": "error",
1240
- // Bans confusing {} type
1241
- "@typescript-eslint/no-unsafe-function-type": "error",
1242
- // Bans Function type
1243
- "@typescript-eslint/no-wrapper-object-types": "error",
1244
- // Bans Number, String, Boolean wrappers
1245
- // B. Explicit Annotations (partially covered, explicit module boundaries is key)
1246
- "@typescript-eslint/explicit-module-boundary-types": "error",
1247
- // Consider enabling if maximum explicitness is desired, even if verbose:
1248
- // C. Null/Undefined Safety (Requires strictNullChecks)
1249
- "@typescript-eslint/no-non-null-assertion": "error",
1250
- "@typescript-eslint/prefer-nullish-coalescing": "error",
1251
- "@typescript-eslint/prefer-optional-chain": "error",
1252
- // D. Promise Handling (Supplementing plugin:promise/recommended)
1253
- "@typescript-eslint/no-floating-promises": [
1254
- "error",
1255
- { ignoreVoid: true }
1256
- ],
1257
- "@typescript-eslint/no-misused-promises": "warn",
1258
- // Already in recommended-type-checked
1259
- "@typescript-eslint/promise-function-async": "error",
1260
- "@typescript-eslint/await-thenable": "error",
1261
- // Already in recommended-type-checked
1262
- // E. Best Practices & Potential Errors (Some covered by presets, fine-tuning here)
1263
- "@typescript-eslint/no-inferrable-types": "warn",
1264
- // Keep as warn or disable if inference is preferred
1265
- "@typescript-eslint/no-require-imports": "error",
1266
- // Use ES modules (replaces deprecated no-var-requires)
1267
- "@typescript-eslint/only-throw-error": "error",
1268
- // Replaces deprecated no-throw-literal
1269
- "@typescript-eslint/no-array-delete": "error",
1270
- // Prevents delete arr[0]
1271
- "@typescript-eslint/return-await": ["error", "in-try-catch"],
1272
- // Better stack traces in try/catch
1273
- "@typescript-eslint/require-await": "error",
1274
- "@typescript-eslint/restrict-template-expressions": [
1275
- "error",
1276
- { allowNumber: true, allowBoolean: true }
1277
- ],
1278
- // Allow numbers/booleans in templates
1279
- "@typescript-eslint/restrict-plus-operands": "error",
1280
- // Enforce consistent types for '+'
1281
- "@typescript-eslint/prefer-as-const": "error",
1282
- "@typescript-eslint/no-shadow": "error",
1283
- "@typescript-eslint/switch-exhaustiveness-check": "error",
1284
- // Ensure switch on unions is exhaustive
1285
- "@typescript-eslint/no-unused-expressions": [
1286
- "error",
1287
- { allowShortCircuit: true, allowTernary: true }
1288
- ],
1289
- "@typescript-eslint/no-useless-constructor": "error",
1290
- "@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
1291
- // Prefer dot notation where possible
1292
- // Strict type-checked rules
1293
- "@typescript-eslint/no-unnecessary-condition": "error",
1294
- // Catches always-true/false conditions
1295
- "@typescript-eslint/no-confusing-void-expression": [
1296
- "error",
1297
- { ignoreArrowShorthand: true, ignoreVoidOperator: true }
1298
- ],
1299
- // Prevent confusing void usage
1300
- "@typescript-eslint/no-dynamic-delete": "error",
1301
- // Use Map instead of delete obj[key]
1302
- "@typescript-eslint/no-extraneous-class": [
1303
- "error",
1304
- { allowWithDecorator: true }
1305
- ],
1306
- // Allow Angular classes
1307
- "@typescript-eslint/no-invalid-void-type": "error",
1308
- // Restrict void to return types
1309
- "@typescript-eslint/unified-signatures": "error",
1310
- // Merge overloads where possible
1311
- "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
1312
- // Forbids foo! ?? bar
1313
- // Stylistic type-checked rules
1314
- "@typescript-eslint/prefer-find": "error",
1315
- // Use find() instead of filter()[0]
1316
- "@typescript-eslint/prefer-for-of": "error",
1317
- // Modern iteration
1318
- "@typescript-eslint/prefer-includes": "error",
1319
- // indexOf() → includes()
1320
- "@typescript-eslint/prefer-string-starts-ends-with": "error",
1321
- // Modern string methods
1322
- "@typescript-eslint/prefer-regexp-exec": "error",
1323
- // Better regex performance
1324
- // F. Consistency & Style (Some covered by stylistic-type-checked)
1325
3280
  "@typescript-eslint/consistent-type-assertions": [
1326
3281
  "error",
1327
3282
  { assertionStyle: "as", objectLiteralTypeAssertions: "never" }
@@ -1334,10 +3289,14 @@ var typescript = defineConfig9([
1334
3289
  "@typescript-eslint/naming-convention": [
1335
3290
  "error",
1336
3291
  {
1337
- selector: "objectLiteralProperty",
3292
+ selector: ["objectLiteralProperty", "objectLiteralMethod"],
1338
3293
  modifiers: ["requiresQuotes"],
1339
3294
  format: null
1340
3295
  },
3296
+ {
3297
+ selector: "objectLiteralMethod",
3298
+ format: ["camelCase", "PascalCase"]
3299
+ },
1341
3300
  {
1342
3301
  selector: "objectLiteralProperty",
1343
3302
  format: ["camelCase", "snake_case", "PascalCase"]
@@ -1360,7 +3319,28 @@ var typescript = defineConfig9([
1360
3319
  selector: "property",
1361
3320
  format: ["camelCase", "PascalCase", "snake_case"]
1362
3321
  }
1363
- ],
3322
+ ]
3323
+ }
3324
+ }
3325
+ ]);
3326
+
3327
+ // packages/lint-suite/src/lib/typescript-imports.ts
3328
+ import { defineConfig as defineConfig15 } from "eslint/config";
3329
+ import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
3330
+ var pathGroups = [
3331
+ { pattern: "@angular/**", group: "builtin", position: "before" },
3332
+ { pattern: "@nestjs/**", group: "builtin", position: "before" },
3333
+ { pattern: "@shared/**", group: "internal", position: "before" },
3334
+ { pattern: "@backend/**", group: "internal", position: "before" },
3335
+ { pattern: "@frontend/**", group: "internal", position: "before" },
3336
+ { pattern: "@*/shared/**", group: "internal", position: "before" },
3337
+ { pattern: "@*/frontend/**", group: "internal", position: "after" },
3338
+ { pattern: "@*/backend/**", group: "internal", position: "after" }
3339
+ ];
3340
+ var typescriptImports = defineConfig15([
3341
+ {
3342
+ files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
3343
+ rules: {
1364
3344
  "import-x/order": [
1365
3345
  "error",
1366
3346
  {
@@ -1372,53 +3352,17 @@ var typescript = defineConfig9([
1372
3352
  "index",
1373
3353
  "unknown"
1374
3354
  ],
1375
- pathGroups: [
1376
- {
1377
- pattern: "@angular/**",
1378
- group: "builtin",
1379
- position: "before"
1380
- },
1381
- {
1382
- pattern: "@nestjs/**",
1383
- group: "builtin",
1384
- position: "before"
1385
- },
1386
- {
1387
- pattern: "@shared/**",
1388
- group: "internal",
1389
- position: "before"
1390
- },
1391
- {
1392
- pattern: "@backend/**",
1393
- group: "internal",
1394
- position: "before"
1395
- },
1396
- {
1397
- pattern: "@frontend/**",
1398
- group: "internal",
1399
- position: "before"
1400
- },
1401
- {
1402
- pattern: "@*/shared/**",
1403
- group: "internal",
1404
- position: "before"
1405
- },
1406
- {
1407
- pattern: "@*/frontend/**",
1408
- group: "internal",
1409
- position: "after"
1410
- },
1411
- {
1412
- pattern: "@*/backend/**",
1413
- group: "internal",
1414
- position: "after"
1415
- }
1416
- ],
3355
+ pathGroups,
1417
3356
  pathGroupsExcludedImportTypes: ["builtin"],
1418
3357
  "newlines-between": "always",
1419
3358
  alphabetize: { order: "asc", caseInsensitive: true }
1420
3359
  }
1421
3360
  ],
3361
+ // TypeScript already checks these four; import-x would re-parse every import with a parser resolved by name, which fails under pnpm.
3362
+ "import-x/namespace": "off",
3363
+ "import-x/default": "off",
3364
+ "import-x/no-named-as-default": "off",
3365
+ "import-x/no-named-as-default-member": "off",
1422
3366
  "import-x/no-unresolved": "error",
1423
3367
  "import-x/no-duplicates": "error",
1424
3368
  "import-x/prefer-default-export": "off",
@@ -1427,13 +3371,9 @@ var typescript = defineConfig9([
1427
3371
  "import-x/first": "error",
1428
3372
  "import-x/no-mutable-exports": "error",
1429
3373
  "import-x/no-self-import": "error",
1430
- // Prevents modules importing themselves
1431
3374
  "import-x/no-cycle": ["error", { maxDepth: 2, ignoreExternal: true }],
1432
- // Prevents circular dependencies
1433
3375
  "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
1434
- // Consistent type imports
1435
3376
  "import-x/no-anonymous-default-export": "error"
1436
- // Improve code searchability
1437
3377
  },
1438
3378
  settings: {
1439
3379
  "import-x/resolver-next": [
@@ -1442,7 +3382,12 @@ var typescript = defineConfig9([
1442
3382
  })
1443
3383
  ]
1444
3384
  }
1445
- },
3385
+ }
3386
+ ]);
3387
+
3388
+ // packages/lint-suite/src/lib/typescript-overrides.ts
3389
+ import { defineConfig as defineConfig16 } from "eslint/config";
3390
+ var typescriptOverrides = defineConfig16([
1446
3391
  {
1447
3392
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
1448
3393
  languageOptions: {
@@ -1475,23 +3420,402 @@ var typescript = defineConfig9([
1475
3420
  "@typescript-eslint/unbound-method": "off",
1476
3421
  "@typescript-eslint/consistent-type-assertions": "off"
1477
3422
  }
1478
- },
3423
+ },
3424
+ {
3425
+ files: ["**/*.stories.ts"],
3426
+ rules: {
3427
+ "@typescript-eslint/consistent-type-assertions": "off",
3428
+ "@typescript-eslint/naming-convention": "off"
3429
+ }
3430
+ }
3431
+ ]);
3432
+
3433
+ // packages/lint-suite/src/lib/typescript-recommended.ts
3434
+ import { configs as configs5 } from "@nx/eslint-plugin";
3435
+ import { defineConfig as defineConfig17 } from "eslint/config";
3436
+ import importPluginX2 from "eslint-plugin-import-x";
3437
+ var nxTypescript = configs5["flat/typescript"];
3438
+ var typescriptRecommended = defineConfig17([
3439
+ {
3440
+ files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
3441
+ extends: [importPluginX2.flatConfigs.typescript, ...nxTypescript]
3442
+ }
3443
+ ]);
3444
+
3445
+ // packages/lint-suite/src/lib/typescript-safety.ts
3446
+ import { defineConfig as defineConfig18 } from "eslint/config";
3447
+
3448
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
3449
+ import { ESLintUtils as ESLintUtils2, TSESTree as TSESTree10 } from "@typescript-eslint/utils";
3450
+
3451
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/common/explicit-accessibility.const.ts
3452
+ var accessibilities = [
3453
+ "public",
3454
+ "private",
3455
+ "protected"
3456
+ ];
3457
+ var docs2 = {
3458
+ description: "Require explicit accessibility modifiers on class members"
3459
+ };
3460
+ var messages2 = {
3461
+ missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected).",
3462
+ setAccessibility: "Add the '{{ accessibility }}' modifier."
3463
+ };
3464
+ var defaultAccessibilitySchema = {
3465
+ type: "string",
3466
+ enum: ["public", "private", "protected", "none"],
3467
+ 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."
3468
+ };
3469
+ var properties2 = {
3470
+ defaultAccessibility: defaultAccessibilitySchema
3471
+ };
3472
+ var optionsSchema2 = {
3473
+ type: "object",
3474
+ properties: properties2,
3475
+ additionalProperties: false
3476
+ };
3477
+ var schema2 = [optionsSchema2];
3478
+ var meta2 = {
3479
+ type: "suggestion",
3480
+ docs: docs2,
3481
+ fixable: "code",
3482
+ hasSuggestions: true,
3483
+ schema: schema2,
3484
+ messages: messages2
3485
+ };
3486
+ var defaultOptions2 = [{ defaultAccessibility: "public" }];
3487
+
3488
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
3489
+ var createRule2 = ESLintUtils2.RuleCreator(
3490
+ () => "https://github.com/F0rty-Tw0/lint-suite#explicit-accessibility"
3491
+ );
3492
+ var isPrivateMember = (node) => {
3493
+ if (node.type === TSESTree10.AST_NODE_TYPES.TSParameterProperty) return false;
3494
+ return node.key.type === TSESTree10.AST_NODE_TYPES.PrivateIdentifier;
3495
+ };
3496
+ var isConstructor = (node) => {
3497
+ if (node.type !== TSESTree10.AST_NODE_TYPES.MethodDefinition) return false;
3498
+ return node.kind === "constructor";
3499
+ };
3500
+ var reportTarget = (node) => {
3501
+ if (node.type !== TSESTree10.AST_NODE_TYPES.TSParameterProperty) {
3502
+ return node.key;
3503
+ }
3504
+ const { parameter } = node;
3505
+ if (parameter.type === TSESTree10.AST_NODE_TYPES.AssignmentPattern) {
3506
+ return parameter.left;
3507
+ }
3508
+ return parameter;
3509
+ };
3510
+ var memberName2 = (target, sourceCode) => {
3511
+ if (target.type === TSESTree10.AST_NODE_TYPES.Identifier) return target.name;
3512
+ return sourceCode.getText(target);
3513
+ };
3514
+ var modifierToken = (node, sourceCode) => {
3515
+ const lastDecorator = node.decorators.at(-1);
3516
+ if (!lastDecorator) return sourceCode.getFirstToken(node);
3517
+ return sourceCode.getTokenAfter(lastDecorator);
3518
+ };
3519
+ var insertModifier = (node, accessibility, sourceCode) => {
3520
+ const token = modifierToken(node, sourceCode);
3521
+ if (!token) return () => null;
3522
+ const lastDecorator = node.decorators.at(-1);
3523
+ const adjacentToDecorator = lastDecorator?.range[1] === token.range[0];
3524
+ const text2 = `${adjacentToDecorator ? " " : ""}${accessibility} `;
3525
+ return (fixer) => fixer.insertTextBefore(token, text2);
3526
+ };
3527
+ var autoFixOf = (node, accessibility, sourceCode) => {
3528
+ if (accessibility === "none") {
3529
+ const noAutoFix = {};
3530
+ return noAutoFix;
3531
+ }
3532
+ const fix = insertModifier(node, accessibility, sourceCode);
3533
+ const autoFix = { fix };
3534
+ return autoFix;
3535
+ };
3536
+ var suggestionOf = (node, accessibility, sourceCode) => {
3537
+ const data = { accessibility };
3538
+ const fix = insertModifier(node, accessibility, sourceCode);
3539
+ const suggestion = {
3540
+ messageId: "setAccessibility",
3541
+ data,
3542
+ fix
3543
+ };
3544
+ return suggestion;
3545
+ };
3546
+ var suggestionsOf = (node, fixAccessibility, sourceCode) => {
3547
+ const isOtherAccessibility = (accessibility) => {
3548
+ return accessibility !== fixAccessibility;
3549
+ };
3550
+ const suggestionFor = (accessibility) => {
3551
+ return suggestionOf(node, accessibility, sourceCode);
3552
+ };
3553
+ return accessibilities.filter(isOtherAccessibility).map(suggestionFor);
3554
+ };
3555
+ var explicit_accessibility_default = createRule2({
3556
+ name: "explicit-accessibility",
3557
+ meta: meta2,
3558
+ defaultOptions: defaultOptions2,
3559
+ create(context, [{ defaultAccessibility = "public" }]) {
3560
+ const { sourceCode } = context;
3561
+ const check = (node) => {
3562
+ const isPrivate = isPrivateMember(node);
3563
+ if (node.accessibility || isPrivate) return;
3564
+ const target = reportTarget(node);
3565
+ const isConstructorMember = isConstructor(node);
3566
+ const usesPublic = isConstructorMember && defaultAccessibility !== "none";
3567
+ const fixAccessibility = usesPublic ? "public" : defaultAccessibility;
3568
+ const autoFix = autoFixOf(node, fixAccessibility, sourceCode);
3569
+ const name = memberName2(target, sourceCode);
3570
+ const data = { name };
3571
+ const suggest = suggestionsOf(node, fixAccessibility, sourceCode);
3572
+ const report = {
3573
+ node: target,
3574
+ messageId: "missingAccessibility",
3575
+ data,
3576
+ ...autoFix,
3577
+ suggest
3578
+ };
3579
+ context.report(report);
3580
+ };
3581
+ const listeners = {
3582
+ AccessorProperty: check,
3583
+ MethodDefinition: check,
3584
+ PropertyDefinition: check,
3585
+ TSAbstractAccessorProperty: check,
3586
+ TSAbstractMethodDefinition: check,
3587
+ TSAbstractPropertyDefinition: check,
3588
+ TSParameterProperty: check
3589
+ };
3590
+ return listeners;
3591
+ }
3592
+ });
3593
+
3594
+ // packages/lint-suite/src/lib/rules/no-inline-object-types/no-inline-object-types.ts
3595
+ import { ESLintUtils as ESLintUtils3, TSESTree as TSESTree11 } from "@typescript-eslint/utils";
3596
+ var docs3 = {
3597
+ description: "Require nested object types inside a type alias to be extracted to a named type alias"
3598
+ };
3599
+ var messages3 = {
3600
+ inlineObjectType: "Inline object type must be extracted to a named type alias."
3601
+ };
3602
+ var meta3 = {
3603
+ type: "suggestion",
3604
+ docs: docs3,
3605
+ schema: [],
3606
+ messages: messages3
3607
+ };
3608
+ var createRule3 = ESLintUtils3.RuleCreator(
3609
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-inline-object-types"
3610
+ );
3611
+ var enclosingTypeAlias = (node) => {
3612
+ let current = node.parent;
3613
+ while (current && current.type !== TSESTree11.AST_NODE_TYPES.Program) {
3614
+ if (current.type === TSESTree11.AST_NODE_TYPES.TSTypeAliasDeclaration) {
3615
+ return current;
3616
+ }
3617
+ current = current.parent;
3618
+ }
3619
+ return void 0;
3620
+ };
3621
+ var no_inline_object_types_default = createRule3({
3622
+ name: "no-inline-object-types",
3623
+ meta: meta3,
3624
+ defaultOptions: [],
3625
+ create(context) {
3626
+ const listeners = {
3627
+ TSTypeLiteral(node) {
3628
+ const isAliasBody = node.parent.type === TSESTree11.AST_NODE_TYPES.TSTypeAliasDeclaration;
3629
+ if (isAliasBody) return;
3630
+ const enclosingAlias = enclosingTypeAlias(node);
3631
+ if (!enclosingAlias) return;
3632
+ const report = {
3633
+ node,
3634
+ messageId: "inlineObjectType"
3635
+ };
3636
+ context.report(report);
3637
+ }
3638
+ };
3639
+ return listeners;
3640
+ }
3641
+ });
3642
+
3643
+ // packages/lint-suite/src/lib/rules/readonly-type-properties/readonly-type-properties.ts
3644
+ import { ESLintUtils as ESLintUtils4, TSESTree as TSESTree12 } from "@typescript-eslint/utils";
3645
+ var docs4 = {
3646
+ description: "Require readonly on primitive-typed type and interface properties"
3647
+ };
3648
+ var messages4 = {
3649
+ missingReadonly: "Type property '{{ name }}' should be readonly. Disable this rule on the line if mutation is required."
3650
+ };
3651
+ var meta4 = {
3652
+ type: "suggestion",
3653
+ docs: docs4,
3654
+ fixable: "code",
3655
+ schema: [],
3656
+ messages: messages4
3657
+ };
3658
+ var createRule4 = ESLintUtils4.RuleCreator(
3659
+ () => "https://github.com/F0rty-Tw0/lint-suite#readonly-type-properties"
3660
+ );
3661
+ var propertyName3 = (node, sourceCode) => {
3662
+ if (node.key.type === TSESTree12.AST_NODE_TYPES.Identifier) {
3663
+ return node.key.name;
3664
+ }
3665
+ return sourceCode.getText(node.key);
3666
+ };
3667
+ var PRIMITIVE_KEYWORDS = /* @__PURE__ */ new Set([
3668
+ TSESTree12.AST_NODE_TYPES.TSStringKeyword,
3669
+ TSESTree12.AST_NODE_TYPES.TSNumberKeyword,
3670
+ TSESTree12.AST_NODE_TYPES.TSBooleanKeyword,
3671
+ TSESTree12.AST_NODE_TYPES.TSBigIntKeyword,
3672
+ TSESTree12.AST_NODE_TYPES.TSSymbolKeyword,
3673
+ TSESTree12.AST_NODE_TYPES.TSNullKeyword,
3674
+ TSESTree12.AST_NODE_TYPES.TSUndefinedKeyword
3675
+ ]);
3676
+ var isPrimitive = (type) => {
3677
+ const isPrimitiveKeyword = PRIMITIVE_KEYWORDS.has(type.type);
3678
+ if (isPrimitiveKeyword) return true;
3679
+ if (type.type === TSESTree12.AST_NODE_TYPES.TSLiteralType || type.type === TSESTree12.AST_NODE_TYPES.TSTemplateLiteralType) {
3680
+ return true;
3681
+ }
3682
+ if (type.type === TSESTree12.AST_NODE_TYPES.TSUnionType || type.type === TSESTree12.AST_NODE_TYPES.TSIntersectionType) {
3683
+ return type.types.every(isPrimitive);
3684
+ }
3685
+ return false;
3686
+ };
3687
+ var readonlyFix = (node, sourceCode) => {
3688
+ return (fixer) => {
3689
+ const firstToken = sourceCode.getFirstToken(node);
3690
+ if (!firstToken) return null;
3691
+ return fixer.insertTextBefore(firstToken, "readonly ");
3692
+ };
3693
+ };
3694
+ var readonly_type_properties_default = createRule4({
3695
+ name: "readonly-type-properties",
3696
+ meta: meta4,
3697
+ defaultOptions: [],
3698
+ create(context) {
3699
+ const { sourceCode } = context;
3700
+ const listeners = {
3701
+ TSPropertySignature(node) {
3702
+ if (node.readonly || !node.typeAnnotation) return;
3703
+ const isPrimitiveType = isPrimitive(node.typeAnnotation.typeAnnotation);
3704
+ if (!isPrimitiveType) return;
3705
+ const name = propertyName3(node, sourceCode);
3706
+ const data = { name };
3707
+ const fix = readonlyFix(node, sourceCode);
3708
+ const report = {
3709
+ node: node.key,
3710
+ messageId: "missingReadonly",
3711
+ data,
3712
+ fix
3713
+ };
3714
+ context.report(report);
3715
+ }
3716
+ };
3717
+ return listeners;
3718
+ }
3719
+ });
3720
+
3721
+ // packages/lint-suite/src/lib/typescript-local-plugin.ts
3722
+ var localPlugin = definePlugin("local", {
3723
+ "explicit-accessibility": explicit_accessibility_default,
3724
+ "readonly-type-properties": readonly_type_properties_default,
3725
+ "no-inline-object-types": no_inline_object_types_default
3726
+ });
3727
+
3728
+ // packages/lint-suite/src/lib/typescript-safety.ts
3729
+ var typescriptSafety = defineConfig18([
1479
3730
  {
1480
- files: ["**/*.stories.ts"],
3731
+ files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
3732
+ plugins: { local: localPlugin },
1481
3733
  rules: {
1482
- "@typescript-eslint/consistent-type-assertions": "off",
1483
- "@typescript-eslint/naming-convention": "off"
3734
+ "local/explicit-accessibility": "error",
3735
+ "local/readonly-type-properties": "error",
3736
+ "local/no-inline-object-types": "error",
3737
+ "@typescript-eslint/explicit-function-return-type": "error",
3738
+ "@typescript-eslint/no-empty-function": "error",
3739
+ "@typescript-eslint/no-unused-vars": "error",
3740
+ "@typescript-eslint/no-use-before-define": "error",
3741
+ "@typescript-eslint/no-explicit-any": "error",
3742
+ "@typescript-eslint/no-unsafe-assignment": "error",
3743
+ "@typescript-eslint/no-unsafe-call": "error",
3744
+ "@typescript-eslint/no-unsafe-member-access": "error",
3745
+ "@typescript-eslint/no-unsafe-return": "error",
3746
+ "@typescript-eslint/no-unsafe-argument": "error",
3747
+ "@typescript-eslint/no-empty-object-type": "error",
3748
+ "@typescript-eslint/no-unsafe-function-type": "error",
3749
+ "@typescript-eslint/no-wrapper-object-types": "error",
3750
+ "@typescript-eslint/explicit-module-boundary-types": "error",
3751
+ "@typescript-eslint/no-non-null-assertion": "error",
3752
+ "@typescript-eslint/prefer-nullish-coalescing": "error",
3753
+ "@typescript-eslint/prefer-optional-chain": "error",
3754
+ "@typescript-eslint/no-floating-promises": [
3755
+ "error",
3756
+ { ignoreVoid: true }
3757
+ ],
3758
+ "@typescript-eslint/no-misused-promises": "warn",
3759
+ "@typescript-eslint/promise-function-async": "error",
3760
+ "@typescript-eslint/await-thenable": "error",
3761
+ "@typescript-eslint/no-inferrable-types": "warn",
3762
+ "@typescript-eslint/no-require-imports": "error",
3763
+ "@typescript-eslint/only-throw-error": "error",
3764
+ "@typescript-eslint/no-array-delete": "error",
3765
+ "@typescript-eslint/return-await": ["error", "in-try-catch"],
3766
+ "@typescript-eslint/require-await": "error",
3767
+ "@typescript-eslint/restrict-template-expressions": [
3768
+ "error",
3769
+ { allowNumber: true, allowBoolean: true }
3770
+ ],
3771
+ "@typescript-eslint/restrict-plus-operands": "error",
3772
+ "@typescript-eslint/prefer-as-const": "error",
3773
+ "@typescript-eslint/no-shadow": "error",
3774
+ "@typescript-eslint/switch-exhaustiveness-check": "error",
3775
+ "@typescript-eslint/no-unused-expressions": [
3776
+ "error",
3777
+ { allowShortCircuit: true, allowTernary: true }
3778
+ ],
3779
+ "@typescript-eslint/no-useless-constructor": "error",
3780
+ "@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
3781
+ "@typescript-eslint/no-unnecessary-condition": "error",
3782
+ "@typescript-eslint/no-confusing-void-expression": [
3783
+ "error",
3784
+ { ignoreArrowShorthand: true, ignoreVoidOperator: true }
3785
+ ],
3786
+ "@typescript-eslint/no-dynamic-delete": "error",
3787
+ "@typescript-eslint/no-extraneous-class": [
3788
+ "error",
3789
+ { allowWithDecorator: true }
3790
+ ],
3791
+ "@typescript-eslint/no-invalid-void-type": "error",
3792
+ "@typescript-eslint/unified-signatures": "error",
3793
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
3794
+ "@typescript-eslint/prefer-find": "error",
3795
+ "@typescript-eslint/prefer-for-of": "error",
3796
+ "@typescript-eslint/prefer-includes": "error",
3797
+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
3798
+ "@typescript-eslint/prefer-regexp-exec": "error"
1484
3799
  }
1485
3800
  }
1486
3801
  ]);
1487
3802
 
3803
+ // packages/lint-suite/src/lib/typescript.ts
3804
+ var typescript = defineConfig19([
3805
+ ...typescriptRecommended,
3806
+ ...typescriptSafety,
3807
+ ...typescriptConsistency,
3808
+ ...typescriptImports,
3809
+ ...typescriptOverrides
3810
+ ]);
3811
+
1488
3812
  // packages/lint-suite/src/lib/vitest.ts
1489
3813
  import vitestEslint from "@vitest/eslint-plugin";
1490
- import { defineConfig as defineConfig10 } from "eslint/config";
3814
+ import { defineConfig as defineConfig20 } from "eslint/config";
1491
3815
  var vitestPlugin = {
1492
3816
  vitest: vitestEslint
1493
3817
  };
1494
- var vitest = defineConfig10([
3818
+ var vitest = defineConfig20([
1495
3819
  {
1496
3820
  files: ["**/*.spec.ts", "**/*.spec.js", "**/*.test.ts", "**/*.test.js"],
1497
3821
  plugins: vitestPlugin,
@@ -1514,147 +3838,6 @@ var vitest = defineConfig10([
1514
3838
  }
1515
3839
  ]);
1516
3840
 
1517
- // packages/lint-suite/src/lib/playwright.ts
1518
- import playwrightEslint from "eslint-plugin-playwright";
1519
- import { defineConfig as defineConfig11 } from "eslint/config";
1520
- var playwright = defineConfig11([
1521
- {
1522
- ...playwrightEslint.configs["flat/recommended"],
1523
- files: ["**/*.e2e.ts", "**/*.e2e.js"],
1524
- rules: {
1525
- ...playwrightEslint.configs["flat/recommended"].rules,
1526
- // Locator best practices
1527
- "playwright/prefer-locator": "error",
1528
- "playwright/prefer-native-locators": "error",
1529
- "playwright/no-nth-methods": "warn",
1530
- // Matcher improvements
1531
- "playwright/prefer-to-be": "error",
1532
- "playwright/prefer-to-have-count": "error",
1533
- "playwright/prefer-to-have-length": "error",
1534
- "playwright/prefer-comparison-matcher": "error",
1535
- // Test structure
1536
- "playwright/prefer-hooks-on-top": "error",
1537
- "playwright/require-top-level-describe": "warn"
1538
- }
1539
- }
1540
- ]);
1541
-
1542
- // packages/lint-suite/src/lib/boundaries.ts
1543
- import boundariesPlugin from "eslint-plugin-boundaries";
1544
- import { defineConfig as defineConfig12 } from "eslint/config";
1545
- var boundaries = defineConfig12([
1546
- {
1547
- files: [
1548
- "**/*.js",
1549
- "**/*.jsx",
1550
- "**/*.ts",
1551
- "**/*.tsx",
1552
- "**/*.cts",
1553
- "**/*.mts"
1554
- ],
1555
- plugins: {
1556
- boundaries: boundariesPlugin
1557
- },
1558
- settings: {
1559
- "boundaries/elements": [
1560
- {
1561
- type: "feature",
1562
- pattern: "**/feature/**"
1563
- },
1564
- {
1565
- type: "data-access",
1566
- pattern: "**/data-access/**"
1567
- },
1568
- {
1569
- type: "domain-logic",
1570
- pattern: "**/domain-logic/**"
1571
- },
1572
- {
1573
- type: "ui",
1574
- pattern: "**/ui/**"
1575
- },
1576
- {
1577
- type: "common",
1578
- pattern: "**/common/**"
1579
- },
1580
- {
1581
- type: "utils",
1582
- pattern: "**/utils/**"
1583
- }
1584
- ],
1585
- "import/resolver": {
1586
- typescript: {
1587
- alwaysTryTypes: true
1588
- }
1589
- }
1590
- },
1591
- rules: {
1592
- ...boundariesPlugin.configs.recommended.rules,
1593
- "boundaries/dependencies": [
1594
- "error",
1595
- {
1596
- default: "disallow",
1597
- message: "{{to.element.types}} is not allowed to be imported in {{from.element.types}}",
1598
- policies: [
1599
- {
1600
- from: { element: { type: "feature" } },
1601
- allow: [
1602
- { to: { element: { type: "feature" } } },
1603
- { to: { element: { type: "ui" } } },
1604
- { to: { element: { type: "domain-logic" } } },
1605
- { to: { element: { type: "utils" } } },
1606
- { to: { element: { type: "common" } } }
1607
- ]
1608
- },
1609
- {
1610
- from: { element: { type: "domain-logic" } },
1611
- allow: [
1612
- { to: { element: { type: "domain-logic" } } },
1613
- { to: { element: { type: "data-access" } } },
1614
- { to: { element: { type: "utils" } } },
1615
- { to: { element: { type: "common" } } }
1616
- ]
1617
- },
1618
- {
1619
- from: { element: { type: "data-access" } },
1620
- allow: [
1621
- { to: { element: { type: "data-access" } } },
1622
- { to: { element: { type: "utils" } } },
1623
- { to: { element: { type: "common" } } }
1624
- ]
1625
- },
1626
- {
1627
- from: { element: { type: "ui" } },
1628
- allow: [
1629
- { to: { element: { type: "ui" } } },
1630
- { to: { element: { type: "utils" } } },
1631
- { to: { element: { type: "common" } } }
1632
- ]
1633
- },
1634
- {
1635
- from: { element: { type: "utils" } },
1636
- allow: [
1637
- { to: { element: { type: "utils" } } },
1638
- { to: { element: { type: "common" } } }
1639
- ]
1640
- },
1641
- {
1642
- from: { element: { type: "common" } },
1643
- allow: [{ to: { element: { type: "common" } } }]
1644
- }
1645
- ]
1646
- }
1647
- ]
1648
- }
1649
- },
1650
- {
1651
- files: ["**/*.stories.ts"],
1652
- rules: {
1653
- "boundaries/dependencies": "off"
1654
- }
1655
- }
1656
- ]);
1657
-
1658
3841
  // packages/lint-suite/src/eslint.ts
1659
3842
  var recommended = [
1660
3843
  ...base,