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.js CHANGED
@@ -6,11 +6,205 @@ import bemPattern from "stylelint-selector-bem-pattern";
6
6
  import stylelint from "stylelint";
7
7
 
8
8
  // packages/lint-suite/src/lib/rules/no-unused-classes/styles/stylesheet-components.ts
9
- import { existsSync, readdirSync } from "node:fs";
10
- import { dirname, join, resolve } from "node:path";
9
+ import { existsSync, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
10
+ import { dirname as dirname3, join as join3, resolve as resolve2 } from "node:path";
11
11
 
12
- // packages/lint-suite/src/lib/rules/no-unused-classes/styles/component-descriptors.ts
13
- import { readFileSync, statSync } from "node:fs";
12
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/linked-templates.ts
13
+ import { readdirSync } from "node:fs";
14
+ import { dirname as dirname2, join as join2, resolve } from "node:path";
15
+
16
+ // packages/lint-suite/src/lib/rules/file-cache.ts
17
+ import {
18
+ mkdirSync,
19
+ readFileSync,
20
+ renameSync,
21
+ statSync,
22
+ writeFileSync
23
+ } from "node:fs";
24
+ import { dirname, join } from "node:path";
25
+ var CACHE_FORMAT_VERSION = 1;
26
+ var caches = /* @__PURE__ */ new Set();
27
+ var isFlushRegistered = false;
28
+ var cacheDirectory = () => {
29
+ const { LINT_SUITE_CACHE, LINT_SUITE_CACHE_DIR } = process.env;
30
+ const isDisabled = LINT_SUITE_CACHE === "0";
31
+ if (isDisabled) return null;
32
+ if (LINT_SUITE_CACHE_DIR) return LINT_SUITE_CACHE_DIR;
33
+ return join(process.cwd(), "node_modules", ".cache", "lint-suite");
34
+ };
35
+ var cacheFile = (name) => {
36
+ const directory = cacheDirectory();
37
+ if (directory === null) return null;
38
+ return join(directory, `${name}.v${CACHE_FORMAT_VERSION}.json`);
39
+ };
40
+ var isCachedFile = (value) => {
41
+ const isObject = typeof value === "object" && value !== null;
42
+ if (!isObject) return false;
43
+ const hasVersion = "version" in value && typeof value.version === "string";
44
+ return hasVersion && "value" in value;
45
+ };
46
+ var storedEntries = (text) => {
47
+ const entries = /* @__PURE__ */ new Map();
48
+ const parsed = JSON.parse(text);
49
+ const isRecord = typeof parsed === "object" && parsed !== null;
50
+ if (!isRecord) return entries;
51
+ for (const [path, entry] of Object.entries(parsed)) {
52
+ if (isCachedFile(entry)) entries.set(path, entry);
53
+ }
54
+ return entries;
55
+ };
56
+ var load = (cache2) => {
57
+ cache2.loaded = true;
58
+ const file = cacheFile(cache2.name);
59
+ if (file === null) return;
60
+ try {
61
+ const stored = storedEntries(readFileSync(file, "utf8"));
62
+ for (const [path, entry] of stored) cache2.entries.set(path, entry);
63
+ } catch {
64
+ return;
65
+ }
66
+ };
67
+ var write = (cache2) => {
68
+ const file = cacheFile(cache2.name);
69
+ if (file === null) return;
70
+ try {
71
+ mkdirSync(dirname(file), { recursive: true });
72
+ const temporary = `${file}.${process.pid}.tmp`;
73
+ const stored = Object.fromEntries(cache2.entries);
74
+ writeFileSync(temporary, JSON.stringify(stored));
75
+ renameSync(temporary, file);
76
+ } catch {
77
+ return;
78
+ }
79
+ };
80
+ var flushFileCaches = () => {
81
+ for (const cache2 of caches) {
82
+ if (!cache2.dirty) continue;
83
+ write(cache2);
84
+ cache2.dirty = false;
85
+ }
86
+ };
87
+ var registerFlush = () => {
88
+ if (isFlushRegistered) return;
89
+ isFlushRegistered = true;
90
+ process.once("exit", flushFileCaches);
91
+ };
92
+ var createFileCache = (name) => {
93
+ const cache2 = {
94
+ name,
95
+ entries: /* @__PURE__ */ new Map(),
96
+ loaded: false,
97
+ dirty: false
98
+ };
99
+ caches.add(cache2);
100
+ registerFlush();
101
+ return cache2;
102
+ };
103
+ var readCached = (cache2, path, parse) => {
104
+ if (!cache2.loaded) load(cache2);
105
+ try {
106
+ const stats = statSync(path, { bigint: true });
107
+ const version = `${stats.mtimeNs}:${stats.size}`;
108
+ const cached = cache2.entries.get(path);
109
+ if (cached?.version === version) return cached.value;
110
+ const value = parse(readFileSync(path, "utf8"), path);
111
+ const fresh = { value, version };
112
+ cache2.entries.set(path, fresh);
113
+ cache2.dirty = true;
114
+ return value;
115
+ } catch {
116
+ return null;
117
+ }
118
+ };
119
+
120
+ // packages/lint-suite/src/lib/rules/utils/template-links.util.ts
121
+ var LINK_TAG = /<link\b[^>]*>/giu;
122
+ var STYLESHEET_REL = /\brel\s*=\s*["']stylesheet["']/iu;
123
+ var HREF = /\bhref\s*=\s*["']([^"']*)["']/iu;
124
+ var UNRESOLVABLE = /^(?:\/|[^:/?#]+:)/u;
125
+ var hrefOf = (tag) => {
126
+ const isStylesheet = STYLESHEET_REL.test(tag);
127
+ if (!isStylesheet) return null;
128
+ const href = HREF.exec(tag)?.[1] ?? "";
129
+ const isUnresolvable = UNRESOLVABLE.test(href);
130
+ const isLocal = href.length > 0 && !isUnresolvable;
131
+ return isLocal ? href : null;
132
+ };
133
+ var linkedStylesheetHrefs = (template) => {
134
+ const tags = template.match(LINK_TAG) ?? [];
135
+ return tags.map(hrefOf).filter((href) => href !== null);
136
+ };
137
+
138
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/linked-templates.ts
139
+ var TEMPLATE_EXTENSION = /\.html$/iu;
140
+ var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
141
+ var links = createFileCache("template-links");
142
+ var linkIndex = null;
143
+ var templateLinks = (templatePath) => {
144
+ return readCached(links, templatePath, linkedStylesheetHrefs) ?? [];
145
+ };
146
+ var isWalkable = (name) => {
147
+ const isDotfile = name.startsWith(".");
148
+ if (isDotfile) return false;
149
+ return !SKIPPED_DIRECTORIES.has(name);
150
+ };
151
+ var walkTemplates = (root) => {
152
+ const found = [];
153
+ const pending = [root];
154
+ while (pending.length > 0) {
155
+ const directory = pending.pop();
156
+ if (directory === void 0) break;
157
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
158
+ const path = join2(directory, entry.name);
159
+ const isDirectory = entry.isDirectory();
160
+ if (isDirectory) {
161
+ const canWalk = isWalkable(entry.name);
162
+ if (canWalk) pending.push(path);
163
+ continue;
164
+ }
165
+ const isTemplate = TEMPLATE_EXTENSION.test(entry.name);
166
+ if (isTemplate) found.push(path);
167
+ }
168
+ }
169
+ return found;
170
+ };
171
+ var templatesUnder = (root) => {
172
+ try {
173
+ return walkTemplates(root);
174
+ } catch {
175
+ return [];
176
+ }
177
+ };
178
+ var templatesByStylesheet = (root) => {
179
+ const byStylesheet = /* @__PURE__ */ new Map();
180
+ for (const template of templatesUnder(root)) {
181
+ const directory = dirname2(template);
182
+ for (const href of templateLinks(template)) {
183
+ const stylesheet = resolve(directory, href);
184
+ const linked = byStylesheet.get(stylesheet) ?? [];
185
+ linked.push(template);
186
+ byStylesheet.set(stylesheet, linked);
187
+ }
188
+ }
189
+ return byStylesheet;
190
+ };
191
+ var currentLinkIndex = () => {
192
+ const now = performance.now();
193
+ const cached = linkIndex;
194
+ const isFresh = cached !== null && now - cached.builtAt < cached.buildDuration * 100;
195
+ if (isFresh) return cached;
196
+ const templates2 = templatesByStylesheet(process.cwd());
197
+ const fresh = {
198
+ builtAt: now,
199
+ buildDuration: performance.now() - now,
200
+ templates: templates2
201
+ };
202
+ linkIndex = fresh;
203
+ return fresh;
204
+ };
205
+ var linkedTemplates = (stylesheetPath) => {
206
+ return currentLinkIndex().templates.get(stylesheetPath) ?? [];
207
+ };
14
208
 
15
209
  // packages/lint-suite/src/lib/rules/component-metadata.ts
16
210
  import {
@@ -26,6 +220,8 @@ import {
26
220
  isPropertyAssignment,
27
221
  isStringLiteralLike
28
222
  } from "typescript";
223
+ var COMPONENT_MARKER = "@Component";
224
+ var cache = createFileCache("component-metadata");
29
225
  var componentArgument = (decorator) => {
30
226
  const { expression } = decorator;
31
227
  if (!isCallExpression(expression)) return null;
@@ -46,17 +242,6 @@ var decoratedMetadata = (node) => {
46
242
  }
47
243
  return null;
48
244
  };
49
- var parseComponentMetadata = (text, path) => {
50
- const found = [];
51
- const visit = (node) => {
52
- const metadata = decoratedMetadata(node);
53
- if (metadata !== null) found.push(metadata);
54
- forEachChild(node, visit);
55
- };
56
- const source = createSourceFile(path, text, ScriptTarget.Latest, true);
57
- forEachChild(source, visit);
58
- return found;
59
- };
60
245
  var literalText = (node) => {
61
246
  if (!isStringLiteralLike(node)) return null;
62
247
  return node.text;
@@ -86,55 +271,60 @@ var metadataTexts = (metadata, name) => {
86
271
  if (value === null) return [];
87
272
  return literalTexts(value);
88
273
  };
89
-
90
- // packages/lint-suite/src/lib/rules/no-unused-classes/styles/component-descriptors.ts
91
- var COMPONENT_MARKER = "@Component";
92
- var cache = /* @__PURE__ */ new Map();
93
274
  var toDescriptor = (metadata) => {
94
275
  const [templateUrl] = metadataTexts(metadata, "templateUrl");
95
276
  const [template] = metadataTexts(metadata, "template");
96
- const styleUrls = [
97
- ...metadataTexts(metadata, "styleUrl"),
98
- ...metadataTexts(metadata, "styleUrls")
99
- ];
277
+ const styleUrl = metadataTexts(metadata, "styleUrl");
278
+ const listedStyleUrls = metadataTexts(metadata, "styleUrls");
279
+ const styleUrls = [...styleUrl, ...listedStyleUrls];
280
+ const styles = metadataTexts(metadata, "styles");
100
281
  const descriptor = {
101
282
  templateUrl: templateUrl ?? null,
102
283
  template: template ?? null,
103
- styleUrls
284
+ styleUrls,
285
+ styles
104
286
  };
105
287
  return descriptor;
106
288
  };
107
- var parseDescriptors = (text, path) => {
289
+ var parseComponentMetadata = (text, path) => {
290
+ const found = [];
108
291
  const hasComponent = text.includes(COMPONENT_MARKER);
109
- if (!hasComponent) return [];
110
- return parseComponentMetadata(text, path).map(toDescriptor);
292
+ if (!hasComponent) return found;
293
+ const visit = (node) => {
294
+ const metadata = decoratedMetadata(node);
295
+ if (metadata !== null) found.push(toDescriptor(metadata));
296
+ forEachChild(node, visit);
297
+ };
298
+ const source = createSourceFile(path, text, ScriptTarget.Latest, true);
299
+ forEachChild(source, visit);
300
+ return found;
111
301
  };
112
- var componentDescriptors = (path) => {
113
- try {
114
- const stats = statSync(path, { bigint: true });
115
- const version = `${stats.mtimeNs}:${stats.size}`;
116
- const cached = cache.get(path);
117
- if (cached?.version === version) return cached.descriptors;
118
- const descriptors = parseDescriptors(readFileSync(path, "utf8"), path);
119
- const fresh = { descriptors, version };
120
- cache.set(path, fresh);
121
- return descriptors;
122
- } catch {
123
- return [];
124
- }
302
+ var componentMetadata = (path) => {
303
+ return readCached(cache, path, parseComponentMetadata) ?? [];
125
304
  };
126
305
 
127
306
  // packages/lint-suite/src/lib/rules/no-unused-classes/styles/stylesheet-components.ts
128
307
  var MODULE_EXTENSION = /\.ts$/iu;
129
308
  var STYLE_EXTENSION = /\.(?:scss|sass|css)$/iu;
130
309
  var UNKNOWN_TEMPLATE = { kind: "unknown" };
310
+ var listings = /* @__PURE__ */ new Map();
311
+ var directoryListing = (directory) => {
312
+ const stats = statSync2(directory, { bigint: true });
313
+ const version = `${stats.mtimeNs}`;
314
+ const cached = listings.get(directory);
315
+ if (cached?.version === version) return cached.files;
316
+ const files = readdirSync2(directory);
317
+ const fresh = { files, version };
318
+ listings.set(directory, fresh);
319
+ return files;
320
+ };
131
321
  var declaredStylesheets = (descriptor, directory) => {
132
- const resolveUrl = (url) => resolve(directory, url);
322
+ const resolveUrl = (url) => resolve2(directory, url);
133
323
  return descriptor.styleUrls.map(resolveUrl);
134
324
  };
135
325
  var fileTemplate = (descriptor, directory) => {
136
326
  if (descriptor.templateUrl === null) return null;
137
- const path = resolve(directory, descriptor.templateUrl);
327
+ const path = resolve2(directory, descriptor.templateUrl);
138
328
  const isPresent = existsSync(path);
139
329
  if (!isPresent) return null;
140
330
  const source = { kind: "file", path };
@@ -150,16 +340,16 @@ var inlineTemplate = (descriptor, componentPath) => {
150
340
  return source;
151
341
  };
152
342
  var componentTemplate = (descriptor, componentPath) => {
153
- const directory = dirname(componentPath);
343
+ const directory = dirname3(componentPath);
154
344
  const file = fileTemplate(descriptor, directory);
155
345
  if (file !== null) return file;
156
346
  const inline = inlineTemplate(descriptor, componentPath);
157
347
  return inline ?? UNKNOWN_TEMPLATE;
158
348
  };
159
349
  var matchedTemplates = (componentPath, stylesheetPath) => {
160
- const directory = dirname(componentPath);
350
+ const directory = dirname3(componentPath);
161
351
  const sources = [];
162
- for (const descriptor of componentDescriptors(componentPath)) {
352
+ for (const descriptor of componentMetadata(componentPath)) {
163
353
  const declared = declaredStylesheets(descriptor, directory);
164
354
  const isMatch = declared.includes(stylesheetPath);
165
355
  if (!isMatch) continue;
@@ -167,30 +357,37 @@ var matchedTemplates = (componentPath, stylesheetPath) => {
167
357
  }
168
358
  return sources;
169
359
  };
170
- var directoryModules = (directory) => {
360
+ var directoryFiles = (directory, extension) => {
171
361
  try {
172
- return readdirSync(directory).filter((file) => MODULE_EXTENSION.test(file));
362
+ return directoryListing(directory).filter((file) => extension.test(file));
173
363
  } catch {
174
364
  return [];
175
365
  }
176
366
  };
177
- var siblingTemplates = (stylesheetPath) => {
178
- const path = stylesheetPath.replace(STYLE_EXTENSION, ".html");
179
- const isPresent = existsSync(path);
180
- if (!isPresent) return [];
181
- const source = { kind: "file", path };
182
- return [source];
367
+ var fallbackTemplates = (stylesheetPath) => {
368
+ const sibling = stylesheetPath.replace(STYLE_EXTENSION, ".html");
369
+ const paths = /* @__PURE__ */ new Set();
370
+ const hasSibling = existsSync(sibling);
371
+ if (hasSibling) paths.add(sibling);
372
+ for (const path of linkedTemplates(stylesheetPath)) paths.add(path);
373
+ const toSource = (path) => {
374
+ const source = { kind: "file", path };
375
+ return source;
376
+ };
377
+ const pathList = [...paths];
378
+ return pathList.map(toSource);
183
379
  };
184
380
  var stylesheetTemplates = (stylesheetPath) => {
185
- const resolved = resolve(stylesheetPath);
186
- const directory = dirname(resolved);
381
+ const resolved = resolve2(stylesheetPath);
382
+ const directory = dirname3(resolved);
187
383
  const sources = [];
188
- for (const file of directoryModules(directory)) {
189
- const componentPath = join(directory, file);
190
- sources.push(...matchedTemplates(componentPath, resolved));
384
+ for (const file of directoryFiles(directory, MODULE_EXTENSION)) {
385
+ const componentPath = join3(directory, file);
386
+ const matched = matchedTemplates(componentPath, resolved);
387
+ sources.push(...matched);
191
388
  }
192
389
  if (sources.length > 0) return sources;
193
- return siblingTemplates(resolved);
390
+ return fallbackTemplates(resolved);
194
391
  };
195
392
 
196
393
  // packages/lint-suite/src/lib/rules/no-unused-classes/template/template-usage.ts
@@ -200,23 +397,8 @@ import {
200
397
  parseTemplate,
201
398
  tmplAstVisitAll
202
399
  } from "@angular/compiler";
203
- import { readFileSync as readFileSync2 } from "node:fs";
204
400
 
205
- // packages/lint-suite/src/lib/rules/utils/class-expression-literals.util.ts
206
- import {
207
- Conditional,
208
- Interpolation,
209
- Lexer,
210
- LiteralArray,
211
- LiteralMap,
212
- LiteralPrimitive,
213
- ParseLocation,
214
- ParseSourceFile,
215
- ParseSourceSpan,
216
- Parser
217
- } from "@angular/compiler";
218
-
219
- // packages/lint-suite/src/lib/rules/utils/selector-classes.util.ts
401
+ // packages/lint-suite/src/lib/rules/selector-classes.ts
220
402
  import selectorParser from "postcss-selector-parser";
221
403
  var INTERPOLATION_TOKEN = "zzinterpzz";
222
404
  var PARENT_TOKEN = "zzparentzz";
@@ -296,7 +478,8 @@ var selectorTemplateClasses = (selector) => {
296
478
  } catch {
297
479
  return [];
298
480
  }
299
- return [...new Set(names)];
481
+ const uniqueNames = new Set(names);
482
+ return [...uniqueNames];
300
483
  };
301
484
  var classMatcher = (exact, patterns) => {
302
485
  const matches = (name) => {
@@ -308,6 +491,18 @@ var classMatcher = (exact, patterns) => {
308
491
  };
309
492
 
310
493
  // packages/lint-suite/src/lib/rules/utils/class-expression-literals.util.ts
494
+ import {
495
+ Conditional,
496
+ Interpolation,
497
+ Lexer,
498
+ LiteralArray,
499
+ LiteralMap,
500
+ LiteralPrimitive,
501
+ ParseLocation,
502
+ ParseSourceFile,
503
+ ParseSourceSpan,
504
+ Parser
505
+ } from "@angular/compiler";
311
506
  var WHITESPACE = /\s+/u;
312
507
  var EXPRESSION_FILE = "class-expression";
313
508
  var INTERPOLATION_TOKEN2 = "zzinterpzz";
@@ -377,7 +572,8 @@ var classExpressionLiterals = (source) => {
377
572
  if (ast === null) return [];
378
573
  const names = [];
379
574
  collect(ast, names);
380
- return [...new Set(names)];
575
+ const uniqueNames = new Set(names);
576
+ return [...uniqueNames];
381
577
  };
382
578
  var isSpreadKey = (key) => key.kind === "spread";
383
579
  var isDynamicAst = (ast) => {
@@ -402,9 +598,11 @@ var interpolationUsage = (ast) => {
402
598
  const named = tokens.filter(isNamedToken);
403
599
  const patterns = named.filter(isPatternToken).map(classNamePattern);
404
600
  const names = named.filter(isPlainToken);
601
+ const uniqueNames = new Set(names);
602
+ const uniquePatterns = new Set(patterns);
405
603
  const usage = {
406
- names: [...new Set(names)],
407
- patterns: [...new Set(patterns)],
604
+ names: [...uniqueNames],
605
+ patterns: [...uniquePatterns],
408
606
  isDynamic: named.length < tokens.length
409
607
  };
410
608
  return usage;
@@ -421,8 +619,9 @@ var classExpressionUsage = (source) => {
421
619
  const names = [];
422
620
  collect(ast, names);
423
621
  const isDynamic = isDynamicAst(ast);
622
+ const uniqueNames = new Set(names);
424
623
  const usage = {
425
- names: [...new Set(names)],
624
+ names: [...uniqueNames],
426
625
  patterns: [],
427
626
  isDynamic
428
627
  };
@@ -490,14 +689,21 @@ var templateClasses = (node) => {
490
689
  const classes = [];
491
690
  for (const attribute of node.attributes) {
492
691
  const isClassName = attribute.name === CLASS_ATTRIBUTE;
493
- if (isClassName) classes.push(...staticClasses(attribute));
692
+ if (isClassName) {
693
+ const found = staticClasses(attribute);
694
+ classes.push(...found);
695
+ }
494
696
  }
495
697
  for (const input of node.inputs) {
496
- classes.push(...inputClasses(input));
698
+ const found = inputClasses(input);
699
+ classes.push(...found);
497
700
  }
498
701
  return classes;
499
702
  };
500
703
 
704
+ // packages/lint-suite/src/lib/rules/utils/to-regexp.util.ts
705
+ var toRegExp = (pattern) => new RegExp(pattern, "u");
706
+
501
707
  // packages/lint-suite/src/lib/rules/no-unused-classes/template/template-usage.ts
502
708
  var CLASS_ATTRIBUTE2 = "class";
503
709
  var NG_CLASS_ATTRIBUTE = "ngClass";
@@ -507,7 +713,7 @@ var UNKNOWN_USAGE2 = {
507
713
  isDynamic: true,
508
714
  size: 0
509
715
  };
510
- var toRegExp = (pattern) => new RegExp(pattern, "u");
716
+ var templates = createFileCache("template-usage");
511
717
  var expressionSource2 = (value) => {
512
718
  const isWithSource = value instanceof ASTWithSource;
513
719
  if (!isWithSource) return null;
@@ -556,33 +762,35 @@ var ClassUsageVisitor = class extends TmplAstRecursiveVisitor {
556
762
  if (usage.isDynamic) this.isDynamic = true;
557
763
  }
558
764
  };
559
- var templateText = (source) => {
765
+ var parseUsage = (text, path) => {
766
+ const parsed = parseTemplate(text, path);
767
+ if (parsed.errors !== null) return null;
768
+ const visitor = new ClassUsageVisitor();
769
+ 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) => {
560
776
  if (source.kind === "unknown") return null;
561
- if (source.kind === "inline") {
562
- const inline = { text: source.source, path: source.path };
563
- return inline;
564
- }
565
- try {
566
- const text = readFileSync2(source.path, "utf8");
567
- const file = { text, path: source.path };
568
- return file;
569
- } catch {
570
- return null;
571
- }
777
+ if (source.kind === "inline") return parseUsage(source.source, source.path);
778
+ return readCached(templates, source.path, parseUsage) ?? null;
572
779
  };
573
780
  var templateUsage = (sources) => {
574
- const visitor = new ClassUsageVisitor();
781
+ const names = /* @__PURE__ */ new Set();
782
+ const patterns = /* @__PURE__ */ new Set();
575
783
  for (const source of sources) {
576
- const template = templateText(source);
577
- if (template === null) return UNKNOWN_USAGE2;
578
- const parsed = parseTemplate(template.text, template.path);
579
- if (parsed.errors !== null) return UNKNOWN_USAGE2;
580
- tmplAstVisitAll(visitor, parsed.nodes);
581
- }
582
- if (visitor.isDynamic) return UNKNOWN_USAGE2;
583
- const patterns = [...visitor.patterns].map(toRegExp);
584
- const has = classMatcher(visitor.names, patterns);
585
- 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;
586
794
  const usage = { has, isDynamic: false, size };
587
795
  return usage;
588
796
  };
@@ -616,7 +824,6 @@ var meta = {
616
824
  url: "https://github.com/F0rty-Tw0/lint-suite#no-unused-classes"
617
825
  };
618
826
  var isString = (value) => typeof value === "string";
619
- var toRegExp2 = (pattern) => new RegExp(pattern, "u");
620
827
  var validate = (result, primary, secondary) => {
621
828
  const primaryOptions = { actual: primary, possible: [true] };
622
829
  const possible = { ignoreClassPatterns: [isString] };
@@ -675,12 +882,12 @@ var noUnusedClasses = (primary, secondary) => {
675
882
  if (!isValid) return;
676
883
  const file = root.source?.input.file;
677
884
  if (file === void 0) return;
678
- const templates = stylesheetTemplates(file);
679
- if (templates.length === 0) return;
680
- const usage = templateUsage(templates);
885
+ const templates2 = stylesheetTemplates(file);
886
+ if (templates2.length === 0) return;
887
+ const usage = templateUsage(templates2);
681
888
  if (usage.isDynamic) return;
682
889
  const patterns = secondary?.ignoreClassPatterns ?? DEFAULT_IGNORE_PATTERNS;
683
- const ignored = patterns.map(toRegExp2);
890
+ const ignored = patterns.map(toRegExp);
684
891
  const extended = extendedClasses(root);
685
892
  const context = {
686
893
  result,
@@ -725,6 +932,9 @@ var stylelint2 = {
725
932
  ignoreCustomProperties: ["^--mdc", "^--sys"]
726
933
  },
727
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
728
938
  "lint-suite/no-unused-classes": true
729
939
  }
730
940
  }