@usefragments/core 1.4.0 → 1.5.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-CBSNXFOD.js → chunk-HXGE3O2F.js} +39 -1
- package/dist/chunk-HXGE3O2F.js.map +1 -0
- package/dist/codes/index.d.ts +2 -2
- package/dist/codes/index.js +1 -1
- package/dist/compiled-types/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/{governance-DxdJV6lx.d.ts → governance-BLsyk56o.d.ts} +11 -0
- package/dist/index.d.ts +359 -139
- package/dist/index.js +349 -95
- package/dist/index.js.map +1 -1
- package/dist/react-types.d.ts +1 -1
- package/dist/registry.d.ts +18 -18
- package/dist/schemas/index.d.ts +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/package.json +1 -1
- package/src/codes/codes.ts +9 -0
- package/src/conform.ts +5 -0
- package/src/facts/builders.ts +45 -1
- package/src/facts/facts.test.ts +30 -0
- package/src/facts/index.ts +6 -0
- package/src/facts/types.ts +61 -1
- package/src/governance.test.ts +26 -0
- package/src/identity/classify.test.ts +185 -0
- package/src/identity/classify.ts +257 -0
- package/src/identity/component-key.test.ts +32 -0
- package/src/identity/component-key.ts +35 -0
- package/src/identity/usage.ts +39 -0
- package/src/index.ts +25 -0
- package/src/rules/components-prefer-library.test.ts +48 -0
- package/src/rules/components-prefer-library.ts +16 -0
- package/src/rules/components-shadow-component.test.ts +145 -0
- package/src/rules/components-shadow-component.ts +81 -0
- package/src/rules/fix-availability.ts +1 -0
- package/src/rules/index.ts +7 -0
- package/src/rules/taxonomy.test.ts +16 -1
- package/src/rules/tiers.ts +7 -5
- package/src/schema.ts +15 -0
- package/src/types.ts +12 -0
- package/dist/chunk-CBSNXFOD.js.map +0 -1
- package/dist/{index-0lmh0Lbo.d.ts → index-h_yWj15D.d.ts} +8 -8
package/dist/index.js
CHANGED
|
@@ -71,6 +71,8 @@ import {
|
|
|
71
71
|
makeClassNameDynamicFact,
|
|
72
72
|
makeClassNameLiteralFact,
|
|
73
73
|
makeComponentCapabilityFact,
|
|
74
|
+
makeComponentDefinitionFact,
|
|
75
|
+
makeComponentIdentityFact,
|
|
74
76
|
makeComponentMetadataFact,
|
|
75
77
|
makeContractTokenFact,
|
|
76
78
|
makeJsxComponentPreferredFact,
|
|
@@ -109,7 +111,7 @@ import {
|
|
|
109
111
|
ownedImportsEqual,
|
|
110
112
|
resolveComponentGovernance,
|
|
111
113
|
scaleGovernanceRecordSchema
|
|
112
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-HXGE3O2F.js";
|
|
113
115
|
import {
|
|
114
116
|
AGENT_FORMAT_SCHEMA_VERSION,
|
|
115
117
|
agentErrorEnvelopeSchema,
|
|
@@ -1776,7 +1778,18 @@ var fragmentsConfigSchema = z4.object({
|
|
|
1776
1778
|
})
|
|
1777
1779
|
).optional()
|
|
1778
1780
|
}).optional(),
|
|
1779
|
-
recognizedClassHelpers: z4.array(z4.string().min(1)).optional()
|
|
1781
|
+
recognizedClassHelpers: z4.array(z4.string().min(1)).optional(),
|
|
1782
|
+
identity: z4.object({
|
|
1783
|
+
decisions: z4.array(
|
|
1784
|
+
z4.object({
|
|
1785
|
+
component: z4.string().min(1),
|
|
1786
|
+
kind: z4.enum(["sanction", "reject", "dismiss"]),
|
|
1787
|
+
canonicalTarget: z4.string().min(1).optional(),
|
|
1788
|
+
reason: z4.string().optional(),
|
|
1789
|
+
decisionId: z4.string().min(1).optional()
|
|
1790
|
+
})
|
|
1791
|
+
).optional()
|
|
1792
|
+
}).optional()
|
|
1780
1793
|
});
|
|
1781
1794
|
var compositionMetadataSchema = z4.object({
|
|
1782
1795
|
pattern: z4.enum(["compound", "simple", "controlled", "wrapper"]).optional(),
|
|
@@ -5077,12 +5090,14 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5077
5090
|
const classTokensByNode = indexClassTokensByNode(ix);
|
|
5078
5091
|
const reimplTargets = canonicalReimplTargets(sources, mappings);
|
|
5079
5092
|
const canonicalDirectoryResolvedNodes = indexCanonicalDirectoryResolvedNodes(ix, sources);
|
|
5093
|
+
const shadowRenderRootNodeIds = indexShadowRenderRootNodeIds(ix);
|
|
5080
5094
|
const findings = [];
|
|
5081
5095
|
const seenImportFixes = /* @__PURE__ */ new Set();
|
|
5082
5096
|
for (const node of ix.byKind("usage_node")) {
|
|
5083
5097
|
if (isCanonicalSourceImplementationFile(node.file, sources)) continue;
|
|
5084
5098
|
if (node.element.includes(".")) continue;
|
|
5085
5099
|
if (canonicalDirectoryResolvedNodes.has(node.id)) continue;
|
|
5100
|
+
if (shadowRenderRootNodeIds.has(node.id)) continue;
|
|
5086
5101
|
const props = propsByNode.get(node.id) ?? [];
|
|
5087
5102
|
const textChildren = textByNode.get(node.id) ?? [];
|
|
5088
5103
|
const importsForFile = importsByFileAndLocal.get(node.file);
|
|
@@ -5330,6 +5345,18 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5330
5345
|
}
|
|
5331
5346
|
return findings;
|
|
5332
5347
|
}
|
|
5348
|
+
function indexShadowRenderRootNodeIds(ix) {
|
|
5349
|
+
const identities = ix.byKind("component_identity");
|
|
5350
|
+
if (identities.length === 0) return /* @__PURE__ */ new Set();
|
|
5351
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
5352
|
+
for (const identity of identities) {
|
|
5353
|
+
if (identity.state !== "shadow") continue;
|
|
5354
|
+
for (const evidenceId of identity.evidence) {
|
|
5355
|
+
if (ix.get(evidenceId)?.kind === "usage_node") nodeIds.add(evidenceId);
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
return nodeIds;
|
|
5359
|
+
}
|
|
5333
5360
|
function findBuiltInRawHtmlMatch(node, props, textChildren, inputType) {
|
|
5334
5361
|
if (isIconOnlyButton(node, props, textChildren)) {
|
|
5335
5362
|
return { canonical: "IconButton", tier: "icon-only" };
|
|
@@ -5656,9 +5683,95 @@ function isIdentifier(value) {
|
|
|
5656
5683
|
return /^[A-Za-z_$][\w$]*$/.test(value);
|
|
5657
5684
|
}
|
|
5658
5685
|
|
|
5659
|
-
// src/
|
|
5660
|
-
|
|
5686
|
+
// src/identity/usage.ts
|
|
5687
|
+
function indexComponentIdentityUsage(ix) {
|
|
5688
|
+
const nodesById = new Map(ix.byKind("usage_node").map((node) => [node.id, node]));
|
|
5689
|
+
const nodesByComponent = /* @__PURE__ */ new Map();
|
|
5690
|
+
for (const usage of ix.byKind("usage_component")) {
|
|
5691
|
+
const node = nodesById.get(usage.nodeId);
|
|
5692
|
+
if (!node) continue;
|
|
5693
|
+
const componentKey = String(usage.componentId);
|
|
5694
|
+
const nodes = nodesByComponent.get(componentKey) ?? /* @__PURE__ */ new Map();
|
|
5695
|
+
nodes.set(node.id, node);
|
|
5696
|
+
nodesByComponent.set(componentKey, nodes);
|
|
5697
|
+
}
|
|
5698
|
+
return new Map(
|
|
5699
|
+
[...nodesByComponent].map(([componentKey, nodesByIdForComponent]) => {
|
|
5700
|
+
const nodes = [...nodesByIdForComponent.values()];
|
|
5701
|
+
return [
|
|
5702
|
+
componentKey,
|
|
5703
|
+
{
|
|
5704
|
+
usageCount: nodes.length,
|
|
5705
|
+
blastRadius: new Set(nodes.map((node) => node.file)).size,
|
|
5706
|
+
nodes
|
|
5707
|
+
}
|
|
5708
|
+
];
|
|
5709
|
+
})
|
|
5710
|
+
);
|
|
5711
|
+
}
|
|
5712
|
+
|
|
5713
|
+
// src/rules/components-shadow-component.ts
|
|
5714
|
+
var RULE_ID3 = "components/shadow-component";
|
|
5661
5715
|
var RULE_VERSION3 = "1";
|
|
5716
|
+
function ruleComponentsShadowComponent(ix) {
|
|
5717
|
+
const policy = ix.policy.ruleConfig(RULE_ID3);
|
|
5718
|
+
if (!policy?.enabled) return [];
|
|
5719
|
+
const definitions = new Map(
|
|
5720
|
+
ix.byKind("component_definition").map((definition) => [definition.componentKey, definition])
|
|
5721
|
+
);
|
|
5722
|
+
const usageByComponent = indexComponentIdentityUsage(ix);
|
|
5723
|
+
const findings = [];
|
|
5724
|
+
for (const identity of ix.byKind("component_identity")) {
|
|
5725
|
+
const reviewVariant = identity.state === "variant" && identity.confidence === "review";
|
|
5726
|
+
if (identity.state !== "shadow" && !reviewVariant) continue;
|
|
5727
|
+
const definition = definitions.get(identity.componentKey);
|
|
5728
|
+
if (!definition) continue;
|
|
5729
|
+
const usage = usageByComponent.get(identity.componentKey);
|
|
5730
|
+
const usageCount = usage?.usageCount ?? 0;
|
|
5731
|
+
const blastRadius = usage?.blastRadius ?? 0;
|
|
5732
|
+
findings.push(
|
|
5733
|
+
makeFinding({
|
|
5734
|
+
ruleId: RULE_ID3,
|
|
5735
|
+
ruleVersion: RULE_VERSION3,
|
|
5736
|
+
severity: reviewVariant ? "info" : policy.severity ?? "warn",
|
|
5737
|
+
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"}.`,
|
|
5738
|
+
location: definitionLocation(ix, definition, identity),
|
|
5739
|
+
evidence: ix.evidence(identity.evidence),
|
|
5740
|
+
fingerprintIdentity: {
|
|
5741
|
+
componentKey: identity.componentKey,
|
|
5742
|
+
canonicalTarget: identity.canonicalTarget
|
|
5743
|
+
},
|
|
5744
|
+
attributes: {
|
|
5745
|
+
canonicalTarget: identity.canonicalTarget,
|
|
5746
|
+
usageCount,
|
|
5747
|
+
blastRadius,
|
|
5748
|
+
confidence: identity.confidence,
|
|
5749
|
+
state: identity.state,
|
|
5750
|
+
...reviewVariant ? { advisory: true } : {}
|
|
5751
|
+
}
|
|
5752
|
+
})
|
|
5753
|
+
);
|
|
5754
|
+
}
|
|
5755
|
+
return findings;
|
|
5756
|
+
}
|
|
5757
|
+
function definitionLocation(ix, definition, identity) {
|
|
5758
|
+
const evidence = ix.evidence(identity.evidence).map(({ fact }) => fact);
|
|
5759
|
+
const inDefinitionFile = evidence.find(
|
|
5760
|
+
(fact) => "location" in fact && fact.location?.file === definition.file
|
|
5761
|
+
);
|
|
5762
|
+
if (inDefinitionFile && "location" in inDefinitionFile && inDefinitionFile.location) {
|
|
5763
|
+
return inDefinitionFile.location;
|
|
5764
|
+
}
|
|
5765
|
+
const sourceLocated = evidence.find((fact) => "location" in fact && fact.location);
|
|
5766
|
+
if (sourceLocated && "location" in sourceLocated && sourceLocated.location) {
|
|
5767
|
+
return sourceLocated.location;
|
|
5768
|
+
}
|
|
5769
|
+
return { file: definition.file, line: 1, column: 0 };
|
|
5770
|
+
}
|
|
5771
|
+
|
|
5772
|
+
// src/rules/components-forbidden-prop-value.ts
|
|
5773
|
+
var RULE_ID4 = "components/forbidden-prop-value";
|
|
5774
|
+
var RULE_VERSION4 = "1";
|
|
5662
5775
|
function ruleComponentsForbiddenPropValue(ix) {
|
|
5663
5776
|
const findings = [];
|
|
5664
5777
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -5688,8 +5801,8 @@ function ruleComponentsForbiddenPropValue(ix) {
|
|
|
5688
5801
|
} : void 0;
|
|
5689
5802
|
findings.push(
|
|
5690
5803
|
makeFinding({
|
|
5691
|
-
ruleId:
|
|
5692
|
-
ruleVersion:
|
|
5804
|
+
ruleId: RULE_ID4,
|
|
5805
|
+
ruleVersion: RULE_VERSION4,
|
|
5693
5806
|
severity: policy.severity,
|
|
5694
5807
|
message: buildMessage(
|
|
5695
5808
|
node.element,
|
|
@@ -5733,8 +5846,8 @@ function buildMessage(element, prop, value, because, replacement) {
|
|
|
5733
5846
|
}
|
|
5734
5847
|
|
|
5735
5848
|
// src/rules/components-unknown-prop.ts
|
|
5736
|
-
var
|
|
5737
|
-
var
|
|
5849
|
+
var RULE_ID5 = "components/unknown-prop";
|
|
5850
|
+
var RULE_VERSION5 = "1";
|
|
5738
5851
|
function ruleComponentsUnknownProp(ix) {
|
|
5739
5852
|
const policy = ix.policy.jsxUnknownPropsForbidden();
|
|
5740
5853
|
if (!policy) return [];
|
|
@@ -5755,8 +5868,8 @@ function ruleComponentsUnknownProp(ix) {
|
|
|
5755
5868
|
if (knownNames.has(propUsage.prop)) continue;
|
|
5756
5869
|
findings.push(
|
|
5757
5870
|
makeFinding({
|
|
5758
|
-
ruleId:
|
|
5759
|
-
ruleVersion:
|
|
5871
|
+
ruleId: RULE_ID5,
|
|
5872
|
+
ruleVersion: RULE_VERSION5,
|
|
5760
5873
|
severity: policy.severity,
|
|
5761
5874
|
message: `Unknown prop "${propUsage.prop}" on <${node.element}>. Allowed props: ${formatPropList(knownNames)}.`,
|
|
5762
5875
|
location: node.location,
|
|
@@ -5788,7 +5901,7 @@ function formatPropList(names) {
|
|
|
5788
5901
|
// src/rules/composition-pattern.ts
|
|
5789
5902
|
var CARDINALITY_RULE_ID = "composition/cardinality";
|
|
5790
5903
|
var CO_OCCURRENCE_RULE_ID = "composition/co-occurrence";
|
|
5791
|
-
var
|
|
5904
|
+
var RULE_VERSION6 = "1";
|
|
5792
5905
|
function ruleCompositionCardinality(ix) {
|
|
5793
5906
|
return evaluate(ix, CARDINALITY_RULE_ID, "cardinality");
|
|
5794
5907
|
}
|
|
@@ -5856,7 +5969,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
5856
5969
|
const extras = selected.slice(constraint.max);
|
|
5857
5970
|
return makeFinding({
|
|
5858
5971
|
ruleId: ctx.ruleId,
|
|
5859
|
-
ruleVersion:
|
|
5972
|
+
ruleVersion: RULE_VERSION6,
|
|
5860
5973
|
severity: ctx.severity,
|
|
5861
5974
|
message: cardinalityMaxMessage(pattern, constraint.max, count),
|
|
5862
5975
|
location: extras[0].location,
|
|
@@ -5867,7 +5980,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
5867
5980
|
if (constraint.min !== void 0 && count < constraint.min) {
|
|
5868
5981
|
return makeFinding({
|
|
5869
5982
|
ruleId: ctx.ruleId,
|
|
5870
|
-
ruleVersion:
|
|
5983
|
+
ruleVersion: RULE_VERSION6,
|
|
5871
5984
|
severity: ctx.severity,
|
|
5872
5985
|
message: cardinalityMinMessage(pattern, constraint.min, count),
|
|
5873
5986
|
location: region.container.location,
|
|
@@ -5888,7 +6001,7 @@ function checkCoOccurrence(ctx, region, pattern, constraint, selected) {
|
|
|
5888
6001
|
if (satisfied) return null;
|
|
5889
6002
|
return makeFinding({
|
|
5890
6003
|
ruleId: ctx.ruleId,
|
|
5891
|
-
ruleVersion:
|
|
6004
|
+
ruleVersion: RULE_VERSION6,
|
|
5892
6005
|
severity: ctx.severity,
|
|
5893
6006
|
message: coOccurrenceMessage(pattern, constraint),
|
|
5894
6007
|
location: region.container.location,
|
|
@@ -5940,8 +6053,8 @@ function coOccurrenceMessage(pattern, constraint) {
|
|
|
5940
6053
|
}
|
|
5941
6054
|
|
|
5942
6055
|
// src/rules/jsx-preferred-component.ts
|
|
5943
|
-
var
|
|
5944
|
-
var
|
|
6056
|
+
var RULE_ID6 = "components/preferred-component";
|
|
6057
|
+
var RULE_VERSION7 = "1";
|
|
5945
6058
|
function ruleJsxPreferredComponent(ix) {
|
|
5946
6059
|
const policies = ix.policy.jsxComponentPreferred();
|
|
5947
6060
|
if (policies.length === 0) return [];
|
|
@@ -5957,8 +6070,8 @@ function ruleJsxPreferredComponent(ix) {
|
|
|
5957
6070
|
if (!replacement) continue;
|
|
5958
6071
|
findings.push(
|
|
5959
6072
|
makeFinding({
|
|
5960
|
-
ruleId:
|
|
5961
|
-
ruleVersion:
|
|
6073
|
+
ruleId: RULE_ID6,
|
|
6074
|
+
ruleVersion: RULE_VERSION7,
|
|
5962
6075
|
severity: policy.severity,
|
|
5963
6076
|
message: `<${node.element}> should use <${replacement}>.`,
|
|
5964
6077
|
location: node.location,
|
|
@@ -5995,8 +6108,8 @@ function componentNameFromId(componentId2) {
|
|
|
5995
6108
|
}
|
|
5996
6109
|
|
|
5997
6110
|
// src/rules/jsx-preferred-import-path.ts
|
|
5998
|
-
var
|
|
5999
|
-
var
|
|
6111
|
+
var RULE_ID7 = "imports/preferred-path";
|
|
6112
|
+
var RULE_VERSION8 = "1";
|
|
6000
6113
|
function ruleJsxPreferredImportPath(ix) {
|
|
6001
6114
|
const policies = ix.policy.jsxImportPathPreferred();
|
|
6002
6115
|
if (policies.length === 0) return [];
|
|
@@ -6016,8 +6129,8 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6016
6129
|
seen.add(key);
|
|
6017
6130
|
findings.push(
|
|
6018
6131
|
makeFinding({
|
|
6019
|
-
ruleId:
|
|
6020
|
-
ruleVersion:
|
|
6132
|
+
ruleId: RULE_ID7,
|
|
6133
|
+
ruleVersion: RULE_VERSION8,
|
|
6021
6134
|
severity: policy.severity,
|
|
6022
6135
|
message: `Import from "${usage.source}" should use "${policy.to}".`,
|
|
6023
6136
|
location: usage.location,
|
|
@@ -6052,10 +6165,10 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6052
6165
|
}
|
|
6053
6166
|
|
|
6054
6167
|
// src/rules/props-invalid-value.ts
|
|
6055
|
-
var
|
|
6056
|
-
var
|
|
6168
|
+
var RULE_ID8 = "props/invalid-value";
|
|
6169
|
+
var RULE_VERSION9 = "1";
|
|
6057
6170
|
function rulePropsInvalidValue(ix) {
|
|
6058
|
-
const policy = ix.policy.ruleConfig(
|
|
6171
|
+
const policy = ix.policy.ruleConfig(RULE_ID8);
|
|
6059
6172
|
if (!policy?.enabled) return [];
|
|
6060
6173
|
const findings = [];
|
|
6061
6174
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -6084,8 +6197,8 @@ function rulePropsInvalidValue(ix) {
|
|
|
6084
6197
|
} : void 0;
|
|
6085
6198
|
findings.push(
|
|
6086
6199
|
makeFinding({
|
|
6087
|
-
ruleId:
|
|
6088
|
-
ruleVersion:
|
|
6200
|
+
ruleId: RULE_ID8,
|
|
6201
|
+
ruleVersion: RULE_VERSION9,
|
|
6089
6202
|
severity: policy.severity ?? "warn",
|
|
6090
6203
|
message: buildMessage2(node.element, propUsage.prop, propUsage.value, allowedValues),
|
|
6091
6204
|
location: propUsage.location ?? node.location,
|
|
@@ -6261,8 +6374,8 @@ function srgbGamma(linear) {
|
|
|
6261
6374
|
}
|
|
6262
6375
|
|
|
6263
6376
|
// src/rules/styles-no-raw-color.ts
|
|
6264
|
-
var
|
|
6265
|
-
var
|
|
6377
|
+
var RULE_ID9 = "styles/no-raw-color";
|
|
6378
|
+
var RULE_VERSION10 = "1";
|
|
6266
6379
|
function ruleStylesNoRawColor(ix) {
|
|
6267
6380
|
const policy = ix.policy.rawColorPolicy();
|
|
6268
6381
|
if (!policy) return [];
|
|
@@ -6280,8 +6393,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6280
6393
|
const evidenceIds = resolution ? [decl.id, policy.id, resolution.token.id] : [decl.id, policy.id];
|
|
6281
6394
|
findings.push(
|
|
6282
6395
|
makeFinding({
|
|
6283
|
-
ruleId:
|
|
6284
|
-
ruleVersion:
|
|
6396
|
+
ruleId: RULE_ID9,
|
|
6397
|
+
ruleVersion: RULE_VERSION10,
|
|
6285
6398
|
severity: policy.severity,
|
|
6286
6399
|
message: colorMessage(color, `\`${decl.property}\``, resolution, preferLabel),
|
|
6287
6400
|
location: decl.location,
|
|
@@ -6316,8 +6429,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6316
6429
|
const evidenceIds2 = componentEvidenceId2 ? [node2.id, componentEvidenceId2, inline.id, policy.id] : [node2.id, inline.id, policy.id];
|
|
6317
6430
|
findings.push(
|
|
6318
6431
|
makeFinding({
|
|
6319
|
-
ruleId:
|
|
6320
|
-
ruleVersion:
|
|
6432
|
+
ruleId: RULE_ID9,
|
|
6433
|
+
ruleVersion: RULE_VERSION10,
|
|
6321
6434
|
severity: advisorySeverity,
|
|
6322
6435
|
message: `Runtime-constructed raw color \`${inline.value}\` on inline \`${inline.property}\`. Use a ${preferLabel} instead.`,
|
|
6323
6436
|
location: node2.location,
|
|
@@ -6353,8 +6466,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6353
6466
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6354
6467
|
findings.push(
|
|
6355
6468
|
makeFinding({
|
|
6356
|
-
ruleId:
|
|
6357
|
-
ruleVersion:
|
|
6469
|
+
ruleId: RULE_ID9,
|
|
6470
|
+
ruleVersion: RULE_VERSION10,
|
|
6358
6471
|
severity: policy.severity,
|
|
6359
6472
|
message: colorMessage(color, `inline \`${inline.property}\``, resolution, preferLabel),
|
|
6360
6473
|
location: node.location,
|
|
@@ -6397,8 +6510,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6397
6510
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6398
6511
|
findings.push(
|
|
6399
6512
|
makeFinding({
|
|
6400
|
-
ruleId:
|
|
6401
|
-
ruleVersion:
|
|
6513
|
+
ruleId: RULE_ID9,
|
|
6514
|
+
ruleVersion: RULE_VERSION10,
|
|
6402
6515
|
severity: advisorySeverity,
|
|
6403
6516
|
message: colorMessage(color, `\`${prop.prop}\``, resolution, preferLabel),
|
|
6404
6517
|
location: prop.location ?? node.location,
|
|
@@ -6549,8 +6662,8 @@ function escapeRegExp(value) {
|
|
|
6549
6662
|
}
|
|
6550
6663
|
|
|
6551
6664
|
// src/rules/styles-no-raw-dimensions.ts
|
|
6552
|
-
var
|
|
6553
|
-
var
|
|
6665
|
+
var RULE_ID10 = "styles/no-raw-dimensions";
|
|
6666
|
+
var RULE_VERSION11 = "1";
|
|
6554
6667
|
function ruleStylesNoRawDimensions(ix) {
|
|
6555
6668
|
const policy = ix.policy.rawDimensionPolicy();
|
|
6556
6669
|
if (!policy) return [];
|
|
@@ -6570,8 +6683,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6570
6683
|
const fixEmission = dimensionFix(decl.property, resolution, ix);
|
|
6571
6684
|
findings.push(
|
|
6572
6685
|
makeFinding({
|
|
6573
|
-
ruleId:
|
|
6574
|
-
ruleVersion:
|
|
6686
|
+
ruleId: RULE_ID10,
|
|
6687
|
+
ruleVersion: RULE_VERSION11,
|
|
6575
6688
|
severity: policy.severity,
|
|
6576
6689
|
message: dimensionMessage(decl.value, decl.property, preferLabel, resolution, false),
|
|
6577
6690
|
location: decl.location,
|
|
@@ -6612,8 +6725,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6612
6725
|
const fixEmission = dimensionFix(inline.property, resolution, ix);
|
|
6613
6726
|
findings.push(
|
|
6614
6727
|
makeFinding({
|
|
6615
|
-
ruleId:
|
|
6616
|
-
ruleVersion:
|
|
6728
|
+
ruleId: RULE_ID10,
|
|
6729
|
+
ruleVersion: RULE_VERSION11,
|
|
6617
6730
|
severity: policy.severity,
|
|
6618
6731
|
message: dimensionMessage(inline.value, inline.property, preferLabel, resolution, true),
|
|
6619
6732
|
location: node.location,
|
|
@@ -6883,8 +6996,8 @@ function parseLengthParts(raw) {
|
|
|
6883
6996
|
}
|
|
6884
6997
|
|
|
6885
6998
|
// src/rules/styles-no-raw-spacing.ts
|
|
6886
|
-
var
|
|
6887
|
-
var
|
|
6999
|
+
var RULE_ID11 = "styles/no-raw-spacing";
|
|
7000
|
+
var RULE_VERSION12 = "1";
|
|
6888
7001
|
function ruleStylesNoRawSpacing(ix) {
|
|
6889
7002
|
const findings = [];
|
|
6890
7003
|
const lookupCache = /* @__PURE__ */ new Map();
|
|
@@ -6926,8 +7039,8 @@ function checkDeclaration(ix, decl, lookupFor) {
|
|
|
6926
7039
|
if (!checked) return null;
|
|
6927
7040
|
const fixEmission = buildSpacingFix(decl.property, checked, ix);
|
|
6928
7041
|
return makeFinding({
|
|
6929
|
-
ruleId:
|
|
6930
|
-
ruleVersion:
|
|
7042
|
+
ruleId: RULE_ID11,
|
|
7043
|
+
ruleVersion: RULE_VERSION12,
|
|
6931
7044
|
severity: policy.severity,
|
|
6932
7045
|
message: spacingMessage(decl.property, decl.value, allowed, scale.unit, checked),
|
|
6933
7046
|
location: decl.location,
|
|
@@ -6981,8 +7094,8 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
|
|
|
6981
7094
|
const componentEvidenceId = componentByNode.get(node.id)?.id;
|
|
6982
7095
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
6983
7096
|
return makeFinding({
|
|
6984
|
-
ruleId:
|
|
6985
|
-
ruleVersion:
|
|
7097
|
+
ruleId: RULE_ID11,
|
|
7098
|
+
ruleVersion: RULE_VERSION12,
|
|
6986
7099
|
severity: policy.severity,
|
|
6987
7100
|
message: spacingMessage(inline.property, inline.value, allowed, scale.unit, checked),
|
|
6988
7101
|
location: node.location,
|
|
@@ -7043,8 +7156,8 @@ function spacingMessage(property, value, allowed, unit, checked) {
|
|
|
7043
7156
|
}
|
|
7044
7157
|
|
|
7045
7158
|
// src/rules/styles-no-raw-typography.ts
|
|
7046
|
-
var
|
|
7047
|
-
var
|
|
7159
|
+
var RULE_ID12 = "styles/no-raw-typography";
|
|
7160
|
+
var RULE_VERSION13 = "1";
|
|
7048
7161
|
var FONT_SIZE_PROPERTIES = /* @__PURE__ */ new Set(["font-size", "fontsize"]);
|
|
7049
7162
|
function ruleStylesNoRawTypography(ix) {
|
|
7050
7163
|
const policy = ix.policy.fontSizeScale();
|
|
@@ -7062,8 +7175,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7062
7175
|
const fixEmission = buildFix(decl.property, checked, ix);
|
|
7063
7176
|
findings.push(
|
|
7064
7177
|
makeFinding({
|
|
7065
|
-
ruleId:
|
|
7066
|
-
ruleVersion:
|
|
7178
|
+
ruleId: RULE_ID12,
|
|
7179
|
+
ruleVersion: RULE_VERSION13,
|
|
7067
7180
|
severity: policy.severity,
|
|
7068
7181
|
message: typographyMessage(decl.property, decl.value, allowed, scale.unit, checked),
|
|
7069
7182
|
location: decl.location,
|
|
@@ -7109,8 +7222,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7109
7222
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
7110
7223
|
findings.push(
|
|
7111
7224
|
makeFinding({
|
|
7112
|
-
ruleId:
|
|
7113
|
-
ruleVersion:
|
|
7225
|
+
ruleId: RULE_ID12,
|
|
7226
|
+
ruleVersion: RULE_VERSION13,
|
|
7114
7227
|
severity: policy.severity,
|
|
7115
7228
|
message: typographyMessage(inline.property, inline.value, allowed, scale.unit, checked),
|
|
7116
7229
|
location: node.location,
|
|
@@ -7212,15 +7325,15 @@ function typographyMessage(property, value, allowed, unit, checked) {
|
|
|
7212
7325
|
}
|
|
7213
7326
|
|
|
7214
7327
|
// src/rules/theme-no-theme-coupled-literal.ts
|
|
7215
|
-
var
|
|
7216
|
-
var
|
|
7328
|
+
var RULE_ID13 = "theme/no-theme-coupled-literal";
|
|
7329
|
+
var RULE_VERSION14 = "1";
|
|
7217
7330
|
var COLOR_LITERAL_GLOBAL = /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?|oklab|oklch|lab|lch)\(\s*[\d.%\s,\/]+\s*\)/g;
|
|
7218
7331
|
var WHITE_RGBA = /^rgba?\(\s*255\s*,\s*255\s*,\s*255/i;
|
|
7219
7332
|
var BLACK_RGBA = /^rgba?\(\s*0\s*,\s*0\s*,\s*0/i;
|
|
7220
7333
|
var BOX_SHADOW_PROP = /^(?:-webkit-|-moz-)?box-shadow$/i;
|
|
7221
7334
|
var NEUTRAL_SHADOW = /^(?:none|inherit|initial|unset|revert|revert-layer)\s*$/i;
|
|
7222
7335
|
function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
7223
|
-
const policy = ix.policy.ruleConfig(
|
|
7336
|
+
const policy = ix.policy.ruleConfig(RULE_ID13);
|
|
7224
7337
|
if (!policy?.enabled) return [];
|
|
7225
7338
|
const findings = [];
|
|
7226
7339
|
for (const decl of ix.byKind("style_declaration")) {
|
|
@@ -7229,8 +7342,8 @@ function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
|
7229
7342
|
if (!hit) continue;
|
|
7230
7343
|
findings.push(
|
|
7231
7344
|
makeFinding({
|
|
7232
|
-
ruleId:
|
|
7233
|
-
ruleVersion:
|
|
7345
|
+
ruleId: RULE_ID13,
|
|
7346
|
+
ruleVersion: RULE_VERSION14,
|
|
7234
7347
|
severity: policy.severity ?? "warn",
|
|
7235
7348
|
message: hit.message,
|
|
7236
7349
|
location: decl.location,
|
|
@@ -7320,8 +7433,8 @@ function isTokenFile(filePath) {
|
|
|
7320
7433
|
}
|
|
7321
7434
|
|
|
7322
7435
|
// src/rules/tailwind-arbitrary-color.ts
|
|
7323
|
-
var
|
|
7324
|
-
var
|
|
7436
|
+
var RULE_ID14 = "tailwind/arbitrary-color";
|
|
7437
|
+
var RULE_VERSION15 = "1";
|
|
7325
7438
|
var COLOR_UTILITIES = /* @__PURE__ */ new Set([
|
|
7326
7439
|
"accent",
|
|
7327
7440
|
"bg",
|
|
@@ -7363,8 +7476,8 @@ function ruleTailwindArbitraryColor(ix) {
|
|
|
7363
7476
|
const evidenceIds = node ? [node.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
7364
7477
|
findings.push(
|
|
7365
7478
|
makeFinding({
|
|
7366
|
-
ruleId:
|
|
7367
|
-
ruleVersion:
|
|
7479
|
+
ruleId: RULE_ID14,
|
|
7480
|
+
ruleVersion: RULE_VERSION15,
|
|
7368
7481
|
severity: policy.severity,
|
|
7369
7482
|
message: `\`${klass.raw}\` uses arbitrary raw color ${color}. Use a ${policy.prefer === "token" ? "design token" : "CSS variable"} instead.`,
|
|
7370
7483
|
location: klass.location,
|
|
@@ -7395,8 +7508,8 @@ function fingerprintIdentity(klass, color) {
|
|
|
7395
7508
|
}
|
|
7396
7509
|
|
|
7397
7510
|
// src/rules/tailwind-arbitrary-spacing.ts
|
|
7398
|
-
var
|
|
7399
|
-
var
|
|
7511
|
+
var RULE_ID15 = "tailwind/arbitrary-spacing";
|
|
7512
|
+
var RULE_VERSION16 = "1";
|
|
7400
7513
|
var SPACING_UTILITY_TO_PROPERTY = /* @__PURE__ */ new Map([
|
|
7401
7514
|
["m", "margin"],
|
|
7402
7515
|
["mt", "margin"],
|
|
@@ -7442,8 +7555,8 @@ function ruleTailwindArbitrarySpacing(ix) {
|
|
|
7442
7555
|
const fix = buildFix2(klass);
|
|
7443
7556
|
findings.push(
|
|
7444
7557
|
makeFinding({
|
|
7445
|
-
ruleId:
|
|
7446
|
-
ruleVersion:
|
|
7558
|
+
ruleId: RULE_ID15,
|
|
7559
|
+
ruleVersion: RULE_VERSION16,
|
|
7447
7560
|
severity: policy.severity,
|
|
7448
7561
|
message: `\`${klass.raw}\` is an arbitrary spacing value not on the project's spacing scale.`,
|
|
7449
7562
|
location: klass.location,
|
|
@@ -7574,8 +7687,8 @@ function matchesTokenGlob(value, pattern) {
|
|
|
7574
7687
|
}
|
|
7575
7688
|
|
|
7576
7689
|
// src/rules/tailwind-forbidden-palette.ts
|
|
7577
|
-
var
|
|
7578
|
-
var
|
|
7690
|
+
var RULE_ID16 = "tailwind/forbidden-palette";
|
|
7691
|
+
var RULE_VERSION17 = "1";
|
|
7579
7692
|
function ruleTailwindForbiddenPalette(ix) {
|
|
7580
7693
|
const deny = ix.policy.tailwindPaletteDeny();
|
|
7581
7694
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
@@ -7600,8 +7713,8 @@ function ruleTailwindForbiddenPalette(ix) {
|
|
|
7600
7713
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
7601
7714
|
findings.push(
|
|
7602
7715
|
makeFinding({
|
|
7603
|
-
ruleId:
|
|
7604
|
-
ruleVersion:
|
|
7716
|
+
ruleId: RULE_ID16,
|
|
7717
|
+
ruleVersion: RULE_VERSION17,
|
|
7605
7718
|
severity: policy.severity,
|
|
7606
7719
|
message: messageFor(klass, token, deniedPattern, allow?.patterns ?? []),
|
|
7607
7720
|
location: klass.location,
|
|
@@ -7648,8 +7761,8 @@ function buildFix3(klass) {
|
|
|
7648
7761
|
}
|
|
7649
7762
|
|
|
7650
7763
|
// src/rules/tailwind-off-scale-spacing-token.ts
|
|
7651
|
-
var
|
|
7652
|
-
var
|
|
7764
|
+
var RULE_ID17 = "tailwind/off-scale-spacing-token";
|
|
7765
|
+
var RULE_VERSION18 = "1";
|
|
7653
7766
|
function ruleTailwindOffScaleSpacingToken(ix) {
|
|
7654
7767
|
const resolvedByKey = indexResolvedTailwindTokens(ix);
|
|
7655
7768
|
const findings = [];
|
|
@@ -7675,8 +7788,8 @@ function ruleTailwindOffScaleSpacingToken(ix) {
|
|
|
7675
7788
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id, scale.id] : [klass.id, resolved.id, policy.id, scale.id];
|
|
7676
7789
|
findings.push(
|
|
7677
7790
|
makeFinding({
|
|
7678
|
-
ruleId:
|
|
7679
|
-
ruleVersion:
|
|
7791
|
+
ruleId: RULE_ID17,
|
|
7792
|
+
ruleVersion: RULE_VERSION18,
|
|
7680
7793
|
severity: policy.severity,
|
|
7681
7794
|
message: `\`${klass.raw}\` resolves to ${formatResolvedValue(signedValue, scale.unit)}, which is not on the project's spacing scale [${allowed.join(", ")}].`,
|
|
7682
7795
|
location: klass.location,
|
|
@@ -7725,8 +7838,8 @@ function buildFix4(raw, utility) {
|
|
|
7725
7838
|
}
|
|
7726
7839
|
|
|
7727
7840
|
// src/rules/tailwind-raw-color-via-token.ts
|
|
7728
|
-
var
|
|
7729
|
-
var
|
|
7841
|
+
var RULE_ID18 = "tailwind/raw-color-via-token";
|
|
7842
|
+
var RULE_VERSION19 = "1";
|
|
7730
7843
|
function ruleTailwindRawColorViaToken(ix) {
|
|
7731
7844
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
7732
7845
|
if (!allow || allow.patterns.length === 0) return [];
|
|
@@ -7746,8 +7859,8 @@ function ruleTailwindRawColorViaToken(ix) {
|
|
|
7746
7859
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, allow.id] : [klass.id, resolved.id, allow.id];
|
|
7747
7860
|
findings.push(
|
|
7748
7861
|
makeFinding({
|
|
7749
|
-
ruleId:
|
|
7750
|
-
ruleVersion:
|
|
7862
|
+
ruleId: RULE_ID18,
|
|
7863
|
+
ruleVersion: RULE_VERSION19,
|
|
7751
7864
|
severity: allow.severity,
|
|
7752
7865
|
message: `\`${klass.raw}\` resolves to raw color \`${color}\` (${sourceLabel2(resolved.resolved.source)}). Project policy requires \`${allow.patterns.join("`, `")}\` palette tokens.`,
|
|
7753
7866
|
location: klass.location,
|
|
@@ -7780,8 +7893,8 @@ function sourceLabel2(source) {
|
|
|
7780
7893
|
}
|
|
7781
7894
|
|
|
7782
7895
|
// src/rules/tailwind-unknown-class.ts
|
|
7783
|
-
var
|
|
7784
|
-
var
|
|
7896
|
+
var RULE_ID19 = "tailwind/unknown-class";
|
|
7897
|
+
var RULE_VERSION20 = "1";
|
|
7785
7898
|
var UNIVERSAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
7786
7899
|
"white",
|
|
7787
7900
|
"black",
|
|
@@ -7802,8 +7915,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
7802
7915
|
const evidenceIds2 = node2 ? [node2.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
7803
7916
|
findings.push(
|
|
7804
7917
|
makeFinding({
|
|
7805
|
-
ruleId:
|
|
7806
|
-
ruleVersion:
|
|
7918
|
+
ruleId: RULE_ID19,
|
|
7919
|
+
ruleVersion: RULE_VERSION20,
|
|
7807
7920
|
severity: policy.severity,
|
|
7808
7921
|
message: `\`${klass.raw}\` could not be parsed as a known Tailwind utility.`,
|
|
7809
7922
|
location: klass.location,
|
|
@@ -7837,8 +7950,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
7837
7950
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
7838
7951
|
findings.push(
|
|
7839
7952
|
makeFinding({
|
|
7840
|
-
ruleId:
|
|
7841
|
-
ruleVersion:
|
|
7953
|
+
ruleId: RULE_ID19,
|
|
7954
|
+
ruleVersion: RULE_VERSION20,
|
|
7842
7955
|
severity: policy.severity,
|
|
7843
7956
|
message: `\`${klass.raw}\` did not resolve to any known palette token.`,
|
|
7844
7957
|
location: klass.location,
|
|
@@ -7866,12 +7979,12 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
7866
7979
|
}
|
|
7867
7980
|
|
|
7868
7981
|
// src/rules/tokens-require-dual-fallback.ts
|
|
7869
|
-
var
|
|
7870
|
-
var
|
|
7982
|
+
var RULE_ID20 = "tokens/require-dual-fallback";
|
|
7983
|
+
var RULE_VERSION21 = "1";
|
|
7871
7984
|
var CSS_VAR_WITH_OPTIONAL_FALLBACK = /var\(\s*(--[A-Za-z0-9_-]+)(\s*,\s*[^)]*)?\)/g;
|
|
7872
7985
|
var SASS_FILE = /\.(scss|sass)$/i;
|
|
7873
7986
|
function ruleTokensRequireDualFallback(ix) {
|
|
7874
|
-
const policy = ix.policy.ruleConfig(
|
|
7987
|
+
const policy = ix.policy.ruleConfig(RULE_ID20);
|
|
7875
7988
|
if (!policy?.enabled) return [];
|
|
7876
7989
|
const tokensByCssVariable = indexTokensByCssVariable(ix.tokens.list());
|
|
7877
7990
|
const findings = [];
|
|
@@ -7906,8 +8019,8 @@ function ruleTokensRequireDualFallback(ix) {
|
|
|
7906
8019
|
) : void 0;
|
|
7907
8020
|
findings.push(
|
|
7908
8021
|
makeFinding({
|
|
7909
|
-
ruleId:
|
|
7910
|
-
ruleVersion:
|
|
8022
|
+
ruleId: RULE_ID20,
|
|
8023
|
+
ruleVersion: RULE_VERSION21,
|
|
7911
8024
|
severity: policy.severity ?? "warn",
|
|
7912
8025
|
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}.`,
|
|
7913
8026
|
location: decl.location,
|
|
@@ -7947,8 +8060,8 @@ function indexTokensByCssVariable(tokens) {
|
|
|
7947
8060
|
}
|
|
7948
8061
|
|
|
7949
8062
|
// src/rules/tokens-css-vars-must-be-defined.ts
|
|
7950
|
-
var
|
|
7951
|
-
var
|
|
8063
|
+
var RULE_ID21 = "tokens/css-vars-must-be-defined";
|
|
8064
|
+
var RULE_VERSION22 = "1";
|
|
7952
8065
|
var CSS_VAR_REFERENCE = /var\(\s*(--[A-Za-z0-9_-]+)/gi;
|
|
7953
8066
|
function ruleTokensCssVarsMustBeDefined(ix) {
|
|
7954
8067
|
const policy = ix.policy.cssVarsMustBeDefined();
|
|
@@ -7975,8 +8088,8 @@ function ruleTokensCssVarsMustBeDefined(ix) {
|
|
|
7975
8088
|
seen.add(tokenName);
|
|
7976
8089
|
findings.push(
|
|
7977
8090
|
makeFinding({
|
|
7978
|
-
ruleId:
|
|
7979
|
-
ruleVersion:
|
|
8091
|
+
ruleId: RULE_ID21,
|
|
8092
|
+
ruleVersion: RULE_VERSION22,
|
|
7980
8093
|
severity: policy.severity,
|
|
7981
8094
|
message: `var(${tokenName}) is not in the contract token vocabulary. Use a token defined in your contract's token source files.`,
|
|
7982
8095
|
location: decl.location,
|
|
@@ -8056,6 +8169,8 @@ function isDenyEligible(finding, failOnWarnings) {
|
|
|
8056
8169
|
// src/rules/tiers.ts
|
|
8057
8170
|
var RULE_TIER = {
|
|
8058
8171
|
// ---- Tier A — contract vocabulary -------------------------------------
|
|
8172
|
+
"components/shadow-component": "contract",
|
|
8173
|
+
// canonical-component identity drift
|
|
8059
8174
|
"components/prefer-library": "contract",
|
|
8060
8175
|
// canonical-component bypass (FUI1004)
|
|
8061
8176
|
"tokens/css-vars-must-be-defined": "contract",
|
|
@@ -8101,6 +8216,7 @@ function hygieneTierRuleIds() {
|
|
|
8101
8216
|
return Object.keys(RULE_TIER).filter((id) => !isContractTierRule(id));
|
|
8102
8217
|
}
|
|
8103
8218
|
var VOCABULARY_ORDER = [
|
|
8219
|
+
"components/shadow-component",
|
|
8104
8220
|
"components/prefer-library",
|
|
8105
8221
|
"tokens/css-vars-must-be-defined"
|
|
8106
8222
|
];
|
|
@@ -8161,6 +8277,11 @@ var RULES = [
|
|
|
8161
8277
|
version: "1",
|
|
8162
8278
|
run: ruleComponentsPreferLibrary
|
|
8163
8279
|
},
|
|
8280
|
+
{
|
|
8281
|
+
id: "components/shadow-component",
|
|
8282
|
+
version: "1",
|
|
8283
|
+
run: ruleComponentsShadowComponent
|
|
8284
|
+
},
|
|
8164
8285
|
{
|
|
8165
8286
|
id: "styles/no-raw-color",
|
|
8166
8287
|
version: "1",
|
|
@@ -8755,6 +8876,133 @@ function readPreferLibraryRule(config) {
|
|
|
8755
8876
|
const raw = config?.rules?.["components/prefer-library"];
|
|
8756
8877
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
8757
8878
|
}
|
|
8879
|
+
|
|
8880
|
+
// src/identity/component-key.ts
|
|
8881
|
+
function buildComponentKey({ repoRelativeFile, exportName }) {
|
|
8882
|
+
const file = normalizeRepoRelativeFile(repoRelativeFile);
|
|
8883
|
+
const name = exportName.trim();
|
|
8884
|
+
if (!file) throw new Error("repoRelativeFile must not be empty");
|
|
8885
|
+
if (!name) throw new Error("exportName must not be empty");
|
|
8886
|
+
return `${file}#${name}`;
|
|
8887
|
+
}
|
|
8888
|
+
function normalizeRepoRelativeFile(file) {
|
|
8889
|
+
const slashNormalized = file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
8890
|
+
const absolute = slashNormalized.startsWith("/");
|
|
8891
|
+
const parts = [];
|
|
8892
|
+
for (const part of slashNormalized.split("/")) {
|
|
8893
|
+
if (!part || part === ".") continue;
|
|
8894
|
+
if (part === ".." && parts.length > 0 && parts.at(-1) !== "..") {
|
|
8895
|
+
parts.pop();
|
|
8896
|
+
continue;
|
|
8897
|
+
}
|
|
8898
|
+
parts.push(part);
|
|
8899
|
+
}
|
|
8900
|
+
return `${absolute ? "/" : ""}${parts.join("/")}`;
|
|
8901
|
+
}
|
|
8902
|
+
|
|
8903
|
+
// src/identity/classify.ts
|
|
8904
|
+
function classifyIdentity(input) {
|
|
8905
|
+
const { definition, renderRoot } = input;
|
|
8906
|
+
if (definition.canonical) return { state: "canonical", confidence: "confirmed" };
|
|
8907
|
+
const decision = input.decisions?.find(
|
|
8908
|
+
(candidate) => candidate.componentKey === definition.componentKey
|
|
8909
|
+
);
|
|
8910
|
+
if (decision?.decision === "sanctioned-wrapper") {
|
|
8911
|
+
return withTarget("variant", "confirmed", decision.canonicalTarget ?? rootTarget(input));
|
|
8912
|
+
}
|
|
8913
|
+
if (decision?.decision === "rejected-wrapper") {
|
|
8914
|
+
return withTarget("shadow", "confirmed", decision.canonicalTarget ?? shadowTarget(input));
|
|
8915
|
+
}
|
|
8916
|
+
if (renderRoot.kind === "canonical") {
|
|
8917
|
+
if ((input.usageCounts?.nestedElements ?? 0) > 0 || (input.usageCounts?.staticTextChildren ?? 0) > 0) {
|
|
8918
|
+
return withTarget("composite", "confirmed", renderRoot.target.componentKey);
|
|
8919
|
+
}
|
|
8920
|
+
const confidence = strongOverlap(input.propSurfaceOverlap) ? "likely" : "review";
|
|
8921
|
+
return withTarget("variant", confidence, renderRoot.target.componentKey);
|
|
8922
|
+
}
|
|
8923
|
+
if (renderRoot.kind === "intrinsic") {
|
|
8924
|
+
return classifyIntrinsic(input, renderRoot);
|
|
8925
|
+
}
|
|
8926
|
+
if (renderRoot.kind === "mixed") {
|
|
8927
|
+
if (renderRoot.canonicalTargets.length > 0) {
|
|
8928
|
+
const target = bestCanonicalReference(
|
|
8929
|
+
definition.exportName,
|
|
8930
|
+
renderRoot.canonicalTargets
|
|
8931
|
+
)?.componentKey;
|
|
8932
|
+
const fullyResolved = renderRoot.externalTargets.length === 0 && renderRoot.unresolvedNames.length === 0;
|
|
8933
|
+
return withTarget("composite", fullyResolved ? "confirmed" : "likely", target);
|
|
8934
|
+
}
|
|
8935
|
+
if (renderRoot.externalTargets.length > 0 && renderRoot.localTargets.length === 0 && renderRoot.unresolvedNames.length === 0) {
|
|
8936
|
+
return { state: "external", confidence: "likely" };
|
|
8937
|
+
}
|
|
8938
|
+
return withTarget("unresolved", "review", rootTarget(input));
|
|
8939
|
+
}
|
|
8940
|
+
if (renderRoot.kind === "external") {
|
|
8941
|
+
return { state: "external", confidence: "confirmed" };
|
|
8942
|
+
}
|
|
8943
|
+
return withTarget("unresolved", "review", rootTarget(input));
|
|
8944
|
+
}
|
|
8945
|
+
function classifyIntrinsic(input, root) {
|
|
8946
|
+
const target = bestCanonicalReference(input.definition.exportName, root.canonicalTargets);
|
|
8947
|
+
if (!target || !root.interactive && !root.hasRole) {
|
|
8948
|
+
return withTarget("unresolved", "review", rootTarget(input));
|
|
8949
|
+
}
|
|
8950
|
+
let confidence = root.precisionTier === "exact-html" || root.precisionTier === "role-reimpl" ? "confirmed" : "likely";
|
|
8951
|
+
if (confidence === "likely" && input.stylingOwnership !== "layout-only" && (strongOverlap(input.propSurfaceOverlap) || input.stylingOwnership === "visual-role")) {
|
|
8952
|
+
confidence = "confirmed";
|
|
8953
|
+
}
|
|
8954
|
+
return { state: "shadow", confidence, canonicalTarget: target.role };
|
|
8955
|
+
}
|
|
8956
|
+
function rootTarget(input) {
|
|
8957
|
+
const { renderRoot } = input;
|
|
8958
|
+
if (renderRoot.kind === "canonical") return renderRoot.target.componentKey;
|
|
8959
|
+
if (renderRoot.kind === "intrinsic") {
|
|
8960
|
+
return bestCanonicalReference(input.definition.exportName, renderRoot.canonicalTargets)?.role;
|
|
8961
|
+
}
|
|
8962
|
+
if (renderRoot.kind === "mixed") {
|
|
8963
|
+
return bestCanonicalReference(input.definition.exportName, renderRoot.canonicalTargets)?.componentKey;
|
|
8964
|
+
}
|
|
8965
|
+
return bestMessageTarget(input.definition.exportName, input.nameSimilarityTargets ?? []);
|
|
8966
|
+
}
|
|
8967
|
+
function shadowTarget(input) {
|
|
8968
|
+
const { renderRoot } = input;
|
|
8969
|
+
if (renderRoot.kind === "canonical") return renderRoot.target.role;
|
|
8970
|
+
if (renderRoot.kind === "intrinsic") {
|
|
8971
|
+
return bestCanonicalReference(input.definition.exportName, renderRoot.canonicalTargets)?.role;
|
|
8972
|
+
}
|
|
8973
|
+
if (renderRoot.kind === "mixed") {
|
|
8974
|
+
return bestCanonicalReference(input.definition.exportName, renderRoot.canonicalTargets)?.role;
|
|
8975
|
+
}
|
|
8976
|
+
return bestMessageTarget(input.definition.exportName, input.nameSimilarityTargets ?? []);
|
|
8977
|
+
}
|
|
8978
|
+
function bestCanonicalReference(definitionName, candidates) {
|
|
8979
|
+
return [...candidates].sort(
|
|
8980
|
+
(left, right) => nameSimilarityScore(definitionName, right.role) - nameSimilarityScore(definitionName, left.role) || left.componentKey.localeCompare(right.componentKey)
|
|
8981
|
+
)[0];
|
|
8982
|
+
}
|
|
8983
|
+
function bestMessageTarget(definitionName, candidates) {
|
|
8984
|
+
return [...candidates].sort(
|
|
8985
|
+
(left, right) => nameSimilarityScore(definitionName, right) - nameSimilarityScore(definitionName, left) || left.localeCompare(right)
|
|
8986
|
+
)[0];
|
|
8987
|
+
}
|
|
8988
|
+
function nameSimilarityScore(definitionName, target) {
|
|
8989
|
+
const definition = normalizeName(definitionName);
|
|
8990
|
+
const candidate = normalizeName(target);
|
|
8991
|
+
if (!candidate) return 0;
|
|
8992
|
+
if (definition === candidate) return 3;
|
|
8993
|
+
if (definition.endsWith(candidate) || definition.startsWith(candidate)) return 2;
|
|
8994
|
+
if (definition.includes(candidate) || candidate.includes(definition)) return 1;
|
|
8995
|
+
return 0;
|
|
8996
|
+
}
|
|
8997
|
+
function normalizeName(value) {
|
|
8998
|
+
return value.split(/[.#/]/).at(-1)?.replace(/[^a-z0-9]/gi, "").toLowerCase() ?? "";
|
|
8999
|
+
}
|
|
9000
|
+
function strongOverlap(overlap) {
|
|
9001
|
+
return overlap !== void 0 && overlap >= 0.7;
|
|
9002
|
+
}
|
|
9003
|
+
function withTarget(state, confidence, canonicalTarget) {
|
|
9004
|
+
return canonicalTarget ? { state, confidence, canonicalTarget } : { state, confidence };
|
|
9005
|
+
}
|
|
8758
9006
|
export {
|
|
8759
9007
|
AGENT_CONTEXT_RELATIVE_PATH,
|
|
8760
9008
|
AGENT_FORMAT_SCHEMA_VERSION,
|
|
@@ -8805,6 +9053,7 @@ export {
|
|
|
8805
9053
|
bridgeSourceViolations,
|
|
8806
9054
|
budgetBar,
|
|
8807
9055
|
buildAgentFormat,
|
|
9056
|
+
buildComponentKey,
|
|
8808
9057
|
buildRegistryArtifact,
|
|
8809
9058
|
buildRegistryFile,
|
|
8810
9059
|
buildRegistryInstallPlan,
|
|
@@ -8831,6 +9080,7 @@ export {
|
|
|
8831
9080
|
checkStoryExclusion,
|
|
8832
9081
|
clampMaxPasses,
|
|
8833
9082
|
classifyComplexity,
|
|
9083
|
+
classifyIdentity,
|
|
8834
9084
|
collectRegistryInstallDependencies,
|
|
8835
9085
|
colorSimilarity,
|
|
8836
9086
|
compareByVocabularyRank,
|
|
@@ -8926,6 +9176,7 @@ export {
|
|
|
8926
9176
|
hashRegistryFileContent,
|
|
8927
9177
|
hexToRgb,
|
|
8928
9178
|
hygieneTierRuleIds,
|
|
9179
|
+
indexComponentIdentityUsage,
|
|
8929
9180
|
inferTokenCategory,
|
|
8930
9181
|
inferTokenGroup,
|
|
8931
9182
|
isColorLike,
|
|
@@ -8952,6 +9203,8 @@ export {
|
|
|
8952
9203
|
makeClassNameDynamicFact,
|
|
8953
9204
|
makeClassNameLiteralFact,
|
|
8954
9205
|
makeComponentCapabilityFact,
|
|
9206
|
+
makeComponentDefinitionFact,
|
|
9207
|
+
makeComponentIdentityFact,
|
|
8955
9208
|
makeComponentMetadataFact,
|
|
8956
9209
|
makeContractTokenFact,
|
|
8957
9210
|
makeFinding,
|
|
@@ -9076,6 +9329,7 @@ export {
|
|
|
9076
9329
|
ruleA11yRequiredAccessibleName,
|
|
9077
9330
|
ruleComponentsForbiddenPropValue,
|
|
9078
9331
|
ruleComponentsPreferLibrary,
|
|
9332
|
+
ruleComponentsShadowComponent,
|
|
9079
9333
|
ruleComponentsUnknownProp,
|
|
9080
9334
|
ruleJsxPreferredComponent,
|
|
9081
9335
|
ruleJsxPreferredImportPath,
|