@usefragments/core 1.6.0 → 1.7.1
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.
- package/dist/chunk-RANPUC6C.js +72 -0
- package/dist/chunk-RANPUC6C.js.map +1 -0
- package/dist/{chunk-WVFNDPM4.js → chunk-WNMWKUYG.js} +26 -14
- package/dist/chunk-WNMWKUYG.js.map +1 -0
- package/dist/codes/index.d.ts +1 -1
- package/dist/codes/index.js +1 -1
- package/dist/compiled-types/index.d.ts +1 -1
- package/dist/compiled-types/index.js +8 -0
- package/dist/generate/index.d.ts +1 -1
- package/dist/{governance-DxFipN5V.d.ts → governance-D9KtH-vg.d.ts} +9 -3
- package/dist/index.d.ts +199 -139
- package/dist/index.js +706 -201
- package/dist/index.js.map +1 -1
- package/dist/react-types.d.ts +1 -1
- package/dist/registry.d.ts +36 -36
- package/dist/schemas/index.d.ts +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/package.json +2 -1
- package/src/agent-format.test.ts +13 -0
- package/src/agent-format.ts +9 -3
- package/src/codes/__tests__/codes.test.ts +0 -1
- package/src/codes/codes.ts +1 -2
- package/src/compiled-types/index.ts +81 -0
- package/src/compiled-types/parse.test.ts +47 -0
- package/src/component-contract.ts +12 -0
- package/src/contract-parser.ts +2 -0
- package/src/facts/builders.ts +13 -0
- package/src/facts/compile.ts +13 -13
- package/src/facts/facts.test.ts +38 -1
- package/src/facts/index.ts +2 -0
- package/src/facts/types.ts +15 -0
- package/src/governance-integrity.test.ts +98 -1
- package/src/governance-integrity.ts +40 -20
- package/src/index.ts +8 -0
- package/src/rules/a11y-required-accessible-name.ts +175 -28
- package/src/rules/a11y-standard.ts +102 -0
- package/src/rules/a11y-utils.ts +7 -0
- package/src/rules/components-prefer-library.test.ts +75 -28
- package/src/rules/components-prefer-library.ts +35 -15
- package/src/rules/components-shadow-component.test.ts +21 -9
- package/src/rules/emit-gate.test.ts +74 -4
- package/src/rules/emit-gate.ts +24 -9
- package/src/rules/families.ts +1 -1
- package/src/rules/fix-availability.ts +1 -0
- package/src/rules/index.ts +12 -2
- package/src/rules/rules.test.ts +63 -7
- package/src/rules/tiers.ts +1 -0
- package/src/tokens/design-token-parser.test.ts +131 -0
- package/src/tokens/design-token-parser.ts +362 -49
- package/src/types.ts +2 -2
- package/dist/chunk-WVFNDPM4.js.map +0 -1
- package/dist/{index-DbkPE46t.d.ts → index-hZAlYCli.d.ts} +8 -8
package/dist/index.js
CHANGED
|
@@ -105,6 +105,7 @@ import {
|
|
|
105
105
|
makeTailwindUnknownClassEnabledFact,
|
|
106
106
|
makeThemeDeclarationFact,
|
|
107
107
|
makeTokenDefinitionFact,
|
|
108
|
+
makeUsageChildContentFact,
|
|
108
109
|
makeUsageComponentFact,
|
|
109
110
|
makeUsageImportFact,
|
|
110
111
|
makeUsageInlineStyleFact,
|
|
@@ -126,7 +127,7 @@ import {
|
|
|
126
127
|
resolveComponentGovernance,
|
|
127
128
|
ruleFamilyMembers,
|
|
128
129
|
scaleGovernanceRecordSchema
|
|
129
|
-
} from "./chunk-
|
|
130
|
+
} from "./chunk-WNMWKUYG.js";
|
|
130
131
|
import {
|
|
131
132
|
AGENT_FORMAT_SCHEMA_VERSION,
|
|
132
133
|
agentErrorEnvelopeSchema,
|
|
@@ -181,6 +182,10 @@ import {
|
|
|
181
182
|
severitySchema,
|
|
182
183
|
sortBySeverity
|
|
183
184
|
} from "./chunk-V4VQB57N.js";
|
|
185
|
+
import {
|
|
186
|
+
CompiledFragmentsFileValidationError,
|
|
187
|
+
parseCompiledFragmentsFile
|
|
188
|
+
} from "./chunk-RANPUC6C.js";
|
|
184
189
|
|
|
185
190
|
// src/constants.ts
|
|
186
191
|
var BRAND = {
|
|
@@ -420,6 +425,7 @@ function buildAgentFormat(input) {
|
|
|
420
425
|
const zeroRulesLoaded = input.integrity.rulesLoaded === 0;
|
|
421
426
|
const integrityFailed = !input.integrity.healthy || input.integrity.inert;
|
|
422
427
|
const score = integrityFailed ? 0 : scoreFindings(input.findings);
|
|
428
|
+
const filesAffected = new Set(input.findings.map((finding) => finding.location.file)).size;
|
|
423
429
|
const visibleFindings = uniqueFindings(input.findings).slice(0, input.maxFindings ?? 50);
|
|
424
430
|
const agentFindings = visibleFindings.map((finding) => ({
|
|
425
431
|
code: finding.code,
|
|
@@ -437,7 +443,7 @@ function buildAgentFormat(input) {
|
|
|
437
443
|
const hasErrors = input.findings.some((finding) => severityLevel(finding.severity) === "error");
|
|
438
444
|
const shared = {
|
|
439
445
|
schemaVersion: AGENT_FORMAT_SCHEMA_VERSION,
|
|
440
|
-
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),
|
|
441
447
|
integrity: input.integrity,
|
|
442
448
|
fix_first: agentFindings.filter((finding) => finding.plan.confidence >= 0.8),
|
|
443
449
|
remaining: agentFindings.filter((finding) => finding.plan.confidence < 0.8),
|
|
@@ -498,9 +504,9 @@ function scoreFindings(findings) {
|
|
|
498
504
|
}
|
|
499
505
|
return Math.max(1, Math.round(100 / (1 + penalty / 40)));
|
|
500
506
|
}
|
|
501
|
-
function agentSummary(count, filesScanned, score) {
|
|
507
|
+
function agentSummary(count, filesAffected, filesScanned, score) {
|
|
502
508
|
if (count === 0) return `passed with score ${score}; no findings`;
|
|
503
|
-
return `${count} finding${count === 1 ? "" : "s"}
|
|
509
|
+
return `${count} finding${count === 1 ? "" : "s"} in ${filesAffected} file${filesAffected === 1 ? "" : "s"}; ${filesScanned} scanned; score ${score}`;
|
|
504
510
|
}
|
|
505
511
|
function planForFinding(finding) {
|
|
506
512
|
if (finding.fix?.deterministic === true) {
|
|
@@ -3302,7 +3308,7 @@ function buildToken(name, rawValue, lineNumber, source) {
|
|
|
3302
3308
|
}
|
|
3303
3309
|
|
|
3304
3310
|
// src/tokens/design-token-parser.ts
|
|
3305
|
-
var
|
|
3311
|
+
var CSS_CUSTOM_PROPERTY_NAME_CHARACTER = /[a-zA-Z0-9_-]/u;
|
|
3306
3312
|
var VAR_REFERENCE_PATTERN = /var\(\s*--([a-zA-Z0-9_-]+)(?:\s*,\s*([^)]+))?\s*\)/;
|
|
3307
3313
|
function parseDesignTokenContent(content, options = {}) {
|
|
3308
3314
|
const startTime = performance.now();
|
|
@@ -3354,19 +3360,48 @@ function parseDesignTokenContent(content, options = {}) {
|
|
|
3354
3360
|
errors.push({ message: err, file: filePath });
|
|
3355
3361
|
}
|
|
3356
3362
|
}
|
|
3357
|
-
const
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
const fullName =
|
|
3361
|
-
const lineNumber =
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
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
|
|
3365
3376
|
});
|
|
3366
3377
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
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;
|
|
3370
3405
|
const { resolvedValue, chain, hasCircular, unresolvedRef } = resolveDesignTokenValue(
|
|
3371
3406
|
rawValue,
|
|
3372
3407
|
tokensByName
|
|
@@ -3388,7 +3423,7 @@ function parseDesignTokenContent(content, options = {}) {
|
|
|
3388
3423
|
lineNumber: line,
|
|
3389
3424
|
theme,
|
|
3390
3425
|
selector,
|
|
3391
|
-
description: extractDescription(
|
|
3426
|
+
description: extractDescription(contentLines, line ?? 1)
|
|
3392
3427
|
});
|
|
3393
3428
|
}
|
|
3394
3429
|
if (tokens.length === 0 && trimmed.length > 0) {
|
|
@@ -3403,13 +3438,6 @@ function parseDesignTokenContent(content, options = {}) {
|
|
|
3403
3438
|
parseTimeMs: performance.now() - startTime
|
|
3404
3439
|
};
|
|
3405
3440
|
}
|
|
3406
|
-
function countNewlines(content, index) {
|
|
3407
|
-
let count = 0;
|
|
3408
|
-
for (let i = 0; i < index && i < content.length; i++) {
|
|
3409
|
-
if (content.charCodeAt(i) === 10) count++;
|
|
3410
|
-
}
|
|
3411
|
-
return count;
|
|
3412
|
-
}
|
|
3413
3441
|
function parsedOutputToDesignTokens(output, filePath) {
|
|
3414
3442
|
const tokens = [];
|
|
3415
3443
|
for (const cat of Object.values(output.categories)) {
|
|
@@ -3564,29 +3592,284 @@ function inferTokenLevel(name, rawValue, referenceChain) {
|
|
|
3564
3592
|
}
|
|
3565
3593
|
return 2;
|
|
3566
3594
|
}
|
|
3567
|
-
function
|
|
3568
|
-
const
|
|
3595
|
+
function scanCssCustomPropertyDeclarations(content) {
|
|
3596
|
+
const declarations = [];
|
|
3597
|
+
const parentSelectors = [];
|
|
3569
3598
|
let currentSelector = ":root";
|
|
3570
|
-
let
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
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++;
|
|
3615
|
+
}
|
|
3616
|
+
continue;
|
|
3617
|
+
}
|
|
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;
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
return declarations;
|
|
3704
|
+
}
|
|
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++;
|
|
3578
3809
|
}
|
|
3810
|
+
continue;
|
|
3579
3811
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
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;
|
|
3584
3827
|
}
|
|
3828
|
+
output += character;
|
|
3829
|
+
if (character === "'" || character === '"') quote = character;
|
|
3585
3830
|
}
|
|
3586
|
-
return
|
|
3831
|
+
return output;
|
|
3587
3832
|
}
|
|
3588
|
-
function
|
|
3589
|
-
|
|
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) {
|
|
3590
3873
|
if (line <= 1) return void 0;
|
|
3591
3874
|
const prevLine = lines[line - 2]?.trim();
|
|
3592
3875
|
const singleLineMatch = prevLine?.match(/\/\/\s*(.+)$/);
|
|
@@ -3815,6 +4098,11 @@ var componentContractSchema = z6.object({
|
|
|
3815
4098
|
tags: z6.array(z6.string()).optional(),
|
|
3816
4099
|
status: z6.enum(["stable", "beta", "deprecated", "experimental"]).optional(),
|
|
3817
4100
|
framework: z6.enum(["react", "vue", "svelte", "web-components", "angular"]).optional(),
|
|
4101
|
+
dependencies: z6.array(z6.object({
|
|
4102
|
+
name: z6.string(),
|
|
4103
|
+
version: z6.string(),
|
|
4104
|
+
reason: z6.string().optional()
|
|
4105
|
+
})).optional(),
|
|
3818
4106
|
sourcePath: z6.string(),
|
|
3819
4107
|
exportName: z6.string(),
|
|
3820
4108
|
propsSummary: z6.array(z6.string()),
|
|
@@ -3845,6 +4133,7 @@ function parseComponentContract(content, filePath) {
|
|
|
3845
4133
|
category: validated.category,
|
|
3846
4134
|
tags: validated.tags,
|
|
3847
4135
|
status: validated.status,
|
|
4136
|
+
dependencies: validated.dependencies,
|
|
3848
4137
|
figma: validated.figma?.nodeUrl,
|
|
3849
4138
|
figmaProps: validated.figma?.propMappings
|
|
3850
4139
|
},
|
|
@@ -4382,6 +4671,13 @@ function discoverComponents(moduleExports, options) {
|
|
|
4382
4671
|
return names.sort();
|
|
4383
4672
|
}
|
|
4384
4673
|
|
|
4674
|
+
// src/rules/a11y-utils.ts
|
|
4675
|
+
function isAriaHidden(props) {
|
|
4676
|
+
const prop = props.find((candidate) => candidate.prop === "aria-hidden");
|
|
4677
|
+
if (!prop || prop.resolution !== "static") return false;
|
|
4678
|
+
return prop.value === true || prop.value === "true";
|
|
4679
|
+
}
|
|
4680
|
+
|
|
4385
4681
|
// src/rules/finding.ts
|
|
4386
4682
|
function makeFinding(input) {
|
|
4387
4683
|
if (input.evidence.length === 0) {
|
|
@@ -4840,36 +5136,71 @@ function isApplicableTokenReference(referenceFormat) {
|
|
|
4840
5136
|
|
|
4841
5137
|
// src/rules/a11y-required-accessible-name.ts
|
|
4842
5138
|
var RULE_ID = "a11y/required-accessible-name";
|
|
4843
|
-
var RULE_VERSION = "
|
|
5139
|
+
var RULE_VERSION = "2";
|
|
4844
5140
|
var NAME_SOURCE_PROPS = /* @__PURE__ */ new Set(["aria-label", "aria-labelledby", "title"]);
|
|
5141
|
+
var COMPONENT_NAME_SOURCE_PROPS = /* @__PURE__ */ new Set([...NAME_SOURCE_PROPS, "label"]);
|
|
5142
|
+
var NAME_REQUIRED_TAGS = /* @__PURE__ */ new Set(["button", "input", "select", "textarea"]);
|
|
5143
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["button", "checkbox", "radio", "switch", "tab"]);
|
|
5144
|
+
var NAME_FROM_CONTENT_TAGS = /* @__PURE__ */ new Set(["button"]);
|
|
5145
|
+
var NAME_FROM_CONTENT_ROLES = /* @__PURE__ */ new Set(["button", "checkbox", "radio", "switch", "tab"]);
|
|
5146
|
+
var NAME_REQUIRED_COMPONENT_SUFFIXES = [
|
|
5147
|
+
"button",
|
|
5148
|
+
"input",
|
|
5149
|
+
"select",
|
|
5150
|
+
"textarea",
|
|
5151
|
+
"checkbox",
|
|
5152
|
+
"radio",
|
|
5153
|
+
"switch"
|
|
5154
|
+
];
|
|
4845
5155
|
function ruleA11yRequiredAccessibleName(ix) {
|
|
4846
5156
|
const findings = [];
|
|
4847
5157
|
const propsByNode = indexPropsByNodeId(ix);
|
|
4848
5158
|
const textByNode = indexTextChildrenByNodeId(ix);
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
5159
|
+
const childContentByNode = new Map(
|
|
5160
|
+
ix.byKind("usage_child_content").map((content) => [content.nodeId, content])
|
|
5161
|
+
);
|
|
5162
|
+
const globalPolicy = ix.policy.ruleConfig(RULE_ID);
|
|
5163
|
+
const usageByNode = new Map(ix.byKind("usage_component").map((usage) => [usage.nodeId, usage]));
|
|
5164
|
+
const definitions = new Map(
|
|
5165
|
+
ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
|
|
5166
|
+
);
|
|
5167
|
+
for (const node of ix.byKind("usage_node")) {
|
|
5168
|
+
const usageComponent = usageByNode.get(node.id);
|
|
5169
|
+
const componentPolicy = usageComponent ? ix.policy.a11yNameRequired(usageComponent.componentId) : void 0;
|
|
4854
5170
|
const props = propsByNode.get(node.id) ?? [];
|
|
4855
|
-
if (props
|
|
4856
|
-
|
|
5171
|
+
if (isAriaHidden(props)) continue;
|
|
5172
|
+
const genericControl = globalPolicy?.enabled === true && requiresAccessibleName(node, usageComponent, definitions, props);
|
|
5173
|
+
if (!componentPolicy && !genericControl) continue;
|
|
5174
|
+
if (hasAccessibleName(
|
|
5175
|
+
props,
|
|
5176
|
+
textByNode.get(node.id) ?? [],
|
|
5177
|
+
childContentByNode.get(node.id),
|
|
5178
|
+
usageComponent !== void 0,
|
|
5179
|
+
canNameFromContent(node, usageComponent, definitions)
|
|
5180
|
+
)) {
|
|
5181
|
+
continue;
|
|
5182
|
+
}
|
|
4857
5183
|
findings.push(
|
|
4858
5184
|
makeFinding({
|
|
4859
5185
|
ruleId: RULE_ID,
|
|
4860
5186
|
ruleVersion: RULE_VERSION,
|
|
4861
|
-
severity:
|
|
4862
|
-
message: `<${node.element}> has no accessible name. ${
|
|
5187
|
+
severity: componentPolicy?.severity ?? globalPolicy?.severity ?? "warn",
|
|
5188
|
+
message: `<${node.element}> has no accessible name. ${componentPolicy?.because ?? "Add visible text, aria-label, aria-labelledby, or a component label."}`.trim(),
|
|
4863
5189
|
location: node.location,
|
|
4864
|
-
evidence: ix.evidence([
|
|
5190
|
+
evidence: ix.evidence([
|
|
5191
|
+
node.id,
|
|
5192
|
+
...usageComponent ? [usageComponent.id] : [],
|
|
5193
|
+
...componentPolicy ? [componentPolicy.id] : [],
|
|
5194
|
+
...genericControl && globalPolicy ? [globalPolicy.id] : []
|
|
5195
|
+
]),
|
|
4865
5196
|
fingerprintIdentity: {
|
|
4866
5197
|
file: node.file,
|
|
4867
|
-
componentId: usageComponent
|
|
5198
|
+
componentId: usageComponent?.componentId,
|
|
4868
5199
|
element: node.element,
|
|
4869
5200
|
nodePath: node.nodePath
|
|
4870
5201
|
},
|
|
4871
5202
|
attributes: {
|
|
4872
|
-
componentId: usageComponent.componentId,
|
|
5203
|
+
...usageComponent ? { componentId: usageComponent.componentId } : {},
|
|
4873
5204
|
element: node.element
|
|
4874
5205
|
}
|
|
4875
5206
|
})
|
|
@@ -4877,12 +5208,18 @@ function ruleA11yRequiredAccessibleName(ix) {
|
|
|
4877
5208
|
}
|
|
4878
5209
|
return findings;
|
|
4879
5210
|
}
|
|
4880
|
-
function hasAccessibleName(props, textChildren) {
|
|
4881
|
-
|
|
4882
|
-
|
|
5211
|
+
function hasAccessibleName(props, textChildren, childContent, componentControl, nameFromContent) {
|
|
5212
|
+
const sourceProps = componentControl ? COMPONENT_NAME_SOURCE_PROPS : NAME_SOURCE_PROPS;
|
|
5213
|
+
if (nameFromContent) {
|
|
5214
|
+
for (const child of textChildren) {
|
|
5215
|
+
if (typeof child.text === "string" && child.text.trim().length > 0) return true;
|
|
5216
|
+
}
|
|
5217
|
+
if (childContent?.content === "static-text" || childContent?.content === "runtime") {
|
|
5218
|
+
return true;
|
|
5219
|
+
}
|
|
4883
5220
|
}
|
|
4884
5221
|
for (const prop of props) {
|
|
4885
|
-
if (!
|
|
5222
|
+
if (!sourceProps.has(prop.prop)) continue;
|
|
4886
5223
|
if (prop.resolution === "spread") continue;
|
|
4887
5224
|
if (prop.resolution === "dynamic") return true;
|
|
4888
5225
|
if (prop.resolution === "static") {
|
|
@@ -4892,10 +5229,137 @@ function hasAccessibleName(props, textChildren) {
|
|
|
4892
5229
|
}
|
|
4893
5230
|
return false;
|
|
4894
5231
|
}
|
|
4895
|
-
function
|
|
4896
|
-
|
|
4897
|
-
if (
|
|
4898
|
-
|
|
5232
|
+
function canNameFromContent(node, usage, definitions) {
|
|
5233
|
+
const element = node.element.toLowerCase();
|
|
5234
|
+
if (NAME_REQUIRED_TAGS.has(element)) return NAME_FROM_CONTENT_TAGS.has(element);
|
|
5235
|
+
if (node.role && NAME_FROM_CONTENT_ROLES.has(node.role.toLowerCase())) return true;
|
|
5236
|
+
if (!usage) return false;
|
|
5237
|
+
const definition = definitions.get(String(usage.componentId));
|
|
5238
|
+
if (definition) {
|
|
5239
|
+
const root = definition.renderRoot;
|
|
5240
|
+
if (root.resolution === "intrinsic") {
|
|
5241
|
+
return intrinsicCanNameFromContent(root.tag, root.role);
|
|
5242
|
+
}
|
|
5243
|
+
if (root.resolution === "canonical") return isContentNamedControl(root.canonical);
|
|
5244
|
+
if (root.resolution === "mixed") {
|
|
5245
|
+
return root.intrinsics.some((tag) => intrinsicCanNameFromContent(tag)) || root.canonicals.some(isContentNamedControl);
|
|
5246
|
+
}
|
|
5247
|
+
}
|
|
5248
|
+
return isContentNamedControl(String(usage.componentId));
|
|
5249
|
+
}
|
|
5250
|
+
function intrinsicCanNameFromContent(tag, role) {
|
|
5251
|
+
const normalizedTag = tag.toLowerCase();
|
|
5252
|
+
if (NAME_REQUIRED_TAGS.has(normalizedTag)) {
|
|
5253
|
+
return NAME_FROM_CONTENT_TAGS.has(normalizedTag);
|
|
5254
|
+
}
|
|
5255
|
+
return role !== void 0 && NAME_FROM_CONTENT_ROLES.has(role.toLowerCase());
|
|
5256
|
+
}
|
|
5257
|
+
function requiresAccessibleName(node, usage, definitions, props) {
|
|
5258
|
+
if (node.role && NAME_REQUIRED_ROLES.has(node.role.toLowerCase())) return true;
|
|
5259
|
+
if (NAME_REQUIRED_TAGS.has(node.element.toLowerCase())) {
|
|
5260
|
+
if (node.element.toLowerCase() === "input" && props.some(
|
|
5261
|
+
(prop) => prop.prop === "type" && prop.resolution === "static" && String(prop.value).toLowerCase() === "hidden"
|
|
5262
|
+
)) {
|
|
5263
|
+
return false;
|
|
5264
|
+
}
|
|
5265
|
+
return true;
|
|
5266
|
+
}
|
|
5267
|
+
if (!usage) return false;
|
|
5268
|
+
const definition = definitions.get(String(usage.componentId));
|
|
5269
|
+
if (definition) {
|
|
5270
|
+
const root = definition.renderRoot;
|
|
5271
|
+
if (root.resolution === "intrinsic") {
|
|
5272
|
+
return NAME_REQUIRED_TAGS.has(root.tag.toLowerCase()) || root.role !== void 0 && NAME_REQUIRED_ROLES.has(root.role.toLowerCase());
|
|
5273
|
+
}
|
|
5274
|
+
if (root.resolution === "canonical") return isNamedControl(root.canonical);
|
|
5275
|
+
if (root.resolution === "mixed") {
|
|
5276
|
+
return root.intrinsics.some((tag) => NAME_REQUIRED_TAGS.has(tag.toLowerCase())) || root.canonicals.some(isNamedControl);
|
|
5277
|
+
}
|
|
5278
|
+
}
|
|
5279
|
+
return isNamedControl(String(usage.componentId));
|
|
5280
|
+
}
|
|
5281
|
+
function isContentNamedControl(identity) {
|
|
5282
|
+
return normalizedControlName(identity).endsWith("button");
|
|
5283
|
+
}
|
|
5284
|
+
function isNamedControl(identity) {
|
|
5285
|
+
const normalized = normalizedControlName(identity);
|
|
5286
|
+
return NAME_REQUIRED_COMPONENT_SUFFIXES.some((suffix) => normalized.endsWith(suffix));
|
|
5287
|
+
}
|
|
5288
|
+
function normalizedControlName(identity) {
|
|
5289
|
+
const exported = identity.split("#").at(-1)?.split(".").at(-1) ?? identity;
|
|
5290
|
+
return exported.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
5291
|
+
}
|
|
5292
|
+
|
|
5293
|
+
// src/rules/a11y-standard.ts
|
|
5294
|
+
var RULE_ID2 = "a11y/standard";
|
|
5295
|
+
var RULE_VERSION2 = "1";
|
|
5296
|
+
var KEY_HANDLERS = /* @__PURE__ */ new Set(["onKeyDown", "onKeyUp", "onKeyPress"]);
|
|
5297
|
+
function ruleA11yStandard(ix) {
|
|
5298
|
+
const policy = ix.policy.ruleConfig(RULE_ID2);
|
|
5299
|
+
if (!policy?.enabled) return [];
|
|
5300
|
+
const propsByNode = indexPropsByNodeId(ix);
|
|
5301
|
+
const findings = [];
|
|
5302
|
+
for (const node of ix.byKind("usage_node")) {
|
|
5303
|
+
if (node.role?.trim().toLowerCase() !== "button") continue;
|
|
5304
|
+
const props = propsByNode.get(node.id) ?? [];
|
|
5305
|
+
if (isAriaHidden(props) || isNativeButton(node.element, props)) continue;
|
|
5306
|
+
const missing = [
|
|
5307
|
+
...hasFocusableTabIndex(props) ? [] : ["tabIndex"],
|
|
5308
|
+
...hasKeyHandler(props) ? [] : ["keyboard handler"]
|
|
5309
|
+
];
|
|
5310
|
+
if (missing.length === 0) continue;
|
|
5311
|
+
findings.push(
|
|
5312
|
+
makeFinding({
|
|
5313
|
+
ruleId: RULE_ID2,
|
|
5314
|
+
ruleVersion: RULE_VERSION2,
|
|
5315
|
+
severity: policy.severity ?? "warn",
|
|
5316
|
+
message: `<${node.element} role="button"> is missing ${missing.join(
|
|
5317
|
+
" and "
|
|
5318
|
+
)}. Non-native buttons need tabIndex plus keyboard activation handling.`,
|
|
5319
|
+
location: node.location,
|
|
5320
|
+
evidence: ix.evidence([
|
|
5321
|
+
node.id,
|
|
5322
|
+
policy.id,
|
|
5323
|
+
...props.filter(
|
|
5324
|
+
(prop) => prop.prop === "tabIndex" || prop.prop === "tabindex" || KEY_HANDLERS.has(prop.prop)
|
|
5325
|
+
).map((prop) => prop.id)
|
|
5326
|
+
]),
|
|
5327
|
+
fingerprintIdentity: {
|
|
5328
|
+
file: node.file,
|
|
5329
|
+
element: node.element,
|
|
5330
|
+
nodePath: node.nodePath,
|
|
5331
|
+
standard: "keyboard-role-button"
|
|
5332
|
+
},
|
|
5333
|
+
attributes: {
|
|
5334
|
+
standard: "keyboard-role-button",
|
|
5335
|
+
role: "button",
|
|
5336
|
+
missing
|
|
5337
|
+
}
|
|
5338
|
+
})
|
|
5339
|
+
);
|
|
5340
|
+
}
|
|
5341
|
+
return findings;
|
|
5342
|
+
}
|
|
5343
|
+
function isNativeButton(element, props) {
|
|
5344
|
+
if (element.toLowerCase() === "button") return true;
|
|
5345
|
+
if (element.toLowerCase() !== "input") return false;
|
|
5346
|
+
return props.some(
|
|
5347
|
+
(prop) => prop.prop === "type" && prop.resolution === "static" && ["button", "submit", "reset"].includes(String(prop.value).toLowerCase())
|
|
5348
|
+
);
|
|
5349
|
+
}
|
|
5350
|
+
function hasFocusableTabIndex(props) {
|
|
5351
|
+
return props.some((prop) => {
|
|
5352
|
+
if (prop.prop !== "tabIndex" && prop.prop !== "tabindex") return false;
|
|
5353
|
+
if (prop.resolution === "dynamic") return true;
|
|
5354
|
+
if (prop.resolution !== "static") return false;
|
|
5355
|
+
const value = typeof prop.value === "number" ? prop.value : Number(prop.value);
|
|
5356
|
+
return Number.isFinite(value) && value >= 0;
|
|
5357
|
+
});
|
|
5358
|
+
}
|
|
5359
|
+
function hasKeyHandler(props) {
|
|
5360
|
+
return props.some(
|
|
5361
|
+
(prop) => KEY_HANDLERS.has(prop.prop) && prop.resolution !== "spread" && prop.value !== null && prop.value !== false
|
|
5362
|
+
);
|
|
4899
5363
|
}
|
|
4900
5364
|
|
|
4901
5365
|
// src/raw-html-canonical.ts
|
|
@@ -5080,8 +5544,8 @@ function decisionTargetsExport(target, exportName) {
|
|
|
5080
5544
|
}
|
|
5081
5545
|
|
|
5082
5546
|
// src/rules/components-prefer-library.ts
|
|
5083
|
-
var
|
|
5084
|
-
var
|
|
5547
|
+
var RULE_ID3 = "components/prefer-library";
|
|
5548
|
+
var RULE_VERSION3 = "1";
|
|
5085
5549
|
function isExplicitTagDeclaration(mapping) {
|
|
5086
5550
|
return mapping.declaredHtmlEquivalent === true;
|
|
5087
5551
|
}
|
|
@@ -5220,7 +5684,7 @@ function findClassNameReimpl(node, classTokensByNode, targets) {
|
|
|
5220
5684
|
return null;
|
|
5221
5685
|
}
|
|
5222
5686
|
function ruleComponentsPreferLibrary(ix) {
|
|
5223
|
-
const policy = ix.policy.ruleConfig(
|
|
5687
|
+
const policy = ix.policy.ruleConfig(RULE_ID3);
|
|
5224
5688
|
if (!policy?.enabled) return [];
|
|
5225
5689
|
const configuredSources = canonicalSourcesFromPolicy(policy.options?.canonicalSources);
|
|
5226
5690
|
const conflicts = projectCanonicalDirectionConflicts(ix, configuredSources);
|
|
@@ -5235,7 +5699,7 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5235
5699
|
const classTokensByNode = indexClassTokensByNode(ix);
|
|
5236
5700
|
const reimplTargets = canonicalReimplTargets(sources, mappings);
|
|
5237
5701
|
const canonicalDirectoryResolvedNodes = indexCanonicalDirectoryResolvedNodes(ix, sources);
|
|
5238
|
-
const shadowRenderRootNodeIds = indexShadowRenderRootNodeIds(ix);
|
|
5702
|
+
const shadowRenderRootNodeIds = ix.policy.ruleConfig("components/shadow-component")?.enabled === true ? indexShadowRenderRootNodeIds(ix) : /* @__PURE__ */ new Set();
|
|
5239
5703
|
const findings = [];
|
|
5240
5704
|
const seenImportFixes = /* @__PURE__ */ new Set();
|
|
5241
5705
|
for (const node of ix.byKind("usage_node")) {
|
|
@@ -5267,8 +5731,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5267
5731
|
seenImportFixes.add(key);
|
|
5268
5732
|
findings.push(
|
|
5269
5733
|
makeFinding({
|
|
5270
|
-
ruleId:
|
|
5271
|
-
ruleVersion:
|
|
5734
|
+
ruleId: RULE_ID3,
|
|
5735
|
+
ruleVersion: RULE_VERSION3,
|
|
5272
5736
|
severity: capAdvisorySeverity(policy.severity),
|
|
5273
5737
|
message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
|
|
5274
5738
|
location: imported.location,
|
|
@@ -5302,8 +5766,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5302
5766
|
if (node.element === suggestedComponent2) continue;
|
|
5303
5767
|
findings.push(
|
|
5304
5768
|
makeFinding({
|
|
5305
|
-
ruleId:
|
|
5306
|
-
ruleVersion:
|
|
5769
|
+
ruleId: RULE_ID3,
|
|
5770
|
+
ruleVersion: RULE_VERSION3,
|
|
5307
5771
|
severity: severityForTier(policy.severity, precisionTier2),
|
|
5308
5772
|
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}.`,
|
|
5309
5773
|
location: node.location,
|
|
@@ -5354,8 +5818,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5354
5818
|
const suggestedImport2 = reimpl.import ?? "the canonical library";
|
|
5355
5819
|
findings.push(
|
|
5356
5820
|
makeFinding({
|
|
5357
|
-
ruleId:
|
|
5358
|
-
ruleVersion:
|
|
5821
|
+
ruleId: RULE_ID3,
|
|
5822
|
+
ruleVersion: RULE_VERSION3,
|
|
5359
5823
|
severity: capAdvisorySeverity(policy.severity),
|
|
5360
5824
|
message: `<${node.element} class="${reimpl.matchedClass}"> looks like a hand-rolled <${reimpl.canonical}>. Use <${reimpl.canonical}> from ${suggestedImport2} instead of restyling a <${node.element}>.`,
|
|
5361
5825
|
location: node.location,
|
|
@@ -5395,8 +5859,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5395
5859
|
seenImportFixes.add(key);
|
|
5396
5860
|
findings.push(
|
|
5397
5861
|
makeFinding({
|
|
5398
|
-
ruleId:
|
|
5399
|
-
ruleVersion:
|
|
5862
|
+
ruleId: RULE_ID3,
|
|
5863
|
+
ruleVersion: RULE_VERSION3,
|
|
5400
5864
|
severity: capAdvisorySeverity(policy.severity),
|
|
5401
5865
|
message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
|
|
5402
5866
|
location: imported.location,
|
|
@@ -5438,14 +5902,15 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5438
5902
|
continue;
|
|
5439
5903
|
}
|
|
5440
5904
|
}
|
|
5441
|
-
const
|
|
5905
|
+
const importReady = suggestion.kind !== "directory";
|
|
5906
|
+
const suggestedImport = importReady ? sourceLabel(suggestion) : "the canonical component directory (resolve its configured import alias)";
|
|
5442
5907
|
const precisionTier = builtIn?.tier ?? "exact-html";
|
|
5443
5908
|
const rawElement = formatRawElement(node, props);
|
|
5444
5909
|
findings.push(
|
|
5445
5910
|
makeFinding({
|
|
5446
|
-
ruleId:
|
|
5447
|
-
ruleVersion:
|
|
5448
|
-
severity: severityForTier(policy.severity, precisionTier),
|
|
5911
|
+
ruleId: RULE_ID3,
|
|
5912
|
+
ruleVersion: RULE_VERSION3,
|
|
5913
|
+
severity: importReady ? severityForTier(policy.severity, precisionTier) : capAdvisorySeverity(policy.severity),
|
|
5449
5914
|
message: messageForBuiltInMatch({
|
|
5450
5915
|
node,
|
|
5451
5916
|
rawElement,
|
|
@@ -5462,7 +5927,7 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5462
5927
|
suggestedComponent,
|
|
5463
5928
|
suggestedImport
|
|
5464
5929
|
},
|
|
5465
|
-
|
|
5930
|
+
...importReady && !isRawHtmlAdvisoryTier(precisionTier) && isIdentifier(node.element) && isIdentifier(suggestedComponent) ? {
|
|
5466
5931
|
fix: {
|
|
5467
5932
|
kind: "replaceComponent",
|
|
5468
5933
|
title: `Replace ${rawElement} with <${suggestedComponent}>`,
|
|
@@ -5477,13 +5942,13 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5477
5942
|
attributes: {
|
|
5478
5943
|
rawValue: node.element,
|
|
5479
5944
|
suggestedComponent,
|
|
5480
|
-
suggestedImport,
|
|
5481
5945
|
suggestedImportSourceKind: suggestion.kind,
|
|
5946
|
+
...importReady ? { suggestedImport } : { canonicalDirectory: suggestion.path, importReady: false },
|
|
5482
5947
|
precisionTier,
|
|
5483
5948
|
propCompatibility: "unknown",
|
|
5484
5949
|
...builtIn?.matchedRole ? { matchedRole: builtIn.matchedRole } : {},
|
|
5485
5950
|
...builtIn?.inputType ? { matchedInputType: builtIn.inputType } : {},
|
|
5486
|
-
...isRawHtmlAdvisoryTier(precisionTier) ? { advisory: true } : {}
|
|
5951
|
+
...isRawHtmlAdvisoryTier(precisionTier) || !importReady ? { advisory: true } : {}
|
|
5487
5952
|
}
|
|
5488
5953
|
})
|
|
5489
5954
|
);
|
|
@@ -5503,11 +5968,19 @@ function suppressConflictedExports(source, conflicts) {
|
|
|
5503
5968
|
function indexShadowRenderRootNodeIds(ix) {
|
|
5504
5969
|
const identities = ix.byKind("component_identity");
|
|
5505
5970
|
if (identities.length === 0) return /* @__PURE__ */ new Set();
|
|
5971
|
+
const definitions = new Map(
|
|
5972
|
+
ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
|
|
5973
|
+
);
|
|
5506
5974
|
const nodeIds = /* @__PURE__ */ new Set();
|
|
5507
5975
|
for (const identity of identities) {
|
|
5508
5976
|
if (identity.state !== "shadow") continue;
|
|
5977
|
+
const definition = definitions.get(identity.componentKey);
|
|
5978
|
+
if (!definition) continue;
|
|
5509
5979
|
for (const evidenceId of identity.evidence) {
|
|
5510
|
-
|
|
5980
|
+
const evidence = ix.get(evidenceId);
|
|
5981
|
+
if (evidence?.kind === "usage_node" && evidence.file === definition.file) {
|
|
5982
|
+
nodeIds.add(evidenceId);
|
|
5983
|
+
}
|
|
5511
5984
|
}
|
|
5512
5985
|
}
|
|
5513
5986
|
return nodeIds;
|
|
@@ -5790,7 +6263,7 @@ function sourceLabel(source) {
|
|
|
5790
6263
|
function canonicalImportPath(source) {
|
|
5791
6264
|
if (source.kind === "npm") return source.specifier;
|
|
5792
6265
|
if (source.kind === "registry") return source.importPath;
|
|
5793
|
-
return
|
|
6266
|
+
return void 0;
|
|
5794
6267
|
}
|
|
5795
6268
|
function indexCanonicalDirectoryResolvedNodes(ix, sources) {
|
|
5796
6269
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -5866,10 +6339,10 @@ function indexComponentIdentityUsage(ix) {
|
|
|
5866
6339
|
}
|
|
5867
6340
|
|
|
5868
6341
|
// src/rules/components-shadow-component.ts
|
|
5869
|
-
var
|
|
5870
|
-
var
|
|
6342
|
+
var RULE_ID4 = "components/shadow-component";
|
|
6343
|
+
var RULE_VERSION4 = "1";
|
|
5871
6344
|
function ruleComponentsShadowComponent(ix) {
|
|
5872
|
-
const policy = ix.policy.ruleConfig(
|
|
6345
|
+
const policy = ix.policy.ruleConfig(RULE_ID4);
|
|
5873
6346
|
if (!policy?.enabled) return [];
|
|
5874
6347
|
const definitions = new Map(
|
|
5875
6348
|
ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
|
|
@@ -5886,8 +6359,8 @@ function ruleComponentsShadowComponent(ix) {
|
|
|
5886
6359
|
const blastRadius = usage?.blastRadius ?? 0;
|
|
5887
6360
|
findings.push(
|
|
5888
6361
|
makeFinding({
|
|
5889
|
-
ruleId:
|
|
5890
|
-
ruleVersion:
|
|
6362
|
+
ruleId: RULE_ID4,
|
|
6363
|
+
ruleVersion: RULE_VERSION4,
|
|
5891
6364
|
severity: reviewVariant ? "info" : policy.severity ?? "warn",
|
|
5892
6365
|
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"}.`,
|
|
5893
6366
|
location: definitionLocation(ix, definition, identity),
|
|
@@ -5925,8 +6398,8 @@ function definitionLocation(ix, definition, identity) {
|
|
|
5925
6398
|
}
|
|
5926
6399
|
|
|
5927
6400
|
// src/rules/components-forbidden-prop-value.ts
|
|
5928
|
-
var
|
|
5929
|
-
var
|
|
6401
|
+
var RULE_ID5 = "components/forbidden-prop-value";
|
|
6402
|
+
var RULE_VERSION5 = "1";
|
|
5930
6403
|
function ruleComponentsForbiddenPropValue(ix) {
|
|
5931
6404
|
const findings = [];
|
|
5932
6405
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -5956,8 +6429,8 @@ function ruleComponentsForbiddenPropValue(ix) {
|
|
|
5956
6429
|
} : void 0;
|
|
5957
6430
|
findings.push(
|
|
5958
6431
|
makeFinding({
|
|
5959
|
-
ruleId:
|
|
5960
|
-
ruleVersion:
|
|
6432
|
+
ruleId: RULE_ID5,
|
|
6433
|
+
ruleVersion: RULE_VERSION5,
|
|
5961
6434
|
severity: policy.severity,
|
|
5962
6435
|
message: buildMessage(
|
|
5963
6436
|
node.element,
|
|
@@ -6001,8 +6474,8 @@ function buildMessage(element, prop, value, because, replacement) {
|
|
|
6001
6474
|
}
|
|
6002
6475
|
|
|
6003
6476
|
// src/rules/components-unknown-prop.ts
|
|
6004
|
-
var
|
|
6005
|
-
var
|
|
6477
|
+
var RULE_ID6 = "components/unknown-prop";
|
|
6478
|
+
var RULE_VERSION6 = "1";
|
|
6006
6479
|
function ruleComponentsUnknownProp(ix) {
|
|
6007
6480
|
const policy = ix.policy.jsxUnknownPropsForbidden();
|
|
6008
6481
|
if (!policy) return [];
|
|
@@ -6023,8 +6496,8 @@ function ruleComponentsUnknownProp(ix) {
|
|
|
6023
6496
|
if (knownNames.has(propUsage.prop)) continue;
|
|
6024
6497
|
findings.push(
|
|
6025
6498
|
makeFinding({
|
|
6026
|
-
ruleId:
|
|
6027
|
-
ruleVersion:
|
|
6499
|
+
ruleId: RULE_ID6,
|
|
6500
|
+
ruleVersion: RULE_VERSION6,
|
|
6028
6501
|
severity: policy.severity,
|
|
6029
6502
|
message: `Unknown prop "${propUsage.prop}" on <${node.element}>. Allowed props: ${formatPropList(knownNames)}.`,
|
|
6030
6503
|
location: node.location,
|
|
@@ -6056,7 +6529,7 @@ function formatPropList(names) {
|
|
|
6056
6529
|
// src/rules/composition-pattern.ts
|
|
6057
6530
|
var CARDINALITY_RULE_ID = "composition/cardinality";
|
|
6058
6531
|
var CO_OCCURRENCE_RULE_ID = "composition/co-occurrence";
|
|
6059
|
-
var
|
|
6532
|
+
var RULE_VERSION7 = "1";
|
|
6060
6533
|
function ruleCompositionCardinality(ix) {
|
|
6061
6534
|
return evaluate(ix, CARDINALITY_RULE_ID, "cardinality");
|
|
6062
6535
|
}
|
|
@@ -6124,7 +6597,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
6124
6597
|
const extras = selected.slice(constraint.max);
|
|
6125
6598
|
return makeFinding({
|
|
6126
6599
|
ruleId: ctx.ruleId,
|
|
6127
|
-
ruleVersion:
|
|
6600
|
+
ruleVersion: RULE_VERSION7,
|
|
6128
6601
|
severity: ctx.severity,
|
|
6129
6602
|
message: cardinalityMaxMessage(pattern, constraint.max, count),
|
|
6130
6603
|
location: extras[0].location,
|
|
@@ -6135,7 +6608,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
6135
6608
|
if (constraint.min !== void 0 && count < constraint.min) {
|
|
6136
6609
|
return makeFinding({
|
|
6137
6610
|
ruleId: ctx.ruleId,
|
|
6138
|
-
ruleVersion:
|
|
6611
|
+
ruleVersion: RULE_VERSION7,
|
|
6139
6612
|
severity: ctx.severity,
|
|
6140
6613
|
message: cardinalityMinMessage(pattern, constraint.min, count),
|
|
6141
6614
|
location: region.container.location,
|
|
@@ -6156,7 +6629,7 @@ function checkCoOccurrence(ctx, region, pattern, constraint, selected) {
|
|
|
6156
6629
|
if (satisfied) return null;
|
|
6157
6630
|
return makeFinding({
|
|
6158
6631
|
ruleId: ctx.ruleId,
|
|
6159
|
-
ruleVersion:
|
|
6632
|
+
ruleVersion: RULE_VERSION7,
|
|
6160
6633
|
severity: ctx.severity,
|
|
6161
6634
|
message: coOccurrenceMessage(pattern, constraint),
|
|
6162
6635
|
location: region.container.location,
|
|
@@ -6208,8 +6681,8 @@ function coOccurrenceMessage(pattern, constraint) {
|
|
|
6208
6681
|
}
|
|
6209
6682
|
|
|
6210
6683
|
// src/rules/jsx-preferred-component.ts
|
|
6211
|
-
var
|
|
6212
|
-
var
|
|
6684
|
+
var RULE_ID7 = "components/preferred-component";
|
|
6685
|
+
var RULE_VERSION8 = "1";
|
|
6213
6686
|
function ruleJsxPreferredComponent(ix) {
|
|
6214
6687
|
const policies = ix.policy.jsxComponentPreferred();
|
|
6215
6688
|
if (policies.length === 0) return [];
|
|
@@ -6225,8 +6698,8 @@ function ruleJsxPreferredComponent(ix) {
|
|
|
6225
6698
|
if (!replacement) continue;
|
|
6226
6699
|
findings.push(
|
|
6227
6700
|
makeFinding({
|
|
6228
|
-
ruleId:
|
|
6229
|
-
ruleVersion:
|
|
6701
|
+
ruleId: RULE_ID7,
|
|
6702
|
+
ruleVersion: RULE_VERSION8,
|
|
6230
6703
|
severity: policy.severity,
|
|
6231
6704
|
message: `<${node.element}> should use <${replacement}>.`,
|
|
6232
6705
|
location: node.location,
|
|
@@ -6263,8 +6736,8 @@ function componentNameFromId(componentId2) {
|
|
|
6263
6736
|
}
|
|
6264
6737
|
|
|
6265
6738
|
// src/rules/jsx-preferred-import-path.ts
|
|
6266
|
-
var
|
|
6267
|
-
var
|
|
6739
|
+
var RULE_ID8 = "imports/preferred-path";
|
|
6740
|
+
var RULE_VERSION9 = "1";
|
|
6268
6741
|
function ruleJsxPreferredImportPath(ix) {
|
|
6269
6742
|
const policies = ix.policy.jsxImportPathPreferred();
|
|
6270
6743
|
if (policies.length === 0) return [];
|
|
@@ -6292,8 +6765,8 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6292
6765
|
seen.add(key);
|
|
6293
6766
|
findings.push(
|
|
6294
6767
|
makeFinding({
|
|
6295
|
-
ruleId:
|
|
6296
|
-
ruleVersion:
|
|
6768
|
+
ruleId: RULE_ID8,
|
|
6769
|
+
ruleVersion: RULE_VERSION9,
|
|
6297
6770
|
severity: policy.severity,
|
|
6298
6771
|
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}".`,
|
|
6299
6772
|
location: usage.location,
|
|
@@ -6352,10 +6825,10 @@ function normalizeRepoPath(path) {
|
|
|
6352
6825
|
}
|
|
6353
6826
|
|
|
6354
6827
|
// src/rules/props-invalid-value.ts
|
|
6355
|
-
var
|
|
6356
|
-
var
|
|
6828
|
+
var RULE_ID9 = "props/invalid-value";
|
|
6829
|
+
var RULE_VERSION10 = "1";
|
|
6357
6830
|
function rulePropsInvalidValue(ix) {
|
|
6358
|
-
const policy = ix.policy.ruleConfig(
|
|
6831
|
+
const policy = ix.policy.ruleConfig(RULE_ID9);
|
|
6359
6832
|
if (!policy?.enabled) return [];
|
|
6360
6833
|
const findings = [];
|
|
6361
6834
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -6384,8 +6857,8 @@ function rulePropsInvalidValue(ix) {
|
|
|
6384
6857
|
} : void 0;
|
|
6385
6858
|
findings.push(
|
|
6386
6859
|
makeFinding({
|
|
6387
|
-
ruleId:
|
|
6388
|
-
ruleVersion:
|
|
6860
|
+
ruleId: RULE_ID9,
|
|
6861
|
+
ruleVersion: RULE_VERSION10,
|
|
6389
6862
|
severity: policy.severity ?? "warn",
|
|
6390
6863
|
message: buildMessage2(node.element, propUsage.prop, propUsage.value, allowedValues),
|
|
6391
6864
|
location: propUsage.location ?? node.location,
|
|
@@ -6584,8 +7057,8 @@ function localTokenCandidates(ix, category) {
|
|
|
6584
7057
|
}
|
|
6585
7058
|
|
|
6586
7059
|
// src/rules/styles-no-raw-color.ts
|
|
6587
|
-
var
|
|
6588
|
-
var
|
|
7060
|
+
var RULE_ID10 = "styles/no-raw-color";
|
|
7061
|
+
var RULE_VERSION11 = "1";
|
|
6589
7062
|
function ruleStylesNoRawColor(ix) {
|
|
6590
7063
|
const policy = ix.policy.rawColorPolicy();
|
|
6591
7064
|
if (!policy) return [];
|
|
@@ -6604,8 +7077,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6604
7077
|
const evidenceIds = resolution ? [decl.id, policy.id, resolution.token.id] : [decl.id, policy.id];
|
|
6605
7078
|
findings.push(
|
|
6606
7079
|
makeFinding({
|
|
6607
|
-
ruleId:
|
|
6608
|
-
ruleVersion:
|
|
7080
|
+
ruleId: RULE_ID10,
|
|
7081
|
+
ruleVersion: RULE_VERSION11,
|
|
6609
7082
|
severity: policy.severity,
|
|
6610
7083
|
message: colorMessage(color, `\`${decl.property}\``, resolution, preferLabel) + (hopped?.suffix ?? ""),
|
|
6611
7084
|
location: decl.location,
|
|
@@ -6641,8 +7114,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6641
7114
|
const evidenceIds2 = componentEvidenceId2 ? [node2.id, componentEvidenceId2, inline.id, policy.id] : [node2.id, inline.id, policy.id];
|
|
6642
7115
|
findings.push(
|
|
6643
7116
|
makeFinding({
|
|
6644
|
-
ruleId:
|
|
6645
|
-
ruleVersion:
|
|
7117
|
+
ruleId: RULE_ID10,
|
|
7118
|
+
ruleVersion: RULE_VERSION11,
|
|
6646
7119
|
severity: advisorySeverity,
|
|
6647
7120
|
message: `Runtime-constructed raw color \`${inline.value}\` on inline \`${inline.property}\`. Use a ${preferLabel} instead.`,
|
|
6648
7121
|
location: node2.location,
|
|
@@ -6679,8 +7152,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6679
7152
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6680
7153
|
findings.push(
|
|
6681
7154
|
makeFinding({
|
|
6682
|
-
ruleId:
|
|
6683
|
-
ruleVersion:
|
|
7155
|
+
ruleId: RULE_ID10,
|
|
7156
|
+
ruleVersion: RULE_VERSION11,
|
|
6684
7157
|
severity: policy.severity,
|
|
6685
7158
|
message: colorMessage(color, `inline \`${inline.property}\``, resolution, preferLabel) + (hopped?.suffix ?? ""),
|
|
6686
7159
|
location: node.location,
|
|
@@ -6724,8 +7197,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6724
7197
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6725
7198
|
findings.push(
|
|
6726
7199
|
makeFinding({
|
|
6727
|
-
ruleId:
|
|
6728
|
-
ruleVersion:
|
|
7200
|
+
ruleId: RULE_ID10,
|
|
7201
|
+
ruleVersion: RULE_VERSION11,
|
|
6729
7202
|
severity: advisorySeverity,
|
|
6730
7203
|
message: colorMessage(color, `\`${prop.prop}\``, resolution, preferLabel),
|
|
6731
7204
|
location: prop.location ?? node.location,
|
|
@@ -6876,8 +7349,8 @@ function escapeRegExp(value) {
|
|
|
6876
7349
|
}
|
|
6877
7350
|
|
|
6878
7351
|
// src/rules/styles-no-raw-dimensions.ts
|
|
6879
|
-
var
|
|
6880
|
-
var
|
|
7352
|
+
var RULE_ID11 = "styles/no-raw-dimensions";
|
|
7353
|
+
var RULE_VERSION12 = "1";
|
|
6881
7354
|
function ruleStylesNoRawDimensions(ix) {
|
|
6882
7355
|
const policy = ix.policy.rawDimensionPolicy();
|
|
6883
7356
|
if (!policy) return [];
|
|
@@ -6898,8 +7371,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6898
7371
|
const fixEmission = hopped ? void 0 : dimensionFix(decl.property, resolution, ix);
|
|
6899
7372
|
findings.push(
|
|
6900
7373
|
makeFinding({
|
|
6901
|
-
ruleId:
|
|
6902
|
-
ruleVersion:
|
|
7374
|
+
ruleId: RULE_ID11,
|
|
7375
|
+
ruleVersion: RULE_VERSION12,
|
|
6903
7376
|
severity: policy.severity,
|
|
6904
7377
|
message: dimensionMessage(decl.value, decl.property, preferLabel, resolution, false) + (hopped?.suffix ?? ""),
|
|
6905
7378
|
location: decl.location,
|
|
@@ -6942,8 +7415,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6942
7415
|
const fixEmission = hopped ? void 0 : dimensionFix(inline.property, resolution, ix);
|
|
6943
7416
|
findings.push(
|
|
6944
7417
|
makeFinding({
|
|
6945
|
-
ruleId:
|
|
6946
|
-
ruleVersion:
|
|
7418
|
+
ruleId: RULE_ID11,
|
|
7419
|
+
ruleVersion: RULE_VERSION12,
|
|
6947
7420
|
severity: policy.severity,
|
|
6948
7421
|
message: dimensionMessage(inline.value, inline.property, preferLabel, resolution, true) + (hopped?.suffix ?? ""),
|
|
6949
7422
|
location: node.location,
|
|
@@ -7212,8 +7685,8 @@ function parseLengthParts(raw) {
|
|
|
7212
7685
|
}
|
|
7213
7686
|
|
|
7214
7687
|
// src/rules/styles-no-raw-spacing.ts
|
|
7215
|
-
var
|
|
7216
|
-
var
|
|
7688
|
+
var RULE_ID12 = "styles/no-raw-spacing";
|
|
7689
|
+
var RULE_VERSION13 = "1";
|
|
7217
7690
|
function ruleStylesNoRawSpacing(ix) {
|
|
7218
7691
|
const findings = [];
|
|
7219
7692
|
const lookupCache = /* @__PURE__ */ new Map();
|
|
@@ -7256,8 +7729,8 @@ function checkDeclaration(ix, decl, lookupFor) {
|
|
|
7256
7729
|
const hopped = hoppedValue(decl);
|
|
7257
7730
|
const fixEmission = hopped ? void 0 : buildSpacingFix(decl.property, checked, ix);
|
|
7258
7731
|
return makeFinding({
|
|
7259
|
-
ruleId:
|
|
7260
|
-
ruleVersion:
|
|
7732
|
+
ruleId: RULE_ID12,
|
|
7733
|
+
ruleVersion: RULE_VERSION13,
|
|
7261
7734
|
severity: policy.severity,
|
|
7262
7735
|
message: spacingMessage(decl.property, decl.value, allowed, scale, checked) + (hopped?.suffix ?? ""),
|
|
7263
7736
|
location: decl.location,
|
|
@@ -7313,8 +7786,8 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
|
|
|
7313
7786
|
const componentEvidenceId = componentByNode.get(node.id)?.id;
|
|
7314
7787
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
7315
7788
|
return makeFinding({
|
|
7316
|
-
ruleId:
|
|
7317
|
-
ruleVersion:
|
|
7789
|
+
ruleId: RULE_ID12,
|
|
7790
|
+
ruleVersion: RULE_VERSION13,
|
|
7318
7791
|
severity: policy.severity,
|
|
7319
7792
|
message: spacingMessage(inline.property, inline.value, allowed, scale, checked) + (hopped?.suffix ?? ""),
|
|
7320
7793
|
location: node.location,
|
|
@@ -7376,8 +7849,8 @@ function spacingMessage(property, value, allowed, scale, checked) {
|
|
|
7376
7849
|
}
|
|
7377
7850
|
|
|
7378
7851
|
// src/rules/styles-no-raw-typography.ts
|
|
7379
|
-
var
|
|
7380
|
-
var
|
|
7852
|
+
var RULE_ID13 = "styles/no-raw-typography";
|
|
7853
|
+
var RULE_VERSION14 = "1";
|
|
7381
7854
|
var FONT_SIZE_PROPERTIES = /* @__PURE__ */ new Set(["font-size", "fontsize"]);
|
|
7382
7855
|
function ruleStylesNoRawTypography(ix) {
|
|
7383
7856
|
const policy = ix.policy.fontSizeScale();
|
|
@@ -7396,8 +7869,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7396
7869
|
const fixEmission = hopped ? void 0 : buildFix(decl.property, checked, ix);
|
|
7397
7870
|
findings.push(
|
|
7398
7871
|
makeFinding({
|
|
7399
|
-
ruleId:
|
|
7400
|
-
ruleVersion:
|
|
7872
|
+
ruleId: RULE_ID13,
|
|
7873
|
+
ruleVersion: RULE_VERSION14,
|
|
7401
7874
|
severity: policy.severity,
|
|
7402
7875
|
message: typographyMessage(decl.property, decl.value, allowed, scale.unit, checked) + (hopped?.suffix ?? ""),
|
|
7403
7876
|
location: decl.location,
|
|
@@ -7445,8 +7918,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7445
7918
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
7446
7919
|
findings.push(
|
|
7447
7920
|
makeFinding({
|
|
7448
|
-
ruleId:
|
|
7449
|
-
ruleVersion:
|
|
7921
|
+
ruleId: RULE_ID13,
|
|
7922
|
+
ruleVersion: RULE_VERSION14,
|
|
7450
7923
|
severity: policy.severity,
|
|
7451
7924
|
message: typographyMessage(inline.property, inline.value, allowed, scale.unit, checked) + (hopped?.suffix ?? ""),
|
|
7452
7925
|
location: node.location,
|
|
@@ -7549,15 +8022,15 @@ function typographyMessage(property, value, allowed, unit, checked) {
|
|
|
7549
8022
|
}
|
|
7550
8023
|
|
|
7551
8024
|
// src/rules/theme-no-theme-coupled-literal.ts
|
|
7552
|
-
var
|
|
7553
|
-
var
|
|
8025
|
+
var RULE_ID14 = "theme/no-theme-coupled-literal";
|
|
8026
|
+
var RULE_VERSION15 = "1";
|
|
7554
8027
|
var COLOR_LITERAL_GLOBAL = /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?|oklab|oklch|lab|lch)\(\s*[\d.%\s,\/]+\s*\)/g;
|
|
7555
8028
|
var WHITE_RGBA = /^rgba?\(\s*255\s*,\s*255\s*,\s*255/i;
|
|
7556
8029
|
var BLACK_RGBA = /^rgba?\(\s*0\s*,\s*0\s*,\s*0/i;
|
|
7557
8030
|
var BOX_SHADOW_PROP = /^(?:-webkit-|-moz-)?box-shadow$/i;
|
|
7558
8031
|
var NEUTRAL_SHADOW = /^(?:none|inherit|initial|unset|revert|revert-layer)\s*$/i;
|
|
7559
8032
|
function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
7560
|
-
const policy = ix.policy.ruleConfig(
|
|
8033
|
+
const policy = ix.policy.ruleConfig(RULE_ID14);
|
|
7561
8034
|
if (!policy?.enabled) return [];
|
|
7562
8035
|
const findings = [];
|
|
7563
8036
|
for (const decl of ix.byKind("style_declaration")) {
|
|
@@ -7566,8 +8039,8 @@ function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
|
7566
8039
|
if (!hit) continue;
|
|
7567
8040
|
findings.push(
|
|
7568
8041
|
makeFinding({
|
|
7569
|
-
ruleId:
|
|
7570
|
-
ruleVersion:
|
|
8042
|
+
ruleId: RULE_ID14,
|
|
8043
|
+
ruleVersion: RULE_VERSION15,
|
|
7571
8044
|
severity: policy.severity ?? "warn",
|
|
7572
8045
|
message: hit.message,
|
|
7573
8046
|
location: decl.location,
|
|
@@ -7657,8 +8130,8 @@ function isTokenFile(filePath) {
|
|
|
7657
8130
|
}
|
|
7658
8131
|
|
|
7659
8132
|
// src/rules/tailwind-arbitrary-color.ts
|
|
7660
|
-
var
|
|
7661
|
-
var
|
|
8133
|
+
var RULE_ID15 = "tailwind/arbitrary-color";
|
|
8134
|
+
var RULE_VERSION16 = "1";
|
|
7662
8135
|
var COLOR_UTILITIES = /* @__PURE__ */ new Set([
|
|
7663
8136
|
"accent",
|
|
7664
8137
|
"bg",
|
|
@@ -7700,8 +8173,8 @@ function ruleTailwindArbitraryColor(ix) {
|
|
|
7700
8173
|
const evidenceIds = node ? [node.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
7701
8174
|
findings.push(
|
|
7702
8175
|
makeFinding({
|
|
7703
|
-
ruleId:
|
|
7704
|
-
ruleVersion:
|
|
8176
|
+
ruleId: RULE_ID15,
|
|
8177
|
+
ruleVersion: RULE_VERSION16,
|
|
7705
8178
|
severity: policy.severity,
|
|
7706
8179
|
message: `\`${klass.raw}\` uses arbitrary raw color ${color}. Use a ${policy.prefer === "token" ? "design token" : "CSS variable"} instead.`,
|
|
7707
8180
|
location: klass.location,
|
|
@@ -7732,8 +8205,8 @@ function fingerprintIdentity(klass, color) {
|
|
|
7732
8205
|
}
|
|
7733
8206
|
|
|
7734
8207
|
// src/rules/tailwind-arbitrary-spacing.ts
|
|
7735
|
-
var
|
|
7736
|
-
var
|
|
8208
|
+
var RULE_ID16 = "tailwind/arbitrary-spacing";
|
|
8209
|
+
var RULE_VERSION17 = "1";
|
|
7737
8210
|
var SPACING_UTILITY_TO_PROPERTY = /* @__PURE__ */ new Map([
|
|
7738
8211
|
["m", "margin"],
|
|
7739
8212
|
["mt", "margin"],
|
|
@@ -7779,8 +8252,8 @@ function ruleTailwindArbitrarySpacing(ix) {
|
|
|
7779
8252
|
const fix = buildFix2(klass);
|
|
7780
8253
|
findings.push(
|
|
7781
8254
|
makeFinding({
|
|
7782
|
-
ruleId:
|
|
7783
|
-
ruleVersion:
|
|
8255
|
+
ruleId: RULE_ID16,
|
|
8256
|
+
ruleVersion: RULE_VERSION17,
|
|
7784
8257
|
severity: policy.severity,
|
|
7785
8258
|
message: `\`${klass.raw}\` is an arbitrary spacing value not on the project's spacing scale.`,
|
|
7786
8259
|
location: klass.location,
|
|
@@ -7911,8 +8384,8 @@ function matchesTokenGlob(value, pattern) {
|
|
|
7911
8384
|
}
|
|
7912
8385
|
|
|
7913
8386
|
// src/rules/tailwind-forbidden-palette.ts
|
|
7914
|
-
var
|
|
7915
|
-
var
|
|
8387
|
+
var RULE_ID17 = "tailwind/forbidden-palette";
|
|
8388
|
+
var RULE_VERSION18 = "1";
|
|
7916
8389
|
function ruleTailwindForbiddenPalette(ix) {
|
|
7917
8390
|
const deny = ix.policy.tailwindPaletteDeny();
|
|
7918
8391
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
@@ -7937,8 +8410,8 @@ function ruleTailwindForbiddenPalette(ix) {
|
|
|
7937
8410
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
7938
8411
|
findings.push(
|
|
7939
8412
|
makeFinding({
|
|
7940
|
-
ruleId:
|
|
7941
|
-
ruleVersion:
|
|
8413
|
+
ruleId: RULE_ID17,
|
|
8414
|
+
ruleVersion: RULE_VERSION18,
|
|
7942
8415
|
severity: policy.severity,
|
|
7943
8416
|
message: messageFor(klass, token, deniedPattern, allow?.patterns ?? []),
|
|
7944
8417
|
location: klass.location,
|
|
@@ -7985,8 +8458,8 @@ function buildFix3(klass) {
|
|
|
7985
8458
|
}
|
|
7986
8459
|
|
|
7987
8460
|
// src/rules/tailwind-off-scale-spacing-token.ts
|
|
7988
|
-
var
|
|
7989
|
-
var
|
|
8461
|
+
var RULE_ID18 = "tailwind/off-scale-spacing-token";
|
|
8462
|
+
var RULE_VERSION19 = "1";
|
|
7990
8463
|
function ruleTailwindOffScaleSpacingToken(ix) {
|
|
7991
8464
|
const resolvedByKey = indexResolvedTailwindTokens(ix);
|
|
7992
8465
|
const findings = [];
|
|
@@ -8012,8 +8485,8 @@ function ruleTailwindOffScaleSpacingToken(ix) {
|
|
|
8012
8485
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id, scale.id] : [klass.id, resolved.id, policy.id, scale.id];
|
|
8013
8486
|
findings.push(
|
|
8014
8487
|
makeFinding({
|
|
8015
|
-
ruleId:
|
|
8016
|
-
ruleVersion:
|
|
8488
|
+
ruleId: RULE_ID18,
|
|
8489
|
+
ruleVersion: RULE_VERSION19,
|
|
8017
8490
|
severity: policy.severity,
|
|
8018
8491
|
message: `\`${klass.raw}\` resolves to ${formatResolvedValue(signedValue, scale.unit)}, which is not on the project's spacing scale [${allowed.join(", ")}].`,
|
|
8019
8492
|
location: klass.location,
|
|
@@ -8062,8 +8535,8 @@ function buildFix4(raw, utility) {
|
|
|
8062
8535
|
}
|
|
8063
8536
|
|
|
8064
8537
|
// src/rules/tailwind-raw-color-via-token.ts
|
|
8065
|
-
var
|
|
8066
|
-
var
|
|
8538
|
+
var RULE_ID19 = "tailwind/raw-color-via-token";
|
|
8539
|
+
var RULE_VERSION20 = "1";
|
|
8067
8540
|
function ruleTailwindRawColorViaToken(ix) {
|
|
8068
8541
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
8069
8542
|
if (!allow || allow.patterns.length === 0) return [];
|
|
@@ -8083,8 +8556,8 @@ function ruleTailwindRawColorViaToken(ix) {
|
|
|
8083
8556
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, allow.id] : [klass.id, resolved.id, allow.id];
|
|
8084
8557
|
findings.push(
|
|
8085
8558
|
makeFinding({
|
|
8086
|
-
ruleId:
|
|
8087
|
-
ruleVersion:
|
|
8559
|
+
ruleId: RULE_ID19,
|
|
8560
|
+
ruleVersion: RULE_VERSION20,
|
|
8088
8561
|
severity: allow.severity,
|
|
8089
8562
|
message: `\`${klass.raw}\` resolves to raw color \`${color}\` (${sourceLabel2(resolved.resolved.source)}). Project policy requires \`${allow.patterns.join("`, `")}\` palette tokens.`,
|
|
8090
8563
|
location: klass.location,
|
|
@@ -8117,8 +8590,8 @@ function sourceLabel2(source) {
|
|
|
8117
8590
|
}
|
|
8118
8591
|
|
|
8119
8592
|
// src/rules/tailwind-unknown-class.ts
|
|
8120
|
-
var
|
|
8121
|
-
var
|
|
8593
|
+
var RULE_ID20 = "tailwind/unknown-class";
|
|
8594
|
+
var RULE_VERSION21 = "1";
|
|
8122
8595
|
var UNIVERSAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
8123
8596
|
"white",
|
|
8124
8597
|
"black",
|
|
@@ -8139,8 +8612,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
8139
8612
|
const evidenceIds2 = node2 ? [node2.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
8140
8613
|
findings.push(
|
|
8141
8614
|
makeFinding({
|
|
8142
|
-
ruleId:
|
|
8143
|
-
ruleVersion:
|
|
8615
|
+
ruleId: RULE_ID20,
|
|
8616
|
+
ruleVersion: RULE_VERSION21,
|
|
8144
8617
|
severity: policy.severity,
|
|
8145
8618
|
message: `\`${klass.raw}\` could not be parsed as a known Tailwind utility.`,
|
|
8146
8619
|
location: klass.location,
|
|
@@ -8174,8 +8647,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
8174
8647
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
8175
8648
|
findings.push(
|
|
8176
8649
|
makeFinding({
|
|
8177
|
-
ruleId:
|
|
8178
|
-
ruleVersion:
|
|
8650
|
+
ruleId: RULE_ID20,
|
|
8651
|
+
ruleVersion: RULE_VERSION21,
|
|
8179
8652
|
severity: policy.severity,
|
|
8180
8653
|
message: `\`${klass.raw}\` did not resolve to any known palette token.`,
|
|
8181
8654
|
location: klass.location,
|
|
@@ -8203,12 +8676,12 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
8203
8676
|
}
|
|
8204
8677
|
|
|
8205
8678
|
// src/rules/tokens-require-dual-fallback.ts
|
|
8206
|
-
var
|
|
8207
|
-
var
|
|
8679
|
+
var RULE_ID21 = "tokens/require-dual-fallback";
|
|
8680
|
+
var RULE_VERSION22 = "1";
|
|
8208
8681
|
var CSS_VAR_WITH_OPTIONAL_FALLBACK = /var\(\s*(--[A-Za-z0-9_-]+)(\s*,\s*[^)]*)?\)/g;
|
|
8209
8682
|
var SASS_FILE = /\.(scss|sass)$/i;
|
|
8210
8683
|
function ruleTokensRequireDualFallback(ix) {
|
|
8211
|
-
const policy = ix.policy.ruleConfig(
|
|
8684
|
+
const policy = ix.policy.ruleConfig(RULE_ID21);
|
|
8212
8685
|
if (!policy?.enabled) return [];
|
|
8213
8686
|
const tokensByCssVariable = indexTokensByCssVariable(localTokenCandidates(ix));
|
|
8214
8687
|
const findings = [];
|
|
@@ -8243,8 +8716,8 @@ function ruleTokensRequireDualFallback(ix) {
|
|
|
8243
8716
|
) : void 0;
|
|
8244
8717
|
findings.push(
|
|
8245
8718
|
makeFinding({
|
|
8246
|
-
ruleId:
|
|
8247
|
-
ruleVersion:
|
|
8719
|
+
ruleId: RULE_ID21,
|
|
8720
|
+
ruleVersion: RULE_VERSION22,
|
|
8248
8721
|
severity: policy.severity ?? "warn",
|
|
8249
8722
|
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}.`,
|
|
8250
8723
|
location: decl.location,
|
|
@@ -8284,8 +8757,8 @@ function indexTokensByCssVariable(tokens) {
|
|
|
8284
8757
|
}
|
|
8285
8758
|
|
|
8286
8759
|
// src/rules/tokens-css-vars-must-be-defined.ts
|
|
8287
|
-
var
|
|
8288
|
-
var
|
|
8760
|
+
var RULE_ID22 = "tokens/css-vars-must-be-defined";
|
|
8761
|
+
var RULE_VERSION23 = "1";
|
|
8289
8762
|
var CSS_VAR_REFERENCE = /var\(\s*(--[A-Za-z0-9_-]+)/gi;
|
|
8290
8763
|
function ruleTokensCssVarsMustBeDefined(ix) {
|
|
8291
8764
|
const policy = ix.policy.cssVarsMustBeDefined();
|
|
@@ -8312,8 +8785,8 @@ function ruleTokensCssVarsMustBeDefined(ix) {
|
|
|
8312
8785
|
seen.add(tokenName);
|
|
8313
8786
|
findings.push(
|
|
8314
8787
|
makeFinding({
|
|
8315
|
-
ruleId:
|
|
8316
|
-
ruleVersion:
|
|
8788
|
+
ruleId: RULE_ID22,
|
|
8789
|
+
ruleVersion: RULE_VERSION23,
|
|
8317
8790
|
severity: policy.severity,
|
|
8318
8791
|
message: `var(${tokenName}) is not in the contract token vocabulary. Use a token defined in your contract's token source files.`,
|
|
8319
8792
|
location: decl.location,
|
|
@@ -8360,8 +8833,8 @@ function skipToCloseParen(value, openParenIndex) {
|
|
|
8360
8833
|
}
|
|
8361
8834
|
|
|
8362
8835
|
// src/rules/tokens-upstream-drift.ts
|
|
8363
|
-
var
|
|
8364
|
-
var
|
|
8836
|
+
var RULE_ID23 = "tokens/upstream-drift";
|
|
8837
|
+
var RULE_VERSION24 = "1";
|
|
8365
8838
|
function ruleTokensUpstreamDrift(ix) {
|
|
8366
8839
|
const local = ix.tokens.list().filter((token) => token.role !== "upstream");
|
|
8367
8840
|
const upstream = ix.tokens.list().filter((token) => token.role === "upstream");
|
|
@@ -8387,8 +8860,8 @@ function ruleTokensUpstreamDrift(ix) {
|
|
|
8387
8860
|
};
|
|
8388
8861
|
findings.push(
|
|
8389
8862
|
makeFinding({
|
|
8390
|
-
ruleId:
|
|
8391
|
-
ruleVersion:
|
|
8863
|
+
ruleId: RULE_ID23,
|
|
8864
|
+
ruleVersion: RULE_VERSION24,
|
|
8392
8865
|
severity: "warn",
|
|
8393
8866
|
message: `Local token ${token.name} is ${token.value}, but upstream ${upstreamName} is ${expected.value}.`,
|
|
8394
8867
|
location,
|
|
@@ -8420,8 +8893,12 @@ var BLOCKING_RULE_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
|
8420
8893
|
// off-scale Tailwind spacing utility
|
|
8421
8894
|
"components/prefer-library",
|
|
8422
8895
|
// raw element bypasses a canonical component
|
|
8423
|
-
"components/
|
|
8896
|
+
"components/shadow-component",
|
|
8897
|
+
// confirmed local semantic control shadows a canonical
|
|
8898
|
+
"components/preferred-component",
|
|
8424
8899
|
// imported component bypasses its canonical
|
|
8900
|
+
"tokens/css-vars-must-be-defined"
|
|
8901
|
+
// off-contract custom property reference
|
|
8425
8902
|
]);
|
|
8426
8903
|
function gatesCi(finding, failOnWarnings) {
|
|
8427
8904
|
if (!canBlock(finding.evidenceGrade ?? "source_backed")) return false;
|
|
@@ -8432,9 +8909,13 @@ function gatesCi(finding, failOnWarnings) {
|
|
|
8432
8909
|
function isDenyEligible(finding, failOnWarnings) {
|
|
8433
8910
|
if (!BLOCKING_RULE_ALLOWLIST.has(finding.ruleId)) return false;
|
|
8434
8911
|
if (finding.attributes?.advisory === true) return false;
|
|
8912
|
+
if (finding.ruleId === "components/shadow-component" && (finding.attributes?.state !== "shadow" || finding.attributes?.confidence !== "confirmed" || typeof finding.attributes?.canonicalTarget !== "string")) {
|
|
8913
|
+
return false;
|
|
8914
|
+
}
|
|
8435
8915
|
if (!gatesCi(finding, failOnWarnings)) return false;
|
|
8436
8916
|
if (finding.fix?.deterministic === false) {
|
|
8437
|
-
|
|
8917
|
+
const exactCanonicalBypass = finding.ruleId === "components/prefer-library" && finding.attributes?.precisionTier === "exact-html" && typeof finding.attributes?.suggestedComponent === "string";
|
|
8918
|
+
if (!exactCanonicalBypass) {
|
|
8438
8919
|
return false;
|
|
8439
8920
|
}
|
|
8440
8921
|
}
|
|
@@ -8471,6 +8952,7 @@ var RULE_TIER = {
|
|
|
8471
8952
|
"tokens/require-dual-fallback": "hygiene",
|
|
8472
8953
|
"theme/no-theme-coupled-literal": "hygiene",
|
|
8473
8954
|
"a11y/required-accessible-name": "hygiene",
|
|
8955
|
+
"a11y/standard": "hygiene",
|
|
8474
8956
|
// Composition constraints are authored contracts, but not part of the
|
|
8475
8957
|
// two-choice headline contract; they self-activate when patterns are declared
|
|
8476
8958
|
// (the compiler injects their configs), so off-by-default here is harmless.
|
|
@@ -8632,9 +9114,14 @@ var RULES = [
|
|
|
8632
9114
|
},
|
|
8633
9115
|
{
|
|
8634
9116
|
id: "a11y/required-accessible-name",
|
|
8635
|
-
version:
|
|
9117
|
+
version: RULE_VERSION,
|
|
8636
9118
|
run: ruleA11yRequiredAccessibleName
|
|
8637
9119
|
},
|
|
9120
|
+
{
|
|
9121
|
+
id: "a11y/standard",
|
|
9122
|
+
version: "1",
|
|
9123
|
+
run: ruleA11yStandard
|
|
9124
|
+
},
|
|
8638
9125
|
{
|
|
8639
9126
|
id: "composition/cardinality",
|
|
8640
9127
|
version: "1",
|
|
@@ -8673,12 +9160,21 @@ function hasEffectiveComponentVocabulary(options) {
|
|
|
8673
9160
|
}
|
|
8674
9161
|
function effectiveRuleConfigs(policy) {
|
|
8675
9162
|
const configs = /* @__PURE__ */ new Map();
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
9163
|
+
const facts = compileGlobalGovernanceFacts(policy);
|
|
9164
|
+
for (const fact of facts) {
|
|
9165
|
+
if (fact.kind === "governance_rule_config") {
|
|
9166
|
+
configs.set(fact.ruleId, {
|
|
9167
|
+
enabled: fact.enabled,
|
|
9168
|
+
severity: fact.severity,
|
|
9169
|
+
options: fact.options
|
|
9170
|
+
});
|
|
9171
|
+
}
|
|
9172
|
+
}
|
|
9173
|
+
const cssVarsPolicy = facts.find((fact) => fact.kind === "style_css_vars_must_be_defined");
|
|
9174
|
+
if (cssVarsPolicy?.kind === "style_css_vars_must_be_defined") {
|
|
9175
|
+
configs.set("tokens/css-vars-must-be-defined", {
|
|
9176
|
+
enabled: true,
|
|
9177
|
+
severity: cssVarsPolicy.severity
|
|
8682
9178
|
});
|
|
8683
9179
|
}
|
|
8684
9180
|
return configs;
|
|
@@ -9082,23 +9578,28 @@ function evaluateGovernanceIntegrity(input) {
|
|
|
9082
9578
|
if (!policyArmed) {
|
|
9083
9579
|
policyFamily.reason = "no governance policy resolved";
|
|
9084
9580
|
}
|
|
9085
|
-
const
|
|
9581
|
+
const preferLibraryConfig = configs.get("components/prefer-library");
|
|
9582
|
+
const shadowComponentConfig = configs.get("components/shadow-component");
|
|
9086
9583
|
const confirmedBridges = input.policy?.canonicalBridges ?? [];
|
|
9087
|
-
const
|
|
9584
|
+
const componentRuleEnabled = preferLibraryConfig?.enabled === true || shadowComponentConfig?.enabled === true;
|
|
9585
|
+
const preferLibraryArmed = preferLibraryConfig?.enabled === true && hasEffectiveComponentVocabulary(preferLibraryConfig.options);
|
|
9586
|
+
const shadowComponentArmed = shadowComponentConfig?.enabled === true && hasEffectiveComponentVocabulary(shadowComponentConfig.options);
|
|
9587
|
+
const componentsArmed = confirmedBridges.length > 0 || preferLibraryArmed || shadowComponentArmed;
|
|
9088
9588
|
const componentsFamily = {
|
|
9089
9589
|
id: "components",
|
|
9090
9590
|
armed: componentsArmed,
|
|
9091
9591
|
rules: [
|
|
9092
9592
|
...confirmedBridges.length > 0 ? ["imports/preferred-path"] : [],
|
|
9093
|
-
...
|
|
9593
|
+
...preferLibraryConfig?.enabled === true ? ["components/prefer-library"] : [],
|
|
9594
|
+
...shadowComponentConfig?.enabled === true ? ["components/shadow-component"] : []
|
|
9094
9595
|
]
|
|
9095
9596
|
};
|
|
9096
9597
|
if (!componentsArmed) {
|
|
9097
|
-
if (
|
|
9098
|
-
componentsFamily.reason = "
|
|
9598
|
+
if (componentRuleEnabled) {
|
|
9599
|
+
componentsFamily.reason = "component governance enabled but no effective canonical source";
|
|
9099
9600
|
componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
|
|
9100
9601
|
} else {
|
|
9101
|
-
componentsFamily.reason = "
|
|
9602
|
+
componentsFamily.reason = "canonical-component rules not enabled";
|
|
9102
9603
|
componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
|
|
9103
9604
|
}
|
|
9104
9605
|
}
|
|
@@ -9141,7 +9642,7 @@ function evaluateGovernanceIntegrity(input) {
|
|
|
9141
9642
|
for (const ruleId of BLOCKING_RULE_ALLOWLIST) {
|
|
9142
9643
|
const config = configs.get(ruleId);
|
|
9143
9644
|
if (config?.enabled !== true) continue;
|
|
9144
|
-
const familyArmed = ruleId === "components/prefer-library" ? componentsArmed : hygieneArmed;
|
|
9645
|
+
const familyArmed = ruleId === "components/prefer-library" || ruleId === "components/shadow-component" ? componentsArmed : ruleId === "tokens/css-vars-must-be-defined" ? tokensArmed : hygieneArmed;
|
|
9145
9646
|
if (!familyArmed) continue;
|
|
9146
9647
|
if (config.severity !== "error" && !failOnWarnings) continue;
|
|
9147
9648
|
blockingRules.push(ruleId);
|
|
@@ -9772,6 +10273,7 @@ export {
|
|
|
9772
10273
|
CONTRACT_DOMAINS,
|
|
9773
10274
|
CONTRACT_PREIMAGE_CAPABILITY_HEADER,
|
|
9774
10275
|
CONTRACT_PREIMAGE_SCHEMA,
|
|
10276
|
+
CompiledFragmentsFileValidationError,
|
|
9775
10277
|
DEFAULTS,
|
|
9776
10278
|
DEFAULT_ENHANCED_STYLE_PROPERTIES,
|
|
9777
10279
|
DEFAULT_STYLE_PROPERTIES,
|
|
@@ -10010,6 +10512,7 @@ export {
|
|
|
10010
10512
|
makeTailwindUnknownClassEnabledFact,
|
|
10011
10513
|
makeThemeDeclarationFact,
|
|
10012
10514
|
makeTokenDefinitionFact,
|
|
10515
|
+
makeUsageChildContentFact,
|
|
10013
10516
|
makeUsageComponentFact,
|
|
10014
10517
|
makeUsageImportFact,
|
|
10015
10518
|
makeUsageInlineStyleFact,
|
|
@@ -10058,6 +10561,7 @@ export {
|
|
|
10058
10561
|
ownedImportEquivalents,
|
|
10059
10562
|
parseColor,
|
|
10060
10563
|
parseColorToRgb,
|
|
10564
|
+
parseCompiledFragmentsFile,
|
|
10061
10565
|
parseComponentContract,
|
|
10062
10566
|
parseContractStamp,
|
|
10063
10567
|
parseCssTokens,
|
|
@@ -10115,6 +10619,7 @@ export {
|
|
|
10115
10619
|
resolveTokenValue,
|
|
10116
10620
|
rgbToHex,
|
|
10117
10621
|
ruleA11yRequiredAccessibleName,
|
|
10622
|
+
ruleA11yStandard,
|
|
10118
10623
|
ruleComponentsForbiddenPropValue,
|
|
10119
10624
|
ruleComponentsPreferLibrary,
|
|
10120
10625
|
ruleComponentsShadowComponent,
|