1use std::sync::LazyLock;
4
5use crate::suppress::IssueKind;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct IssueKindMeta {
14 pub kind: Option<IssueKind>,
16 pub code: &'static str,
18 pub aliases: &'static [&'static str],
20 pub label: &'static str,
22 pub config_key: Option<&'static str>,
24 pub filter_flag: Option<&'static str>,
26 pub mcp_issue_type: Option<&'static str>,
28 pub suppress_token: Option<&'static str>,
30 pub suppress_file_level: bool,
32 pub lsp: bool,
35 pub docs_category: &'static str,
37}
38
39impl IssueKindMeta {
40 #[must_use]
42 pub const fn mcp_pair(self) -> Option<(&'static str, &'static str)> {
43 match (self.mcp_issue_type, self.filter_flag) {
44 (Some(issue_type), Some(flag)) => Some((issue_type, flag)),
45 _ => None,
46 }
47 }
48
49 #[must_use]
51 pub fn has_result_contract(self) -> bool {
52 issue_result_meta_by_code(self.code).is_some()
53 }
54
55 #[must_use]
57 pub fn sarif_rule_ids(self) -> Vec<String> {
58 issue_sarif_rule_ids(self.code)
59 }
60
61 #[must_use]
63 pub fn codeclimate_check_names(self) -> Vec<String> {
64 issue_codeclimate_check_names(self.code)
65 }
66
67 #[must_use]
69 pub fn docs_anchor(self) -> Option<&'static str> {
70 issue_docs_anchor(self.code)
71 }
72
73 #[must_use]
75 pub fn ts_alias(self) -> Option<TsAliasMeta> {
76 issue_ts_alias(self.code)
77 }
78}
79
80pub const ISSUE_KIND_META: &[IssueKindMeta] = &[
82 IssueKindMeta {
83 kind: Some(IssueKind::CodeDuplication),
84 code: "code-duplication",
85 aliases: &[],
86 label: "Code Duplication",
87 config_key: None,
88 filter_flag: None,
89 mcp_issue_type: None,
90 suppress_token: Some("code-duplication"),
91 suppress_file_level: false,
92 lsp: true,
93 docs_category: "dupes",
94 },
95 IssueKindMeta {
96 kind: Some(IssueKind::UnusedFile),
97 code: "unused-file",
98 aliases: &[],
99 label: "Unused Files",
100 config_key: Some("unused-files"),
101 filter_flag: Some("--unused-files"),
102 mcp_issue_type: Some("unused-files"),
103 suppress_token: Some("unused-file"),
104 suppress_file_level: true,
105 lsp: true,
106 docs_category: "source",
107 },
108 IssueKindMeta {
109 kind: Some(IssueKind::UnusedExport),
110 code: "unused-export",
111 aliases: &[],
112 label: "Unused Exports",
113 config_key: Some("unused-exports"),
114 filter_flag: Some("--unused-exports"),
115 mcp_issue_type: Some("unused-exports"),
116 suppress_token: Some("unused-export"),
117 suppress_file_level: false,
118 lsp: true,
119 docs_category: "source",
120 },
121 IssueKindMeta {
122 kind: Some(IssueKind::UnusedType),
123 code: "unused-type",
124 aliases: &[],
125 label: "Unused Types",
126 config_key: Some("unused-types"),
127 filter_flag: Some("--unused-types"),
128 mcp_issue_type: Some("unused-types"),
129 suppress_token: Some("unused-type"),
130 suppress_file_level: false,
131 lsp: true,
132 docs_category: "source",
133 },
134 IssueKindMeta {
135 kind: Some(IssueKind::PrivateTypeLeak),
136 code: "private-type-leak",
137 aliases: &[],
138 label: "Private Type Leaks",
139 config_key: Some("private-type-leaks"),
140 filter_flag: Some("--private-type-leaks"),
141 mcp_issue_type: Some("private-type-leaks"),
142 suppress_token: Some("private-type-leak"),
143 suppress_file_level: false,
144 lsp: true,
145 docs_category: "source",
146 },
147 IssueKindMeta {
148 kind: Some(IssueKind::DeprecatedExportInUse),
149 code: "deprecated-export-in-use",
150 aliases: &[],
151 label: "Deprecated Exports in Use",
152 config_key: Some("deprecated-exports-in-use"),
153 filter_flag: Some("--deprecated-exports-in-use"),
154 mcp_issue_type: Some("deprecated-exports-in-use"),
155 suppress_token: Some("deprecated-export-in-use"),
156 suppress_file_level: false,
157 lsp: false,
158 docs_category: "source",
159 },
160 IssueKindMeta {
161 kind: Some(IssueKind::UnusedDependency),
162 code: "unused-dependency",
163 aliases: &[],
164 label: "Unused Dependencies",
165 config_key: Some("unused-dependencies"),
166 filter_flag: Some("--unused-deps"),
167 mcp_issue_type: Some("unused-deps"),
168 suppress_token: None,
169 suppress_file_level: false,
170 lsp: true,
171 docs_category: "dependency",
172 },
173 IssueKindMeta {
174 kind: Some(IssueKind::UnusedDevDependency),
175 code: "unused-dev-dependency",
176 aliases: &["unused-dev-deps", "unused-dev-dependencies"],
177 label: "Unused Dev Dependencies",
178 config_key: Some("unused-dev-dependencies"),
179 filter_flag: Some("--unused-deps"),
180 mcp_issue_type: None,
181 suppress_token: None,
182 suppress_file_level: false,
183 lsp: true,
184 docs_category: "dependency",
185 },
186 IssueKindMeta {
187 kind: None,
188 code: "unused-optional-dependency",
189 aliases: &["unused-optional-deps", "unused-optional-dependencies"],
190 label: "Unused Optional Dependencies",
191 config_key: Some("unused-optional-dependencies"),
192 filter_flag: Some("--unused-deps"),
193 mcp_issue_type: None,
194 suppress_token: None,
195 suppress_file_level: false,
196 lsp: true,
197 docs_category: "dependency",
198 },
199 IssueKindMeta {
200 kind: Some(IssueKind::UnusedEnumMember),
201 code: "unused-enum-member",
202 aliases: &[],
203 label: "Unused Enum Members",
204 config_key: Some("unused-enum-members"),
205 filter_flag: Some("--unused-enum-members"),
206 mcp_issue_type: Some("unused-enum-members"),
207 suppress_token: Some("unused-enum-member"),
208 suppress_file_level: false,
209 lsp: true,
210 docs_category: "source",
211 },
212 IssueKindMeta {
213 kind: Some(IssueKind::UnusedClassMember),
214 code: "unused-class-member",
215 aliases: &[],
216 label: "Unused Class Members",
217 config_key: Some("unused-class-members"),
218 filter_flag: Some("--unused-class-members"),
219 mcp_issue_type: Some("unused-class-members"),
220 suppress_token: Some("unused-class-member"),
221 suppress_file_level: false,
222 lsp: true,
223 docs_category: "source",
224 },
225 IssueKindMeta {
226 kind: Some(IssueKind::UnusedStoreMember),
227 code: "unused-store-member",
228 aliases: &["unused-store-members"],
229 label: "Unused Store Members",
230 config_key: Some("unused-store-members"),
231 filter_flag: Some("--unused-store-members"),
232 mcp_issue_type: Some("unused-store-members"),
233 suppress_token: Some("unused-store-member"),
234 suppress_file_level: false,
235 lsp: true,
236 docs_category: "framework",
237 },
238 IssueKindMeta {
239 kind: Some(IssueKind::UnresolvedImport),
240 code: "unresolved-import",
241 aliases: &[],
242 label: "Unresolved Imports",
243 config_key: Some("unresolved-imports"),
244 filter_flag: Some("--unresolved-imports"),
245 mcp_issue_type: Some("unresolved-imports"),
246 suppress_token: Some("unresolved-import"),
247 suppress_file_level: false,
248 lsp: true,
249 docs_category: "dependency",
250 },
251 IssueKindMeta {
252 kind: Some(IssueKind::UnlistedDependency),
253 code: "unlisted-dependency",
254 aliases: &[],
255 label: "Unlisted Dependencies",
256 config_key: Some("unlisted-dependencies"),
257 filter_flag: Some("--unlisted-deps"),
258 mcp_issue_type: Some("unlisted-deps"),
259 suppress_token: None,
260 suppress_file_level: false,
261 lsp: true,
262 docs_category: "dependency",
263 },
264 IssueKindMeta {
265 kind: Some(IssueKind::DuplicateExport),
266 code: "duplicate-export",
267 aliases: &[],
268 label: "Duplicate Exports",
269 config_key: Some("duplicate-exports"),
270 filter_flag: Some("--duplicate-exports"),
271 mcp_issue_type: Some("duplicate-exports"),
272 suppress_token: Some("duplicate-export"),
273 suppress_file_level: true,
274 lsp: true,
275 docs_category: "source",
276 },
277 IssueKindMeta {
278 kind: Some(IssueKind::TypeOnlyDependency),
279 code: "type-only-dependency",
280 aliases: &[],
281 label: "Type-Only Dependencies",
282 config_key: Some("type-only-dependencies"),
283 filter_flag: Some("--unused-deps"),
284 mcp_issue_type: None,
285 suppress_token: None,
286 suppress_file_level: false,
287 lsp: true,
288 docs_category: "dependency",
289 },
290 IssueKindMeta {
291 kind: Some(IssueKind::TestOnlyDependency),
292 code: "test-only-dependency",
293 aliases: &[],
294 label: "Test-Only Dependencies",
295 config_key: Some("test-only-dependencies"),
296 filter_flag: Some("--unused-deps"),
297 mcp_issue_type: None,
298 suppress_token: None,
299 suppress_file_level: false,
300 lsp: true,
301 docs_category: "dependency",
302 },
303 IssueKindMeta {
304 kind: Some(IssueKind::DevDependencyInProduction),
305 code: "dev-dependency-in-production",
306 aliases: &[],
307 label: "Dev Dependencies Used in Production",
308 config_key: Some("dev-dependencies-in-production"),
309 filter_flag: Some("--unused-deps"),
310 mcp_issue_type: None,
311 suppress_token: None,
312 suppress_file_level: false,
313 lsp: true,
314 docs_category: "dependency",
315 },
316 IssueKindMeta {
317 kind: Some(IssueKind::CircularDependency),
318 code: "circular-dependency",
319 aliases: &["circular-dependencies"],
320 label: "Circular Dependencies",
321 config_key: Some("circular-dependencies"),
322 filter_flag: Some("--circular-deps"),
323 mcp_issue_type: Some("circular-deps"),
324 suppress_token: Some("circular-dependency"),
325 suppress_file_level: false,
326 lsp: true,
327 docs_category: "architecture",
328 },
329 IssueKindMeta {
330 kind: Some(IssueKind::ReExportCycle),
331 code: "re-export-cycle",
332 aliases: &["re-export-cycles", "reexport-cycle", "reexport-cycles"],
333 label: "Re-Export Cycles",
334 config_key: Some("re-export-cycle"),
335 filter_flag: Some("--re-export-cycles"),
336 mcp_issue_type: Some("re-export-cycles"),
337 suppress_token: Some("re-export-cycle"),
338 suppress_file_level: true,
339 lsp: true,
340 docs_category: "architecture",
341 },
342 IssueKindMeta {
343 kind: Some(IssueKind::BoundaryViolation),
344 code: "boundary-violation",
345 aliases: &[],
346 label: "Boundary Violations",
347 config_key: Some("boundary-violation"),
348 filter_flag: Some("--boundary-violations"),
349 mcp_issue_type: Some("boundary-violations"),
350 suppress_token: Some("boundary-violation"),
351 suppress_file_level: false,
352 lsp: true,
353 docs_category: "architecture",
354 },
355 IssueKindMeta {
356 kind: None,
357 code: "boundary-coverage",
358 aliases: &["boundary-coverage-violations"],
359 label: "Boundary Coverage",
360 config_key: Some("boundary-violation"),
361 filter_flag: Some("--boundary-violations"),
362 mcp_issue_type: None,
363 suppress_token: Some("boundary-violation"),
364 suppress_file_level: true,
365 lsp: false,
366 docs_category: "architecture",
367 },
368 IssueKindMeta {
369 kind: Some(IssueKind::BoundaryViolation),
370 code: "boundary-call-violation",
371 aliases: &["boundary-calls", "boundary-call-violations"],
372 label: "Boundary Call Violations",
373 config_key: Some("boundary-violation"),
374 filter_flag: Some("--boundary-violations"),
375 mcp_issue_type: None,
376 suppress_token: Some("boundary-call-violation"),
377 suppress_file_level: false,
378 lsp: false,
379 docs_category: "architecture",
380 },
381 IssueKindMeta {
382 kind: Some(IssueKind::PolicyViolation),
383 code: "policy-violation",
384 aliases: &["policy-violations"],
385 label: "Policy Violations",
386 config_key: Some("policy-violation"),
387 filter_flag: Some("--policy-violations"),
388 mcp_issue_type: Some("policy-violations"),
389 suppress_token: Some("policy-violation"),
390 suppress_file_level: false,
391 lsp: true,
392 docs_category: "architecture",
393 },
394 IssueKindMeta {
395 kind: Some(IssueKind::InvalidClientExport),
396 code: "invalid-client-export",
397 aliases: &["invalid-client-exports"],
398 label: "Invalid Client Exports",
399 config_key: Some("invalid-client-export"),
400 filter_flag: None,
401 mcp_issue_type: None,
402 suppress_token: Some("invalid-client-export"),
403 suppress_file_level: false,
404 lsp: true,
405 docs_category: "framework",
406 },
407 IssueKindMeta {
408 kind: Some(IssueKind::MixedClientServerBarrel),
409 code: "mixed-client-server-barrel",
410 aliases: &["mixed-client-server-barrels"],
411 label: "Mixed Client/Server Barrels",
412 config_key: Some("mixed-client-server-barrel"),
413 filter_flag: None,
414 mcp_issue_type: None,
415 suppress_token: Some("mixed-client-server-barrel"),
416 suppress_file_level: false,
417 lsp: true,
418 docs_category: "framework",
419 },
420 IssueKindMeta {
421 kind: Some(IssueKind::MisplacedDirective),
422 code: "misplaced-directive",
423 aliases: &["misplaced-directives"],
424 label: "Misplaced Directives",
425 config_key: Some("misplaced-directive"),
426 filter_flag: None,
427 mcp_issue_type: None,
428 suppress_token: Some("misplaced-directive"),
429 suppress_file_level: false,
430 lsp: true,
431 docs_category: "framework",
432 },
433 IssueKindMeta {
434 kind: Some(IssueKind::UnprovidedInject),
435 code: "unprovided-inject",
436 aliases: &["unprovided-injects"],
437 label: "Unprovided Injects",
438 config_key: Some("unprovided-injects"),
439 filter_flag: Some("--unprovided-injects"),
440 mcp_issue_type: Some("unprovided-injects"),
441 suppress_token: Some("unprovided-inject"),
442 suppress_file_level: false,
443 lsp: true,
444 docs_category: "framework",
445 },
446 IssueKindMeta {
447 kind: Some(IssueKind::UnrenderedComponent),
448 code: "unrendered-component",
449 aliases: &["unrendered-components"],
450 label: "Unrendered Components",
451 config_key: Some("unrendered-components"),
452 filter_flag: Some("--unrendered-components"),
453 mcp_issue_type: Some("unrendered-components"),
454 suppress_token: Some("unrendered-component"),
455 suppress_file_level: false,
456 lsp: true,
457 docs_category: "framework",
458 },
459 IssueKindMeta {
460 kind: Some(IssueKind::UnusedComponentProp),
461 code: "unused-component-prop",
462 aliases: &["unused-component-props"],
463 label: "Unused Component Props",
464 config_key: Some("unused-component-props"),
465 filter_flag: Some("--unused-component-props"),
466 mcp_issue_type: Some("unused-component-props"),
467 suppress_token: Some("unused-component-prop"),
468 suppress_file_level: false,
469 lsp: true,
470 docs_category: "framework",
471 },
472 IssueKindMeta {
473 kind: Some(IssueKind::UnusedComponentEmit),
474 code: "unused-component-emit",
475 aliases: &["unused-component-emits"],
476 label: "Unused Component Emits",
477 config_key: Some("unused-component-emits"),
478 filter_flag: Some("--unused-component-emits"),
479 mcp_issue_type: Some("unused-component-emits"),
480 suppress_token: Some("unused-component-emit"),
481 suppress_file_level: false,
482 lsp: true,
483 docs_category: "framework",
484 },
485 IssueKindMeta {
486 kind: Some(IssueKind::UnusedComponentInput),
487 code: "unused-component-input",
488 aliases: &["unused-component-inputs"],
489 label: "Unused Component Inputs",
490 config_key: Some("unused-component-inputs"),
491 filter_flag: Some("--unused-component-inputs"),
492 mcp_issue_type: Some("unused-component-inputs"),
493 suppress_token: Some("unused-component-input"),
494 suppress_file_level: false,
495 lsp: true,
496 docs_category: "framework",
497 },
498 IssueKindMeta {
499 kind: Some(IssueKind::UnusedComponentOutput),
500 code: "unused-component-output",
501 aliases: &["unused-component-outputs"],
502 label: "Unused Component Outputs",
503 config_key: Some("unused-component-outputs"),
504 filter_flag: Some("--unused-component-outputs"),
505 mcp_issue_type: Some("unused-component-outputs"),
506 suppress_token: Some("unused-component-output"),
507 suppress_file_level: false,
508 lsp: true,
509 docs_category: "framework",
510 },
511 IssueKindMeta {
512 kind: Some(IssueKind::UnusedSvelteEvent),
513 code: "unused-svelte-event",
514 aliases: &["unused-svelte-events"],
515 label: "Unused Svelte Events",
516 config_key: Some("unused-svelte-events"),
517 filter_flag: Some("--unused-svelte-events"),
518 mcp_issue_type: Some("unused-svelte-events"),
519 suppress_token: Some("unused-svelte-event"),
520 suppress_file_level: false,
521 lsp: true,
522 docs_category: "framework",
523 },
524 IssueKindMeta {
525 kind: Some(IssueKind::UnusedServerAction),
526 code: "unused-server-action",
527 aliases: &["unused-server-actions"],
528 label: "Unused Server Actions",
529 config_key: Some("unused-server-actions"),
530 filter_flag: Some("--unused-server-actions"),
531 mcp_issue_type: Some("unused-server-actions"),
532 suppress_token: Some("unused-server-action"),
533 suppress_file_level: false,
534 lsp: true,
535 docs_category: "framework",
536 },
537 IssueKindMeta {
538 kind: Some(IssueKind::UnusedLoadDataKey),
539 code: "unused-load-data-key",
540 aliases: &["unused-load-data-keys"],
541 label: "Unused Load Data Keys",
542 config_key: Some("unused-load-data-keys"),
543 filter_flag: Some("--unused-load-data-keys"),
544 mcp_issue_type: Some("unused-load-data-keys"),
545 suppress_token: Some("unused-load-data-key"),
546 suppress_file_level: false,
547 lsp: true,
548 docs_category: "framework",
549 },
550 IssueKindMeta {
551 kind: Some(IssueKind::RouteCollision),
552 code: "route-collision",
553 aliases: &["route-collisions"],
554 label: "Route Collisions",
555 config_key: Some("route-collision"),
556 filter_flag: None,
557 mcp_issue_type: None,
558 suppress_token: Some("route-collision"),
559 suppress_file_level: true,
560 lsp: true,
561 docs_category: "framework",
562 },
563 IssueKindMeta {
564 kind: Some(IssueKind::DynamicSegmentNameConflict),
565 code: "dynamic-segment-name-conflict",
566 aliases: &["dynamic-segment-name-conflicts"],
567 label: "Dynamic Segment Conflicts",
568 config_key: Some("dynamic-segment-name-conflict"),
569 filter_flag: None,
570 mcp_issue_type: None,
571 suppress_token: Some("dynamic-segment-name-conflict"),
572 suppress_file_level: true,
573 lsp: true,
574 docs_category: "framework",
575 },
576 IssueKindMeta {
577 kind: Some(IssueKind::StaleSuppression),
578 code: "stale-suppression",
579 aliases: &[],
580 label: "Stale Suppressions",
581 config_key: Some("stale-suppressions"),
582 filter_flag: Some("--stale-suppressions"),
583 mcp_issue_type: Some("stale-suppressions"),
584 suppress_token: None,
585 suppress_file_level: false,
586 lsp: true,
587 docs_category: "source",
588 },
589 IssueKindMeta {
590 kind: Some(IssueKind::StaleSuppression),
591 code: "missing-suppression-reason",
592 aliases: &["missing-suppression-reasons"],
593 label: "Missing Suppression Reasons",
594 config_key: Some("require-suppression-reason"),
595 filter_flag: Some("--stale-suppressions"),
596 mcp_issue_type: None,
597 suppress_token: None,
598 suppress_file_level: false,
599 lsp: false,
600 docs_category: "source",
601 },
602 IssueKindMeta {
603 kind: Some(IssueKind::PnpmCatalogEntry),
604 code: "unused-catalog-entry",
605 aliases: &["catalog", "unused-catalog-entries"],
606 label: "Unused Catalog Entries",
607 config_key: Some("unused-catalog-entries"),
608 filter_flag: Some("--unused-catalog-entries"),
609 mcp_issue_type: Some("unused-catalog-entries"),
610 suppress_token: None,
611 suppress_file_level: false,
612 lsp: true,
613 docs_category: "dependency",
614 },
615 IssueKindMeta {
616 kind: Some(IssueKind::EmptyCatalogGroup),
617 code: "empty-catalog-group",
618 aliases: &["empty-catalog", "empty-catalog-groups"],
619 label: "Empty Catalog Groups",
620 config_key: Some("empty-catalog-groups"),
621 filter_flag: Some("--empty-catalog-groups"),
622 mcp_issue_type: Some("empty-catalog-groups"),
623 suppress_token: None,
624 suppress_file_level: false,
625 lsp: true,
626 docs_category: "dependency",
627 },
628 IssueKindMeta {
629 kind: Some(IssueKind::UnresolvedCatalogReference),
630 code: "unresolved-catalog-reference",
631 aliases: &["unresolved-catalog", "unresolved-catalog-references"],
632 label: "Unresolved Catalog References",
633 config_key: Some("unresolved-catalog-references"),
634 filter_flag: Some("--unresolved-catalog-references"),
635 mcp_issue_type: Some("unresolved-catalog-references"),
636 suppress_token: None,
637 suppress_file_level: false,
638 lsp: true,
639 docs_category: "dependency",
640 },
641 IssueKindMeta {
642 kind: Some(IssueKind::UnusedDependencyOverride),
643 code: "unused-dependency-override",
644 aliases: &[
645 "unused-dependency-overrides",
646 "unused-override",
647 "unused-overrides",
648 ],
649 label: "Unused Dependency Overrides",
650 config_key: Some("unused-dependency-overrides"),
651 filter_flag: Some("--unused-dependency-overrides"),
652 mcp_issue_type: Some("unused-dependency-overrides"),
653 suppress_token: None,
654 suppress_file_level: false,
655 lsp: true,
656 docs_category: "dependency",
657 },
658 IssueKindMeta {
659 kind: Some(IssueKind::MisconfiguredDependencyOverride),
660 code: "misconfigured-dependency-override",
661 aliases: &[
662 "misconfigured-dependency-overrides",
663 "misconfigured-override",
664 "misconfigured-overrides",
665 ],
666 label: "Misconfigured Dependency Overrides",
667 config_key: Some("misconfigured-dependency-overrides"),
668 filter_flag: Some("--misconfigured-dependency-overrides"),
669 mcp_issue_type: Some("misconfigured-dependency-overrides"),
670 suppress_token: None,
671 suppress_file_level: false,
672 lsp: true,
673 docs_category: "dependency",
674 },
675 IssueKindMeta {
676 kind: Some(IssueKind::SecuritySink),
677 code: "security-sink",
678 aliases: &[],
679 label: "Security Sink Candidates",
680 config_key: Some("security-sink"),
681 filter_flag: None,
682 mcp_issue_type: None,
683 suppress_token: Some("security-sink"),
684 suppress_file_level: false,
685 lsp: true,
686 docs_category: "security",
687 },
688 IssueKindMeta {
689 kind: Some(IssueKind::SecurityClientServerLeak),
690 code: "security-client-server-leak",
691 aliases: &[],
692 label: "Security Client-Server Leaks",
693 config_key: Some("security-client-server-leak"),
694 filter_flag: None,
695 mcp_issue_type: None,
696 suppress_token: Some("security-client-server-leak"),
697 suppress_file_level: true,
698 lsp: true,
699 docs_category: "security",
700 },
701 IssueKindMeta {
702 kind: Some(IssueKind::CoverageGaps),
703 code: "coverage-gaps",
704 aliases: &[],
705 label: "Coverage Gaps",
706 config_key: Some("coverage-gaps"),
707 filter_flag: None,
708 mcp_issue_type: None,
709 suppress_token: Some("coverage-gaps"),
710 suppress_file_level: true,
711 lsp: false,
712 docs_category: "health",
713 },
714 IssueKindMeta {
715 kind: Some(IssueKind::FeatureFlag),
716 code: "feature-flag",
717 aliases: &[],
718 label: "Feature Flags",
719 config_key: Some("feature-flags"),
720 filter_flag: None,
721 mcp_issue_type: None,
722 suppress_token: Some("feature-flag"),
723 suppress_file_level: false,
724 lsp: false,
725 docs_category: "flags",
726 },
727 IssueKindMeta {
728 kind: Some(IssueKind::Complexity),
729 code: "complexity",
730 aliases: &[],
731 label: "Complexity",
732 config_key: None,
733 filter_flag: None,
734 mcp_issue_type: None,
735 suppress_token: Some("complexity"),
736 suppress_file_level: false,
737 lsp: false,
738 docs_category: "health",
739 },
740 IssueKindMeta {
741 kind: Some(IssueKind::PropDrilling),
742 code: "prop-drilling",
743 aliases: &[],
744 label: "Prop Drilling",
745 config_key: Some("prop-drilling"),
746 filter_flag: None,
747 mcp_issue_type: None,
748 suppress_token: Some("prop-drilling"),
749 suppress_file_level: false,
750 lsp: false,
751 docs_category: "source",
752 },
753 IssueKindMeta {
754 kind: Some(IssueKind::ThinWrapper),
755 code: "thin-wrapper",
756 aliases: &["thin-wrappers"],
757 label: "Thin Wrappers",
758 config_key: Some("thin-wrapper"),
759 filter_flag: None,
760 mcp_issue_type: None,
761 suppress_token: Some("thin-wrapper"),
762 suppress_file_level: false,
763 lsp: false,
764 docs_category: "source",
765 },
766 IssueKindMeta {
767 kind: Some(IssueKind::DuplicatePropShape),
768 code: "duplicate-prop-shape",
769 aliases: &["duplicate-prop-shapes"],
770 label: "Duplicate Prop Shapes",
771 config_key: Some("duplicate-prop-shape"),
772 filter_flag: None,
773 mcp_issue_type: None,
774 suppress_token: Some("duplicate-prop-shape"),
775 suppress_file_level: false,
776 lsp: false,
777 docs_category: "source",
778 },
779 IssueKindMeta {
780 kind: Some(IssueKind::CssTokenDrift),
781 code: "css-token-drift",
782 aliases: &[],
783 label: "CSS Token Drift",
784 config_key: Some("css-token-drift"),
785 filter_flag: None,
786 mcp_issue_type: None,
787 suppress_token: Some("css-token-drift"),
788 suppress_file_level: false,
789 lsp: false,
790 docs_category: "health",
791 },
792 IssueKindMeta {
793 kind: Some(IssueKind::CssDuplicateBlock),
794 code: "css-duplicate-block",
795 aliases: &[],
796 label: "CSS Duplicate Block",
797 config_key: Some("css-duplicate-block"),
798 filter_flag: None,
799 mcp_issue_type: None,
800 suppress_token: Some("css-duplicate-block"),
801 suppress_file_level: false,
802 lsp: false,
803 docs_category: "health",
804 },
805 IssueKindMeta {
806 kind: Some(IssueKind::CssSelectorComplexity),
807 code: "css-selector-complexity",
808 aliases: &[],
809 label: "CSS Selector Complexity",
810 config_key: Some("css-selector-complexity"),
811 filter_flag: None,
812 mcp_issue_type: None,
813 suppress_token: Some("css-selector-complexity"),
814 suppress_file_level: false,
815 lsp: false,
816 docs_category: "health",
817 },
818 IssueKindMeta {
819 kind: Some(IssueKind::CssDeadSurface),
820 code: "css-dead-surface",
821 aliases: &[],
822 label: "CSS Dead Surface",
823 config_key: Some("css-dead-surface"),
824 filter_flag: None,
825 mcp_issue_type: None,
826 suppress_token: Some("css-dead-surface"),
827 suppress_file_level: false,
828 lsp: false,
829 docs_category: "health",
830 },
831 IssueKindMeta {
832 kind: Some(IssueKind::CssBrokenReference),
833 code: "css-broken-reference",
834 aliases: &[],
835 label: "CSS Broken Reference",
836 config_key: Some("css-broken-reference"),
837 filter_flag: None,
838 mcp_issue_type: None,
839 suppress_token: Some("css-broken-reference"),
840 suppress_file_level: false,
841 lsp: false,
842 docs_category: "health",
843 },
844];
845
846#[derive(Debug, Clone, Copy, PartialEq, Eq)]
848#[non_exhaustive]
849pub struct IssueResultMeta {
850 pub code: &'static str,
852 pub sarif_description: &'static str,
854 pub meta_description: &'static str,
856 pub meta_docs_path: &'static str,
858 pub meta_name: &'static str,
860 pub summary_label: &'static str,
862 pub docs_anchor: &'static str,
864 pub result_key: &'static str,
866 pub counts_in_total: bool,
868}
869
870#[derive(Debug, Clone, Copy, PartialEq, Eq)]
872pub struct TsAliasMeta {
873 pub name: &'static str,
875 pub parent: &'static str,
877}
878
879#[derive(Debug, Clone, Copy, PartialEq, Eq)]
881pub struct IssueTsAliasMeta {
882 pub code: &'static str,
884 pub alias: TsAliasMeta,
886}
887
888pub const ISSUE_TS_ALIAS_META: &[IssueTsAliasMeta] = &[
890 IssueTsAliasMeta {
891 code: "unused-file",
892 alias: TsAliasMeta {
893 name: "UnusedFile",
894 parent: "UnusedFileFinding",
895 },
896 },
897 IssueTsAliasMeta {
898 code: "unused-export",
899 alias: TsAliasMeta {
900 name: "UnusedExport",
901 parent: "UnusedExportFinding",
902 },
903 },
904 IssueTsAliasMeta {
905 code: "private-type-leak",
906 alias: TsAliasMeta {
907 name: "PrivateTypeLeak",
908 parent: "PrivateTypeLeakFinding",
909 },
910 },
911 IssueTsAliasMeta {
912 code: "deprecated-export-in-use",
913 alias: TsAliasMeta {
914 name: "DeprecatedExportInUse",
915 parent: "DeprecatedExportInUseFinding",
916 },
917 },
918 IssueTsAliasMeta {
919 code: "unused-dependency",
920 alias: TsAliasMeta {
921 name: "UnusedDependency",
922 parent: "UnusedDependencyFinding",
923 },
924 },
925 IssueTsAliasMeta {
926 code: "unused-dev-dependency",
927 alias: TsAliasMeta {
928 name: "UnusedDependency",
929 parent: "UnusedDevDependencyFinding",
930 },
931 },
932 IssueTsAliasMeta {
933 code: "unused-optional-dependency",
934 alias: TsAliasMeta {
935 name: "UnusedDependency",
936 parent: "UnusedOptionalDependencyFinding",
937 },
938 },
939 IssueTsAliasMeta {
940 code: "unused-enum-member",
941 alias: TsAliasMeta {
942 name: "UnusedMember",
943 parent: "UnusedEnumMemberFinding",
944 },
945 },
946 IssueTsAliasMeta {
947 code: "unused-class-member",
948 alias: TsAliasMeta {
949 name: "UnusedMember",
950 parent: "UnusedClassMemberFinding",
951 },
952 },
953 IssueTsAliasMeta {
954 code: "unused-store-member",
955 alias: TsAliasMeta {
956 name: "UnusedMember",
957 parent: "UnusedStoreMemberFinding",
958 },
959 },
960 IssueTsAliasMeta {
961 code: "unresolved-import",
962 alias: TsAliasMeta {
963 name: "UnresolvedImport",
964 parent: "UnresolvedImportFinding",
965 },
966 },
967 IssueTsAliasMeta {
968 code: "unlisted-dependency",
969 alias: TsAliasMeta {
970 name: "UnlistedDependency",
971 parent: "UnlistedDependencyFinding",
972 },
973 },
974 IssueTsAliasMeta {
975 code: "duplicate-export",
976 alias: TsAliasMeta {
977 name: "DuplicateExport",
978 parent: "DuplicateExportFinding",
979 },
980 },
981 IssueTsAliasMeta {
982 code: "type-only-dependency",
983 alias: TsAliasMeta {
984 name: "TypeOnlyDependency",
985 parent: "TypeOnlyDependencyFinding",
986 },
987 },
988 IssueTsAliasMeta {
989 code: "test-only-dependency",
990 alias: TsAliasMeta {
991 name: "TestOnlyDependency",
992 parent: "TestOnlyDependencyFinding",
993 },
994 },
995 IssueTsAliasMeta {
996 code: "dev-dependency-in-production",
997 alias: TsAliasMeta {
998 name: "DevDependencyInProduction",
999 parent: "DevDependencyInProductionFinding",
1000 },
1001 },
1002 IssueTsAliasMeta {
1003 code: "circular-dependency",
1004 alias: TsAliasMeta {
1005 name: "CircularDependency",
1006 parent: "CircularDependencyFinding",
1007 },
1008 },
1009 IssueTsAliasMeta {
1010 code: "re-export-cycle",
1011 alias: TsAliasMeta {
1012 name: "ReExportCycle",
1013 parent: "ReExportCycleFinding",
1014 },
1015 },
1016 IssueTsAliasMeta {
1017 code: "boundary-violation",
1018 alias: TsAliasMeta {
1019 name: "BoundaryViolation",
1020 parent: "BoundaryViolationFinding",
1021 },
1022 },
1023 IssueTsAliasMeta {
1024 code: "unused-catalog-entry",
1025 alias: TsAliasMeta {
1026 name: "UnusedCatalogEntry",
1027 parent: "UnusedCatalogEntryFinding",
1028 },
1029 },
1030 IssueTsAliasMeta {
1031 code: "empty-catalog-group",
1032 alias: TsAliasMeta {
1033 name: "EmptyCatalogGroup",
1034 parent: "EmptyCatalogGroupFinding",
1035 },
1036 },
1037 IssueTsAliasMeta {
1038 code: "unresolved-catalog-reference",
1039 alias: TsAliasMeta {
1040 name: "UnresolvedCatalogReference",
1041 parent: "UnresolvedCatalogReferenceFinding",
1042 },
1043 },
1044 IssueTsAliasMeta {
1045 code: "unused-dependency-override",
1046 alias: TsAliasMeta {
1047 name: "UnusedDependencyOverride",
1048 parent: "UnusedDependencyOverrideFinding",
1049 },
1050 },
1051 IssueTsAliasMeta {
1052 code: "misconfigured-dependency-override",
1053 alias: TsAliasMeta {
1054 name: "MisconfiguredDependencyOverride",
1055 parent: "MisconfiguredDependencyOverrideFinding",
1056 },
1057 },
1058];
1059
1060pub const ISSUE_RESULT_META: &[IssueResultMeta] = &[
1062 IssueResultMeta {
1063 code: "unused-file",
1064 sarif_description: "File is not reachable from any entry point",
1065 meta_description: "Source files that are not imported by any other module and are not entry points. Detection uses graph reachability from configured entry points.",
1066 meta_docs_path: "explanations/dead-code#unused-files",
1067 meta_name: "Unused Files",
1068 summary_label: "Unused files",
1069 docs_anchor: "unused-files",
1070 result_key: "unused_files",
1071 counts_in_total: true,
1072 },
1073 IssueResultMeta {
1074 code: "unused-export",
1075 sarif_description: "Export is never imported",
1076 meta_description: "Named exports that are never imported by any other module in the project, including direct exports and re-exports through barrel files.",
1077 meta_docs_path: "explanations/dead-code#unused-exports",
1078 meta_name: "Unused Exports",
1079 summary_label: "Unused exports",
1080 docs_anchor: "unused-exports",
1081 result_key: "unused_exports",
1082 counts_in_total: true,
1083 },
1084 IssueResultMeta {
1085 code: "unused-type",
1086 sarif_description: "Type export is never imported",
1087 meta_description: "Type-only exports that are never imported. These do not generate runtime code but add maintenance burden.",
1088 meta_docs_path: "explanations/dead-code#unused-types",
1089 meta_name: "Unused Type Exports",
1090 summary_label: "Unused types",
1091 docs_anchor: "unused-types",
1092 result_key: "unused_types",
1093 counts_in_total: true,
1094 },
1095 IssueResultMeta {
1096 code: "private-type-leak",
1097 sarif_description: "Exported signature references a private type",
1098 meta_description: "Exported values or types whose public TypeScript signature references a same-file type declaration that is not exported.",
1099 meta_docs_path: "explanations/dead-code#private-type-leaks",
1100 meta_name: "Private Type Leaks",
1101 summary_label: "Private type leaks",
1102 docs_anchor: "private-type-leaks",
1103 result_key: "private_type_leaks",
1104 counts_in_total: true,
1105 },
1106 IssueResultMeta {
1107 code: "deprecated-export-in-use",
1108 sarif_description: "Export marked @deprecated is still referenced",
1109 meta_description: "Exports whose leading JSDoc carries @deprecated and that still have at least one consumer in a reachable file. Each finding lists the consumer count and a capped consumer sample.",
1110 meta_docs_path: "explanations/dead-code#deprecated-exports-in-use",
1111 meta_name: "Deprecated Exports in Use",
1112 summary_label: "Deprecated exports in use",
1113 docs_anchor: "deprecated-exports-in-use",
1114 result_key: "deprecated_exports_in_use",
1115 counts_in_total: true,
1116 },
1117 IssueResultMeta {
1118 code: "unused-dependency",
1119 sarif_description: "Dependency listed but never imported",
1120 meta_description: "Packages listed in dependencies that are never imported or required by any source file.",
1121 meta_docs_path: "explanations/dead-code#unused-dependencies",
1122 meta_name: "Unused Dependencies",
1123 summary_label: "Unused dependencies",
1124 docs_anchor: "unused-dependencies",
1125 result_key: "unused_dependencies",
1126 counts_in_total: true,
1127 },
1128 IssueResultMeta {
1129 code: "unused-dev-dependency",
1130 sarif_description: "Dev dependency listed but never imported",
1131 meta_description: "Packages listed in devDependencies that are never imported by test files, config files, or scripts.",
1132 meta_docs_path: "explanations/dead-code#unused-devdependencies",
1133 meta_name: "Unused Dev Dependencies",
1134 summary_label: "Unused devDependencies",
1135 docs_anchor: "unused-dependencies",
1136 result_key: "unused_dev_dependencies",
1137 counts_in_total: true,
1138 },
1139 IssueResultMeta {
1140 code: "unused-optional-dependency",
1141 sarif_description: "Optional dependency listed but never imported",
1142 meta_description: "Packages listed in optionalDependencies that are never imported.",
1143 meta_docs_path: "explanations/dead-code#unused-optionaldependencies",
1144 meta_name: "Unused Optional Dependencies",
1145 summary_label: "Unused optionalDependencies",
1146 docs_anchor: "unused-dependencies",
1147 result_key: "unused_optional_dependencies",
1148 counts_in_total: true,
1149 },
1150 IssueResultMeta {
1151 code: "unused-enum-member",
1152 sarif_description: "Enum member is never referenced",
1153 meta_description: "Enum members that are never referenced in the codebase.",
1154 meta_docs_path: "explanations/dead-code#unused-enum-members",
1155 meta_name: "Unused Enum Members",
1156 summary_label: "Unused enum members",
1157 docs_anchor: "unused-enum-members",
1158 result_key: "unused_enum_members",
1159 counts_in_total: true,
1160 },
1161 IssueResultMeta {
1162 code: "unused-class-member",
1163 sarif_description: "Class member is never referenced",
1164 meta_description: "Class methods and properties that are never referenced outside the class.",
1165 meta_docs_path: "explanations/dead-code#unused-class-members",
1166 meta_name: "Unused Class Members",
1167 summary_label: "Unused class members",
1168 docs_anchor: "unused-class-members",
1169 result_key: "unused_class_members",
1170 counts_in_total: true,
1171 },
1172 IssueResultMeta {
1173 code: "unused-store-member",
1174 sarif_description: "Store member is never accessed by any consumer",
1175 meta_description: "Pinia store members declared but never accessed by any consumer project-wide.",
1176 meta_docs_path: "explanations/dead-code#unused-store-members",
1177 meta_name: "Unused Store Members",
1178 summary_label: "Unused store members",
1179 docs_anchor: "unused-store-members",
1180 result_key: "unused_store_members",
1181 counts_in_total: true,
1182 },
1183 IssueResultMeta {
1184 code: "unresolved-import",
1185 sarif_description: "Import could not be resolved",
1186 meta_description: "Import specifiers that could not be resolved to a file on disk.",
1187 meta_docs_path: "explanations/dead-code#unresolved-imports",
1188 meta_name: "Unresolved Imports",
1189 summary_label: "Unresolved imports",
1190 docs_anchor: "unresolved-imports",
1191 result_key: "unresolved_imports",
1192 counts_in_total: true,
1193 },
1194 IssueResultMeta {
1195 code: "unlisted-dependency",
1196 sarif_description: "Dependency used but not in package.json",
1197 meta_description: "Packages imported in source code but not listed in package.json.",
1198 meta_docs_path: "explanations/dead-code#unlisted-dependencies",
1199 meta_name: "Unlisted Dependencies",
1200 summary_label: "Unlisted dependencies",
1201 docs_anchor: "unlisted-dependencies",
1202 result_key: "unlisted_dependencies",
1203 counts_in_total: true,
1204 },
1205 IssueResultMeta {
1206 code: "duplicate-export",
1207 sarif_description: "Export name appears in multiple modules",
1208 meta_description: "The same export name is defined in multiple modules.",
1209 meta_docs_path: "explanations/dead-code#duplicate-exports",
1210 meta_name: "Duplicate Exports",
1211 summary_label: "Duplicate exports",
1212 docs_anchor: "duplicate-exports",
1213 result_key: "duplicate_exports",
1214 counts_in_total: true,
1215 },
1216 IssueResultMeta {
1217 code: "type-only-dependency",
1218 sarif_description: "Production dependency only used via type-only imports",
1219 meta_description: "Production dependencies that are only imported via type-only imports.",
1220 meta_docs_path: "explanations/dead-code#type-only-dependencies",
1221 meta_name: "Type-only Dependencies",
1222 summary_label: "Type-only dependencies",
1223 docs_anchor: "type-only-dependencies",
1224 result_key: "type_only_dependencies",
1225 counts_in_total: true,
1226 },
1227 IssueResultMeta {
1228 code: "test-only-dependency",
1229 sarif_description: "Production dependency only imported by test files",
1230 meta_description: "Production dependencies that are only imported from test files.",
1231 meta_docs_path: "explanations/dead-code#test-only-dependencies",
1232 meta_name: "Test-only Dependencies",
1233 summary_label: "Test-only dependencies",
1234 docs_anchor: "test-only-dependencies",
1235 result_key: "test_only_dependencies",
1236 counts_in_total: true,
1237 },
1238 IssueResultMeta {
1239 code: "dev-dependency-in-production",
1240 sarif_description: "devDependency imported by production code with a runtime import",
1241 meta_description: "devDependencies imported by production source code via a runtime/value import; they should be promoted to dependencies.",
1242 meta_docs_path: "explanations/dead-code#dev-dependencies-in-production",
1243 meta_name: "Dev Dependencies Used in Production",
1244 summary_label: "Dev dependencies used in production",
1245 docs_anchor: "dev-dependencies-in-production",
1246 result_key: "dev_dependencies_in_production",
1247 counts_in_total: true,
1248 },
1249 IssueResultMeta {
1250 code: "circular-dependency",
1251 sarif_description: "Circular dependency chain detected",
1252 meta_description: "A cycle in the module import graph.",
1253 meta_docs_path: "explanations/dead-code#circular-dependencies",
1254 meta_name: "Circular Dependencies",
1255 summary_label: "Circular dependencies",
1256 docs_anchor: "circular-dependencies",
1257 result_key: "circular_dependencies",
1258 counts_in_total: true,
1259 },
1260 IssueResultMeta {
1261 code: "re-export-cycle",
1262 sarif_description: "Two or more barrel files re-export from each other in a loop",
1263 meta_description: "A barrel file re-exports from another barrel that ultimately re-exports back.",
1264 meta_docs_path: "explanations/dead-code#re-export-cycles",
1265 meta_name: "Re-Export Cycles",
1266 summary_label: "Re-export cycles",
1267 docs_anchor: "re-export-cycles",
1268 result_key: "re_export_cycles",
1269 counts_in_total: true,
1270 },
1271 IssueResultMeta {
1272 code: "boundary-violation",
1273 sarif_description: "Import crosses a configured architecture boundary",
1274 meta_description: "A module imports from a zone that its configured boundary rules do not allow.",
1275 meta_docs_path: "explanations/dead-code#boundary-violations",
1276 meta_name: "Boundary Violations",
1277 summary_label: "Boundary violations",
1278 docs_anchor: "boundary-violations",
1279 result_key: "boundary_violations",
1280 counts_in_total: true,
1281 },
1282 IssueResultMeta {
1283 code: "boundary-coverage",
1284 sarif_description: "Source file matches no configured architecture boundary zone",
1285 meta_description: "A reachable source file is not assigned to any configured boundary zone while boundary coverage is required.",
1286 meta_docs_path: "explanations/dead-code#boundary-violations",
1287 meta_name: "Boundary Coverage",
1288 summary_label: "Boundary coverage",
1289 docs_anchor: "boundary-violations",
1290 result_key: "boundary_coverage_violations",
1291 counts_in_total: true,
1292 },
1293 IssueResultMeta {
1294 code: "boundary-call-violation",
1295 sarif_description: "Zoned file calls a callee its zone forbids",
1296 meta_description: "A file classified into a boundary zone calls a callee matching one of the zone's forbidden call patterns.",
1297 meta_docs_path: "explanations/dead-code#boundary-violations",
1298 meta_name: "Boundary Call Violation",
1299 summary_label: "Boundary calls",
1300 docs_anchor: "boundary-violations",
1301 result_key: "boundary_call_violations",
1302 counts_in_total: true,
1303 },
1304 IssueResultMeta {
1305 code: "policy-violation",
1306 sarif_description: "Banned usage matched a rule-pack rule",
1307 meta_description: "A call site, import, or catalogue-derived effect matched a configured rule pack rule.",
1308 meta_docs_path: "explanations/dead-code#policy-violations",
1309 meta_name: "Policy Violation",
1310 summary_label: "Policy violations",
1311 docs_anchor: "policy-violations",
1312 result_key: "policy_violations",
1313 counts_in_total: true,
1314 },
1315 IssueResultMeta {
1316 code: "invalid-client-export",
1317 sarif_description: "\"use client\" file exports a server-only / route-config name",
1318 meta_description: "A file carrying the use client directive also exports a Next.js server-only or route-segment config name.",
1319 meta_docs_path: "explanations/dead-code#invalid-client-exports",
1320 meta_name: "Invalid client export",
1321 summary_label: "Invalid client exports",
1322 docs_anchor: "invalid-client-exports",
1323 result_key: "invalid_client_exports",
1324 counts_in_total: true,
1325 },
1326 IssueResultMeta {
1327 code: "mixed-client-server-barrel",
1328 sarif_description: "Barrel re-exports both a \"use client\" module and a server-only module",
1329 meta_description: "A barrel file forwards a name from a use client module alongside a name from a server-only module.",
1330 meta_docs_path: "explanations/dead-code#mixed-client-server-barrels",
1331 meta_name: "Mixed client/server barrel",
1332 summary_label: "Mixed client/server barrels",
1333 docs_anchor: "mixed-client-server-barrels",
1334 result_key: "mixed_client_server_barrels",
1335 counts_in_total: true,
1336 },
1337 IssueResultMeta {
1338 code: "misplaced-directive",
1339 sarif_description: "\"use client\" / \"use server\" directive is not in the leading position and is ignored",
1340 meta_description: "A use client or use server directive string appears after a non-directive statement and is ignored.",
1341 meta_docs_path: "explanations/dead-code#misplaced-directives",
1342 meta_name: "Misplaced directive",
1343 summary_label: "Misplaced directives",
1344 docs_anchor: "misplaced-directives",
1345 result_key: "misplaced_directives",
1346 counts_in_total: true,
1347 },
1348 IssueResultMeta {
1349 code: "unprovided-inject",
1350 sarif_description: "inject() / getContext() reads a key that no provide() / setContext() supplies",
1351 meta_description: "A Vue inject or Svelte getContext reads a dependency-injection key that no matching provider supplies.",
1352 meta_docs_path: "explanations/dead-code#unprovided-injects",
1353 meta_name: "Unprovided injects",
1354 summary_label: "Unprovided injects",
1355 docs_anchor: "unprovided-inject",
1356 result_key: "unprovided_injects",
1357 counts_in_total: true,
1358 },
1359 IssueResultMeta {
1360 code: "unrendered-component",
1361 sarif_description: "A Vue / Svelte component is reachable through a barrel but rendered nowhere",
1362 meta_description: "A Vue or Svelte single-file component is reachable through the graph but rendered nowhere in the project.",
1363 meta_docs_path: "explanations/dead-code#unrendered-components",
1364 meta_name: "Unrendered components",
1365 summary_label: "Unrendered components",
1366 docs_anchor: "unrendered-component",
1367 result_key: "unrendered_components",
1368 counts_in_total: true,
1369 },
1370 IssueResultMeta {
1371 code: "unused-component-prop",
1372 sarif_description: "A Vue, Svelte, or React component prop is referenced nowhere in its own component",
1373 meta_description: "A declared Vue, Svelte, React, or Preact component prop is referenced nowhere inside its own component.",
1374 meta_docs_path: "explanations/dead-code#unused-component-props",
1375 meta_name: "Unused component props",
1376 summary_label: "Unused component props",
1377 docs_anchor: "unused-component-prop",
1378 result_key: "unused_component_props",
1379 counts_in_total: true,
1380 },
1381 IssueResultMeta {
1382 code: "unused-component-emit",
1383 sarif_description: "A Vue <script setup> defineEmits event is emitted nowhere in its own component",
1384 meta_description: "A Vue script setup defineEmits event is emitted nowhere in its own component.",
1385 meta_docs_path: "explanations/dead-code#unused-component-emits",
1386 meta_name: "Unused component emits",
1387 summary_label: "Unused component emits",
1388 docs_anchor: "unused-component-emit",
1389 result_key: "unused_component_emits",
1390 counts_in_total: true,
1391 },
1392 IssueResultMeta {
1393 code: "unused-component-input",
1394 sarif_description: "An Angular @Input() / signal input() / model() input is read nowhere in its own component",
1395 meta_description: "An Angular input is read nowhere in its own component.",
1396 meta_docs_path: "explanations/dead-code#unused-component-inputs",
1397 meta_name: "Unused component inputs",
1398 summary_label: "Unused component inputs",
1399 docs_anchor: "unused-component-input",
1400 result_key: "unused_component_inputs",
1401 counts_in_total: true,
1402 },
1403 IssueResultMeta {
1404 code: "unused-component-output",
1405 sarif_description: "An Angular @Output() / signal output() output is emitted nowhere in its own component",
1406 meta_description: "An Angular output is emitted nowhere in its own component.",
1407 meta_docs_path: "explanations/dead-code#unused-component-outputs",
1408 meta_name: "Unused component outputs",
1409 summary_label: "Unused component outputs",
1410 docs_anchor: "unused-component-output",
1411 result_key: "unused_component_outputs",
1412 counts_in_total: true,
1413 },
1414 IssueResultMeta {
1415 code: "unused-svelte-event",
1416 sarif_description: "A Svelte component dispatches a createEventDispatcher event whose name is listened to nowhere in the project",
1417 meta_description: "A Svelte component dispatches a custom event whose name is listened to nowhere in the analyzed project.",
1418 meta_docs_path: "explanations/dead-code#unused-svelte-events",
1419 meta_name: "Unused Svelte events",
1420 summary_label: "Unused Svelte events",
1421 docs_anchor: "unused-svelte-event",
1422 result_key: "unused_svelte_events",
1423 counts_in_total: true,
1424 },
1425 IssueResultMeta {
1426 code: "unused-server-action",
1427 sarif_description: "A Next.js Server Action exported from a \"use server\" file is referenced by no code in the project",
1428 meta_description: "A Next.js Server Action exported from a use server file is referenced by no code in the project.",
1429 meta_docs_path: "explanations/dead-code#unused-server-actions",
1430 meta_name: "Unused server actions",
1431 summary_label: "Unused server actions",
1432 docs_anchor: "unused-server-action",
1433 result_key: "unused_server_actions",
1434 counts_in_total: true,
1435 },
1436 IssueResultMeta {
1437 code: "unused-load-data-key",
1438 sarif_description: "A SvelteKit load() return-object key is read by no consumer",
1439 meta_description: "A SvelteKit load return-object key is read by no route or project-wide consumer.",
1440 meta_docs_path: "explanations/dead-code#unused-load-data-keys",
1441 meta_name: "Unused load data keys",
1442 summary_label: "Unused load data keys",
1443 docs_anchor: "unused-load-data-key",
1444 result_key: "unused_load_data_keys",
1445 counts_in_total: true,
1446 },
1447 IssueResultMeta {
1448 code: "route-collision",
1449 sarif_description: "Two or more Next.js App Router route files resolve to the same URL",
1450 meta_description: "Two or more Next.js App Router route files resolve to the same URL within one app root.",
1451 meta_docs_path: "explanations/dead-code#route-collisions",
1452 meta_name: "Route collision",
1453 summary_label: "Route collisions",
1454 docs_anchor: "route-collisions",
1455 result_key: "route_collisions",
1456 counts_in_total: true,
1457 },
1458 IssueResultMeta {
1459 code: "dynamic-segment-name-conflict",
1460 sarif_description: "Sibling Next.js dynamic route segments use different slug names at the same position",
1461 meta_description: "Sibling Next.js dynamic route segments use different slug names at the same position.",
1462 meta_docs_path: "explanations/dead-code#dynamic-segment-name-conflicts",
1463 meta_name: "Dynamic segment name conflict",
1464 summary_label: "Dynamic segment conflicts",
1465 docs_anchor: "dynamic-segment-name-conflicts",
1466 result_key: "dynamic_segment_name_conflicts",
1467 counts_in_total: true,
1468 },
1469 IssueResultMeta {
1470 code: "stale-suppression",
1471 sarif_description: "Suppression comment or tag no longer matches any issue",
1472 meta_description: "A fallow suppression comment or tag no longer matches any active issue.",
1473 meta_docs_path: "explanations/dead-code#stale-suppressions",
1474 meta_name: "Stale Suppressions",
1475 summary_label: "Stale suppressions",
1476 docs_anchor: "stale-suppressions",
1477 result_key: "stale_suppressions",
1478 counts_in_total: true,
1479 },
1480 IssueResultMeta {
1481 code: "unused-catalog-entry",
1482 sarif_description: "Catalog entry not referenced by any workspace package",
1483 meta_description: "A package manager catalog entry is not referenced by any workspace package.json.",
1484 meta_docs_path: "explanations/dead-code#unused-catalog-entries",
1485 meta_name: "Unused catalog entry",
1486 summary_label: "Unused catalog entries",
1487 docs_anchor: "unused-catalog-entries",
1488 result_key: "unused_catalog_entries",
1489 counts_in_total: true,
1490 },
1491 IssueResultMeta {
1492 code: "empty-catalog-group",
1493 sarif_description: "Named catalog group has no entries",
1494 meta_description: "A named package manager catalog group has no package entries.",
1495 meta_docs_path: "explanations/dead-code#empty-catalog-groups",
1496 meta_name: "Empty catalog group",
1497 summary_label: "Empty catalog groups",
1498 docs_anchor: "empty-catalog-groups",
1499 result_key: "empty_catalog_groups",
1500 counts_in_total: true,
1501 },
1502 IssueResultMeta {
1503 code: "unresolved-catalog-reference",
1504 sarif_description: "package.json references a catalog that does not declare the package",
1505 meta_description: "A workspace package.json uses a catalog protocol reference that no catalog declares.",
1506 meta_docs_path: "explanations/dead-code#unresolved-catalog-references",
1507 meta_name: "Unresolved catalog reference",
1508 summary_label: "Unresolved catalog references",
1509 docs_anchor: "unresolved-catalog-references",
1510 result_key: "unresolved_catalog_references",
1511 counts_in_total: true,
1512 },
1513 IssueResultMeta {
1514 code: "unused-dependency-override",
1515 sarif_description: "Package-manager override target is not declared or resolved",
1516 meta_description: "A dependency override targets a package not declared by any workspace package and not present in the active readable lockfile.",
1517 meta_docs_path: "explanations/dead-code#unused-dependency-overrides",
1518 meta_name: "Unused dependency override",
1519 summary_label: "Unused dependency overrides",
1520 docs_anchor: "unused-dependency-overrides",
1521 result_key: "unused_dependency_overrides",
1522 counts_in_total: true,
1523 },
1524 IssueResultMeta {
1525 code: "misconfigured-dependency-override",
1526 sarif_description: "Package-manager override has an unparsable key or value",
1527 meta_description: "A dependency override key or value does not parse according to its package manager's grammar.",
1528 meta_docs_path: "explanations/dead-code#misconfigured-dependency-overrides",
1529 meta_name: "Misconfigured dependency override",
1530 summary_label: "Misconfigured dependency overrides",
1531 docs_anchor: "misconfigured-dependency-overrides",
1532 result_key: "misconfigured_dependency_overrides",
1533 counts_in_total: true,
1534 },
1535 IssueResultMeta {
1536 code: "prop-drilling",
1537 sarif_description: "A React/Preact prop is forwarded unchanged through 3+ pass-through components to a distant consumer",
1538 meta_description: "A React or Preact prop is forwarded unchanged through multiple pass-through components to a distant consumer.",
1539 meta_docs_path: "explanations/dead-code#prop-drilling",
1540 meta_name: "Prop drilling",
1541 summary_label: "Prop drilling",
1542 docs_anchor: "prop-drilling",
1543 result_key: "prop_drilling_chains",
1544 counts_in_total: false,
1545 },
1546 IssueResultMeta {
1547 code: "thin-wrapper",
1548 sarif_description: "A React/Preact component whose whole body is a single spread-forwarded child render (a candidate for inlining)",
1549 meta_description: "A React or Preact component is structural indirection around a single spread-forwarded child render.",
1550 meta_docs_path: "explanations/dead-code#thin-wrapper",
1551 meta_name: "Thin wrapper",
1552 summary_label: "Thin wrappers",
1553 docs_anchor: "thin-wrapper",
1554 result_key: "thin_wrappers",
1555 counts_in_total: false,
1556 },
1557 IssueResultMeta {
1558 code: "duplicate-prop-shape",
1559 sarif_description: "Three or more React/Preact components across two or more files declare an identical prop-name set (a missing shared Props type)",
1560 meta_description: "Multiple React or Preact components declare an identical significant prop-name set.",
1561 meta_docs_path: "explanations/dead-code#duplicate-prop-shape",
1562 meta_name: "Duplicate prop shape",
1563 summary_label: "Duplicate prop shapes",
1564 docs_anchor: "duplicate-prop-shape",
1565 result_key: "duplicate_prop_shapes",
1566 counts_in_total: false,
1567 },
1568];
1569
1570pub static KNOWN_ISSUE_KIND_NAMES: LazyLock<Vec<&'static str>> =
1572 LazyLock::new(known_issue_kind_names_from_meta);
1573
1574pub static DEAD_CODE_FILTER_FLAGS: LazyLock<Vec<&'static str>> =
1576 LazyLock::new(dead_code_filter_flags_from_meta);
1577
1578pub static MCP_ISSUE_TYPE_FLAGS: LazyLock<Vec<(&'static str, &'static str)>> =
1580 LazyLock::new(mcp_issue_type_flags_from_meta);
1581
1582pub static CODECLIMATE_RESULT_CODES: LazyLock<Vec<&'static str>> =
1584 LazyLock::new(codeclimate_result_codes_from_meta);
1585
1586fn known_issue_kind_names_from_meta() -> Vec<&'static str> {
1587 let mut names = Vec::new();
1588 for meta in ISSUE_KIND_META.iter().filter(|meta| meta.kind.is_some()) {
1589 push_unique(&mut names, meta.code);
1590 for alias in meta.aliases {
1591 push_unique(&mut names, *alias);
1592 }
1593 }
1594 names
1595}
1596
1597fn dead_code_filter_flags_from_meta() -> Vec<&'static str> {
1598 let mut flags = Vec::new();
1599 for meta in ISSUE_KIND_META {
1600 if let Some(flag) = meta.filter_flag {
1601 push_unique(&mut flags, flag);
1602 }
1603 }
1604 flags
1605}
1606
1607fn mcp_issue_type_flags_from_meta() -> Vec<(&'static str, &'static str)> {
1608 ISSUE_KIND_META
1609 .iter()
1610 .filter_map(|meta| meta.mcp_pair())
1611 .collect()
1612}
1613
1614fn codeclimate_result_codes_from_meta() -> Vec<&'static str> {
1615 ISSUE_RESULT_META
1616 .iter()
1617 .filter(|meta| meta.counts_in_total)
1618 .map(|meta| meta.code)
1619 .collect()
1620}
1621
1622fn push_unique<T: Copy + PartialEq>(items: &mut Vec<T>, item: T) {
1623 if !items.contains(&item) {
1624 items.push(item);
1625 }
1626}
1627
1628#[must_use]
1630pub fn issue_meta_by_code(code: &str) -> Option<&'static IssueKindMeta> {
1631 ISSUE_KIND_META.iter().find(|meta| meta.code == code)
1632}
1633
1634#[must_use]
1636pub fn issue_meta_for_token(token: &str) -> Option<&'static IssueKindMeta> {
1637 ISSUE_KIND_META
1638 .iter()
1639 .find(|meta| meta.code == token || meta.aliases.contains(&token))
1640}
1641
1642#[must_use]
1645pub fn issue_meta_for_contract_token(token: &str) -> Option<&'static IssueKindMeta> {
1646 let normalized = token.trim().trim_start_matches("--").replace('_', "-");
1647 ISSUE_KIND_META
1648 .iter()
1649 .find(|meta| issue_meta_matches_contract_token(meta, &normalized))
1650}
1651
1652#[must_use]
1654pub fn issue_meta_matches_contract_token(meta: &IssueKindMeta, token: &str) -> bool {
1655 let normalized = token.trim().trim_start_matches("--").replace('_', "-");
1656 meta.code == normalized
1657 || meta.aliases.contains(&normalized.as_str())
1658 || meta.config_key == Some(normalized.as_str())
1659 || meta.mcp_issue_type == Some(normalized.as_str())
1660 || meta.suppress_token == Some(normalized.as_str())
1661 || meta.filter_flag.map(|flag| flag.trim_start_matches("--")) == Some(normalized.as_str())
1662}
1663
1664#[must_use]
1666pub fn issue_meta_by_kind(kind: IssueKind) -> Option<&'static IssueKindMeta> {
1667 ISSUE_KIND_META.iter().find(|meta| meta.kind == Some(kind))
1668}
1669
1670#[must_use]
1672pub fn issue_result_meta_by_code(code: &str) -> Option<&'static IssueResultMeta> {
1673 ISSUE_RESULT_META.iter().find(|meta| meta.code == code)
1674}
1675
1676#[must_use]
1678pub fn issue_sarif_rule_ids(code: &str) -> Vec<String> {
1679 let mut ids = vec![format!("fallow/{code}")];
1680 if code == "stale-suppression" {
1681 ids.push("fallow/missing-suppression-reason".to_string());
1682 }
1683 ids
1684}
1685
1686#[must_use]
1688pub fn issue_sarif_rule_description(rule_id: &str) -> Option<&'static str> {
1689 let code = rule_id.strip_prefix("fallow/")?;
1690 if code == "missing-suppression-reason" {
1691 return Some("Suppression comment omits a required reason");
1692 }
1693 issue_result_meta_by_code(code).map(|meta| meta.sarif_description)
1694}
1695
1696#[must_use]
1698pub fn issue_codeclimate_check_names(code: &str) -> Vec<String> {
1699 if !CODECLIMATE_RESULT_CODES.contains(&code) {
1700 return Vec::new();
1701 }
1702 issue_sarif_rule_ids(code)
1703}
1704
1705#[must_use]
1708pub fn issue_docs_anchor(code: &str) -> Option<&'static str> {
1709 issue_result_meta_by_code(code).map(|meta| meta.docs_anchor)
1710}
1711
1712#[must_use]
1714pub fn issue_ts_alias(code: &str) -> Option<TsAliasMeta> {
1715 ISSUE_TS_ALIAS_META
1716 .iter()
1717 .find(|meta| meta.code == code)
1718 .map(|meta| meta.alias)
1719}
1720
1721pub fn diagnostic_issue_metas() -> impl Iterator<Item = &'static IssueKindMeta> {
1723 ISSUE_KIND_META.iter().filter(|meta| meta.lsp)
1724}
1725
1726pub fn result_issue_metas() -> impl Iterator<Item = &'static IssueResultMeta> {
1728 ISSUE_RESULT_META.iter()
1729}
1730
1731pub fn counted_result_issue_metas() -> impl Iterator<Item = &'static IssueResultMeta> {
1733 result_issue_metas().filter(|meta| meta.counts_in_total)
1734}
1735
1736pub const FIXABLE_ISSUE_CODES: &[&str] = &[
1740 "unused-export",
1741 "unused-enum-member",
1742 "unused-dependency",
1743 "unused-dev-dependency",
1744 "unused-optional-dependency",
1745 "unused-catalog-entry",
1746];
1747
1748#[must_use]
1750pub fn issue_is_fixable(code: &str) -> bool {
1751 FIXABLE_ISSUE_CODES.contains(&code)
1752}
1753
1754#[cfg(test)]
1755mod tests {
1756 use std::collections::BTreeSet;
1757
1758 use crate::results::TOTAL_ISSUE_RESULT_KEYS;
1759
1760 use super::*;
1761
1762 #[test]
1763 fn fixable_codes_are_canonical_issue_codes() {
1764 for code in FIXABLE_ISSUE_CODES {
1765 assert!(
1766 issue_meta_by_code(code).is_some(),
1767 "fixable code {code} is not a canonical ISSUE_KIND_META code"
1768 );
1769 }
1770 assert!(issue_is_fixable("unused-export"));
1771 assert!(!issue_is_fixable("unused-file"));
1772 }
1773
1774 #[test]
1775 fn known_names_round_trip_through_metadata() {
1776 for name in KNOWN_ISSUE_KIND_NAMES.iter() {
1777 let meta = issue_meta_for_token(name)
1778 .unwrap_or_else(|| panic!("known issue name {name} missing metadata row"));
1779 assert!(
1780 meta.kind.is_some(),
1781 "known issue name {name} maps to non-IssueKind metadata"
1782 );
1783 }
1784 }
1785
1786 #[test]
1787 fn issue_kind_variants_have_metadata() {
1788 for &kind in IssueKind::ALL {
1789 assert!(
1790 issue_meta_by_kind(kind).is_some(),
1791 "IssueKind {kind:?} has no metadata row"
1792 );
1793 }
1794 }
1795
1796 #[test]
1797 fn dead_code_filter_flags_match_metadata() {
1798 let from_constants: BTreeSet<&str> = DEAD_CODE_FILTER_FLAGS.iter().copied().collect();
1799 let from_meta: BTreeSet<&str> = ISSUE_KIND_META
1800 .iter()
1801 .filter_map(|meta| meta.filter_flag)
1802 .collect();
1803 assert_eq!(from_constants, from_meta);
1804 }
1805
1806 #[test]
1807 fn mcp_issue_type_flags_match_metadata() {
1808 let from_constants: BTreeSet<(&str, &str)> = MCP_ISSUE_TYPE_FLAGS.iter().copied().collect();
1809 let from_meta: BTreeSet<(&str, &str)> = ISSUE_KIND_META
1810 .iter()
1811 .filter_map(|meta| meta.mcp_pair())
1812 .collect();
1813 assert_eq!(from_constants, from_meta);
1814 }
1815
1816 #[test]
1817 fn lsp_exposes_only_actual_diagnostic_codes() {
1818 let codes: BTreeSet<&str> = diagnostic_issue_metas().map(|meta| meta.code).collect();
1819 assert!(codes.contains("boundary-violation"));
1820 assert!(!codes.contains("boundary-coverage"));
1821 assert!(!codes.contains("boundary-call-violation"));
1822 }
1823
1824 #[test]
1825 fn issue_codes_are_unique() {
1826 let mut seen = BTreeSet::new();
1827 for meta in ISSUE_KIND_META {
1828 assert!(seen.insert(meta.code), "duplicate issue code {}", meta.code);
1829 }
1830 }
1831
1832 #[test]
1833 fn contract_tokens_resolve_through_metadata() {
1834 for meta in ISSUE_KIND_META {
1835 assert_eq!(
1836 issue_meta_for_contract_token(meta.code).map(|resolved| resolved.code),
1837 Some(meta.code),
1838 "canonical issue code {} should resolve through contract token lookup",
1839 meta.code
1840 );
1841 for token in meta.aliases {
1842 assert!(
1843 issue_meta_matches_contract_token(meta, token),
1844 "alias {token} should match {}",
1845 meta.code
1846 );
1847 }
1848 for token in [
1849 meta.config_key,
1850 meta.mcp_issue_type,
1851 meta.suppress_token,
1852 meta.filter_flag,
1853 ]
1854 .into_iter()
1855 .flatten()
1856 {
1857 assert!(
1858 issue_meta_for_contract_token(token).is_some(),
1859 "contract token {token} should resolve through metadata"
1860 );
1861 assert!(
1862 issue_meta_matches_contract_token(meta, token),
1863 "contract token {token} should match {}",
1864 meta.code
1865 );
1866 }
1867 }
1868 }
1869
1870 #[test]
1871 fn sarif_rule_descriptions_cover_result_metadata() {
1872 for meta in result_issue_metas() {
1873 for rule_id in issue_sarif_rule_ids(meta.code) {
1874 assert!(
1875 issue_sarif_rule_description(&rule_id).is_some(),
1876 "SARIF rule {rule_id} for {} needs a central description",
1877 meta.code
1878 );
1879 }
1880 }
1881 }
1882
1883 #[test]
1884 fn result_meta_codes_have_issue_metadata() {
1885 for meta in ISSUE_RESULT_META {
1886 assert!(
1887 issue_meta_by_code(meta.code).is_some(),
1888 "result metadata code {} has no issue metadata row",
1889 meta.code
1890 );
1891 }
1892 }
1893
1894 #[test]
1895 fn result_meta_codes_have_docs_anchors() {
1896 for meta in ISSUE_RESULT_META {
1897 let issue = issue_meta_by_code(meta.code)
1898 .unwrap_or_else(|| panic!("result metadata code {} has no issue row", meta.code));
1899 assert_eq!(
1900 issue.docs_anchor(),
1901 Some(meta.docs_anchor),
1902 "result metadata code {} has mismatched docs anchor",
1903 meta.code
1904 );
1905 }
1906 }
1907
1908 #[test]
1909 fn result_meta_codes_have_summary_labels() {
1910 for meta in ISSUE_RESULT_META {
1911 assert!(
1912 !meta.summary_label.is_empty(),
1913 "result metadata code {} has no summary label",
1914 meta.code
1915 );
1916 }
1917 }
1918
1919 #[test]
1920 fn result_meta_codes_have_meta_names() {
1921 for meta in ISSUE_RESULT_META {
1922 assert!(
1923 !meta.meta_name.is_empty(),
1924 "result metadata code {} has no meta name",
1925 meta.code
1926 );
1927 }
1928 }
1929
1930 #[test]
1931 fn result_meta_codes_have_meta_docs_paths() {
1932 for meta in ISSUE_RESULT_META {
1933 assert!(
1934 meta.meta_docs_path.starts_with("explanations/dead-code#"),
1935 "result metadata code {} has invalid meta docs path",
1936 meta.code
1937 );
1938 }
1939 }
1940
1941 #[test]
1942 fn result_meta_codes_have_meta_descriptions() {
1943 for meta in ISSUE_RESULT_META {
1944 assert!(
1945 !meta.meta_description.is_empty(),
1946 "result metadata code {} has no meta description",
1947 meta.code
1948 );
1949 }
1950 }
1951
1952 #[test]
1953 fn ci_format_ids_are_prefixed_and_known() {
1954 let result_codes: BTreeSet<&str> = result_issue_metas().map(|meta| meta.code).collect();
1955 let codeclimate_codes: BTreeSet<&str> = CODECLIMATE_RESULT_CODES.iter().copied().collect();
1956 assert!(codeclimate_codes.is_subset(&result_codes));
1957
1958 for meta in result_issue_metas() {
1959 let issue = issue_meta_by_code(meta.code)
1960 .unwrap_or_else(|| panic!("result metadata code {} has no issue row", meta.code));
1961 assert!(issue.has_result_contract());
1962 let sarif_ids = issue.sarif_rule_ids();
1963 assert!(sarif_ids.contains(&format!("fallow/{}", meta.code)));
1964 for rule_id in sarif_ids {
1965 assert!(
1966 rule_id.starts_with("fallow/"),
1967 "result metadata code {} has unprefixed SARIF rule id {rule_id}",
1968 meta.code
1969 );
1970 }
1971 for check_name in issue.codeclimate_check_names() {
1972 assert!(
1973 check_name.starts_with("fallow/"),
1974 "result metadata code {} has unprefixed CodeClimate check name {check_name}",
1975 meta.code
1976 );
1977 }
1978 }
1979 }
1980
1981 #[test]
1982 fn ts_alias_policy_is_explicit() {
1983 let aliases: BTreeSet<(&str, &str)> = ISSUE_TS_ALIAS_META
1984 .iter()
1985 .map(|meta| (meta.alias.name, meta.alias.parent))
1986 .collect();
1987
1988 assert_eq!(
1989 BTreeSet::from([
1990 ("BoundaryViolation", "BoundaryViolationFinding"),
1991 ("CircularDependency", "CircularDependencyFinding"),
1992 ("DeprecatedExportInUse", "DeprecatedExportInUseFinding"),
1993 (
1994 "DevDependencyInProduction",
1995 "DevDependencyInProductionFinding",
1996 ),
1997 ("DuplicateExport", "DuplicateExportFinding"),
1998 ("EmptyCatalogGroup", "EmptyCatalogGroupFinding"),
1999 (
2000 "MisconfiguredDependencyOverride",
2001 "MisconfiguredDependencyOverrideFinding",
2002 ),
2003 ("PrivateTypeLeak", "PrivateTypeLeakFinding"),
2004 ("ReExportCycle", "ReExportCycleFinding"),
2005 ("TestOnlyDependency", "TestOnlyDependencyFinding"),
2006 ("TypeOnlyDependency", "TypeOnlyDependencyFinding"),
2007 (
2008 "UnresolvedCatalogReference",
2009 "UnresolvedCatalogReferenceFinding",
2010 ),
2011 ("UnresolvedImport", "UnresolvedImportFinding"),
2012 ("UnlistedDependency", "UnlistedDependencyFinding"),
2013 ("UnusedCatalogEntry", "UnusedCatalogEntryFinding"),
2014 ("UnusedDependency", "UnusedDependencyFinding"),
2015 ("UnusedDependency", "UnusedDevDependencyFinding"),
2016 ("UnusedDependency", "UnusedOptionalDependencyFinding"),
2017 (
2018 "UnusedDependencyOverride",
2019 "UnusedDependencyOverrideFinding",
2020 ),
2021 ("UnusedExport", "UnusedExportFinding"),
2022 ("UnusedFile", "UnusedFileFinding"),
2023 ("UnusedMember", "UnusedClassMemberFinding"),
2024 ("UnusedMember", "UnusedEnumMemberFinding"),
2025 ("UnusedMember", "UnusedStoreMemberFinding"),
2026 ]),
2027 aliases
2028 );
2029 }
2030
2031 #[test]
2032 fn ts_alias_registry_rows_match_result_metadata() {
2033 let result_codes: BTreeSet<&str> = result_issue_metas().map(|meta| meta.code).collect();
2034 let mut seen_codes = BTreeSet::new();
2035 for meta in ISSUE_TS_ALIAS_META {
2036 assert!(
2037 seen_codes.insert(meta.code),
2038 "duplicate TypeScript alias row for {}",
2039 meta.code
2040 );
2041 assert!(
2042 result_codes.contains(meta.code),
2043 "TypeScript alias row {} has no result metadata",
2044 meta.code
2045 );
2046 assert!(
2047 meta.alias.parent.ends_with("Finding"),
2048 "TypeScript alias row {} must point at a generated Finding wrapper",
2049 meta.code
2050 );
2051 assert_eq!(
2052 Some(meta.alias),
2053 issue_meta_by_code(meta.code).and_then(|issue| issue.ts_alias()),
2054 "IssueKindMeta helper must resolve TypeScript alias row {} through the registry",
2055 meta.code
2056 );
2057 }
2058 }
2059
2060 #[test]
2061 fn result_keys_are_unique() {
2062 let mut seen = BTreeSet::new();
2063 for meta in ISSUE_RESULT_META {
2064 assert!(
2065 seen.insert(meta.result_key),
2066 "duplicate result key {}",
2067 meta.result_key
2068 );
2069 }
2070 }
2071
2072 #[test]
2073 fn counted_result_keys_match_total_issue_fields() {
2074 let from_total: BTreeSet<&str> = TOTAL_ISSUE_RESULT_KEYS.iter().copied().collect();
2075 let from_meta: BTreeSet<&str> = counted_result_issue_metas()
2076 .map(|meta| meta.result_key)
2077 .collect();
2078 assert_eq!(from_total, from_meta);
2079 }
2080
2081 #[test]
2082 fn advisory_result_keys_are_explicitly_excluded_from_total() {
2083 let expected = BTreeSet::from([
2084 "duplicate_prop_shapes",
2085 "prop_drilling_chains",
2086 "thin_wrappers",
2087 ]);
2088 let from_meta: BTreeSet<&str> = result_issue_metas()
2089 .filter(|meta| !meta.counts_in_total)
2090 .map(|meta| meta.result_key)
2091 .collect();
2092 assert_eq!(expected, from_meta);
2093 }
2094}