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/stylelint.cjs CHANGED
@@ -40,14 +40,204 @@ var import_stylelint_selector_bem_pattern = __toESM(require("stylelint-selector-
40
40
  var import_stylelint = __toESM(require("stylelint"), 1);
41
41
 
42
42
  // packages/lint-suite/src/lib/rules/no-unused-classes/styles/stylesheet-components.ts
43
+ var import_node_fs3 = require("node:fs");
44
+ var import_node_path3 = require("node:path");
45
+
46
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/linked-templates.ts
43
47
  var import_node_fs2 = require("node:fs");
44
- var import_node_path = require("node:path");
48
+ var import_node_path2 = require("node:path");
45
49
 
46
- // packages/lint-suite/src/lib/rules/no-unused-classes/styles/component-descriptors.ts
50
+ // packages/lint-suite/src/lib/rules/file-cache.ts
47
51
  var import_node_fs = require("node:fs");
52
+ var import_node_path = require("node:path");
53
+ var CACHE_FORMAT_VERSION = 1;
54
+ var caches = /* @__PURE__ */ new Set();
55
+ var isFlushRegistered = false;
56
+ var cacheDirectory = () => {
57
+ const { LINT_SUITE_CACHE, LINT_SUITE_CACHE_DIR } = process.env;
58
+ const isDisabled = LINT_SUITE_CACHE === "0";
59
+ if (isDisabled) return null;
60
+ if (LINT_SUITE_CACHE_DIR) return LINT_SUITE_CACHE_DIR;
61
+ return (0, import_node_path.join)(process.cwd(), "node_modules", ".cache", "lint-suite");
62
+ };
63
+ var cacheFile = (name) => {
64
+ const directory = cacheDirectory();
65
+ if (directory === null) return null;
66
+ return (0, import_node_path.join)(directory, `${name}.v${CACHE_FORMAT_VERSION}.json`);
67
+ };
68
+ var isCachedFile = (value) => {
69
+ const isObject = typeof value === "object" && value !== null;
70
+ if (!isObject) return false;
71
+ const hasVersion = "version" in value && typeof value.version === "string";
72
+ return hasVersion && "value" in value;
73
+ };
74
+ var storedEntries = (text) => {
75
+ const entries = /* @__PURE__ */ new Map();
76
+ const parsed = JSON.parse(text);
77
+ const isRecord = typeof parsed === "object" && parsed !== null;
78
+ if (!isRecord) return entries;
79
+ for (const [path, entry] of Object.entries(parsed)) {
80
+ if (isCachedFile(entry)) entries.set(path, entry);
81
+ }
82
+ return entries;
83
+ };
84
+ var load = (cache2) => {
85
+ cache2.loaded = true;
86
+ const file = cacheFile(cache2.name);
87
+ if (file === null) return;
88
+ try {
89
+ const stored = storedEntries((0, import_node_fs.readFileSync)(file, "utf8"));
90
+ for (const [path, entry] of stored) cache2.entries.set(path, entry);
91
+ } catch {
92
+ return;
93
+ }
94
+ };
95
+ var write = (cache2) => {
96
+ const file = cacheFile(cache2.name);
97
+ if (file === null) return;
98
+ try {
99
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(file), { recursive: true });
100
+ const temporary = `${file}.${process.pid}.tmp`;
101
+ const stored = Object.fromEntries(cache2.entries);
102
+ (0, import_node_fs.writeFileSync)(temporary, JSON.stringify(stored));
103
+ (0, import_node_fs.renameSync)(temporary, file);
104
+ } catch {
105
+ return;
106
+ }
107
+ };
108
+ var flushFileCaches = () => {
109
+ for (const cache2 of caches) {
110
+ if (!cache2.dirty) continue;
111
+ write(cache2);
112
+ cache2.dirty = false;
113
+ }
114
+ };
115
+ var registerFlush = () => {
116
+ if (isFlushRegistered) return;
117
+ isFlushRegistered = true;
118
+ process.once("exit", flushFileCaches);
119
+ };
120
+ var createFileCache = (name) => {
121
+ const cache2 = {
122
+ name,
123
+ entries: /* @__PURE__ */ new Map(),
124
+ loaded: false,
125
+ dirty: false
126
+ };
127
+ caches.add(cache2);
128
+ registerFlush();
129
+ return cache2;
130
+ };
131
+ var readCached = (cache2, path, parse) => {
132
+ if (!cache2.loaded) load(cache2);
133
+ try {
134
+ const stats = (0, import_node_fs.statSync)(path, { bigint: true });
135
+ const version = `${stats.mtimeNs}:${stats.size}`;
136
+ const cached = cache2.entries.get(path);
137
+ if (cached?.version === version) return cached.value;
138
+ const value = parse((0, import_node_fs.readFileSync)(path, "utf8"), path);
139
+ const fresh = { value, version };
140
+ cache2.entries.set(path, fresh);
141
+ cache2.dirty = true;
142
+ return value;
143
+ } catch {
144
+ return null;
145
+ }
146
+ };
147
+
148
+ // packages/lint-suite/src/lib/rules/utils/template-links.util.ts
149
+ var LINK_TAG = /<link\b[^>]*>/giu;
150
+ var STYLESHEET_REL = /\brel\s*=\s*["']stylesheet["']/iu;
151
+ var HREF = /\bhref\s*=\s*["']([^"']*)["']/iu;
152
+ var UNRESOLVABLE = /^(?:\/|[^:/?#]+:)/u;
153
+ var hrefOf = (tag) => {
154
+ const isStylesheet = STYLESHEET_REL.test(tag);
155
+ if (!isStylesheet) return null;
156
+ const href = HREF.exec(tag)?.[1] ?? "";
157
+ const isUnresolvable = UNRESOLVABLE.test(href);
158
+ const isLocal = href.length > 0 && !isUnresolvable;
159
+ return isLocal ? href : null;
160
+ };
161
+ var linkedStylesheetHrefs = (template) => {
162
+ const tags = template.match(LINK_TAG) ?? [];
163
+ return tags.map(hrefOf).filter((href) => href !== null);
164
+ };
165
+
166
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/linked-templates.ts
167
+ var TEMPLATE_EXTENSION = /\.html$/iu;
168
+ var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
169
+ var links = createFileCache("template-links");
170
+ var linkIndex = null;
171
+ var templateLinks = (templatePath) => {
172
+ return readCached(links, templatePath, linkedStylesheetHrefs) ?? [];
173
+ };
174
+ var isWalkable = (name) => {
175
+ const isDotfile = name.startsWith(".");
176
+ if (isDotfile) return false;
177
+ return !SKIPPED_DIRECTORIES.has(name);
178
+ };
179
+ var walkTemplates = (root) => {
180
+ const found = [];
181
+ const pending = [root];
182
+ while (pending.length > 0) {
183
+ const directory = pending.pop();
184
+ if (directory === void 0) break;
185
+ for (const entry of (0, import_node_fs2.readdirSync)(directory, { withFileTypes: true })) {
186
+ const path = (0, import_node_path2.join)(directory, entry.name);
187
+ const isDirectory = entry.isDirectory();
188
+ if (isDirectory) {
189
+ const canWalk = isWalkable(entry.name);
190
+ if (canWalk) pending.push(path);
191
+ continue;
192
+ }
193
+ const isTemplate = TEMPLATE_EXTENSION.test(entry.name);
194
+ if (isTemplate) found.push(path);
195
+ }
196
+ }
197
+ return found;
198
+ };
199
+ var templatesUnder = (root) => {
200
+ try {
201
+ return walkTemplates(root);
202
+ } catch {
203
+ return [];
204
+ }
205
+ };
206
+ var templatesByStylesheet = (root) => {
207
+ const byStylesheet = /* @__PURE__ */ new Map();
208
+ for (const template of templatesUnder(root)) {
209
+ const directory = (0, import_node_path2.dirname)(template);
210
+ for (const href of templateLinks(template)) {
211
+ const stylesheet = (0, import_node_path2.resolve)(directory, href);
212
+ const linked = byStylesheet.get(stylesheet) ?? [];
213
+ linked.push(template);
214
+ byStylesheet.set(stylesheet, linked);
215
+ }
216
+ }
217
+ return byStylesheet;
218
+ };
219
+ var currentLinkIndex = () => {
220
+ const now = performance.now();
221
+ const cached = linkIndex;
222
+ const isFresh = cached !== null && now - cached.builtAt < cached.buildDuration * 100;
223
+ if (isFresh) return cached;
224
+ const templates2 = templatesByStylesheet(process.cwd());
225
+ const fresh = {
226
+ builtAt: now,
227
+ buildDuration: performance.now() - now,
228
+ templates: templates2
229
+ };
230
+ linkIndex = fresh;
231
+ return fresh;
232
+ };
233
+ var linkedTemplates = (stylesheetPath) => {
234
+ return currentLinkIndex().templates.get(stylesheetPath) ?? [];
235
+ };
48
236
 
49
237
  // packages/lint-suite/src/lib/rules/component-metadata.ts
50
238
  var import_typescript = require("typescript");
239
+ var COMPONENT_MARKER = "@Component";
240
+ var cache = createFileCache("component-metadata");
51
241
  var componentArgument = (decorator) => {
52
242
  const { expression } = decorator;
53
243
  if (!(0, import_typescript.isCallExpression)(expression)) return null;
@@ -68,17 +258,6 @@ var decoratedMetadata = (node) => {
68
258
  }
69
259
  return null;
70
260
  };
71
- var parseComponentMetadata = (text, path) => {
72
- const found = [];
73
- const visit = (node) => {
74
- const metadata = decoratedMetadata(node);
75
- if (metadata !== null) found.push(metadata);
76
- (0, import_typescript.forEachChild)(node, visit);
77
- };
78
- const source = (0, import_typescript.createSourceFile)(path, text, import_typescript.ScriptTarget.Latest, true);
79
- (0, import_typescript.forEachChild)(source, visit);
80
- return found;
81
- };
82
261
  var literalText = (node) => {
83
262
  if (!(0, import_typescript.isStringLiteralLike)(node)) return null;
84
263
  return node.text;
@@ -108,56 +287,61 @@ var metadataTexts = (metadata, name) => {
108
287
  if (value === null) return [];
109
288
  return literalTexts(value);
110
289
  };
111
-
112
- // packages/lint-suite/src/lib/rules/no-unused-classes/styles/component-descriptors.ts
113
- var COMPONENT_MARKER = "@Component";
114
- var cache = /* @__PURE__ */ new Map();
115
290
  var toDescriptor = (metadata) => {
116
291
  const [templateUrl] = metadataTexts(metadata, "templateUrl");
117
292
  const [template] = metadataTexts(metadata, "template");
118
- const styleUrls = [
119
- ...metadataTexts(metadata, "styleUrl"),
120
- ...metadataTexts(metadata, "styleUrls")
121
- ];
293
+ const styleUrl = metadataTexts(metadata, "styleUrl");
294
+ const listedStyleUrls = metadataTexts(metadata, "styleUrls");
295
+ const styleUrls = [...styleUrl, ...listedStyleUrls];
296
+ const styles = metadataTexts(metadata, "styles");
122
297
  const descriptor = {
123
298
  templateUrl: templateUrl ?? null,
124
299
  template: template ?? null,
125
- styleUrls
300
+ styleUrls,
301
+ styles
126
302
  };
127
303
  return descriptor;
128
304
  };
129
- var parseDescriptors = (text, path) => {
305
+ var parseComponentMetadata = (text, path) => {
306
+ const found = [];
130
307
  const hasComponent = text.includes(COMPONENT_MARKER);
131
- if (!hasComponent) return [];
132
- return parseComponentMetadata(text, path).map(toDescriptor);
308
+ if (!hasComponent) return found;
309
+ const visit = (node) => {
310
+ const metadata = decoratedMetadata(node);
311
+ if (metadata !== null) found.push(toDescriptor(metadata));
312
+ (0, import_typescript.forEachChild)(node, visit);
313
+ };
314
+ const source = (0, import_typescript.createSourceFile)(path, text, import_typescript.ScriptTarget.Latest, true);
315
+ (0, import_typescript.forEachChild)(source, visit);
316
+ return found;
133
317
  };
134
- var componentDescriptors = (path) => {
135
- try {
136
- const stats = (0, import_node_fs.statSync)(path, { bigint: true });
137
- const version = `${stats.mtimeNs}:${stats.size}`;
138
- const cached = cache.get(path);
139
- if (cached?.version === version) return cached.descriptors;
140
- const descriptors = parseDescriptors((0, import_node_fs.readFileSync)(path, "utf8"), path);
141
- const fresh = { descriptors, version };
142
- cache.set(path, fresh);
143
- return descriptors;
144
- } catch {
145
- return [];
146
- }
318
+ var componentMetadata = (path) => {
319
+ return readCached(cache, path, parseComponentMetadata) ?? [];
147
320
  };
148
321
 
149
322
  // packages/lint-suite/src/lib/rules/no-unused-classes/styles/stylesheet-components.ts
150
323
  var MODULE_EXTENSION = /\.ts$/iu;
151
324
  var STYLE_EXTENSION = /\.(?:scss|sass|css)$/iu;
152
325
  var UNKNOWN_TEMPLATE = { kind: "unknown" };
326
+ var listings = /* @__PURE__ */ new Map();
327
+ var directoryListing = (directory) => {
328
+ const stats = (0, import_node_fs3.statSync)(directory, { bigint: true });
329
+ const version = `${stats.mtimeNs}`;
330
+ const cached = listings.get(directory);
331
+ if (cached?.version === version) return cached.files;
332
+ const files = (0, import_node_fs3.readdirSync)(directory);
333
+ const fresh = { files, version };
334
+ listings.set(directory, fresh);
335
+ return files;
336
+ };
153
337
  var declaredStylesheets = (descriptor, directory) => {
154
- const resolveUrl = (url) => (0, import_node_path.resolve)(directory, url);
338
+ const resolveUrl = (url) => (0, import_node_path3.resolve)(directory, url);
155
339
  return descriptor.styleUrls.map(resolveUrl);
156
340
  };
157
341
  var fileTemplate = (descriptor, directory) => {
158
342
  if (descriptor.templateUrl === null) return null;
159
- const path = (0, import_node_path.resolve)(directory, descriptor.templateUrl);
160
- const isPresent = (0, import_node_fs2.existsSync)(path);
343
+ const path = (0, import_node_path3.resolve)(directory, descriptor.templateUrl);
344
+ const isPresent = (0, import_node_fs3.existsSync)(path);
161
345
  if (!isPresent) return null;
162
346
  const source = { kind: "file", path };
163
347
  return source;
@@ -172,16 +356,16 @@ var inlineTemplate = (descriptor, componentPath) => {
172
356
  return source;
173
357
  };
174
358
  var componentTemplate = (descriptor, componentPath) => {
175
- const directory = (0, import_node_path.dirname)(componentPath);
359
+ const directory = (0, import_node_path3.dirname)(componentPath);
176
360
  const file = fileTemplate(descriptor, directory);
177
361
  if (file !== null) return file;
178
362
  const inline = inlineTemplate(descriptor, componentPath);
179
363
  return inline ?? UNKNOWN_TEMPLATE;
180
364
  };
181
365
  var matchedTemplates = (componentPath, stylesheetPath) => {
182
- const directory = (0, import_node_path.dirname)(componentPath);
366
+ const directory = (0, import_node_path3.dirname)(componentPath);
183
367
  const sources = [];
184
- for (const descriptor of componentDescriptors(componentPath)) {
368
+ for (const descriptor of componentMetadata(componentPath)) {
185
369
  const declared = declaredStylesheets(descriptor, directory);
186
370
  const isMatch = declared.includes(stylesheetPath);
187
371
  if (!isMatch) continue;
@@ -189,40 +373,43 @@ var matchedTemplates = (componentPath, stylesheetPath) => {
189
373
  }
190
374
  return sources;
191
375
  };
192
- var directoryModules = (directory) => {
376
+ var directoryFiles = (directory, extension) => {
193
377
  try {
194
- return (0, import_node_fs2.readdirSync)(directory).filter((file) => MODULE_EXTENSION.test(file));
378
+ return directoryListing(directory).filter((file) => extension.test(file));
195
379
  } catch {
196
380
  return [];
197
381
  }
198
382
  };
199
- var siblingTemplates = (stylesheetPath) => {
200
- const path = stylesheetPath.replace(STYLE_EXTENSION, ".html");
201
- const isPresent = (0, import_node_fs2.existsSync)(path);
202
- if (!isPresent) return [];
203
- const source = { kind: "file", path };
204
- return [source];
383
+ var fallbackTemplates = (stylesheetPath) => {
384
+ const sibling = stylesheetPath.replace(STYLE_EXTENSION, ".html");
385
+ const paths = /* @__PURE__ */ new Set();
386
+ const hasSibling = (0, import_node_fs3.existsSync)(sibling);
387
+ if (hasSibling) paths.add(sibling);
388
+ for (const path of linkedTemplates(stylesheetPath)) paths.add(path);
389
+ const toSource = (path) => {
390
+ const source = { kind: "file", path };
391
+ return source;
392
+ };
393
+ const pathList = [...paths];
394
+ return pathList.map(toSource);
205
395
  };
206
396
  var stylesheetTemplates = (stylesheetPath) => {
207
- const resolved = (0, import_node_path.resolve)(stylesheetPath);
208
- const directory = (0, import_node_path.dirname)(resolved);
397
+ const resolved = (0, import_node_path3.resolve)(stylesheetPath);
398
+ const directory = (0, import_node_path3.dirname)(resolved);
209
399
  const sources = [];
210
- for (const file of directoryModules(directory)) {
211
- const componentPath = (0, import_node_path.join)(directory, file);
212
- sources.push(...matchedTemplates(componentPath, resolved));
400
+ for (const file of directoryFiles(directory, MODULE_EXTENSION)) {
401
+ const componentPath = (0, import_node_path3.join)(directory, file);
402
+ const matched = matchedTemplates(componentPath, resolved);
403
+ sources.push(...matched);
213
404
  }
214
405
  if (sources.length > 0) return sources;
215
- return siblingTemplates(resolved);
406
+ return fallbackTemplates(resolved);
216
407
  };
217
408
 
218
409
  // packages/lint-suite/src/lib/rules/no-unused-classes/template/template-usage.ts
219
410
  var import_compiler3 = require("@angular/compiler");
220
- var import_node_fs3 = require("node:fs");
221
411
 
222
- // packages/lint-suite/src/lib/rules/utils/class-expression-literals.util.ts
223
- var import_compiler = require("@angular/compiler");
224
-
225
- // packages/lint-suite/src/lib/rules/utils/selector-classes.util.ts
412
+ // packages/lint-suite/src/lib/rules/selector-classes.ts
226
413
  var import_postcss_selector_parser = __toESM(require("postcss-selector-parser"), 1);
227
414
  var INTERPOLATION_TOKEN = "zzinterpzz";
228
415
  var PARENT_TOKEN = "zzparentzz";
@@ -302,7 +489,8 @@ var selectorTemplateClasses = (selector) => {
302
489
  } catch {
303
490
  return [];
304
491
  }
305
- return [...new Set(names)];
492
+ const uniqueNames = new Set(names);
493
+ return [...uniqueNames];
306
494
  };
307
495
  var classMatcher = (exact, patterns) => {
308
496
  const matches = (name) => {
@@ -314,6 +502,7 @@ var classMatcher = (exact, patterns) => {
314
502
  };
315
503
 
316
504
  // packages/lint-suite/src/lib/rules/utils/class-expression-literals.util.ts
505
+ var import_compiler = require("@angular/compiler");
317
506
  var WHITESPACE = /\s+/u;
318
507
  var EXPRESSION_FILE = "class-expression";
319
508
  var INTERPOLATION_TOKEN2 = "zzinterpzz";
@@ -383,7 +572,8 @@ var classExpressionLiterals = (source) => {
383
572
  if (ast === null) return [];
384
573
  const names = [];
385
574
  collect(ast, names);
386
- return [...new Set(names)];
575
+ const uniqueNames = new Set(names);
576
+ return [...uniqueNames];
387
577
  };
388
578
  var isSpreadKey = (key) => key.kind === "spread";
389
579
  var isDynamicAst = (ast) => {
@@ -408,9 +598,11 @@ var interpolationUsage = (ast) => {
408
598
  const named = tokens.filter(isNamedToken);
409
599
  const patterns = named.filter(isPatternToken).map(classNamePattern);
410
600
  const names = named.filter(isPlainToken);
601
+ const uniqueNames = new Set(names);
602
+ const uniquePatterns = new Set(patterns);
411
603
  const usage = {
412
- names: [...new Set(names)],
413
- patterns: [...new Set(patterns)],
604
+ names: [...uniqueNames],
605
+ patterns: [...uniquePatterns],
414
606
  isDynamic: named.length < tokens.length
415
607
  };
416
608
  return usage;
@@ -427,8 +619,9 @@ var classExpressionUsage = (source) => {
427
619
  const names = [];
428
620
  collect(ast, names);
429
621
  const isDynamic = isDynamicAst(ast);
622
+ const uniqueNames = new Set(names);
430
623
  const usage = {
431
- names: [...new Set(names)],
624
+ names: [...uniqueNames],
432
625
  patterns: [],
433
626
  isDynamic
434
627
  };
@@ -496,14 +689,21 @@ var templateClasses = (node) => {
496
689
  const classes = [];
497
690
  for (const attribute of node.attributes) {
498
691
  const isClassName = attribute.name === CLASS_ATTRIBUTE;
499
- if (isClassName) classes.push(...staticClasses(attribute));
692
+ if (isClassName) {
693
+ const found = staticClasses(attribute);
694
+ classes.push(...found);
695
+ }
500
696
  }
501
697
  for (const input of node.inputs) {
502
- classes.push(...inputClasses(input));
698
+ const found = inputClasses(input);
699
+ classes.push(...found);
503
700
  }
504
701
  return classes;
505
702
  };
506
703
 
704
+ // packages/lint-suite/src/lib/rules/utils/to-regexp.util.ts
705
+ var toRegExp = (pattern) => new RegExp(pattern, "u");
706
+
507
707
  // packages/lint-suite/src/lib/rules/no-unused-classes/template/template-usage.ts
508
708
  var CLASS_ATTRIBUTE2 = "class";
509
709
  var NG_CLASS_ATTRIBUTE = "ngClass";
@@ -513,7 +713,7 @@ var UNKNOWN_USAGE2 = {
513
713
  isDynamic: true,
514
714
  size: 0
515
715
  };
516
- var toRegExp = (pattern) => new RegExp(pattern, "u");
716
+ var templates = createFileCache("template-usage");
517
717
  var expressionSource2 = (value) => {
518
718
  const isWithSource = value instanceof import_compiler3.ASTWithSource;
519
719
  if (!isWithSource) return null;
@@ -562,33 +762,35 @@ var ClassUsageVisitor = class extends import_compiler3.TmplAstRecursiveVisitor {
562
762
  if (usage.isDynamic) this.isDynamic = true;
563
763
  }
564
764
  };
565
- var templateText = (source) => {
765
+ var parseUsage = (text, path) => {
766
+ const parsed = (0, import_compiler3.parseTemplate)(text, path);
767
+ if (parsed.errors !== null) return null;
768
+ const visitor = new ClassUsageVisitor();
769
+ (0, import_compiler3.tmplAstVisitAll)(visitor, parsed.nodes);
770
+ const names = [...visitor.names];
771
+ const patterns = [...visitor.patterns];
772
+ const entry = { names, patterns, isDynamic: visitor.isDynamic };
773
+ return entry;
774
+ };
775
+ var sourceUsage = (source) => {
566
776
  if (source.kind === "unknown") return null;
567
- if (source.kind === "inline") {
568
- const inline = { text: source.source, path: source.path };
569
- return inline;
570
- }
571
- try {
572
- const text = (0, import_node_fs3.readFileSync)(source.path, "utf8");
573
- const file = { text, path: source.path };
574
- return file;
575
- } catch {
576
- return null;
577
- }
777
+ if (source.kind === "inline") return parseUsage(source.source, source.path);
778
+ return readCached(templates, source.path, parseUsage) ?? null;
578
779
  };
579
780
  var templateUsage = (sources) => {
580
- const visitor = new ClassUsageVisitor();
781
+ const names = /* @__PURE__ */ new Set();
782
+ const patterns = /* @__PURE__ */ new Set();
581
783
  for (const source of sources) {
582
- const template = templateText(source);
583
- if (template === null) return UNKNOWN_USAGE2;
584
- const parsed = (0, import_compiler3.parseTemplate)(template.text, template.path);
585
- if (parsed.errors !== null) return UNKNOWN_USAGE2;
586
- (0, import_compiler3.tmplAstVisitAll)(visitor, parsed.nodes);
587
- }
588
- if (visitor.isDynamic) return UNKNOWN_USAGE2;
589
- const patterns = [...visitor.patterns].map(toRegExp);
590
- const has = classMatcher(visitor.names, patterns);
591
- const size = visitor.names.size;
784
+ const entry = sourceUsage(source);
785
+ if (entry === null) return UNKNOWN_USAGE2;
786
+ if (entry.isDynamic) return UNKNOWN_USAGE2;
787
+ for (const name of entry.names) names.add(name);
788
+ for (const pattern of entry.patterns) patterns.add(pattern);
789
+ }
790
+ const patternList = [...patterns];
791
+ const compiled = patternList.map(toRegExp);
792
+ const has = classMatcher(names, compiled);
793
+ const size = names.size;
592
794
  const usage = { has, isDynamic: false, size };
593
795
  return usage;
594
796
  };
@@ -622,7 +824,6 @@ var meta = {
622
824
  url: "https://github.com/F0rty-Tw0/lint-suite#no-unused-classes"
623
825
  };
624
826
  var isString = (value) => typeof value === "string";
625
- var toRegExp2 = (pattern) => new RegExp(pattern, "u");
626
827
  var validate = (result, primary, secondary) => {
627
828
  const primaryOptions = { actual: primary, possible: [true] };
628
829
  const possible = { ignoreClassPatterns: [isString] };
@@ -681,12 +882,12 @@ var noUnusedClasses = (primary, secondary) => {
681
882
  if (!isValid) return;
682
883
  const file = root.source?.input.file;
683
884
  if (file === void 0) return;
684
- const templates = stylesheetTemplates(file);
685
- if (templates.length === 0) return;
686
- const usage = templateUsage(templates);
885
+ const templates2 = stylesheetTemplates(file);
886
+ if (templates2.length === 0) return;
887
+ const usage = templateUsage(templates2);
687
888
  if (usage.isDynamic) return;
688
889
  const patterns = secondary?.ignoreClassPatterns ?? DEFAULT_IGNORE_PATTERNS;
689
- const ignored = patterns.map(toRegExp2);
890
+ const ignored = patterns.map(toRegExp);
690
891
  const extended = extendedClasses(root);
691
892
  const context = {
692
893
  result,
@@ -731,6 +932,9 @@ var stylelint2 = {
731
932
  ignoreCustomProperties: ["^--mdc", "^--sys"]
732
933
  },
733
934
  "no-descending-specificity": null,
935
+ // A stylesheet class no linked template uses is reported.
936
+ // Bad: .ghost {} used by no template
937
+ // Good: delete it, or use it in a template
734
938
  "lint-suite/no-unused-classes": true
735
939
  }
736
940
  }