@usefragments/core 1.5.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/chunk-RANPUC6C.js +72 -0
  2. package/dist/chunk-RANPUC6C.js.map +1 -0
  3. package/dist/{chunk-BAHCOAVG.js → chunk-WNMWKUYG.js} +447 -201
  4. package/dist/chunk-WNMWKUYG.js.map +1 -0
  5. package/dist/codes/index.d.ts +2 -2
  6. package/dist/codes/index.js +1 -1
  7. package/dist/compiled-types/index.d.ts +1 -1
  8. package/dist/compiled-types/index.js +8 -0
  9. package/dist/generate/index.d.ts +1 -1
  10. package/dist/{governance-pKrfh517.d.ts → governance-D9KtH-vg.d.ts} +617 -22
  11. package/dist/index.d.ts +800 -179
  12. package/dist/index.js +1003 -237
  13. package/dist/index.js.map +1 -1
  14. package/dist/react-types.d.ts +1 -1
  15. package/dist/registry.d.ts +36 -36
  16. package/dist/schemas/index.d.ts +1 -1
  17. package/dist/test-utils.d.ts +1 -1
  18. package/package.json +2 -1
  19. package/src/__tests__/policy-exclude.test.ts +180 -0
  20. package/src/agent-format.test.ts +13 -0
  21. package/src/agent-format.ts +9 -3
  22. package/src/canonical-bridge.ts +69 -1
  23. package/src/canonical-direction.test.ts +118 -0
  24. package/src/canonical-direction.ts +43 -2
  25. package/src/codes/__tests__/codes.test.ts +14 -2
  26. package/src/codes/codes.ts +41 -2
  27. package/src/compiled-types/index.ts +81 -0
  28. package/src/compiled-types/parse.test.ts +47 -0
  29. package/src/facts/builders.ts +48 -0
  30. package/src/facts/compile.ts +148 -34
  31. package/src/facts/fact-index.ts +15 -2
  32. package/src/facts/facts.test.ts +44 -3
  33. package/src/facts/index.ts +11 -6
  34. package/src/facts/types.ts +60 -9
  35. package/src/governance-integrity.test.ts +272 -4
  36. package/src/governance-integrity.ts +345 -25
  37. package/src/governance.ts +87 -1
  38. package/src/index.ts +46 -1
  39. package/src/policy-exclude.ts +113 -0
  40. package/src/rules/a11y-required-accessible-name.ts +175 -28
  41. package/src/rules/a11y-standard.ts +102 -0
  42. package/src/rules/a11y-utils.ts +7 -0
  43. package/src/rules/components-prefer-library.test.ts +75 -28
  44. package/src/rules/components-prefer-library.ts +35 -15
  45. package/src/rules/components-shadow-component.test.ts +21 -9
  46. package/src/rules/emit-gate.test.ts +74 -4
  47. package/src/rules/emit-gate.ts +24 -9
  48. package/src/rules/families.test.ts +69 -0
  49. package/src/rules/families.ts +52 -0
  50. package/src/rules/fix-availability.ts +1 -0
  51. package/src/rules/index.ts +18 -2
  52. package/src/rules/jsx-preferred-import-path.ts +29 -11
  53. package/src/rules/rules.test.ts +188 -8
  54. package/src/rules/styles-no-raw-color.ts +13 -4
  55. package/src/rules/styles-no-raw-dimensions.ts +13 -4
  56. package/src/rules/styles-no-raw-spacing.ts +12 -4
  57. package/src/rules/styles-no-raw-typography.ts +13 -4
  58. package/src/rules/tiers.ts +1 -0
  59. package/src/rules/utils.ts +39 -0
  60. package/src/tokens/design-token-parser.test.ts +131 -0
  61. package/src/tokens/design-token-parser.ts +362 -49
  62. package/src/types.ts +2 -2
  63. package/dist/chunk-BAHCOAVG.js.map +0 -1
  64. package/dist/{index-DbkPE46t.d.ts → index-hZAlYCli.d.ts} +8 -8
package/dist/index.js CHANGED
@@ -44,6 +44,8 @@ import {
44
44
  CODES,
45
45
  EXPLAIN_URL_BASE,
46
46
  FactIndex,
47
+ RULE_FAMILY_IDS,
48
+ RULE_FAMILY_MEMBERS,
47
49
  asComponentId,
48
50
  bridgeSourceViolation,
49
51
  bridgeSourceViolations,
@@ -56,6 +58,8 @@ import {
56
58
  componentGovernanceRecordSchema,
57
59
  componentGovernanceRecordsSchema,
58
60
  componentId,
61
+ describePolicyExclude,
62
+ excludeGlobToRegExp,
59
63
  explainUrlForCode,
60
64
  factId,
61
65
  g,
@@ -65,6 +69,8 @@ import {
65
69
  governanceConfigSchema,
66
70
  governanceSeveritySchema,
67
71
  hash64Hex,
72
+ isPresetSourcedRule,
73
+ isRuleFamilyId,
68
74
  makeA11yNameRequiredFact,
69
75
  makeCanonicalCandidateFact,
70
76
  makeCanonicalMappingFact,
@@ -99,20 +105,29 @@ import {
99
105
  makeTailwindUnknownClassEnabledFact,
100
106
  makeThemeDeclarationFact,
101
107
  makeTokenDefinitionFact,
108
+ makeUsageChildContentFact,
102
109
  makeUsageComponentFact,
103
110
  makeUsageImportFact,
104
111
  makeUsageInlineStyleFact,
105
112
  makeUsageNodeFact,
106
113
  makeUsagePropResolvedFact,
107
114
  makeUsageTextChildFact,
115
+ markPresetSourcedRules,
116
+ matchPolicyExclude,
108
117
  matchesGlob,
118
+ normalizeExcludePath,
109
119
  normalizeGovernanceConfig,
120
+ normalizePolicyExcludes,
110
121
  ownedComponentIdsEqual,
111
122
  ownedImportMatchesRoot,
112
123
  ownedImportsEqual,
124
+ policyExcludeMatchesPath,
125
+ policyExcludeSchema,
126
+ projectSupersededImportPathPreferences,
113
127
  resolveComponentGovernance,
128
+ ruleFamilyMembers,
114
129
  scaleGovernanceRecordSchema
115
- } from "./chunk-BAHCOAVG.js";
130
+ } from "./chunk-WNMWKUYG.js";
116
131
  import {
117
132
  AGENT_FORMAT_SCHEMA_VERSION,
118
133
  agentErrorEnvelopeSchema,
@@ -167,6 +182,10 @@ import {
167
182
  severitySchema,
168
183
  sortBySeverity
169
184
  } from "./chunk-V4VQB57N.js";
185
+ import {
186
+ CompiledFragmentsFileValidationError,
187
+ parseCompiledFragmentsFile
188
+ } from "./chunk-RANPUC6C.js";
170
189
 
171
190
  // src/constants.ts
172
191
  var BRAND = {
@@ -406,6 +425,7 @@ function buildAgentFormat(input) {
406
425
  const zeroRulesLoaded = input.integrity.rulesLoaded === 0;
407
426
  const integrityFailed = !input.integrity.healthy || input.integrity.inert;
408
427
  const score = integrityFailed ? 0 : scoreFindings(input.findings);
428
+ const filesAffected = new Set(input.findings.map((finding) => finding.location.file)).size;
409
429
  const visibleFindings = uniqueFindings(input.findings).slice(0, input.maxFindings ?? 50);
410
430
  const agentFindings = visibleFindings.map((finding) => ({
411
431
  code: finding.code,
@@ -423,7 +443,7 @@ function buildAgentFormat(input) {
423
443
  const hasErrors = input.findings.some((finding) => severityLevel(finding.severity) === "error");
424
444
  const shared = {
425
445
  schemaVersion: AGENT_FORMAT_SCHEMA_VERSION,
426
- summary: zeroRulesLoaded ? "indeterminate; zero_rules_loaded" : integrityFailed ? `failed with score ${score}; ${input.integrity.reasons.join("; ") || "governance integrity is unhealthy"}` : agentSummary(input.findings.length, input.filesScanned, score),
446
+ summary: zeroRulesLoaded ? "indeterminate; zero_rules_loaded" : integrityFailed ? `failed with score ${score}; ${input.integrity.reasons.join("; ") || "governance integrity is unhealthy"}` : agentSummary(input.findings.length, filesAffected, input.filesScanned, score),
427
447
  integrity: input.integrity,
428
448
  fix_first: agentFindings.filter((finding) => finding.plan.confidence >= 0.8),
429
449
  remaining: agentFindings.filter((finding) => finding.plan.confidence < 0.8),
@@ -484,9 +504,9 @@ function scoreFindings(findings) {
484
504
  }
485
505
  return Math.max(1, Math.round(100 / (1 + penalty / 40)));
486
506
  }
487
- function agentSummary(count, filesScanned, score) {
507
+ function agentSummary(count, filesAffected, filesScanned, score) {
488
508
  if (count === 0) return `passed with score ${score}; no findings`;
489
- return `${count} finding${count === 1 ? "" : "s"} across ${filesScanned} file${filesScanned === 1 ? "" : "s"}; score ${score}`;
509
+ return `${count} finding${count === 1 ? "" : "s"} in ${filesAffected} file${filesAffected === 1 ? "" : "s"}; ${filesScanned} scanned; score ${score}`;
490
510
  }
491
511
  function planForFinding(finding) {
492
512
  if (finding.fix?.deterministic === true) {
@@ -3288,7 +3308,7 @@ function buildToken(name, rawValue, lineNumber, source) {
3288
3308
  }
3289
3309
 
3290
3310
  // src/tokens/design-token-parser.ts
3291
- var TOKEN_DECLARATION_PATTERN = /--([a-zA-Z0-9_-]+)\s*:\s*([^;{}]+?)\s*(?:;|(?=\})|$)/g;
3311
+ var CSS_CUSTOM_PROPERTY_NAME_CHARACTER = /[a-zA-Z0-9_-]/u;
3292
3312
  var VAR_REFERENCE_PATTERN = /var\(\s*--([a-zA-Z0-9_-]+)(?:\s*,\s*([^)]+))?\s*\)/;
3293
3313
  function parseDesignTokenContent(content, options = {}) {
3294
3314
  const startTime = performance.now();
@@ -3340,19 +3360,48 @@ function parseDesignTokenContent(content, options = {}) {
3340
3360
  errors.push({ message: err, file: filePath });
3341
3361
  }
3342
3362
  }
3343
- const tokensByName = /* @__PURE__ */ new Map();
3344
- for (const match of content.matchAll(TOKEN_DECLARATION_PATTERN)) {
3345
- const [, name, rawValue] = match;
3346
- const fullName = `--${name}`;
3347
- const lineNumber = countNewlines(content, match.index ?? 0) + 1;
3348
- tokensByName.set(fullName, {
3349
- rawValue: rawValue.trim().replace(/\s+/g, " "),
3350
- line: lineNumber
3363
+ const declarationsByThemeAndName = /* @__PURE__ */ new Map();
3364
+ const declarationMatches = scanCssCustomPropertyDeclarations(content);
3365
+ for (const match of declarationMatches) {
3366
+ const fullName = match.name;
3367
+ const lineNumber = match.line;
3368
+ const selector = match.selector;
3369
+ const theme = themeSelectors[selector] || "default";
3370
+ declarationsByThemeAndName.set(`${theme}\0${fullName}`, {
3371
+ name: fullName,
3372
+ rawValue: normalizeCssCustomPropertyValue(match.rawValue),
3373
+ line: lineNumber,
3374
+ selector,
3375
+ theme
3351
3376
  });
3352
3377
  }
3353
- for (const [name, { rawValue, line }] of tokensByName) {
3354
- const selector = findSelectorForLine(content, line ?? 1);
3355
- const theme = themeSelectors[selector] || "default";
3378
+ const declarations = [...declarationsByThemeAndName.values()];
3379
+ const defaults = /* @__PURE__ */ new Map();
3380
+ const declarationsByTheme = /* @__PURE__ */ new Map();
3381
+ for (const declaration of declarations) {
3382
+ if (declaration.theme === "default") defaults.set(declaration.name, declaration);
3383
+ let themeDeclarations = declarationsByTheme.get(declaration.theme);
3384
+ if (!themeDeclarations) {
3385
+ themeDeclarations = /* @__PURE__ */ new Map();
3386
+ declarationsByTheme.set(declaration.theme, themeDeclarations);
3387
+ }
3388
+ themeDeclarations.set(declaration.name, declaration);
3389
+ }
3390
+ const tokensByTheme = /* @__PURE__ */ new Map();
3391
+ for (const [theme, themeDeclarations] of declarationsByTheme) {
3392
+ if (theme === "default") {
3393
+ tokensByTheme.set(theme, defaults);
3394
+ continue;
3395
+ }
3396
+ const tokensByName = new Map(defaults);
3397
+ for (const [name, declaration] of themeDeclarations) {
3398
+ tokensByName.set(name, declaration);
3399
+ }
3400
+ tokensByTheme.set(theme, tokensByName);
3401
+ }
3402
+ const contentLines = content.split("\n");
3403
+ for (const { name, rawValue, line, selector, theme } of declarations) {
3404
+ const tokensByName = tokensByTheme.get(theme) ?? defaults;
3356
3405
  const { resolvedValue, chain, hasCircular, unresolvedRef } = resolveDesignTokenValue(
3357
3406
  rawValue,
3358
3407
  tokensByName
@@ -3374,7 +3423,7 @@ function parseDesignTokenContent(content, options = {}) {
3374
3423
  lineNumber: line,
3375
3424
  theme,
3376
3425
  selector,
3377
- description: extractDescription(content, line ?? 1)
3426
+ description: extractDescription(contentLines, line ?? 1)
3378
3427
  });
3379
3428
  }
3380
3429
  if (tokens.length === 0 && trimmed.length > 0) {
@@ -3389,13 +3438,6 @@ function parseDesignTokenContent(content, options = {}) {
3389
3438
  parseTimeMs: performance.now() - startTime
3390
3439
  };
3391
3440
  }
3392
- function countNewlines(content, index) {
3393
- let count = 0;
3394
- for (let i = 0; i < index && i < content.length; i++) {
3395
- if (content.charCodeAt(i) === 10) count++;
3396
- }
3397
- return count;
3398
- }
3399
3441
  function parsedOutputToDesignTokens(output, filePath) {
3400
3442
  const tokens = [];
3401
3443
  for (const cat of Object.values(output.categories)) {
@@ -3550,29 +3592,284 @@ function inferTokenLevel(name, rawValue, referenceChain) {
3550
3592
  }
3551
3593
  return 2;
3552
3594
  }
3553
- function findSelectorForLine(content, targetLine) {
3554
- const lines = content.split("\n");
3595
+ function scanCssCustomPropertyDeclarations(content) {
3596
+ const declarations = [];
3597
+ const parentSelectors = [];
3555
3598
  let currentSelector = ":root";
3556
- let braceDepth = 0;
3557
- for (let i = 0; i < Math.min(targetLine, lines.length); i++) {
3558
- const line = lines[i];
3559
- const selectorMatch = line.match(/^\s*([^{]+)\s*\{/);
3560
- if (selectorMatch) {
3561
- const selector = selectorMatch[1].trim();
3562
- if ((line.match(/\{/g) || []).length > (line.match(/\}/g) || []).length) {
3563
- currentSelector = selector;
3599
+ let boundary = 0;
3600
+ let statementHasContent = false;
3601
+ let line = 1;
3602
+ let quote = null;
3603
+ let escaped = false;
3604
+ let inComment = false;
3605
+ let parentheses = 0;
3606
+ let brackets = 0;
3607
+ for (let index = 0; index < content.length; index++) {
3608
+ const character = content[index];
3609
+ const next = content[index + 1];
3610
+ if (character === "\n") line++;
3611
+ if (inComment) {
3612
+ if (character === "*" && next === "/") {
3613
+ inComment = false;
3614
+ index++;
3564
3615
  }
3616
+ continue;
3565
3617
  }
3566
- braceDepth += (line.match(/\{/g) || []).length;
3567
- braceDepth -= (line.match(/\}/g) || []).length;
3568
- if (braceDepth === 0) {
3569
- currentSelector = ":root";
3618
+ if (quote) {
3619
+ if (escaped) {
3620
+ escaped = false;
3621
+ } else if (character === "\\") {
3622
+ escaped = true;
3623
+ } else if (character === quote) {
3624
+ quote = null;
3625
+ }
3626
+ continue;
3627
+ }
3628
+ if (character === "/" && next === "*") {
3629
+ inComment = true;
3630
+ index++;
3631
+ continue;
3632
+ }
3633
+ if (character === "'" || character === '"') {
3634
+ quote = character;
3635
+ statementHasContent = true;
3636
+ continue;
3637
+ }
3638
+ if (character === "\\") {
3639
+ statementHasContent = true;
3640
+ if (next === "\n") line++;
3641
+ index++;
3642
+ continue;
3643
+ }
3644
+ if (character === "(") {
3645
+ parentheses++;
3646
+ statementHasContent = true;
3647
+ continue;
3648
+ }
3649
+ if (character === ")") {
3650
+ parentheses = Math.max(0, parentheses - 1);
3651
+ statementHasContent = true;
3652
+ continue;
3653
+ }
3654
+ if (character === "[") {
3655
+ brackets++;
3656
+ statementHasContent = true;
3657
+ continue;
3658
+ }
3659
+ if (character === "]") {
3660
+ brackets = Math.max(0, brackets - 1);
3661
+ statementHasContent = true;
3662
+ continue;
3663
+ }
3664
+ if (parentheses > 0 || brackets > 0) {
3665
+ if (!/\s/u.test(character)) statementHasContent = true;
3666
+ continue;
3667
+ }
3668
+ if (character === "{") {
3669
+ const header = stripCssComments(content.slice(boundary, index)).trim();
3670
+ parentSelectors.push(currentSelector);
3671
+ if (header && !header.startsWith("@")) currentSelector = header;
3672
+ boundary = index + 1;
3673
+ statementHasContent = false;
3674
+ } else if (character === "}") {
3675
+ currentSelector = parentSelectors.pop() ?? ":root";
3676
+ boundary = index + 1;
3677
+ statementHasContent = false;
3678
+ } else if (character === ";") {
3679
+ boundary = index + 1;
3680
+ statementHasContent = false;
3681
+ } else if (character === "-" && next === "-" && !statementHasContent) {
3682
+ const declaration = scanCssCustomPropertyAt(content, index);
3683
+ if (!declaration) {
3684
+ statementHasContent = true;
3685
+ continue;
3686
+ }
3687
+ const rawValue = content.slice(declaration.valueStart, declaration.end).trim();
3688
+ if (rawValue) {
3689
+ declarations.push({
3690
+ name: declaration.name,
3691
+ rawValue,
3692
+ line,
3693
+ selector: currentSelector
3694
+ });
3695
+ }
3696
+ line += countNewlinesInRange(content, index + 1, declaration.end);
3697
+ statementHasContent = true;
3698
+ index = declaration.end - 1;
3699
+ } else if (!/\s/u.test(character)) {
3700
+ statementHasContent = true;
3570
3701
  }
3571
3702
  }
3572
- return currentSelector;
3703
+ return declarations;
3573
3704
  }
3574
- function extractDescription(content, line) {
3575
- const lines = content.split("\n");
3705
+ function scanCssCustomPropertyAt(content, start) {
3706
+ let cursor = start + 2;
3707
+ while (cursor < content.length && CSS_CUSTOM_PROPERTY_NAME_CHARACTER.test(content[cursor] ?? "")) {
3708
+ cursor++;
3709
+ }
3710
+ if (cursor === start + 2) return null;
3711
+ const nameEnd = cursor;
3712
+ cursor = skipCssWhitespaceAndComments(content, cursor);
3713
+ if (content[cursor] !== ":") return null;
3714
+ const valueStart = cursor + 1;
3715
+ return {
3716
+ name: content.slice(start, nameEnd),
3717
+ valueStart,
3718
+ end: findCssCustomPropertyValueEnd(content, valueStart)
3719
+ };
3720
+ }
3721
+ function findCssCustomPropertyValueEnd(content, start) {
3722
+ let quote = null;
3723
+ let escaped = false;
3724
+ let inComment = false;
3725
+ let parentheses = 0;
3726
+ let brackets = 0;
3727
+ let braces = 0;
3728
+ for (let index = start; index < content.length; index++) {
3729
+ const character = content[index];
3730
+ const next = content[index + 1];
3731
+ if (inComment) {
3732
+ if (character === "*" && next === "/") {
3733
+ inComment = false;
3734
+ index++;
3735
+ }
3736
+ continue;
3737
+ }
3738
+ if (quote) {
3739
+ if (escaped) {
3740
+ escaped = false;
3741
+ } else if (character === "\\") {
3742
+ escaped = true;
3743
+ } else if (character === quote) {
3744
+ quote = null;
3745
+ }
3746
+ continue;
3747
+ }
3748
+ if (character === "/" && next === "*") {
3749
+ inComment = true;
3750
+ index++;
3751
+ continue;
3752
+ }
3753
+ if (character === "'" || character === '"') {
3754
+ quote = character;
3755
+ continue;
3756
+ }
3757
+ if (character === "\\") {
3758
+ index++;
3759
+ continue;
3760
+ }
3761
+ if (character === "(") {
3762
+ parentheses++;
3763
+ } else if (character === ")") {
3764
+ parentheses = Math.max(0, parentheses - 1);
3765
+ } else if (character === "[") {
3766
+ brackets++;
3767
+ } else if (character === "]") {
3768
+ brackets = Math.max(0, brackets - 1);
3769
+ } else if (character === "{") {
3770
+ braces++;
3771
+ } else if (character === "}") {
3772
+ if (braces > 0) {
3773
+ braces--;
3774
+ } else if (parentheses === 0 && brackets === 0) {
3775
+ return index;
3776
+ }
3777
+ } else if (character === ";" && parentheses === 0 && brackets === 0 && braces === 0) {
3778
+ return index;
3779
+ }
3780
+ }
3781
+ return content.length;
3782
+ }
3783
+ function skipCssWhitespaceAndComments(content, start) {
3784
+ let cursor = start;
3785
+ while (cursor < content.length) {
3786
+ if (/\s/u.test(content[cursor] ?? "")) {
3787
+ cursor++;
3788
+ continue;
3789
+ }
3790
+ if (content[cursor] !== "/" || content[cursor + 1] !== "*") break;
3791
+ const commentEnd = content.indexOf("*/", cursor + 2);
3792
+ if (commentEnd < 0) return content.length;
3793
+ cursor = commentEnd + 2;
3794
+ }
3795
+ return cursor;
3796
+ }
3797
+ function stripCssComments(content) {
3798
+ let output = "";
3799
+ let quote = null;
3800
+ let escaped = false;
3801
+ let inComment = false;
3802
+ for (let index = 0; index < content.length; index++) {
3803
+ const character = content[index];
3804
+ const next = content[index + 1];
3805
+ if (inComment) {
3806
+ if (character === "*" && next === "/") {
3807
+ inComment = false;
3808
+ index++;
3809
+ }
3810
+ continue;
3811
+ }
3812
+ if (quote) {
3813
+ output += character;
3814
+ if (escaped) {
3815
+ escaped = false;
3816
+ } else if (character === "\\") {
3817
+ escaped = true;
3818
+ } else if (character === quote) {
3819
+ quote = null;
3820
+ }
3821
+ continue;
3822
+ }
3823
+ if (character === "/" && next === "*") {
3824
+ inComment = true;
3825
+ index++;
3826
+ continue;
3827
+ }
3828
+ output += character;
3829
+ if (character === "'" || character === '"') quote = character;
3830
+ }
3831
+ return output;
3832
+ }
3833
+ function countNewlinesInRange(content, start, end) {
3834
+ let count = 0;
3835
+ for (let index = start; index < end; index++) {
3836
+ if (content.charCodeAt(index) === 10) count++;
3837
+ }
3838
+ return count;
3839
+ }
3840
+ function normalizeCssCustomPropertyValue(value) {
3841
+ let output = "";
3842
+ let quote = null;
3843
+ let escaped = false;
3844
+ let pendingWhitespace = false;
3845
+ for (const character of value.trim()) {
3846
+ if (quote) {
3847
+ output += character;
3848
+ if (escaped) {
3849
+ escaped = false;
3850
+ } else if (character === "\\") {
3851
+ escaped = true;
3852
+ } else if (character === quote) {
3853
+ quote = null;
3854
+ }
3855
+ continue;
3856
+ }
3857
+ if (character === "'" || character === '"') {
3858
+ if (pendingWhitespace && output) output += " ";
3859
+ pendingWhitespace = false;
3860
+ quote = character;
3861
+ output += character;
3862
+ } else if (/\s/u.test(character)) {
3863
+ pendingWhitespace = true;
3864
+ } else {
3865
+ if (pendingWhitespace && output) output += " ";
3866
+ pendingWhitespace = false;
3867
+ output += character;
3868
+ }
3869
+ }
3870
+ return output;
3871
+ }
3872
+ function extractDescription(lines, line) {
3576
3873
  if (line <= 1) return void 0;
3577
3874
  const prevLine = lines[line - 2]?.trim();
3578
3875
  const singleLineMatch = prevLine?.match(/\/\/\s*(.+)$/);
@@ -4368,6 +4665,13 @@ function discoverComponents(moduleExports, options) {
4368
4665
  return names.sort();
4369
4666
  }
4370
4667
 
4668
+ // src/rules/a11y-utils.ts
4669
+ function isAriaHidden(props) {
4670
+ const prop = props.find((candidate) => candidate.prop === "aria-hidden");
4671
+ if (!prop || prop.resolution !== "static") return false;
4672
+ return prop.value === true || prop.value === "true";
4673
+ }
4674
+
4371
4675
  // src/rules/finding.ts
4372
4676
  function makeFinding(input) {
4373
4677
  if (input.evidence.length === 0) {
@@ -4496,6 +4800,19 @@ function readUsageNode(ix, id) {
4496
4800
  const fact = ix.get(id);
4497
4801
  return fact && fact.kind === "usage_node" ? fact : void 0;
4498
4802
  }
4803
+ function hoppedValue(fact) {
4804
+ const from = fact.valueFrom;
4805
+ if (!from) return void 0;
4806
+ const declaredAt = `${from.location.file}:${from.location.line}`;
4807
+ return {
4808
+ suffix: ` Value comes from \`${from.name}\`, declared at ${declaredAt}.`,
4809
+ attributes: {
4810
+ valueFrom: "const-binding",
4811
+ declaredAs: from.name,
4812
+ declaredAt
4813
+ }
4814
+ };
4815
+ }
4499
4816
  function contractPrefixFamilies(names) {
4500
4817
  const families = /* @__PURE__ */ new Set();
4501
4818
  for (const name of names) {
@@ -4813,36 +5130,71 @@ function isApplicableTokenReference(referenceFormat) {
4813
5130
 
4814
5131
  // src/rules/a11y-required-accessible-name.ts
4815
5132
  var RULE_ID = "a11y/required-accessible-name";
4816
- var RULE_VERSION = "1";
5133
+ var RULE_VERSION = "2";
4817
5134
  var NAME_SOURCE_PROPS = /* @__PURE__ */ new Set(["aria-label", "aria-labelledby", "title"]);
5135
+ var COMPONENT_NAME_SOURCE_PROPS = /* @__PURE__ */ new Set([...NAME_SOURCE_PROPS, "label"]);
5136
+ var NAME_REQUIRED_TAGS = /* @__PURE__ */ new Set(["button", "input", "select", "textarea"]);
5137
+ var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["button", "checkbox", "radio", "switch", "tab"]);
5138
+ var NAME_FROM_CONTENT_TAGS = /* @__PURE__ */ new Set(["button"]);
5139
+ var NAME_FROM_CONTENT_ROLES = /* @__PURE__ */ new Set(["button", "checkbox", "radio", "switch", "tab"]);
5140
+ var NAME_REQUIRED_COMPONENT_SUFFIXES = [
5141
+ "button",
5142
+ "input",
5143
+ "select",
5144
+ "textarea",
5145
+ "checkbox",
5146
+ "radio",
5147
+ "switch"
5148
+ ];
4818
5149
  function ruleA11yRequiredAccessibleName(ix) {
4819
5150
  const findings = [];
4820
5151
  const propsByNode = indexPropsByNodeId(ix);
4821
5152
  const textByNode = indexTextChildrenByNodeId(ix);
4822
- for (const usageComponent of ix.byKind("usage_component")) {
4823
- const policy = ix.policy.a11yNameRequired(usageComponent.componentId);
4824
- if (!policy) continue;
4825
- const node = readUsageNode(ix, usageComponent.nodeId);
4826
- if (!node) continue;
5153
+ const childContentByNode = new Map(
5154
+ ix.byKind("usage_child_content").map((content) => [content.nodeId, content])
5155
+ );
5156
+ const globalPolicy = ix.policy.ruleConfig(RULE_ID);
5157
+ const usageByNode = new Map(ix.byKind("usage_component").map((usage) => [usage.nodeId, usage]));
5158
+ const definitions = new Map(
5159
+ ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
5160
+ );
5161
+ for (const node of ix.byKind("usage_node")) {
5162
+ const usageComponent = usageByNode.get(node.id);
5163
+ const componentPolicy = usageComponent ? ix.policy.a11yNameRequired(usageComponent.componentId) : void 0;
4827
5164
  const props = propsByNode.get(node.id) ?? [];
4828
- if (props.some(isAriaHidden)) continue;
4829
- if (hasAccessibleName(props, textByNode.get(node.id) ?? [])) continue;
5165
+ if (isAriaHidden(props)) continue;
5166
+ const genericControl = globalPolicy?.enabled === true && requiresAccessibleName(node, usageComponent, definitions, props);
5167
+ if (!componentPolicy && !genericControl) continue;
5168
+ if (hasAccessibleName(
5169
+ props,
5170
+ textByNode.get(node.id) ?? [],
5171
+ childContentByNode.get(node.id),
5172
+ usageComponent !== void 0,
5173
+ canNameFromContent(node, usageComponent, definitions)
5174
+ )) {
5175
+ continue;
5176
+ }
4830
5177
  findings.push(
4831
5178
  makeFinding({
4832
5179
  ruleId: RULE_ID,
4833
5180
  ruleVersion: RULE_VERSION,
4834
- severity: policy.severity,
4835
- message: `<${node.element}> has no accessible name. ${policy.because}`.trim(),
5181
+ severity: componentPolicy?.severity ?? globalPolicy?.severity ?? "warn",
5182
+ message: `<${node.element}> has no accessible name. ${componentPolicy?.because ?? "Add visible text, aria-label, aria-labelledby, or a component label."}`.trim(),
4836
5183
  location: node.location,
4837
- evidence: ix.evidence([node.id, usageComponent.id, policy.id]),
5184
+ evidence: ix.evidence([
5185
+ node.id,
5186
+ ...usageComponent ? [usageComponent.id] : [],
5187
+ ...componentPolicy ? [componentPolicy.id] : [],
5188
+ ...genericControl && globalPolicy ? [globalPolicy.id] : []
5189
+ ]),
4838
5190
  fingerprintIdentity: {
4839
5191
  file: node.file,
4840
- componentId: usageComponent.componentId,
5192
+ componentId: usageComponent?.componentId,
4841
5193
  element: node.element,
4842
5194
  nodePath: node.nodePath
4843
5195
  },
4844
5196
  attributes: {
4845
- componentId: usageComponent.componentId,
5197
+ ...usageComponent ? { componentId: usageComponent.componentId } : {},
4846
5198
  element: node.element
4847
5199
  }
4848
5200
  })
@@ -4850,12 +5202,18 @@ function ruleA11yRequiredAccessibleName(ix) {
4850
5202
  }
4851
5203
  return findings;
4852
5204
  }
4853
- function hasAccessibleName(props, textChildren) {
4854
- for (const child of textChildren) {
4855
- if (typeof child.text === "string" && child.text.trim().length > 0) return true;
5205
+ function hasAccessibleName(props, textChildren, childContent, componentControl, nameFromContent) {
5206
+ const sourceProps = componentControl ? COMPONENT_NAME_SOURCE_PROPS : NAME_SOURCE_PROPS;
5207
+ if (nameFromContent) {
5208
+ for (const child of textChildren) {
5209
+ if (typeof child.text === "string" && child.text.trim().length > 0) return true;
5210
+ }
5211
+ if (childContent?.content === "static-text" || childContent?.content === "runtime") {
5212
+ return true;
5213
+ }
4856
5214
  }
4857
5215
  for (const prop of props) {
4858
- if (!NAME_SOURCE_PROPS.has(prop.prop)) continue;
5216
+ if (!sourceProps.has(prop.prop)) continue;
4859
5217
  if (prop.resolution === "spread") continue;
4860
5218
  if (prop.resolution === "dynamic") return true;
4861
5219
  if (prop.resolution === "static") {
@@ -4865,10 +5223,137 @@ function hasAccessibleName(props, textChildren) {
4865
5223
  }
4866
5224
  return false;
4867
5225
  }
4868
- function isAriaHidden(prop) {
4869
- if (prop.prop !== "aria-hidden") return false;
4870
- if (prop.resolution !== "static") return false;
4871
- return prop.value === true || prop.value === "true";
5226
+ function canNameFromContent(node, usage, definitions) {
5227
+ const element = node.element.toLowerCase();
5228
+ if (NAME_REQUIRED_TAGS.has(element)) return NAME_FROM_CONTENT_TAGS.has(element);
5229
+ if (node.role && NAME_FROM_CONTENT_ROLES.has(node.role.toLowerCase())) return true;
5230
+ if (!usage) return false;
5231
+ const definition = definitions.get(String(usage.componentId));
5232
+ if (definition) {
5233
+ const root = definition.renderRoot;
5234
+ if (root.resolution === "intrinsic") {
5235
+ return intrinsicCanNameFromContent(root.tag, root.role);
5236
+ }
5237
+ if (root.resolution === "canonical") return isContentNamedControl(root.canonical);
5238
+ if (root.resolution === "mixed") {
5239
+ return root.intrinsics.some((tag) => intrinsicCanNameFromContent(tag)) || root.canonicals.some(isContentNamedControl);
5240
+ }
5241
+ }
5242
+ return isContentNamedControl(String(usage.componentId));
5243
+ }
5244
+ function intrinsicCanNameFromContent(tag, role) {
5245
+ const normalizedTag = tag.toLowerCase();
5246
+ if (NAME_REQUIRED_TAGS.has(normalizedTag)) {
5247
+ return NAME_FROM_CONTENT_TAGS.has(normalizedTag);
5248
+ }
5249
+ return role !== void 0 && NAME_FROM_CONTENT_ROLES.has(role.toLowerCase());
5250
+ }
5251
+ function requiresAccessibleName(node, usage, definitions, props) {
5252
+ if (node.role && NAME_REQUIRED_ROLES.has(node.role.toLowerCase())) return true;
5253
+ if (NAME_REQUIRED_TAGS.has(node.element.toLowerCase())) {
5254
+ if (node.element.toLowerCase() === "input" && props.some(
5255
+ (prop) => prop.prop === "type" && prop.resolution === "static" && String(prop.value).toLowerCase() === "hidden"
5256
+ )) {
5257
+ return false;
5258
+ }
5259
+ return true;
5260
+ }
5261
+ if (!usage) return false;
5262
+ const definition = definitions.get(String(usage.componentId));
5263
+ if (definition) {
5264
+ const root = definition.renderRoot;
5265
+ if (root.resolution === "intrinsic") {
5266
+ return NAME_REQUIRED_TAGS.has(root.tag.toLowerCase()) || root.role !== void 0 && NAME_REQUIRED_ROLES.has(root.role.toLowerCase());
5267
+ }
5268
+ if (root.resolution === "canonical") return isNamedControl(root.canonical);
5269
+ if (root.resolution === "mixed") {
5270
+ return root.intrinsics.some((tag) => NAME_REQUIRED_TAGS.has(tag.toLowerCase())) || root.canonicals.some(isNamedControl);
5271
+ }
5272
+ }
5273
+ return isNamedControl(String(usage.componentId));
5274
+ }
5275
+ function isContentNamedControl(identity) {
5276
+ return normalizedControlName(identity).endsWith("button");
5277
+ }
5278
+ function isNamedControl(identity) {
5279
+ const normalized = normalizedControlName(identity);
5280
+ return NAME_REQUIRED_COMPONENT_SUFFIXES.some((suffix) => normalized.endsWith(suffix));
5281
+ }
5282
+ function normalizedControlName(identity) {
5283
+ const exported = identity.split("#").at(-1)?.split(".").at(-1) ?? identity;
5284
+ return exported.replace(/[^a-z0-9]/gi, "").toLowerCase();
5285
+ }
5286
+
5287
+ // src/rules/a11y-standard.ts
5288
+ var RULE_ID2 = "a11y/standard";
5289
+ var RULE_VERSION2 = "1";
5290
+ var KEY_HANDLERS = /* @__PURE__ */ new Set(["onKeyDown", "onKeyUp", "onKeyPress"]);
5291
+ function ruleA11yStandard(ix) {
5292
+ const policy = ix.policy.ruleConfig(RULE_ID2);
5293
+ if (!policy?.enabled) return [];
5294
+ const propsByNode = indexPropsByNodeId(ix);
5295
+ const findings = [];
5296
+ for (const node of ix.byKind("usage_node")) {
5297
+ if (node.role?.trim().toLowerCase() !== "button") continue;
5298
+ const props = propsByNode.get(node.id) ?? [];
5299
+ if (isAriaHidden(props) || isNativeButton(node.element, props)) continue;
5300
+ const missing = [
5301
+ ...hasFocusableTabIndex(props) ? [] : ["tabIndex"],
5302
+ ...hasKeyHandler(props) ? [] : ["keyboard handler"]
5303
+ ];
5304
+ if (missing.length === 0) continue;
5305
+ findings.push(
5306
+ makeFinding({
5307
+ ruleId: RULE_ID2,
5308
+ ruleVersion: RULE_VERSION2,
5309
+ severity: policy.severity ?? "warn",
5310
+ message: `<${node.element} role="button"> is missing ${missing.join(
5311
+ " and "
5312
+ )}. Non-native buttons need tabIndex plus keyboard activation handling.`,
5313
+ location: node.location,
5314
+ evidence: ix.evidence([
5315
+ node.id,
5316
+ policy.id,
5317
+ ...props.filter(
5318
+ (prop) => prop.prop === "tabIndex" || prop.prop === "tabindex" || KEY_HANDLERS.has(prop.prop)
5319
+ ).map((prop) => prop.id)
5320
+ ]),
5321
+ fingerprintIdentity: {
5322
+ file: node.file,
5323
+ element: node.element,
5324
+ nodePath: node.nodePath,
5325
+ standard: "keyboard-role-button"
5326
+ },
5327
+ attributes: {
5328
+ standard: "keyboard-role-button",
5329
+ role: "button",
5330
+ missing
5331
+ }
5332
+ })
5333
+ );
5334
+ }
5335
+ return findings;
5336
+ }
5337
+ function isNativeButton(element, props) {
5338
+ if (element.toLowerCase() === "button") return true;
5339
+ if (element.toLowerCase() !== "input") return false;
5340
+ return props.some(
5341
+ (prop) => prop.prop === "type" && prop.resolution === "static" && ["button", "submit", "reset"].includes(String(prop.value).toLowerCase())
5342
+ );
5343
+ }
5344
+ function hasFocusableTabIndex(props) {
5345
+ return props.some((prop) => {
5346
+ if (prop.prop !== "tabIndex" && prop.prop !== "tabindex") return false;
5347
+ if (prop.resolution === "dynamic") return true;
5348
+ if (prop.resolution !== "static") return false;
5349
+ const value = typeof prop.value === "number" ? prop.value : Number(prop.value);
5350
+ return Number.isFinite(value) && value >= 0;
5351
+ });
5352
+ }
5353
+ function hasKeyHandler(props) {
5354
+ return props.some(
5355
+ (prop) => KEY_HANDLERS.has(prop.prop) && prop.resolution !== "spread" && prop.value !== null && prop.value !== false
5356
+ );
4872
5357
  }
4873
5358
 
4874
5359
  // src/raw-html-canonical.ts
@@ -4977,7 +5462,7 @@ function projectCanonicalDirectionConflicts(ix, canonicalSources) {
4977
5462
  const conflicts = [];
4978
5463
  for (const definition of ix.byKind("component_definition")) {
4979
5464
  if (!definition.exported || definition.renderRoot.resolution !== "canonical") continue;
4980
- const target = npmTarget(definition.renderRoot.canonical, npmSources);
5465
+ const target = npmTarget(definition.renderRoot, npmSources);
4981
5466
  if (!target) continue;
4982
5467
  const identity = identityByComponent.get(definition.componentKey);
4983
5468
  if (identity?.decisionId && decisionTargetsExport(identity.canonicalTarget, target.exportName)) {
@@ -5011,7 +5496,10 @@ function canonicalDirectionConflictsTarget(conflicts, importPath, exportName) {
5011
5496
  (conflict) => ownedImportMatchesRoot(importPath, conflict.packageName) && conflict.exportName === exportName
5012
5497
  );
5013
5498
  }
5014
- function npmTarget(canonicalTarget, sources) {
5499
+ function npmTarget(renderRoot, sources) {
5500
+ return npmTargetFromCanonicalKey(renderRoot.canonical, sources) ?? npmTargetFromImportSource(renderRoot, sources);
5501
+ }
5502
+ function npmTargetFromCanonicalKey(canonicalTarget, sources) {
5015
5503
  const separator = canonicalTarget.lastIndexOf("#");
5016
5504
  if (separator <= 0 || separator === canonicalTarget.length - 1) return null;
5017
5505
  const importPath = canonicalTarget.slice(0, separator);
@@ -5028,6 +5516,19 @@ function npmTarget(canonicalTarget, sources) {
5028
5516
  }
5029
5517
  return null;
5030
5518
  }
5519
+ function npmTargetFromImportSource(renderRoot, sources) {
5520
+ const separator = renderRoot.canonical.lastIndexOf("#");
5521
+ if (separator <= 0 || separator === renderRoot.canonical.length - 1) return null;
5522
+ const exportName = renderRoot.canonical.slice(separator + 1).split(".").at(-1);
5523
+ if (!exportName || exportName === "default") return null;
5524
+ for (const source of sources) {
5525
+ if (!ownedImportMatchesRoot(renderRoot.importSource, source.specifier)) continue;
5526
+ if (source.exclude?.includes(exportName)) continue;
5527
+ if (source.include && !source.include.includes(exportName)) continue;
5528
+ return { packageName: source.specifier, exportName };
5529
+ }
5530
+ return null;
5531
+ }
5031
5532
  function packageSubpath(importPath, packageName) {
5032
5533
  return importPath === packageName ? "" : importPath.slice(packageName.length + 1);
5033
5534
  }
@@ -5037,8 +5538,8 @@ function decisionTargetsExport(target, exportName) {
5037
5538
  }
5038
5539
 
5039
5540
  // src/rules/components-prefer-library.ts
5040
- var RULE_ID2 = "components/prefer-library";
5041
- var RULE_VERSION2 = "1";
5541
+ var RULE_ID3 = "components/prefer-library";
5542
+ var RULE_VERSION3 = "1";
5042
5543
  function isExplicitTagDeclaration(mapping) {
5043
5544
  return mapping.declaredHtmlEquivalent === true;
5044
5545
  }
@@ -5177,7 +5678,7 @@ function findClassNameReimpl(node, classTokensByNode, targets) {
5177
5678
  return null;
5178
5679
  }
5179
5680
  function ruleComponentsPreferLibrary(ix) {
5180
- const policy = ix.policy.ruleConfig(RULE_ID2);
5681
+ const policy = ix.policy.ruleConfig(RULE_ID3);
5181
5682
  if (!policy?.enabled) return [];
5182
5683
  const configuredSources = canonicalSourcesFromPolicy(policy.options?.canonicalSources);
5183
5684
  const conflicts = projectCanonicalDirectionConflicts(ix, configuredSources);
@@ -5192,7 +5693,7 @@ function ruleComponentsPreferLibrary(ix) {
5192
5693
  const classTokensByNode = indexClassTokensByNode(ix);
5193
5694
  const reimplTargets = canonicalReimplTargets(sources, mappings);
5194
5695
  const canonicalDirectoryResolvedNodes = indexCanonicalDirectoryResolvedNodes(ix, sources);
5195
- const shadowRenderRootNodeIds = indexShadowRenderRootNodeIds(ix);
5696
+ const shadowRenderRootNodeIds = ix.policy.ruleConfig("components/shadow-component")?.enabled === true ? indexShadowRenderRootNodeIds(ix) : /* @__PURE__ */ new Set();
5196
5697
  const findings = [];
5197
5698
  const seenImportFixes = /* @__PURE__ */ new Set();
5198
5699
  for (const node of ix.byKind("usage_node")) {
@@ -5224,8 +5725,8 @@ function ruleComponentsPreferLibrary(ix) {
5224
5725
  seenImportFixes.add(key);
5225
5726
  findings.push(
5226
5727
  makeFinding({
5227
- ruleId: RULE_ID2,
5228
- ruleVersion: RULE_VERSION2,
5728
+ ruleId: RULE_ID3,
5729
+ ruleVersion: RULE_VERSION3,
5229
5730
  severity: capAdvisorySeverity(policy.severity),
5230
5731
  message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
5231
5732
  location: imported.location,
@@ -5259,8 +5760,8 @@ function ruleComponentsPreferLibrary(ix) {
5259
5760
  if (node.element === suggestedComponent2) continue;
5260
5761
  findings.push(
5261
5762
  makeFinding({
5262
- ruleId: RULE_ID2,
5263
- ruleVersion: RULE_VERSION2,
5763
+ ruleId: RULE_ID3,
5764
+ ruleVersion: RULE_VERSION3,
5264
5765
  severity: severityForTier(policy.severity, precisionTier2),
5265
5766
  message: node.element === "button" ? `Bespoke <button> has a library equivalent. Swap to <${suggestedComponent2}> from ${suggestedImport2}.` : roleMatch ? `Custom <${node.element} role="${node.role}"> reimplements a primitive. Use <${suggestedComponent2}> from ${suggestedImport2}.` : `Bespoke <${node.element}> should use <${suggestedComponent2}> from ${suggestedImport2}.`,
5266
5767
  location: node.location,
@@ -5311,8 +5812,8 @@ function ruleComponentsPreferLibrary(ix) {
5311
5812
  const suggestedImport2 = reimpl.import ?? "the canonical library";
5312
5813
  findings.push(
5313
5814
  makeFinding({
5314
- ruleId: RULE_ID2,
5315
- ruleVersion: RULE_VERSION2,
5815
+ ruleId: RULE_ID3,
5816
+ ruleVersion: RULE_VERSION3,
5316
5817
  severity: capAdvisorySeverity(policy.severity),
5317
5818
  message: `<${node.element} class="${reimpl.matchedClass}"> looks like a hand-rolled <${reimpl.canonical}>. Use <${reimpl.canonical}> from ${suggestedImport2} instead of restyling a <${node.element}>.`,
5318
5819
  location: node.location,
@@ -5352,8 +5853,8 @@ function ruleComponentsPreferLibrary(ix) {
5352
5853
  seenImportFixes.add(key);
5353
5854
  findings.push(
5354
5855
  makeFinding({
5355
- ruleId: RULE_ID2,
5356
- ruleVersion: RULE_VERSION2,
5856
+ ruleId: RULE_ID3,
5857
+ ruleVersion: RULE_VERSION3,
5357
5858
  severity: capAdvisorySeverity(policy.severity),
5358
5859
  message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
5359
5860
  location: imported.location,
@@ -5395,14 +5896,15 @@ function ruleComponentsPreferLibrary(ix) {
5395
5896
  continue;
5396
5897
  }
5397
5898
  }
5398
- const suggestedImport = sourceLabel(suggestion);
5899
+ const importReady = suggestion.kind !== "directory";
5900
+ const suggestedImport = importReady ? sourceLabel(suggestion) : "the canonical component directory (resolve its configured import alias)";
5399
5901
  const precisionTier = builtIn?.tier ?? "exact-html";
5400
5902
  const rawElement = formatRawElement(node, props);
5401
5903
  findings.push(
5402
5904
  makeFinding({
5403
- ruleId: RULE_ID2,
5404
- ruleVersion: RULE_VERSION2,
5405
- severity: severityForTier(policy.severity, precisionTier),
5905
+ ruleId: RULE_ID3,
5906
+ ruleVersion: RULE_VERSION3,
5907
+ severity: importReady ? severityForTier(policy.severity, precisionTier) : capAdvisorySeverity(policy.severity),
5406
5908
  message: messageForBuiltInMatch({
5407
5909
  node,
5408
5910
  rawElement,
@@ -5419,7 +5921,7 @@ function ruleComponentsPreferLibrary(ix) {
5419
5921
  suggestedComponent,
5420
5922
  suggestedImport
5421
5923
  },
5422
- ...!isRawHtmlAdvisoryTier(precisionTier) && isIdentifier(node.element) && isIdentifier(suggestedComponent) ? {
5924
+ ...importReady && !isRawHtmlAdvisoryTier(precisionTier) && isIdentifier(node.element) && isIdentifier(suggestedComponent) ? {
5423
5925
  fix: {
5424
5926
  kind: "replaceComponent",
5425
5927
  title: `Replace ${rawElement} with <${suggestedComponent}>`,
@@ -5434,13 +5936,13 @@ function ruleComponentsPreferLibrary(ix) {
5434
5936
  attributes: {
5435
5937
  rawValue: node.element,
5436
5938
  suggestedComponent,
5437
- suggestedImport,
5438
5939
  suggestedImportSourceKind: suggestion.kind,
5940
+ ...importReady ? { suggestedImport } : { canonicalDirectory: suggestion.path, importReady: false },
5439
5941
  precisionTier,
5440
5942
  propCompatibility: "unknown",
5441
5943
  ...builtIn?.matchedRole ? { matchedRole: builtIn.matchedRole } : {},
5442
5944
  ...builtIn?.inputType ? { matchedInputType: builtIn.inputType } : {},
5443
- ...isRawHtmlAdvisoryTier(precisionTier) ? { advisory: true } : {}
5945
+ ...isRawHtmlAdvisoryTier(precisionTier) || !importReady ? { advisory: true } : {}
5444
5946
  }
5445
5947
  })
5446
5948
  );
@@ -5460,11 +5962,19 @@ function suppressConflictedExports(source, conflicts) {
5460
5962
  function indexShadowRenderRootNodeIds(ix) {
5461
5963
  const identities = ix.byKind("component_identity");
5462
5964
  if (identities.length === 0) return /* @__PURE__ */ new Set();
5965
+ const definitions = new Map(
5966
+ ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
5967
+ );
5463
5968
  const nodeIds = /* @__PURE__ */ new Set();
5464
5969
  for (const identity of identities) {
5465
5970
  if (identity.state !== "shadow") continue;
5971
+ const definition = definitions.get(identity.componentKey);
5972
+ if (!definition) continue;
5466
5973
  for (const evidenceId of identity.evidence) {
5467
- if (ix.get(evidenceId)?.kind === "usage_node") nodeIds.add(evidenceId);
5974
+ const evidence = ix.get(evidenceId);
5975
+ if (evidence?.kind === "usage_node" && evidence.file === definition.file) {
5976
+ nodeIds.add(evidenceId);
5977
+ }
5468
5978
  }
5469
5979
  }
5470
5980
  return nodeIds;
@@ -5747,7 +6257,7 @@ function sourceLabel(source) {
5747
6257
  function canonicalImportPath(source) {
5748
6258
  if (source.kind === "npm") return source.specifier;
5749
6259
  if (source.kind === "registry") return source.importPath;
5750
- return source.include?.length ? source.path : void 0;
6260
+ return void 0;
5751
6261
  }
5752
6262
  function indexCanonicalDirectoryResolvedNodes(ix, sources) {
5753
6263
  const out = /* @__PURE__ */ new Set();
@@ -5823,10 +6333,10 @@ function indexComponentIdentityUsage(ix) {
5823
6333
  }
5824
6334
 
5825
6335
  // src/rules/components-shadow-component.ts
5826
- var RULE_ID3 = "components/shadow-component";
5827
- var RULE_VERSION3 = "1";
6336
+ var RULE_ID4 = "components/shadow-component";
6337
+ var RULE_VERSION4 = "1";
5828
6338
  function ruleComponentsShadowComponent(ix) {
5829
- const policy = ix.policy.ruleConfig(RULE_ID3);
6339
+ const policy = ix.policy.ruleConfig(RULE_ID4);
5830
6340
  if (!policy?.enabled) return [];
5831
6341
  const definitions = new Map(
5832
6342
  ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
@@ -5843,8 +6353,8 @@ function ruleComponentsShadowComponent(ix) {
5843
6353
  const blastRadius = usage?.blastRadius ?? 0;
5844
6354
  findings.push(
5845
6355
  makeFinding({
5846
- ruleId: RULE_ID3,
5847
- ruleVersion: RULE_VERSION3,
6356
+ ruleId: RULE_ID4,
6357
+ ruleVersion: RULE_VERSION4,
5848
6358
  severity: reviewVariant ? "info" : policy.severity ?? "warn",
5849
6359
  message: reviewVariant ? `${definition.exportName} wraps ${identity.canonicalTarget ?? "a canonical component"}; sanction it as a variant or consolidate its usages.` : `${definition.exportName} shadows ${identity.canonicalTarget ?? "a canonical component"} across ${usageCount} usage${usageCount === 1 ? "" : "s"}.`,
5850
6360
  location: definitionLocation(ix, definition, identity),
@@ -5882,8 +6392,8 @@ function definitionLocation(ix, definition, identity) {
5882
6392
  }
5883
6393
 
5884
6394
  // src/rules/components-forbidden-prop-value.ts
5885
- var RULE_ID4 = "components/forbidden-prop-value";
5886
- var RULE_VERSION4 = "1";
6395
+ var RULE_ID5 = "components/forbidden-prop-value";
6396
+ var RULE_VERSION5 = "1";
5887
6397
  function ruleComponentsForbiddenPropValue(ix) {
5888
6398
  const findings = [];
5889
6399
  const componentByNode = indexComponentByNodeId(ix);
@@ -5913,8 +6423,8 @@ function ruleComponentsForbiddenPropValue(ix) {
5913
6423
  } : void 0;
5914
6424
  findings.push(
5915
6425
  makeFinding({
5916
- ruleId: RULE_ID4,
5917
- ruleVersion: RULE_VERSION4,
6426
+ ruleId: RULE_ID5,
6427
+ ruleVersion: RULE_VERSION5,
5918
6428
  severity: policy.severity,
5919
6429
  message: buildMessage(
5920
6430
  node.element,
@@ -5958,8 +6468,8 @@ function buildMessage(element, prop, value, because, replacement) {
5958
6468
  }
5959
6469
 
5960
6470
  // src/rules/components-unknown-prop.ts
5961
- var RULE_ID5 = "components/unknown-prop";
5962
- var RULE_VERSION5 = "1";
6471
+ var RULE_ID6 = "components/unknown-prop";
6472
+ var RULE_VERSION6 = "1";
5963
6473
  function ruleComponentsUnknownProp(ix) {
5964
6474
  const policy = ix.policy.jsxUnknownPropsForbidden();
5965
6475
  if (!policy) return [];
@@ -5980,8 +6490,8 @@ function ruleComponentsUnknownProp(ix) {
5980
6490
  if (knownNames.has(propUsage.prop)) continue;
5981
6491
  findings.push(
5982
6492
  makeFinding({
5983
- ruleId: RULE_ID5,
5984
- ruleVersion: RULE_VERSION5,
6493
+ ruleId: RULE_ID6,
6494
+ ruleVersion: RULE_VERSION6,
5985
6495
  severity: policy.severity,
5986
6496
  message: `Unknown prop "${propUsage.prop}" on <${node.element}>. Allowed props: ${formatPropList(knownNames)}.`,
5987
6497
  location: node.location,
@@ -6013,7 +6523,7 @@ function formatPropList(names) {
6013
6523
  // src/rules/composition-pattern.ts
6014
6524
  var CARDINALITY_RULE_ID = "composition/cardinality";
6015
6525
  var CO_OCCURRENCE_RULE_ID = "composition/co-occurrence";
6016
- var RULE_VERSION6 = "1";
6526
+ var RULE_VERSION7 = "1";
6017
6527
  function ruleCompositionCardinality(ix) {
6018
6528
  return evaluate(ix, CARDINALITY_RULE_ID, "cardinality");
6019
6529
  }
@@ -6081,7 +6591,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
6081
6591
  const extras = selected.slice(constraint.max);
6082
6592
  return makeFinding({
6083
6593
  ruleId: ctx.ruleId,
6084
- ruleVersion: RULE_VERSION6,
6594
+ ruleVersion: RULE_VERSION7,
6085
6595
  severity: ctx.severity,
6086
6596
  message: cardinalityMaxMessage(pattern, constraint.max, count),
6087
6597
  location: extras[0].location,
@@ -6092,7 +6602,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
6092
6602
  if (constraint.min !== void 0 && count < constraint.min) {
6093
6603
  return makeFinding({
6094
6604
  ruleId: ctx.ruleId,
6095
- ruleVersion: RULE_VERSION6,
6605
+ ruleVersion: RULE_VERSION7,
6096
6606
  severity: ctx.severity,
6097
6607
  message: cardinalityMinMessage(pattern, constraint.min, count),
6098
6608
  location: region.container.location,
@@ -6113,7 +6623,7 @@ function checkCoOccurrence(ctx, region, pattern, constraint, selected) {
6113
6623
  if (satisfied) return null;
6114
6624
  return makeFinding({
6115
6625
  ruleId: ctx.ruleId,
6116
- ruleVersion: RULE_VERSION6,
6626
+ ruleVersion: RULE_VERSION7,
6117
6627
  severity: ctx.severity,
6118
6628
  message: coOccurrenceMessage(pattern, constraint),
6119
6629
  location: region.container.location,
@@ -6165,8 +6675,8 @@ function coOccurrenceMessage(pattern, constraint) {
6165
6675
  }
6166
6676
 
6167
6677
  // src/rules/jsx-preferred-component.ts
6168
- var RULE_ID6 = "components/preferred-component";
6169
- var RULE_VERSION7 = "1";
6678
+ var RULE_ID7 = "components/preferred-component";
6679
+ var RULE_VERSION8 = "1";
6170
6680
  function ruleJsxPreferredComponent(ix) {
6171
6681
  const policies = ix.policy.jsxComponentPreferred();
6172
6682
  if (policies.length === 0) return [];
@@ -6182,8 +6692,8 @@ function ruleJsxPreferredComponent(ix) {
6182
6692
  if (!replacement) continue;
6183
6693
  findings.push(
6184
6694
  makeFinding({
6185
- ruleId: RULE_ID6,
6186
- ruleVersion: RULE_VERSION7,
6695
+ ruleId: RULE_ID7,
6696
+ ruleVersion: RULE_VERSION8,
6187
6697
  severity: policy.severity,
6188
6698
  message: `<${node.element}> should use <${replacement}>.`,
6189
6699
  location: node.location,
@@ -6220,27 +6730,23 @@ function componentNameFromId(componentId2) {
6220
6730
  }
6221
6731
 
6222
6732
  // src/rules/jsx-preferred-import-path.ts
6223
- var RULE_ID7 = "imports/preferred-path";
6224
- var RULE_VERSION8 = "1";
6733
+ var RULE_ID8 = "imports/preferred-path";
6734
+ var RULE_VERSION9 = "1";
6225
6735
  function ruleJsxPreferredImportPath(ix) {
6226
6736
  const policies = ix.policy.jsxImportPathPreferred();
6227
6737
  if (policies.length === 0) return [];
6228
6738
  const findings = [];
6229
6739
  const seen = /* @__PURE__ */ new Set();
6230
6740
  for (const usage of ix.byKind("usage_import")) {
6741
+ const bridgeGoverned = policies.some((policy) => bridgeGovernsUnderlyingImport(policy, usage));
6231
6742
  const matches = policies.filter((policy) => {
6232
6743
  if (policy.bridge) {
6233
6744
  if (policy.bridge.implementationFiles.some((file) => sameRepoPath(file, usage.file))) {
6234
6745
  return false;
6235
6746
  }
6236
- if (!ownedImportMatchesRoot(usage.source, policy.from)) return false;
6237
- if (ownedImportsEqual(usage.source, policy.from)) {
6238
- return usage.imported === policy.bridge.underlyingExportName;
6239
- }
6240
- const subpath = usage.source.slice(policy.from.length + 1);
6241
- const leaf = subpath.split("/").at(-1);
6242
- return leaf === policy.bridge.underlyingExportName && (usage.imported === "default" || usage.imported === policy.bridge.underlyingExportName);
6747
+ return bridgeGovernsUnderlyingImport(policy, usage);
6243
6748
  }
6749
+ if (bridgeGoverned) return false;
6244
6750
  if (!ownedImportsEqual(policy.from, usage.source)) return false;
6245
6751
  if (policy.imported !== void 0 && policy.imported !== usage.imported) return false;
6246
6752
  return true;
@@ -6253,8 +6759,8 @@ function ruleJsxPreferredImportPath(ix) {
6253
6759
  seen.add(key);
6254
6760
  findings.push(
6255
6761
  makeFinding({
6256
- ruleId: RULE_ID7,
6257
- ruleVersion: RULE_VERSION8,
6762
+ ruleId: RULE_ID8,
6763
+ ruleVersion: RULE_VERSION9,
6258
6764
  severity: policy.severity,
6259
6765
  message: policy.bridge ? `Import ${policy.bridge.underlyingExportName} through the confirmed local ${policy.bridge.localExportName} wrapper from "${policy.to}".` : `Import from "${usage.source}" should use "${policy.to}".`,
6260
6766
  location: usage.location,
@@ -6294,6 +6800,17 @@ function ruleJsxPreferredImportPath(ix) {
6294
6800
  }
6295
6801
  return findings;
6296
6802
  }
6803
+ function bridgeGovernsUnderlyingImport(policy, usage) {
6804
+ const bridge = policy.bridge;
6805
+ if (!bridge) return false;
6806
+ if (!ownedImportMatchesRoot(usage.source, policy.from)) return false;
6807
+ if (ownedImportsEqual(usage.source, policy.from)) {
6808
+ return usage.imported === bridge.underlyingExportName;
6809
+ }
6810
+ const subpath = usage.source.slice(policy.from.length + 1);
6811
+ const leaf = subpath.split("/").at(-1);
6812
+ return leaf === bridge.underlyingExportName && (usage.imported === "default" || usage.imported === bridge.underlyingExportName);
6813
+ }
6297
6814
  function sameRepoPath(left, right) {
6298
6815
  return normalizeRepoPath(left) === normalizeRepoPath(right);
6299
6816
  }
@@ -6302,10 +6819,10 @@ function normalizeRepoPath(path) {
6302
6819
  }
6303
6820
 
6304
6821
  // src/rules/props-invalid-value.ts
6305
- var RULE_ID8 = "props/invalid-value";
6306
- var RULE_VERSION9 = "1";
6822
+ var RULE_ID9 = "props/invalid-value";
6823
+ var RULE_VERSION10 = "1";
6307
6824
  function rulePropsInvalidValue(ix) {
6308
- const policy = ix.policy.ruleConfig(RULE_ID8);
6825
+ const policy = ix.policy.ruleConfig(RULE_ID9);
6309
6826
  if (!policy?.enabled) return [];
6310
6827
  const findings = [];
6311
6828
  const componentByNode = indexComponentByNodeId(ix);
@@ -6334,8 +6851,8 @@ function rulePropsInvalidValue(ix) {
6334
6851
  } : void 0;
6335
6852
  findings.push(
6336
6853
  makeFinding({
6337
- ruleId: RULE_ID8,
6338
- ruleVersion: RULE_VERSION9,
6854
+ ruleId: RULE_ID9,
6855
+ ruleVersion: RULE_VERSION10,
6339
6856
  severity: policy.severity ?? "warn",
6340
6857
  message: buildMessage2(node.element, propUsage.prop, propUsage.value, allowedValues),
6341
6858
  location: propUsage.location ?? node.location,
@@ -6534,8 +7051,8 @@ function localTokenCandidates(ix, category) {
6534
7051
  }
6535
7052
 
6536
7053
  // src/rules/styles-no-raw-color.ts
6537
- var RULE_ID9 = "styles/no-raw-color";
6538
- var RULE_VERSION10 = "1";
7054
+ var RULE_ID10 = "styles/no-raw-color";
7055
+ var RULE_VERSION11 = "1";
6539
7056
  function ruleStylesNoRawColor(ix) {
6540
7057
  const policy = ix.policy.rawColorPolicy();
6541
7058
  if (!policy) return [];
@@ -6549,14 +7066,15 @@ function ruleStylesNoRawColor(ix) {
6549
7066
  if (!color) continue;
6550
7067
  if (isExemptColor(color, policy.except)) continue;
6551
7068
  const resolution = resolveColorToken(decl.property, color, colorTokens);
6552
- const fixEmission = resolution?.kind === "exact" ? buildTokenFix(decl.property, decl.value, color, resolution, ix) : void 0;
7069
+ const hopped = hoppedValue(decl);
7070
+ const fixEmission = resolution?.kind === "exact" && !hopped ? buildTokenFix(decl.property, decl.value, color, resolution, ix) : void 0;
6553
7071
  const evidenceIds = resolution ? [decl.id, policy.id, resolution.token.id] : [decl.id, policy.id];
6554
7072
  findings.push(
6555
7073
  makeFinding({
6556
- ruleId: RULE_ID9,
6557
- ruleVersion: RULE_VERSION10,
7074
+ ruleId: RULE_ID10,
7075
+ ruleVersion: RULE_VERSION11,
6558
7076
  severity: policy.severity,
6559
- message: colorMessage(color, `\`${decl.property}\``, resolution, preferLabel),
7077
+ message: colorMessage(color, `\`${decl.property}\``, resolution, preferLabel) + (hopped?.suffix ?? ""),
6560
7078
  location: decl.location,
6561
7079
  evidence: ix.evidence(evidenceIds),
6562
7080
  fingerprintIdentity: {
@@ -6575,7 +7093,8 @@ function ruleStylesNoRawColor(ix) {
6575
7093
  source: "css",
6576
7094
  suggestedToken: resolution?.token.name,
6577
7095
  ...resolution ? { tokenMatch: resolution.kind } : {},
6578
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7096
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7097
+ ...hopped?.attributes ?? {}
6579
7098
  }
6580
7099
  })
6581
7100
  );
@@ -6589,8 +7108,8 @@ function ruleStylesNoRawColor(ix) {
6589
7108
  const evidenceIds2 = componentEvidenceId2 ? [node2.id, componentEvidenceId2, inline.id, policy.id] : [node2.id, inline.id, policy.id];
6590
7109
  findings.push(
6591
7110
  makeFinding({
6592
- ruleId: RULE_ID9,
6593
- ruleVersion: RULE_VERSION10,
7111
+ ruleId: RULE_ID10,
7112
+ ruleVersion: RULE_VERSION11,
6594
7113
  severity: advisorySeverity,
6595
7114
  message: `Runtime-constructed raw color \`${inline.value}\` on inline \`${inline.property}\`. Use a ${preferLabel} instead.`,
6596
7115
  location: node2.location,
@@ -6621,15 +7140,16 @@ function ruleStylesNoRawColor(ix) {
6621
7140
  if (!node) continue;
6622
7141
  const componentEvidenceId = componentByNode.get(node.id)?.id;
6623
7142
  const resolution = resolveColorToken(inline.property, color, colorTokens);
6624
- const fixEmission = resolution?.kind === "exact" ? buildTokenFix(inline.property, inline.value, color, resolution, ix) : void 0;
7143
+ const hopped = hoppedValue(inline);
7144
+ const fixEmission = resolution?.kind === "exact" && !hopped ? buildTokenFix(inline.property, inline.value, color, resolution, ix) : void 0;
6625
7145
  const baseEvidence = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id] : [node.id, inline.id, policy.id];
6626
7146
  const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
6627
7147
  findings.push(
6628
7148
  makeFinding({
6629
- ruleId: RULE_ID9,
6630
- ruleVersion: RULE_VERSION10,
7149
+ ruleId: RULE_ID10,
7150
+ ruleVersion: RULE_VERSION11,
6631
7151
  severity: policy.severity,
6632
- message: colorMessage(color, `inline \`${inline.property}\``, resolution, preferLabel),
7152
+ message: colorMessage(color, `inline \`${inline.property}\``, resolution, preferLabel) + (hopped?.suffix ?? ""),
6633
7153
  location: node.location,
6634
7154
  evidence: ix.evidence(evidenceIds),
6635
7155
  fingerprintIdentity: {
@@ -6648,7 +7168,8 @@ function ruleStylesNoRawColor(ix) {
6648
7168
  source: "jsx",
6649
7169
  suggestedToken: resolution?.token.name,
6650
7170
  ...resolution ? { tokenMatch: resolution.kind } : {},
6651
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7171
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7172
+ ...hopped?.attributes ?? {}
6652
7173
  }
6653
7174
  })
6654
7175
  );
@@ -6670,8 +7191,8 @@ function ruleStylesNoRawColor(ix) {
6670
7191
  const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
6671
7192
  findings.push(
6672
7193
  makeFinding({
6673
- ruleId: RULE_ID9,
6674
- ruleVersion: RULE_VERSION10,
7194
+ ruleId: RULE_ID10,
7195
+ ruleVersion: RULE_VERSION11,
6675
7196
  severity: advisorySeverity,
6676
7197
  message: colorMessage(color, `\`${prop.prop}\``, resolution, preferLabel),
6677
7198
  location: prop.location ?? node.location,
@@ -6822,8 +7343,8 @@ function escapeRegExp(value) {
6822
7343
  }
6823
7344
 
6824
7345
  // src/rules/styles-no-raw-dimensions.ts
6825
- var RULE_ID10 = "styles/no-raw-dimensions";
6826
- var RULE_VERSION11 = "1";
7346
+ var RULE_ID11 = "styles/no-raw-dimensions";
7347
+ var RULE_VERSION12 = "1";
6827
7348
  function ruleStylesNoRawDimensions(ix) {
6828
7349
  const policy = ix.policy.rawDimensionPolicy();
6829
7350
  if (!policy) return [];
@@ -6840,13 +7361,14 @@ function ruleStylesNoRawDimensions(ix) {
6840
7361
  if (!resolution) continue;
6841
7362
  const tokenIds = resolutionTokenIds(resolution);
6842
7363
  const evidenceIds = tokenIds.length ? [decl.id, policy.id, ...tokenIds] : [decl.id, policy.id];
6843
- const fixEmission = dimensionFix(decl.property, resolution, ix);
7364
+ const hopped = hoppedValue(decl);
7365
+ const fixEmission = hopped ? void 0 : dimensionFix(decl.property, resolution, ix);
6844
7366
  findings.push(
6845
7367
  makeFinding({
6846
- ruleId: RULE_ID10,
6847
- ruleVersion: RULE_VERSION11,
7368
+ ruleId: RULE_ID11,
7369
+ ruleVersion: RULE_VERSION12,
6848
7370
  severity: policy.severity,
6849
- message: dimensionMessage(decl.value, decl.property, preferLabel, resolution, false),
7371
+ message: dimensionMessage(decl.value, decl.property, preferLabel, resolution, false) + (hopped?.suffix ?? ""),
6850
7372
  location: decl.location,
6851
7373
  evidence: ix.evidence(evidenceIds),
6852
7374
  fingerprintIdentity: {
@@ -6863,7 +7385,8 @@ function ruleStylesNoRawDimensions(ix) {
6863
7385
  rawValue: decl.value,
6864
7386
  source: "css",
6865
7387
  suggestedToken: resolutionSuggestedToken(resolution),
6866
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7388
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7389
+ ...hopped?.attributes ?? {}
6867
7390
  }
6868
7391
  })
6869
7392
  );
@@ -6882,13 +7405,14 @@ function ruleStylesNoRawDimensions(ix) {
6882
7405
  const baseEvidence = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id] : [node.id, inline.id, policy.id];
6883
7406
  const tokenIds = resolutionTokenIds(resolution);
6884
7407
  const evidenceIds = tokenIds.length ? [...baseEvidence, ...tokenIds] : baseEvidence;
6885
- const fixEmission = dimensionFix(inline.property, resolution, ix);
7408
+ const hopped = hoppedValue(inline);
7409
+ const fixEmission = hopped ? void 0 : dimensionFix(inline.property, resolution, ix);
6886
7410
  findings.push(
6887
7411
  makeFinding({
6888
- ruleId: RULE_ID10,
6889
- ruleVersion: RULE_VERSION11,
7412
+ ruleId: RULE_ID11,
7413
+ ruleVersion: RULE_VERSION12,
6890
7414
  severity: policy.severity,
6891
- message: dimensionMessage(inline.value, inline.property, preferLabel, resolution, true),
7415
+ message: dimensionMessage(inline.value, inline.property, preferLabel, resolution, true) + (hopped?.suffix ?? ""),
6892
7416
  location: node.location,
6893
7417
  evidence: ix.evidence(evidenceIds),
6894
7418
  fingerprintIdentity: {
@@ -6905,7 +7429,8 @@ function ruleStylesNoRawDimensions(ix) {
6905
7429
  rawValue: inline.value,
6906
7430
  source: "jsx",
6907
7431
  suggestedToken: resolutionSuggestedToken(resolution),
6908
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7432
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7433
+ ...hopped?.attributes ?? {}
6909
7434
  }
6910
7435
  })
6911
7436
  );
@@ -7154,8 +7679,8 @@ function parseLengthParts(raw) {
7154
7679
  }
7155
7680
 
7156
7681
  // src/rules/styles-no-raw-spacing.ts
7157
- var RULE_ID11 = "styles/no-raw-spacing";
7158
- var RULE_VERSION12 = "1";
7682
+ var RULE_ID12 = "styles/no-raw-spacing";
7683
+ var RULE_VERSION13 = "1";
7159
7684
  function ruleStylesNoRawSpacing(ix) {
7160
7685
  const findings = [];
7161
7686
  const lookupCache = /* @__PURE__ */ new Map();
@@ -7195,12 +7720,13 @@ function checkDeclaration(ix, decl, lookupFor) {
7195
7720
  tokens: lookupFor(scale)
7196
7721
  });
7197
7722
  if (!checked) return null;
7198
- const fixEmission = buildSpacingFix(decl.property, checked, ix);
7723
+ const hopped = hoppedValue(decl);
7724
+ const fixEmission = hopped ? void 0 : buildSpacingFix(decl.property, checked, ix);
7199
7725
  return makeFinding({
7200
- ruleId: RULE_ID11,
7201
- ruleVersion: RULE_VERSION12,
7726
+ ruleId: RULE_ID12,
7727
+ ruleVersion: RULE_VERSION13,
7202
7728
  severity: policy.severity,
7203
- message: spacingMessage(decl.property, decl.value, allowed, scale, checked),
7729
+ message: spacingMessage(decl.property, decl.value, allowed, scale, checked) + (hopped?.suffix ?? ""),
7204
7730
  location: decl.location,
7205
7731
  evidence: ix.evidence([decl.id, policy.id, scale.id]),
7206
7732
  fingerprintIdentity: {
@@ -7226,7 +7752,8 @@ function checkDeclaration(ix, decl, lookupFor) {
7226
7752
  assumedRootFontSizePx: checked.assumedRootFontSizePx,
7227
7753
  assumedEmBasePx: checked.assumedEmBasePx,
7228
7754
  source: "css",
7229
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7755
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7756
+ ...hopped?.attributes ?? {}
7230
7757
  }
7231
7758
  });
7232
7759
  }
@@ -7246,16 +7773,17 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
7246
7773
  bareNumberFix: inline.valueKind === "number"
7247
7774
  });
7248
7775
  if (!checked) return null;
7249
- const fixEmission = buildSpacingFix(inline.property, checked, ix);
7776
+ const hopped = hoppedValue(inline);
7777
+ const fixEmission = hopped ? void 0 : buildSpacingFix(inline.property, checked, ix);
7250
7778
  const node = readUsageNode(ix, inline.nodeId);
7251
7779
  if (!node) return null;
7252
7780
  const componentEvidenceId = componentByNode.get(node.id)?.id;
7253
7781
  const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
7254
7782
  return makeFinding({
7255
- ruleId: RULE_ID11,
7256
- ruleVersion: RULE_VERSION12,
7783
+ ruleId: RULE_ID12,
7784
+ ruleVersion: RULE_VERSION13,
7257
7785
  severity: policy.severity,
7258
- message: spacingMessage(inline.property, inline.value, allowed, scale, checked),
7786
+ message: spacingMessage(inline.property, inline.value, allowed, scale, checked) + (hopped?.suffix ?? ""),
7259
7787
  location: node.location,
7260
7788
  evidence: ix.evidence(evidenceIds),
7261
7789
  fingerprintIdentity: {
@@ -7281,7 +7809,8 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
7281
7809
  assumedRootFontSizePx: checked.assumedRootFontSizePx,
7282
7810
  assumedEmBasePx: checked.assumedEmBasePx,
7283
7811
  source: "jsx",
7284
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7812
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7813
+ ...hopped?.attributes ?? {}
7285
7814
  }
7286
7815
  });
7287
7816
  }
@@ -7314,8 +7843,8 @@ function spacingMessage(property, value, allowed, scale, checked) {
7314
7843
  }
7315
7844
 
7316
7845
  // src/rules/styles-no-raw-typography.ts
7317
- var RULE_ID12 = "styles/no-raw-typography";
7318
- var RULE_VERSION13 = "1";
7846
+ var RULE_ID13 = "styles/no-raw-typography";
7847
+ var RULE_VERSION14 = "1";
7319
7848
  var FONT_SIZE_PROPERTIES = /* @__PURE__ */ new Set(["font-size", "fontsize"]);
7320
7849
  function ruleStylesNoRawTypography(ix) {
7321
7850
  const policy = ix.policy.fontSizeScale();
@@ -7330,13 +7859,14 @@ function ruleStylesNoRawTypography(ix) {
7330
7859
  if (decl.property.toLowerCase() !== "font-size") continue;
7331
7860
  const checked = checkFontSize(decl.value, allowed, scale, tokens);
7332
7861
  if (!checked) continue;
7333
- const fixEmission = buildFix(decl.property, checked, ix);
7862
+ const hopped = hoppedValue(decl);
7863
+ const fixEmission = hopped ? void 0 : buildFix(decl.property, checked, ix);
7334
7864
  findings.push(
7335
7865
  makeFinding({
7336
- ruleId: RULE_ID12,
7337
- ruleVersion: RULE_VERSION13,
7866
+ ruleId: RULE_ID13,
7867
+ ruleVersion: RULE_VERSION14,
7338
7868
  severity: policy.severity,
7339
- message: typographyMessage(decl.property, decl.value, allowed, scale.unit, checked),
7869
+ message: typographyMessage(decl.property, decl.value, allowed, scale.unit, checked) + (hopped?.suffix ?? ""),
7340
7870
  location: decl.location,
7341
7871
  evidence: ix.evidence([decl.id, policy.id, scale.id]),
7342
7872
  fingerprintIdentity: {
@@ -7356,7 +7886,8 @@ function ruleStylesNoRawTypography(ix) {
7356
7886
  suggestedValue: checked.suggestedValue,
7357
7887
  matchedToken: checked.matchedToken,
7358
7888
  source: "css",
7359
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7889
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7890
+ ...hopped?.attributes ?? {}
7360
7891
  }
7361
7892
  })
7362
7893
  );
@@ -7373,17 +7904,18 @@ function ruleStylesNoRawTypography(ix) {
7373
7904
  inline.valueKind === "number"
7374
7905
  );
7375
7906
  if (!checked) continue;
7376
- const fixEmission = buildFix(inline.property, checked, ix);
7907
+ const hopped = hoppedValue(inline);
7908
+ const fixEmission = hopped ? void 0 : buildFix(inline.property, checked, ix);
7377
7909
  const node = readUsageNode(ix, inline.nodeId);
7378
7910
  if (!node) continue;
7379
7911
  const componentEvidenceId = componentByNode.get(node.id)?.id;
7380
7912
  const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
7381
7913
  findings.push(
7382
7914
  makeFinding({
7383
- ruleId: RULE_ID12,
7384
- ruleVersion: RULE_VERSION13,
7915
+ ruleId: RULE_ID13,
7916
+ ruleVersion: RULE_VERSION14,
7385
7917
  severity: policy.severity,
7386
- message: typographyMessage(inline.property, inline.value, allowed, scale.unit, checked),
7918
+ message: typographyMessage(inline.property, inline.value, allowed, scale.unit, checked) + (hopped?.suffix ?? ""),
7387
7919
  location: node.location,
7388
7920
  evidence: ix.evidence(evidenceIds),
7389
7921
  fingerprintIdentity: {
@@ -7403,7 +7935,8 @@ function ruleStylesNoRawTypography(ix) {
7403
7935
  suggestedValue: checked.suggestedValue,
7404
7936
  matchedToken: checked.matchedToken,
7405
7937
  source: "jsx",
7406
- ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
7938
+ ...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {},
7939
+ ...hopped?.attributes ?? {}
7407
7940
  }
7408
7941
  })
7409
7942
  );
@@ -7483,15 +8016,15 @@ function typographyMessage(property, value, allowed, unit, checked) {
7483
8016
  }
7484
8017
 
7485
8018
  // src/rules/theme-no-theme-coupled-literal.ts
7486
- var RULE_ID13 = "theme/no-theme-coupled-literal";
7487
- var RULE_VERSION14 = "1";
8019
+ var RULE_ID14 = "theme/no-theme-coupled-literal";
8020
+ var RULE_VERSION15 = "1";
7488
8021
  var COLOR_LITERAL_GLOBAL = /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?|oklab|oklch|lab|lch)\(\s*[\d.%\s,\/]+\s*\)/g;
7489
8022
  var WHITE_RGBA = /^rgba?\(\s*255\s*,\s*255\s*,\s*255/i;
7490
8023
  var BLACK_RGBA = /^rgba?\(\s*0\s*,\s*0\s*,\s*0/i;
7491
8024
  var BOX_SHADOW_PROP = /^(?:-webkit-|-moz-)?box-shadow$/i;
7492
8025
  var NEUTRAL_SHADOW = /^(?:none|inherit|initial|unset|revert|revert-layer)\s*$/i;
7493
8026
  function ruleThemeNoThemeCoupledLiteral(ix) {
7494
- const policy = ix.policy.ruleConfig(RULE_ID13);
8027
+ const policy = ix.policy.ruleConfig(RULE_ID14);
7495
8028
  if (!policy?.enabled) return [];
7496
8029
  const findings = [];
7497
8030
  for (const decl of ix.byKind("style_declaration")) {
@@ -7500,8 +8033,8 @@ function ruleThemeNoThemeCoupledLiteral(ix) {
7500
8033
  if (!hit) continue;
7501
8034
  findings.push(
7502
8035
  makeFinding({
7503
- ruleId: RULE_ID13,
7504
- ruleVersion: RULE_VERSION14,
8036
+ ruleId: RULE_ID14,
8037
+ ruleVersion: RULE_VERSION15,
7505
8038
  severity: policy.severity ?? "warn",
7506
8039
  message: hit.message,
7507
8040
  location: decl.location,
@@ -7591,8 +8124,8 @@ function isTokenFile(filePath) {
7591
8124
  }
7592
8125
 
7593
8126
  // src/rules/tailwind-arbitrary-color.ts
7594
- var RULE_ID14 = "tailwind/arbitrary-color";
7595
- var RULE_VERSION15 = "1";
8127
+ var RULE_ID15 = "tailwind/arbitrary-color";
8128
+ var RULE_VERSION16 = "1";
7596
8129
  var COLOR_UTILITIES = /* @__PURE__ */ new Set([
7597
8130
  "accent",
7598
8131
  "bg",
@@ -7634,8 +8167,8 @@ function ruleTailwindArbitraryColor(ix) {
7634
8167
  const evidenceIds = node ? [node.id, klass.id, policy.id] : [klass.id, policy.id];
7635
8168
  findings.push(
7636
8169
  makeFinding({
7637
- ruleId: RULE_ID14,
7638
- ruleVersion: RULE_VERSION15,
8170
+ ruleId: RULE_ID15,
8171
+ ruleVersion: RULE_VERSION16,
7639
8172
  severity: policy.severity,
7640
8173
  message: `\`${klass.raw}\` uses arbitrary raw color ${color}. Use a ${policy.prefer === "token" ? "design token" : "CSS variable"} instead.`,
7641
8174
  location: klass.location,
@@ -7666,8 +8199,8 @@ function fingerprintIdentity(klass, color) {
7666
8199
  }
7667
8200
 
7668
8201
  // src/rules/tailwind-arbitrary-spacing.ts
7669
- var RULE_ID15 = "tailwind/arbitrary-spacing";
7670
- var RULE_VERSION16 = "1";
8202
+ var RULE_ID16 = "tailwind/arbitrary-spacing";
8203
+ var RULE_VERSION17 = "1";
7671
8204
  var SPACING_UTILITY_TO_PROPERTY = /* @__PURE__ */ new Map([
7672
8205
  ["m", "margin"],
7673
8206
  ["mt", "margin"],
@@ -7713,8 +8246,8 @@ function ruleTailwindArbitrarySpacing(ix) {
7713
8246
  const fix = buildFix2(klass);
7714
8247
  findings.push(
7715
8248
  makeFinding({
7716
- ruleId: RULE_ID15,
7717
- ruleVersion: RULE_VERSION16,
8249
+ ruleId: RULE_ID16,
8250
+ ruleVersion: RULE_VERSION17,
7718
8251
  severity: policy.severity,
7719
8252
  message: `\`${klass.raw}\` is an arbitrary spacing value not on the project's spacing scale.`,
7720
8253
  location: klass.location,
@@ -7845,8 +8378,8 @@ function matchesTokenGlob(value, pattern) {
7845
8378
  }
7846
8379
 
7847
8380
  // src/rules/tailwind-forbidden-palette.ts
7848
- var RULE_ID16 = "tailwind/forbidden-palette";
7849
- var RULE_VERSION17 = "1";
8381
+ var RULE_ID17 = "tailwind/forbidden-palette";
8382
+ var RULE_VERSION18 = "1";
7850
8383
  function ruleTailwindForbiddenPalette(ix) {
7851
8384
  const deny = ix.policy.tailwindPaletteDeny();
7852
8385
  const allow = ix.policy.tailwindPaletteAllow();
@@ -7871,8 +8404,8 @@ function ruleTailwindForbiddenPalette(ix) {
7871
8404
  const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
7872
8405
  findings.push(
7873
8406
  makeFinding({
7874
- ruleId: RULE_ID16,
7875
- ruleVersion: RULE_VERSION17,
8407
+ ruleId: RULE_ID17,
8408
+ ruleVersion: RULE_VERSION18,
7876
8409
  severity: policy.severity,
7877
8410
  message: messageFor(klass, token, deniedPattern, allow?.patterns ?? []),
7878
8411
  location: klass.location,
@@ -7919,8 +8452,8 @@ function buildFix3(klass) {
7919
8452
  }
7920
8453
 
7921
8454
  // src/rules/tailwind-off-scale-spacing-token.ts
7922
- var RULE_ID17 = "tailwind/off-scale-spacing-token";
7923
- var RULE_VERSION18 = "1";
8455
+ var RULE_ID18 = "tailwind/off-scale-spacing-token";
8456
+ var RULE_VERSION19 = "1";
7924
8457
  function ruleTailwindOffScaleSpacingToken(ix) {
7925
8458
  const resolvedByKey = indexResolvedTailwindTokens(ix);
7926
8459
  const findings = [];
@@ -7946,8 +8479,8 @@ function ruleTailwindOffScaleSpacingToken(ix) {
7946
8479
  const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id, scale.id] : [klass.id, resolved.id, policy.id, scale.id];
7947
8480
  findings.push(
7948
8481
  makeFinding({
7949
- ruleId: RULE_ID17,
7950
- ruleVersion: RULE_VERSION18,
8482
+ ruleId: RULE_ID18,
8483
+ ruleVersion: RULE_VERSION19,
7951
8484
  severity: policy.severity,
7952
8485
  message: `\`${klass.raw}\` resolves to ${formatResolvedValue(signedValue, scale.unit)}, which is not on the project's spacing scale [${allowed.join(", ")}].`,
7953
8486
  location: klass.location,
@@ -7996,8 +8529,8 @@ function buildFix4(raw, utility) {
7996
8529
  }
7997
8530
 
7998
8531
  // src/rules/tailwind-raw-color-via-token.ts
7999
- var RULE_ID18 = "tailwind/raw-color-via-token";
8000
- var RULE_VERSION19 = "1";
8532
+ var RULE_ID19 = "tailwind/raw-color-via-token";
8533
+ var RULE_VERSION20 = "1";
8001
8534
  function ruleTailwindRawColorViaToken(ix) {
8002
8535
  const allow = ix.policy.tailwindPaletteAllow();
8003
8536
  if (!allow || allow.patterns.length === 0) return [];
@@ -8017,8 +8550,8 @@ function ruleTailwindRawColorViaToken(ix) {
8017
8550
  const evidenceIds = node ? [node.id, klass.id, resolved.id, allow.id] : [klass.id, resolved.id, allow.id];
8018
8551
  findings.push(
8019
8552
  makeFinding({
8020
- ruleId: RULE_ID18,
8021
- ruleVersion: RULE_VERSION19,
8553
+ ruleId: RULE_ID19,
8554
+ ruleVersion: RULE_VERSION20,
8022
8555
  severity: allow.severity,
8023
8556
  message: `\`${klass.raw}\` resolves to raw color \`${color}\` (${sourceLabel2(resolved.resolved.source)}). Project policy requires \`${allow.patterns.join("`, `")}\` palette tokens.`,
8024
8557
  location: klass.location,
@@ -8051,8 +8584,8 @@ function sourceLabel2(source) {
8051
8584
  }
8052
8585
 
8053
8586
  // src/rules/tailwind-unknown-class.ts
8054
- var RULE_ID19 = "tailwind/unknown-class";
8055
- var RULE_VERSION20 = "1";
8587
+ var RULE_ID20 = "tailwind/unknown-class";
8588
+ var RULE_VERSION21 = "1";
8056
8589
  var UNIVERSAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
8057
8590
  "white",
8058
8591
  "black",
@@ -8073,8 +8606,8 @@ function ruleTailwindUnknownClass(ix) {
8073
8606
  const evidenceIds2 = node2 ? [node2.id, klass.id, policy.id] : [klass.id, policy.id];
8074
8607
  findings.push(
8075
8608
  makeFinding({
8076
- ruleId: RULE_ID19,
8077
- ruleVersion: RULE_VERSION20,
8609
+ ruleId: RULE_ID20,
8610
+ ruleVersion: RULE_VERSION21,
8078
8611
  severity: policy.severity,
8079
8612
  message: `\`${klass.raw}\` could not be parsed as a known Tailwind utility.`,
8080
8613
  location: klass.location,
@@ -8108,8 +8641,8 @@ function ruleTailwindUnknownClass(ix) {
8108
8641
  const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
8109
8642
  findings.push(
8110
8643
  makeFinding({
8111
- ruleId: RULE_ID19,
8112
- ruleVersion: RULE_VERSION20,
8644
+ ruleId: RULE_ID20,
8645
+ ruleVersion: RULE_VERSION21,
8113
8646
  severity: policy.severity,
8114
8647
  message: `\`${klass.raw}\` did not resolve to any known palette token.`,
8115
8648
  location: klass.location,
@@ -8137,12 +8670,12 @@ function ruleTailwindUnknownClass(ix) {
8137
8670
  }
8138
8671
 
8139
8672
  // src/rules/tokens-require-dual-fallback.ts
8140
- var RULE_ID20 = "tokens/require-dual-fallback";
8141
- var RULE_VERSION21 = "1";
8673
+ var RULE_ID21 = "tokens/require-dual-fallback";
8674
+ var RULE_VERSION22 = "1";
8142
8675
  var CSS_VAR_WITH_OPTIONAL_FALLBACK = /var\(\s*(--[A-Za-z0-9_-]+)(\s*,\s*[^)]*)?\)/g;
8143
8676
  var SASS_FILE = /\.(scss|sass)$/i;
8144
8677
  function ruleTokensRequireDualFallback(ix) {
8145
- const policy = ix.policy.ruleConfig(RULE_ID20);
8678
+ const policy = ix.policy.ruleConfig(RULE_ID21);
8146
8679
  if (!policy?.enabled) return [];
8147
8680
  const tokensByCssVariable = indexTokensByCssVariable(localTokenCandidates(ix));
8148
8681
  const findings = [];
@@ -8177,8 +8710,8 @@ function ruleTokensRequireDualFallback(ix) {
8177
8710
  ) : void 0;
8178
8711
  findings.push(
8179
8712
  makeFinding({
8180
- ruleId: RULE_ID20,
8181
- ruleVersion: RULE_VERSION21,
8713
+ ruleId: RULE_ID21,
8714
+ ruleVersion: RULE_VERSION22,
8182
8715
  severity: policy.severity ?? "warn",
8183
8716
  message: hasScssVariable ? `var(${tokenName}) is missing an SCSS fallback. Use var(${tokenName}, ${scssVar}).` : `var(${tokenName}) is missing a fallback. No matching SCSS variable was found in the token source; add a fallback manually or define ${scssVar}.`,
8184
8717
  location: decl.location,
@@ -8218,8 +8751,8 @@ function indexTokensByCssVariable(tokens) {
8218
8751
  }
8219
8752
 
8220
8753
  // src/rules/tokens-css-vars-must-be-defined.ts
8221
- var RULE_ID21 = "tokens/css-vars-must-be-defined";
8222
- var RULE_VERSION22 = "1";
8754
+ var RULE_ID22 = "tokens/css-vars-must-be-defined";
8755
+ var RULE_VERSION23 = "1";
8223
8756
  var CSS_VAR_REFERENCE = /var\(\s*(--[A-Za-z0-9_-]+)/gi;
8224
8757
  function ruleTokensCssVarsMustBeDefined(ix) {
8225
8758
  const policy = ix.policy.cssVarsMustBeDefined();
@@ -8246,8 +8779,8 @@ function ruleTokensCssVarsMustBeDefined(ix) {
8246
8779
  seen.add(tokenName);
8247
8780
  findings.push(
8248
8781
  makeFinding({
8249
- ruleId: RULE_ID21,
8250
- ruleVersion: RULE_VERSION22,
8782
+ ruleId: RULE_ID22,
8783
+ ruleVersion: RULE_VERSION23,
8251
8784
  severity: policy.severity,
8252
8785
  message: `var(${tokenName}) is not in the contract token vocabulary. Use a token defined in your contract's token source files.`,
8253
8786
  location: decl.location,
@@ -8294,8 +8827,8 @@ function skipToCloseParen(value, openParenIndex) {
8294
8827
  }
8295
8828
 
8296
8829
  // src/rules/tokens-upstream-drift.ts
8297
- var RULE_ID22 = "tokens/upstream-drift";
8298
- var RULE_VERSION23 = "1";
8830
+ var RULE_ID23 = "tokens/upstream-drift";
8831
+ var RULE_VERSION24 = "1";
8299
8832
  function ruleTokensUpstreamDrift(ix) {
8300
8833
  const local = ix.tokens.list().filter((token) => token.role !== "upstream");
8301
8834
  const upstream = ix.tokens.list().filter((token) => token.role === "upstream");
@@ -8321,8 +8854,8 @@ function ruleTokensUpstreamDrift(ix) {
8321
8854
  };
8322
8855
  findings.push(
8323
8856
  makeFinding({
8324
- ruleId: RULE_ID22,
8325
- ruleVersion: RULE_VERSION23,
8857
+ ruleId: RULE_ID23,
8858
+ ruleVersion: RULE_VERSION24,
8326
8859
  severity: "warn",
8327
8860
  message: `Local token ${token.name} is ${token.value}, but upstream ${upstreamName} is ${expected.value}.`,
8328
8861
  location,
@@ -8354,8 +8887,12 @@ var BLOCKING_RULE_ALLOWLIST = /* @__PURE__ */ new Set([
8354
8887
  // off-scale Tailwind spacing utility
8355
8888
  "components/prefer-library",
8356
8889
  // raw element bypasses a canonical component
8357
- "components/preferred-component"
8890
+ "components/shadow-component",
8891
+ // confirmed local semantic control shadows a canonical
8892
+ "components/preferred-component",
8358
8893
  // imported component bypasses its canonical
8894
+ "tokens/css-vars-must-be-defined"
8895
+ // off-contract custom property reference
8359
8896
  ]);
8360
8897
  function gatesCi(finding, failOnWarnings) {
8361
8898
  if (!canBlock(finding.evidenceGrade ?? "source_backed")) return false;
@@ -8366,9 +8903,13 @@ function gatesCi(finding, failOnWarnings) {
8366
8903
  function isDenyEligible(finding, failOnWarnings) {
8367
8904
  if (!BLOCKING_RULE_ALLOWLIST.has(finding.ruleId)) return false;
8368
8905
  if (finding.attributes?.advisory === true) return false;
8906
+ if (finding.ruleId === "components/shadow-component" && (finding.attributes?.state !== "shadow" || finding.attributes?.confidence !== "confirmed" || typeof finding.attributes?.canonicalTarget !== "string")) {
8907
+ return false;
8908
+ }
8369
8909
  if (!gatesCi(finding, failOnWarnings)) return false;
8370
8910
  if (finding.fix?.deterministic === false) {
8371
- if (finding.ruleId !== "components/prefer-library" || finding.attributes?.propCompatibility !== "observed-complete") {
8911
+ const exactCanonicalBypass = finding.ruleId === "components/prefer-library" && finding.attributes?.precisionTier === "exact-html" && typeof finding.attributes?.suggestedComponent === "string";
8912
+ if (!exactCanonicalBypass) {
8372
8913
  return false;
8373
8914
  }
8374
8915
  }
@@ -8405,6 +8946,7 @@ var RULE_TIER = {
8405
8946
  "tokens/require-dual-fallback": "hygiene",
8406
8947
  "theme/no-theme-coupled-literal": "hygiene",
8407
8948
  "a11y/required-accessible-name": "hygiene",
8949
+ "a11y/standard": "hygiene",
8408
8950
  // Composition constraints are authored contracts, but not part of the
8409
8951
  // two-choice headline contract; they self-activate when patterns are declared
8410
8952
  // (the compiler injects their configs), so off-by-default here is harmless.
@@ -8566,9 +9108,14 @@ var RULES = [
8566
9108
  },
8567
9109
  {
8568
9110
  id: "a11y/required-accessible-name",
8569
- version: "1",
9111
+ version: RULE_VERSION,
8570
9112
  run: ruleA11yRequiredAccessibleName
8571
9113
  },
9114
+ {
9115
+ id: "a11y/standard",
9116
+ version: "1",
9117
+ run: ruleA11yStandard
9118
+ },
8572
9119
  {
8573
9120
  id: "composition/cardinality",
8574
9121
  version: "1",
@@ -8607,12 +9154,21 @@ function hasEffectiveComponentVocabulary(options) {
8607
9154
  }
8608
9155
  function effectiveRuleConfigs(policy) {
8609
9156
  const configs = /* @__PURE__ */ new Map();
8610
- for (const fact of compileGlobalGovernanceFacts(policy)) {
8611
- if (fact.kind !== "governance_rule_config") continue;
8612
- configs.set(fact.ruleId, {
8613
- enabled: fact.enabled,
8614
- severity: fact.severity,
8615
- options: fact.options
9157
+ const facts = compileGlobalGovernanceFacts(policy);
9158
+ for (const fact of facts) {
9159
+ if (fact.kind === "governance_rule_config") {
9160
+ configs.set(fact.ruleId, {
9161
+ enabled: fact.enabled,
9162
+ severity: fact.severity,
9163
+ options: fact.options
9164
+ });
9165
+ }
9166
+ }
9167
+ const cssVarsPolicy = facts.find((fact) => fact.kind === "style_css_vars_must_be_defined");
9168
+ if (cssVarsPolicy?.kind === "style_css_vars_must_be_defined") {
9169
+ configs.set("tokens/css-vars-must-be-defined", {
9170
+ enabled: true,
9171
+ severity: cssVarsPolicy.severity
8616
9172
  });
8617
9173
  }
8618
9174
  return configs;
@@ -8623,10 +9179,9 @@ function policyDeclaresCssVars(policy) {
8623
9179
  function dedupe(values) {
8624
9180
  return [...new Set(values)];
8625
9181
  }
8626
- var RULE_FAMILY_IDS = /* @__PURE__ */ new Set(["tokens/hardcoded-values", "components/usage", "a11y/wcag"]);
8627
9182
  var CONSUMED_RULE_IDS = new Set(Object.keys(RULE_TIER));
8628
9183
  var RECOGNIZED_RULE_IDS = /* @__PURE__ */ new Set([...CONSUMED_RULE_IDS, ...RULE_FAMILY_IDS]);
8629
- var RULE_CONFIG_KEYS = /* @__PURE__ */ new Set(["enabled", "severity", "options"]);
9184
+ var RULE_CONFIG_KEYS = /* @__PURE__ */ new Set(["enabled", "severity", "options", "exclude"]);
8630
9185
  var PASSTHROUGH_KEYS = [
8631
9186
  {
8632
9187
  path: ["tokens"],
@@ -8665,7 +9220,7 @@ var PASSTHROUGH_KEYS = [
8665
9220
  },
8666
9221
  {
8667
9222
  path: ["govern", "ci"],
8668
- keys: ["failOnWarnings"]
9223
+ keys: ["failOnWarnings", "failOnInert"]
8669
9224
  }
8670
9225
  ];
8671
9226
  function objectRecord(value) {
@@ -8697,6 +9252,139 @@ function unconsumedKeyDiagnostic(path) {
8697
9252
  message: `${renderedPath} is not consumed by Fragments and has no effect. Remove it or use a supported config key.`
8698
9253
  };
8699
9254
  }
9255
+ var CONFIG_RECORD_FACT_KINDS = {
9256
+ scale: { section: "govern.scales", record: "scale", keys: ["name"] },
9257
+ scale_value: { section: "govern.scales", record: "scale.values", keys: ["scale", "value"] },
9258
+ style_raw_color_forbidden: {
9259
+ section: "govern.styles",
9260
+ record: "style.rawColors.forbid",
9261
+ keys: []
9262
+ },
9263
+ style_raw_dimension_forbidden: {
9264
+ section: "govern.styles",
9265
+ record: "style.rawDimensions.forbid",
9266
+ keys: []
9267
+ },
9268
+ style_property_scale: {
9269
+ section: "govern.styles",
9270
+ record: "style.rawSpacing.mustMatchScale",
9271
+ keys: ["property"]
9272
+ },
9273
+ style_font_size_scale: {
9274
+ section: "govern.styles",
9275
+ record: "style.fontSize.mustMatchScale",
9276
+ keys: []
9277
+ },
9278
+ style_css_vars_must_be_defined: {
9279
+ section: "govern.styles",
9280
+ record: "style.cssVars.mustBeDefined",
9281
+ keys: []
9282
+ },
9283
+ jsx_unknown_props_forbidden: {
9284
+ section: "govern.jsx",
9285
+ record: "jsx.unknownProps.forbid",
9286
+ keys: []
9287
+ },
9288
+ jsx_inline_style_forbidden_raw: {
9289
+ section: "govern.jsx",
9290
+ record: "jsx.inlineStyle.forbidRaw",
9291
+ keys: ["property"]
9292
+ },
9293
+ jsx_import_path_preferred: {
9294
+ section: "govern.jsx",
9295
+ record: "jsx.importPath.prefer",
9296
+ keys: ["from", "to", "imported"]
9297
+ },
9298
+ jsx_component_preferred: {
9299
+ section: "govern.jsx",
9300
+ record: "jsx.component.prefer",
9301
+ keys: ["from", "to"]
9302
+ },
9303
+ prop_value_avoided: {
9304
+ section: "govern.components",
9305
+ record: "prop.value.avoid",
9306
+ keys: ["componentId", "prop", "value"]
9307
+ },
9308
+ prop_value_forbidden: {
9309
+ section: "govern.components",
9310
+ record: "prop.value.forbid",
9311
+ keys: ["componentId", "prop", "value", "pathPattern"]
9312
+ },
9313
+ a11y_name_required: {
9314
+ section: "govern.components",
9315
+ record: "a11y.requireName",
9316
+ keys: ["componentId"]
9317
+ },
9318
+ tailwind_palette_allow: {
9319
+ section: "govern.tailwind",
9320
+ record: "palette.allow",
9321
+ keys: []
9322
+ },
9323
+ tailwind_palette_deny: { section: "govern.tailwind", record: "palette.deny", keys: [] },
9324
+ tailwind_unknown_class_enabled: {
9325
+ section: "govern.tailwind",
9326
+ record: "unknownClass",
9327
+ keys: []
9328
+ },
9329
+ governance_rule_config: { section: "govern.rules", record: "rule", keys: ["ruleId"] }
9330
+ };
9331
+ var COLLISION_DETAIL_KEYS = [
9332
+ "severity",
9333
+ "scale",
9334
+ "prefer",
9335
+ "enabled",
9336
+ "appliesTo",
9337
+ "except",
9338
+ "properties",
9339
+ "because"
9340
+ ];
9341
+ function renderFactFields(fact, keys) {
9342
+ return keys.filter((key) => fact[key] !== void 0).map((key) => `${key}=${Array.isArray(fact[key]) ? JSON.stringify(fact[key]) : fact[key]}`).join(" ");
9343
+ }
9344
+ function collidingRecordDiagnostic(conflict) {
9345
+ return collidingRecordDiagnostics([conflict])[0] ?? null;
9346
+ }
9347
+ function collidingRecordDiagnostics(conflicts) {
9348
+ const groups = /* @__PURE__ */ new Map();
9349
+ for (const conflict of conflicts) {
9350
+ const shape = CONFIG_RECORD_FACT_KINDS[conflict.kept.kind];
9351
+ if (!shape || conflict.kept.kind !== conflict.skipped.kind) continue;
9352
+ const keptFact = conflict.kept;
9353
+ const skippedFact = conflict.skipped;
9354
+ const identity = renderFactFields(keptFact, shape.keys);
9355
+ const path = `${shape.section}[${identity ? `${shape.record} ${identity}` : shape.record}]`;
9356
+ const group = groups.get(path) ?? {
9357
+ record: shape.record,
9358
+ kept: renderFactFields(keptFact, COLLISION_DETAIL_KEYS),
9359
+ dropped: [],
9360
+ keptFallback: "the first record"
9361
+ };
9362
+ const dropped = renderFactFields(skippedFact, COLLISION_DETAIL_KEYS);
9363
+ group.dropped.push(dropped || `record ${group.dropped.length + 2}`);
9364
+ groups.set(path, group);
9365
+ }
9366
+ return [...groups].map(([path, group]) => ({
9367
+ code: "FUI9007",
9368
+ kind: "colliding-record",
9369
+ severity: "warn",
9370
+ path,
9371
+ message: `${path} has ${group.dropped.length + 1} \`${group.record}\` records that compile to the same policy fact \u2014 enforcing ${group.kept || group.keptFallback} and dropping ${group.dropped.join("; ")}. Author one record for this policy, or scope them apart, so the enforced setting is the one you can read.`
9372
+ }));
9373
+ }
9374
+ function configRecordShape(kind) {
9375
+ const shape = CONFIG_RECORD_FACT_KINDS[kind];
9376
+ return shape ? { section: shape.section, record: shape.record } : null;
9377
+ }
9378
+ function overriddenRecordSeverityDiagnostic(input) {
9379
+ const path = `${input.section}[${input.record}]`;
9380
+ return {
9381
+ code: "FUI9008",
9382
+ kind: "overridden-record-severity",
9383
+ severity: "warn",
9384
+ path,
9385
+ message: `${path} authored severity=${input.authored}, but ${input.source} enforces severity=${input.enforced} on \`${input.ruleId}\`. The record's severity is not what gates CI \u2014 drop the rule override, or author the record at the severity you want enforced.`
9386
+ };
9387
+ }
8700
9388
  function collectStrippedKeys(authored, parsed, path, diagnostics) {
8701
9389
  if (Array.isArray(authored)) {
8702
9390
  if (!Array.isArray(parsed)) return;
@@ -8786,6 +9474,41 @@ function detectUnconsumedConfigKeys(authoredConfig, parsedConfig) {
8786
9474
  collectPassthroughRecordValues(authoredConfig, ["govern", "runners"], /* @__PURE__ */ new Set(), diagnostics);
8787
9475
  return stableConfigDiagnostics(diagnostics);
8788
9476
  }
9477
+ function authoredPolicyExcludes(policy) {
9478
+ const out = [];
9479
+ const collect = (path, scope, raw) => {
9480
+ for (const exclude of normalizePolicyExcludes(raw) ?? []) {
9481
+ out.push({ path, scope, exclude });
9482
+ }
9483
+ };
9484
+ (policy?.styles ?? []).forEach((record, index) => {
9485
+ collect(`govern.styles[${index}].exclude`, record.kind, record.exclude);
9486
+ });
9487
+ (policy?.jsx ?? []).forEach((record, index) => {
9488
+ collect(`govern.jsx[${index}].exclude`, record.kind, record.exclude);
9489
+ });
9490
+ for (const ruleId of Object.keys(policy?.rules ?? {}).sort()) {
9491
+ const record = objectRecord(policy?.rules?.[ruleId]);
9492
+ if (!record) continue;
9493
+ collect(`govern.rules.${ruleId}.exclude`, ruleId, record.exclude);
9494
+ }
9495
+ return out;
9496
+ }
9497
+ function detectUnmatchedPolicyExcludes(policy, scannedFiles) {
9498
+ if (scannedFiles.length === 0) return [];
9499
+ const diagnostics = [];
9500
+ for (const { path, scope, exclude } of authoredPolicyExcludes(policy)) {
9501
+ if (scannedFiles.some((file) => policyExcludeMatchesPath(exclude, file))) continue;
9502
+ diagnostics.push({
9503
+ code: "FUI9006",
9504
+ kind: "unmatched-exclude",
9505
+ severity: "warn",
9506
+ path,
9507
+ message: `${path} pattern "${exclude.glob}" matched no scanned file, so ${scope} is not actually scoped by it. Correct the glob to a path this scan covers, or remove it.`
9508
+ });
9509
+ }
9510
+ return stableConfigDiagnostics(diagnostics);
9511
+ }
8789
9512
  function effectiveScaleBindings(policy) {
8790
9513
  const bindings = [];
8791
9514
  for (const style of policy?.styles ?? []) {
@@ -8849,23 +9572,28 @@ function evaluateGovernanceIntegrity(input) {
8849
9572
  if (!policyArmed) {
8850
9573
  policyFamily.reason = "no governance policy resolved";
8851
9574
  }
8852
- const componentsConfig = configs.get("components/prefer-library");
9575
+ const preferLibraryConfig = configs.get("components/prefer-library");
9576
+ const shadowComponentConfig = configs.get("components/shadow-component");
8853
9577
  const confirmedBridges = input.policy?.canonicalBridges ?? [];
8854
- const componentsArmed = confirmedBridges.length > 0 || componentsConfig?.enabled === true && hasEffectiveComponentVocabulary(componentsConfig.options);
9578
+ const componentRuleEnabled = preferLibraryConfig?.enabled === true || shadowComponentConfig?.enabled === true;
9579
+ const preferLibraryArmed = preferLibraryConfig?.enabled === true && hasEffectiveComponentVocabulary(preferLibraryConfig.options);
9580
+ const shadowComponentArmed = shadowComponentConfig?.enabled === true && hasEffectiveComponentVocabulary(shadowComponentConfig.options);
9581
+ const componentsArmed = confirmedBridges.length > 0 || preferLibraryArmed || shadowComponentArmed;
8855
9582
  const componentsFamily = {
8856
9583
  id: "components",
8857
9584
  armed: componentsArmed,
8858
9585
  rules: [
8859
9586
  ...confirmedBridges.length > 0 ? ["imports/preferred-path"] : [],
8860
- ...componentsConfig?.enabled === true ? ["components/prefer-library"] : []
9587
+ ...preferLibraryConfig?.enabled === true ? ["components/prefer-library"] : [],
9588
+ ...shadowComponentConfig?.enabled === true ? ["components/shadow-component"] : []
8861
9589
  ]
8862
9590
  };
8863
9591
  if (!componentsArmed) {
8864
- if (componentsConfig?.enabled === true) {
8865
- componentsFamily.reason = "components/prefer-library enabled but no effective canonical source";
9592
+ if (componentRuleEnabled) {
9593
+ componentsFamily.reason = "component governance enabled but no effective canonical source";
8866
9594
  componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
8867
9595
  } else {
8868
- componentsFamily.reason = "components/prefer-library not enabled";
9596
+ componentsFamily.reason = "canonical-component rules not enabled";
8869
9597
  componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
8870
9598
  }
8871
9599
  }
@@ -8908,7 +9636,7 @@ function evaluateGovernanceIntegrity(input) {
8908
9636
  for (const ruleId of BLOCKING_RULE_ALLOWLIST) {
8909
9637
  const config = configs.get(ruleId);
8910
9638
  if (config?.enabled !== true) continue;
8911
- const familyArmed = ruleId === "components/prefer-library" ? componentsArmed : hygieneArmed;
9639
+ const familyArmed = ruleId === "components/prefer-library" || ruleId === "components/shadow-component" ? componentsArmed : ruleId === "tokens/css-vars-must-be-defined" ? tokensArmed : hygieneArmed;
8912
9640
  if (!familyArmed) continue;
8913
9641
  if (config.severity !== "error" && !failOnWarnings) continue;
8914
9642
  blockingRules.push(ruleId);
@@ -8982,6 +9710,19 @@ function canonicalBridgeContractMappings(bridges) {
8982
9710
  }
8983
9711
  }));
8984
9712
  }
9713
+ function canonicalBridgeIdentityBindings(bridges) {
9714
+ return (bridges ?? []).map((bridge) => ({
9715
+ source: {
9716
+ kind: "npm",
9717
+ specifier: bridge.underlying.packageName,
9718
+ include: [bridge.underlying.exportName]
9719
+ },
9720
+ localComponentKey: bridge.local.componentKey
9721
+ }));
9722
+ }
9723
+ function canonicalBridgeIdentitySources(bridges) {
9724
+ return canonicalBridgeIdentityBindings(bridges).map((binding) => binding.source);
9725
+ }
8985
9726
 
8986
9727
  // src/effective-governance-inputs.ts
8987
9728
  var EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA = "effective-governance-inputs:v1";
@@ -9526,6 +10267,7 @@ export {
9526
10267
  CONTRACT_DOMAINS,
9527
10268
  CONTRACT_PREIMAGE_CAPABILITY_HEADER,
9528
10269
  CONTRACT_PREIMAGE_SCHEMA,
10270
+ CompiledFragmentsFileValidationError,
9529
10271
  DEFAULTS,
9530
10272
  DEFAULT_ENHANCED_STYLE_PROPERTIES,
9531
10273
  DEFAULT_STYLE_PROPERTIES,
@@ -9548,6 +10290,8 @@ export {
9548
10290
  REGISTRY_INSTALL_RECEIPT_SCHEMA_VERSION,
9549
10291
  REGISTRY_MANIFEST_SCHEMA_VERSION,
9550
10292
  RULES,
10293
+ RULE_FAMILY_IDS,
10294
+ RULE_FAMILY_MEMBERS,
9551
10295
  RULE_TIER,
9552
10296
  SEVERITIES,
9553
10297
  SEVERITY_RANK,
@@ -9585,6 +10329,8 @@ export {
9585
10329
  calculateDeltaE,
9586
10330
  canBlock,
9587
10331
  canonicalBridgeContractMappings,
10332
+ canonicalBridgeIdentityBindings,
10333
+ canonicalBridgeIdentitySources,
9588
10334
  canonicalBridgeV1Schema,
9589
10335
  canonicalDirectionConflictsTarget,
9590
10336
  canonicalJson,
@@ -9599,6 +10345,8 @@ export {
9599
10345
  classifyComplexity,
9600
10346
  classifyIdentity,
9601
10347
  collectRegistryInstallDependencies,
10348
+ collidingRecordDiagnostic,
10349
+ collidingRecordDiagnostics,
9602
10350
  colorSimilarity,
9603
10351
  compareByVocabularyRank,
9604
10352
  compareColors,
@@ -9623,6 +10371,7 @@ export {
9623
10371
  compositionPatternSchema,
9624
10372
  computeRegistryHash,
9625
10373
  configDeclarationForDiagnostics,
10374
+ configRecordShape,
9626
10375
  containsTailwindV4Theme,
9627
10376
  contractComponentsFromFragments,
9628
10377
  contractHash,
@@ -9633,14 +10382,17 @@ export {
9633
10382
  defineConfig,
9634
10383
  defineFragment,
9635
10384
  defineRecipe,
10385
+ describePolicyExclude,
9636
10386
  detectOrphanGovernanceScales,
9637
10387
  detectSubComponentPaths,
9638
10388
  detectUnconsumedConfigKeys,
10389
+ detectUnmatchedPolicyExcludes,
9639
10390
  diffContractDomains,
9640
10391
  discoverComponents,
9641
10392
  dtcgTokenFileSchema,
9642
10393
  emptyConformResult,
9643
10394
  evaluateGovernanceIntegrity,
10395
+ excludeGlobToRegExp,
9644
10396
  executeVariantLoaders,
9645
10397
  explainUrlForCode,
9646
10398
  factEvidenceSchema,
@@ -9713,8 +10465,10 @@ export {
9713
10465
  isFigmaPropMapping,
9714
10466
  isForceIncluded,
9715
10467
  isPortableRepoPath,
10468
+ isPresetSourcedRule,
9716
10469
  isRawHtmlAdvisoryTier,
9717
10470
  isReactComponent,
10471
+ isRuleFamilyId,
9718
10472
  localCanonicalContractMappings,
9719
10473
  lowerCompositionContract,
9720
10474
  makeA11yNameRequiredFact,
@@ -9752,12 +10506,15 @@ export {
9752
10506
  makeTailwindUnknownClassEnabledFact,
9753
10507
  makeThemeDeclarationFact,
9754
10508
  makeTokenDefinitionFact,
10509
+ makeUsageChildContentFact,
9755
10510
  makeUsageComponentFact,
9756
10511
  makeUsageImportFact,
9757
10512
  makeUsageInlineStyleFact,
9758
10513
  makeUsageNodeFact,
9759
10514
  makeUsagePropResolvedFact,
9760
10515
  makeUsageTextChildFact,
10516
+ markPresetSourcedRules,
10517
+ matchPolicyExclude,
9761
10518
  matchesGlob,
9762
10519
  maxSeverity,
9763
10520
  mcpBlockSchema,
@@ -9783,8 +10540,10 @@ export {
9783
10540
  nearestSignedScaleValue,
9784
10541
  normalizeColor,
9785
10542
  normalizeConfigPath,
10543
+ normalizeExcludePath,
9786
10544
  normalizeFinding,
9787
10545
  normalizeGovernanceConfig,
10546
+ normalizePolicyExcludes,
9788
10547
  normalizeRegistryRepoPath,
9789
10548
  normalizeSeverity,
9790
10549
  normalizeStyleValue,
@@ -9792,9 +10551,11 @@ export {
9792
10551
  normalizeTokenGroupComment,
9793
10552
  normalizeTokenValue,
9794
10553
  normalizeViolation,
10554
+ overriddenRecordSeverityDiagnostic,
9795
10555
  ownedImportEquivalents,
9796
10556
  parseColor,
9797
10557
  parseColorToRgb,
10558
+ parseCompiledFragmentsFile,
9798
10559
  parseComponentContract,
9799
10560
  parseContractStamp,
9800
10561
  parseCssTokens,
@@ -9807,9 +10568,12 @@ export {
9807
10568
  parseTailwindV4Theme,
9808
10569
  parseTokenFile,
9809
10570
  parseTokens,
10571
+ policyExcludeMatchesPath,
10572
+ policyExcludeSchema,
9810
10573
  portableRepoPathError,
9811
10574
  projectCanonicalDirectionConflicts,
9812
10575
  projectContractPreimage,
10576
+ projectSupersededImportPathPreferences,
9813
10577
  projectV1OwnedComponentId,
9814
10578
  projectV1OwnedImportIdentity,
9815
10579
  propDefinitionSchema,
@@ -9849,10 +10613,12 @@ export {
9849
10613
  resolveTokenValue,
9850
10614
  rgbToHex,
9851
10615
  ruleA11yRequiredAccessibleName,
10616
+ ruleA11yStandard,
9852
10617
  ruleComponentsForbiddenPropValue,
9853
10618
  ruleComponentsPreferLibrary,
9854
10619
  ruleComponentsShadowComponent,
9855
10620
  ruleComponentsUnknownProp,
10621
+ ruleFamilyMembers,
9856
10622
  ruleJsxPreferredComponent,
9857
10623
  ruleJsxPreferredImportPath,
9858
10624
  rulePropsInvalidValue,