lint-suite 1.6.2 → 1.6.3

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 (63) hide show
  1. package/README.md +149 -2
  2. package/eslint.cjs +1049 -309
  3. package/eslint.cjs.map +4 -4
  4. package/eslint.js +938 -175
  5. package/eslint.js.map +4 -4
  6. package/lib/angular-template.d.ts +1 -0
  7. package/lib/angular-template.d.ts.map +1 -1
  8. package/lib/angular.d.ts.map +1 -1
  9. package/lib/base-practices.d.ts.map +1 -1
  10. package/lib/line-length.const.d.ts +2 -0
  11. package/lib/line-length.const.d.ts.map +1 -0
  12. package/lib/rules/common/class-usage.type.d.ts +25 -0
  13. package/lib/rules/common/class-usage.type.d.ts.map +1 -0
  14. package/lib/rules/component-metadata.d.ts +5 -0
  15. package/lib/rules/component-metadata.d.ts.map +1 -0
  16. package/lib/rules/no-unstyled-classes/common/no-unstyled-classes.type.d.ts +33 -0
  17. package/lib/rules/no-unstyled-classes/common/no-unstyled-classes.type.d.ts.map +1 -0
  18. package/lib/rules/no-unstyled-classes/no-unstyled-classes.d.ts +8 -0
  19. package/lib/rules/no-unstyled-classes/no-unstyled-classes.d.ts.map +1 -0
  20. package/lib/rules/no-unstyled-classes/styles/component-stylesheets.d.ts +4 -0
  21. package/lib/rules/no-unstyled-classes/styles/component-stylesheets.d.ts.map +1 -0
  22. package/lib/rules/no-unstyled-classes/styles/stylesheet-classes.d.ts +3 -0
  23. package/lib/rules/no-unstyled-classes/styles/stylesheet-classes.d.ts.map +1 -0
  24. package/lib/rules/no-unstyled-classes/styles/stylesheet-collection.d.ts +4 -0
  25. package/lib/rules/no-unstyled-classes/styles/stylesheet-collection.d.ts.map +1 -0
  26. package/lib/rules/no-unstyled-classes/styles/stylesheet-imports.d.ts +2 -0
  27. package/lib/rules/no-unstyled-classes/styles/stylesheet-imports.d.ts.map +1 -0
  28. package/lib/rules/no-unstyled-classes/styles/template-stylesheets.d.ts +3 -0
  29. package/lib/rules/no-unstyled-classes/styles/template-stylesheets.d.ts.map +1 -0
  30. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.d.ts.map +1 -1
  31. package/lib/rules/no-unused-classes/common/no-unused-classes.type.d.ts +24 -0
  32. package/lib/rules/no-unused-classes/common/no-unused-classes.type.d.ts.map +1 -0
  33. package/lib/rules/no-unused-classes/no-unused-classes.d.ts +8 -0
  34. package/lib/rules/no-unused-classes/no-unused-classes.d.ts.map +1 -0
  35. package/lib/rules/no-unused-classes/styles/component-descriptors.d.ts +3 -0
  36. package/lib/rules/no-unused-classes/styles/component-descriptors.d.ts.map +1 -0
  37. package/lib/rules/no-unused-classes/styles/stylesheet-components.d.ts +3 -0
  38. package/lib/rules/no-unused-classes/styles/stylesheet-components.d.ts.map +1 -0
  39. package/lib/rules/no-unused-classes/template/template-usage.d.ts +3 -0
  40. package/lib/rules/no-unused-classes/template/template-usage.d.ts.map +1 -0
  41. package/lib/rules/one-line-guard/one-line-guard.d.ts +10 -0
  42. package/lib/rules/one-line-guard/one-line-guard.d.ts.map +1 -0
  43. package/lib/rules/utils/class-expression-literals.util.d.ts +4 -0
  44. package/lib/rules/utils/class-expression-literals.util.d.ts.map +1 -0
  45. package/lib/rules/utils/resolved-rules.util.d.ts +5 -0
  46. package/lib/rules/utils/resolved-rules.util.d.ts.map +1 -0
  47. package/lib/rules/utils/selector-classes.util.d.ts +7 -0
  48. package/lib/rules/utils/selector-classes.util.d.ts.map +1 -0
  49. package/lib/rules/utils/template-classes.util.d.ts +3 -0
  50. package/lib/rules/utils/template-classes.util.d.ts.map +1 -0
  51. package/lib/typescript-local-plugin.d.ts.map +1 -1
  52. package/lib/typescript-safety.d.ts.map +1 -1
  53. package/package.json +4 -1
  54. package/prettier.cjs +6 -1
  55. package/prettier.cjs.map +3 -3
  56. package/prettier.d.ts.map +1 -1
  57. package/prettier.js +4 -1
  58. package/prettier.js.map +3 -3
  59. package/stylelint.cjs +680 -4
  60. package/stylelint.cjs.map +4 -4
  61. package/stylelint.d.ts.map +1 -1
  62. package/stylelint.js +708 -4
  63. package/stylelint.js.map +4 -4
package/stylelint.cjs CHANGED
@@ -30,12 +30,687 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // packages/lint-suite/src/stylelint.ts
31
31
  var stylelint_exports = {};
32
32
  __export(stylelint_exports, {
33
- stylelint: () => stylelint
33
+ stylelint: () => stylelint2
34
34
  });
35
35
  module.exports = __toCommonJS(stylelint_exports);
36
36
  var import_stylelint_scss = __toESM(require("stylelint-scss"), 1);
37
37
  var import_stylelint_selector_bem_pattern = __toESM(require("stylelint-selector-bem-pattern"), 1);
38
- var stylelint = {
38
+
39
+ // packages/lint-suite/src/lib/rules/no-unused-classes/no-unused-classes.ts
40
+ var import_stylelint = __toESM(require("stylelint"), 1);
41
+
42
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/stylesheet-components.ts
43
+ var import_node_fs2 = require("node:fs");
44
+ var import_node_path = require("node:path");
45
+
46
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/component-descriptors.ts
47
+ var import_node_fs = require("node:fs");
48
+
49
+ // packages/lint-suite/src/lib/rules/component-metadata.ts
50
+ var import_typescript = require("typescript");
51
+ var componentArgument = (decorator) => {
52
+ const { expression } = decorator;
53
+ if (!(0, import_typescript.isCallExpression)(expression)) return null;
54
+ if (!(0, import_typescript.isIdentifier)(expression.expression)) return null;
55
+ const isComponent = expression.expression.text === "Component";
56
+ if (!isComponent) return null;
57
+ const [argument] = expression.arguments;
58
+ if (argument === void 0) return null;
59
+ if (!(0, import_typescript.isObjectLiteralExpression)(argument)) return null;
60
+ return argument;
61
+ };
62
+ var decoratedMetadata = (node) => {
63
+ if (!(0, import_typescript.canHaveDecorators)(node)) return null;
64
+ const decorators = (0, import_typescript.getDecorators)(node) ?? [];
65
+ for (const decorator of decorators) {
66
+ const metadata = componentArgument(decorator);
67
+ if (metadata !== null) return metadata;
68
+ }
69
+ return null;
70
+ };
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
+ var literalText = (node) => {
83
+ if (!(0, import_typescript.isStringLiteralLike)(node)) return null;
84
+ return node.text;
85
+ };
86
+ var literalTexts = (node) => {
87
+ const single = literalText(node);
88
+ if (single !== null) return [single];
89
+ if (!(0, import_typescript.isArrayLiteralExpression)(node)) return [];
90
+ const texts = [];
91
+ for (const element of node.elements) {
92
+ const text = literalText(element);
93
+ if (text !== null) texts.push(text);
94
+ }
95
+ return texts;
96
+ };
97
+ var metadataValue = (metadata, name) => {
98
+ for (const property of metadata.properties) {
99
+ if (!(0, import_typescript.isPropertyAssignment)(property)) continue;
100
+ if (!(0, import_typescript.isIdentifier)(property.name)) continue;
101
+ const isMatch = property.name.text === name;
102
+ if (isMatch) return property.initializer;
103
+ }
104
+ return null;
105
+ };
106
+ var metadataTexts = (metadata, name) => {
107
+ const value = metadataValue(metadata, name);
108
+ if (value === null) return [];
109
+ return literalTexts(value);
110
+ };
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
+ var toDescriptor = (metadata) => {
116
+ const [templateUrl] = metadataTexts(metadata, "templateUrl");
117
+ const [template] = metadataTexts(metadata, "template");
118
+ const styleUrls = [
119
+ ...metadataTexts(metadata, "styleUrl"),
120
+ ...metadataTexts(metadata, "styleUrls")
121
+ ];
122
+ const descriptor = {
123
+ templateUrl: templateUrl ?? null,
124
+ template: template ?? null,
125
+ styleUrls
126
+ };
127
+ return descriptor;
128
+ };
129
+ var parseDescriptors = (text, path) => {
130
+ const hasComponent = text.includes(COMPONENT_MARKER);
131
+ if (!hasComponent) return [];
132
+ return parseComponentMetadata(text, path).map(toDescriptor);
133
+ };
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
+ }
147
+ };
148
+
149
+ // packages/lint-suite/src/lib/rules/no-unused-classes/styles/stylesheet-components.ts
150
+ var MODULE_EXTENSION = /\.ts$/iu;
151
+ var STYLE_EXTENSION = /\.(?:scss|sass|css)$/iu;
152
+ var UNKNOWN_TEMPLATE = { kind: "unknown" };
153
+ var declaredStylesheets = (descriptor, directory) => {
154
+ const resolveUrl = (url) => (0, import_node_path.resolve)(directory, url);
155
+ return descriptor.styleUrls.map(resolveUrl);
156
+ };
157
+ var fileTemplate = (descriptor, directory) => {
158
+ 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);
161
+ if (!isPresent) return null;
162
+ const source = { kind: "file", path };
163
+ return source;
164
+ };
165
+ var inlineTemplate = (descriptor, componentPath) => {
166
+ if (descriptor.template === null) return null;
167
+ const source = {
168
+ kind: "inline",
169
+ source: descriptor.template,
170
+ path: componentPath
171
+ };
172
+ return source;
173
+ };
174
+ var componentTemplate = (descriptor, componentPath) => {
175
+ const directory = (0, import_node_path.dirname)(componentPath);
176
+ const file = fileTemplate(descriptor, directory);
177
+ if (file !== null) return file;
178
+ const inline = inlineTemplate(descriptor, componentPath);
179
+ return inline ?? UNKNOWN_TEMPLATE;
180
+ };
181
+ var matchedTemplates = (componentPath, stylesheetPath) => {
182
+ const directory = (0, import_node_path.dirname)(componentPath);
183
+ const sources = [];
184
+ for (const descriptor of componentDescriptors(componentPath)) {
185
+ const declared = declaredStylesheets(descriptor, directory);
186
+ const isMatch = declared.includes(stylesheetPath);
187
+ if (!isMatch) continue;
188
+ sources.push(componentTemplate(descriptor, componentPath));
189
+ }
190
+ return sources;
191
+ };
192
+ var directoryModules = (directory) => {
193
+ try {
194
+ return (0, import_node_fs2.readdirSync)(directory).filter((file) => MODULE_EXTENSION.test(file));
195
+ } catch {
196
+ return [];
197
+ }
198
+ };
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];
205
+ };
206
+ var stylesheetTemplates = (stylesheetPath) => {
207
+ const resolved = (0, import_node_path.resolve)(stylesheetPath);
208
+ const directory = (0, import_node_path.dirname)(resolved);
209
+ 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));
213
+ }
214
+ if (sources.length > 0) return sources;
215
+ return siblingTemplates(resolved);
216
+ };
217
+
218
+ // packages/lint-suite/src/lib/rules/no-unused-classes/template/template-usage.ts
219
+ var import_compiler3 = require("@angular/compiler");
220
+ var import_node_fs3 = require("node:fs");
221
+
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
226
+ var import_postcss_selector_parser = __toESM(require("postcss-selector-parser"), 1);
227
+ var INTERPOLATION_TOKEN = "zzinterpzz";
228
+ var PARENT_TOKEN = "zzparentzz";
229
+ var UNKNOWN_PARENT_SELECTOR = `.${PARENT_TOKEN}`;
230
+ var UNKNOWN_PARTS = new RegExp(
231
+ `${INTERPOLATION_TOKEN}|${PARENT_TOKEN}`,
232
+ "gu"
233
+ );
234
+ var INTERPOLATION = /#\{[^}]*\}/gu;
235
+ var DEEP_COMBINATORS = /* @__PURE__ */ new Set(["/deep/", ">>>"]);
236
+ var HOST_PSEUDOS = /* @__PURE__ */ new Set([":host", ":host-context"]);
237
+ var NG_DEEP_PSEUDO = "::ng-deep";
238
+ var REGEXP_META = /[.*+?^${}()|[\]\\]/gu;
239
+ var escapeRegExp = (part) => {
240
+ return part.replaceAll(REGEXP_META, "\\$&");
241
+ };
242
+ var classNamePattern = (value) => {
243
+ const literals = value.split(UNKNOWN_PARTS).map(escapeRegExp);
244
+ const joined = literals.join(".*");
245
+ return `^${joined}$`;
246
+ };
247
+ var withoutParent = (selector) => {
248
+ return selector.replaceAll("&", UNKNOWN_PARENT_SELECTOR);
249
+ };
250
+ var withParent = (selector, parent) => {
251
+ const hasNesting = selector.includes("&");
252
+ if (hasNesting) return selector.replaceAll("&", parent);
253
+ return `${parent} ${selector}`;
254
+ };
255
+ var resolveSelectors = (selectors, parents) => {
256
+ const hasParents = parents.length > 0;
257
+ if (!hasParents) return selectors.map(withoutParent);
258
+ const resolved = [];
259
+ for (const parent of parents) {
260
+ for (const selector of selectors) {
261
+ resolved.push(withParent(selector, parent));
262
+ }
263
+ }
264
+ return resolved;
265
+ };
266
+ var isBoundaryNode = (node) => {
267
+ if (node.type === "combinator") return DEEP_COMBINATORS.has(node.value);
268
+ if (node.type !== "pseudo") return false;
269
+ return node.value === NG_DEEP_PSEUDO;
270
+ };
271
+ var addTemplateClass = (value, names) => {
272
+ const hasInterpolation = value.includes(INTERPOLATION_TOKEN);
273
+ const hasUnknownParent = value.includes(PARENT_TOKEN);
274
+ const isDynamic = hasInterpolation || hasUnknownParent;
275
+ if (isDynamic) return;
276
+ names.push(value);
277
+ };
278
+ var collectTemplateClasses = (nodes, names) => {
279
+ for (const node of nodes) {
280
+ const isBoundary = isBoundaryNode(node);
281
+ if (isBoundary) return true;
282
+ if (node.type === "class") {
283
+ addTemplateClass(node.value, names);
284
+ continue;
285
+ }
286
+ if (node.type !== "pseudo") continue;
287
+ const isHost = HOST_PSEUDOS.has(node.value);
288
+ if (isHost) continue;
289
+ for (const argument of node.nodes) {
290
+ const isStopped = collectTemplateClasses(argument.nodes, names);
291
+ if (isStopped) return true;
292
+ }
293
+ }
294
+ return false;
295
+ };
296
+ var selectorTemplateClasses = (selector) => {
297
+ const names = [];
298
+ const normalized = selector.replaceAll(INTERPOLATION, INTERPOLATION_TOKEN);
299
+ try {
300
+ const root = (0, import_postcss_selector_parser.default)().astSync(normalized);
301
+ for (const each of root.nodes) collectTemplateClasses(each.nodes, names);
302
+ } catch {
303
+ return [];
304
+ }
305
+ return [...new Set(names)];
306
+ };
307
+ var classMatcher = (exact, patterns) => {
308
+ const matches = (name) => {
309
+ const isExact = exact.has(name);
310
+ if (isExact) return true;
311
+ return patterns.some((pattern) => pattern.test(name));
312
+ };
313
+ return matches;
314
+ };
315
+
316
+ // packages/lint-suite/src/lib/rules/utils/class-expression-literals.util.ts
317
+ var WHITESPACE = /\s+/u;
318
+ var EXPRESSION_FILE = "class-expression";
319
+ var INTERPOLATION_TOKEN2 = "zzinterpzz";
320
+ var parser = new import_compiler.Parser(new import_compiler.Lexer());
321
+ var spanOf = (source) => {
322
+ const file = new import_compiler.ParseSourceFile(source, EXPRESSION_FILE);
323
+ const start = new import_compiler.ParseLocation(file, 0, 0, 0);
324
+ const span = new import_compiler.ParseSourceSpan(start, start.moveBy(source.length));
325
+ return span;
326
+ };
327
+ var astOf = (parsed) => {
328
+ const hasErrors = parsed.errors.length > 0;
329
+ if (hasErrors) return null;
330
+ return parsed.ast;
331
+ };
332
+ var parseClassExpression = (source) => {
333
+ try {
334
+ const span = spanOf(source);
335
+ const interpolation = parser.parseInterpolation(source, span, 0, null);
336
+ if (interpolation) return astOf(interpolation);
337
+ const binding = parser.parseBinding(source, span, 0);
338
+ return astOf(binding);
339
+ } catch {
340
+ return null;
341
+ }
342
+ };
343
+ var addNames = (value, names) => {
344
+ for (const token of value.split(WHITESPACE)) {
345
+ if (token) names.push(token);
346
+ }
347
+ };
348
+ var addPrimitive = (ast, names) => {
349
+ if (typeof ast.value === "string") addNames(ast.value, names);
350
+ };
351
+ var addMapKeys = (ast, names) => {
352
+ for (const key of ast.keys) {
353
+ if (key.kind !== "spread") addNames(key.key, names);
354
+ }
355
+ };
356
+ var addInterpolation = (ast, names) => {
357
+ for (const text of ast.strings) addNames(text, names);
358
+ };
359
+ var collect = (ast, names) => {
360
+ if (ast instanceof import_compiler.LiteralPrimitive) {
361
+ addPrimitive(ast, names);
362
+ return;
363
+ }
364
+ if (ast instanceof import_compiler.LiteralMap) {
365
+ addMapKeys(ast, names);
366
+ return;
367
+ }
368
+ if (ast instanceof import_compiler.Interpolation) {
369
+ addInterpolation(ast, names);
370
+ return;
371
+ }
372
+ if (ast instanceof import_compiler.LiteralArray) {
373
+ for (const expression of ast.expressions) collect(expression, names);
374
+ return;
375
+ }
376
+ if (ast instanceof import_compiler.Conditional) {
377
+ collect(ast.trueExp, names);
378
+ collect(ast.falseExp, names);
379
+ }
380
+ };
381
+ var classExpressionLiterals = (source) => {
382
+ const ast = parseClassExpression(source);
383
+ if (ast === null) return [];
384
+ const names = [];
385
+ collect(ast, names);
386
+ return [...new Set(names)];
387
+ };
388
+ var isSpreadKey = (key) => key.kind === "spread";
389
+ var isDynamicAst = (ast) => {
390
+ if (ast instanceof import_compiler.LiteralPrimitive) return false;
391
+ if (ast instanceof import_compiler.LiteralMap) return ast.keys.some(isSpreadKey);
392
+ if (ast instanceof import_compiler.LiteralArray) return ast.expressions.some(isDynamicAst);
393
+ if (ast instanceof import_compiler.Conditional) {
394
+ const isTrueDynamic = isDynamicAst(ast.trueExp);
395
+ const isFalseDynamic = isDynamicAst(ast.falseExp);
396
+ return isTrueDynamic || isFalseDynamic;
397
+ }
398
+ return true;
399
+ };
400
+ var isNamedToken = (token) => token !== INTERPOLATION_TOKEN2;
401
+ var isPatternToken = (token) => {
402
+ return token.includes(INTERPOLATION_TOKEN2);
403
+ };
404
+ var isPlainToken = (token) => !isPatternToken(token);
405
+ var interpolationUsage = (ast) => {
406
+ const joined = ast.strings.join(INTERPOLATION_TOKEN2);
407
+ const tokens = joined.split(WHITESPACE).filter(Boolean);
408
+ const named = tokens.filter(isNamedToken);
409
+ const patterns = named.filter(isPatternToken).map(classNamePattern);
410
+ const names = named.filter(isPlainToken);
411
+ const usage = {
412
+ names: [...new Set(names)],
413
+ patterns: [...new Set(patterns)],
414
+ isDynamic: named.length < tokens.length
415
+ };
416
+ return usage;
417
+ };
418
+ var UNKNOWN_USAGE = {
419
+ names: [],
420
+ patterns: [],
421
+ isDynamic: true
422
+ };
423
+ var classExpressionUsage = (source) => {
424
+ const ast = parseClassExpression(source);
425
+ if (ast === null) return UNKNOWN_USAGE;
426
+ if (ast instanceof import_compiler.Interpolation) return interpolationUsage(ast);
427
+ const names = [];
428
+ collect(ast, names);
429
+ const isDynamic = isDynamicAst(ast);
430
+ const usage = {
431
+ names: [...new Set(names)],
432
+ patterns: [],
433
+ isDynamic
434
+ };
435
+ return usage;
436
+ };
437
+
438
+ // packages/lint-suite/src/lib/rules/utils/template-classes.util.ts
439
+ var import_compiler2 = require("@angular/compiler");
440
+ var CLASS_ATTRIBUTE = "class";
441
+ var CLASS_BINDING_PREFIX = "class.";
442
+ var TOKEN = /\S+/gu;
443
+ var expressionSource = (value) => {
444
+ if (!("source" in value)) return null;
445
+ const { source } = value;
446
+ if (typeof source !== "string") return null;
447
+ return source;
448
+ };
449
+ var staticClasses = (attribute) => {
450
+ const classes = [];
451
+ const valueSpan = attribute.valueSpan ?? attribute.sourceSpan;
452
+ for (const token of attribute.value.matchAll(TOKEN)) {
453
+ const start = valueSpan.start.moveBy(token.index);
454
+ const end = valueSpan.start.moveBy(token.index + token[0].length);
455
+ const span = new import_compiler2.ParseSourceSpan(start, end);
456
+ const templateClass = { name: token[0], span };
457
+ classes.push(templateClass);
458
+ }
459
+ return classes;
460
+ };
461
+ var boundClassName = (input) => {
462
+ const { details } = input.keySpan;
463
+ if (details === null) return null;
464
+ const isClassBinding = details.startsWith(CLASS_BINDING_PREFIX);
465
+ if (!isClassBinding) return null;
466
+ return input.name;
467
+ };
468
+ var isClassExpression = (input) => {
469
+ const isClassName = input.name === CLASS_ATTRIBUTE;
470
+ if (!isClassName) return false;
471
+ const { details } = input.keySpan;
472
+ if (details === null) return true;
473
+ return details === CLASS_ATTRIBUTE;
474
+ };
475
+ var expressionClasses = (input) => {
476
+ const source = expressionSource(input.value);
477
+ if (source === null) return [];
478
+ const names = classExpressionLiterals(source);
479
+ const templateClassOf = (name) => {
480
+ const templateClass = { name, span: input.sourceSpan };
481
+ return templateClass;
482
+ };
483
+ return names.map(templateClassOf);
484
+ };
485
+ var inputClasses = (input) => {
486
+ const bound = boundClassName(input);
487
+ if (bound !== null) {
488
+ const templateClass = { name: bound, span: input.keySpan };
489
+ return [templateClass];
490
+ }
491
+ const isExpression = isClassExpression(input);
492
+ if (!isExpression) return [];
493
+ return expressionClasses(input);
494
+ };
495
+ var templateClasses = (node) => {
496
+ const classes = [];
497
+ for (const attribute of node.attributes) {
498
+ const isClassName = attribute.name === CLASS_ATTRIBUTE;
499
+ if (isClassName) classes.push(...staticClasses(attribute));
500
+ }
501
+ for (const input of node.inputs) {
502
+ classes.push(...inputClasses(input));
503
+ }
504
+ return classes;
505
+ };
506
+
507
+ // packages/lint-suite/src/lib/rules/no-unused-classes/template/template-usage.ts
508
+ var CLASS_ATTRIBUTE2 = "class";
509
+ var NG_CLASS_ATTRIBUTE = "ngClass";
510
+ var matchesNone = () => false;
511
+ var UNKNOWN_USAGE2 = {
512
+ has: matchesNone,
513
+ isDynamic: true,
514
+ size: 0
515
+ };
516
+ var toRegExp = (pattern) => new RegExp(pattern, "u");
517
+ var expressionSource2 = (value) => {
518
+ const isWithSource = value instanceof import_compiler3.ASTWithSource;
519
+ if (!isWithSource) return null;
520
+ return value.source;
521
+ };
522
+ var isClassInput = (input) => {
523
+ const isNgClass = input.name === NG_CLASS_ATTRIBUTE;
524
+ if (isNgClass) return true;
525
+ const isClass = input.name === CLASS_ATTRIBUTE2;
526
+ if (!isClass) return false;
527
+ const { details } = input.keySpan;
528
+ if (details === null) return true;
529
+ return details === CLASS_ATTRIBUTE2;
530
+ };
531
+ var ClassUsageVisitor = class extends import_compiler3.TmplAstRecursiveVisitor {
532
+ names = /* @__PURE__ */ new Set();
533
+ patterns = /* @__PURE__ */ new Set();
534
+ isDynamic = false;
535
+ visitElement(element) {
536
+ this.collect(element);
537
+ super.visitElement(element);
538
+ }
539
+ visitTemplate(template) {
540
+ this.collect(template);
541
+ super.visitTemplate(template);
542
+ }
543
+ visitComponent(component) {
544
+ this.collect(component);
545
+ super.visitComponent(component);
546
+ }
547
+ collect(node) {
548
+ for (const { name } of templateClasses(node)) this.names.add(name);
549
+ for (const input of node.inputs) this.collectInput(input);
550
+ }
551
+ collectInput(input) {
552
+ const isClass = isClassInput(input);
553
+ if (!isClass) return;
554
+ const source = expressionSource2(input.value);
555
+ if (source === null) {
556
+ this.isDynamic = true;
557
+ return;
558
+ }
559
+ const usage = classExpressionUsage(source);
560
+ for (const name of usage.names) this.names.add(name);
561
+ for (const pattern of usage.patterns) this.patterns.add(pattern);
562
+ if (usage.isDynamic) this.isDynamic = true;
563
+ }
564
+ };
565
+ var templateText = (source) => {
566
+ 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
+ }
578
+ };
579
+ var templateUsage = (sources) => {
580
+ const visitor = new ClassUsageVisitor();
581
+ 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;
592
+ const usage = { has, isDynamic: false, size };
593
+ return usage;
594
+ };
595
+
596
+ // packages/lint-suite/src/lib/rules/utils/resolved-rules.util.ts
597
+ var walkContainer = (container, parents, visit) => {
598
+ for (const node of container.nodes ?? []) {
599
+ if (node.type === "rule") {
600
+ const resolved = resolveSelectors(node.selectors, parents);
601
+ visit(node, resolved, parents);
602
+ walkContainer(node, resolved, visit);
603
+ continue;
604
+ }
605
+ if (node.type !== "atrule") continue;
606
+ walkContainer(node, parents, visit);
607
+ }
608
+ };
609
+ var walkResolvedRules = (root, visit) => {
610
+ walkContainer(root, [], visit);
611
+ };
612
+
613
+ // packages/lint-suite/src/lib/rules/no-unused-classes/no-unused-classes.ts
614
+ var { createPlugin, utils } = import_stylelint.default;
615
+ var DEFAULT_IGNORE_PATTERNS = ["^(js|qa|mat|cdk|mdc)-"];
616
+ var ruleName = "lint-suite/no-unused-classes";
617
+ var rejected = (name) => {
618
+ return `Unexpected class ".${name}" not used by any template of this stylesheet`;
619
+ };
620
+ var messages = utils.ruleMessages(ruleName, { rejected });
621
+ var meta = {
622
+ url: "https://github.com/F0rty-Tw0/lint-suite#no-unused-classes"
623
+ };
624
+ var isString = (value) => typeof value === "string";
625
+ var toRegExp2 = (pattern) => new RegExp(pattern, "u");
626
+ var validate = (result, primary, secondary) => {
627
+ const primaryOptions = { actual: primary, possible: [true] };
628
+ const possible = { ignoreClassPatterns: [isString] };
629
+ const secondaryOptions = { actual: secondary, possible, optional: true };
630
+ return utils.validateOptions(
631
+ result,
632
+ ruleName,
633
+ primaryOptions,
634
+ secondaryOptions
635
+ );
636
+ };
637
+ var addNames2 = (selector, names) => {
638
+ for (const name of selectorTemplateClasses(selector)) names.add(name);
639
+ };
640
+ var extendedClasses = (root) => {
641
+ const names = /* @__PURE__ */ new Set();
642
+ const collect2 = (atRule) => addNames2(atRule.params, names);
643
+ root.walkAtRules("extend", collect2);
644
+ return names;
645
+ };
646
+ var ruleClasses = (resolved, parents) => {
647
+ const inherited = /* @__PURE__ */ new Set();
648
+ for (const parent of parents) addNames2(parent, inherited);
649
+ const own = /* @__PURE__ */ new Set();
650
+ for (const selector of resolved) {
651
+ for (const name of selectorTemplateClasses(selector)) {
652
+ const isInherited = inherited.has(name);
653
+ if (isInherited) continue;
654
+ own.add(name);
655
+ }
656
+ }
657
+ return [...own];
658
+ };
659
+ var reportUnused = (context, rule, name) => {
660
+ const matchesIgnored = (pattern) => pattern.test(name);
661
+ const isIgnored = context.ignored.some(matchesIgnored);
662
+ if (isIgnored) return;
663
+ const isUsed = context.isUsed(name);
664
+ if (isUsed) return;
665
+ const isExtended = context.extended.has(name);
666
+ if (isExtended) return;
667
+ const message = messages.rejected(name);
668
+ const word = `.${name}`;
669
+ const problem = {
670
+ ruleName,
671
+ result: context.result,
672
+ node: rule,
673
+ message,
674
+ word
675
+ };
676
+ utils.report(problem);
677
+ };
678
+ var noUnusedClasses = (primary, secondary) => {
679
+ const walk = (root, result) => {
680
+ const isValid = validate(result, primary, secondary);
681
+ if (!isValid) return;
682
+ const file = root.source?.input.file;
683
+ if (file === void 0) return;
684
+ const templates = stylesheetTemplates(file);
685
+ if (templates.length === 0) return;
686
+ const usage = templateUsage(templates);
687
+ if (usage.isDynamic) return;
688
+ const patterns = secondary?.ignoreClassPatterns ?? DEFAULT_IGNORE_PATTERNS;
689
+ const ignored = patterns.map(toRegExp2);
690
+ const extended = extendedClasses(root);
691
+ const context = {
692
+ result,
693
+ ignored,
694
+ extended,
695
+ isUsed: usage.has
696
+ };
697
+ const visit = (rule, resolved, parents) => {
698
+ for (const name of ruleClasses(resolved, parents)) {
699
+ reportUnused(context, rule, name);
700
+ }
701
+ };
702
+ walkResolvedRules(root, visit);
703
+ };
704
+ return walk;
705
+ };
706
+ noUnusedClasses.ruleName = ruleName;
707
+ noUnusedClasses.messages = messages;
708
+ noUnusedClasses.meta = meta;
709
+ var plugin = createPlugin(ruleName, noUnusedClasses);
710
+ var no_unused_classes_default = plugin;
711
+
712
+ // packages/lint-suite/src/stylelint.ts
713
+ var stylelint2 = {
39
714
  // Stylelint resolves config for cwd when picking a formatter; without a
40
715
  // top-level `rules` an overrides-only config fails with "No rules found".
41
716
  rules: {},
@@ -47,7 +722,7 @@ var stylelint = {
47
722
  "stylelint-config-standard-scss",
48
723
  "stylelint-config-recess-order"
49
724
  ],
50
- plugins: [...import_stylelint_scss.default, import_stylelint_selector_bem_pattern.default],
725
+ plugins: [...import_stylelint_scss.default, import_stylelint_selector_bem_pattern.default, no_unused_classes_default],
51
726
  rules: {
52
727
  "selector-class-pattern": "^(?:(?:o|c|u|t|s|is|has|_|js|qa)-)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*(?:__[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:--[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*)?(?:\\[.+\\])?$",
53
728
  "plugin/selector-bem-pattern": {
@@ -55,7 +730,8 @@ var stylelint = {
55
730
  implicitComponents: ["**/*.component.scss", "**/*.component.css"],
56
731
  ignoreCustomProperties: ["^--mdc", "^--sys"]
57
732
  },
58
- "no-descending-specificity": null
733
+ "no-descending-specificity": null,
734
+ "lint-suite/no-unused-classes": true
59
735
  }
60
736
  }
61
737
  ]