lint-suite 1.6.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/README.md +117 -20
  2. package/eslint.cjs +3312 -492
  3. package/eslint.cjs.map +4 -4
  4. package/eslint.js +3304 -445
  5. package/eslint.js.map +4 -4
  6. package/lib/angular-template.d.ts +0 -1
  7. package/lib/angular-template.d.ts.map +1 -1
  8. package/lib/angular.d.ts +0 -1
  9. package/lib/angular.d.ts.map +1 -1
  10. package/lib/base-limits.d.ts.map +1 -1
  11. package/lib/rules/arrow-body-fits-line/arrow-body-fits-line.d.ts +6 -0
  12. package/lib/rules/arrow-body-fits-line/arrow-body-fits-line.d.ts.map +1 -0
  13. package/lib/rules/chain-fits-line/chain-fits-line.d.ts +6 -0
  14. package/lib/rules/chain-fits-line/chain-fits-line.d.ts.map +1 -0
  15. package/lib/rules/chain-receiver-is-name/chain-receiver-is-name.d.ts +7 -0
  16. package/lib/rules/chain-receiver-is-name/chain-receiver-is-name.d.ts.map +1 -0
  17. package/lib/rules/common/class-usage.type.d.ts +1 -0
  18. package/lib/rules/common/class-usage.type.d.ts.map +1 -1
  19. package/lib/rules/common/file-cache.type.d.ts +11 -0
  20. package/lib/rules/common/file-cache.type.d.ts.map +1 -0
  21. package/lib/rules/common/glob-matcher.type.d.ts +2 -0
  22. package/lib/rules/common/glob-matcher.type.d.ts.map +1 -0
  23. package/lib/rules/component-metadata.d.ts +2 -4
  24. package/lib/rules/component-metadata.d.ts.map +1 -1
  25. package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts +1 -1
  26. package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts.map +1 -1
  27. package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts +4 -1
  28. package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts.map +1 -1
  29. package/lib/rules/file-cache.d.ts +14 -0
  30. package/lib/rules/file-cache.d.ts.map +1 -0
  31. package/lib/rules/max-condition-operands/max-condition-operands.d.ts +10 -0
  32. package/lib/rules/max-condition-operands/max-condition-operands.d.ts.map +1 -0
  33. package/lib/rules/no-call-in-condition/common/no-call-in-condition.const.d.ts +6 -0
  34. package/lib/rules/no-call-in-condition/common/no-call-in-condition.const.d.ts.map +1 -0
  35. package/lib/rules/no-call-in-condition/common/no-call-in-condition.type.d.ts +21 -0
  36. package/lib/rules/no-call-in-condition/common/no-call-in-condition.type.d.ts.map +1 -0
  37. package/lib/rules/no-call-in-condition/no-call-in-condition.d.ts +7 -0
  38. package/lib/rules/no-call-in-condition/no-call-in-condition.d.ts.map +1 -0
  39. package/lib/rules/no-call-in-condition/utils/call-exemptions.util.d.ts +4 -0
  40. package/lib/rules/no-call-in-condition/utils/call-exemptions.util.d.ts.map +1 -0
  41. package/lib/rules/no-call-in-condition/utils/condition-calls.util.d.ts +7 -0
  42. package/lib/rules/no-call-in-condition/utils/condition-calls.util.d.ts.map +1 -0
  43. package/lib/rules/no-grouped-condition/no-grouped-condition.d.ts +7 -0
  44. package/lib/rules/no-grouped-condition/no-grouped-condition.d.ts.map +1 -0
  45. package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts.map +1 -1
  46. package/lib/rules/no-inline-return-object/no-inline-return-object.d.ts +6 -0
  47. package/lib/rules/no-inline-return-object/no-inline-return-object.d.ts.map +1 -0
  48. package/lib/rules/no-nested-object-value/common/no-nested-object-value.const.d.ts +5 -0
  49. package/lib/rules/no-nested-object-value/common/no-nested-object-value.const.d.ts.map +1 -0
  50. package/lib/rules/no-nested-object-value/common/no-nested-object-value.type.d.ts +7 -0
  51. package/lib/rules/no-nested-object-value/common/no-nested-object-value.type.d.ts.map +1 -0
  52. package/lib/rules/no-nested-object-value/no-nested-object-value.d.ts +7 -0
  53. package/lib/rules/no-nested-object-value/no-nested-object-value.d.ts.map +1 -0
  54. package/lib/rules/no-nested-object-value/utils/decorator-config.util.d.ts +3 -0
  55. package/lib/rules/no-nested-object-value/utils/decorator-config.util.d.ts.map +1 -0
  56. package/lib/rules/no-spread-expression/no-spread-expression.d.ts +6 -0
  57. package/lib/rules/no-spread-expression/no-spread-expression.d.ts.map +1 -0
  58. package/lib/rules/no-unstyled-classes/no-unstyled-classes.d.ts.map +1 -1
  59. package/lib/rules/no-unstyled-classes/styles/component-stylesheets.d.ts.map +1 -1
  60. package/lib/rules/no-unstyled-classes/styles/linked-stylesheets.d.ts +3 -0
  61. package/lib/rules/no-unstyled-classes/styles/linked-stylesheets.d.ts.map +1 -0
  62. package/lib/rules/no-unstyled-classes/styles/stylesheet-classes.d.ts.map +1 -1
  63. package/lib/rules/no-unstyled-classes/styles/template-stylesheets.d.ts +1 -1
  64. package/lib/rules/no-unstyled-classes/styles/template-stylesheets.d.ts.map +1 -1
  65. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts +5 -0
  66. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts.map +1 -1
  67. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.d.ts.map +1 -1
  68. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts +0 -1
  69. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts.map +1 -1
  70. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts.map +1 -1
  71. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.d.ts.map +1 -1
  72. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts +6 -8
  73. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts.map +1 -1
  74. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts +3 -14
  75. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts.map +1 -1
  76. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.d.ts.map +1 -1
  77. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts +3 -2
  78. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts.map +1 -1
  79. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts +1 -1
  80. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts.map +1 -1
  81. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts +8 -4
  82. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts.map +1 -1
  83. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts +3 -2
  84. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts.map +1 -1
  85. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts.map +1 -1
  86. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts.map +1 -1
  87. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/class-members.util.d.ts +10 -0
  88. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/class-members.util.d.ts.map +1 -0
  89. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts.map +1 -1
  90. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.d.ts.map +1 -1
  91. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.d.ts.map +1 -1
  92. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts +2 -1
  93. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts.map +1 -1
  94. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts.map +1 -1
  95. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.d.ts.map +1 -1
  96. package/lib/rules/no-unused-classes/no-unused-classes.d.ts.map +1 -1
  97. package/lib/rules/no-unused-classes/styles/linked-templates.d.ts +9 -0
  98. package/lib/rules/no-unused-classes/styles/linked-templates.d.ts.map +1 -0
  99. package/lib/rules/no-unused-classes/styles/stylesheet-components.d.ts.map +1 -1
  100. package/lib/rules/no-unused-classes/template/template-usage.d.ts.map +1 -1
  101. package/lib/rules/no-unused-exports/common/no-unused-exports.const.d.ts +7 -0
  102. package/lib/rules/no-unused-exports/common/no-unused-exports.const.d.ts.map +1 -0
  103. package/lib/rules/no-unused-exports/common/no-unused-exports.type.d.ts +70 -0
  104. package/lib/rules/no-unused-exports/common/no-unused-exports.type.d.ts.map +1 -0
  105. package/lib/rules/no-unused-exports/export-index.d.ts +6 -0
  106. package/lib/rules/no-unused-exports/export-index.d.ts.map +1 -0
  107. package/lib/rules/no-unused-exports/no-unused-exports.d.ts +7 -0
  108. package/lib/rules/no-unused-exports/no-unused-exports.d.ts.map +1 -0
  109. package/lib/rules/no-unused-exports/report-unused-exports.d.ts +12 -0
  110. package/lib/rules/no-unused-exports/report-unused-exports.d.ts.map +1 -0
  111. package/lib/rules/no-unused-exports/utils/aggregate-update.util.d.ts +4 -0
  112. package/lib/rules/no-unused-exports/utils/aggregate-update.util.d.ts.map +1 -0
  113. package/lib/rules/no-unused-exports/utils/export-edges.util.d.ts +5 -0
  114. package/lib/rules/no-unused-exports/utils/export-edges.util.d.ts.map +1 -0
  115. package/lib/rules/no-unused-exports/utils/export-nodes.util.d.ts +5 -0
  116. package/lib/rules/no-unused-exports/utils/export-nodes.util.d.ts.map +1 -0
  117. package/lib/rules/no-unused-exports/utils/export-usage.util.d.ts +4 -0
  118. package/lib/rules/no-unused-exports/utils/export-usage.util.d.ts.map +1 -0
  119. package/lib/rules/no-unused-exports/utils/exported-names.util.d.ts +3 -0
  120. package/lib/rules/no-unused-exports/utils/exported-names.util.d.ts.map +1 -0
  121. package/lib/rules/no-unused-exports/utils/file-changes.util.d.ts +4 -0
  122. package/lib/rules/no-unused-exports/utils/file-changes.util.d.ts.map +1 -0
  123. package/lib/rules/no-unused-exports/utils/module-edges.util.d.ts +4 -0
  124. package/lib/rules/no-unused-exports/utils/module-edges.util.d.ts.map +1 -0
  125. package/lib/rules/no-unused-exports/utils/module-target.util.d.ts +4 -0
  126. package/lib/rules/no-unused-exports/utils/module-target.util.d.ts.map +1 -0
  127. package/lib/rules/no-unused-exports/utils/public-exports.util.d.ts +3 -0
  128. package/lib/rules/no-unused-exports/utils/public-exports.util.d.ts.map +1 -0
  129. package/lib/rules/one-line-guard/one-line-guard.d.ts.map +1 -1
  130. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts +2 -1
  131. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts.map +1 -1
  132. package/lib/rules/{utils/selector-classes.util.d.ts → selector-classes.d.ts} +2 -2
  133. package/lib/rules/selector-classes.d.ts.map +1 -0
  134. package/lib/rules/ternary-branch-shape/ternary-branch-shape.d.ts +6 -0
  135. package/lib/rules/ternary-branch-shape/ternary-branch-shape.d.ts.map +1 -0
  136. package/lib/rules/test-file-shape/common/test-file-shape.const.d.ts +14 -0
  137. package/lib/rules/test-file-shape/common/test-file-shape.const.d.ts.map +1 -0
  138. package/lib/rules/test-file-shape/common/test-file-shape.type.d.ts +7 -0
  139. package/lib/rules/test-file-shape/common/test-file-shape.type.d.ts.map +1 -0
  140. package/lib/rules/test-file-shape/mock-exports.d.ts +7 -0
  141. package/lib/rules/test-file-shape/mock-exports.d.ts.map +1 -0
  142. package/lib/rules/test-file-shape/stub-exports.d.ts +7 -0
  143. package/lib/rules/test-file-shape/stub-exports.d.ts.map +1 -0
  144. package/lib/rules/test-file-shape/test-file-shape.d.ts +7 -0
  145. package/lib/rules/test-file-shape/test-file-shape.d.ts.map +1 -0
  146. package/lib/rules/type-placement/common/type-placement.const.d.ts +5 -0
  147. package/lib/rules/type-placement/common/type-placement.const.d.ts.map +1 -0
  148. package/lib/rules/type-placement/common/type-placement.type.d.ts +7 -0
  149. package/lib/rules/type-placement/common/type-placement.type.d.ts.map +1 -0
  150. package/lib/rules/type-placement/type-placement.d.ts +7 -0
  151. package/lib/rules/type-placement/type-placement.d.ts.map +1 -0
  152. package/lib/rules/util-purity/common/util-purity.const.d.ts +10 -0
  153. package/lib/rules/util-purity/common/util-purity.const.d.ts.map +1 -0
  154. package/lib/rules/util-purity/common/util-purity.type.d.ts +7 -0
  155. package/lib/rules/util-purity/common/util-purity.type.d.ts.map +1 -0
  156. package/lib/rules/util-purity/util-purity.d.ts +7 -0
  157. package/lib/rules/util-purity/util-purity.d.ts.map +1 -0
  158. package/lib/rules/utils/class-expression-literals.util.d.ts.map +1 -1
  159. package/lib/rules/utils/enclosing-statement.util.d.ts +3 -0
  160. package/lib/rules/utils/enclosing-statement.util.d.ts.map +1 -0
  161. package/lib/rules/utils/glob-matcher.util.d.ts +3 -0
  162. package/lib/rules/utils/glob-matcher.util.d.ts.map +1 -0
  163. package/lib/rules/utils/indent-of.util.d.ts +3 -0
  164. package/lib/rules/utils/indent-of.util.d.ts.map +1 -0
  165. package/lib/rules/utils/template-classes.util.d.ts.map +1 -1
  166. package/lib/rules/utils/template-links.util.d.ts +2 -0
  167. package/lib/rules/utils/template-links.util.d.ts.map +1 -0
  168. package/lib/rules/utils/to-regexp.util.d.ts +2 -0
  169. package/lib/rules/utils/to-regexp.util.d.ts.map +1 -0
  170. package/lib/typescript-local-plugin.d.ts.map +1 -1
  171. package/lib/typescript-safety.d.ts.map +1 -1
  172. package/lib/typescript.d.ts +0 -1
  173. package/lib/typescript.d.ts.map +1 -1
  174. package/lib/vitest.d.ts.map +1 -1
  175. package/package.json +1 -1
  176. package/stylelint.cjs +303 -99
  177. package/stylelint.cjs.map +4 -4
  178. package/stylelint.d.ts.map +1 -1
  179. package/stylelint.js +321 -111
  180. package/stylelint.js.map +4 -4
  181. package/lib/rules/no-unused-classes/styles/component-descriptors.d.ts +0 -3
  182. package/lib/rules/no-unused-classes/styles/component-descriptors.d.ts.map +0 -1
  183. package/lib/rules/utils/selector-classes.util.d.ts.map +0 -1
package/eslint.js CHANGED
@@ -4,8 +4,8 @@ import { defineConfig } from "eslint/config";
4
4
 
5
5
  // packages/lint-suite/src/lib/rules/define-plugin.util.ts
6
6
  var definePlugin = (name, rules) => {
7
- const meta7 = { name };
8
- const plugin = { meta: meta7, rules };
7
+ const meta21 = { name };
8
+ const plugin = { meta: meta21, rules };
9
9
  return plugin;
10
10
  };
11
11
 
@@ -14,10 +14,9 @@ import { ESLintUtils } from "@typescript-eslint/utils";
14
14
 
15
15
  // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/component-stylesheets.ts
16
16
  import { existsSync } from "node:fs";
17
- import { dirname, resolve } from "node:path";
17
+ import { dirname as dirname2, resolve } from "node:path";
18
18
 
19
19
  // packages/lint-suite/src/lib/rules/component-metadata.ts
20
- import { readFileSync } from "node:fs";
21
20
  import {
22
21
  ScriptTarget,
23
22
  canHaveDecorators,
@@ -31,6 +30,114 @@ import {
31
30
  isPropertyAssignment,
32
31
  isStringLiteralLike
33
32
  } from "typescript";
33
+
34
+ // packages/lint-suite/src/lib/rules/file-cache.ts
35
+ import {
36
+ mkdirSync,
37
+ readFileSync,
38
+ renameSync,
39
+ statSync,
40
+ writeFileSync
41
+ } from "node:fs";
42
+ import { dirname, join } from "node:path";
43
+ var CACHE_FORMAT_VERSION = 1;
44
+ var caches = /* @__PURE__ */ new Set();
45
+ var isFlushRegistered = false;
46
+ var cacheDirectory = () => {
47
+ const { LINT_SUITE_CACHE, LINT_SUITE_CACHE_DIR } = process.env;
48
+ const isDisabled = LINT_SUITE_CACHE === "0";
49
+ if (isDisabled) return null;
50
+ if (LINT_SUITE_CACHE_DIR) return LINT_SUITE_CACHE_DIR;
51
+ return join(process.cwd(), "node_modules", ".cache", "lint-suite");
52
+ };
53
+ var cacheFile = (name) => {
54
+ const directory = cacheDirectory();
55
+ if (directory === null) return null;
56
+ return join(directory, `${name}.v${CACHE_FORMAT_VERSION}.json`);
57
+ };
58
+ var isCachedFile = (value) => {
59
+ const isObject = typeof value === "object" && value !== null;
60
+ if (!isObject) return false;
61
+ const hasVersion = "version" in value && typeof value.version === "string";
62
+ return hasVersion && "value" in value;
63
+ };
64
+ var storedEntries = (text2) => {
65
+ const entries = /* @__PURE__ */ new Map();
66
+ const parsed = JSON.parse(text2);
67
+ const isRecord = typeof parsed === "object" && parsed !== null;
68
+ if (!isRecord) return entries;
69
+ for (const [path, entry] of Object.entries(parsed)) {
70
+ if (isCachedFile(entry)) entries.set(path, entry);
71
+ }
72
+ return entries;
73
+ };
74
+ var load = (cache2) => {
75
+ cache2.loaded = true;
76
+ const file = cacheFile(cache2.name);
77
+ if (file === null) return;
78
+ try {
79
+ const stored = storedEntries(readFileSync(file, "utf8"));
80
+ for (const [path, entry] of stored) cache2.entries.set(path, entry);
81
+ } catch {
82
+ return;
83
+ }
84
+ };
85
+ var write = (cache2) => {
86
+ const file = cacheFile(cache2.name);
87
+ if (file === null) return;
88
+ try {
89
+ mkdirSync(dirname(file), { recursive: true });
90
+ const temporary = `${file}.${process.pid}.tmp`;
91
+ const stored = Object.fromEntries(cache2.entries);
92
+ writeFileSync(temporary, JSON.stringify(stored));
93
+ renameSync(temporary, file);
94
+ } catch {
95
+ return;
96
+ }
97
+ };
98
+ var flushFileCaches = () => {
99
+ for (const cache2 of caches) {
100
+ if (!cache2.dirty) continue;
101
+ write(cache2);
102
+ cache2.dirty = false;
103
+ }
104
+ };
105
+ var registerFlush = () => {
106
+ if (isFlushRegistered) return;
107
+ isFlushRegistered = true;
108
+ process.once("exit", flushFileCaches);
109
+ };
110
+ var createFileCache = (name) => {
111
+ const cache2 = {
112
+ name,
113
+ entries: /* @__PURE__ */ new Map(),
114
+ loaded: false,
115
+ dirty: false
116
+ };
117
+ caches.add(cache2);
118
+ registerFlush();
119
+ return cache2;
120
+ };
121
+ var readCached = (cache2, path, parse2) => {
122
+ if (!cache2.loaded) load(cache2);
123
+ try {
124
+ const stats = statSync(path, { bigint: true });
125
+ const version = `${stats.mtimeNs}:${stats.size}`;
126
+ const cached = cache2.entries.get(path);
127
+ if (cached?.version === version) return cached.value;
128
+ const value = parse2(readFileSync(path, "utf8"), path);
129
+ const fresh = { value, version };
130
+ cache2.entries.set(path, fresh);
131
+ cache2.dirty = true;
132
+ return value;
133
+ } catch {
134
+ return null;
135
+ }
136
+ };
137
+
138
+ // packages/lint-suite/src/lib/rules/component-metadata.ts
139
+ var COMPONENT_MARKER = "@Component";
140
+ var cache = createFileCache("component-metadata");
34
141
  var componentArgument = (decorator) => {
35
142
  const { expression } = decorator;
36
143
  if (!isCallExpression(expression)) return null;
@@ -51,25 +158,6 @@ var decoratedMetadata = (node) => {
51
158
  }
52
159
  return null;
53
160
  };
54
- var parseComponentMetadata = (text2, path) => {
55
- const found = [];
56
- const visit = (node) => {
57
- const metadata = decoratedMetadata(node);
58
- if (metadata !== null) found.push(metadata);
59
- forEachChild(node, visit);
60
- };
61
- const source = createSourceFile(path, text2, ScriptTarget.Latest, true);
62
- forEachChild(source, visit);
63
- return found;
64
- };
65
- var componentMetadata = (path) => {
66
- try {
67
- const text2 = readFileSync(path, "utf8");
68
- return parseComponentMetadata(text2, path);
69
- } catch {
70
- return [];
71
- }
72
- };
73
161
  var literalText = (node) => {
74
162
  if (!isStringLiteralLike(node)) return null;
75
163
  return node.text;
@@ -99,21 +187,51 @@ var metadataTexts = (metadata, name) => {
99
187
  if (value === null) return [];
100
188
  return literalTexts(value);
101
189
  };
190
+ var toDescriptor = (metadata) => {
191
+ const [templateUrl] = metadataTexts(metadata, "templateUrl");
192
+ const [template] = metadataTexts(metadata, "template");
193
+ const styleUrl = metadataTexts(metadata, "styleUrl");
194
+ const listedStyleUrls = metadataTexts(metadata, "styleUrls");
195
+ const styleUrls = [...styleUrl, ...listedStyleUrls];
196
+ const styles = metadataTexts(metadata, "styles");
197
+ const descriptor = {
198
+ templateUrl: templateUrl ?? null,
199
+ template: template ?? null,
200
+ styleUrls,
201
+ styles
202
+ };
203
+ return descriptor;
204
+ };
205
+ var parseComponentMetadata = (text2, path) => {
206
+ const found = [];
207
+ const hasComponent = text2.includes(COMPONENT_MARKER);
208
+ if (!hasComponent) return found;
209
+ const visit = (node) => {
210
+ const metadata = decoratedMetadata(node);
211
+ if (metadata !== null) found.push(toDescriptor(metadata));
212
+ forEachChild(node, visit);
213
+ };
214
+ const source = createSourceFile(path, text2, ScriptTarget.Latest, true);
215
+ forEachChild(source, visit);
216
+ return found;
217
+ };
218
+ var componentMetadata = (path) => {
219
+ return readCached(cache, path, parseComponentMetadata) ?? [];
220
+ };
102
221
 
103
222
  // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/component-stylesheets.ts
104
223
  var SIBLING_EXTENSIONS = [".scss", ".css"];
105
224
  var HTML_EXTENSION = /\.html$/iu;
106
225
  var matchesTemplate = (metadata, directory, templateFilename) => {
107
- const urls = metadataTexts(metadata, "templateUrl");
108
- const [templateUrl] = urls;
109
- if (templateUrl === void 0) return false;
226
+ const { templateUrl } = metadata;
227
+ if (templateUrl === null) return false;
110
228
  const declared = resolve(directory, templateUrl);
111
229
  const linted = resolve(templateFilename);
112
230
  return declared === linted;
113
231
  };
114
232
  var templateMetadata = (componentPath, templateFilename) => {
115
233
  const components = componentMetadata(componentPath);
116
- const directory = dirname(componentPath);
234
+ const directory = dirname2(componentPath);
117
235
  const matches = (metadata) => {
118
236
  return matchesTemplate(metadata, directory, templateFilename);
119
237
  };
@@ -149,20 +267,16 @@ var inlineSources = (styles, componentPath) => {
149
267
  var metadataSources = (componentPath, templateFilename) => {
150
268
  const metadata = templateMetadata(componentPath, templateFilename);
151
269
  if (metadata === null) return [];
152
- const singleUrl = metadataTexts(metadata, "styleUrl");
153
- const listedUrls = metadataTexts(metadata, "styleUrls");
154
- const styles = metadataTexts(metadata, "styles");
155
- const directory = dirname(componentPath);
156
- const urls = [...singleUrl, ...listedUrls];
157
- const files = stylesheetFiles(urls, directory);
158
- const inline = inlineSources(styles, componentPath);
270
+ const directory = dirname2(componentPath);
271
+ const files = stylesheetFiles(metadata.styleUrls, directory);
272
+ const inline = inlineSources(metadata.styles, componentPath);
159
273
  return [...files, ...inline];
160
274
  };
161
275
  var siblingSources = (templateFilename) => {
162
276
  const base2 = templateFilename.replace(HTML_EXTENSION, "");
163
277
  const candidateOf = (extension) => `${base2}${extension}`;
164
278
  const candidates = SIBLING_EXTENSIONS.map(candidateOf);
165
- return stylesheetFiles(candidates, dirname(templateFilename));
279
+ return stylesheetFiles(candidates, dirname2(templateFilename));
166
280
  };
167
281
  var componentStylesheets = (templateFilename) => {
168
282
  const componentPath = templateFilename.replace(HTML_EXTENSION, ".ts");
@@ -171,14 +285,40 @@ var componentStylesheets = (templateFilename) => {
171
285
  return siblingSources(templateFilename);
172
286
  };
173
287
 
174
- // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/stylesheet-classes.ts
175
- import { readFileSync as readFileSync2, statSync } from "node:fs";
288
+ // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/linked-stylesheets.ts
176
289
  import { dirname as dirname3 } from "node:path";
290
+
291
+ // packages/lint-suite/src/lib/rules/utils/template-links.util.ts
292
+ var LINK_TAG = /<link\b[^>]*>/giu;
293
+ var STYLESHEET_REL = /\brel\s*=\s*["']stylesheet["']/iu;
294
+ var HREF = /\bhref\s*=\s*["']([^"']*)["']/iu;
295
+ var UNRESOLVABLE = /^(?:\/|[^:/?#]+:)/u;
296
+ var hrefOf = (tag) => {
297
+ const isStylesheet = STYLESHEET_REL.test(tag);
298
+ if (!isStylesheet) return null;
299
+ const href = HREF.exec(tag)?.[1] ?? "";
300
+ const isUnresolvable = UNRESOLVABLE.test(href);
301
+ const isLocal = href.length > 0 && !isUnresolvable;
302
+ return isLocal ? href : null;
303
+ };
304
+ var linkedStylesheetHrefs = (template) => {
305
+ const tags = template.match(LINK_TAG) ?? [];
306
+ return tags.map(hrefOf).filter((href) => href !== null);
307
+ };
308
+
309
+ // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/linked-stylesheets.ts
310
+ var linkedStylesheets = (template, templateFilename) => {
311
+ const hrefs = linkedStylesheetHrefs(template);
312
+ return stylesheetFiles(hrefs, dirname3(templateFilename));
313
+ };
314
+
315
+ // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/stylesheet-classes.ts
316
+ import { dirname as dirname5 } from "node:path";
177
317
  import { parse } from "postcss-scss";
178
318
 
179
319
  // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/stylesheet-imports.ts
180
320
  import { existsSync as existsSync2 } from "node:fs";
181
- import { basename, dirname as dirname2, join, resolve as resolve2 } from "node:path";
321
+ import { basename, dirname as dirname4, join as join2, resolve as resolve2 } from "node:path";
182
322
  var QUOTED = /'([^']*)'|"([^"]*)"/u;
183
323
  var SKIPPED_PREFIXES = ["sass:", "http", "url("];
184
324
  var specifierOf = (params) => {
@@ -197,13 +337,13 @@ var isResolvable = (specifier) => {
197
337
  };
198
338
  var candidatesOf = (path) => {
199
339
  const isStylesheet = path.endsWith(".scss");
200
- if (isStylesheet) return [path, join(dirname2(path), `_${basename(path)}`)];
201
- const partial = join(dirname2(path), `_${basename(path)}.scss`);
340
+ if (isStylesheet) return [path, join2(dirname4(path), `_${basename(path)}`)];
341
+ const partial = join2(dirname4(path), `_${basename(path)}.scss`);
202
342
  return [
203
343
  `${path}.scss`,
204
344
  partial,
205
- join(path, "index.scss"),
206
- join(path, "_index.scss")
345
+ join2(path, "index.scss"),
346
+ join2(path, "_index.scss")
207
347
  ];
208
348
  };
209
349
  var resolveStylesheetImport = (params, directory) => {
@@ -217,7 +357,7 @@ var resolveStylesheetImport = (params, directory) => {
217
357
  return existing ?? null;
218
358
  };
219
359
 
220
- // packages/lint-suite/src/lib/rules/utils/selector-classes.util.ts
360
+ // packages/lint-suite/src/lib/rules/selector-classes.ts
221
361
  import selectorParser from "postcss-selector-parser";
222
362
  var INTERPOLATION_TOKEN = "zzinterpzz";
223
363
  var PARENT_TOKEN = "zzparentzz";
@@ -335,78 +475,104 @@ var collectStylesheet = (root, directory) => {
335
475
  return entry;
336
476
  };
337
477
 
478
+ // packages/lint-suite/src/lib/rules/utils/to-regexp.util.ts
479
+ var toRegExp = (pattern) => new RegExp(pattern, "u");
480
+
338
481
  // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/stylesheet-classes.ts
339
- var stylesheets = /* @__PURE__ */ new Map();
340
- var emptyEntry = () => {
341
- const entry = { classes: [], patterns: [], imports: [] };
342
- return entry;
343
- };
482
+ var EMPTY_ENTRY = { classes: [], patterns: [], imports: [] };
483
+ var stylesheets = createFileCache("stylesheets");
484
+ var inlineStylesheets = /* @__PURE__ */ new Map();
485
+ var merged = /* @__PURE__ */ new Map();
344
486
  var parseStylesheet = (source, path) => {
345
487
  try {
346
488
  const root = parse(source, { from: path });
347
- return collectStylesheet(root, dirname3(path));
489
+ return collectStylesheet(root, dirname5(path));
348
490
  } catch {
349
- return emptyEntry();
491
+ return EMPTY_ENTRY;
350
492
  }
351
493
  };
352
494
  var fileEntry = (path) => {
353
- try {
354
- const stats = statSync(path, { bigint: true });
355
- const version = `${stats.mtimeNs}:${stats.size}`;
356
- const cached = stylesheets.get(path);
357
- if (cached?.version === version) return cached.entry;
358
- const entry = parseStylesheet(readFileSync2(path, "utf8"), path);
359
- const fresh = { entry, version };
360
- stylesheets.set(path, fresh);
361
- return entry;
362
- } catch {
363
- return emptyEntry();
364
- }
495
+ return readCached(stylesheets, path, parseStylesheet) ?? EMPTY_ENTRY;
496
+ };
497
+ var inlineEntry = (source, path) => {
498
+ const key2 = `${path}\0${source}`;
499
+ const cached = inlineStylesheets.get(key2);
500
+ if (cached !== void 0) return cached;
501
+ const entry = parseStylesheet(source, path);
502
+ inlineStylesheets.set(key2, entry);
503
+ return entry;
365
504
  };
366
- var addEntry = (entry, sink, visited) => {
367
- for (const name of entry.classes) sink.exact.add(name);
368
- sink.patterns.push(...entry.patterns);
505
+ var collectImports = (entry, chain, visited) => {
369
506
  for (const imported of entry.imports) {
370
507
  const isVisited = visited.has(imported);
371
508
  if (isVisited) continue;
372
509
  visited.add(imported);
373
- addEntry(fileEntry(imported), sink, visited);
510
+ const importedEntry = fileEntry(imported);
511
+ chain.push(importedEntry);
512
+ collectImports(importedEntry, chain, visited);
374
513
  }
375
514
  };
376
- var addSource = (source, sink, visited) => {
515
+ var entryOf = (source) => {
516
+ if (source.kind === "file") return fileEntry(source.path);
517
+ return inlineEntry(source.source, source.path);
518
+ };
519
+ var collectChain = (source, chain, visited) => {
377
520
  if (source.kind === "file") {
378
521
  const isVisited = visited.has(source.path);
379
522
  if (isVisited) return;
380
523
  visited.add(source.path);
381
- addEntry(fileEntry(source.path), sink, visited);
382
- return;
383
524
  }
384
- addEntry(parseStylesheet(source.source, source.path), sink, visited);
385
- };
386
- var toRegExp = (pattern) => {
387
- return new RegExp(pattern, "u");
525
+ const entry = entryOf(source);
526
+ chain.push(entry);
527
+ collectImports(entry, chain, visited);
388
528
  };
389
- var stylesheetClasses = (sources) => {
529
+ var mergeChain = (chain) => {
390
530
  const exact = /* @__PURE__ */ new Set();
391
531
  const patterns = [];
392
- const sink = { exact, patterns };
393
- const visited = /* @__PURE__ */ new Set();
394
- for (const source of sources) addSource(source, sink, visited);
532
+ for (const entry of chain) {
533
+ for (const name of entry.classes) exact.add(name);
534
+ patterns.push(...entry.patterns);
535
+ }
395
536
  const compiled = patterns.map(toRegExp);
396
537
  const has = classMatcher(exact, compiled);
397
538
  const size = exact.size + compiled.length;
398
539
  const classes = { has, size };
399
540
  return classes;
400
541
  };
542
+ var isCurrentChain = (cached, chain) => {
543
+ if (cached === void 0) return false;
544
+ if (cached.chain.length !== chain.length) return false;
545
+ return cached.chain.every((entry, position) => entry === chain[position]);
546
+ };
547
+ var sourceKey = (source) => {
548
+ if (source.kind === "file") return source.path;
549
+ return `${source.path}\0${source.source}`;
550
+ };
551
+ var keyOf = (sources) => {
552
+ return sources.map(sourceKey).join("\n");
553
+ };
554
+ var stylesheetClasses = (sources) => {
555
+ const chain = [];
556
+ const visited = /* @__PURE__ */ new Set();
557
+ for (const source of sources) collectChain(source, chain, visited);
558
+ const key2 = keyOf(sources);
559
+ const cached = merged.get(key2);
560
+ if (isCurrentChain(cached, chain)) return cached.classes;
561
+ const classes = mergeChain(chain);
562
+ const fresh = { chain, classes };
563
+ merged.set(key2, fresh);
564
+ return classes;
565
+ };
401
566
 
402
567
  // packages/lint-suite/src/lib/rules/no-unstyled-classes/styles/template-stylesheets.ts
403
- var templateStylesheets = (templateFilename, cwd, globalStyles) => {
568
+ var templateStylesheets = (templateFilename, template, cwd, globalStyles) => {
404
569
  let known = null;
405
570
  const lookup = () => {
406
571
  if (known !== null) return known;
407
572
  const component = componentStylesheets(templateFilename);
573
+ const linked = linkedStylesheets(template, templateFilename);
408
574
  const global = stylesheetFiles(globalStyles, cwd);
409
- const sources = [...component, ...global];
575
+ const sources = [...component, ...linked, ...global];
410
576
  known = stylesheetClasses(sources);
411
577
  return known;
412
578
  };
@@ -497,7 +663,8 @@ var classExpressionLiterals = (source) => {
497
663
  if (ast === null) return [];
498
664
  const names = [];
499
665
  collect(ast, names);
500
- return [...new Set(names)];
666
+ const uniqueNames = new Set(names);
667
+ return [...uniqueNames];
501
668
  };
502
669
 
503
670
  // packages/lint-suite/src/lib/rules/utils/template-classes.util.ts
@@ -560,10 +727,14 @@ var templateClasses = (node) => {
560
727
  const classes = [];
561
728
  for (const attribute of node.attributes) {
562
729
  const isClassName = attribute.name === CLASS_ATTRIBUTE;
563
- if (isClassName) classes.push(...staticClasses(attribute));
730
+ if (isClassName) {
731
+ const found = staticClasses(attribute);
732
+ classes.push(...found);
733
+ }
564
734
  }
565
735
  for (const input of node.inputs) {
566
- classes.push(...inputClasses(input));
736
+ const found = inputClasses(input);
737
+ classes.push(...found);
567
738
  }
568
739
  return classes;
569
740
  };
@@ -615,7 +786,6 @@ var isTemplateServices = (services) => {
615
786
  if (!("convertNodeSourceSpanToLoc" in services)) return false;
616
787
  return typeof services.convertNodeSourceSpanToLoc === "function";
617
788
  };
618
- var toRegExp2 = (pattern) => new RegExp(pattern, "u");
619
789
  var no_unstyled_classes_default = createRule({
620
790
  name: "no-unstyled-classes",
621
791
  meta,
@@ -624,9 +794,10 @@ var no_unstyled_classes_default = createRule({
624
794
  const { parserServices } = context.sourceCode;
625
795
  if (parserServices === void 0) throw new Error(PARSER_REQUIRED);
626
796
  if (!isTemplateServices(parserServices)) throw new Error(PARSER_REQUIRED);
627
- const ignored = options.ignoreClassPatterns.map(toRegExp2);
797
+ const ignored = options.ignoreClassPatterns.map(toRegExp);
628
798
  const stylesheets2 = templateStylesheets(
629
799
  context.filename,
800
+ context.sourceCode.text,
630
801
  context.cwd,
631
802
  options.globalStyles
632
803
  );
@@ -645,12 +816,12 @@ var no_unstyled_classes_default = createRule({
645
816
  if (isStyled) continue;
646
817
  const loc = parserServices.convertNodeSourceSpanToLoc(span);
647
818
  const data = { name };
648
- const report = {
819
+ const report3 = {
649
820
  loc,
650
821
  messageId: "unstyledClass",
651
822
  data
652
823
  };
653
- context.report(report);
824
+ context.report(report3);
654
825
  }
655
826
  }
656
827
  };
@@ -671,6 +842,14 @@ var angularTemplate = defineConfig([
671
842
  "lint-suite-angular-template": lintSuiteAngularTemplatePlugin
672
843
  },
673
844
  rules: {
845
+ "max-lines": [
846
+ "error",
847
+ { max: 150, skipBlankLines: true, skipComments: true }
848
+ ],
849
+ // A template class name no stylesheet of the component selects is
850
+ // reported.
851
+ // Bad: class="ghost" with no .ghost rule
852
+ // Good: add .ghost, or drop the class
674
853
  "lint-suite-angular-template/no-unstyled-classes": "error",
675
854
  "@angular-eslint/template/prefer-template-literal": "error",
676
855
  "@angular-eslint/template/no-any": "error",
@@ -786,8 +965,8 @@ var addAngularImport = (node, imports) => {
786
965
  imports.set(specifier.local.name, null);
787
966
  } else if (specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type") {
788
967
  const { imported } = specifier;
789
- const isIdentifier9 = imported.type === TSESTree.AST_NODE_TYPES.Identifier;
790
- const importedName = isIdentifier9 ? imported.name : imported.value;
968
+ const isIdentifier11 = imported.type === TSESTree.AST_NODE_TYPES.Identifier;
969
+ const importedName = isIdentifier11 ? imported.name : imported.value;
791
970
  imports.set(specifier.local.name, importedName);
792
971
  }
793
972
  }
@@ -818,10 +997,10 @@ var angularClassImports = (ast) => {
818
997
  }
819
998
  }
820
999
  for (const imported of imports.values()) {
821
- const isNamespaceImport = imported === null;
1000
+ const isNamespaceImport2 = imported === null;
822
1001
  const isComponent = imported === "Component";
823
1002
  const isDirective = imported === "Directive";
824
- const isAngularClassImport = isNamespaceImport || isComponent || isDirective;
1003
+ const isAngularClassImport = isNamespaceImport2 || isComponent || isDirective;
825
1004
  if (isAngularClassImport) return imports;
826
1005
  }
827
1006
  return null;
@@ -841,17 +1020,20 @@ var managedApis = /* @__PURE__ */ new Set([
841
1020
  "contentChild",
842
1021
  "contentChildren"
843
1022
  ]);
1023
+ var isManualCleanupDisabledProperty = (property) => {
1024
+ if (property.type !== TSESTree2.AST_NODE_TYPES.Property || property.computed) {
1025
+ return false;
1026
+ }
1027
+ const isIdentifierManualCleanup = property.key.type === TSESTree2.AST_NODE_TYPES.Identifier && property.key.name === "manualCleanup";
1028
+ const isLiteralManualCleanup = property.key.type === TSESTree2.AST_NODE_TYPES.Literal && property.key.value === "manualCleanup";
1029
+ const manualCleanup = isIdentifierManualCleanup || isLiteralManualCleanup;
1030
+ return !manualCleanup || property.value.type === TSESTree2.AST_NODE_TYPES.Literal && property.value.value === false;
1031
+ };
844
1032
  var hasAutomaticEffectCleanup = (node) => {
845
1033
  const options = node.arguments.at(1);
846
1034
  if (options === void 0) return true;
847
1035
  if (options.type !== TSESTree2.AST_NODE_TYPES.ObjectExpression) return false;
848
- return options.properties.every((property) => {
849
- if (property.type !== TSESTree2.AST_NODE_TYPES.Property || property.computed) {
850
- return false;
851
- }
852
- 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";
853
- return !manualCleanup || property.value.type === TSESTree2.AST_NODE_TYPES.Literal && property.value.value === false;
854
- });
1036
+ return options.properties.every(isManualCleanupDisabledProperty);
855
1037
  };
856
1038
  var angularCoreImportedName = (definition) => {
857
1039
  if (definition?.type !== "ImportBinding") return null;
@@ -873,7 +1055,10 @@ var isAngularComponentRefField = (node, sourceCode) => {
873
1055
  if (type?.type !== TSESTree2.AST_NODE_TYPES.TSTypeReference || type.typeName.type !== TSESTree2.AST_NODE_TYPES.Identifier) {
874
1056
  return false;
875
1057
  }
876
- const reference = sourceCode.getScope(type.typeName).references.find(({ identifier }) => identifier === type.typeName);
1058
+ const scope = sourceCode.getScope(type.typeName);
1059
+ const reference = scope.references.find(
1060
+ ({ identifier }) => identifier === type.typeName
1061
+ );
877
1062
  const definition = reference?.resolved?.defs[0];
878
1063
  return angularCoreImportedName(definition) === "ComponentRef";
879
1064
  };
@@ -992,7 +1177,7 @@ var methodCandidate = (node, localPrivateOnly, implementedMethods) => {
992
1177
  };
993
1178
 
994
1179
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
995
- import { dirname as dirname4, resolve as resolve3 } from "node:path";
1180
+ import { dirname as dirname6, resolve as resolve3 } from "node:path";
996
1181
  import { TSESTree as TSESTree5 } from "@typescript-eslint/utils";
997
1182
 
998
1183
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.ts
@@ -1036,7 +1221,7 @@ import {
1036
1221
  R3TargetBinder,
1037
1222
  parseTemplate
1038
1223
  } from "@angular/compiler";
1039
- import { readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
1224
+ import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
1040
1225
 
1041
1226
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.ts
1042
1227
  import {
@@ -1135,7 +1320,7 @@ var externalTemplateReads = (filename) => {
1135
1320
  const version = `${stats.mtimeNs}:${stats.size}`;
1136
1321
  const cached = externalTemplates.get(filename);
1137
1322
  if (cached?.version === version) return cached.reads;
1138
- const reads = templateReads(readFileSync3(filename, "utf8"), filename);
1323
+ const reads = templateReads(readFileSync2(filename, "utf8"), filename);
1139
1324
  externalTemplates.set(filename, { reads, version });
1140
1325
  return reads;
1141
1326
  } catch {
@@ -1203,7 +1388,7 @@ var inlineTemplateReads = (template, reads, filename) => {
1203
1388
  var externalTemplatePropertyReads = (templateUrl, reads, filename) => {
1204
1389
  if (!templateUrl) return true;
1205
1390
  const url = text(templateUrl);
1206
- return url !== null && addReads(reads, externalTemplateReads(resolve3(dirname4(filename), url)));
1391
+ return url !== null && addReads(reads, externalTemplateReads(resolve3(dirname6(filename), url)));
1207
1392
  };
1208
1393
  var componentTemplateReads = (metadata, reads, filename) => {
1209
1394
  const template = metadataValue2(metadata, "template");
@@ -1236,7 +1421,13 @@ var memberCandidates = (node, options) => {
1236
1421
  const implementedMethods = implementedFormsMethods(node);
1237
1422
  const members = [];
1238
1423
  for (const element of node.body.body) {
1239
- const candidate = fieldCandidate(element, options) ?? methodCandidate(element, options.localPrivateOnly, implementedMethods);
1424
+ const fieldMatch = fieldCandidate(element, options);
1425
+ const methodMatch = methodCandidate(
1426
+ element,
1427
+ options.localPrivateOnly,
1428
+ implementedMethods
1429
+ );
1430
+ const candidate = fieldMatch ?? methodMatch;
1240
1431
  if (candidate) {
1241
1432
  members.push(candidate);
1242
1433
  }
@@ -1245,12 +1436,12 @@ var memberCandidates = (node, options) => {
1245
1436
  };
1246
1437
  var reportMember = (context, candidate) => {
1247
1438
  const data = { name: candidate.name };
1248
- const report = {
1439
+ const report3 = {
1249
1440
  data,
1250
1441
  messageId: candidate.messageId,
1251
1442
  node: candidate.node.key
1252
1443
  };
1253
- context.report(report);
1444
+ context.report(report3);
1254
1445
  };
1255
1446
  var reportUnreadMembers = (members, reads, options) => {
1256
1447
  for (const candidate of members) {
@@ -1261,8 +1452,8 @@ var reportUnreadMembers = (members, reads, options) => {
1261
1452
  reportMember(options.context, candidate);
1262
1453
  }
1263
1454
  };
1264
- var reportClassMembers = (entry, ngClass, report) => {
1265
- const { options, projectAnalysis } = report;
1455
+ var reportClassMembers = (entry, ngClass, report3) => {
1456
+ const { options, projectAnalysis } = report3;
1266
1457
  const isLocalOnlyClass = !ngClass.component || entry.node.abstract === true;
1267
1458
  const candidateOptions = {
1268
1459
  allowEffectFields: options.allowEffectFields,
@@ -1277,7 +1468,7 @@ var reportClassMembers = (entry, ngClass, report) => {
1277
1468
  if (unreadMembers.length === 0) return;
1278
1469
  const remainingNames = unreadMembers.map((candidate) => candidate.name);
1279
1470
  const readsOptions = {
1280
- component: ngClass.component && report.localTemplates,
1471
+ component: ngClass.component && report3.localTemplates,
1281
1472
  filename: options.context.filename,
1282
1473
  metadata: ngClass.metadata,
1283
1474
  remainingNames,
@@ -1290,7 +1481,7 @@ var reportClassMembers = (entry, ngClass, report) => {
1290
1481
  var reportUnusedMembers = (options) => {
1291
1482
  const projectAnalysis = options.projectMemberUsed !== void 0;
1292
1483
  const localTemplates = !projectAnalysis || !options.projectIndexed();
1293
- const report = {
1484
+ const report3 = {
1294
1485
  localTemplates,
1295
1486
  options,
1296
1487
  projectAnalysis
@@ -1299,7 +1490,7 @@ var reportUnusedMembers = (options) => {
1299
1490
  const ngClass = angularClassMetadata(entry.node, options.imports);
1300
1491
  const isDynamicClass = options.dynamicClasses.has(entry);
1301
1492
  if (!ngClass || isDynamicClass) continue;
1302
- reportClassMembers(entry, ngClass, report);
1493
+ reportClassMembers(entry, ngClass, report3);
1303
1494
  }
1304
1495
  };
1305
1496
 
@@ -1544,6 +1735,7 @@ var trackDestructuringRead = (node, stack, thisStack, dynamicClasses) => {
1544
1735
  };
1545
1736
  var classReadVisitor = (classes, stack, dynamicClasses) => {
1546
1737
  const thisStack = [];
1738
+ const lexicalThisListeners = lexicalThisVisitor(thisStack);
1547
1739
  const listeners = {
1548
1740
  ClassDeclaration(node) {
1549
1741
  enterClass(node, classes, stack, thisStack);
@@ -1559,7 +1751,7 @@ var classReadVisitor = (classes, stack, dynamicClasses) => {
1559
1751
  stack.pop();
1560
1752
  thisStack.pop();
1561
1753
  },
1562
- ...lexicalThisVisitor(thisStack),
1754
+ ...lexicalThisListeners,
1563
1755
  AssignmentExpression(node) {
1564
1756
  trackDestructuringRead(node, stack, thisStack, dynamicClasses);
1565
1757
  },
@@ -1574,7 +1766,7 @@ var classReadVisitor = (classes, stack, dynamicClasses) => {
1574
1766
  };
1575
1767
 
1576
1768
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
1577
- import { isClassElement, isIdentifier as isIdentifier8, isStringLiteralLike as isStringLiteralLike7 } from "typescript";
1769
+ import { isClassElement, isIdentifier as isIdentifier9, isStringLiteralLike as isStringLiteralLike8 } from "typescript";
1578
1770
 
1579
1771
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.ts
1580
1772
  import { CssSelector, SelectorMatcher } from "@angular/compiler";
@@ -1597,7 +1789,8 @@ var classShape = ({
1597
1789
  return fields.join("\0");
1598
1790
  };
1599
1791
  var directiveShape = (classes) => {
1600
- const shapes = [...classes].map(classShape);
1792
+ const classList = [...classes];
1793
+ const shapes = classList.map(classShape);
1601
1794
  return shapes.sort().join("\n");
1602
1795
  };
1603
1796
  var parseSelector = (selector) => {
@@ -1607,10 +1800,21 @@ var parseSelector = (selector) => {
1607
1800
  return null;
1608
1801
  }
1609
1802
  };
1803
+ var slotsOf = /* @__PURE__ */ new WeakMap();
1804
+ var classKey = ({ declaration, name }) => {
1805
+ return `${declaration.getSourceFile().fileName}\0${name}`;
1806
+ };
1807
+ var addSlot = (slots, angularClass2, holder) => {
1808
+ const key2 = classKey(angularClass2);
1809
+ const holders = slots.get(key2) ?? [];
1810
+ holders.push(holder);
1811
+ slots.set(key2, holders);
1812
+ };
1610
1813
  var buildDirectiveIndex = (classes) => {
1611
1814
  const byDeclaration = /* @__PURE__ */ new Map();
1612
1815
  const byExportAs = /* @__PURE__ */ new Map();
1613
1816
  const componentMatcher = new SelectorMatcher();
1817
+ const slots = /* @__PURE__ */ new Map();
1614
1818
  for (const angularClass2 of classes) {
1615
1819
  const { component, declaration, exportAs, selector } = angularClass2;
1616
1820
  byDeclaration.set(declaration, angularClass2);
@@ -1618,11 +1822,14 @@ var buildDirectiveIndex = (classes) => {
1618
1822
  const declarations = byExportAs.get(name) ?? [];
1619
1823
  declarations.push(declaration);
1620
1824
  byExportAs.set(name, declarations);
1825
+ addSlot(slots, angularClass2, declarations);
1621
1826
  }
1622
1827
  if (component && selector !== null) {
1623
1828
  const selectors = parseSelector(selector);
1624
1829
  if (selectors) {
1625
- componentMatcher.addSelectables(selectors, [declaration]);
1830
+ const holder = [declaration];
1831
+ componentMatcher.addSelectables(selectors, holder);
1832
+ addSlot(slots, angularClass2, holder);
1626
1833
  }
1627
1834
  }
1628
1835
  }
@@ -1631,12 +1838,39 @@ var buildDirectiveIndex = (classes) => {
1631
1838
  byExportAs,
1632
1839
  componentMatcher
1633
1840
  };
1841
+ slotsOf.set(directiveIndex, slots);
1634
1842
  return directiveIndex;
1635
1843
  };
1844
+ var repointHolders = (holders, previous, next) => {
1845
+ for (const holder of holders) {
1846
+ const position = holder.indexOf(previous);
1847
+ if (position >= 0) holder[position] = next;
1848
+ }
1849
+ };
1850
+ var replaceDeclarations = (index, previous, next) => {
1851
+ const slots = slotsOf.get(index) ?? /* @__PURE__ */ new Map();
1852
+ const nextByKey = /* @__PURE__ */ new Map();
1853
+ for (const angularClass2 of next) {
1854
+ nextByKey.set(classKey(angularClass2), angularClass2);
1855
+ }
1856
+ for (const angularClass2 of previous) {
1857
+ const key2 = classKey(angularClass2);
1858
+ const replacement = nextByKey.get(key2);
1859
+ if (!replacement) continue;
1860
+ const holders = slots.get(key2) ?? [];
1861
+ repointHolders(holders, angularClass2.declaration, replacement.declaration);
1862
+ index.byDeclaration.delete(angularClass2.declaration);
1863
+ index.byDeclaration.set(replacement.declaration, replacement);
1864
+ }
1865
+ };
1866
+
1867
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.ts
1868
+ import { dirname as dirname9, normalize as normalize2 } from "node:path";
1636
1869
 
1637
1870
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
1638
- import { readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1639
- import { isIdentifier as isIdentifier2, isStringLiteralLike as isStringLiteralLike2 } from "typescript";
1871
+ import { readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
1872
+ import { dirname as dirname7, normalize } from "node:path";
1873
+ import { isIdentifier as isIdentifier3, isStringLiteralLike as isStringLiteralLike3 } from "typescript";
1640
1874
 
1641
1875
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
1642
1876
  import {
@@ -1649,6 +1883,66 @@ import {
1649
1883
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.ts
1650
1884
  import { SignatureKind, TypeFlags } from "typescript";
1651
1885
 
1886
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/class-members.util.ts
1887
+ import {
1888
+ SyntaxKind,
1889
+ isClassLike,
1890
+ isFunctionDeclaration,
1891
+ isFunctionExpression,
1892
+ isGetAccessor,
1893
+ isIdentifier as isIdentifier2,
1894
+ isMethodDeclaration,
1895
+ isObjectLiteralExpression as isObjectLiteralExpression2,
1896
+ isPrivateIdentifier,
1897
+ isSetAccessor,
1898
+ isSourceFile,
1899
+ isStringLiteralLike as isStringLiteralLike2
1900
+ } from "typescript";
1901
+ var memberNameOf = (member) => {
1902
+ const { name } = member;
1903
+ if (!name) return null;
1904
+ const isIdentifierName = isIdentifier2(name);
1905
+ const isPrivateName = isPrivateIdentifier(name);
1906
+ const isStringName = isStringLiteralLike2(name);
1907
+ const isTextName = isIdentifierName || isPrivateName || isStringName;
1908
+ if (!isTextName) return null;
1909
+ return name.text;
1910
+ };
1911
+ var ownMembersNamed = (declaration, name) => {
1912
+ const isNamed = (member) => {
1913
+ return memberNameOf(member) === name;
1914
+ };
1915
+ return declaration.members.filter(isNamed);
1916
+ };
1917
+ var isObjectLiteralMember = (node) => {
1918
+ const isMethod = isMethodDeclaration(node);
1919
+ const isGetter = isGetAccessor(node);
1920
+ const isSetter = isSetAccessor(node);
1921
+ const isMember = isMethod || isGetter || isSetter;
1922
+ if (!isMember) return false;
1923
+ return isObjectLiteralExpression2(node.parent);
1924
+ };
1925
+ var isThisBoundary = (node) => {
1926
+ const isDeclaration = isFunctionDeclaration(node);
1927
+ const isExpression = isFunctionExpression(node);
1928
+ const isFunction = isDeclaration || isExpression;
1929
+ if (isFunction) return true;
1930
+ return isObjectLiteralMember(node);
1931
+ };
1932
+ var thisClassOf = (node) => {
1933
+ let current = node.parent;
1934
+ while (!isSourceFile(current)) {
1935
+ if (isClassLike(current)) return current;
1936
+ const isBoundary = isThisBoundary(current);
1937
+ if (isBoundary) return null;
1938
+ current = current.parent;
1939
+ }
1940
+ return null;
1941
+ };
1942
+ var isThisExpression2 = (node) => {
1943
+ return node.kind === SyntaxKind.ThisKeyword;
1944
+ };
1945
+
1652
1946
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.ts
1653
1947
  import { IndexKind } from "typescript";
1654
1948
  var symbolsForName = (checker, type, name) => {
@@ -1708,6 +2002,15 @@ var memberTypes = (symbols, declaration, checker, sink) => {
1708
2002
  }
1709
2003
  return types;
1710
2004
  };
2005
+ var addOwnMemberPath = (declaration, names, sink) => {
2006
+ const [only] = names;
2007
+ const isSingle = names.length === 1 && only !== void 0;
2008
+ if (!isSingle) return false;
2009
+ const members = ownMembersNamed(declaration, only.name);
2010
+ if (members.length === 0) return false;
2011
+ for (const member of members) sink.addDeclaration(member);
2012
+ return true;
2013
+ };
1711
2014
  var addResolvedPath = ({
1712
2015
  allowMissingRoot,
1713
2016
  checker,
@@ -1715,6 +2018,8 @@ var addResolvedPath = ({
1715
2018
  names,
1716
2019
  sink
1717
2020
  }) => {
2021
+ const isOwnMember = addOwnMemberPath(declaration, names, sink);
2022
+ if (isOwnMember) return true;
1718
2023
  let types = [checker.getTypeAtLocation(declaration)];
1719
2024
  const indexTypesOf = (type) => stringIndexTypes(checker, type);
1720
2025
  const returnTypesOf = (type) => callReturnTypes(checker, type);
@@ -1868,14 +2173,13 @@ var referenceTargets = (reference, owner, directives, scope) => {
1868
2173
  } else if (owner instanceof TmplAstElement) {
1869
2174
  directives.componentMatcher.match(
1870
2175
  createCssSelectorFromNode(owner),
1871
- (_selector, declarations) => {
1872
- targets.push(...declarations);
1873
- }
2176
+ (_selector, declarations) => targets.push(...declarations)
1874
2177
  );
1875
2178
  }
1876
- const scoped = scope !== null && !scope.some(
2179
+ const hostDirectiveInScope = scope?.some(
1877
2180
  (declaration) => directives.byDeclaration.get(declaration)?.hostDirectives
1878
- );
2181
+ ) ?? false;
2182
+ const scoped = scope !== null && !hostDirectiveInScope;
1879
2183
  if (!scoped) return targets;
1880
2184
  const scopedTargets = targets.filter((target) => scope.includes(target));
1881
2185
  return scopedTargets;
@@ -1948,6 +2252,23 @@ var addChainRead = (context, reads, boundTarget, chain) => {
1948
2252
  addReferenceRead(context, entity, owner, names);
1949
2253
  return true;
1950
2254
  };
2255
+ var PARSE_OPTIONS = {
2256
+ leadingTriviaChars: [],
2257
+ preserveWhitespaces: true
2258
+ };
2259
+ var PARSE_CACHE_SIZE = 64;
2260
+ var parsedTemplates = /* @__PURE__ */ new Map();
2261
+ var parsedTemplate = (source, fileName) => {
2262
+ const key2 = `${fileName}\0${source}`;
2263
+ const cached = parsedTemplates.get(key2);
2264
+ if (cached !== void 0) return cached;
2265
+ const parsed = parseTemplate2(source, fileName, PARSE_OPTIONS);
2266
+ const isFull = parsedTemplates.size >= PARSE_CACHE_SIZE;
2267
+ const [oldest] = parsedTemplates.keys();
2268
+ if (isFull && oldest !== void 0) parsedTemplates.delete(oldest);
2269
+ parsedTemplates.set(key2, parsed);
2270
+ return parsed;
2271
+ };
1951
2272
  var addTemplateReads = ({
1952
2273
  angularClass: angularClass2,
1953
2274
  checker,
@@ -1967,7 +2288,7 @@ var addTemplateReads = ({
1967
2288
  scope,
1968
2289
  sink
1969
2290
  };
1970
- const parsed = parseTemplate2(source, fileName);
2291
+ const parsed = parsedTemplate(source, fileName);
1971
2292
  if (parsed.errors?.length) {
1972
2293
  const error = parsed.errors[0]?.msg;
1973
2294
  return unparsedTemplateReads(context, source, error);
@@ -1987,28 +2308,34 @@ var addTemplateReads = ({
1987
2308
  };
1988
2309
 
1989
2310
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
1990
- var memberNameOf = (member) => {
2311
+ var memberNameOf2 = (member) => {
1991
2312
  const name = member.name;
1992
2313
  if (!name) return [];
1993
- const isIdentifierName = isIdentifier2(name);
1994
- const isStringName = isStringLiteralLike2(name);
2314
+ const isIdentifierName = isIdentifier3(name);
2315
+ const isStringName = isStringLiteralLike3(name);
1995
2316
  const isTextName = isIdentifierName || isStringName;
1996
2317
  if (!isTextName) return [];
1997
2318
  const names = [name.text];
1998
2319
  return names;
1999
2320
  };
2000
2321
  var memberNames = (declaration) => {
2001
- return declaration.members.flatMap(memberNameOf);
2322
+ return declaration.members.flatMap(memberNameOf2);
2002
2323
  };
2003
2324
  var unknownTemplateNames = ({ declaration, scope }, allNames) => {
2004
2325
  if (scope === null) return allNames;
2005
2326
  const scopeNames = scope.flatMap(memberNames);
2006
- const names = [...memberNames(declaration), ...scopeNames];
2327
+ const declarationNames = memberNames(declaration);
2328
+ const names = [...declarationNames, ...scopeNames];
2007
2329
  return names;
2008
2330
  };
2331
+ var IDENTIFIER = /[A-Za-z_$][\w$]*/gu;
2332
+ var addTemplateMentions = (source, sink) => {
2333
+ for (const [name] of source.matchAll(IDENTIFIER)) sink.addMention(name);
2334
+ };
2009
2335
  var templateFileVersion = (fileName) => {
2010
2336
  const { mtimeNs, size } = statSync3(fileName, { bigint: true });
2011
- const version = { fileName, mtimeNs, size };
2337
+ const directory = dirname7(normalize(fileName));
2338
+ const version = { directory, fileName, mtimeNs, size };
2012
2339
  return version;
2013
2340
  };
2014
2341
  var templateFileIsCurrent = (version) => {
@@ -2037,7 +2364,7 @@ var templateSourceOf = (angularClass2, allNames, sink, versions) => {
2037
2364
  const templateFileName = template.fileName;
2038
2365
  try {
2039
2366
  versions.push(templateFileVersion(templateFileName));
2040
- const source = readFileSync4(templateFileName, "utf8");
2367
+ const source = readFileSync3(templateFileName, "utf8");
2041
2368
  const externalSource = {
2042
2369
  fileName: templateFileName,
2043
2370
  source
@@ -2067,6 +2394,7 @@ var collectAngularTemplateReads = ({
2067
2394
  templateVersions
2068
2395
  );
2069
2396
  if (template === null) continue;
2397
+ addTemplateMentions(template.source, sink);
2070
2398
  const result = addTemplateReads({
2071
2399
  angularClass: angularClass2,
2072
2400
  checker,
@@ -2087,22 +2415,22 @@ var collectAngularTemplateReads = ({
2087
2415
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
2088
2416
  import {
2089
2417
  forEachChild as forEachChild2,
2090
- isClassLike,
2418
+ isClassLike as isClassLike2,
2091
2419
  isElementAccessExpression,
2092
2420
  isPropertyAccessExpression
2093
2421
  } from "typescript";
2094
2422
 
2095
2423
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.ts
2096
- import { SyntaxKind, isMethodSignature } from "typescript";
2424
+ import { SyntaxKind as SyntaxKind2, isMethodSignature } from "typescript";
2097
2425
 
2098
2426
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.ts
2099
2427
  import {
2100
2428
  SymbolFlags,
2101
2429
  isBigIntLiteral,
2102
- isIdentifier as isIdentifier3,
2430
+ isIdentifier as isIdentifier4,
2103
2431
  isNumericLiteral,
2104
- isPrivateIdentifier,
2105
- isStringLiteralLike as isStringLiteralLike3
2432
+ isPrivateIdentifier as isPrivateIdentifier2,
2433
+ isStringLiteralLike as isStringLiteralLike4
2106
2434
  } from "typescript";
2107
2435
  var aliasTarget = (checker, symbol) => {
2108
2436
  const isAlias = (symbol.flags & SymbolFlags.Alias) !== 0;
@@ -2157,9 +2485,9 @@ var literalPropertyNames = (type) => {
2157
2485
  return names;
2158
2486
  };
2159
2487
  var propertyName2 = (checker, node) => {
2160
- const isIdentifierName = isIdentifier3(node);
2161
- const isPrivateName = isPrivateIdentifier(node);
2162
- const isStringName = isStringLiteralLike3(node);
2488
+ const isIdentifierName = isIdentifier4(node);
2489
+ const isPrivateName = isPrivateIdentifier2(node);
2490
+ const isStringName = isStringLiteralLike4(node);
2163
2491
  const isNumericName = isNumericLiteral(node);
2164
2492
  const isBigIntName = isBigIntLiteral(node);
2165
2493
  const isLiteralName = isIdentifierName || isPrivateName || isStringName || isNumericName || isBigIntName;
@@ -2191,7 +2519,7 @@ var addImplementations = (heritageType, classType, checker, sink) => {
2191
2519
  var collectAngularInterfaceMethods = (node, checker, sink) => {
2192
2520
  const heritageClauses = node.heritageClauses ?? [];
2193
2521
  const implementsClauses = heritageClauses.filter(
2194
- (clause) => clause.token === SyntaxKind.ImplementsKeyword
2522
+ (clause) => clause.token === SyntaxKind2.ImplementsKeyword
2195
2523
  );
2196
2524
  if (implementsClauses.length === 0) return;
2197
2525
  const classType = checker.getTypeAtLocation(node);
@@ -2206,18 +2534,18 @@ var collectAngularInterfaceMethods = (node, checker, sink) => {
2206
2534
 
2207
2535
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
2208
2536
  import {
2209
- SyntaxKind as SyntaxKind2,
2537
+ SyntaxKind as SyntaxKind3,
2210
2538
  isArrayLiteralExpression as isArrayLiteralExpression3,
2211
2539
  isBinaryExpression,
2212
2540
  isBindingElement,
2213
- isObjectLiteralExpression as isObjectLiteralExpression3,
2541
+ isObjectLiteralExpression as isObjectLiteralExpression4,
2214
2542
  isVariableDeclaration
2215
2543
  } from "typescript";
2216
2544
 
2217
2545
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.ts
2218
2546
  import {
2219
2547
  isArrayLiteralExpression as isArrayLiteralExpression2,
2220
- isObjectLiteralExpression as isObjectLiteralExpression2,
2548
+ isObjectLiteralExpression as isObjectLiteralExpression3,
2221
2549
  isOmittedExpression,
2222
2550
  isPropertyAssignment as isPropertyAssignment2,
2223
2551
  isSpreadAssignment,
@@ -2225,7 +2553,7 @@ import {
2225
2553
  } from "typescript";
2226
2554
  var nestedPattern = (expression) => {
2227
2555
  const isArrayLiteral = isArrayLiteralExpression2(expression);
2228
- const isObjectLiteral = isObjectLiteralExpression2(expression);
2556
+ const isObjectLiteral = isObjectLiteralExpression3(expression);
2229
2557
  const isNested = isArrayLiteral || isObjectLiteral;
2230
2558
  if (!isNested) return null;
2231
2559
  return expression;
@@ -2287,7 +2615,7 @@ var assignmentPatternElements = (pattern, checker) => {
2287
2615
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.ts
2288
2616
  import {
2289
2617
  isArrayBindingPattern,
2290
- isIdentifier as isIdentifier4,
2618
+ isIdentifier as isIdentifier5,
2291
2619
  isObjectBindingPattern,
2292
2620
  isOmittedExpression as isOmittedExpression2
2293
2621
  } from "typescript";
@@ -2303,7 +2631,7 @@ var nestedPattern2 = (element) => {
2303
2631
  };
2304
2632
  var bindingNames = (checker, element) => {
2305
2633
  if (element.propertyName) return propertyName2(checker, element.propertyName);
2306
- const isIdentifierName = isIdentifier4(element.name);
2634
+ const isIdentifierName = isIdentifier5(element.name);
2307
2635
  if (!isIdentifierName) return null;
2308
2636
  return [element.name.text];
2309
2637
  };
@@ -2391,6 +2719,7 @@ var collectPatternReads = (pattern, type, context) => {
2391
2719
  }
2392
2720
  for (const name of read.names ?? []) {
2393
2721
  consumed.add(name);
2722
+ sink.addMention(name);
2394
2723
  }
2395
2724
  const skip = isSkippable(read, candidateNames);
2396
2725
  if (skip) continue;
@@ -2398,9 +2727,8 @@ var collectPatternReads = (pattern, type, context) => {
2398
2727
  const nested = read.nested;
2399
2728
  if (nested === null) continue;
2400
2729
  for (const symbol of symbols) {
2401
- const nestedType = lazyType(() => {
2402
- return checker.getTypeOfSymbolAtLocation(symbol, read.location);
2403
- });
2730
+ const nestedTypeOf = () => checker.getTypeOfSymbolAtLocation(symbol, read.location);
2731
+ const nestedType = lazyType(nestedTypeOf);
2404
2732
  collectPatternReads(nested, nestedType, context);
2405
2733
  }
2406
2734
  }
@@ -2422,10 +2750,10 @@ var collectBindingReads = (node, context) => {
2422
2750
  var collectAssignmentReads = (node, context) => {
2423
2751
  const isBinary = isBinaryExpression(node);
2424
2752
  if (!isBinary) return;
2425
- const isEqualsAssignment = node.operatorToken.kind === SyntaxKind2.EqualsToken;
2753
+ const isEqualsAssignment = node.operatorToken.kind === SyntaxKind3.EqualsToken;
2426
2754
  if (!isEqualsAssignment) return;
2427
2755
  const isArrayTarget = isArrayLiteralExpression3(node.left);
2428
- const isObjectTarget = isObjectLiteralExpression3(node.left);
2756
+ const isObjectTarget = isObjectLiteralExpression4(node.left);
2429
2757
  const isDestructuringAssignment = isArrayTarget || isObjectTarget;
2430
2758
  if (!isDestructuringAssignment) return;
2431
2759
  const rightTypeOf = () => context.checker.getTypeAtLocation(node.right);
@@ -2440,7 +2768,7 @@ var collectDestructuringReads = (node, checker, sink, candidateNames) => {
2440
2768
 
2441
2769
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.ts
2442
2770
  import {
2443
- SyntaxKind as SyntaxKind3,
2771
+ SyntaxKind as SyntaxKind4,
2444
2772
  isArrayLiteralExpression as isArrayLiteralExpression4,
2445
2773
  isAsExpression,
2446
2774
  isBinaryExpression as isBinaryExpression2,
@@ -2448,7 +2776,7 @@ import {
2448
2776
  isForInStatement,
2449
2777
  isForOfStatement,
2450
2778
  isNonNullExpression,
2451
- isObjectLiteralExpression as isObjectLiteralExpression4,
2779
+ isObjectLiteralExpression as isObjectLiteralExpression5,
2452
2780
  isParenthesizedExpression,
2453
2781
  isPropertyAssignment as isPropertyAssignment3,
2454
2782
  isSatisfiesExpression,
@@ -2471,7 +2799,7 @@ var isPatternContainer = (parent, child) => {
2471
2799
  if (isArrayLiteral) {
2472
2800
  return parent.elements.some((element) => element === child);
2473
2801
  }
2474
- const isObjectLiteral = isObjectLiteralExpression4(parent);
2802
+ const isObjectLiteral = isObjectLiteralExpression5(parent);
2475
2803
  if (isObjectLiteral) {
2476
2804
  return parent.properties.some((property) => property === child);
2477
2805
  }
@@ -2483,7 +2811,7 @@ var isPatternContainer = (parent, child) => {
2483
2811
  };
2484
2812
  var isWriteTarget = (parent, current) => {
2485
2813
  const isBinary = isBinaryExpression2(parent);
2486
- const isBinaryTarget = isBinary && parent.left === current && parent.operatorToken.kind === SyntaxKind3.EqualsToken;
2814
+ const isBinaryTarget = isBinary && parent.left === current && parent.operatorToken.kind === SyntaxKind4.EqualsToken;
2487
2815
  if (isBinaryTarget) return true;
2488
2816
  const isDelete = isDeleteExpression(parent);
2489
2817
  const isDeleteTarget = isDelete && parent.expression === current;
@@ -2504,11 +2832,24 @@ var isWriteOnly2 = (node) => {
2504
2832
  };
2505
2833
 
2506
2834
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
2835
+ var addOwnMemberRead = (node, sink) => {
2836
+ const isThisAccess = isThisExpression2(node.expression);
2837
+ if (!isThisAccess) return false;
2838
+ const owner = thisClassOf(node);
2839
+ if (owner === null) return false;
2840
+ const members = ownMembersNamed(owner, node.name.text);
2841
+ if (members.length === 0) return false;
2842
+ for (const member of members) sink.addDeclaration(member);
2843
+ return true;
2844
+ };
2507
2845
  var addPropertyAccessRead = (node, checker, sink, candidateNames) => {
2846
+ sink.addMention(node.name.text);
2508
2847
  const isCandidateName = candidateNames.has(node.name.text);
2509
2848
  if (!isCandidateName) return;
2510
2849
  const isWriteTarget2 = isWriteOnly2(node);
2511
2850
  if (isWriteTarget2) return;
2851
+ const isOwnMember = addOwnMemberRead(node, sink);
2852
+ if (isOwnMember) return;
2512
2853
  sink.addType(checker.getTypeAtLocation(node.expression));
2513
2854
  const symbol = checker.getSymbolAtLocation(node.name);
2514
2855
  if (symbol) {
@@ -2520,6 +2861,7 @@ var addElementAccessRead = (node, checker, sink, candidateNames) => {
2520
2861
  if (isWriteTarget2) return;
2521
2862
  const argumentType = checker.getTypeAtLocation(node.argumentExpression);
2522
2863
  const names = literalPropertyNames(argumentType);
2864
+ for (const name of names ?? []) sink.addMention(name);
2523
2865
  if (names) {
2524
2866
  const hasCandidateName = names.some((name) => candidateNames.has(name));
2525
2867
  if (!hasCandidateName) return;
@@ -2531,7 +2873,7 @@ var collectTypeScriptReads = (sourceFile, checker, sink, candidateNames) => {
2531
2873
  const visit = (node) => {
2532
2874
  const isPropertyAccess = isPropertyAccessExpression(node);
2533
2875
  const isElementAccess = isElementAccessExpression(node);
2534
- const isClass = isClassLike(node);
2876
+ const isClass = isClassLike2(node);
2535
2877
  if (isPropertyAccess) {
2536
2878
  addPropertyAccessRead(node, checker, sink, candidateNames);
2537
2879
  } else if (isElementAccess) {
@@ -2631,6 +2973,7 @@ var computeEntry = (index, sourceFile, checker) => {
2631
2973
  const declarations = /* @__PURE__ */ new Set();
2632
2974
  const dependencies = /* @__PURE__ */ new Set();
2633
2975
  const fallbackNames = /* @__PURE__ */ new Set();
2976
+ const mentionedNames = /* @__PURE__ */ new Set();
2634
2977
  const seenTypes = /* @__PURE__ */ new Set();
2635
2978
  const sink = {
2636
2979
  addDeclaration: (declaration) => {
@@ -2642,6 +2985,9 @@ var computeEntry = (index, sourceFile, checker) => {
2642
2985
  fallbackNames.add(name);
2643
2986
  }
2644
2987
  },
2988
+ addMention: (name) => {
2989
+ mentionedNames.add(name);
2990
+ },
2645
2991
  addType: (type) => {
2646
2992
  addTypeDependencies(checker, type, dependencies, seenTypes);
2647
2993
  }
@@ -2653,6 +2999,7 @@ var computeEntry = (index, sourceFile, checker) => {
2653
2999
  declarations,
2654
3000
  dependencies,
2655
3001
  fallbackNames,
3002
+ mentionedNames,
2656
3003
  sourceFile
2657
3004
  };
2658
3005
  return fileEntry2;
@@ -2664,16 +3011,16 @@ import {
2664
3011
  forEachChild as forEachChild3,
2665
3012
  getDecorators as getDecorators3,
2666
3013
  isCallExpression as isCallExpression3,
2667
- isClassLike as isClassLike3,
2668
- isObjectLiteralExpression as isObjectLiteralExpression5
3014
+ isClassLike as isClassLike4,
3015
+ isObjectLiteralExpression as isObjectLiteralExpression6
2669
3016
  } from "typescript";
2670
3017
 
2671
3018
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.ts
2672
3019
  import {
2673
- SyntaxKind as SyntaxKind4,
3020
+ SyntaxKind as SyntaxKind5,
2674
3021
  isArrayLiteralExpression as isArrayLiteralExpression5,
2675
- isClassLike as isClassLike2,
2676
- isIdentifier as isIdentifier6,
3022
+ isClassLike as isClassLike3,
3023
+ isIdentifier as isIdentifier7,
2677
3024
  isPropertyAccessExpression as isPropertyAccessExpression3
2678
3025
  } from "typescript";
2679
3026
 
@@ -2684,7 +3031,7 @@ import {
2684
3031
  getDecorators as getDecorators2,
2685
3032
  isCallExpression as isCallExpression2,
2686
3033
  isImportDeclaration,
2687
- isStringLiteralLike as isStringLiteralLike4
3034
+ isStringLiteralLike as isStringLiteralLike5
2688
3035
  } from "typescript";
2689
3036
  var decoratorKinds = [
2690
3037
  "Component",
@@ -2706,7 +3053,7 @@ var importedFromAngularCore = (declaration) => {
2706
3053
  let current = parentOf2(declaration);
2707
3054
  while (current) {
2708
3055
  if (isImportDeclaration(current)) {
2709
- return isStringLiteralLike4(current.moduleSpecifier) && current.moduleSpecifier.text === "@angular/core";
3056
+ return isStringLiteralLike5(current.moduleSpecifier) && current.moduleSpecifier.text === "@angular/core";
2710
3057
  }
2711
3058
  current = parentOf2(current);
2712
3059
  }
@@ -2756,23 +3103,23 @@ var classDecoratorKind = (declaration, discovery) => {
2756
3103
  };
2757
3104
 
2758
3105
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.ts
2759
- import { dirname as dirname5, resolve as resolve4 } from "node:path";
3106
+ import { dirname as dirname8, resolve as resolve4 } from "node:path";
2760
3107
  import {
2761
- isIdentifier as isIdentifier5,
3108
+ isIdentifier as isIdentifier6,
2762
3109
  isNumericLiteral as isNumericLiteral2,
2763
3110
  isPropertyAccessExpression as isPropertyAccessExpression2,
2764
3111
  isPropertyAssignment as isPropertyAssignment4,
2765
- isStringLiteralLike as isStringLiteralLike5
3112
+ isStringLiteralLike as isStringLiteralLike6
2766
3113
  } from "typescript";
2767
3114
  var stringValue = (node, discovery) => {
2768
3115
  if (!node) return null;
2769
- const isStringLiteral = isStringLiteralLike5(node);
3116
+ const isStringLiteral = isStringLiteralLike6(node);
2770
3117
  if (isStringLiteral) return node.text;
2771
- const isIdentifierNode = isIdentifier5(node);
3118
+ const isIdentifierNode = isIdentifier6(node);
2772
3119
  const isPropertyAccessNode = isPropertyAccessExpression2(node);
2773
3120
  if (isIdentifierNode || isPropertyAccessNode) {
2774
3121
  const symbol = discovery.checker.getSymbolAtLocation(
2775
- isIdentifier5(node) ? node : node.name
3122
+ isIdentifier6(node) ? node : node.name
2776
3123
  );
2777
3124
  if (symbol) {
2778
3125
  resolveAlias(symbol, discovery);
@@ -2785,8 +3132,8 @@ var metadataProperty = (metadata, name) => {
2785
3132
  for (const property of metadata.properties) {
2786
3133
  const isAssignment = isPropertyAssignment4(property);
2787
3134
  if (!isAssignment) continue;
2788
- const isIdentifierName = isIdentifier5(property.name);
2789
- const isStringName = isStringLiteralLike5(property.name);
3135
+ const isIdentifierName = isIdentifier6(property.name);
3136
+ const isStringName = isStringLiteralLike6(property.name);
2790
3137
  const isNumericName = isNumericLiteral2(property.name);
2791
3138
  const isLiteralName = isIdentifierName || isStringName || isNumericName;
2792
3139
  const isPropertyNamed = isLiteralName && property.name.text === name;
@@ -2806,7 +3153,7 @@ var templateOf = (metadata, fileName, discovery) => {
2806
3153
  if (url) {
2807
3154
  const text2 = stringValue(url, discovery);
2808
3155
  if (text2 === null) return void 0;
2809
- const externalFileName = resolve4(dirname5(fileName), text2);
3156
+ const externalFileName = resolve4(dirname8(fileName), text2);
2810
3157
  const externalTemplate = {
2811
3158
  fileName: externalFileName,
2812
3159
  kind: "external"
@@ -2825,14 +3172,14 @@ var exportAsOf = (metadata, discovery) => {
2825
3172
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.ts
2826
3173
  var booleanValue = (node) => {
2827
3174
  if (!node) return void 0;
2828
- if (node.kind === SyntaxKind4.TrueKeyword) return true;
2829
- if (node.kind === SyntaxKind4.FalseKeyword) return false;
3175
+ if (node.kind === SyntaxKind5.TrueKeyword) return true;
3176
+ if (node.kind === SyntaxKind5.FalseKeyword) return false;
2830
3177
  return void 0;
2831
3178
  };
2832
3179
  var classesOf = (symbol, discovery) => {
2833
3180
  const resolved = resolveAlias(symbol, discovery);
2834
3181
  const declarations = resolved.declarations ?? [];
2835
- return declarations.filter(isClassLike2);
3182
+ return declarations.filter(isClassLike3);
2836
3183
  };
2837
3184
  var scopedClasses = (classes, discovery) => {
2838
3185
  const scoped = [];
@@ -2846,7 +3193,7 @@ var scopedClasses = (classes, discovery) => {
2846
3193
  return scoped;
2847
3194
  };
2848
3195
  var importedClasses = (element, discovery) => {
2849
- const isIdentifierElement = isIdentifier6(element);
3196
+ const isIdentifierElement = isIdentifier7(element);
2850
3197
  const isPropertyAccessElement = isPropertyAccessExpression3(element);
2851
3198
  if (!isIdentifierElement && !isPropertyAccessElement) return null;
2852
3199
  const target = isIdentifierElement ? element : element.name;
@@ -2915,7 +3262,7 @@ var decoratedClass = (declaration, call, discovery) => {
2915
3262
  const bareDiscovery = { ...base2, valid: !component };
2916
3263
  return bareDiscovery;
2917
3264
  }
2918
- const isMetadataObject = isObjectLiteralExpression5(metadata);
3265
+ const isMetadataObject = isObjectLiteralExpression6(metadata);
2919
3266
  if (!isMetadataObject) {
2920
3267
  const invalidDiscovery = { ...base2, valid: false };
2921
3268
  return invalidDiscovery;
@@ -2937,7 +3284,7 @@ var angularClasses = (sourceFile, checker) => {
2937
3284
  const classes = [];
2938
3285
  const discovery = { checker, dependencies: /* @__PURE__ */ new Set() };
2939
3286
  const visit = (node) => {
2940
- const isClass = isClassLike3(node);
3287
+ const isClass = isClassLike4(node);
2941
3288
  if (isClass) {
2942
3289
  const found = angularClass(node, discovery);
2943
3290
  if (found) {
@@ -2975,38 +3322,51 @@ var indexableSourceFiles = (program) => {
2975
3322
  const sourceFiles = program.getSourceFiles();
2976
3323
  return sourceFiles.filter((sourceFile) => isIndexable(program, sourceFile));
2977
3324
  };
2978
- var sourceFileMaps = (program) => {
2979
- const all = /* @__PURE__ */ new Map();
2980
- const current = /* @__PURE__ */ new Map();
2981
- for (const sourceFile of program.getSourceFiles()) {
2982
- all.set(sourceFile.fileName, sourceFile);
2983
- }
2984
- for (const sourceFile of indexableSourceFiles(program)) {
2985
- current.set(sourceFile.fileName, sourceFile);
3325
+ var adjustCounts = (counts, keys, delta) => {
3326
+ for (const key2 of keys) {
3327
+ const current = counts.get(key2) ?? 0;
3328
+ const count = current + delta;
3329
+ if (count > 0) {
3330
+ counts.set(key2, count);
3331
+ } else {
3332
+ counts.delete(key2);
3333
+ }
2986
3334
  }
2987
- const maps = { all, current };
2988
- return maps;
3335
+ };
3336
+ var removeEntry = (index, fileName) => {
3337
+ const entry = index.entries.get(fileName);
3338
+ if (!entry) return;
3339
+ index.entries.delete(fileName);
3340
+ adjustCounts(index.declarationCounts, entry.declarations, -1);
3341
+ adjustCounts(index.fallbackNameCounts, entry.fallbackNames, -1);
3342
+ };
3343
+ var addEntry = (index, entry) => {
3344
+ removeEntry(index, entry.sourceFile.fileName);
3345
+ index.entries.set(entry.sourceFile.fileName, entry);
3346
+ adjustCounts(index.declarationCounts, entry.declarations, 1);
3347
+ adjustCounts(index.fallbackNameCounts, entry.fallbackNames, 1);
2989
3348
  };
2990
3349
  var dropEntries = (index, stale) => {
2991
3350
  for (const [fileName, entry] of index.entries) {
2992
3351
  const isStale = stale(entry);
2993
- if (isStale) {
2994
- index.entries.delete(fileName);
2995
- }
3352
+ if (isStale) removeEntry(index, fileName);
2996
3353
  }
2997
3354
  };
2998
- var isReplaced = (sourceFile, dependencies, maps) => {
2999
- const indexedSourceFile = maps.current.get(sourceFile.fileName);
3000
- if (indexedSourceFile !== sourceFile) return true;
3355
+ var currentSourceFiles = (program) => {
3356
+ return new Set(program.getSourceFiles());
3357
+ };
3358
+ var isReplaced = (sourceFile, dependencies, current) => {
3359
+ const isCurrent = current.has(sourceFile);
3360
+ if (!isCurrent) return true;
3001
3361
  for (const dependency of dependencies) {
3002
- const indexedDependency = maps.all.get(dependency.fileName);
3003
- if (indexedDependency !== dependency) return true;
3362
+ const isDependencyCurrent = current.has(dependency);
3363
+ if (!isDependencyCurrent) return true;
3004
3364
  }
3005
3365
  return false;
3006
3366
  };
3007
- var dropReplacedEntries = (index, maps) => {
3367
+ var dropReplacedEntries = (index, current) => {
3008
3368
  const isEntryReplaced = (entry) => {
3009
- return isReplaced(entry.sourceFile, entry.dependencies, maps);
3369
+ return isReplaced(entry.sourceFile, entry.dependencies, current);
3010
3370
  };
3011
3371
  dropEntries(index, isEntryReplaced);
3012
3372
  };
@@ -3015,7 +3375,7 @@ var dropEntriesMentioning = (index, names) => {
3015
3375
  if (index.entries.size === 0) return;
3016
3376
  const nameList = [...names];
3017
3377
  const mentionsName = (entry) => {
3018
- return nameList.some((name) => entry.sourceFile.text.includes(name));
3378
+ return nameList.some((name) => entry.mentionedNames.has(name));
3019
3379
  };
3020
3380
  dropEntries(index, mentionsName);
3021
3381
  };
@@ -3030,16 +3390,19 @@ var templateVersionChecker = () => {
3030
3390
  return current;
3031
3391
  };
3032
3392
  };
3393
+ var hasStaleTemplate = (entry) => {
3394
+ return !entry.templateVersions.every(templateFileIsCurrent);
3395
+ };
3033
3396
  var dropStaleTemplateEntries = (index, force) => {
3034
3397
  const now = performance.now();
3035
3398
  const sinceLastCheck = now - index.templateCheckedAt;
3036
3399
  const isThrottled = !force && sinceLastCheck < index.templateCheckDuration * 100;
3037
3400
  if (isThrottled) return;
3038
3401
  const isCurrent = templateVersionChecker();
3039
- const hasStaleTemplate = (entry) => {
3402
+ const isStale = (entry) => {
3040
3403
  return !entry.templateVersions.every(isCurrent);
3041
3404
  };
3042
- dropEntries(index, hasStaleTemplate);
3405
+ dropEntries(index, isStale);
3043
3406
  index.templateCheckedAt = now;
3044
3407
  index.templateCheckDuration = performance.now() - now;
3045
3408
  };
@@ -3049,9 +3412,9 @@ import {
3049
3412
  canHaveDecorators as canHaveDecorators4,
3050
3413
  forEachChild as forEachChild4,
3051
3414
  getDecorators as getDecorators4,
3052
- isClassLike as isClassLike4,
3053
- isIdentifier as isIdentifier7,
3054
- isStringLiteralLike as isStringLiteralLike6
3415
+ isClassLike as isClassLike5,
3416
+ isIdentifier as isIdentifier8,
3417
+ isStringLiteralLike as isStringLiteralLike7
3055
3418
  } from "typescript";
3056
3419
  var isDecoratedClass = (node) => {
3057
3420
  const isDecoratable = canHaveDecorators4(node);
@@ -3061,14 +3424,14 @@ var isDecoratedClass = (node) => {
3061
3424
  return decoratorCount > 0;
3062
3425
  };
3063
3426
  var addMemberNames = (node, names) => {
3064
- const isClass = isClassLike4(node);
3427
+ const isClass = isClassLike5(node);
3065
3428
  if (!isClass) return;
3066
3429
  const isDecorated = isDecoratedClass(node);
3067
3430
  if (!isDecorated) return;
3068
3431
  for (const member of node.members) {
3069
3432
  if (!member.name) continue;
3070
- const isIdentifierName = isIdentifier7(member.name);
3071
- const isStringName = isStringLiteralLike6(member.name);
3433
+ const isIdentifierName = isIdentifier8(member.name);
3434
+ const isStringName = isStringLiteralLike7(member.name);
3072
3435
  const isNamedMember = isIdentifierName || isStringName;
3073
3436
  if (isNamedMember) {
3074
3437
  names.add(member.name.text);
@@ -3086,19 +3449,19 @@ var collectCandidateNames = (sourceFile) => {
3086
3449
  };
3087
3450
 
3088
3451
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.ts
3089
- var dropReplacedClasses = (index, maps) => {
3090
- let changed = false;
3452
+ var dropReplacedClasses = (index, current) => {
3453
+ const dropped = [];
3091
3454
  for (const [fileName, fileClasses] of index.classes) {
3092
3455
  const replaced = isReplaced(
3093
3456
  fileClasses.sourceFile,
3094
3457
  fileClasses.dependencies,
3095
- maps
3458
+ current
3096
3459
  );
3097
3460
  if (!replaced) continue;
3098
3461
  index.classes.delete(fileName);
3099
- changed = true;
3462
+ dropped.push(fileClasses);
3100
3463
  }
3101
- return changed;
3464
+ return dropped;
3102
3465
  };
3103
3466
  var addCandidateNames = (index, candidateNames, newNames) => {
3104
3467
  for (const name of candidateNames) {
@@ -3112,27 +3475,30 @@ var indexFileClasses = (index, sourceFile, checker, newNames) => {
3112
3475
  const candidateNames = collectCandidateNames(sourceFile);
3113
3476
  addCandidateNames(index, candidateNames, newNames);
3114
3477
  const { classes, dependencies } = angularClasses(sourceFile, checker());
3478
+ const shape = directiveShape(classes);
3115
3479
  const fileClasses = {
3116
3480
  candidateNames,
3117
3481
  classes,
3118
3482
  dependencies,
3483
+ shape,
3119
3484
  sourceFile
3120
3485
  };
3121
3486
  index.classes.set(sourceFile.fileName, fileClasses);
3122
- index.entries.delete(sourceFile.fileName);
3487
+ removeEntry(index, sourceFile.fileName);
3488
+ return fileClasses;
3123
3489
  };
3124
3490
  var indexNewClasses = (index, current, checker, newNames) => {
3125
- let changed = false;
3126
- for (const [fileName, sourceFile] of current) {
3127
- const isIndexed = index.classes.has(fileName);
3491
+ const added = [];
3492
+ for (const sourceFile of current) {
3493
+ const isIndexed = index.classes.has(sourceFile.fileName);
3128
3494
  if (isIndexed) continue;
3129
- indexFileClasses(index, sourceFile, checker, newNames);
3130
- changed = true;
3495
+ added.push(indexFileClasses(index, sourceFile, checker, newNames));
3131
3496
  }
3132
- return changed;
3497
+ return added;
3133
3498
  };
3134
3499
  var rebuildDirectives = (index) => {
3135
- const fileClasses = [...index.classes.values()];
3500
+ const classesValues = index.classes.values();
3501
+ const fileClasses = [...classesValues];
3136
3502
  const classes = fileClasses.flatMap((entry) => entry.classes);
3137
3503
  const shape = directiveShape(classes);
3138
3504
  index.directives = buildDirectiveIndex(classes);
@@ -3140,12 +3506,26 @@ var rebuildDirectives = (index) => {
3140
3506
  index.directiveShape = shape;
3141
3507
  dropEntries(index, (entry) => entry.usedDirectiveIndex);
3142
3508
  };
3143
- var reconcileClasses = (index, maps, checker) => {
3509
+ var shapesOf = (files) => {
3510
+ return files.map((file) => file.shape).sort().join("\n");
3511
+ };
3512
+ var repointDirectives = (index, dropped, added) => {
3513
+ const previous = dropped.flatMap((file) => file.classes);
3514
+ const next = added.flatMap((file) => file.classes);
3515
+ replaceDeclarations(index.directives, previous, next);
3516
+ };
3517
+ var reconcileClasses = (index, indexable, current, checker) => {
3144
3518
  const newNames = /* @__PURE__ */ new Set();
3145
- const droppedClasses = dropReplacedClasses(index, maps);
3146
- const addedClasses = indexNewClasses(index, maps.current, checker, newNames);
3147
- const changed = droppedClasses || addedClasses;
3148
- if (changed) {
3519
+ const dropped = dropReplacedClasses(index, current);
3520
+ const added = indexNewClasses(index, indexable, checker, newNames);
3521
+ const isUntouched = dropped.length === 0 && added.length === 0;
3522
+ if (isUntouched) return newNames;
3523
+ const droppedShape = shapesOf(dropped);
3524
+ const addedShape = shapesOf(added);
3525
+ const isSameShape = droppedShape === addedShape;
3526
+ if (isSameShape) {
3527
+ repointDirectives(index, dropped, added);
3528
+ } else {
3149
3529
  rebuildDirectives(index);
3150
3530
  }
3151
3531
  return newNames;
@@ -3160,36 +3540,59 @@ var indexMissingEntries = (index, sourceFiles, checker) => {
3160
3540
  for (const sourceFile of sourceFiles) {
3161
3541
  const isEntryIndexed = index.entries.has(sourceFile.fileName);
3162
3542
  if (isEntryIndexed) continue;
3163
- const entry = computeEntry(index, sourceFile, checker());
3164
- index.entries.set(sourceFile.fileName, entry);
3543
+ addEntry(index, computeEntry(index, sourceFile, checker()));
3165
3544
  }
3166
3545
  };
3167
3546
  var reindexProgram = (index, program, checker) => {
3168
- const maps = sourceFileMaps(program);
3169
- dropReplacedEntries(index, maps);
3170
- const newNames = reconcileClasses(index, maps, checker);
3547
+ const indexable = indexableSourceFiles(program);
3548
+ const current = currentSourceFiles(program);
3549
+ dropReplacedEntries(index, current);
3550
+ const newNames = reconcileClasses(index, indexable, current, checker);
3171
3551
  dropEntriesMentioning(index, newNames);
3172
3552
  index.program = program;
3173
- index.usage = void 0;
3174
- dropStaleTemplateEntries(index, true);
3175
- indexMissingEntries(index, maps.current.values(), checker);
3553
+ dropStaleTemplateEntries(index, false);
3554
+ indexMissingEntries(index, indexable, checker);
3176
3555
  };
3177
3556
  var refreshTemplateEntries = (index, program, checker) => {
3178
3557
  const entryCount = index.entries.size;
3179
3558
  dropStaleTemplateEntries(index, false);
3180
3559
  const isUnchanged = index.entries.size === entryCount;
3181
3560
  if (isUnchanged) return;
3182
- index.usage = void 0;
3183
3561
  indexMissingEntries(index, indexableSourceFiles(program), checker);
3184
3562
  };
3185
- var reconcile = (index, program) => {
3563
+ var isLocalEntry = (entry, sourceFile, directory) => {
3564
+ const isOwn = entry.sourceFile === sourceFile;
3565
+ if (isOwn) return true;
3566
+ const isNearby = (version) => {
3567
+ return version.directory === directory;
3568
+ };
3569
+ return entry.templateVersions.some(isNearby);
3570
+ };
3571
+ var refreshLocalEntries = (index, program, fileName, checker) => {
3572
+ const sourceFile = program.getSourceFile(fileName);
3573
+ if (!sourceFile) return;
3574
+ const directory = dirname9(normalize2(sourceFile.fileName));
3575
+ const stale = [];
3576
+ for (const entry of index.entries.values()) {
3577
+ if (entry.templateVersions.length === 0) continue;
3578
+ const isLocal = isLocalEntry(entry, sourceFile, directory);
3579
+ if (!isLocal) continue;
3580
+ const isStale = hasStaleTemplate(entry);
3581
+ if (isStale) stale.push(entry);
3582
+ }
3583
+ for (const entry of stale) {
3584
+ addEntry(index, computeEntry(index, entry.sourceFile, checker()));
3585
+ }
3586
+ };
3587
+ var reconcile = (index, program, fileName) => {
3186
3588
  const checker = lazyChecker(program);
3187
3589
  const isNewProgram = index.program !== program;
3188
3590
  if (isNewProgram) {
3189
3591
  reindexProgram(index, program, checker);
3190
- return;
3592
+ } else {
3593
+ refreshTemplateEntries(index, program, checker);
3191
3594
  }
3192
- refreshTemplateEntries(index, program, checker);
3595
+ refreshLocalEntries(index, program, fileName, checker);
3193
3596
  };
3194
3597
 
3195
3598
  // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
@@ -3198,46 +3601,36 @@ var memberName = (node) => {
3198
3601
  const isMember = isClassElement(node);
3199
3602
  if (!isMember) return null;
3200
3603
  if (!node.name) return null;
3201
- const isIdentifierName = isIdentifier8(node.name);
3202
- const isStringName = isStringLiteralLike7(node.name);
3604
+ const isIdentifierName = isIdentifier9(node.name);
3605
+ const isStringName = isStringLiteralLike8(node.name);
3203
3606
  const isNamedMember = isIdentifierName || isStringName;
3204
3607
  if (!isNamedMember) return null;
3205
3608
  return node.name.text;
3206
3609
  };
3207
3610
  var usageOf = (index) => {
3208
- if (index.usage !== void 0) return index.usage;
3209
- const declarations = /* @__PURE__ */ new Set();
3210
- const fallbackNames = /* @__PURE__ */ new Set();
3211
- for (const entry of index.entries.values()) {
3212
- for (const declaration of entry.declarations) {
3213
- declarations.add(declaration);
3214
- }
3215
- for (const name of entry.fallbackNames) {
3216
- fallbackNames.add(name);
3217
- }
3218
- }
3219
- index.usage = {
3220
- has: (node) => {
3221
- const hasDeclaration = declarations.has(node);
3222
- if (hasDeclaration) return true;
3223
- if (fallbackNames.size === 0) return false;
3224
- const name = memberName(node);
3225
- return name !== null && fallbackNames.has(name);
3226
- }
3611
+ const { declarationCounts, fallbackNameCounts } = index;
3612
+ const has = (node) => {
3613
+ const hasDeclaration = declarationCounts.has(node);
3614
+ if (hasDeclaration) return true;
3615
+ if (fallbackNameCounts.size === 0) return false;
3616
+ const name = memberName(node);
3617
+ return name !== null && fallbackNameCounts.has(name);
3227
3618
  };
3228
- return index.usage;
3619
+ const usage = { has };
3620
+ return usage;
3229
3621
  };
3230
3622
  var createIndex = () => {
3231
3623
  const emptyIndex = {
3232
3624
  candidateNames: /* @__PURE__ */ new Set(),
3233
3625
  classes: /* @__PURE__ */ new Map(),
3626
+ declarationCounts: /* @__PURE__ */ new Map(),
3234
3627
  directives: buildDirectiveIndex([]),
3235
3628
  directiveShape: "",
3236
3629
  entries: /* @__PURE__ */ new Map(),
3630
+ fallbackNameCounts: /* @__PURE__ */ new Map(),
3237
3631
  program: null,
3238
3632
  templateCheckDuration: 0,
3239
- templateCheckedAt: 0,
3240
- usage: void 0
3633
+ templateCheckedAt: 0
3241
3634
  };
3242
3635
  return emptyIndex;
3243
3636
  };
@@ -3245,7 +3638,7 @@ var projectUsageIsCurrent = (program) => {
3245
3638
  const configFilePath = program.getCompilerOptions()["configFilePath"];
3246
3639
  return typeof configFilePath === "string" && indexes.get(configFilePath)?.program === program;
3247
3640
  };
3248
- var projectUsage = (program) => {
3641
+ var projectUsage = (program, fileName) => {
3249
3642
  const configFilePath = program.getCompilerOptions()["configFilePath"];
3250
3643
  if (typeof configFilePath !== "string") return null;
3251
3644
  let index = indexes.get(configFilePath);
@@ -3254,7 +3647,7 @@ var projectUsage = (program) => {
3254
3647
  indexes.set(configFilePath, index);
3255
3648
  }
3256
3649
  try {
3257
- reconcile(index, program);
3650
+ reconcile(index, program, fileName);
3258
3651
  return usageOf(index);
3259
3652
  } catch {
3260
3653
  indexes.delete(configFilePath);
@@ -3313,12 +3706,12 @@ var projectParserServices = (context, analysis) => {
3313
3706
  );
3314
3707
  }
3315
3708
  };
3316
- var projectMemberUsedFor = (parserServices) => {
3709
+ var projectMemberUsedFor = (parserServices, fileName) => {
3317
3710
  if (parserServices === void 0) return void 0;
3318
3711
  let usage;
3319
3712
  const memberUsed = (node) => {
3320
3713
  if (usage === void 0) {
3321
- usage = projectUsage(parserServices.program);
3714
+ usage = projectUsage(parserServices.program, fileName);
3322
3715
  }
3323
3716
  return usage?.has(parserServices.esTreeNodeToTSNodeMap.get(node)) ?? true;
3324
3717
  };
@@ -3350,7 +3743,7 @@ var no_unused_angular_instance_fields_default = createRule2({
3350
3743
  dynamicClasses,
3351
3744
  imports,
3352
3745
  projectIndexed,
3353
- projectMemberUsed: projectMemberUsedFor(parserServices)
3746
+ projectMemberUsed: projectMemberUsedFor(parserServices, context.filename)
3354
3747
  };
3355
3748
  const readListeners = classReadVisitor(classes, stack, dynamicClasses);
3356
3749
  const listeners = {
@@ -3389,6 +3782,10 @@ var angular = defineConfig2([
3389
3782
  },
3390
3783
  ...inlineTemplates,
3391
3784
  rules: {
3785
+ // Component and directive members nothing reads (class, template, host)
3786
+ // are dead.
3787
+ // Bad: private unused = 1; read nowhere
3788
+ // Good: delete it, or read it in the template
3392
3789
  "lint-suite-angular/no-unused-instance-fields": [
3393
3790
  "error",
3394
3791
  { analysis: "project" }
@@ -3445,8 +3842,7 @@ var baseLimits = defineConfig3([
3445
3842
  "**/*.ts",
3446
3843
  "**/*.tsx",
3447
3844
  "**/*.cts",
3448
- "**/*.mts",
3449
- "**/*.html"
3845
+ "**/*.mts"
3450
3846
  ],
3451
3847
  rules: {
3452
3848
  "max-lines": [
@@ -3948,7 +4344,7 @@ var typescriptConsistency = defineConfig14([
3948
4344
  rules: {
3949
4345
  "@typescript-eslint/consistent-type-assertions": [
3950
4346
  "error",
3951
- { assertionStyle: "as", objectLiteralTypeAssertions: "never" }
4347
+ { assertionStyle: "never" }
3952
4348
  ],
3953
4349
  "@typescript-eslint/consistent-type-definitions": ["error", "type"],
3954
4350
  "@typescript-eslint/consistent-type-imports": [
@@ -4114,71 +4510,286 @@ var typescriptRecommended = defineConfig17([
4114
4510
  // packages/lint-suite/src/lib/typescript-safety.ts
4115
4511
  import { defineConfig as defineConfig18 } from "eslint/config";
4116
4512
 
4117
- // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
4118
- import { ESLintUtils as ESLintUtils3, TSESTree as TSESTree10 } from "@typescript-eslint/utils";
4119
-
4120
- // packages/lint-suite/src/lib/rules/explicit-accessibility/common/explicit-accessibility.const.ts
4121
- var accessibilities = [
4122
- "public",
4123
- "private",
4124
- "protected"
4125
- ];
4513
+ // packages/lint-suite/src/lib/rules/arrow-body-fits-line/arrow-body-fits-line.ts
4514
+ import { ASTUtils, ESLintUtils as ESLintUtils3, TSESTree as TSESTree10 } from "@typescript-eslint/utils";
4126
4515
  var docs3 = {
4127
- description: "Require explicit accessibility modifiers on class members"
4516
+ description: "Require an expression-bodied arrow function to fit on one line"
4128
4517
  };
4129
4518
  var messages3 = {
4130
- missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected).",
4131
- setAccessibility: "Add the '{{ accessibility }}' modifier."
4132
- };
4133
- var defaultAccessibilitySchema = {
4134
- type: "string",
4135
- enum: ["public", "private", "protected", "none"],
4136
- 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."
4137
- };
4138
- var properties3 = {
4139
- defaultAccessibility: defaultAccessibilitySchema
4140
- };
4141
- var optionsSchema3 = {
4142
- type: "object",
4143
- properties: properties3,
4144
- additionalProperties: false
4519
+ arrowBodyWrapped: "Expression body of this arrow function spans multiple lines; wrap it in a block with an explicit return."
4145
4520
  };
4146
- var schema3 = [optionsSchema3];
4521
+ var schema3 = [];
4147
4522
  var meta3 = {
4148
4523
  type: "suggestion",
4149
4524
  docs: docs3,
4150
4525
  fixable: "code",
4151
- hasSuggestions: true,
4152
4526
  schema: schema3,
4153
4527
  messages: messages3
4154
4528
  };
4155
- var defaultOptions3 = [{ defaultAccessibility: "public" }];
4156
-
4157
- // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
4158
4529
  var createRule3 = ESLintUtils3.RuleCreator(
4159
- () => "https://github.com/F0rty-Tw0/lint-suite#explicit-accessibility"
4530
+ () => "https://github.com/F0rty-Tw0/lint-suite#arrow-body-fits-line"
4160
4531
  );
4161
- var isPrivateMember = (node) => {
4162
- if (node.type === TSESTree10.AST_NODE_TYPES.TSParameterProperty) return false;
4163
- return node.key.type === TSESTree10.AST_NODE_TYPES.PrivateIdentifier;
4164
- };
4165
- var isConstructor = (node) => {
4166
- if (node.type !== TSESTree10.AST_NODE_TYPES.MethodDefinition) return false;
4167
- return node.kind === "constructor";
4168
- };
4169
- var reportTarget = (node) => {
4170
- if (node.type !== TSESTree10.AST_NODE_TYPES.TSParameterProperty) {
4171
- return node.key;
4172
- }
4173
- const { parameter } = node;
4174
- if (parameter.type === TSESTree10.AST_NODE_TYPES.AssignmentPattern) {
4175
- return parameter.left;
4532
+ var isMultiLine = (body) => body.loc.start.line !== body.loc.end.line;
4533
+ var arrowIndent = (node, sourceCode) => {
4534
+ const line = sourceCode.lines[node.loc.start.line - 1] ?? "";
4535
+ const leadingWhitespace = /^\s*/u.exec(line);
4536
+ return leadingWhitespace ? leadingWhitespace[0] : "";
4537
+ };
4538
+ var wrappedBody = (indent, bodyText) => {
4539
+ const innerIndent = `${indent} `;
4540
+ return `{
4541
+ ${innerIndent}return ${bodyText};
4542
+ ${indent}}`;
4543
+ };
4544
+ var replacementRange = (node, sourceCode) => {
4545
+ const isParenthesized = ASTUtils.isParenthesized(node.body, sourceCode);
4546
+ if (!isParenthesized) return node.body.range;
4547
+ const openParen = sourceCode.getTokenBefore(node.body);
4548
+ const closeParen = sourceCode.getTokenAfter(node.body);
4549
+ if (!openParen || !closeParen) return node.body.range;
4550
+ return [openParen.range[0], closeParen.range[1]];
4551
+ };
4552
+ var arrow_body_fits_line_default = createRule3({
4553
+ name: "arrow-body-fits-line",
4554
+ meta: meta3,
4555
+ defaultOptions: [],
4556
+ create(context) {
4557
+ const { sourceCode } = context;
4558
+ const listeners = {
4559
+ "ArrowFunctionExpression[expression=true]"(node) {
4560
+ const isObjectBody = node.body.type === TSESTree10.AST_NODE_TYPES.ObjectExpression;
4561
+ if (isObjectBody) return;
4562
+ const isMultiLineBody = isMultiLine(node.body);
4563
+ if (!isMultiLineBody) return;
4564
+ const indent = arrowIndent(node, sourceCode);
4565
+ const bodyText = sourceCode.getText(node.body);
4566
+ const range = replacementRange(node, sourceCode);
4567
+ const fix = (fixer) => fixer.replaceTextRange(range, wrappedBody(indent, bodyText));
4568
+ const report3 = {
4569
+ node,
4570
+ messageId: "arrowBodyWrapped",
4571
+ fix
4572
+ };
4573
+ context.report(report3);
4574
+ }
4575
+ };
4576
+ return listeners;
4176
4577
  }
4177
- return parameter;
4578
+ });
4579
+
4580
+ // packages/lint-suite/src/lib/rules/chain-fits-line/chain-fits-line.ts
4581
+ import { ESLintUtils as ESLintUtils4, TSESTree as TSESTree11 } from "@typescript-eslint/utils";
4582
+ var docs4 = {
4583
+ description: "Require every call in a member call chain to keep its property on the same line"
4178
4584
  };
4179
- var memberName2 = (target, sourceCode) => {
4180
- if (target.type === TSESTree10.AST_NODE_TYPES.Identifier) return target.name;
4181
- return sourceCode.getText(target);
4585
+ var messages4 = {
4586
+ chainWrapped: "Call chain is wrapped across lines; keep every call on the same line or name intermediate steps."
4587
+ };
4588
+ var meta4 = {
4589
+ type: "suggestion",
4590
+ docs: docs4,
4591
+ schema: [],
4592
+ messages: messages4
4593
+ };
4594
+ var createRule4 = ESLintUtils4.RuleCreator(
4595
+ () => "https://github.com/F0rty-Tw0/lint-suite#chain-fits-line"
4596
+ );
4597
+ var isMemberCall = (node) => {
4598
+ if (node.type !== TSESTree11.AST_NODE_TYPES.CallExpression) return false;
4599
+ return node.callee.type === TSESTree11.AST_NODE_TYPES.MemberExpression;
4600
+ };
4601
+ var spinePropertyLines = (node) => {
4602
+ const lines = [node.callee.property.loc.start.line];
4603
+ let current = node.callee.object;
4604
+ for (; ; ) {
4605
+ if (isMemberCall(current)) {
4606
+ lines.push(current.callee.property.loc.start.line);
4607
+ current = current.callee.object;
4608
+ continue;
4609
+ }
4610
+ if (current.type === TSESTree11.AST_NODE_TYPES.MemberExpression) {
4611
+ current = current.object;
4612
+ continue;
4613
+ }
4614
+ break;
4615
+ }
4616
+ return lines;
4617
+ };
4618
+ var chain_fits_line_default = createRule4({
4619
+ name: "chain-fits-line",
4620
+ meta: meta4,
4621
+ defaultOptions: [],
4622
+ create(context) {
4623
+ const listeners = {
4624
+ CallExpression(node) {
4625
+ if (!isMemberCall(node)) return;
4626
+ const isOutermost = node.parent.type !== TSESTree11.AST_NODE_TYPES.MemberExpression;
4627
+ if (!isOutermost) return;
4628
+ const lines = spinePropertyLines(node);
4629
+ if (lines.length < 2) return;
4630
+ const isWrapped = new Set(lines).size > 1;
4631
+ if (!isWrapped) return;
4632
+ const report3 = {
4633
+ node,
4634
+ messageId: "chainWrapped"
4635
+ };
4636
+ context.report(report3);
4637
+ }
4638
+ };
4639
+ return listeners;
4640
+ }
4641
+ });
4642
+
4643
+ // packages/lint-suite/src/lib/rules/chain-receiver-is-name/chain-receiver-is-name.ts
4644
+ import { ESLintUtils as ESLintUtils5, TSESTree as TSESTree12 } from "@typescript-eslint/utils";
4645
+
4646
+ // packages/lint-suite/src/lib/rules/utils/indent-of.util.ts
4647
+ var indentOf = (node, sourceCode) => {
4648
+ const line = sourceCode.lines[node.loc.start.line - 1] ?? "";
4649
+ const [leading] = /^[ \t]*/.exec(line) ?? [""];
4650
+ return leading;
4651
+ };
4652
+
4653
+ // packages/lint-suite/src/lib/rules/chain-receiver-is-name/chain-receiver-is-name.ts
4654
+ var docs5 = {
4655
+ description: "Require a call chain to start from a name, not an inline expression"
4656
+ };
4657
+ var messages5 = {
4658
+ chainReceiver: "Call chain starts from an inline expression; extract it to a named constant first.",
4659
+ extractReceiver: "Extract the receiver to a 'value' constant."
4660
+ };
4661
+ var meta5 = {
4662
+ type: "suggestion",
4663
+ docs: docs5,
4664
+ hasSuggestions: true,
4665
+ schema: [],
4666
+ messages: messages5
4667
+ };
4668
+ var createRule5 = ESLintUtils5.RuleCreator(
4669
+ () => "https://github.com/F0rty-Tw0/lint-suite#chain-receiver-is-name"
4670
+ );
4671
+ var UNNAMED_RECEIVER_TYPES = /* @__PURE__ */ new Set([
4672
+ TSESTree12.AST_NODE_TYPES.LogicalExpression,
4673
+ TSESTree12.AST_NODE_TYPES.ConditionalExpression,
4674
+ TSESTree12.AST_NODE_TYPES.ObjectExpression,
4675
+ TSESTree12.AST_NODE_TYPES.ArrayExpression,
4676
+ TSESTree12.AST_NODE_TYPES.AwaitExpression,
4677
+ TSESTree12.AST_NODE_TYPES.BinaryExpression,
4678
+ TSESTree12.AST_NODE_TYPES.TemplateLiteral
4679
+ ]);
4680
+ var enclosingStatement = (node) => {
4681
+ let current = node;
4682
+ while (current.parent.type !== TSESTree12.AST_NODE_TYPES.BlockStatement && current.parent.type !== TSESTree12.AST_NODE_TYPES.Program) {
4683
+ current = current.parent;
4684
+ }
4685
+ return current;
4686
+ };
4687
+ var suggestionOf = (node, sourceCode) => {
4688
+ const statement = enclosingStatement(node);
4689
+ const indent = indentOf(statement, sourceCode);
4690
+ const objectText = sourceCode.getText(node.object);
4691
+ const declaration = `const value = ${objectText};
4692
+ ${indent}`;
4693
+ const fix = (fixer) => {
4694
+ const hoisted = fixer.insertTextBefore(statement, declaration);
4695
+ const replaced = fixer.replaceText(node.object, "value");
4696
+ const fixes = [hoisted, replaced];
4697
+ return fixes;
4698
+ };
4699
+ const suggestion = {
4700
+ messageId: "extractReceiver",
4701
+ fix
4702
+ };
4703
+ return suggestion;
4704
+ };
4705
+ var chain_receiver_is_name_default = createRule5({
4706
+ name: "chain-receiver-is-name",
4707
+ meta: meta5,
4708
+ defaultOptions: [],
4709
+ create(context) {
4710
+ const { sourceCode } = context;
4711
+ const listeners = {
4712
+ MemberExpression(node) {
4713
+ const isUnnamedReceiver = UNNAMED_RECEIVER_TYPES.has(node.object.type);
4714
+ if (!isUnnamedReceiver) return;
4715
+ const suggest = [suggestionOf(node, sourceCode)];
4716
+ const report3 = {
4717
+ node,
4718
+ messageId: "chainReceiver",
4719
+ suggest
4720
+ };
4721
+ context.report(report3);
4722
+ }
4723
+ };
4724
+ return listeners;
4725
+ }
4726
+ });
4727
+
4728
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
4729
+ import { ESLintUtils as ESLintUtils6, TSESTree as TSESTree13 } from "@typescript-eslint/utils";
4730
+
4731
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/common/explicit-accessibility.const.ts
4732
+ var accessibilities = [
4733
+ "public",
4734
+ "private",
4735
+ "protected"
4736
+ ];
4737
+ var docs6 = {
4738
+ description: "Require explicit accessibility modifiers on class members"
4739
+ };
4740
+ var messages6 = {
4741
+ missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected).",
4742
+ setAccessibility: "Add the '{{ accessibility }}' modifier."
4743
+ };
4744
+ var defaultAccessibilitySchema = {
4745
+ type: "string",
4746
+ enum: ["public", "private", "protected", "none"],
4747
+ 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."
4748
+ };
4749
+ var properties3 = {
4750
+ defaultAccessibility: defaultAccessibilitySchema
4751
+ };
4752
+ var optionsSchema3 = {
4753
+ type: "object",
4754
+ properties: properties3,
4755
+ additionalProperties: false
4756
+ };
4757
+ var schema4 = [optionsSchema3];
4758
+ var meta6 = {
4759
+ type: "suggestion",
4760
+ docs: docs6,
4761
+ fixable: "code",
4762
+ hasSuggestions: true,
4763
+ schema: schema4,
4764
+ messages: messages6
4765
+ };
4766
+ var defaultOptions3 = [{ defaultAccessibility: "public" }];
4767
+
4768
+ // packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
4769
+ var createRule6 = ESLintUtils6.RuleCreator(
4770
+ () => "https://github.com/F0rty-Tw0/lint-suite#explicit-accessibility"
4771
+ );
4772
+ var isPrivateMember = (node) => {
4773
+ if (node.type === TSESTree13.AST_NODE_TYPES.TSParameterProperty) return false;
4774
+ return node.key.type === TSESTree13.AST_NODE_TYPES.PrivateIdentifier;
4775
+ };
4776
+ var isConstructor = (node) => {
4777
+ if (node.type !== TSESTree13.AST_NODE_TYPES.MethodDefinition) return false;
4778
+ return node.kind === "constructor";
4779
+ };
4780
+ var reportTarget = (node) => {
4781
+ if (node.type !== TSESTree13.AST_NODE_TYPES.TSParameterProperty) {
4782
+ return node.key;
4783
+ }
4784
+ const { parameter } = node;
4785
+ if (parameter.type === TSESTree13.AST_NODE_TYPES.AssignmentPattern) {
4786
+ return parameter.left;
4787
+ }
4788
+ return parameter;
4789
+ };
4790
+ var memberName2 = (target, sourceCode) => {
4791
+ if (target.type === TSESTree13.AST_NODE_TYPES.Identifier) return target.name;
4792
+ return sourceCode.getText(target);
4182
4793
  };
4183
4794
  var modifierToken = (node, sourceCode) => {
4184
4795
  const lastDecorator = node.decorators.at(-1);
@@ -4202,7 +4813,7 @@ var autoFixOf = (node, accessibility, sourceCode) => {
4202
4813
  const autoFix = { fix };
4203
4814
  return autoFix;
4204
4815
  };
4205
- var suggestionOf = (node, accessibility, sourceCode) => {
4816
+ var suggestionOf2 = (node, accessibility, sourceCode) => {
4206
4817
  const data = { accessibility };
4207
4818
  const fix = insertModifier(node, accessibility, sourceCode);
4208
4819
  const suggestion = {
@@ -4216,93 +4827,1602 @@ var suggestionsOf = (node, fixAccessibility, sourceCode) => {
4216
4827
  const isOtherAccessibility = (accessibility) => {
4217
4828
  return accessibility !== fixAccessibility;
4218
4829
  };
4219
- const suggestionFor = (accessibility) => {
4220
- return suggestionOf(node, accessibility, sourceCode);
4830
+ const suggestionFor2 = (accessibility) => {
4831
+ return suggestionOf2(node, accessibility, sourceCode);
4832
+ };
4833
+ return accessibilities.filter(isOtherAccessibility).map(suggestionFor2);
4834
+ };
4835
+ var explicit_accessibility_default = createRule6({
4836
+ name: "explicit-accessibility",
4837
+ meta: meta6,
4838
+ defaultOptions: defaultOptions3,
4839
+ create(context, [{ defaultAccessibility = "public" }]) {
4840
+ const { sourceCode } = context;
4841
+ const check = (node) => {
4842
+ const isPrivate = isPrivateMember(node);
4843
+ if (node.accessibility || isPrivate) return;
4844
+ const target = reportTarget(node);
4845
+ const isConstructorMember = isConstructor(node);
4846
+ const usesPublic = isConstructorMember && defaultAccessibility !== "none";
4847
+ const fixAccessibility = usesPublic ? "public" : defaultAccessibility;
4848
+ const autoFix = autoFixOf(node, fixAccessibility, sourceCode);
4849
+ const name = memberName2(target, sourceCode);
4850
+ const data = { name };
4851
+ const suggest = suggestionsOf(node, fixAccessibility, sourceCode);
4852
+ const report3 = {
4853
+ node: target,
4854
+ messageId: "missingAccessibility",
4855
+ data,
4856
+ ...autoFix,
4857
+ suggest
4858
+ };
4859
+ context.report(report3);
4860
+ };
4861
+ const listeners = {
4862
+ AccessorProperty: check,
4863
+ MethodDefinition: check,
4864
+ PropertyDefinition: check,
4865
+ TSAbstractAccessorProperty: check,
4866
+ TSAbstractMethodDefinition: check,
4867
+ TSAbstractPropertyDefinition: check,
4868
+ TSParameterProperty: check
4869
+ };
4870
+ return listeners;
4871
+ }
4872
+ });
4873
+
4874
+ // packages/lint-suite/src/lib/rules/max-condition-operands/max-condition-operands.ts
4875
+ import { ESLintUtils as ESLintUtils7, TSESTree as TSESTree14 } from "@typescript-eslint/utils";
4876
+ var docs7 = {
4877
+ description: "Limit the number of operands in an if statement condition to keep it readable"
4878
+ };
4879
+ var messages7 = {
4880
+ tooManyOperands: "This condition has {{ count }} operands; extract it into a named const when it exceeds {{ max }}."
4881
+ };
4882
+ var maxSchema = { type: "integer", minimum: 1 };
4883
+ var properties4 = {
4884
+ max: maxSchema
4885
+ };
4886
+ var optionsSchema4 = {
4887
+ type: "object",
4888
+ properties: properties4,
4889
+ additionalProperties: false
4890
+ };
4891
+ var schema5 = [optionsSchema4];
4892
+ var meta7 = {
4893
+ type: "suggestion",
4894
+ docs: docs7,
4895
+ schema: schema5,
4896
+ messages: messages7
4897
+ };
4898
+ var createRule7 = ESLintUtils7.RuleCreator(
4899
+ () => "https://github.com/F0rty-Tw0/lint-suite#max-condition-operands"
4900
+ );
4901
+ var countOperands = (node) => {
4902
+ if (node.type !== TSESTree14.AST_NODE_TYPES.LogicalExpression) return 1;
4903
+ return countOperands(node.left) + countOperands(node.right);
4904
+ };
4905
+ var defaultOptions4 = [{ max: 3 }];
4906
+ var max_condition_operands_default = createRule7({
4907
+ name: "max-condition-operands",
4908
+ meta: meta7,
4909
+ defaultOptions: defaultOptions4,
4910
+ create(context, [{ max }]) {
4911
+ const listeners = {
4912
+ IfStatement(node) {
4913
+ const count = countOperands(node.test);
4914
+ if (count <= max) return;
4915
+ const data = { count, max };
4916
+ const report3 = {
4917
+ node: node.test,
4918
+ messageId: "tooManyOperands",
4919
+ data
4920
+ };
4921
+ context.report(report3);
4922
+ }
4923
+ };
4924
+ return listeners;
4925
+ }
4926
+ });
4927
+
4928
+ // packages/lint-suite/src/lib/rules/no-call-in-condition/no-call-in-condition.ts
4929
+ import { ESLintUtils as ESLintUtils8, TSESTree as TSESTree17 } from "@typescript-eslint/utils";
4930
+
4931
+ // packages/lint-suite/src/lib/rules/no-call-in-condition/common/no-call-in-condition.const.ts
4932
+ var PREDICATE_PATTERNS = ["^(is|has)[A-Z]"];
4933
+ var docs8 = {
4934
+ description: "Disallow a function call inside an if condition or a boolean const that feeds one"
4935
+ };
4936
+ var messages8 = {
4937
+ callInCondition: "Call '{{ callee }}' decides inside a condition; assign it to a named const first.",
4938
+ hoistCall: "Assign the call to '{{ name }}' above the statement."
4939
+ };
4940
+ var patternSchema = { type: "string" };
4941
+ var allowPredicatesSchema = {
4942
+ type: "array",
4943
+ items: patternSchema,
4944
+ description: "Regular expression sources for callee names treated as type predicates when no TypeScript program is available."
4945
+ };
4946
+ var properties5 = {
4947
+ allowPredicates: allowPredicatesSchema
4948
+ };
4949
+ var optionsSchema5 = {
4950
+ type: "object",
4951
+ properties: properties5,
4952
+ additionalProperties: false
4953
+ };
4954
+ var schema6 = [optionsSchema5];
4955
+ var meta8 = {
4956
+ type: "suggestion",
4957
+ docs: docs8,
4958
+ hasSuggestions: true,
4959
+ schema: schema6,
4960
+ messages: messages8
4961
+ };
4962
+ var ruleDefaults3 = {
4963
+ allowPredicates: PREDICATE_PATTERNS
4964
+ };
4965
+ var defaultOptions5 = [ruleDefaults3];
4966
+
4967
+ // packages/lint-suite/src/lib/rules/no-call-in-condition/utils/call-exemptions.util.ts
4968
+ import { ASTUtils as ASTUtils3, TSESTree as TSESTree16 } from "@typescript-eslint/utils";
4969
+
4970
+ // packages/lint-suite/src/lib/rules/no-call-in-condition/utils/condition-calls.util.ts
4971
+ import { ASTUtils as ASTUtils2, TSESTree as TSESTree15 } from "@typescript-eslint/utils";
4972
+ var collectParts = (node, parts) => {
4973
+ if (node.type === TSESTree15.AST_NODE_TYPES.CallExpression) {
4974
+ parts.calls.push(node);
4975
+ return;
4976
+ }
4977
+ if (node.type === TSESTree15.AST_NODE_TYPES.Identifier) {
4978
+ parts.identifiers.push(node);
4979
+ return;
4980
+ }
4981
+ if (node.type === TSESTree15.AST_NODE_TYPES.UnaryExpression) {
4982
+ collectParts(node.argument, parts);
4983
+ return;
4984
+ }
4985
+ const isLogical = node.type === TSESTree15.AST_NODE_TYPES.LogicalExpression;
4986
+ const isBinary = node.type === TSESTree15.AST_NODE_TYPES.BinaryExpression;
4987
+ if (!isLogical && !isBinary) return;
4988
+ collectParts(node.left, parts);
4989
+ collectParts(node.right, parts);
4990
+ };
4991
+ var conditionParts = (node) => {
4992
+ const parts = { calls: [], identifiers: [] };
4993
+ collectParts(node, parts);
4994
+ return parts;
4995
+ };
4996
+ var calleeName = (callee) => {
4997
+ if (callee.type === TSESTree15.AST_NODE_TYPES.Identifier) return callee.name;
4998
+ if (callee.type !== TSESTree15.AST_NODE_TYPES.MemberExpression) {
4999
+ return void 0;
5000
+ }
5001
+ const { property } = callee;
5002
+ if (property.type !== TSESTree15.AST_NODE_TYPES.Identifier) return void 0;
5003
+ return property.name;
5004
+ };
5005
+ var constFeed = (identifier, scope) => {
5006
+ const variable = ASTUtils2.findVariable(scope, identifier);
5007
+ if (variable?.defs.length !== 1) return void 0;
5008
+ const definition = variable.defs.at(0);
5009
+ if (!definition) return void 0;
5010
+ const declaration = definition.parent;
5011
+ if (declaration?.type !== TSESTree15.AST_NODE_TYPES.VariableDeclaration) {
5012
+ return void 0;
5013
+ }
5014
+ if (declaration.kind !== "const") return void 0;
5015
+ const { node } = definition;
5016
+ if (node.type !== TSESTree15.AST_NODE_TYPES.VariableDeclarator) {
5017
+ return void 0;
5018
+ }
5019
+ const { init } = node;
5020
+ if (!init) return void 0;
5021
+ if (init.type === TSESTree15.AST_NODE_TYPES.CallExpression) return void 0;
5022
+ const feed = { init, statement: declaration };
5023
+ return feed;
5024
+ };
5025
+
5026
+ // packages/lint-suite/src/lib/rules/no-call-in-condition/utils/call-exemptions.util.ts
5027
+ var PREDICATE_HOLDERS = [
5028
+ TSESTree16.AST_NODE_TYPES.ArrowFunctionExpression,
5029
+ TSESTree16.AST_NODE_TYPES.FunctionDeclaration,
5030
+ TSESTree16.AST_NODE_TYPES.FunctionExpression,
5031
+ TSESTree16.AST_NODE_TYPES.TSDeclareFunction
5032
+ ];
5033
+ var isPredicateHolder = ASTUtils3.isNodeOfTypes(PREDICATE_HOLDERS);
5034
+ var hasPredicateReturn = (node) => {
5035
+ if (!isPredicateHolder(node)) return false;
5036
+ const returned = node.returnType?.typeAnnotation.type;
5037
+ return returned === TSESTree16.AST_NODE_TYPES.TSTypePredicate;
5038
+ };
5039
+ var declaredFunction = (definition) => {
5040
+ const { node } = definition;
5041
+ if (node.type !== TSESTree16.AST_NODE_TYPES.VariableDeclarator) return node;
5042
+ return node.init ?? void 0;
5043
+ };
5044
+ var isThisSignalRead = (call) => {
5045
+ if (call.arguments.length > 0) return false;
5046
+ const { callee } = call;
5047
+ if (callee.type !== TSESTree16.AST_NODE_TYPES.MemberExpression) return false;
5048
+ if (callee.computed) return false;
5049
+ return callee.object.type === TSESTree16.AST_NODE_TYPES.ThisExpression;
5050
+ };
5051
+ var isScopePredicate = (callee, scope) => {
5052
+ if (callee.type !== TSESTree16.AST_NODE_TYPES.Identifier) return false;
5053
+ const variable = ASTUtils3.findVariable(scope, callee);
5054
+ const definition = variable?.defs.at(0);
5055
+ if (!definition) return false;
5056
+ const declaration = declaredFunction(definition);
5057
+ if (!declaration) return false;
5058
+ return hasPredicateReturn(declaration);
5059
+ };
5060
+ var isCheckerPredicate = (callee, services) => {
5061
+ const checker = services.program.getTypeChecker();
5062
+ const calleeType = services.getTypeAtLocation(callee);
5063
+ const signatures = calleeType.getCallSignatures();
5064
+ const isPredicateSignature = (signature) => {
5065
+ const predicate = checker.getTypePredicateOfSignature(signature);
5066
+ return predicate !== void 0;
5067
+ };
5068
+ return signatures.some(isPredicateSignature);
5069
+ };
5070
+ var isAllowedName = (callee, allowed) => {
5071
+ const name = calleeName(callee);
5072
+ if (name === void 0) return false;
5073
+ return allowed.some((pattern) => pattern.test(name));
5074
+ };
5075
+ var isExemptCall = (call, exemptions) => {
5076
+ const isSignalRead = isThisSignalRead(call);
5077
+ if (isSignalRead) return true;
5078
+ const { allowed, scope, services } = exemptions;
5079
+ const { callee } = call;
5080
+ const isDeclaredPredicate = isScopePredicate(callee, scope);
5081
+ if (isDeclaredPredicate) return true;
5082
+ if (services) return isCheckerPredicate(callee, services);
5083
+ return isAllowedName(callee, allowed);
5084
+ };
5085
+
5086
+ // packages/lint-suite/src/lib/rules/no-call-in-condition/no-call-in-condition.ts
5087
+ var createRule8 = ESLintUtils8.RuleCreator(
5088
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-call-in-condition"
5089
+ );
5090
+ var HOISTABLE_PARENTS = /* @__PURE__ */ new Set([
5091
+ TSESTree17.AST_NODE_TYPES.BlockStatement,
5092
+ TSESTree17.AST_NODE_TYPES.Program,
5093
+ TSESTree17.AST_NODE_TYPES.StaticBlock,
5094
+ TSESTree17.AST_NODE_TYPES.SwitchCase,
5095
+ TSESTree17.AST_NODE_TYPES.TSModuleBlock
5096
+ ]);
5097
+ var INDENT_PATTERN = /^[\t ]*/u;
5098
+ var typedServices = (context) => {
5099
+ const { parserServices } = context.sourceCode;
5100
+ if (!parserServices?.program) return void 0;
5101
+ return ESLintUtils8.getParserServices(context);
5102
+ };
5103
+ var statementIndent = (statement, sourceCode) => {
5104
+ const line = sourceCode.lines[statement.loc.start.line - 1] ?? "";
5105
+ const indent = INDENT_PATTERN.exec(line)?.[0];
5106
+ return indent ?? "";
5107
+ };
5108
+ var suggestionsOf2 = (call, statement, sourceCode) => {
5109
+ const empty = [];
5110
+ const isHoistable = HOISTABLE_PARENTS.has(statement.parent.type);
5111
+ if (!isHoistable) return empty;
5112
+ const name = calleeName(call.callee);
5113
+ if (name === void 0) return empty;
5114
+ const constName = `${name}Result`;
5115
+ const callText = sourceCode.getText(call);
5116
+ const indent = statementIndent(statement, sourceCode);
5117
+ const declaration = `const ${constName} = ${callText};
5118
+ ${indent}`;
5119
+ const fix = (fixer) => {
5120
+ const hoisted = fixer.insertTextBefore(statement, declaration);
5121
+ const replaced = fixer.replaceText(call, constName);
5122
+ const fixes = [hoisted, replaced];
5123
+ return fixes;
5124
+ };
5125
+ const data = { name: constName };
5126
+ const suggestion = {
5127
+ messageId: "hoistCall",
5128
+ data,
5129
+ fix
5130
+ };
5131
+ const suggestions = [suggestion];
5132
+ return suggestions;
5133
+ };
5134
+ var reportOf = (call, statement, sourceCode) => {
5135
+ const suggest = suggestionsOf2(call, statement, sourceCode);
5136
+ const callee = sourceCode.getText(call.callee);
5137
+ const data = { callee };
5138
+ const report3 = {
5139
+ node: call,
5140
+ messageId: "callInCondition",
5141
+ data,
5142
+ suggest
5143
+ };
5144
+ return report3;
5145
+ };
5146
+ var no_call_in_condition_default = createRule8({
5147
+ name: "no-call-in-condition",
5148
+ meta: meta8,
5149
+ defaultOptions: defaultOptions5,
5150
+ create(context, [{ allowPredicates = PREDICATE_PATTERNS }]) {
5151
+ const { sourceCode } = context;
5152
+ const services = typedServices(context);
5153
+ const allowed = allowPredicates.map(toRegExp);
5154
+ const reported = /* @__PURE__ */ new Set();
5155
+ const reportCalls = (calls, statement, exemptions) => {
5156
+ for (const call of calls) {
5157
+ const isReported = reported.has(call);
5158
+ if (isReported) continue;
5159
+ const isExempt = isExemptCall(call, exemptions);
5160
+ if (isExempt) continue;
5161
+ reported.add(call);
5162
+ const report3 = reportOf(call, statement, sourceCode);
5163
+ context.report(report3);
5164
+ }
5165
+ };
5166
+ const listeners = {
5167
+ IfStatement(node) {
5168
+ const { calls, identifiers } = conditionParts(node.test);
5169
+ const isEmpty = calls.length === 0 && identifiers.length === 0;
5170
+ if (isEmpty) return;
5171
+ const scope = sourceCode.getScope(node);
5172
+ const exemptions = { allowed, scope, services };
5173
+ reportCalls(calls, node, exemptions);
5174
+ for (const identifier of identifiers) {
5175
+ const feed = constFeed(identifier, scope);
5176
+ if (feed) {
5177
+ const fed = conditionParts(feed.init);
5178
+ reportCalls(fed.calls, feed.statement, exemptions);
5179
+ }
5180
+ }
5181
+ }
5182
+ };
5183
+ return listeners;
5184
+ }
5185
+ });
5186
+
5187
+ // packages/lint-suite/src/lib/rules/no-grouped-condition/no-grouped-condition.ts
5188
+ import { ESLintUtils as ESLintUtils9, TSESTree as TSESTree18 } from "@typescript-eslint/utils";
5189
+ var docs9 = {
5190
+ description: "Require a mixed-operator group inside a logical condition to be hoisted into a named const"
5191
+ };
5192
+ var messages9 = {
5193
+ groupedCondition: "This group mixes a different operator than its enclosing condition; hoist it into a named const.",
5194
+ hoistGroup: "Extract the group into a named const."
5195
+ };
5196
+ var meta9 = {
5197
+ type: "suggestion",
5198
+ docs: docs9,
5199
+ hasSuggestions: true,
5200
+ schema: [],
5201
+ messages: messages9
5202
+ };
5203
+ var createRule9 = ESLintUtils9.RuleCreator(
5204
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-grouped-condition"
5205
+ );
5206
+ var GROUP_NAME = "isGroup";
5207
+ var outermostLogical = (node) => {
5208
+ let current = node;
5209
+ while (current.parent.type === TSESTree18.AST_NODE_TYPES.LogicalExpression) {
5210
+ current = current.parent;
5211
+ }
5212
+ return current;
5213
+ };
5214
+ var reportRootOf = (node) => {
5215
+ const outermost = outermostLogical(node);
5216
+ const { parent } = outermost;
5217
+ if (parent.type === TSESTree18.AST_NODE_TYPES.IfStatement) {
5218
+ if (parent.test !== outermost) return void 0;
5219
+ const reportRoot2 = { statement: parent };
5220
+ return reportRoot2;
5221
+ }
5222
+ if (parent.type !== TSESTree18.AST_NODE_TYPES.VariableDeclarator) {
5223
+ return void 0;
5224
+ }
5225
+ const isConstInit = parent.init === outermost && parent.parent.kind === "const";
5226
+ if (!isConstInit) return void 0;
5227
+ const reportRoot = { statement: parent.parent };
5228
+ return reportRoot;
5229
+ };
5230
+ var suggestionFor = (node, statement, sourceCode) => {
5231
+ const groupText = sourceCode.getText(node);
5232
+ const line = sourceCode.lines[statement.loc.start.line - 1] ?? "";
5233
+ const indent = line.slice(0, statement.loc.start.column);
5234
+ const declaration = `const ${GROUP_NAME} = ${groupText};
5235
+ ${indent}`;
5236
+ const fix = (fixer) => {
5237
+ const hoisted = fixer.insertTextBefore(statement, declaration);
5238
+ const replaced = fixer.replaceText(node, GROUP_NAME);
5239
+ const fixes = [hoisted, replaced];
5240
+ return fixes;
5241
+ };
5242
+ const suggestion = {
5243
+ messageId: "hoistGroup",
5244
+ fix
5245
+ };
5246
+ return suggestion;
5247
+ };
5248
+ var no_grouped_condition_default = createRule9({
5249
+ name: "no-grouped-condition",
5250
+ meta: meta9,
5251
+ defaultOptions: [],
5252
+ create(context) {
5253
+ const { sourceCode } = context;
5254
+ const listeners = {
5255
+ "LogicalExpression > LogicalExpression"(node) {
5256
+ const { parent } = node;
5257
+ if (parent.type !== TSESTree18.AST_NODE_TYPES.LogicalExpression) return;
5258
+ const hasSameOperator = node.operator === parent.operator;
5259
+ if (hasSameOperator) return;
5260
+ const reportRoot = reportRootOf(node);
5261
+ if (!reportRoot) return;
5262
+ const suggestion = suggestionFor(
5263
+ node,
5264
+ reportRoot.statement,
5265
+ sourceCode
5266
+ );
5267
+ const report3 = {
5268
+ node,
5269
+ messageId: "groupedCondition",
5270
+ suggest: [suggestion]
5271
+ };
5272
+ context.report(report3);
5273
+ }
5274
+ };
5275
+ return listeners;
5276
+ }
5277
+ });
5278
+
5279
+ // packages/lint-suite/src/lib/rules/no-inline-object-types/no-inline-object-types.ts
5280
+ import { ESLintUtils as ESLintUtils10, TSESTree as TSESTree19 } from "@typescript-eslint/utils";
5281
+ var docs10 = {
5282
+ description: "Require every object type literal to be the body of a named type alias"
5283
+ };
5284
+ var messages10 = {
5285
+ inlineObjectType: "Object type literal must be the body of a named type alias."
5286
+ };
5287
+ var meta10 = {
5288
+ type: "suggestion",
5289
+ docs: docs10,
5290
+ schema: [],
5291
+ messages: messages10
5292
+ };
5293
+ var createRule10 = ESLintUtils10.RuleCreator(
5294
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-inline-object-types"
5295
+ );
5296
+ var no_inline_object_types_default = createRule10({
5297
+ name: "no-inline-object-types",
5298
+ meta: meta10,
5299
+ defaultOptions: [],
5300
+ create(context) {
5301
+ const listeners = {
5302
+ TSTypeLiteral(node) {
5303
+ const isAliasBody = node.parent.type === TSESTree19.AST_NODE_TYPES.TSTypeAliasDeclaration;
5304
+ if (isAliasBody) return;
5305
+ const report3 = {
5306
+ node,
5307
+ messageId: "inlineObjectType"
5308
+ };
5309
+ context.report(report3);
5310
+ }
5311
+ };
5312
+ return listeners;
5313
+ }
5314
+ });
5315
+
5316
+ // packages/lint-suite/src/lib/rules/no-inline-return-object/no-inline-return-object.ts
5317
+ import { ESLintUtils as ESLintUtils11, TSESTree as TSESTree20 } from "@typescript-eslint/utils";
5318
+ var docs11 = {
5319
+ description: "Require an inline returned object literal to be named as a constant first"
5320
+ };
5321
+ var messages11 = {
5322
+ inlineReturnObject: "Object literal is returned inline; name it as a constant first."
5323
+ };
5324
+ var meta11 = {
5325
+ type: "suggestion",
5326
+ docs: docs11,
5327
+ hasSuggestions: true,
5328
+ schema: [],
5329
+ messages: messages11
5330
+ };
5331
+ var createRule11 = ESLintUtils11.RuleCreator(
5332
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-inline-return-object"
5333
+ );
5334
+ var wrappingRange = (objectExpression, sourceCode) => {
5335
+ const before = sourceCode.getTokenBefore(objectExpression);
5336
+ const after = sourceCode.getTokenAfter(objectExpression);
5337
+ const isWrapped = before?.value === "(" && after?.value === ")";
5338
+ if (!isWrapped) return objectExpression.range;
5339
+ return [before.range[0], after.range[1]];
5340
+ };
5341
+ var returnSuggestion = (node, objectExpression, sourceCode) => {
5342
+ const indent = indentOf(node, sourceCode);
5343
+ const objectText = sourceCode.getText(objectExpression);
5344
+ const fix = (fixer) => {
5345
+ const declaration = fixer.insertTextBefore(
5346
+ node,
5347
+ `const result = ${objectText};
5348
+
5349
+ ${indent}`
5350
+ );
5351
+ const replaced = fixer.replaceText(objectExpression, "result");
5352
+ const fixes = [declaration, replaced];
5353
+ return fixes;
5354
+ };
5355
+ const suggestion = {
5356
+ messageId: "inlineReturnObject",
5357
+ fix
5358
+ };
5359
+ return suggestion;
5360
+ };
5361
+ var arrowSuggestion = (node, objectExpression, sourceCode) => {
5362
+ const indent = indentOf(node, sourceCode);
5363
+ const bodyIndent = `${indent} `;
5364
+ const objectText = sourceCode.getText(objectExpression);
5365
+ const replacement = `{
5366
+ ${bodyIndent}const result = ${objectText};
5367
+
5368
+ ${bodyIndent}return result;
5369
+ ${indent}}`;
5370
+ const range = wrappingRange(objectExpression, sourceCode);
5371
+ const fix = (fixer) => fixer.replaceTextRange(range, replacement);
5372
+ const suggestion = {
5373
+ messageId: "inlineReturnObject",
5374
+ fix
5375
+ };
5376
+ return suggestion;
5377
+ };
5378
+ var no_inline_return_object_default = createRule11({
5379
+ name: "no-inline-return-object",
5380
+ meta: meta11,
5381
+ defaultOptions: [],
5382
+ create(context) {
5383
+ const { sourceCode } = context;
5384
+ const listeners = {
5385
+ ReturnStatement(node) {
5386
+ if (node.argument?.type !== TSESTree20.AST_NODE_TYPES.ObjectExpression) {
5387
+ return;
5388
+ }
5389
+ const objectExpression = node.argument;
5390
+ const suggest = [returnSuggestion(node, objectExpression, sourceCode)];
5391
+ const report3 = {
5392
+ node,
5393
+ messageId: "inlineReturnObject",
5394
+ suggest
5395
+ };
5396
+ context.report(report3);
5397
+ },
5398
+ ArrowFunctionExpression(node) {
5399
+ if (!node.expression) return;
5400
+ if (node.body.type !== TSESTree20.AST_NODE_TYPES.ObjectExpression) return;
5401
+ const objectExpression = node.body;
5402
+ const suggest = [arrowSuggestion(node, objectExpression, sourceCode)];
5403
+ const report3 = {
5404
+ node,
5405
+ messageId: "inlineReturnObject",
5406
+ suggest
5407
+ };
5408
+ context.report(report3);
5409
+ }
5410
+ };
5411
+ return listeners;
5412
+ }
5413
+ });
5414
+
5415
+ // packages/lint-suite/src/lib/rules/no-nested-object-value/no-nested-object-value.ts
5416
+ import { ESLintUtils as ESLintUtils12, TSESTree as TSESTree23 } from "@typescript-eslint/utils";
5417
+
5418
+ // packages/lint-suite/src/lib/rules/utils/glob-matcher.util.ts
5419
+ var GLOB_TOKEN = /\*\*\/|\*\*|\*|\?|\{[^}]*\}|[^*?{]+/gu;
5420
+ var REGEXP_ESCAPE = /[.*+?^${}()|[\]\\]/gu;
5421
+ var escapeLiteral = (value) => value.replaceAll(REGEXP_ESCAPE, "\\$&");
5422
+ var braceAlternatives = (token) => {
5423
+ const inner = token.slice(1, -1);
5424
+ const alternatives = inner.split(",").map(escapeLiteral);
5425
+ return `(?:${alternatives.join("|")})`;
5426
+ };
5427
+ var translateToken = (token) => {
5428
+ if (token === "**/") return "(?:.*/)?";
5429
+ if (token === "**") return ".*";
5430
+ if (token === "*") return "[^/]*";
5431
+ if (token === "?") return "[^/]";
5432
+ const startsWithBrace = token.startsWith("{");
5433
+ const endsWithBrace = token.endsWith("}");
5434
+ if (startsWithBrace && endsWithBrace) return braceAlternatives(token);
5435
+ return escapeLiteral(token);
5436
+ };
5437
+ var globSource = (pattern) => {
5438
+ const tokens = pattern.match(GLOB_TOKEN) ?? [];
5439
+ return tokens.map(translateToken).join("");
5440
+ };
5441
+ var ALL_DEPTH_PREFIX = "**/";
5442
+ var ALL_DEPTH_GROUP = "(?:.*/)?";
5443
+ var stripAllDepthPrefix = (pattern) => {
5444
+ const hasPrefix = pattern.startsWith(ALL_DEPTH_PREFIX);
5445
+ if (!hasPrefix) return pattern;
5446
+ return pattern.slice(ALL_DEPTH_PREFIX.length);
5447
+ };
5448
+ var sharesAllDepthPrefix = (patterns) => patterns.every((pattern) => pattern.startsWith(ALL_DEPTH_PREFIX));
5449
+ var combinedSource = (patterns) => {
5450
+ const hoistPrefix = sharesAllDepthPrefix(patterns);
5451
+ if (!hoistPrefix) {
5452
+ const sources2 = patterns.map(globSource);
5453
+ return `(?:${sources2.join("|")})`;
5454
+ }
5455
+ const bodies = patterns.map(stripAllDepthPrefix);
5456
+ const sources = bodies.map(globSource);
5457
+ return `${ALL_DEPTH_GROUP}(?:${sources.join("|")})`;
5458
+ };
5459
+ var compileGlobs = (patterns) => {
5460
+ const source = combinedSource(patterns);
5461
+ const combined = new RegExp(`^${source}$`, "u");
5462
+ const matchPath = (path) => {
5463
+ const normalised = path.replaceAll("\\", "/");
5464
+ return combined.test(normalised);
5465
+ };
5466
+ return matchPath;
5467
+ };
5468
+
5469
+ // packages/lint-suite/src/lib/rules/no-nested-object-value/common/no-nested-object-value.const.ts
5470
+ var DEFAULT_CONFIG_FILES = [
5471
+ "**/*.config.{ts,js,mjs,cjs}",
5472
+ "**/eslint.config.*",
5473
+ "**/*.schema.ts"
5474
+ ];
5475
+ var docs12 = {
5476
+ description: "Require a nested object literal, array of objects, conditional, or chained call used as a property value to be named as a constant first"
5477
+ };
5478
+ var messages12 = {
5479
+ nestedValue: "Property '{{ key }}' holds a nested value inline; name it as a constant first."
5480
+ };
5481
+ var configFilesItemsSchema = { type: "string" };
5482
+ var configFilesSchema = {
5483
+ type: "array",
5484
+ items: configFilesItemsSchema
5485
+ };
5486
+ var properties6 = {
5487
+ configFiles: configFilesSchema
5488
+ };
5489
+ var optionsSchema6 = {
5490
+ type: "object",
5491
+ properties: properties6,
5492
+ additionalProperties: false
5493
+ };
5494
+ var schema7 = [optionsSchema6];
5495
+ var meta12 = {
5496
+ type: "suggestion",
5497
+ docs: docs12,
5498
+ hasSuggestions: true,
5499
+ schema: schema7,
5500
+ messages: messages12
5501
+ };
5502
+ var defaultOptions6 = [{ configFiles: DEFAULT_CONFIG_FILES }];
5503
+
5504
+ // packages/lint-suite/src/lib/rules/no-nested-object-value/utils/decorator-config.util.ts
5505
+ import { TSESTree as TSESTree21 } from "@typescript-eslint/utils";
5506
+ var MAX_DECORATOR_OBJECT_HOPS = 3;
5507
+ var isDecoratorArgument = (node) => {
5508
+ const { parent } = node;
5509
+ if (parent.type !== TSESTree21.AST_NODE_TYPES.CallExpression) return false;
5510
+ const { parent: grandparent } = parent;
5511
+ return grandparent.type === TSESTree21.AST_NODE_TYPES.Decorator;
5512
+ };
5513
+ var isInsideDecoratorConfig = (objectExpression, ancestors) => {
5514
+ let objectHops = 0;
5515
+ const isDecoratorConfig = isDecoratorArgument(objectExpression);
5516
+ if (isDecoratorConfig) return true;
5517
+ for (let index = ancestors.length - 1; index >= 0; index -= 1) {
5518
+ const ancestor = ancestors[index];
5519
+ if (ancestor?.type !== TSESTree21.AST_NODE_TYPES.ObjectExpression) continue;
5520
+ objectHops += 1;
5521
+ const isAncestorDecoratorConfig = isDecoratorArgument(ancestor);
5522
+ if (isAncestorDecoratorConfig) return true;
5523
+ if (objectHops >= MAX_DECORATOR_OBJECT_HOPS) return false;
5524
+ }
5525
+ return false;
5526
+ };
5527
+
5528
+ // packages/lint-suite/src/lib/rules/utils/enclosing-statement.util.ts
5529
+ import { TSESTree as TSESTree22 } from "@typescript-eslint/utils";
5530
+ var isBodyContainer = (node) => {
5531
+ return node.type === TSESTree22.AST_NODE_TYPES.Program || node.type === TSESTree22.AST_NODE_TYPES.BlockStatement || node.type === TSESTree22.AST_NODE_TYPES.StaticBlock;
5532
+ };
5533
+ var enclosingStatement2 = (node) => {
5534
+ let current = node;
5535
+ for (; ; ) {
5536
+ const { parent } = current;
5537
+ if (!parent || isBodyContainer(parent)) return current;
5538
+ current = parent;
5539
+ }
5540
+ };
5541
+
5542
+ // packages/lint-suite/src/lib/rules/no-nested-object-value/no-nested-object-value.ts
5543
+ var createRule12 = ESLintUtils12.RuleCreator(
5544
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-nested-object-value"
5545
+ );
5546
+ var isChainedCall = (node) => {
5547
+ const { callee } = node;
5548
+ if (callee.type !== TSESTree23.AST_NODE_TYPES.MemberExpression) return false;
5549
+ return callee.object.type === TSESTree23.AST_NODE_TYPES.CallExpression;
5550
+ };
5551
+ var isObjectElement = (element) => {
5552
+ return element?.type === TSESTree23.AST_NODE_TYPES.ObjectExpression;
5553
+ };
5554
+ var hasNestedObjectElement = (node) => {
5555
+ return node.elements.some(isObjectElement);
5556
+ };
5557
+ var isNestedValue = (value) => {
5558
+ if (value.type === TSESTree23.AST_NODE_TYPES.ObjectExpression) {
5559
+ return value.properties.length > 0;
5560
+ }
5561
+ if (value.type === TSESTree23.AST_NODE_TYPES.ArrayExpression) {
5562
+ return hasNestedObjectElement(value);
5563
+ }
5564
+ if (value.type === TSESTree23.AST_NODE_TYPES.ConditionalExpression) return true;
5565
+ if (value.type === TSESTree23.AST_NODE_TYPES.CallExpression) {
5566
+ return isChainedCall(value);
5567
+ }
5568
+ return false;
5569
+ };
5570
+ var CONFIG_FILE_MATCHERS = /* @__PURE__ */ new WeakMap();
5571
+ var configFileMatcher = (configFiles) => {
5572
+ const cached = CONFIG_FILE_MATCHERS.get(configFiles);
5573
+ if (cached) return cached;
5574
+ const matcher = compileGlobs(configFiles);
5575
+ CONFIG_FILE_MATCHERS.set(configFiles, matcher);
5576
+ return matcher;
5577
+ };
5578
+ var isConfigFile = (filename, configFiles) => {
5579
+ const matchesConfig = configFileMatcher(configFiles);
5580
+ return matchesConfig(filename);
5581
+ };
5582
+ var suggestionsFor = (property, key2, sourceCode) => {
5583
+ const empty = [];
5584
+ const isNamedKey = property.key.type === TSESTree23.AST_NODE_TYPES.Identifier && !property.computed;
5585
+ if (!isNamedKey) return empty;
5586
+ const statement = enclosingStatement2(property);
5587
+ const indent = indentOf(statement, sourceCode);
5588
+ const valueText = sourceCode.getText(property.value);
5589
+ const fix = (fixer) => {
5590
+ const declaration = fixer.insertTextBefore(
5591
+ statement,
5592
+ `const ${key2} = ${valueText};
5593
+ ${indent}`
5594
+ );
5595
+ const replaced = fixer.replaceText(property.value, key2);
5596
+ const fixes = [declaration, replaced];
5597
+ return fixes;
5598
+ };
5599
+ const data = { key: key2 };
5600
+ const suggestion = {
5601
+ messageId: "nestedValue",
5602
+ data,
5603
+ fix
5604
+ };
5605
+ const suggestions = [
5606
+ suggestion
5607
+ ];
5608
+ return suggestions;
5609
+ };
5610
+ var no_nested_object_value_default = createRule12({
5611
+ name: "no-nested-object-value",
5612
+ meta: meta12,
5613
+ defaultOptions: defaultOptions6,
5614
+ create(context, [{ configFiles }]) {
5615
+ const isExemptFile2 = isConfigFile(context.filename, configFiles);
5616
+ if (isExemptFile2) {
5617
+ const noListeners = {};
5618
+ return noListeners;
5619
+ }
5620
+ const { sourceCode } = context;
5621
+ const listeners = {
5622
+ Property(node) {
5623
+ if (node.parent.type !== TSESTree23.AST_NODE_TYPES.ObjectExpression)
5624
+ return;
5625
+ const isNested = isNestedValue(node.value);
5626
+ if (!isNested) return;
5627
+ const ancestors = sourceCode.getAncestors(node.parent);
5628
+ const isDecoratorConfig = isInsideDecoratorConfig(
5629
+ node.parent,
5630
+ ancestors
5631
+ );
5632
+ if (isDecoratorConfig) return;
5633
+ const key2 = sourceCode.getText(node.key);
5634
+ const suggest = suggestionsFor(node, key2, sourceCode);
5635
+ const data = { key: key2 };
5636
+ const report3 = {
5637
+ node,
5638
+ messageId: "nestedValue",
5639
+ data,
5640
+ suggest
5641
+ };
5642
+ context.report(report3);
5643
+ }
5644
+ };
5645
+ return listeners;
5646
+ }
5647
+ });
5648
+
5649
+ // packages/lint-suite/src/lib/rules/no-spread-expression/no-spread-expression.ts
5650
+ import { ESLintUtils as ESLintUtils13, TSESTree as TSESTree24 } from "@typescript-eslint/utils";
5651
+ var docs13 = {
5652
+ description: "Require a spread argument to be an identifier or member access, not an inline expression"
5653
+ };
5654
+ var messages13 = {
5655
+ spreadExpression: "Spread argument is an inline expression; name it as a constant first."
5656
+ };
5657
+ var meta13 = {
5658
+ type: "suggestion",
5659
+ docs: docs13,
5660
+ hasSuggestions: true,
5661
+ schema: [],
5662
+ messages: messages13
5663
+ };
5664
+ var createRule13 = ESLintUtils13.RuleCreator(
5665
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-spread-expression"
5666
+ );
5667
+ var ALLOWED_ARGUMENT_TYPES = /* @__PURE__ */ new Set([
5668
+ TSESTree24.AST_NODE_TYPES.Identifier,
5669
+ TSESTree24.AST_NODE_TYPES.MemberExpression
5670
+ ]);
5671
+ var no_spread_expression_default = createRule13({
5672
+ name: "no-spread-expression",
5673
+ meta: meta13,
5674
+ defaultOptions: [],
5675
+ create(context) {
5676
+ const { sourceCode } = context;
5677
+ const listeners = {
5678
+ SpreadElement(node) {
5679
+ const isAllowed = ALLOWED_ARGUMENT_TYPES.has(node.argument.type);
5680
+ if (isAllowed) return;
5681
+ const statement = enclosingStatement2(node);
5682
+ const indent = indentOf(statement, sourceCode);
5683
+ const argumentText = sourceCode.getText(node.argument);
5684
+ const fix = (fixer) => {
5685
+ const declaration = fixer.insertTextBefore(
5686
+ statement,
5687
+ `const spread = ${argumentText};
5688
+ ${indent}`
5689
+ );
5690
+ const replaced = fixer.replaceText(node.argument, "spread");
5691
+ const fixes = [declaration, replaced];
5692
+ return fixes;
5693
+ };
5694
+ const suggestion = {
5695
+ messageId: "spreadExpression",
5696
+ fix
5697
+ };
5698
+ const report3 = {
5699
+ node,
5700
+ messageId: "spreadExpression",
5701
+ suggest: [suggestion]
5702
+ };
5703
+ context.report(report3);
5704
+ }
5705
+ };
5706
+ return listeners;
5707
+ }
5708
+ });
5709
+
5710
+ // packages/lint-suite/src/lib/rules/no-unused-exports/no-unused-exports.ts
5711
+ import { ESLintUtils as ESLintUtils14 } from "@typescript-eslint/utils";
5712
+
5713
+ // packages/lint-suite/src/lib/rules/no-unused-exports/common/no-unused-exports.const.ts
5714
+ var entryPointDefaults = [
5715
+ "**/main.ts",
5716
+ "**/main.*.ts",
5717
+ "**/public-api.ts",
5718
+ "**/index.ts",
5719
+ "**/*.config.ts",
5720
+ "**/*.config.mts",
5721
+ "**/*.config.cts",
5722
+ "**/*.spec.ts",
5723
+ "**/*.spec.util.ts",
5724
+ "**/*.stub.ts",
5725
+ "**/*.mock.ts",
5726
+ "**/*.d.ts",
5727
+ "**/*.stories.ts",
5728
+ "**/environment*.ts"
5729
+ ];
5730
+ var STAR_NAME = "*";
5731
+ var docs14 = {
5732
+ description: "Disallow exports that no other file in the TypeScript program imports"
5733
+ };
5734
+ var messages14 = {
5735
+ unusedExport: "Export '{{ name }}' is never imported by another file.",
5736
+ unusedModule: "This module exports names but is never imported."
5737
+ };
5738
+ var entryPointItemsSchema = { type: "string" };
5739
+ var entryPointsSchema = {
5740
+ type: "array",
5741
+ items: entryPointItemsSchema,
5742
+ description: "Globs whose files are treated as project entry points."
5743
+ };
5744
+ var properties7 = {
5745
+ entryPoints: entryPointsSchema
5746
+ };
5747
+ var optionsSchema7 = {
5748
+ type: "object",
5749
+ properties: properties7,
5750
+ additionalProperties: false
5751
+ };
5752
+ var schema8 = [optionsSchema7];
5753
+ var meta14 = {
5754
+ type: "problem",
5755
+ docs: docs14,
5756
+ schema: schema8,
5757
+ messages: messages14
5758
+ };
5759
+ var ruleDefaults4 = { entryPoints: [...entryPointDefaults] };
5760
+ var defaultOptions7 = [ruleDefaults4];
5761
+
5762
+ // packages/lint-suite/src/lib/rules/no-unused-exports/export-index.ts
5763
+ import { dirname as dirname10 } from "node:path";
5764
+
5765
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/export-usage.util.ts
5766
+ var MAX_DEPTH = 50;
5767
+ var reExportIndex = (byFile) => {
5768
+ const index = /* @__PURE__ */ new Map();
5769
+ for (const [fileName, edges] of byFile) {
5770
+ const names = /* @__PURE__ */ new Map();
5771
+ for (const edge of edges.reExports) {
5772
+ names.set(edge.exported, edge);
5773
+ }
5774
+ index.set(fileName, names);
5775
+ }
5776
+ return index;
5777
+ };
5778
+ var push = (walk, fileName, name) => {
5779
+ const key2 = { fileName, name };
5780
+ walk.keys.push(key2);
5781
+ };
5782
+ var deeper = (walk) => {
5783
+ const next = {
5784
+ visited: walk.visited,
5785
+ depth: walk.depth + 1,
5786
+ keys: walk.keys
5787
+ };
5788
+ return next;
5789
+ };
5790
+ var isNewStep = (walk, key2) => {
5791
+ if (walk.depth > MAX_DEPTH) return false;
5792
+ const isVisited = walk.visited.has(key2);
5793
+ if (isVisited) return false;
5794
+ walk.visited.add(key2);
5795
+ return true;
5796
+ };
5797
+ var markName = (context, fileName, name, walk) => {
5798
+ const isNew = isNewStep(walk, `${fileName} ${name}`);
5799
+ if (!isNew) return;
5800
+ const edges = context.byFile.get(fileName);
5801
+ if (!edges) return;
5802
+ push(walk, fileName, name);
5803
+ const reExport = context.reExportsByName.get(fileName)?.get(name);
5804
+ if (reExport) {
5805
+ markName(context, reExport.target, reExport.source, deeper(walk));
5806
+ }
5807
+ const isDeclaredLocally = edges.declared.has(name);
5808
+ if (isDeclaredLocally) return;
5809
+ for (const target of edges.starTargets) {
5810
+ markName(context, target, name, deeper(walk));
5811
+ }
5812
+ };
5813
+ var markAll = (context, fileName, walk) => {
5814
+ const isNew = isNewStep(walk, `${fileName} ${STAR_NAME}`);
5815
+ if (!isNew) return;
5816
+ const edges = context.byFile.get(fileName);
5817
+ if (!edges) return;
5818
+ for (const name of edges.exports) {
5819
+ push(walk, fileName, name);
5820
+ }
5821
+ for (const edge of edges.reExports) {
5822
+ markName(context, edge.target, edge.source, deeper(walk));
5823
+ }
5824
+ for (const target of edges.starTargets) {
5825
+ markAll(context, target, deeper(walk));
5826
+ }
5827
+ };
5828
+ var markUsage = (context, target, name) => {
5829
+ const keys = [];
5830
+ const walk = { visited: /* @__PURE__ */ new Set(), depth: 0, keys };
5831
+ if (name === STAR_NAME) {
5832
+ markAll(context, target, walk);
5833
+ return keys;
5834
+ }
5835
+ markName(context, target, name, walk);
5836
+ return keys;
5837
+ };
5838
+
5839
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/aggregate-update.util.ts
5840
+ var countDelta = (counts, key2, delta) => {
5841
+ const current = counts.get(key2);
5842
+ const next = (current ?? 0) + delta;
5843
+ if (next > 0) {
5844
+ counts.set(key2, next);
5845
+ return;
5846
+ }
5847
+ counts.delete(key2);
5848
+ };
5849
+ var usageDelta = (usage, key2, delta) => {
5850
+ const existing = usage.get(key2.fileName);
5851
+ const names = existing ?? /* @__PURE__ */ new Map();
5852
+ const current = names.get(key2.name);
5853
+ const next = (current ?? 0) + delta;
5854
+ if (next > 0) names.set(key2.name, next);
5855
+ else names.delete(key2.name);
5856
+ if (names.size > 0) usage.set(key2.fileName, names);
5857
+ else usage.delete(key2.fileName);
5858
+ };
5859
+ var fileContribution = (context, edges) => {
5860
+ const usage = [];
5861
+ const imported = [];
5862
+ const starTouched = [];
5863
+ for (const edge of edges.imports) {
5864
+ imported.push(edge.target);
5865
+ for (const name of edge.names) {
5866
+ if (name === STAR_NAME) starTouched.push(edge.target);
5867
+ const keys = markUsage(context, edge.target, name);
5868
+ for (const key2 of keys) usage.push(key2);
5869
+ }
5870
+ }
5871
+ for (const edge of edges.reExports) {
5872
+ imported.push(edge.target);
5873
+ starTouched.push(edge.target);
5874
+ }
5875
+ for (const target of edges.starTargets) {
5876
+ imported.push(target);
5877
+ starTouched.push(target);
5878
+ }
5879
+ const contribution = { usage, imported, starTouched };
5880
+ return contribution;
5881
+ };
5882
+ var applyContribution = (aggregate, contribution, delta) => {
5883
+ for (const key2 of contribution.usage) {
5884
+ usageDelta(aggregate.usage, key2, delta);
5885
+ }
5886
+ for (const target of contribution.imported) {
5887
+ countDelta(aggregate.imported, target, delta);
5888
+ }
5889
+ for (const target of contribution.starTouched) {
5890
+ countDelta(aggregate.starTouched, target, delta);
5891
+ }
5892
+ };
5893
+ var contextOf = (aggregate) => {
5894
+ const context = {
5895
+ byFile: aggregate.byFile,
5896
+ reExportsByName: aggregate.reExportsByName
5897
+ };
5898
+ return context;
5899
+ };
5900
+ var addFile = (aggregate, edges) => {
5901
+ const context = contextOf(aggregate);
5902
+ const contribution = fileContribution(context, edges);
5903
+ aggregate.contributions.set(edges.fileName, contribution);
5904
+ applyContribution(aggregate, contribution, 1);
5905
+ };
5906
+ var buildAggregate = (byFile) => {
5907
+ const aggregate = {
5908
+ byFile,
5909
+ usage: /* @__PURE__ */ new Map(),
5910
+ imported: /* @__PURE__ */ new Map(),
5911
+ reExportsByName: reExportIndex(byFile),
5912
+ contributions: /* @__PURE__ */ new Map(),
5913
+ starTouched: /* @__PURE__ */ new Map()
5914
+ };
5915
+ for (const edges of byFile.values()) {
5916
+ addFile(aggregate, edges);
5917
+ }
5918
+ return aggregate;
5919
+ };
5920
+ var dropFile = (aggregate, fileName) => {
5921
+ const contribution = aggregate.contributions.get(fileName);
5922
+ if (!contribution) return;
5923
+ applyContribution(aggregate, contribution, -1);
5924
+ aggregate.contributions.delete(fileName);
5925
+ };
5926
+ var replaceEdges = (aggregate, change) => {
5927
+ if (!change.after) {
5928
+ aggregate.byFile.delete(change.fileName);
5929
+ aggregate.reExportsByName.delete(change.fileName);
5930
+ return;
5931
+ }
5932
+ aggregate.byFile.set(change.fileName, change.after);
5933
+ aggregate.reExportsByName.set(change.fileName, /* @__PURE__ */ new Map());
5934
+ };
5935
+ var updateAggregate = (aggregate, changes) => {
5936
+ for (const change of changes) {
5937
+ dropFile(aggregate, change.fileName);
5938
+ replaceEdges(aggregate, change);
5939
+ }
5940
+ for (const change of changes) {
5941
+ if (change.after) addFile(aggregate, change.after);
5942
+ }
5943
+ };
5944
+
5945
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/file-changes.util.ts
5946
+ var fileChanges = (before, after) => {
5947
+ const changes = [];
5948
+ for (const [fileName, edges] of after) {
5949
+ const previous = before.get(fileName);
5950
+ if (previous === edges) continue;
5951
+ const change = { fileName, before: previous, after: edges };
5952
+ changes.push(change);
5953
+ }
5954
+ for (const [fileName, edges] of before) {
5955
+ const isKept = after.has(fileName);
5956
+ if (isKept) continue;
5957
+ const change = { fileName, before: edges, after: void 0 };
5958
+ changes.push(change);
5959
+ }
5960
+ return changes;
5961
+ };
5962
+ var isBarrel = (edges) => {
5963
+ if (!edges) return false;
5964
+ const hasStarTargets = edges.starTargets.length > 0;
5965
+ return edges.reExports.length > 0 || hasStarTargets;
5966
+ };
5967
+ var hasSameExports = (change) => {
5968
+ const before = change.before?.exports ?? [];
5969
+ const after = change.after?.exports ?? [];
5970
+ if (before.length !== after.length) return false;
5971
+ return before.every((name, index) => name === after[index]);
5972
+ };
5973
+ var isIncremental = (changes, starTouched) => {
5974
+ for (const change of changes) {
5975
+ const wasBarrel = isBarrel(change.before);
5976
+ const isNowBarrel = isBarrel(change.after);
5977
+ const touchesBarrel = wasBarrel || isNowBarrel;
5978
+ if (touchesBarrel) return false;
5979
+ const isStarTouched = starTouched.has(change.fileName);
5980
+ if (!isStarTouched) continue;
5981
+ const sameExports = hasSameExports(change);
5982
+ if (!sameExports) return false;
5983
+ }
5984
+ return true;
5985
+ };
5986
+
5987
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/module-edges.util.ts
5988
+ import {
5989
+ SyntaxKind as SyntaxKind7,
5990
+ forEachChild as forEachChild5,
5991
+ isCallExpression as isCallExpression4,
5992
+ isExportAssignment,
5993
+ isExportDeclaration,
5994
+ isImportDeclaration as isImportDeclaration2,
5995
+ isModuleDeclaration,
5996
+ isNamespaceImport
5997
+ } from "typescript";
5998
+
5999
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/export-edges.util.ts
6000
+ import { isNamespaceExport } from "typescript";
6001
+ var addNamespaceExport = (clause, edges, target) => {
6002
+ const exported = clause.name.text;
6003
+ edges.exports.push(exported);
6004
+ edges.declared.add(exported);
6005
+ if (target === void 0) return;
6006
+ const edge = { target, names: ["*"] };
6007
+ edges.imports.push(edge);
6008
+ };
6009
+ var addExportFrom = (statement, edges, resolve5) => {
6010
+ const target = resolve5(statement.moduleSpecifier);
6011
+ const clause = statement.exportClause;
6012
+ if (!clause) {
6013
+ if (target !== void 0) edges.starTargets.push(target);
6014
+ return;
6015
+ }
6016
+ if (isNamespaceExport(clause)) {
6017
+ addNamespaceExport(clause, edges, target);
6018
+ return;
6019
+ }
6020
+ for (const element of clause.elements) {
6021
+ const origin = element.propertyName ?? element.name;
6022
+ const exported = element.name.text;
6023
+ edges.exports.push(exported);
6024
+ if (target === void 0) continue;
6025
+ const edge = { target, source: origin.text, exported };
6026
+ edges.reExports.push(edge);
6027
+ }
6028
+ };
6029
+ var addLocalExport = (statement, edges) => {
6030
+ const clause = statement.exportClause;
6031
+ if (!clause) return;
6032
+ if (isNamespaceExport(clause)) return;
6033
+ for (const element of clause.elements) {
6034
+ const exported = element.name.text;
6035
+ edges.exports.push(exported);
6036
+ edges.declared.add(exported);
6037
+ }
6038
+ };
6039
+
6040
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/exported-names.util.ts
6041
+ import {
6042
+ SyntaxKind as SyntaxKind6,
6043
+ canHaveModifiers,
6044
+ getModifiers,
6045
+ isClassDeclaration,
6046
+ isEnumDeclaration,
6047
+ isFunctionDeclaration as isFunctionDeclaration2,
6048
+ isIdentifier as isIdentifier10,
6049
+ isInterfaceDeclaration,
6050
+ isTypeAliasDeclaration,
6051
+ isVariableStatement
6052
+ } from "typescript";
6053
+ var hasModifier = (statement, kind) => {
6054
+ if (!canHaveModifiers(statement)) return false;
6055
+ const modifiers = getModifiers(statement);
6056
+ const matched = modifiers?.some((modifier) => modifier.kind === kind);
6057
+ return matched === true;
6058
+ };
6059
+ var variableNames = (statement) => {
6060
+ const names = [];
6061
+ for (const declaration of statement.declarationList.declarations) {
6062
+ if (!isIdentifier10(declaration.name)) continue;
6063
+ names.push(declaration.name.text);
6064
+ }
6065
+ return names;
6066
+ };
6067
+ var declaredName = (statement) => {
6068
+ if (isFunctionDeclaration2(statement)) return statement.name?.text;
6069
+ if (isClassDeclaration(statement)) return statement.name?.text;
6070
+ if (isTypeAliasDeclaration(statement)) return statement.name.text;
6071
+ if (isInterfaceDeclaration(statement)) return statement.name.text;
6072
+ if (isEnumDeclaration(statement)) return statement.name.text;
6073
+ return void 0;
6074
+ };
6075
+ var exportedNames = (statement) => {
6076
+ const isExported = hasModifier(statement, SyntaxKind6.ExportKeyword);
6077
+ if (!isExported) return [];
6078
+ const isDefault = hasModifier(statement, SyntaxKind6.DefaultKeyword);
6079
+ if (isDefault) return ["default"];
6080
+ if (isVariableStatement(statement)) return variableNames(statement);
6081
+ const name = declaredName(statement);
6082
+ if (name === void 0) return [];
6083
+ return [name];
6084
+ };
6085
+
6086
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/module-target.util.ts
6087
+ var declarationOf = (specifier, checker) => {
6088
+ const symbol = checker.getSymbolAtLocation(specifier);
6089
+ if (!symbol) return void 0;
6090
+ return symbol.valueDeclaration ?? symbol.declarations?.[0];
6091
+ };
6092
+ var moduleTarget = (specifier, checker, isExternal) => {
6093
+ if (!specifier) return void 0;
6094
+ const declaration = declarationOf(specifier, checker);
6095
+ if (!declaration) return void 0;
6096
+ const sourceFile = declaration.getSourceFile();
6097
+ if (sourceFile.isDeclarationFile) return void 0;
6098
+ const isSourceExternal = isExternal(sourceFile);
6099
+ if (isSourceExternal) return void 0;
6100
+ return sourceFile.fileName;
6101
+ };
6102
+
6103
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/module-edges.util.ts
6104
+ var importNames = (clause) => {
6105
+ if (!clause) return [];
6106
+ const names = [];
6107
+ if (clause.name) names.push("default");
6108
+ const bindings = clause.namedBindings;
6109
+ if (!bindings) return names;
6110
+ if (isNamespaceImport(bindings)) {
6111
+ names.push("*");
6112
+ return names;
6113
+ }
6114
+ for (const element of bindings.elements) {
6115
+ const origin = element.propertyName ?? element.name;
6116
+ names.push(origin.text);
6117
+ }
6118
+ return names;
6119
+ };
6120
+ var addImport2 = (statement, edges, resolve5) => {
6121
+ const target = resolve5(statement.moduleSpecifier);
6122
+ if (target === void 0) return;
6123
+ const names = importNames(statement.importClause);
6124
+ const edge = { target, names };
6125
+ edges.imports.push(edge);
6126
+ };
6127
+ var DYNAMIC_IMPORT_TEXT = "import(";
6128
+ var dynamicImportTarget = (node, resolve5) => {
6129
+ if (!isCallExpression4(node)) return void 0;
6130
+ const isDynamicImport = node.expression.kind === SyntaxKind7.ImportKeyword;
6131
+ if (!isDynamicImport) return void 0;
6132
+ return resolve5(node.arguments[0]);
6133
+ };
6134
+ var collectDynamicImports = (node, resolve5, targets) => {
6135
+ const target = dynamicImportTarget(node, resolve5);
6136
+ if (target !== void 0) targets.push(target);
6137
+ forEachChild5(node, (child) => {
6138
+ collectDynamicImports(child, resolve5, targets);
6139
+ });
6140
+ };
6141
+ var addDynamicImports = (sourceFile, edges, resolve5) => {
6142
+ const hasDynamicImport = sourceFile.text.includes(DYNAMIC_IMPORT_TEXT);
6143
+ if (!hasDynamicImport) return;
6144
+ const targets = [];
6145
+ collectDynamicImports(sourceFile, resolve5, targets);
6146
+ for (const target of targets) {
6147
+ const edge = { target, names: ["*"] };
6148
+ edges.imports.push(edge);
6149
+ }
6150
+ };
6151
+ var addDefaultExport = (edges) => {
6152
+ edges.exports.push("default");
6153
+ edges.declared.add("default");
6154
+ };
6155
+ var addDeclaredExports = (statement, edges) => {
6156
+ for (const name of exportedNames(statement)) {
6157
+ edges.exports.push(name);
6158
+ edges.declared.add(name);
6159
+ }
6160
+ };
6161
+ var emptyEdges = () => {
6162
+ const edges = {
6163
+ exports: [],
6164
+ declared: /* @__PURE__ */ new Set(),
6165
+ imports: [],
6166
+ reExports: [],
6167
+ starTargets: []
6168
+ };
6169
+ return edges;
6170
+ };
6171
+ var moduleEdges = (sourceFile, checker, isExternal) => {
6172
+ const resolve5 = (specifier) => moduleTarget(specifier, checker, isExternal);
6173
+ const edges = emptyEdges();
6174
+ let skipped = false;
6175
+ for (const statement of sourceFile.statements) {
6176
+ if (isImportDeclaration2(statement)) {
6177
+ addImport2(statement, edges, resolve5);
6178
+ } else if (isExportDeclaration(statement)) {
6179
+ if (statement.moduleSpecifier) addExportFrom(statement, edges, resolve5);
6180
+ else addLocalExport(statement, edges);
6181
+ } else if (isExportAssignment(statement)) {
6182
+ if (statement.isExportEquals) skipped = true;
6183
+ else addDefaultExport(edges);
6184
+ } else if (isModuleDeclaration(statement)) {
6185
+ skipped = true;
6186
+ } else {
6187
+ addDeclaredExports(statement, edges);
6188
+ }
6189
+ }
6190
+ addDynamicImports(sourceFile, edges, resolve5);
6191
+ const fileEdges2 = {
6192
+ fileName: sourceFile.fileName,
6193
+ ...edges,
6194
+ skipped
6195
+ };
6196
+ return fileEdges2;
6197
+ };
6198
+
6199
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/public-exports.util.ts
6200
+ var publicExports = (context, entryFiles) => {
6201
+ const index = /* @__PURE__ */ new Map();
6202
+ for (const entryFile of entryFiles) {
6203
+ const keys = markUsage(context, entryFile, STAR_NAME);
6204
+ for (const key2 of keys) {
6205
+ const names = index.get(key2.fileName) ?? /* @__PURE__ */ new Set();
6206
+ names.add(key2.name);
6207
+ index.set(key2.fileName, names);
6208
+ }
6209
+ }
6210
+ return index;
6211
+ };
6212
+
6213
+ // packages/lint-suite/src/lib/rules/no-unused-exports/export-index.ts
6214
+ var FILE_EDGES = /* @__PURE__ */ new WeakMap();
6215
+ var AGGREGATES = /* @__PURE__ */ new WeakMap();
6216
+ var PUBLIC = /* @__PURE__ */ new WeakMap();
6217
+ var LAST_AGGREGATE = /* @__PURE__ */ new Map();
6218
+ var projectKey = (program) => {
6219
+ const configFilePath = program.getCompilerOptions()["configFilePath"];
6220
+ if (typeof configFilePath === "string") return configFilePath;
6221
+ const directories = program.getRootFileNames().map(dirname10);
6222
+ const uniqueDirectorySet = new Set(directories);
6223
+ const uniqueDirectories = [...uniqueDirectorySet];
6224
+ const sortedDirectories = uniqueDirectories.sort();
6225
+ return sortedDirectories.join("|");
6226
+ };
6227
+ var fileEdges = (sourceFile, checker, isExternal) => {
6228
+ const cached = FILE_EDGES.get(sourceFile);
6229
+ if (cached) return cached;
6230
+ const edges = moduleEdges(sourceFile, checker, isExternal);
6231
+ FILE_EDGES.set(sourceFile, edges);
6232
+ return edges;
6233
+ };
6234
+ var programEdges = (program) => {
6235
+ const checker = program.getTypeChecker();
6236
+ const isExternal = (sourceFile) => program.isSourceFileFromExternalLibrary(sourceFile);
6237
+ const byFile = /* @__PURE__ */ new Map();
6238
+ for (const sourceFile of program.getSourceFiles()) {
6239
+ if (sourceFile.isDeclarationFile) continue;
6240
+ const isSourceExternal = isExternal(sourceFile);
6241
+ if (isSourceExternal) continue;
6242
+ byFile.set(sourceFile.fileName, fileEdges(sourceFile, checker, isExternal));
6243
+ }
6244
+ return byFile;
6245
+ };
6246
+ var nextAggregate = (byFile, previous) => {
6247
+ if (!previous) return buildAggregate(byFile);
6248
+ const changes = fileChanges(previous.byFile, byFile);
6249
+ const isPatchable = isIncremental(changes, previous.starTouched);
6250
+ if (!isPatchable) return buildAggregate(byFile);
6251
+ updateAggregate(previous, changes);
6252
+ PUBLIC.delete(previous);
6253
+ return previous;
6254
+ };
6255
+ var exportIndex = (program) => {
6256
+ const cached = AGGREGATES.get(program);
6257
+ if (cached) return cached;
6258
+ const key2 = projectKey(program);
6259
+ const byFile = programEdges(program);
6260
+ const aggregate = nextAggregate(byFile, LAST_AGGREGATE.get(key2));
6261
+ AGGREGATES.set(program, aggregate);
6262
+ LAST_AGGREGATE.set(key2, aggregate);
6263
+ return aggregate;
6264
+ };
6265
+ var publicExportIndex = (aggregate, isEntry) => {
6266
+ const byMatcher = PUBLIC.get(aggregate) ?? /* @__PURE__ */ new WeakMap();
6267
+ const cached = byMatcher.get(isEntry);
6268
+ if (cached) return cached;
6269
+ const keys = aggregate.byFile.keys();
6270
+ const fileNames = [...keys];
6271
+ const entryFiles = fileNames.filter(isEntry);
6272
+ const context = {
6273
+ byFile: aggregate.byFile,
6274
+ reExportsByName: aggregate.reExportsByName
4221
6275
  };
4222
- return accessibilities.filter(isOtherAccessibility).map(suggestionFor);
6276
+ const index = publicExports(context, entryFiles);
6277
+ byMatcher.set(isEntry, index);
6278
+ PUBLIC.set(aggregate, byMatcher);
6279
+ return index;
4223
6280
  };
4224
- var explicit_accessibility_default = createRule3({
4225
- name: "explicit-accessibility",
4226
- meta: meta3,
4227
- defaultOptions: defaultOptions3,
4228
- create(context, [{ defaultAccessibility = "public" }]) {
4229
- const { sourceCode } = context;
4230
- const check = (node) => {
4231
- const isPrivate = isPrivateMember(node);
4232
- if (node.accessibility || isPrivate) return;
4233
- const target = reportTarget(node);
4234
- const isConstructorMember = isConstructor(node);
4235
- const usesPublic = isConstructorMember && defaultAccessibility !== "none";
4236
- const fixAccessibility = usesPublic ? "public" : defaultAccessibility;
4237
- const autoFix = autoFixOf(node, fixAccessibility, sourceCode);
4238
- const name = memberName2(target, sourceCode);
4239
- const data = { name };
4240
- const suggest = suggestionsOf(node, fixAccessibility, sourceCode);
4241
- const report = {
4242
- node: target,
4243
- messageId: "missingAccessibility",
4244
- data,
4245
- ...autoFix,
4246
- suggest
4247
- };
4248
- context.report(report);
4249
- };
4250
- const listeners = {
4251
- AccessorProperty: check,
4252
- MethodDefinition: check,
4253
- PropertyDefinition: check,
4254
- TSAbstractAccessorProperty: check,
4255
- TSAbstractMethodDefinition: check,
4256
- TSAbstractPropertyDefinition: check,
4257
- TSParameterProperty: check
4258
- };
4259
- return listeners;
4260
- }
4261
- });
4262
6281
 
4263
- // packages/lint-suite/src/lib/rules/no-inline-object-types/no-inline-object-types.ts
4264
- import { ESLintUtils as ESLintUtils4, TSESTree as TSESTree11 } from "@typescript-eslint/utils";
4265
- var docs4 = {
4266
- description: "Require nested object types inside a type alias to be extracted to a named type alias"
6282
+ // packages/lint-suite/src/lib/rules/no-unused-exports/utils/export-nodes.util.ts
6283
+ import { TSESTree as TSESTree25 } from "@typescript-eslint/utils";
6284
+ var { AST_NODE_TYPES } = TSESTree25;
6285
+ var exportedName = (exported) => {
6286
+ if (exported.type === AST_NODE_TYPES.Identifier) return exported.name;
6287
+ return exported.value;
6288
+ };
6289
+ var addVariableNodes = (declaration, nodes) => {
6290
+ for (const declarator of declaration.declarations) {
6291
+ const { id } = declarator;
6292
+ if (id.type !== AST_NODE_TYPES.Identifier) continue;
6293
+ nodes.set(id.name, id);
6294
+ }
6295
+ };
6296
+ var addNamedDeclarationNode = (declaration, nodes) => {
6297
+ const { id } = declaration;
6298
+ if (!id) return;
6299
+ if (id.type !== AST_NODE_TYPES.Identifier) return;
6300
+ nodes.set(id.name, id);
6301
+ };
6302
+ var addNamedExportNodes = (statement, nodes) => {
6303
+ for (const specifier of statement.specifiers) {
6304
+ const name = exportedName(specifier.exported);
6305
+ nodes.set(name, specifier);
6306
+ }
6307
+ const { declaration } = statement;
6308
+ if (!declaration) return;
6309
+ if (declaration.type === AST_NODE_TYPES.VariableDeclaration) {
6310
+ addVariableNodes(declaration, nodes);
6311
+ return;
6312
+ }
6313
+ addNamedDeclarationNode(declaration, nodes);
4267
6314
  };
4268
- var messages4 = {
4269
- inlineObjectType: "Inline object type must be extracted to a named type alias."
6315
+ var exportNodes = (program) => {
6316
+ const nodes = /* @__PURE__ */ new Map();
6317
+ for (const statement of program.body) {
6318
+ if (statement.type === AST_NODE_TYPES.ExportDefaultDeclaration) {
6319
+ nodes.set("default", statement);
6320
+ continue;
6321
+ }
6322
+ if (statement.type !== AST_NODE_TYPES.ExportNamedDeclaration) continue;
6323
+ addNamedExportNodes(statement, nodes);
6324
+ }
6325
+ return nodes;
4270
6326
  };
4271
- var meta4 = {
4272
- type: "suggestion",
4273
- docs: docs4,
4274
- schema: [],
4275
- messages: messages4
6327
+
6328
+ // packages/lint-suite/src/lib/rules/no-unused-exports/report-unused-exports.ts
6329
+ var reportModule = (context, node) => {
6330
+ const report3 = {
6331
+ node,
6332
+ messageId: "unusedModule"
6333
+ };
6334
+ context.report(report3);
6335
+ };
6336
+ var reportNames = ({
6337
+ context,
6338
+ edges,
6339
+ node,
6340
+ publicNames,
6341
+ used
6342
+ }) => {
6343
+ const nodes = exportNodes(node);
6344
+ const uniqueExports = new Set(edges.exports);
6345
+ for (const name of uniqueExports) {
6346
+ const count = used?.get(name) ?? 0;
6347
+ const isPublic = publicNames?.has(name) ?? false;
6348
+ if (count > 0 || isPublic) continue;
6349
+ const data = { name };
6350
+ const target = nodes.get(name) ?? node;
6351
+ const report3 = {
6352
+ node: target,
6353
+ messageId: "unusedExport",
6354
+ data
6355
+ };
6356
+ context.report(report3);
6357
+ }
4276
6358
  };
4277
- var createRule4 = ESLintUtils4.RuleCreator(
4278
- () => "https://github.com/F0rty-Tw0/lint-suite#no-inline-object-types"
6359
+ var reportUnusedExports = ({
6360
+ context,
6361
+ isEntry,
6362
+ node,
6363
+ services
6364
+ }) => {
6365
+ const sourceFile = services.esTreeNodeToTSNodeMap.get(node);
6366
+ const aggregate = exportIndex(services.program);
6367
+ const edges = aggregate.byFile.get(sourceFile.fileName);
6368
+ if (!edges) return;
6369
+ if (edges.skipped) return;
6370
+ const hasStarExports = edges.starTargets.length > 0;
6371
+ const hasExports = edges.exports.length > 0 || hasStarExports;
6372
+ if (!hasExports) return;
6373
+ const isImported = aggregate.imported.has(edges.fileName);
6374
+ if (!isImported) {
6375
+ reportModule(context, node);
6376
+ return;
6377
+ }
6378
+ const used = aggregate.usage.get(edges.fileName);
6379
+ const publicIndex = publicExportIndex(aggregate, isEntry);
6380
+ const publicNames = publicIndex.get(edges.fileName);
6381
+ const nameOptions = {
6382
+ context,
6383
+ edges,
6384
+ node,
6385
+ publicNames,
6386
+ used
6387
+ };
6388
+ reportNames(nameOptions);
6389
+ };
6390
+
6391
+ // packages/lint-suite/src/lib/rules/no-unused-exports/no-unused-exports.ts
6392
+ var createRule14 = ESLintUtils14.RuleCreator(
6393
+ () => "https://github.com/F0rty-Tw0/lint-suite#no-unused-exports"
4279
6394
  );
4280
- var enclosingTypeAlias = (node) => {
4281
- let current = node.parent;
4282
- while (current && current.type !== TSESTree11.AST_NODE_TYPES.Program) {
4283
- if (current.type === TSESTree11.AST_NODE_TYPES.TSTypeAliasDeclaration) {
4284
- return current;
4285
- }
4286
- current = current.parent;
6395
+ var typedParserServices = (context) => {
6396
+ try {
6397
+ const services = ESLintUtils14.getParserServices(context, true);
6398
+ if (!services.program) return void 0;
6399
+ return services;
6400
+ } catch {
6401
+ return void 0;
4287
6402
  }
4288
- return void 0;
4289
6403
  };
4290
- var no_inline_object_types_default = createRule4({
4291
- name: "no-inline-object-types",
4292
- meta: meta4,
4293
- defaultOptions: [],
4294
- create(context) {
6404
+ var ENTRY_POINT_MATCHERS = /* @__PURE__ */ new WeakMap();
6405
+ var entryPointMatcher = (entryPoints) => {
6406
+ const cached = ENTRY_POINT_MATCHERS.get(entryPoints);
6407
+ if (cached) return cached;
6408
+ const matcher = compileGlobs(entryPoints);
6409
+ ENTRY_POINT_MATCHERS.set(entryPoints, matcher);
6410
+ return matcher;
6411
+ };
6412
+ var no_unused_exports_default = createRule14({
6413
+ name: "no-unused-exports",
6414
+ meta: meta14,
6415
+ defaultOptions: defaultOptions7,
6416
+ create(context, [{ entryPoints }]) {
6417
+ const noListeners = {};
6418
+ const isEntry = entryPointMatcher(entryPoints);
6419
+ const isEntryFile = isEntry(context.filename);
6420
+ if (isEntryFile) return noListeners;
6421
+ const services = typedParserServices(context);
6422
+ if (!services) return noListeners;
4295
6423
  const listeners = {
4296
- TSTypeLiteral(node) {
4297
- const isAliasBody = node.parent.type === TSESTree11.AST_NODE_TYPES.TSTypeAliasDeclaration;
4298
- if (isAliasBody) return;
4299
- const enclosingAlias = enclosingTypeAlias(node);
4300
- if (!enclosingAlias) return;
4301
- const report = {
4302
- node,
4303
- messageId: "inlineObjectType"
4304
- };
4305
- context.report(report);
6424
+ Program(node) {
6425
+ reportUnusedExports({ context, isEntry, node, services });
4306
6426
  }
4307
6427
  };
4308
6428
  return listeners;
@@ -4310,40 +6430,45 @@ var no_inline_object_types_default = createRule4({
4310
6430
  });
4311
6431
 
4312
6432
  // packages/lint-suite/src/lib/rules/one-line-guard/one-line-guard.ts
4313
- import { ESLintUtils as ESLintUtils5, TSESTree as TSESTree12 } from "@typescript-eslint/utils";
4314
- var docs5 = {
6433
+ import { ESLintUtils as ESLintUtils15, TSESTree as TSESTree26 } from "@typescript-eslint/utils";
6434
+ var docs15 = {
4315
6435
  description: "Require a lone return/throw/continue/break guard to drop its braces when the whole if statement fits on one line"
4316
6436
  };
4317
- var messages5 = {
6437
+ var messages15 = {
4318
6438
  oneLineGuard: "Lone {{ keyword }} guard fits within {{ max }} columns; write it on one line without braces."
4319
6439
  };
4320
- var schema4 = [
6440
+ var maxLineLengthSchema = {
6441
+ type: "integer",
6442
+ minimum: 1
6443
+ };
6444
+ var properties8 = {
6445
+ maxLineLength: maxLineLengthSchema
6446
+ };
6447
+ var schema9 = [
4321
6448
  {
4322
6449
  type: "object",
4323
- properties: {
4324
- maxLineLength: { type: "integer", minimum: 1 }
4325
- },
6450
+ properties: properties8,
4326
6451
  additionalProperties: false
4327
6452
  }
4328
6453
  ];
4329
- var meta5 = {
6454
+ var meta15 = {
4330
6455
  type: "suggestion",
4331
- docs: docs5,
6456
+ docs: docs15,
4332
6457
  fixable: "code",
4333
- schema: schema4,
4334
- messages: messages5
6458
+ schema: schema9,
6459
+ messages: messages15
4335
6460
  };
4336
- var createRule5 = ESLintUtils5.RuleCreator(
6461
+ var createRule15 = ESLintUtils15.RuleCreator(
4337
6462
  () => "https://github.com/F0rty-Tw0/lint-suite#one-line-guard"
4338
6463
  );
4339
6464
  var GUARD_KEYWORDS = /* @__PURE__ */ new Set([
4340
- TSESTree12.AST_NODE_TYPES.ReturnStatement,
4341
- TSESTree12.AST_NODE_TYPES.ThrowStatement,
4342
- TSESTree12.AST_NODE_TYPES.ContinueStatement,
4343
- TSESTree12.AST_NODE_TYPES.BreakStatement
6465
+ TSESTree26.AST_NODE_TYPES.ReturnStatement,
6466
+ TSESTree26.AST_NODE_TYPES.ThrowStatement,
6467
+ TSESTree26.AST_NODE_TYPES.ContinueStatement,
6468
+ TSESTree26.AST_NODE_TYPES.BreakStatement
4344
6469
  ]);
4345
6470
  var soleStatement = (body) => {
4346
- if (body.type !== TSESTree12.AST_NODE_TYPES.BlockStatement) return void 0;
6471
+ if (body.type !== TSESTree26.AST_NODE_TYPES.BlockStatement) return void 0;
4347
6472
  if (body.body.length !== 1) return void 0;
4348
6473
  return body.body[0];
4349
6474
  };
@@ -4356,17 +6481,21 @@ var guardStatement = (node) => {
4356
6481
  return statement;
4357
6482
  };
4358
6483
  var headText = (node, sourceCode) => {
4359
- return sourceCode.text.slice(node.range[0], node.consequent.range[0]).trimEnd();
6484
+ const start = node.range[0];
6485
+ const end = node.consequent.range[0];
6486
+ const rawHead = sourceCode.text.slice(start, end);
6487
+ return rawHead.trimEnd();
4360
6488
  };
4361
6489
  var collapsedLine = (node, head, bodyText, sourceCode) => {
4362
6490
  const line = sourceCode.lines[node.loc.start.line - 1] ?? "";
4363
6491
  const prefix = line.slice(0, node.loc.start.column);
4364
6492
  return `${prefix}${head} ${bodyText}`;
4365
6493
  };
4366
- var one_line_guard_default = createRule5({
6494
+ var defaultOptions8 = [{ maxLineLength: 80 }];
6495
+ var one_line_guard_default = createRule15({
4367
6496
  name: "one-line-guard",
4368
- meta: meta5,
4369
- defaultOptions: [{ maxLineLength: 80 }],
6497
+ meta: meta15,
6498
+ defaultOptions: defaultOptions8,
4370
6499
  create(context, [{ maxLineLength }]) {
4371
6500
  const { sourceCode } = context;
4372
6501
  const listeners = {
@@ -4388,13 +6517,13 @@ var one_line_guard_default = createRule5({
4388
6517
  const keyword = keywordToken.value;
4389
6518
  const data = { keyword, max: maxLineLength };
4390
6519
  const fix = (fixer) => fixer.replaceText(node.consequent, bodyText);
4391
- const report = {
6520
+ const report3 = {
4392
6521
  node,
4393
6522
  messageId: "oneLineGuard",
4394
6523
  data,
4395
6524
  fix
4396
6525
  };
4397
- context.report(report);
6526
+ context.report(report3);
4398
6527
  }
4399
6528
  };
4400
6529
  return listeners;
@@ -4402,45 +6531,46 @@ var one_line_guard_default = createRule5({
4402
6531
  });
4403
6532
 
4404
6533
  // packages/lint-suite/src/lib/rules/readonly-type-properties/readonly-type-properties.ts
4405
- import { ESLintUtils as ESLintUtils6, TSESTree as TSESTree13 } from "@typescript-eslint/utils";
4406
- var docs6 = {
4407
- description: "Require readonly on primitive-typed type and interface properties"
6534
+ import { ESLintUtils as ESLintUtils16, TSESTree as TSESTree27 } from "@typescript-eslint/utils";
6535
+ var docs16 = {
6536
+ description: "Require readonly on primitive-typed type and interface properties, and require array types to be written as T[] instead of readonly T[] or ReadonlyArray<T>"
4408
6537
  };
4409
- var messages6 = {
4410
- missingReadonly: "Type property '{{ name }}' should be readonly. Disable this rule on the line if mutation is required."
6538
+ var messages16 = {
6539
+ missingReadonly: "Type property '{{ name }}' should be readonly. Disable this rule on the line if mutation is required.",
6540
+ readonlyArray: "Write a readonly array type as T[]."
4411
6541
  };
4412
- var meta6 = {
6542
+ var meta16 = {
4413
6543
  type: "suggestion",
4414
- docs: docs6,
6544
+ docs: docs16,
4415
6545
  fixable: "code",
4416
6546
  schema: [],
4417
- messages: messages6
6547
+ messages: messages16
4418
6548
  };
4419
- var createRule6 = ESLintUtils6.RuleCreator(
6549
+ var createRule16 = ESLintUtils16.RuleCreator(
4420
6550
  () => "https://github.com/F0rty-Tw0/lint-suite#readonly-type-properties"
4421
6551
  );
4422
6552
  var propertyName3 = (node, sourceCode) => {
4423
- if (node.key.type === TSESTree13.AST_NODE_TYPES.Identifier) {
6553
+ if (node.key.type === TSESTree27.AST_NODE_TYPES.Identifier) {
4424
6554
  return node.key.name;
4425
6555
  }
4426
6556
  return sourceCode.getText(node.key);
4427
6557
  };
4428
6558
  var PRIMITIVE_KEYWORDS = /* @__PURE__ */ new Set([
4429
- TSESTree13.AST_NODE_TYPES.TSStringKeyword,
4430
- TSESTree13.AST_NODE_TYPES.TSNumberKeyword,
4431
- TSESTree13.AST_NODE_TYPES.TSBooleanKeyword,
4432
- TSESTree13.AST_NODE_TYPES.TSBigIntKeyword,
4433
- TSESTree13.AST_NODE_TYPES.TSSymbolKeyword,
4434
- TSESTree13.AST_NODE_TYPES.TSNullKeyword,
4435
- TSESTree13.AST_NODE_TYPES.TSUndefinedKeyword
6559
+ TSESTree27.AST_NODE_TYPES.TSStringKeyword,
6560
+ TSESTree27.AST_NODE_TYPES.TSNumberKeyword,
6561
+ TSESTree27.AST_NODE_TYPES.TSBooleanKeyword,
6562
+ TSESTree27.AST_NODE_TYPES.TSBigIntKeyword,
6563
+ TSESTree27.AST_NODE_TYPES.TSSymbolKeyword,
6564
+ TSESTree27.AST_NODE_TYPES.TSNullKeyword,
6565
+ TSESTree27.AST_NODE_TYPES.TSUndefinedKeyword
4436
6566
  ]);
4437
6567
  var isPrimitive = (type) => {
4438
6568
  const isPrimitiveKeyword = PRIMITIVE_KEYWORDS.has(type.type);
4439
6569
  if (isPrimitiveKeyword) return true;
4440
- if (type.type === TSESTree13.AST_NODE_TYPES.TSLiteralType || type.type === TSESTree13.AST_NODE_TYPES.TSTemplateLiteralType) {
6570
+ if (type.type === TSESTree27.AST_NODE_TYPES.TSLiteralType || type.type === TSESTree27.AST_NODE_TYPES.TSTemplateLiteralType) {
4441
6571
  return true;
4442
6572
  }
4443
- if (type.type === TSESTree13.AST_NODE_TYPES.TSUnionType || type.type === TSESTree13.AST_NODE_TYPES.TSIntersectionType) {
6573
+ if (type.type === TSESTree27.AST_NODE_TYPES.TSUnionType || type.type === TSESTree27.AST_NODE_TYPES.TSIntersectionType) {
4444
6574
  return type.types.every(isPrimitive);
4445
6575
  }
4446
6576
  return false;
@@ -4452,9 +6582,29 @@ var readonlyFix = (node, sourceCode) => {
4452
6582
  return fixer.insertTextBefore(firstToken, "readonly ");
4453
6583
  };
4454
6584
  };
4455
- var readonly_type_properties_default = createRule6({
6585
+ var NEEDS_PARENS = /* @__PURE__ */ new Set([
6586
+ TSESTree27.AST_NODE_TYPES.TSUnionType,
6587
+ TSESTree27.AST_NODE_TYPES.TSIntersectionType,
6588
+ TSESTree27.AST_NODE_TYPES.TSFunctionType,
6589
+ TSESTree27.AST_NODE_TYPES.TSConditionalType
6590
+ ]);
6591
+ var arrayTypeText = (argument, sourceCode) => {
6592
+ const argumentText = sourceCode.getText(argument);
6593
+ const needsParens = NEEDS_PARENS.has(argument.type);
6594
+ if (needsParens) return `(${argumentText})[]`;
6595
+ return `${argumentText}[]`;
6596
+ };
6597
+ var readonlyArrayReport = (node, fix) => {
6598
+ const report3 = {
6599
+ node,
6600
+ messageId: "readonlyArray",
6601
+ fix
6602
+ };
6603
+ return report3;
6604
+ };
6605
+ var readonly_type_properties_default = createRule16({
4456
6606
  name: "readonly-type-properties",
4457
- meta: meta6,
6607
+ meta: meta16,
4458
6608
  defaultOptions: [],
4459
6609
  create(context) {
4460
6610
  const { sourceCode } = context;
@@ -4466,13 +6616,629 @@ var readonly_type_properties_default = createRule6({
4466
6616
  const name = propertyName3(node, sourceCode);
4467
6617
  const data = { name };
4468
6618
  const fix = readonlyFix(node, sourceCode);
4469
- const report = {
6619
+ const report3 = {
4470
6620
  node: node.key,
4471
6621
  messageId: "missingReadonly",
4472
6622
  data,
4473
6623
  fix
4474
6624
  };
4475
- context.report(report);
6625
+ context.report(report3);
6626
+ },
6627
+ TSTypeOperator(node) {
6628
+ if (node.operator !== "readonly" || !node.typeAnnotation) return;
6629
+ const isArray = node.typeAnnotation.type === TSESTree27.AST_NODE_TYPES.TSArrayType;
6630
+ if (!isArray) return;
6631
+ const replacement = sourceCode.getText(node.typeAnnotation);
6632
+ const fix = (fixer) => fixer.replaceText(node, replacement);
6633
+ context.report(readonlyArrayReport(node, fix));
6634
+ },
6635
+ TSTypeReference(node) {
6636
+ if (node.typeName.type !== TSESTree27.AST_NODE_TYPES.Identifier) return;
6637
+ if (node.typeName.name !== "ReadonlyArray") return;
6638
+ const params = node.typeArguments?.params ?? [];
6639
+ if (params.length !== 1) return;
6640
+ const [argument] = params;
6641
+ if (!argument) return;
6642
+ const replacement = arrayTypeText(argument, sourceCode);
6643
+ const fix = (fixer) => fixer.replaceText(node, replacement);
6644
+ context.report(readonlyArrayReport(node, fix));
6645
+ }
6646
+ };
6647
+ return listeners;
6648
+ }
6649
+ });
6650
+
6651
+ // packages/lint-suite/src/lib/rules/ternary-branch-shape/ternary-branch-shape.ts
6652
+ import { ESLintUtils as ESLintUtils17, TSESTree as TSESTree28 } from "@typescript-eslint/utils";
6653
+ var docs17 = {
6654
+ description: "Require ternary branches to hold only names, literals, or plain member access"
6655
+ };
6656
+ var messages17 = {
6657
+ ternaryBranch: "Ternary {{ branch }} must be a name, literal, or plain member access; name the expression first."
6658
+ };
6659
+ var meta17 = {
6660
+ type: "suggestion",
6661
+ docs: docs17,
6662
+ schema: [],
6663
+ messages: messages17
6664
+ };
6665
+ var createRule17 = ESLintUtils17.RuleCreator(
6666
+ () => "https://github.com/F0rty-Tw0/lint-suite#ternary-branch-shape"
6667
+ );
6668
+ var isPlainMemberAccess = (node) => {
6669
+ let current = node;
6670
+ while (current.type === TSESTree28.AST_NODE_TYPES.MemberExpression) {
6671
+ current = current.object;
6672
+ }
6673
+ return current.type !== TSESTree28.AST_NODE_TYPES.CallExpression;
6674
+ };
6675
+ var isAllowedBranch = (node) => {
6676
+ if (node.type === TSESTree28.AST_NODE_TYPES.Identifier) return true;
6677
+ if (node.type === TSESTree28.AST_NODE_TYPES.Literal) return true;
6678
+ if (node.type === TSESTree28.AST_NODE_TYPES.TemplateLiteral) return true;
6679
+ if (node.type === TSESTree28.AST_NODE_TYPES.UnaryExpression) {
6680
+ return node.argument.type === TSESTree28.AST_NODE_TYPES.Literal;
6681
+ }
6682
+ if (node.type === TSESTree28.AST_NODE_TYPES.MemberExpression) {
6683
+ return isPlainMemberAccess(node);
6684
+ }
6685
+ return false;
6686
+ };
6687
+ var ternary_branch_shape_default = createRule17({
6688
+ name: "ternary-branch-shape",
6689
+ meta: meta17,
6690
+ defaultOptions: [],
6691
+ create(context) {
6692
+ const check = (node, branch) => {
6693
+ const isAllowed = isAllowedBranch(node);
6694
+ if (isAllowed) return;
6695
+ const data = { branch };
6696
+ const report3 = {
6697
+ node,
6698
+ messageId: "ternaryBranch",
6699
+ data
6700
+ };
6701
+ context.report(report3);
6702
+ };
6703
+ const listeners = {
6704
+ ConditionalExpression(node) {
6705
+ check(node.consequent, "consequent");
6706
+ check(node.alternate, "alternate");
6707
+ }
6708
+ };
6709
+ return listeners;
6710
+ }
6711
+ });
6712
+
6713
+ // packages/lint-suite/src/lib/rules/test-file-shape/test-file-shape.ts
6714
+ import { ESLintUtils as ESLintUtils18 } from "@typescript-eslint/utils";
6715
+
6716
+ // packages/lint-suite/src/lib/rules/test-file-shape/common/test-file-shape.const.ts
6717
+ var docs18 = {
6718
+ description: "Restrict test-support file shapes and enforce naming and typing on stub and mock exports"
6719
+ };
6720
+ var messages18 = {
6721
+ forbiddenTestFile: "Test-support file shape '{{ shape }}' is not allowed; test-only files live under test/stubs, test/mocks, test/fixtures, or test/utils.",
6722
+ stubName: "Stub export '{{ name }}' must be named in SCREAMING_SNAKE_CASE ending in '_STUB'.",
6723
+ stubType: "Stub export '{{ name }}' is missing an explicit type annotation.",
6724
+ mockName: "Mock export '{{ name }}' must be a camelCase name ending in 'Mock'.",
6725
+ mockFactory: "Mock export '{{ name }}' must be a function that returns fresh spies.",
6726
+ mockReturnType: "Mock export '{{ name }}' is missing an explicit return type."
6727
+ };
6728
+ var meta18 = {
6729
+ type: "problem",
6730
+ docs: docs18,
6731
+ schema: [],
6732
+ messages: messages18
6733
+ };
6734
+ var defaultOptions9 = [];
6735
+ var FORBIDDEN_SHAPES = [
6736
+ { pattern: /\.spec-support\.ts$/, shape: ".spec-support.ts" },
6737
+ { pattern: /\.spec-helper\.ts$/, shape: ".spec-helper.ts" },
6738
+ { pattern: /\.test-utils\.ts$/, shape: ".test-utils.ts" },
6739
+ { pattern: /-fixture\.ts$/, shape: "-fixture.ts" },
6740
+ { pattern: /\/__mocks__\//, shape: "__mocks__/" },
6741
+ { pattern: /\/helpers\//, shape: "helpers/" },
6742
+ { pattern: /\/common\/stubs\//, shape: "common/stubs/" }
6743
+ ];
6744
+ var STUB_FILE_PATTERN = /\/test\/stubs\/.+\.stub\.ts$/;
6745
+ var MOCK_FILE_PATTERN = /\/test\/mocks\/.+\.mock\.ts$/;
6746
+ var SPEC_UTIL_FILE_PATTERN = /\/test\/utils\/.+\.spec\.util\.ts$/;
6747
+ var TESTING_LIB_SEGMENT = "/testing/";
6748
+ var TEST_FIXTURES_SEGMENT = "/test/fixtures/";
6749
+ var FIXTURES_SEGMENT = /\/fixtures\//;
6750
+ var STUB_NAME_PATTERN = /^[A-Z0-9]+(_[A-Z0-9]+)*_STUB$/;
6751
+ var MOCK_NAME_PATTERN = /^[a-z][A-Za-z0-9]*Mock$/;
6752
+
6753
+ // packages/lint-suite/src/lib/rules/test-file-shape/mock-exports.ts
6754
+ import { TSESTree as TSESTree29 } from "@typescript-eslint/utils";
6755
+ var isMockFile = (filename) => {
6756
+ const isUnderTestMocks = MOCK_FILE_PATTERN.test(filename);
6757
+ if (isUnderTestMocks) return true;
6758
+ const isUnderTestingLib2 = filename.includes(TESTING_LIB_SEGMENT);
6759
+ return isUnderTestingLib2 && filename.endsWith(".mock.ts");
6760
+ };
6761
+ var isMockIdentifier = (id) => id.type === TSESTree29.AST_NODE_TYPES.Identifier;
6762
+ var isMockFunction = (init) => {
6763
+ if (init === null) return false;
6764
+ const { type } = init;
6765
+ if (type === TSESTree29.AST_NODE_TYPES.ArrowFunctionExpression) return true;
6766
+ return type === TSESTree29.AST_NODE_TYPES.FunctionExpression;
6767
+ };
6768
+ var isConstVariableDeclaration = (declaration) => {
6769
+ if (declaration.type !== TSESTree29.AST_NODE_TYPES.VariableDeclaration) {
6770
+ return false;
6771
+ }
6772
+ return declaration.kind === "const";
6773
+ };
6774
+ var declarationName = (context, declaration) => {
6775
+ if (declaration.type === TSESTree29.AST_NODE_TYPES.VariableDeclaration) {
6776
+ const [firstDeclarator] = declaration.declarations;
6777
+ return context.sourceCode.getText(firstDeclarator.id);
6778
+ }
6779
+ if ("id" in declaration && declaration.id !== null) {
6780
+ return context.sourceCode.getText(declaration.id);
6781
+ }
6782
+ return context.sourceCode.getText(declaration);
6783
+ };
6784
+ var reportMock = (context, node, messageId, name) => {
6785
+ const data = { name };
6786
+ const descriptor = {
6787
+ node,
6788
+ messageId,
6789
+ data
6790
+ };
6791
+ context.report(descriptor);
6792
+ };
6793
+ var checkMockDeclarator = (context, node) => {
6794
+ const fallbackName = context.sourceCode.getText(node.id);
6795
+ const name = isMockIdentifier(node.id) ? node.id.name : fallbackName;
6796
+ const hasValidPattern = MOCK_NAME_PATTERN.test(name);
6797
+ const hasValidName = isMockIdentifier(node.id) && hasValidPattern;
6798
+ if (!hasValidName) {
6799
+ reportMock(context, node.id, "mockName", name);
6800
+ }
6801
+ if (!isMockFunction(node.init)) {
6802
+ reportMock(context, node.id, "mockFactory", name);
6803
+ return;
6804
+ }
6805
+ const hasReturnType = node.init.returnType !== void 0;
6806
+ if (hasReturnType) return;
6807
+ reportMock(context, node.id, "mockReturnType", name);
6808
+ };
6809
+ var mockExportListeners = (context) => {
6810
+ const listeners = {
6811
+ ExportNamedDeclaration(node) {
6812
+ if (node.declaration === null) return;
6813
+ const { declaration } = node;
6814
+ const isConstVar = isConstVariableDeclaration(declaration);
6815
+ if (!isConstVar) {
6816
+ const name = declarationName(context, declaration);
6817
+ reportMock(context, node, "mockFactory", name);
6818
+ return;
6819
+ }
6820
+ for (const declarator of declaration.declarations) {
6821
+ checkMockDeclarator(context, declarator);
6822
+ }
6823
+ }
6824
+ };
6825
+ return listeners;
6826
+ };
6827
+
6828
+ // packages/lint-suite/src/lib/rules/test-file-shape/stub-exports.ts
6829
+ import { TSESTree as TSESTree30 } from "@typescript-eslint/utils";
6830
+ var isStubFile = (filename) => {
6831
+ const isUnderTestStubs = STUB_FILE_PATTERN.test(filename);
6832
+ if (isUnderTestStubs) return true;
6833
+ const isUnderTestingLib2 = filename.includes(TESTING_LIB_SEGMENT);
6834
+ return isUnderTestingLib2 && filename.endsWith(".stub.ts");
6835
+ };
6836
+ var isStubIdentifier = (id) => id.type === TSESTree30.AST_NODE_TYPES.Identifier;
6837
+ var checkStubDeclarator = (context, node) => {
6838
+ const fallbackName = context.sourceCode.getText(node.id);
6839
+ const name = isStubIdentifier(node.id) ? node.id.name : fallbackName;
6840
+ const hasValidPattern = STUB_NAME_PATTERN.test(name);
6841
+ const hasValidName = isStubIdentifier(node.id) && hasValidPattern;
6842
+ if (!hasValidName) {
6843
+ const nameData = { name };
6844
+ const nameReport = {
6845
+ node: node.id,
6846
+ messageId: "stubName",
6847
+ data: nameData
6848
+ };
6849
+ context.report(nameReport);
6850
+ }
6851
+ const hasTypeAnnotation = node.id.typeAnnotation !== void 0;
6852
+ if (hasTypeAnnotation) return;
6853
+ const typeData = { name };
6854
+ const typeReport = {
6855
+ node: node.id,
6856
+ messageId: "stubType",
6857
+ data: typeData
6858
+ };
6859
+ context.report(typeReport);
6860
+ };
6861
+ var stubExportListeners = (context) => {
6862
+ const listeners = {
6863
+ "ExportNamedDeclaration > VariableDeclaration > VariableDeclarator"(node) {
6864
+ checkStubDeclarator(context, node);
6865
+ }
6866
+ };
6867
+ return listeners;
6868
+ };
6869
+
6870
+ // packages/lint-suite/src/lib/rules/test-file-shape/test-file-shape.ts
6871
+ var createRule18 = ESLintUtils18.RuleCreator(
6872
+ () => "https://github.com/F0rty-Tw0/lint-suite#test-file-shape"
6873
+ );
6874
+ var isUnderTestingLib = (filename) => filename.includes(TESTING_LIB_SEGMENT);
6875
+ var misplacedFixtures = (filename) => {
6876
+ const hasFixturesSegment = FIXTURES_SEGMENT.test(filename);
6877
+ if (!hasFixturesSegment) return void 0;
6878
+ const isTestFixtures = filename.includes(TEST_FIXTURES_SEGMENT);
6879
+ if (isTestFixtures) return void 0;
6880
+ return "fixtures/ outside test/fixtures/";
6881
+ };
6882
+ var misplacedShape = (filename) => {
6883
+ const isTestingLib = isUnderTestingLib(filename);
6884
+ if (isTestingLib) return void 0;
6885
+ const isStub = filename.endsWith(".stub.ts");
6886
+ const isPlacedStub = STUB_FILE_PATTERN.test(filename);
6887
+ if (isStub && !isPlacedStub) return ".stub.ts outside test/stubs/";
6888
+ const isMock = filename.endsWith(".mock.ts");
6889
+ const isPlacedMock = MOCK_FILE_PATTERN.test(filename);
6890
+ if (isMock && !isPlacedMock) return ".mock.ts outside test/mocks/";
6891
+ const isSpecUtil = filename.endsWith(".spec.util.ts");
6892
+ const isPlacedSpecUtil = SPEC_UTIL_FILE_PATTERN.test(filename);
6893
+ if (isSpecUtil && !isPlacedSpecUtil) {
6894
+ return ".spec.util.ts outside test/utils/";
6895
+ }
6896
+ return misplacedFixtures(filename);
6897
+ };
6898
+ var forbiddenShape = (filename) => {
6899
+ const match = FORBIDDEN_SHAPES.find((entry) => entry.pattern.test(filename));
6900
+ if (match) return match.shape;
6901
+ return misplacedShape(filename);
6902
+ };
6903
+ var forbiddenTestFileListeners = (context, shape) => {
6904
+ const listeners = {
6905
+ Program(node) {
6906
+ const data = { shape };
6907
+ const report3 = {
6908
+ node,
6909
+ messageId: "forbiddenTestFile",
6910
+ data
6911
+ };
6912
+ context.report(report3);
6913
+ }
6914
+ };
6915
+ return listeners;
6916
+ };
6917
+ var test_file_shape_default = createRule18({
6918
+ name: "test-file-shape",
6919
+ meta: meta18,
6920
+ defaultOptions: defaultOptions9,
6921
+ create(context) {
6922
+ const filename = context.filename.replaceAll("\\", "/");
6923
+ const shape = forbiddenShape(filename);
6924
+ if (shape) return forbiddenTestFileListeners(context, shape);
6925
+ const isStub = isStubFile(filename);
6926
+ if (isStub) return stubExportListeners(context);
6927
+ const isMock = isMockFile(filename);
6928
+ if (isMock) return mockExportListeners(context);
6929
+ const noListeners = {};
6930
+ return noListeners;
6931
+ }
6932
+ });
6933
+
6934
+ // packages/lint-suite/src/lib/rules/type-placement/type-placement.ts
6935
+ import { ESLintUtils as ESLintUtils19, TSESTree as TSESTree31 } from "@typescript-eslint/utils";
6936
+
6937
+ // packages/lint-suite/src/lib/rules/type-placement/common/type-placement.const.ts
6938
+ var DEFAULT_INTERNAL_PATTERNS = [];
6939
+ var docs19 = {
6940
+ description: "Require types to live in common/*.type.ts files, *.const.ts files to export only consts, and type-only imports to come from *.type.ts files or a common barrel"
6941
+ };
6942
+ var messages19 = {
6943
+ typeOutsideTypeFile: "Exported type '{{ name }}' belongs in a common/*.type.ts or test/common/*.type.ts file, not here.",
6944
+ valueInTypeFile: "A *.type.ts file may only export type aliases and interfaces.",
6945
+ nonConstInConstFile: "A *.const.ts file may only export const variable declarations.",
6946
+ typeImportNotFromTypeFile: "Type-only import from '{{ source }}' must come from a *.type.ts file or a common barrel."
6947
+ };
6948
+ var stringItems2 = { type: "string" };
6949
+ var internalPatternsSchema = {
6950
+ type: "array",
6951
+ items: stringItems2,
6952
+ description: "Regular expressions matching internal, non-relative import sources that must point at a *.type.ts file. Empty by default: a workspace alias resolves to a library entry point, never to a type file."
6953
+ };
6954
+ var properties9 = {
6955
+ internalPatterns: internalPatternsSchema
6956
+ };
6957
+ var optionsSchema8 = {
6958
+ type: "object",
6959
+ properties: properties9,
6960
+ additionalProperties: false
6961
+ };
6962
+ var schema10 = [optionsSchema8];
6963
+ var meta19 = {
6964
+ type: "problem",
6965
+ docs: docs19,
6966
+ schema: schema10,
6967
+ messages: messages19
6968
+ };
6969
+ var defaultOptions10 = [
6970
+ { internalPatterns: DEFAULT_INTERNAL_PATTERNS }
6971
+ ];
6972
+
6973
+ // packages/lint-suite/src/lib/rules/type-placement/type-placement.ts
6974
+ var TYPE_FILE_SUFFIX = ".type.ts";
6975
+ var CONST_FILE_SUFFIX = ".const.ts";
6976
+ var COMMON_SEGMENT = "/common/";
6977
+ var EXEMPT_FILE = /\.(spec|stub|d)\.ts$/;
6978
+ var STATE_TYPE_FILE = /(^|[./])state\.type\.ts$/;
6979
+ var FIXTURES_SEGMENT2 = "/fixtures/";
6980
+ var TYPE_FILE_SEGMENT = /\.type(\.ts)?$/;
6981
+ var COMMON_BARREL = /(^|\/)common(\/index(\.ts)?)?$/;
6982
+ var createRule19 = ESLintUtils19.RuleCreator(
6983
+ () => "https://github.com/F0rty-Tw0/lint-suite#type-placement"
6984
+ );
6985
+ var isExemptFile = (filename) => {
6986
+ const isExemptSuffix = EXEMPT_FILE.test(filename);
6987
+ if (isExemptSuffix) return true;
6988
+ const isStateTypeFile = STATE_TYPE_FILE.test(filename);
6989
+ if (isStateTypeFile) return true;
6990
+ return filename.includes(FIXTURES_SEGMENT2);
6991
+ };
6992
+ var lastSegment = (source) => {
6993
+ const segments = source.split("/");
6994
+ return segments[segments.length - 1] ?? source;
6995
+ };
6996
+ var isTypeOnlySource = (source) => {
6997
+ const isCommonBarrel = COMMON_BARREL.test(source);
6998
+ if (isCommonBarrel) return true;
6999
+ return TYPE_FILE_SEGMENT.test(lastSegment(source));
7000
+ };
7001
+ var isInternalSource = (source, patterns) => {
7002
+ const isRelative = source.startsWith(".");
7003
+ if (isRelative) return true;
7004
+ return patterns.some((pattern) => pattern.test(source));
7005
+ };
7006
+ var isTypeDeclaration = (declaration) => {
7007
+ const { type } = declaration;
7008
+ if (type === TSESTree31.AST_NODE_TYPES.TSTypeAliasDeclaration) return true;
7009
+ return type === TSESTree31.AST_NODE_TYPES.TSInterfaceDeclaration;
7010
+ };
7011
+ var isConstVariableDeclaration2 = (declaration) => {
7012
+ if (declaration.type !== TSESTree31.AST_NODE_TYPES.VariableDeclaration) {
7013
+ return false;
7014
+ }
7015
+ return declaration.kind === "const";
7016
+ };
7017
+ var report = (context, node, messageId, data) => {
7018
+ const base2 = { node, messageId };
7019
+ if (data === void 0) {
7020
+ context.report(base2);
7021
+ return;
7022
+ }
7023
+ const withData = {
7024
+ node,
7025
+ messageId,
7026
+ data
7027
+ };
7028
+ context.report(withData);
7029
+ };
7030
+ var handleExport = (context, node, shape) => {
7031
+ if (node.declaration === null) return;
7032
+ const { declaration } = node;
7033
+ if (isTypeDeclaration(declaration)) {
7034
+ const isCommonTypeFile = shape.isTypeFile && shape.inCommon;
7035
+ if (!isCommonTypeFile) {
7036
+ const data = { name: declaration.id.name };
7037
+ report(context, node, "typeOutsideTypeFile", data);
7038
+ }
7039
+ } else if (shape.isTypeFile) {
7040
+ report(context, node, "valueInTypeFile", void 0);
7041
+ }
7042
+ const isConstVar = isConstVariableDeclaration2(declaration);
7043
+ if (shape.isConstFile && !isConstVar) {
7044
+ report(context, node, "nonConstInConstFile", void 0);
7045
+ }
7046
+ };
7047
+ var handleTypeImport = (context, node, patterns) => {
7048
+ const { value: source } = node.source;
7049
+ const isInternal = isInternalSource(source, patterns);
7050
+ if (!isInternal) return;
7051
+ const isTypeSource = isTypeOnlySource(source);
7052
+ if (isTypeSource) return;
7053
+ report(context, node, "typeImportNotFromTypeFile", { source });
7054
+ };
7055
+ var type_placement_default = createRule19({
7056
+ name: "type-placement",
7057
+ meta: meta19,
7058
+ defaultOptions: defaultOptions10,
7059
+ create(context, [{ internalPatterns }]) {
7060
+ const filename = context.filename.replaceAll("\\", "/");
7061
+ const isExempt = isExemptFile(filename);
7062
+ if (isExempt) {
7063
+ const noListeners = {};
7064
+ return noListeners;
7065
+ }
7066
+ const isTypeFile = filename.endsWith(TYPE_FILE_SUFFIX);
7067
+ const isConstFile = filename.endsWith(CONST_FILE_SUFFIX);
7068
+ const inCommon = filename.includes(COMMON_SEGMENT);
7069
+ const shape = { isTypeFile, isConstFile, inCommon };
7070
+ const internalPatternRegexes = internalPatterns.map(toRegExp);
7071
+ const listeners = {
7072
+ ExportNamedDeclaration(node) {
7073
+ handleExport(context, node, shape);
7074
+ },
7075
+ 'ImportDeclaration[importKind="type"]'(node) {
7076
+ handleTypeImport(context, node, internalPatternRegexes);
7077
+ }
7078
+ };
7079
+ return listeners;
7080
+ }
7081
+ });
7082
+
7083
+ // packages/lint-suite/src/lib/rules/util-purity/util-purity.ts
7084
+ import { ESLintUtils as ESLintUtils20, TSESTree as TSESTree32 } from "@typescript-eslint/utils";
7085
+
7086
+ // packages/lint-suite/src/lib/rules/util-purity/common/util-purity.const.ts
7087
+ var DEFAULT_BANNED_MODULES = [
7088
+ "node:fs",
7089
+ "fs",
7090
+ "node:fs/promises",
7091
+ "fs/promises",
7092
+ "node:child_process",
7093
+ "child_process",
7094
+ "node:os",
7095
+ "os",
7096
+ "node:process",
7097
+ "process",
7098
+ "node:http",
7099
+ "http",
7100
+ "node:https",
7101
+ "https",
7102
+ "node:net",
7103
+ "net",
7104
+ "node:worker_threads",
7105
+ "worker_threads"
7106
+ ];
7107
+ var docs20 = {
7108
+ description: "Disallow impure, I/O-touching, or nondeterministic code inside *.util.ts files"
7109
+ };
7110
+ var messages20 = {
7111
+ impureImport: "Importing '{{ source }}' makes this util impure; utils must not perform I/O.",
7112
+ moduleLet: "Module-level `let` creates mutable shared state; utils must be pure.",
7113
+ moduleState: "Module-level Map/Set/WeakMap/WeakSet is mutable shared state; utils must be pure.",
7114
+ ambientAccess: "Accessing ambient '{{ name }}' makes this util impure and environment-dependent.",
7115
+ nondeterministic: "'{{ name }}' is nondeterministic; a util must return the same output for the same input.",
7116
+ impureCall: "Calling '{{ name }}' makes this util impure or side-effecting."
7117
+ };
7118
+ var stringItems3 = { type: "string" };
7119
+ var bannedModulesSchema = {
7120
+ type: "array",
7121
+ items: stringItems3,
7122
+ description: "Import sources that are forbidden inside util files."
7123
+ };
7124
+ var properties10 = {
7125
+ bannedModules: bannedModulesSchema
7126
+ };
7127
+ var optionsSchema9 = {
7128
+ type: "object",
7129
+ properties: properties10,
7130
+ additionalProperties: false
7131
+ };
7132
+ var schema11 = [optionsSchema9];
7133
+ var meta20 = {
7134
+ type: "problem",
7135
+ docs: docs20,
7136
+ schema: schema11,
7137
+ messages: messages20
7138
+ };
7139
+ var defaultOptions11 = [
7140
+ { bannedModules: DEFAULT_BANNED_MODULES }
7141
+ ];
7142
+ var MODULE_LET_SELECTOR = 'Program > VariableDeclaration[kind="let"]';
7143
+ var MODULE_STATE_SELECTOR = "Program > VariableDeclaration > VariableDeclarator > NewExpression[callee.name=/^(Map|Set|WeakMap|WeakSet)$/]";
7144
+ var AMBIENT_SELECTOR = "MemberExpression[object.name=/^(process|globalThis|window|document|localStorage|sessionStorage|console)$/]";
7145
+ var NONDETERMINISTIC_SELECTORS = [
7146
+ 'MemberExpression[object.name="Date"][property.name="now"]',
7147
+ 'MemberExpression[object.name="Math"][property.name="random"]',
7148
+ 'MemberExpression[object.name="performance"][property.name="now"]',
7149
+ 'MemberExpression[object.name="crypto"][property.name="randomUUID"]'
7150
+ ];
7151
+ var NONDETERMINISTIC_SELECTOR = NONDETERMINISTIC_SELECTORS.join(", ");
7152
+ var CALL_SELECTOR = "CallExpression[callee.name=/^(setTimeout|setInterval|fetch|inject|require)$/]";
7153
+
7154
+ // packages/lint-suite/src/lib/rules/util-purity/util-purity.ts
7155
+ var UTIL_FILE = /\/utils\/[^/]+\.util\.ts$/;
7156
+ var TEST_SEGMENT = "/test/";
7157
+ var TESTING_SEGMENT = "/testing/";
7158
+ var createRule20 = ESLintUtils20.RuleCreator(
7159
+ () => "https://github.com/F0rty-Tw0/lint-suite#util-purity"
7160
+ );
7161
+ var isUtilFile = (filename) => {
7162
+ const isUtilPath = UTIL_FILE.test(filename);
7163
+ if (!isUtilPath) return false;
7164
+ const isUnderTest = filename.includes(TEST_SEGMENT) || filename.includes(TESTING_SEGMENT);
7165
+ return !isUnderTest;
7166
+ };
7167
+ var identifierName = (node) => {
7168
+ if (node.type !== TSESTree32.AST_NODE_TYPES.Identifier) return void 0;
7169
+ return node.name;
7170
+ };
7171
+ var memberName3 = (node) => {
7172
+ const objectName = identifierName(node.object);
7173
+ if (objectName === void 0) return void 0;
7174
+ const propertyName4 = identifierName(node.property);
7175
+ if (propertyName4 === void 0) return void 0;
7176
+ return `${objectName}.${propertyName4}`;
7177
+ };
7178
+ var report2 = (context, node, messageId, data) => {
7179
+ const base2 = { node, messageId };
7180
+ if (data === void 0) {
7181
+ context.report(base2);
7182
+ return;
7183
+ }
7184
+ const withData = {
7185
+ node,
7186
+ messageId,
7187
+ data
7188
+ };
7189
+ context.report(withData);
7190
+ };
7191
+ var reportAmbient = (context, node) => {
7192
+ const name = identifierName(node.object);
7193
+ if (name === void 0) return;
7194
+ report2(context, node, "ambientAccess", { name });
7195
+ };
7196
+ var reportNondeterministic = (context, node) => {
7197
+ const name = memberName3(node);
7198
+ if (name === void 0) return;
7199
+ report2(context, node, "nondeterministic", { name });
7200
+ };
7201
+ var reportCall = (context, node) => {
7202
+ const name = identifierName(node.callee);
7203
+ if (name === void 0) return;
7204
+ report2(context, node, "impureCall", { name });
7205
+ };
7206
+ var handleImport = (context, node, banned) => {
7207
+ const { source } = node;
7208
+ const isBanned = banned.has(source.value);
7209
+ if (!isBanned) return;
7210
+ report2(context, node, "impureImport", { source: source.value });
7211
+ };
7212
+ var util_purity_default = createRule20({
7213
+ name: "util-purity",
7214
+ meta: meta20,
7215
+ defaultOptions: defaultOptions11,
7216
+ create(context, [{ bannedModules }]) {
7217
+ const filename = context.filename.replaceAll("\\", "/");
7218
+ const isUtil = isUtilFile(filename);
7219
+ if (!isUtil) {
7220
+ const noListeners = {};
7221
+ return noListeners;
7222
+ }
7223
+ const banned = new Set(bannedModules);
7224
+ const listeners = {
7225
+ ImportDeclaration(node) {
7226
+ handleImport(context, node, banned);
7227
+ },
7228
+ [MODULE_LET_SELECTOR](node) {
7229
+ report2(context, node, "moduleLet", void 0);
7230
+ },
7231
+ [MODULE_STATE_SELECTOR](node) {
7232
+ report2(context, node, "moduleState", void 0);
7233
+ },
7234
+ [AMBIENT_SELECTOR](node) {
7235
+ reportAmbient(context, node);
7236
+ },
7237
+ [NONDETERMINISTIC_SELECTOR](node) {
7238
+ reportNondeterministic(context, node);
7239
+ },
7240
+ [CALL_SELECTOR](node) {
7241
+ reportCall(context, node);
4476
7242
  }
4477
7243
  };
4478
7244
  return listeners;
@@ -4481,10 +7247,24 @@ var readonly_type_properties_default = createRule6({
4481
7247
 
4482
7248
  // packages/lint-suite/src/lib/typescript-local-plugin.ts
4483
7249
  var localPlugin = definePlugin("local", {
7250
+ "arrow-body-fits-line": arrow_body_fits_line_default,
7251
+ "chain-fits-line": chain_fits_line_default,
7252
+ "chain-receiver-is-name": chain_receiver_is_name_default,
4484
7253
  "explicit-accessibility": explicit_accessibility_default,
4485
- "readonly-type-properties": readonly_type_properties_default,
7254
+ "max-condition-operands": max_condition_operands_default,
7255
+ "no-call-in-condition": no_call_in_condition_default,
7256
+ "no-grouped-condition": no_grouped_condition_default,
4486
7257
  "no-inline-object-types": no_inline_object_types_default,
4487
- "one-line-guard": one_line_guard_default
7258
+ "no-inline-return-object": no_inline_return_object_default,
7259
+ "no-nested-object-value": no_nested_object_value_default,
7260
+ "no-spread-expression": no_spread_expression_default,
7261
+ "no-unused-exports": no_unused_exports_default,
7262
+ "one-line-guard": one_line_guard_default,
7263
+ "readonly-type-properties": readonly_type_properties_default,
7264
+ "ternary-branch-shape": ternary_branch_shape_default,
7265
+ "test-file-shape": test_file_shape_default,
7266
+ "type-placement": type_placement_default,
7267
+ "util-purity": util_purity_default
4488
7268
  });
4489
7269
 
4490
7270
  // packages/lint-suite/src/lib/typescript-safety.ts
@@ -4493,11 +7273,82 @@ var typescriptSafety = defineConfig18([
4493
7273
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
4494
7274
  plugins: { local: localPlugin },
4495
7275
  rules: {
7276
+ // Expression arrow whose body wraps gets a block body. Fixes.
7277
+ // Bad: (x) => a && b on two lines Good: (x) => { return a && b; }
7278
+ "local/arrow-body-fits-line": "error",
7279
+ // Two or more calls in one chain stay on one line.
7280
+ // Bad: a .b() .c() over three lines Good: const b = a.b(); b.c();
7281
+ "local/chain-fits-line": "error",
7282
+ // A member chain starts on a name, never on an inline expression.
7283
+ // Bad: (a ?? b).c Good: const ab = a ?? b; ab.c
7284
+ "local/chain-receiver-is-name": "error",
7285
+ // Every class member states public, private, or protected. Fixes.
7286
+ // Bad: field = 1; Good: private field = 1;
4496
7287
  "local/explicit-accessibility": "error",
4497
- "local/readonly-type-properties": "error",
7288
+ // An if condition holds at most 3 operands joined by && or ||.
7289
+ // Bad: if (a && b && c && d) Good: const isAll = a && b && c && d;
7290
+ "local/max-condition-operands": "error",
7291
+ // No call inside an if; type predicates and this.x() reads are exempt.
7292
+ // Bad: if (validate(o)) Good: const isValid = validate(o); if (isValid)
7293
+ "local/no-call-in-condition": "error",
7294
+ // No parenthesised group with a different operator inside an if.
7295
+ // Bad: if (a && (b || c)) Good: const isBOrC = b || c; if (a && isBOrC)
7296
+ "local/no-grouped-condition": "error",
7297
+ // An object type literal appears only as the body of a type alias.
7298
+ // Bad: (f: { a: string }) Good: type F = { a: string }; (f: F)
4498
7299
  "local/no-inline-object-types": "error",
7300
+ // Never return an object literal inline; name it first.
7301
+ // Bad: return { a: 1 }; Good: const result = { a: 1 }; return result;
7302
+ "local/no-inline-return-object": "error",
7303
+ // A nested object, array of objects, ternary, or chain as a value gets a
7304
+ // name.
7305
+ // Bad: { a: { b: 1 } } Good: const a = { b: 1 }; { a }
7306
+ "local/no-nested-object-value": "error",
7307
+ // Spread only a name or a member access.
7308
+ // Bad: f(...getArgs()) Good: const args = getArgs(); f(...args)
7309
+ "local/no-spread-expression": "error",
7310
+ // An export no file in the program imports is dead; entry points exempt.
7311
+ // Bad: export const unused = 1; Good: delete it, or import it
7312
+ "local/no-unused-exports": "error",
7313
+ // A lone return/throw/continue/break guard drops braces when it fits.
7314
+ // Bad: braced three-line if (x) { return; } Good: if (x) return;
4499
7315
  "local/one-line-guard": ["error", { maxLineLength: MAX_LINE_LENGTH }],
7316
+ // Type properties are readonly; arrays stay T[], never readonly T[].
7317
+ // Fixes.
7318
+ // Bad: { id: string; items: readonly T[] }
7319
+ // Good: { readonly id: string; readonly items: T[] }
7320
+ "local/readonly-type-properties": "error",
7321
+ // A ternary branch is a name, literal, or plain member access.
7322
+ // Bad: c ? foo() : b Good: const called = foo(); c ? called : b
7323
+ "local/ternary-branch-shape": "error",
7324
+ // No spec-support, helpers, or __mocks__ files; stubs, mocks, and
7325
+ // spec utils live under test/; stubs are UPPER_SNAKE_STUB: Type.
7326
+ // Bad: user.spec-helper.ts, common/stubs/user.stub.ts
7327
+ // Good: test/utils/user.spec.util.ts, test/stubs USER_STUB: User
7328
+ "local/test-file-shape": "error",
7329
+ // Exported types live in common/*.type.ts; type-only imports come from
7330
+ // there.
7331
+ // Bad: export type Order in order.ts
7332
+ // Good: export type Order in common/order.type.ts
7333
+ "local/type-placement": "error",
7334
+ // utils/*.util.ts hold pure code: no fs, process, Date.now, module-level
7335
+ // let or Map. Files under test/ or testing/ are exempt.
7336
+ // Bad: const cache = new Map(); at module level
7337
+ // Good: pass the state in as an argument
7338
+ "local/util-purity": "error",
4500
7339
  "@typescript-eslint/explicit-function-return-type": "error",
7340
+ "@typescript-eslint/parameter-properties": "error",
7341
+ "no-restricted-syntax": [
7342
+ "error",
7343
+ {
7344
+ selector: "TSEnumDeclaration[declare!=true]",
7345
+ message: "enum is not erasable syntax; use a union of literals or an as-const object."
7346
+ },
7347
+ {
7348
+ selector: 'TSModuleDeclaration[kind="namespace"][declare!=true]',
7349
+ message: "namespace is not erasable syntax; use a module."
7350
+ }
7351
+ ],
4501
7352
  "@typescript-eslint/no-empty-function": "error",
4502
7353
  "@typescript-eslint/no-unused-vars": "error",
4503
7354
  "@typescript-eslint/no-use-before-define": "error",
@@ -4560,6 +7411,13 @@ var typescriptSafety = defineConfig18([
4560
7411
  "@typescript-eslint/prefer-string-starts-ends-with": "error",
4561
7412
  "@typescript-eslint/prefer-regexp-exec": "error"
4562
7413
  }
7414
+ },
7415
+ {
7416
+ files: ["**/*.action.ts"],
7417
+ rules: {
7418
+ // Class actions carry their payload as constructor parameter properties.
7419
+ "@typescript-eslint/parameter-properties": "off"
7420
+ }
4563
7421
  }
4564
7422
  ]);
4565
7423
 
@@ -4576,7 +7434,8 @@ var typescript = defineConfig19([
4576
7434
  import vitestEslint from "@vitest/eslint-plugin";
4577
7435
  import { defineConfig as defineConfig20 } from "eslint/config";
4578
7436
  var vitestPlugin = {
4579
- vitest: vitestEslint
7437
+ vitest: vitestEslint,
7438
+ local: localPlugin
4580
7439
  };
4581
7440
  var vitest = defineConfig20([
4582
7441
  {