@usefragments/core 1.6.0 → 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.
- 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 +165 -139
- package/dist/index.js +700 -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/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*(.+)$/);
|
|
@@ -4382,6 +4665,13 @@ function discoverComponents(moduleExports, options) {
|
|
|
4382
4665
|
return names.sort();
|
|
4383
4666
|
}
|
|
4384
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
|
+
|
|
4385
4675
|
// src/rules/finding.ts
|
|
4386
4676
|
function makeFinding(input) {
|
|
4387
4677
|
if (input.evidence.length === 0) {
|
|
@@ -4840,36 +5130,71 @@ function isApplicableTokenReference(referenceFormat) {
|
|
|
4840
5130
|
|
|
4841
5131
|
// src/rules/a11y-required-accessible-name.ts
|
|
4842
5132
|
var RULE_ID = "a11y/required-accessible-name";
|
|
4843
|
-
var RULE_VERSION = "
|
|
5133
|
+
var RULE_VERSION = "2";
|
|
4844
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
|
+
];
|
|
4845
5149
|
function ruleA11yRequiredAccessibleName(ix) {
|
|
4846
5150
|
const findings = [];
|
|
4847
5151
|
const propsByNode = indexPropsByNodeId(ix);
|
|
4848
5152
|
const textByNode = indexTextChildrenByNodeId(ix);
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
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;
|
|
4854
5164
|
const props = propsByNode.get(node.id) ?? [];
|
|
4855
|
-
if (props
|
|
4856
|
-
|
|
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
|
+
}
|
|
4857
5177
|
findings.push(
|
|
4858
5178
|
makeFinding({
|
|
4859
5179
|
ruleId: RULE_ID,
|
|
4860
5180
|
ruleVersion: RULE_VERSION,
|
|
4861
|
-
severity:
|
|
4862
|
-
message: `<${node.element}> has no accessible name. ${
|
|
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(),
|
|
4863
5183
|
location: node.location,
|
|
4864
|
-
evidence: ix.evidence([
|
|
5184
|
+
evidence: ix.evidence([
|
|
5185
|
+
node.id,
|
|
5186
|
+
...usageComponent ? [usageComponent.id] : [],
|
|
5187
|
+
...componentPolicy ? [componentPolicy.id] : [],
|
|
5188
|
+
...genericControl && globalPolicy ? [globalPolicy.id] : []
|
|
5189
|
+
]),
|
|
4865
5190
|
fingerprintIdentity: {
|
|
4866
5191
|
file: node.file,
|
|
4867
|
-
componentId: usageComponent
|
|
5192
|
+
componentId: usageComponent?.componentId,
|
|
4868
5193
|
element: node.element,
|
|
4869
5194
|
nodePath: node.nodePath
|
|
4870
5195
|
},
|
|
4871
5196
|
attributes: {
|
|
4872
|
-
componentId: usageComponent.componentId,
|
|
5197
|
+
...usageComponent ? { componentId: usageComponent.componentId } : {},
|
|
4873
5198
|
element: node.element
|
|
4874
5199
|
}
|
|
4875
5200
|
})
|
|
@@ -4877,12 +5202,18 @@ function ruleA11yRequiredAccessibleName(ix) {
|
|
|
4877
5202
|
}
|
|
4878
5203
|
return findings;
|
|
4879
5204
|
}
|
|
4880
|
-
function hasAccessibleName(props, textChildren) {
|
|
4881
|
-
|
|
4882
|
-
|
|
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
|
+
}
|
|
4883
5214
|
}
|
|
4884
5215
|
for (const prop of props) {
|
|
4885
|
-
if (!
|
|
5216
|
+
if (!sourceProps.has(prop.prop)) continue;
|
|
4886
5217
|
if (prop.resolution === "spread") continue;
|
|
4887
5218
|
if (prop.resolution === "dynamic") return true;
|
|
4888
5219
|
if (prop.resolution === "static") {
|
|
@@ -4892,10 +5223,137 @@ function hasAccessibleName(props, textChildren) {
|
|
|
4892
5223
|
}
|
|
4893
5224
|
return false;
|
|
4894
5225
|
}
|
|
4895
|
-
function
|
|
4896
|
-
|
|
4897
|
-
if (
|
|
4898
|
-
|
|
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
|
+
);
|
|
4899
5357
|
}
|
|
4900
5358
|
|
|
4901
5359
|
// src/raw-html-canonical.ts
|
|
@@ -5080,8 +5538,8 @@ function decisionTargetsExport(target, exportName) {
|
|
|
5080
5538
|
}
|
|
5081
5539
|
|
|
5082
5540
|
// src/rules/components-prefer-library.ts
|
|
5083
|
-
var
|
|
5084
|
-
var
|
|
5541
|
+
var RULE_ID3 = "components/prefer-library";
|
|
5542
|
+
var RULE_VERSION3 = "1";
|
|
5085
5543
|
function isExplicitTagDeclaration(mapping) {
|
|
5086
5544
|
return mapping.declaredHtmlEquivalent === true;
|
|
5087
5545
|
}
|
|
@@ -5220,7 +5678,7 @@ function findClassNameReimpl(node, classTokensByNode, targets) {
|
|
|
5220
5678
|
return null;
|
|
5221
5679
|
}
|
|
5222
5680
|
function ruleComponentsPreferLibrary(ix) {
|
|
5223
|
-
const policy = ix.policy.ruleConfig(
|
|
5681
|
+
const policy = ix.policy.ruleConfig(RULE_ID3);
|
|
5224
5682
|
if (!policy?.enabled) return [];
|
|
5225
5683
|
const configuredSources = canonicalSourcesFromPolicy(policy.options?.canonicalSources);
|
|
5226
5684
|
const conflicts = projectCanonicalDirectionConflicts(ix, configuredSources);
|
|
@@ -5235,7 +5693,7 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5235
5693
|
const classTokensByNode = indexClassTokensByNode(ix);
|
|
5236
5694
|
const reimplTargets = canonicalReimplTargets(sources, mappings);
|
|
5237
5695
|
const canonicalDirectoryResolvedNodes = indexCanonicalDirectoryResolvedNodes(ix, sources);
|
|
5238
|
-
const shadowRenderRootNodeIds = indexShadowRenderRootNodeIds(ix);
|
|
5696
|
+
const shadowRenderRootNodeIds = ix.policy.ruleConfig("components/shadow-component")?.enabled === true ? indexShadowRenderRootNodeIds(ix) : /* @__PURE__ */ new Set();
|
|
5239
5697
|
const findings = [];
|
|
5240
5698
|
const seenImportFixes = /* @__PURE__ */ new Set();
|
|
5241
5699
|
for (const node of ix.byKind("usage_node")) {
|
|
@@ -5267,8 +5725,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5267
5725
|
seenImportFixes.add(key);
|
|
5268
5726
|
findings.push(
|
|
5269
5727
|
makeFinding({
|
|
5270
|
-
ruleId:
|
|
5271
|
-
ruleVersion:
|
|
5728
|
+
ruleId: RULE_ID3,
|
|
5729
|
+
ruleVersion: RULE_VERSION3,
|
|
5272
5730
|
severity: capAdvisorySeverity(policy.severity),
|
|
5273
5731
|
message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
|
|
5274
5732
|
location: imported.location,
|
|
@@ -5302,8 +5760,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5302
5760
|
if (node.element === suggestedComponent2) continue;
|
|
5303
5761
|
findings.push(
|
|
5304
5762
|
makeFinding({
|
|
5305
|
-
ruleId:
|
|
5306
|
-
ruleVersion:
|
|
5763
|
+
ruleId: RULE_ID3,
|
|
5764
|
+
ruleVersion: RULE_VERSION3,
|
|
5307
5765
|
severity: severityForTier(policy.severity, precisionTier2),
|
|
5308
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}.`,
|
|
5309
5767
|
location: node.location,
|
|
@@ -5354,8 +5812,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5354
5812
|
const suggestedImport2 = reimpl.import ?? "the canonical library";
|
|
5355
5813
|
findings.push(
|
|
5356
5814
|
makeFinding({
|
|
5357
|
-
ruleId:
|
|
5358
|
-
ruleVersion:
|
|
5815
|
+
ruleId: RULE_ID3,
|
|
5816
|
+
ruleVersion: RULE_VERSION3,
|
|
5359
5817
|
severity: capAdvisorySeverity(policy.severity),
|
|
5360
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}>.`,
|
|
5361
5819
|
location: node.location,
|
|
@@ -5395,8 +5853,8 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5395
5853
|
seenImportFixes.add(key);
|
|
5396
5854
|
findings.push(
|
|
5397
5855
|
makeFinding({
|
|
5398
|
-
ruleId:
|
|
5399
|
-
ruleVersion:
|
|
5856
|
+
ruleId: RULE_ID3,
|
|
5857
|
+
ruleVersion: RULE_VERSION3,
|
|
5400
5858
|
severity: capAdvisorySeverity(policy.severity),
|
|
5401
5859
|
message: `<${node.element}> may need to import from ${importPath}; verify that the local component contract is compatible first.`,
|
|
5402
5860
|
location: imported.location,
|
|
@@ -5438,14 +5896,15 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5438
5896
|
continue;
|
|
5439
5897
|
}
|
|
5440
5898
|
}
|
|
5441
|
-
const
|
|
5899
|
+
const importReady = suggestion.kind !== "directory";
|
|
5900
|
+
const suggestedImport = importReady ? sourceLabel(suggestion) : "the canonical component directory (resolve its configured import alias)";
|
|
5442
5901
|
const precisionTier = builtIn?.tier ?? "exact-html";
|
|
5443
5902
|
const rawElement = formatRawElement(node, props);
|
|
5444
5903
|
findings.push(
|
|
5445
5904
|
makeFinding({
|
|
5446
|
-
ruleId:
|
|
5447
|
-
ruleVersion:
|
|
5448
|
-
severity: severityForTier(policy.severity, precisionTier),
|
|
5905
|
+
ruleId: RULE_ID3,
|
|
5906
|
+
ruleVersion: RULE_VERSION3,
|
|
5907
|
+
severity: importReady ? severityForTier(policy.severity, precisionTier) : capAdvisorySeverity(policy.severity),
|
|
5449
5908
|
message: messageForBuiltInMatch({
|
|
5450
5909
|
node,
|
|
5451
5910
|
rawElement,
|
|
@@ -5462,7 +5921,7 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5462
5921
|
suggestedComponent,
|
|
5463
5922
|
suggestedImport
|
|
5464
5923
|
},
|
|
5465
|
-
|
|
5924
|
+
...importReady && !isRawHtmlAdvisoryTier(precisionTier) && isIdentifier(node.element) && isIdentifier(suggestedComponent) ? {
|
|
5466
5925
|
fix: {
|
|
5467
5926
|
kind: "replaceComponent",
|
|
5468
5927
|
title: `Replace ${rawElement} with <${suggestedComponent}>`,
|
|
@@ -5477,13 +5936,13 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5477
5936
|
attributes: {
|
|
5478
5937
|
rawValue: node.element,
|
|
5479
5938
|
suggestedComponent,
|
|
5480
|
-
suggestedImport,
|
|
5481
5939
|
suggestedImportSourceKind: suggestion.kind,
|
|
5940
|
+
...importReady ? { suggestedImport } : { canonicalDirectory: suggestion.path, importReady: false },
|
|
5482
5941
|
precisionTier,
|
|
5483
5942
|
propCompatibility: "unknown",
|
|
5484
5943
|
...builtIn?.matchedRole ? { matchedRole: builtIn.matchedRole } : {},
|
|
5485
5944
|
...builtIn?.inputType ? { matchedInputType: builtIn.inputType } : {},
|
|
5486
|
-
...isRawHtmlAdvisoryTier(precisionTier) ? { advisory: true } : {}
|
|
5945
|
+
...isRawHtmlAdvisoryTier(precisionTier) || !importReady ? { advisory: true } : {}
|
|
5487
5946
|
}
|
|
5488
5947
|
})
|
|
5489
5948
|
);
|
|
@@ -5503,11 +5962,19 @@ function suppressConflictedExports(source, conflicts) {
|
|
|
5503
5962
|
function indexShadowRenderRootNodeIds(ix) {
|
|
5504
5963
|
const identities = ix.byKind("component_identity");
|
|
5505
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
|
+
);
|
|
5506
5968
|
const nodeIds = /* @__PURE__ */ new Set();
|
|
5507
5969
|
for (const identity of identities) {
|
|
5508
5970
|
if (identity.state !== "shadow") continue;
|
|
5971
|
+
const definition = definitions.get(identity.componentKey);
|
|
5972
|
+
if (!definition) continue;
|
|
5509
5973
|
for (const evidenceId of identity.evidence) {
|
|
5510
|
-
|
|
5974
|
+
const evidence = ix.get(evidenceId);
|
|
5975
|
+
if (evidence?.kind === "usage_node" && evidence.file === definition.file) {
|
|
5976
|
+
nodeIds.add(evidenceId);
|
|
5977
|
+
}
|
|
5511
5978
|
}
|
|
5512
5979
|
}
|
|
5513
5980
|
return nodeIds;
|
|
@@ -5790,7 +6257,7 @@ function sourceLabel(source) {
|
|
|
5790
6257
|
function canonicalImportPath(source) {
|
|
5791
6258
|
if (source.kind === "npm") return source.specifier;
|
|
5792
6259
|
if (source.kind === "registry") return source.importPath;
|
|
5793
|
-
return
|
|
6260
|
+
return void 0;
|
|
5794
6261
|
}
|
|
5795
6262
|
function indexCanonicalDirectoryResolvedNodes(ix, sources) {
|
|
5796
6263
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -5866,10 +6333,10 @@ function indexComponentIdentityUsage(ix) {
|
|
|
5866
6333
|
}
|
|
5867
6334
|
|
|
5868
6335
|
// src/rules/components-shadow-component.ts
|
|
5869
|
-
var
|
|
5870
|
-
var
|
|
6336
|
+
var RULE_ID4 = "components/shadow-component";
|
|
6337
|
+
var RULE_VERSION4 = "1";
|
|
5871
6338
|
function ruleComponentsShadowComponent(ix) {
|
|
5872
|
-
const policy = ix.policy.ruleConfig(
|
|
6339
|
+
const policy = ix.policy.ruleConfig(RULE_ID4);
|
|
5873
6340
|
if (!policy?.enabled) return [];
|
|
5874
6341
|
const definitions = new Map(
|
|
5875
6342
|
ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
|
|
@@ -5886,8 +6353,8 @@ function ruleComponentsShadowComponent(ix) {
|
|
|
5886
6353
|
const blastRadius = usage?.blastRadius ?? 0;
|
|
5887
6354
|
findings.push(
|
|
5888
6355
|
makeFinding({
|
|
5889
|
-
ruleId:
|
|
5890
|
-
ruleVersion:
|
|
6356
|
+
ruleId: RULE_ID4,
|
|
6357
|
+
ruleVersion: RULE_VERSION4,
|
|
5891
6358
|
severity: reviewVariant ? "info" : policy.severity ?? "warn",
|
|
5892
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"}.`,
|
|
5893
6360
|
location: definitionLocation(ix, definition, identity),
|
|
@@ -5925,8 +6392,8 @@ function definitionLocation(ix, definition, identity) {
|
|
|
5925
6392
|
}
|
|
5926
6393
|
|
|
5927
6394
|
// src/rules/components-forbidden-prop-value.ts
|
|
5928
|
-
var
|
|
5929
|
-
var
|
|
6395
|
+
var RULE_ID5 = "components/forbidden-prop-value";
|
|
6396
|
+
var RULE_VERSION5 = "1";
|
|
5930
6397
|
function ruleComponentsForbiddenPropValue(ix) {
|
|
5931
6398
|
const findings = [];
|
|
5932
6399
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -5956,8 +6423,8 @@ function ruleComponentsForbiddenPropValue(ix) {
|
|
|
5956
6423
|
} : void 0;
|
|
5957
6424
|
findings.push(
|
|
5958
6425
|
makeFinding({
|
|
5959
|
-
ruleId:
|
|
5960
|
-
ruleVersion:
|
|
6426
|
+
ruleId: RULE_ID5,
|
|
6427
|
+
ruleVersion: RULE_VERSION5,
|
|
5961
6428
|
severity: policy.severity,
|
|
5962
6429
|
message: buildMessage(
|
|
5963
6430
|
node.element,
|
|
@@ -6001,8 +6468,8 @@ function buildMessage(element, prop, value, because, replacement) {
|
|
|
6001
6468
|
}
|
|
6002
6469
|
|
|
6003
6470
|
// src/rules/components-unknown-prop.ts
|
|
6004
|
-
var
|
|
6005
|
-
var
|
|
6471
|
+
var RULE_ID6 = "components/unknown-prop";
|
|
6472
|
+
var RULE_VERSION6 = "1";
|
|
6006
6473
|
function ruleComponentsUnknownProp(ix) {
|
|
6007
6474
|
const policy = ix.policy.jsxUnknownPropsForbidden();
|
|
6008
6475
|
if (!policy) return [];
|
|
@@ -6023,8 +6490,8 @@ function ruleComponentsUnknownProp(ix) {
|
|
|
6023
6490
|
if (knownNames.has(propUsage.prop)) continue;
|
|
6024
6491
|
findings.push(
|
|
6025
6492
|
makeFinding({
|
|
6026
|
-
ruleId:
|
|
6027
|
-
ruleVersion:
|
|
6493
|
+
ruleId: RULE_ID6,
|
|
6494
|
+
ruleVersion: RULE_VERSION6,
|
|
6028
6495
|
severity: policy.severity,
|
|
6029
6496
|
message: `Unknown prop "${propUsage.prop}" on <${node.element}>. Allowed props: ${formatPropList(knownNames)}.`,
|
|
6030
6497
|
location: node.location,
|
|
@@ -6056,7 +6523,7 @@ function formatPropList(names) {
|
|
|
6056
6523
|
// src/rules/composition-pattern.ts
|
|
6057
6524
|
var CARDINALITY_RULE_ID = "composition/cardinality";
|
|
6058
6525
|
var CO_OCCURRENCE_RULE_ID = "composition/co-occurrence";
|
|
6059
|
-
var
|
|
6526
|
+
var RULE_VERSION7 = "1";
|
|
6060
6527
|
function ruleCompositionCardinality(ix) {
|
|
6061
6528
|
return evaluate(ix, CARDINALITY_RULE_ID, "cardinality");
|
|
6062
6529
|
}
|
|
@@ -6124,7 +6591,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
6124
6591
|
const extras = selected.slice(constraint.max);
|
|
6125
6592
|
return makeFinding({
|
|
6126
6593
|
ruleId: ctx.ruleId,
|
|
6127
|
-
ruleVersion:
|
|
6594
|
+
ruleVersion: RULE_VERSION7,
|
|
6128
6595
|
severity: ctx.severity,
|
|
6129
6596
|
message: cardinalityMaxMessage(pattern, constraint.max, count),
|
|
6130
6597
|
location: extras[0].location,
|
|
@@ -6135,7 +6602,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
6135
6602
|
if (constraint.min !== void 0 && count < constraint.min) {
|
|
6136
6603
|
return makeFinding({
|
|
6137
6604
|
ruleId: ctx.ruleId,
|
|
6138
|
-
ruleVersion:
|
|
6605
|
+
ruleVersion: RULE_VERSION7,
|
|
6139
6606
|
severity: ctx.severity,
|
|
6140
6607
|
message: cardinalityMinMessage(pattern, constraint.min, count),
|
|
6141
6608
|
location: region.container.location,
|
|
@@ -6156,7 +6623,7 @@ function checkCoOccurrence(ctx, region, pattern, constraint, selected) {
|
|
|
6156
6623
|
if (satisfied) return null;
|
|
6157
6624
|
return makeFinding({
|
|
6158
6625
|
ruleId: ctx.ruleId,
|
|
6159
|
-
ruleVersion:
|
|
6626
|
+
ruleVersion: RULE_VERSION7,
|
|
6160
6627
|
severity: ctx.severity,
|
|
6161
6628
|
message: coOccurrenceMessage(pattern, constraint),
|
|
6162
6629
|
location: region.container.location,
|
|
@@ -6208,8 +6675,8 @@ function coOccurrenceMessage(pattern, constraint) {
|
|
|
6208
6675
|
}
|
|
6209
6676
|
|
|
6210
6677
|
// src/rules/jsx-preferred-component.ts
|
|
6211
|
-
var
|
|
6212
|
-
var
|
|
6678
|
+
var RULE_ID7 = "components/preferred-component";
|
|
6679
|
+
var RULE_VERSION8 = "1";
|
|
6213
6680
|
function ruleJsxPreferredComponent(ix) {
|
|
6214
6681
|
const policies = ix.policy.jsxComponentPreferred();
|
|
6215
6682
|
if (policies.length === 0) return [];
|
|
@@ -6225,8 +6692,8 @@ function ruleJsxPreferredComponent(ix) {
|
|
|
6225
6692
|
if (!replacement) continue;
|
|
6226
6693
|
findings.push(
|
|
6227
6694
|
makeFinding({
|
|
6228
|
-
ruleId:
|
|
6229
|
-
ruleVersion:
|
|
6695
|
+
ruleId: RULE_ID7,
|
|
6696
|
+
ruleVersion: RULE_VERSION8,
|
|
6230
6697
|
severity: policy.severity,
|
|
6231
6698
|
message: `<${node.element}> should use <${replacement}>.`,
|
|
6232
6699
|
location: node.location,
|
|
@@ -6263,8 +6730,8 @@ function componentNameFromId(componentId2) {
|
|
|
6263
6730
|
}
|
|
6264
6731
|
|
|
6265
6732
|
// src/rules/jsx-preferred-import-path.ts
|
|
6266
|
-
var
|
|
6267
|
-
var
|
|
6733
|
+
var RULE_ID8 = "imports/preferred-path";
|
|
6734
|
+
var RULE_VERSION9 = "1";
|
|
6268
6735
|
function ruleJsxPreferredImportPath(ix) {
|
|
6269
6736
|
const policies = ix.policy.jsxImportPathPreferred();
|
|
6270
6737
|
if (policies.length === 0) return [];
|
|
@@ -6292,8 +6759,8 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6292
6759
|
seen.add(key);
|
|
6293
6760
|
findings.push(
|
|
6294
6761
|
makeFinding({
|
|
6295
|
-
ruleId:
|
|
6296
|
-
ruleVersion:
|
|
6762
|
+
ruleId: RULE_ID8,
|
|
6763
|
+
ruleVersion: RULE_VERSION9,
|
|
6297
6764
|
severity: policy.severity,
|
|
6298
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}".`,
|
|
6299
6766
|
location: usage.location,
|
|
@@ -6352,10 +6819,10 @@ function normalizeRepoPath(path) {
|
|
|
6352
6819
|
}
|
|
6353
6820
|
|
|
6354
6821
|
// src/rules/props-invalid-value.ts
|
|
6355
|
-
var
|
|
6356
|
-
var
|
|
6822
|
+
var RULE_ID9 = "props/invalid-value";
|
|
6823
|
+
var RULE_VERSION10 = "1";
|
|
6357
6824
|
function rulePropsInvalidValue(ix) {
|
|
6358
|
-
const policy = ix.policy.ruleConfig(
|
|
6825
|
+
const policy = ix.policy.ruleConfig(RULE_ID9);
|
|
6359
6826
|
if (!policy?.enabled) return [];
|
|
6360
6827
|
const findings = [];
|
|
6361
6828
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -6384,8 +6851,8 @@ function rulePropsInvalidValue(ix) {
|
|
|
6384
6851
|
} : void 0;
|
|
6385
6852
|
findings.push(
|
|
6386
6853
|
makeFinding({
|
|
6387
|
-
ruleId:
|
|
6388
|
-
ruleVersion:
|
|
6854
|
+
ruleId: RULE_ID9,
|
|
6855
|
+
ruleVersion: RULE_VERSION10,
|
|
6389
6856
|
severity: policy.severity ?? "warn",
|
|
6390
6857
|
message: buildMessage2(node.element, propUsage.prop, propUsage.value, allowedValues),
|
|
6391
6858
|
location: propUsage.location ?? node.location,
|
|
@@ -6584,8 +7051,8 @@ function localTokenCandidates(ix, category) {
|
|
|
6584
7051
|
}
|
|
6585
7052
|
|
|
6586
7053
|
// src/rules/styles-no-raw-color.ts
|
|
6587
|
-
var
|
|
6588
|
-
var
|
|
7054
|
+
var RULE_ID10 = "styles/no-raw-color";
|
|
7055
|
+
var RULE_VERSION11 = "1";
|
|
6589
7056
|
function ruleStylesNoRawColor(ix) {
|
|
6590
7057
|
const policy = ix.policy.rawColorPolicy();
|
|
6591
7058
|
if (!policy) return [];
|
|
@@ -6604,8 +7071,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6604
7071
|
const evidenceIds = resolution ? [decl.id, policy.id, resolution.token.id] : [decl.id, policy.id];
|
|
6605
7072
|
findings.push(
|
|
6606
7073
|
makeFinding({
|
|
6607
|
-
ruleId:
|
|
6608
|
-
ruleVersion:
|
|
7074
|
+
ruleId: RULE_ID10,
|
|
7075
|
+
ruleVersion: RULE_VERSION11,
|
|
6609
7076
|
severity: policy.severity,
|
|
6610
7077
|
message: colorMessage(color, `\`${decl.property}\``, resolution, preferLabel) + (hopped?.suffix ?? ""),
|
|
6611
7078
|
location: decl.location,
|
|
@@ -6641,8 +7108,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6641
7108
|
const evidenceIds2 = componentEvidenceId2 ? [node2.id, componentEvidenceId2, inline.id, policy.id] : [node2.id, inline.id, policy.id];
|
|
6642
7109
|
findings.push(
|
|
6643
7110
|
makeFinding({
|
|
6644
|
-
ruleId:
|
|
6645
|
-
ruleVersion:
|
|
7111
|
+
ruleId: RULE_ID10,
|
|
7112
|
+
ruleVersion: RULE_VERSION11,
|
|
6646
7113
|
severity: advisorySeverity,
|
|
6647
7114
|
message: `Runtime-constructed raw color \`${inline.value}\` on inline \`${inline.property}\`. Use a ${preferLabel} instead.`,
|
|
6648
7115
|
location: node2.location,
|
|
@@ -6679,8 +7146,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6679
7146
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6680
7147
|
findings.push(
|
|
6681
7148
|
makeFinding({
|
|
6682
|
-
ruleId:
|
|
6683
|
-
ruleVersion:
|
|
7149
|
+
ruleId: RULE_ID10,
|
|
7150
|
+
ruleVersion: RULE_VERSION11,
|
|
6684
7151
|
severity: policy.severity,
|
|
6685
7152
|
message: colorMessage(color, `inline \`${inline.property}\``, resolution, preferLabel) + (hopped?.suffix ?? ""),
|
|
6686
7153
|
location: node.location,
|
|
@@ -6724,8 +7191,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6724
7191
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6725
7192
|
findings.push(
|
|
6726
7193
|
makeFinding({
|
|
6727
|
-
ruleId:
|
|
6728
|
-
ruleVersion:
|
|
7194
|
+
ruleId: RULE_ID10,
|
|
7195
|
+
ruleVersion: RULE_VERSION11,
|
|
6729
7196
|
severity: advisorySeverity,
|
|
6730
7197
|
message: colorMessage(color, `\`${prop.prop}\``, resolution, preferLabel),
|
|
6731
7198
|
location: prop.location ?? node.location,
|
|
@@ -6876,8 +7343,8 @@ function escapeRegExp(value) {
|
|
|
6876
7343
|
}
|
|
6877
7344
|
|
|
6878
7345
|
// src/rules/styles-no-raw-dimensions.ts
|
|
6879
|
-
var
|
|
6880
|
-
var
|
|
7346
|
+
var RULE_ID11 = "styles/no-raw-dimensions";
|
|
7347
|
+
var RULE_VERSION12 = "1";
|
|
6881
7348
|
function ruleStylesNoRawDimensions(ix) {
|
|
6882
7349
|
const policy = ix.policy.rawDimensionPolicy();
|
|
6883
7350
|
if (!policy) return [];
|
|
@@ -6898,8 +7365,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6898
7365
|
const fixEmission = hopped ? void 0 : dimensionFix(decl.property, resolution, ix);
|
|
6899
7366
|
findings.push(
|
|
6900
7367
|
makeFinding({
|
|
6901
|
-
ruleId:
|
|
6902
|
-
ruleVersion:
|
|
7368
|
+
ruleId: RULE_ID11,
|
|
7369
|
+
ruleVersion: RULE_VERSION12,
|
|
6903
7370
|
severity: policy.severity,
|
|
6904
7371
|
message: dimensionMessage(decl.value, decl.property, preferLabel, resolution, false) + (hopped?.suffix ?? ""),
|
|
6905
7372
|
location: decl.location,
|
|
@@ -6942,8 +7409,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6942
7409
|
const fixEmission = hopped ? void 0 : dimensionFix(inline.property, resolution, ix);
|
|
6943
7410
|
findings.push(
|
|
6944
7411
|
makeFinding({
|
|
6945
|
-
ruleId:
|
|
6946
|
-
ruleVersion:
|
|
7412
|
+
ruleId: RULE_ID11,
|
|
7413
|
+
ruleVersion: RULE_VERSION12,
|
|
6947
7414
|
severity: policy.severity,
|
|
6948
7415
|
message: dimensionMessage(inline.value, inline.property, preferLabel, resolution, true) + (hopped?.suffix ?? ""),
|
|
6949
7416
|
location: node.location,
|
|
@@ -7212,8 +7679,8 @@ function parseLengthParts(raw) {
|
|
|
7212
7679
|
}
|
|
7213
7680
|
|
|
7214
7681
|
// src/rules/styles-no-raw-spacing.ts
|
|
7215
|
-
var
|
|
7216
|
-
var
|
|
7682
|
+
var RULE_ID12 = "styles/no-raw-spacing";
|
|
7683
|
+
var RULE_VERSION13 = "1";
|
|
7217
7684
|
function ruleStylesNoRawSpacing(ix) {
|
|
7218
7685
|
const findings = [];
|
|
7219
7686
|
const lookupCache = /* @__PURE__ */ new Map();
|
|
@@ -7256,8 +7723,8 @@ function checkDeclaration(ix, decl, lookupFor) {
|
|
|
7256
7723
|
const hopped = hoppedValue(decl);
|
|
7257
7724
|
const fixEmission = hopped ? void 0 : buildSpacingFix(decl.property, checked, ix);
|
|
7258
7725
|
return makeFinding({
|
|
7259
|
-
ruleId:
|
|
7260
|
-
ruleVersion:
|
|
7726
|
+
ruleId: RULE_ID12,
|
|
7727
|
+
ruleVersion: RULE_VERSION13,
|
|
7261
7728
|
severity: policy.severity,
|
|
7262
7729
|
message: spacingMessage(decl.property, decl.value, allowed, scale, checked) + (hopped?.suffix ?? ""),
|
|
7263
7730
|
location: decl.location,
|
|
@@ -7313,8 +7780,8 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
|
|
|
7313
7780
|
const componentEvidenceId = componentByNode.get(node.id)?.id;
|
|
7314
7781
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
7315
7782
|
return makeFinding({
|
|
7316
|
-
ruleId:
|
|
7317
|
-
ruleVersion:
|
|
7783
|
+
ruleId: RULE_ID12,
|
|
7784
|
+
ruleVersion: RULE_VERSION13,
|
|
7318
7785
|
severity: policy.severity,
|
|
7319
7786
|
message: spacingMessage(inline.property, inline.value, allowed, scale, checked) + (hopped?.suffix ?? ""),
|
|
7320
7787
|
location: node.location,
|
|
@@ -7376,8 +7843,8 @@ function spacingMessage(property, value, allowed, scale, checked) {
|
|
|
7376
7843
|
}
|
|
7377
7844
|
|
|
7378
7845
|
// src/rules/styles-no-raw-typography.ts
|
|
7379
|
-
var
|
|
7380
|
-
var
|
|
7846
|
+
var RULE_ID13 = "styles/no-raw-typography";
|
|
7847
|
+
var RULE_VERSION14 = "1";
|
|
7381
7848
|
var FONT_SIZE_PROPERTIES = /* @__PURE__ */ new Set(["font-size", "fontsize"]);
|
|
7382
7849
|
function ruleStylesNoRawTypography(ix) {
|
|
7383
7850
|
const policy = ix.policy.fontSizeScale();
|
|
@@ -7396,8 +7863,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7396
7863
|
const fixEmission = hopped ? void 0 : buildFix(decl.property, checked, ix);
|
|
7397
7864
|
findings.push(
|
|
7398
7865
|
makeFinding({
|
|
7399
|
-
ruleId:
|
|
7400
|
-
ruleVersion:
|
|
7866
|
+
ruleId: RULE_ID13,
|
|
7867
|
+
ruleVersion: RULE_VERSION14,
|
|
7401
7868
|
severity: policy.severity,
|
|
7402
7869
|
message: typographyMessage(decl.property, decl.value, allowed, scale.unit, checked) + (hopped?.suffix ?? ""),
|
|
7403
7870
|
location: decl.location,
|
|
@@ -7445,8 +7912,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7445
7912
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
7446
7913
|
findings.push(
|
|
7447
7914
|
makeFinding({
|
|
7448
|
-
ruleId:
|
|
7449
|
-
ruleVersion:
|
|
7915
|
+
ruleId: RULE_ID13,
|
|
7916
|
+
ruleVersion: RULE_VERSION14,
|
|
7450
7917
|
severity: policy.severity,
|
|
7451
7918
|
message: typographyMessage(inline.property, inline.value, allowed, scale.unit, checked) + (hopped?.suffix ?? ""),
|
|
7452
7919
|
location: node.location,
|
|
@@ -7549,15 +8016,15 @@ function typographyMessage(property, value, allowed, unit, checked) {
|
|
|
7549
8016
|
}
|
|
7550
8017
|
|
|
7551
8018
|
// src/rules/theme-no-theme-coupled-literal.ts
|
|
7552
|
-
var
|
|
7553
|
-
var
|
|
8019
|
+
var RULE_ID14 = "theme/no-theme-coupled-literal";
|
|
8020
|
+
var RULE_VERSION15 = "1";
|
|
7554
8021
|
var COLOR_LITERAL_GLOBAL = /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?|oklab|oklch|lab|lch)\(\s*[\d.%\s,\/]+\s*\)/g;
|
|
7555
8022
|
var WHITE_RGBA = /^rgba?\(\s*255\s*,\s*255\s*,\s*255/i;
|
|
7556
8023
|
var BLACK_RGBA = /^rgba?\(\s*0\s*,\s*0\s*,\s*0/i;
|
|
7557
8024
|
var BOX_SHADOW_PROP = /^(?:-webkit-|-moz-)?box-shadow$/i;
|
|
7558
8025
|
var NEUTRAL_SHADOW = /^(?:none|inherit|initial|unset|revert|revert-layer)\s*$/i;
|
|
7559
8026
|
function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
7560
|
-
const policy = ix.policy.ruleConfig(
|
|
8027
|
+
const policy = ix.policy.ruleConfig(RULE_ID14);
|
|
7561
8028
|
if (!policy?.enabled) return [];
|
|
7562
8029
|
const findings = [];
|
|
7563
8030
|
for (const decl of ix.byKind("style_declaration")) {
|
|
@@ -7566,8 +8033,8 @@ function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
|
7566
8033
|
if (!hit) continue;
|
|
7567
8034
|
findings.push(
|
|
7568
8035
|
makeFinding({
|
|
7569
|
-
ruleId:
|
|
7570
|
-
ruleVersion:
|
|
8036
|
+
ruleId: RULE_ID14,
|
|
8037
|
+
ruleVersion: RULE_VERSION15,
|
|
7571
8038
|
severity: policy.severity ?? "warn",
|
|
7572
8039
|
message: hit.message,
|
|
7573
8040
|
location: decl.location,
|
|
@@ -7657,8 +8124,8 @@ function isTokenFile(filePath) {
|
|
|
7657
8124
|
}
|
|
7658
8125
|
|
|
7659
8126
|
// src/rules/tailwind-arbitrary-color.ts
|
|
7660
|
-
var
|
|
7661
|
-
var
|
|
8127
|
+
var RULE_ID15 = "tailwind/arbitrary-color";
|
|
8128
|
+
var RULE_VERSION16 = "1";
|
|
7662
8129
|
var COLOR_UTILITIES = /* @__PURE__ */ new Set([
|
|
7663
8130
|
"accent",
|
|
7664
8131
|
"bg",
|
|
@@ -7700,8 +8167,8 @@ function ruleTailwindArbitraryColor(ix) {
|
|
|
7700
8167
|
const evidenceIds = node ? [node.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
7701
8168
|
findings.push(
|
|
7702
8169
|
makeFinding({
|
|
7703
|
-
ruleId:
|
|
7704
|
-
ruleVersion:
|
|
8170
|
+
ruleId: RULE_ID15,
|
|
8171
|
+
ruleVersion: RULE_VERSION16,
|
|
7705
8172
|
severity: policy.severity,
|
|
7706
8173
|
message: `\`${klass.raw}\` uses arbitrary raw color ${color}. Use a ${policy.prefer === "token" ? "design token" : "CSS variable"} instead.`,
|
|
7707
8174
|
location: klass.location,
|
|
@@ -7732,8 +8199,8 @@ function fingerprintIdentity(klass, color) {
|
|
|
7732
8199
|
}
|
|
7733
8200
|
|
|
7734
8201
|
// src/rules/tailwind-arbitrary-spacing.ts
|
|
7735
|
-
var
|
|
7736
|
-
var
|
|
8202
|
+
var RULE_ID16 = "tailwind/arbitrary-spacing";
|
|
8203
|
+
var RULE_VERSION17 = "1";
|
|
7737
8204
|
var SPACING_UTILITY_TO_PROPERTY = /* @__PURE__ */ new Map([
|
|
7738
8205
|
["m", "margin"],
|
|
7739
8206
|
["mt", "margin"],
|
|
@@ -7779,8 +8246,8 @@ function ruleTailwindArbitrarySpacing(ix) {
|
|
|
7779
8246
|
const fix = buildFix2(klass);
|
|
7780
8247
|
findings.push(
|
|
7781
8248
|
makeFinding({
|
|
7782
|
-
ruleId:
|
|
7783
|
-
ruleVersion:
|
|
8249
|
+
ruleId: RULE_ID16,
|
|
8250
|
+
ruleVersion: RULE_VERSION17,
|
|
7784
8251
|
severity: policy.severity,
|
|
7785
8252
|
message: `\`${klass.raw}\` is an arbitrary spacing value not on the project's spacing scale.`,
|
|
7786
8253
|
location: klass.location,
|
|
@@ -7911,8 +8378,8 @@ function matchesTokenGlob(value, pattern) {
|
|
|
7911
8378
|
}
|
|
7912
8379
|
|
|
7913
8380
|
// src/rules/tailwind-forbidden-palette.ts
|
|
7914
|
-
var
|
|
7915
|
-
var
|
|
8381
|
+
var RULE_ID17 = "tailwind/forbidden-palette";
|
|
8382
|
+
var RULE_VERSION18 = "1";
|
|
7916
8383
|
function ruleTailwindForbiddenPalette(ix) {
|
|
7917
8384
|
const deny = ix.policy.tailwindPaletteDeny();
|
|
7918
8385
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
@@ -7937,8 +8404,8 @@ function ruleTailwindForbiddenPalette(ix) {
|
|
|
7937
8404
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
7938
8405
|
findings.push(
|
|
7939
8406
|
makeFinding({
|
|
7940
|
-
ruleId:
|
|
7941
|
-
ruleVersion:
|
|
8407
|
+
ruleId: RULE_ID17,
|
|
8408
|
+
ruleVersion: RULE_VERSION18,
|
|
7942
8409
|
severity: policy.severity,
|
|
7943
8410
|
message: messageFor(klass, token, deniedPattern, allow?.patterns ?? []),
|
|
7944
8411
|
location: klass.location,
|
|
@@ -7985,8 +8452,8 @@ function buildFix3(klass) {
|
|
|
7985
8452
|
}
|
|
7986
8453
|
|
|
7987
8454
|
// src/rules/tailwind-off-scale-spacing-token.ts
|
|
7988
|
-
var
|
|
7989
|
-
var
|
|
8455
|
+
var RULE_ID18 = "tailwind/off-scale-spacing-token";
|
|
8456
|
+
var RULE_VERSION19 = "1";
|
|
7990
8457
|
function ruleTailwindOffScaleSpacingToken(ix) {
|
|
7991
8458
|
const resolvedByKey = indexResolvedTailwindTokens(ix);
|
|
7992
8459
|
const findings = [];
|
|
@@ -8012,8 +8479,8 @@ function ruleTailwindOffScaleSpacingToken(ix) {
|
|
|
8012
8479
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id, scale.id] : [klass.id, resolved.id, policy.id, scale.id];
|
|
8013
8480
|
findings.push(
|
|
8014
8481
|
makeFinding({
|
|
8015
|
-
ruleId:
|
|
8016
|
-
ruleVersion:
|
|
8482
|
+
ruleId: RULE_ID18,
|
|
8483
|
+
ruleVersion: RULE_VERSION19,
|
|
8017
8484
|
severity: policy.severity,
|
|
8018
8485
|
message: `\`${klass.raw}\` resolves to ${formatResolvedValue(signedValue, scale.unit)}, which is not on the project's spacing scale [${allowed.join(", ")}].`,
|
|
8019
8486
|
location: klass.location,
|
|
@@ -8062,8 +8529,8 @@ function buildFix4(raw, utility) {
|
|
|
8062
8529
|
}
|
|
8063
8530
|
|
|
8064
8531
|
// src/rules/tailwind-raw-color-via-token.ts
|
|
8065
|
-
var
|
|
8066
|
-
var
|
|
8532
|
+
var RULE_ID19 = "tailwind/raw-color-via-token";
|
|
8533
|
+
var RULE_VERSION20 = "1";
|
|
8067
8534
|
function ruleTailwindRawColorViaToken(ix) {
|
|
8068
8535
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
8069
8536
|
if (!allow || allow.patterns.length === 0) return [];
|
|
@@ -8083,8 +8550,8 @@ function ruleTailwindRawColorViaToken(ix) {
|
|
|
8083
8550
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, allow.id] : [klass.id, resolved.id, allow.id];
|
|
8084
8551
|
findings.push(
|
|
8085
8552
|
makeFinding({
|
|
8086
|
-
ruleId:
|
|
8087
|
-
ruleVersion:
|
|
8553
|
+
ruleId: RULE_ID19,
|
|
8554
|
+
ruleVersion: RULE_VERSION20,
|
|
8088
8555
|
severity: allow.severity,
|
|
8089
8556
|
message: `\`${klass.raw}\` resolves to raw color \`${color}\` (${sourceLabel2(resolved.resolved.source)}). Project policy requires \`${allow.patterns.join("`, `")}\` palette tokens.`,
|
|
8090
8557
|
location: klass.location,
|
|
@@ -8117,8 +8584,8 @@ function sourceLabel2(source) {
|
|
|
8117
8584
|
}
|
|
8118
8585
|
|
|
8119
8586
|
// src/rules/tailwind-unknown-class.ts
|
|
8120
|
-
var
|
|
8121
|
-
var
|
|
8587
|
+
var RULE_ID20 = "tailwind/unknown-class";
|
|
8588
|
+
var RULE_VERSION21 = "1";
|
|
8122
8589
|
var UNIVERSAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
8123
8590
|
"white",
|
|
8124
8591
|
"black",
|
|
@@ -8139,8 +8606,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
8139
8606
|
const evidenceIds2 = node2 ? [node2.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
8140
8607
|
findings.push(
|
|
8141
8608
|
makeFinding({
|
|
8142
|
-
ruleId:
|
|
8143
|
-
ruleVersion:
|
|
8609
|
+
ruleId: RULE_ID20,
|
|
8610
|
+
ruleVersion: RULE_VERSION21,
|
|
8144
8611
|
severity: policy.severity,
|
|
8145
8612
|
message: `\`${klass.raw}\` could not be parsed as a known Tailwind utility.`,
|
|
8146
8613
|
location: klass.location,
|
|
@@ -8174,8 +8641,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
8174
8641
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
8175
8642
|
findings.push(
|
|
8176
8643
|
makeFinding({
|
|
8177
|
-
ruleId:
|
|
8178
|
-
ruleVersion:
|
|
8644
|
+
ruleId: RULE_ID20,
|
|
8645
|
+
ruleVersion: RULE_VERSION21,
|
|
8179
8646
|
severity: policy.severity,
|
|
8180
8647
|
message: `\`${klass.raw}\` did not resolve to any known palette token.`,
|
|
8181
8648
|
location: klass.location,
|
|
@@ -8203,12 +8670,12 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
8203
8670
|
}
|
|
8204
8671
|
|
|
8205
8672
|
// src/rules/tokens-require-dual-fallback.ts
|
|
8206
|
-
var
|
|
8207
|
-
var
|
|
8673
|
+
var RULE_ID21 = "tokens/require-dual-fallback";
|
|
8674
|
+
var RULE_VERSION22 = "1";
|
|
8208
8675
|
var CSS_VAR_WITH_OPTIONAL_FALLBACK = /var\(\s*(--[A-Za-z0-9_-]+)(\s*,\s*[^)]*)?\)/g;
|
|
8209
8676
|
var SASS_FILE = /\.(scss|sass)$/i;
|
|
8210
8677
|
function ruleTokensRequireDualFallback(ix) {
|
|
8211
|
-
const policy = ix.policy.ruleConfig(
|
|
8678
|
+
const policy = ix.policy.ruleConfig(RULE_ID21);
|
|
8212
8679
|
if (!policy?.enabled) return [];
|
|
8213
8680
|
const tokensByCssVariable = indexTokensByCssVariable(localTokenCandidates(ix));
|
|
8214
8681
|
const findings = [];
|
|
@@ -8243,8 +8710,8 @@ function ruleTokensRequireDualFallback(ix) {
|
|
|
8243
8710
|
) : void 0;
|
|
8244
8711
|
findings.push(
|
|
8245
8712
|
makeFinding({
|
|
8246
|
-
ruleId:
|
|
8247
|
-
ruleVersion:
|
|
8713
|
+
ruleId: RULE_ID21,
|
|
8714
|
+
ruleVersion: RULE_VERSION22,
|
|
8248
8715
|
severity: policy.severity ?? "warn",
|
|
8249
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}.`,
|
|
8250
8717
|
location: decl.location,
|
|
@@ -8284,8 +8751,8 @@ function indexTokensByCssVariable(tokens) {
|
|
|
8284
8751
|
}
|
|
8285
8752
|
|
|
8286
8753
|
// src/rules/tokens-css-vars-must-be-defined.ts
|
|
8287
|
-
var
|
|
8288
|
-
var
|
|
8754
|
+
var RULE_ID22 = "tokens/css-vars-must-be-defined";
|
|
8755
|
+
var RULE_VERSION23 = "1";
|
|
8289
8756
|
var CSS_VAR_REFERENCE = /var\(\s*(--[A-Za-z0-9_-]+)/gi;
|
|
8290
8757
|
function ruleTokensCssVarsMustBeDefined(ix) {
|
|
8291
8758
|
const policy = ix.policy.cssVarsMustBeDefined();
|
|
@@ -8312,8 +8779,8 @@ function ruleTokensCssVarsMustBeDefined(ix) {
|
|
|
8312
8779
|
seen.add(tokenName);
|
|
8313
8780
|
findings.push(
|
|
8314
8781
|
makeFinding({
|
|
8315
|
-
ruleId:
|
|
8316
|
-
ruleVersion:
|
|
8782
|
+
ruleId: RULE_ID22,
|
|
8783
|
+
ruleVersion: RULE_VERSION23,
|
|
8317
8784
|
severity: policy.severity,
|
|
8318
8785
|
message: `var(${tokenName}) is not in the contract token vocabulary. Use a token defined in your contract's token source files.`,
|
|
8319
8786
|
location: decl.location,
|
|
@@ -8360,8 +8827,8 @@ function skipToCloseParen(value, openParenIndex) {
|
|
|
8360
8827
|
}
|
|
8361
8828
|
|
|
8362
8829
|
// src/rules/tokens-upstream-drift.ts
|
|
8363
|
-
var
|
|
8364
|
-
var
|
|
8830
|
+
var RULE_ID23 = "tokens/upstream-drift";
|
|
8831
|
+
var RULE_VERSION24 = "1";
|
|
8365
8832
|
function ruleTokensUpstreamDrift(ix) {
|
|
8366
8833
|
const local = ix.tokens.list().filter((token) => token.role !== "upstream");
|
|
8367
8834
|
const upstream = ix.tokens.list().filter((token) => token.role === "upstream");
|
|
@@ -8387,8 +8854,8 @@ function ruleTokensUpstreamDrift(ix) {
|
|
|
8387
8854
|
};
|
|
8388
8855
|
findings.push(
|
|
8389
8856
|
makeFinding({
|
|
8390
|
-
ruleId:
|
|
8391
|
-
ruleVersion:
|
|
8857
|
+
ruleId: RULE_ID23,
|
|
8858
|
+
ruleVersion: RULE_VERSION24,
|
|
8392
8859
|
severity: "warn",
|
|
8393
8860
|
message: `Local token ${token.name} is ${token.value}, but upstream ${upstreamName} is ${expected.value}.`,
|
|
8394
8861
|
location,
|
|
@@ -8420,8 +8887,12 @@ var BLOCKING_RULE_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
|
8420
8887
|
// off-scale Tailwind spacing utility
|
|
8421
8888
|
"components/prefer-library",
|
|
8422
8889
|
// raw element bypasses a canonical component
|
|
8423
|
-
"components/
|
|
8890
|
+
"components/shadow-component",
|
|
8891
|
+
// confirmed local semantic control shadows a canonical
|
|
8892
|
+
"components/preferred-component",
|
|
8424
8893
|
// imported component bypasses its canonical
|
|
8894
|
+
"tokens/css-vars-must-be-defined"
|
|
8895
|
+
// off-contract custom property reference
|
|
8425
8896
|
]);
|
|
8426
8897
|
function gatesCi(finding, failOnWarnings) {
|
|
8427
8898
|
if (!canBlock(finding.evidenceGrade ?? "source_backed")) return false;
|
|
@@ -8432,9 +8903,13 @@ function gatesCi(finding, failOnWarnings) {
|
|
|
8432
8903
|
function isDenyEligible(finding, failOnWarnings) {
|
|
8433
8904
|
if (!BLOCKING_RULE_ALLOWLIST.has(finding.ruleId)) return false;
|
|
8434
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
|
+
}
|
|
8435
8909
|
if (!gatesCi(finding, failOnWarnings)) return false;
|
|
8436
8910
|
if (finding.fix?.deterministic === false) {
|
|
8437
|
-
|
|
8911
|
+
const exactCanonicalBypass = finding.ruleId === "components/prefer-library" && finding.attributes?.precisionTier === "exact-html" && typeof finding.attributes?.suggestedComponent === "string";
|
|
8912
|
+
if (!exactCanonicalBypass) {
|
|
8438
8913
|
return false;
|
|
8439
8914
|
}
|
|
8440
8915
|
}
|
|
@@ -8471,6 +8946,7 @@ var RULE_TIER = {
|
|
|
8471
8946
|
"tokens/require-dual-fallback": "hygiene",
|
|
8472
8947
|
"theme/no-theme-coupled-literal": "hygiene",
|
|
8473
8948
|
"a11y/required-accessible-name": "hygiene",
|
|
8949
|
+
"a11y/standard": "hygiene",
|
|
8474
8950
|
// Composition constraints are authored contracts, but not part of the
|
|
8475
8951
|
// two-choice headline contract; they self-activate when patterns are declared
|
|
8476
8952
|
// (the compiler injects their configs), so off-by-default here is harmless.
|
|
@@ -8632,9 +9108,14 @@ var RULES = [
|
|
|
8632
9108
|
},
|
|
8633
9109
|
{
|
|
8634
9110
|
id: "a11y/required-accessible-name",
|
|
8635
|
-
version:
|
|
9111
|
+
version: RULE_VERSION,
|
|
8636
9112
|
run: ruleA11yRequiredAccessibleName
|
|
8637
9113
|
},
|
|
9114
|
+
{
|
|
9115
|
+
id: "a11y/standard",
|
|
9116
|
+
version: "1",
|
|
9117
|
+
run: ruleA11yStandard
|
|
9118
|
+
},
|
|
8638
9119
|
{
|
|
8639
9120
|
id: "composition/cardinality",
|
|
8640
9121
|
version: "1",
|
|
@@ -8673,12 +9154,21 @@ function hasEffectiveComponentVocabulary(options) {
|
|
|
8673
9154
|
}
|
|
8674
9155
|
function effectiveRuleConfigs(policy) {
|
|
8675
9156
|
const configs = /* @__PURE__ */ new Map();
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
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
|
|
8682
9172
|
});
|
|
8683
9173
|
}
|
|
8684
9174
|
return configs;
|
|
@@ -9082,23 +9572,28 @@ function evaluateGovernanceIntegrity(input) {
|
|
|
9082
9572
|
if (!policyArmed) {
|
|
9083
9573
|
policyFamily.reason = "no governance policy resolved";
|
|
9084
9574
|
}
|
|
9085
|
-
const
|
|
9575
|
+
const preferLibraryConfig = configs.get("components/prefer-library");
|
|
9576
|
+
const shadowComponentConfig = configs.get("components/shadow-component");
|
|
9086
9577
|
const confirmedBridges = input.policy?.canonicalBridges ?? [];
|
|
9087
|
-
const
|
|
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;
|
|
9088
9582
|
const componentsFamily = {
|
|
9089
9583
|
id: "components",
|
|
9090
9584
|
armed: componentsArmed,
|
|
9091
9585
|
rules: [
|
|
9092
9586
|
...confirmedBridges.length > 0 ? ["imports/preferred-path"] : [],
|
|
9093
|
-
...
|
|
9587
|
+
...preferLibraryConfig?.enabled === true ? ["components/prefer-library"] : [],
|
|
9588
|
+
...shadowComponentConfig?.enabled === true ? ["components/shadow-component"] : []
|
|
9094
9589
|
]
|
|
9095
9590
|
};
|
|
9096
9591
|
if (!componentsArmed) {
|
|
9097
|
-
if (
|
|
9098
|
-
componentsFamily.reason = "
|
|
9592
|
+
if (componentRuleEnabled) {
|
|
9593
|
+
componentsFamily.reason = "component governance enabled but no effective canonical source";
|
|
9099
9594
|
componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
|
|
9100
9595
|
} else {
|
|
9101
|
-
componentsFamily.reason = "
|
|
9596
|
+
componentsFamily.reason = "canonical-component rules not enabled";
|
|
9102
9597
|
componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
|
|
9103
9598
|
}
|
|
9104
9599
|
}
|
|
@@ -9141,7 +9636,7 @@ function evaluateGovernanceIntegrity(input) {
|
|
|
9141
9636
|
for (const ruleId of BLOCKING_RULE_ALLOWLIST) {
|
|
9142
9637
|
const config = configs.get(ruleId);
|
|
9143
9638
|
if (config?.enabled !== true) continue;
|
|
9144
|
-
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;
|
|
9145
9640
|
if (!familyArmed) continue;
|
|
9146
9641
|
if (config.severity !== "error" && !failOnWarnings) continue;
|
|
9147
9642
|
blockingRules.push(ruleId);
|
|
@@ -9772,6 +10267,7 @@ export {
|
|
|
9772
10267
|
CONTRACT_DOMAINS,
|
|
9773
10268
|
CONTRACT_PREIMAGE_CAPABILITY_HEADER,
|
|
9774
10269
|
CONTRACT_PREIMAGE_SCHEMA,
|
|
10270
|
+
CompiledFragmentsFileValidationError,
|
|
9775
10271
|
DEFAULTS,
|
|
9776
10272
|
DEFAULT_ENHANCED_STYLE_PROPERTIES,
|
|
9777
10273
|
DEFAULT_STYLE_PROPERTIES,
|
|
@@ -10010,6 +10506,7 @@ export {
|
|
|
10010
10506
|
makeTailwindUnknownClassEnabledFact,
|
|
10011
10507
|
makeThemeDeclarationFact,
|
|
10012
10508
|
makeTokenDefinitionFact,
|
|
10509
|
+
makeUsageChildContentFact,
|
|
10013
10510
|
makeUsageComponentFact,
|
|
10014
10511
|
makeUsageImportFact,
|
|
10015
10512
|
makeUsageInlineStyleFact,
|
|
@@ -10058,6 +10555,7 @@ export {
|
|
|
10058
10555
|
ownedImportEquivalents,
|
|
10059
10556
|
parseColor,
|
|
10060
10557
|
parseColorToRgb,
|
|
10558
|
+
parseCompiledFragmentsFile,
|
|
10061
10559
|
parseComponentContract,
|
|
10062
10560
|
parseContractStamp,
|
|
10063
10561
|
parseCssTokens,
|
|
@@ -10115,6 +10613,7 @@ export {
|
|
|
10115
10613
|
resolveTokenValue,
|
|
10116
10614
|
rgbToHex,
|
|
10117
10615
|
ruleA11yRequiredAccessibleName,
|
|
10616
|
+
ruleA11yStandard,
|
|
10118
10617
|
ruleComponentsForbiddenPropValue,
|
|
10119
10618
|
ruleComponentsPreferLibrary,
|
|
10120
10619
|
ruleComponentsShadowComponent,
|