@usefragments/core 1.3.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-MLRDNSSA.js → chunk-HXGE3O2F.js} +39 -1
- package/dist/chunk-HXGE3O2F.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/generate/index.d.ts +1 -1
- package/dist/{governance-DYSirwJu.d.ts → governance-BLsyk56o.d.ts} +21 -0
- package/dist/index.d.ts +324 -82
- package/dist/index.js +507 -120
- package/dist/index.js.map +1 -1
- package/dist/react-types.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/constants.ts +3 -0
- package/src/facts/builders.ts +46 -2
- package/src/facts/facts.test.ts +30 -0
- package/src/facts/index.ts +6 -0
- package/src/facts/types.ts +75 -2
- 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 +196 -0
- package/src/rules/components-prefer-library.ts +68 -1
- 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/styles-no-raw-color.test.ts +213 -0
- package/src/rules/styles-no-raw-color.ts +125 -0
- package/src/rules/taxonomy.test.ts +16 -1
- package/src/rules/tiers.ts +7 -5
- package/src/schema.ts +21 -2
- package/src/token-types.ts +12 -0
- package/src/tokens/categories.ts +54 -24
- package/src/tokens/scss.test.ts +94 -0
- package/src/tokens/scss.ts +11 -4
- package/src/types.ts +23 -0
- package/dist/chunk-MLRDNSSA.js.map +0 -1
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,
|
|
@@ -185,6 +187,8 @@ var BRAND = {
|
|
|
185
187
|
legacyConfigFile: "segments.config.ts",
|
|
186
188
|
/** CLI command name (e.g., "fragments") */
|
|
187
189
|
cliCommand: "fragments",
|
|
190
|
+
/** Full user-facing CLI invocation for copy printed to end users (e.g., "npx @usefragments/cli") */
|
|
191
|
+
cliInvocation: "npx @usefragments/cli",
|
|
188
192
|
/** Directory for storing fragments, registry, and cache */
|
|
189
193
|
dataDir: ".fragments",
|
|
190
194
|
/** Components subdirectory within .fragments/ */
|
|
@@ -1679,16 +1683,20 @@ var tokenSourceSchema = z4.object({
|
|
|
1679
1683
|
var tokenConfigSchema = z4.object({
|
|
1680
1684
|
include: repoRelativePathsSchema.optional(),
|
|
1681
1685
|
sources: z4.array(tokenSourceSchema).min(1).optional(),
|
|
1686
|
+
// Declared design-system packages whose shipped fragments.json token
|
|
1687
|
+
// vocabulary seeds the known-token set (#7c). A packages-only tokens block
|
|
1688
|
+
// is valid — see the superRefine below.
|
|
1689
|
+
packages: z4.array(z4.string().min(1)).optional(),
|
|
1682
1690
|
exclude: z4.array(repoRelativePathSchema).optional(),
|
|
1683
1691
|
themeSelectors: z4.record(z4.string()).optional(),
|
|
1684
1692
|
enabled: z4.boolean().optional(),
|
|
1685
1693
|
format: z4.enum(["auto", "css", "scss", "dtcg", "tailwind"]).optional(),
|
|
1686
1694
|
namespace: z4.string().min(1).optional()
|
|
1687
1695
|
}).passthrough().superRefine((tokens, ctx) => {
|
|
1688
|
-
if (!tokens.include?.length && !tokens.sources?.length) {
|
|
1696
|
+
if (!tokens.include?.length && !tokens.sources?.length && !tokens.packages?.length) {
|
|
1689
1697
|
ctx.addIssue({
|
|
1690
1698
|
code: z4.ZodIssueCode.custom,
|
|
1691
|
-
message: "Add tokens.include or tokens.
|
|
1699
|
+
message: "Add tokens.include, tokens.sources, or tokens.packages",
|
|
1692
1700
|
path: ["sources"]
|
|
1693
1701
|
});
|
|
1694
1702
|
}
|
|
@@ -1770,7 +1778,18 @@ var fragmentsConfigSchema = z4.object({
|
|
|
1770
1778
|
})
|
|
1771
1779
|
).optional()
|
|
1772
1780
|
}).optional(),
|
|
1773
|
-
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()
|
|
1774
1793
|
});
|
|
1775
1794
|
var compositionMetadataSchema = z4.object({
|
|
1776
1795
|
pattern: z4.enum(["compound", "simple", "controlled", "wrapper"]).optional(),
|
|
@@ -2931,30 +2950,41 @@ function inferTokenGroup(name) {
|
|
|
2931
2950
|
return category;
|
|
2932
2951
|
}
|
|
2933
2952
|
}
|
|
2953
|
+
var TOKEN_GROUP_COMMENT_MAPPINGS = {
|
|
2954
|
+
"base configuration": "base",
|
|
2955
|
+
typography: "typography",
|
|
2956
|
+
"spacing (micro)": "spacing",
|
|
2957
|
+
spacing: "spacing",
|
|
2958
|
+
"density padding": "spacing",
|
|
2959
|
+
"border radius": "radius",
|
|
2960
|
+
transitions: "transitions",
|
|
2961
|
+
colors: "colors",
|
|
2962
|
+
surfaces: "surfaces",
|
|
2963
|
+
text: "text",
|
|
2964
|
+
borders: "borders",
|
|
2965
|
+
shadows: "shadows",
|
|
2966
|
+
focus: "focus",
|
|
2967
|
+
scrollbar: "scrollbar",
|
|
2968
|
+
"component heights": "component-sizing",
|
|
2969
|
+
"appshell layout": "layout",
|
|
2970
|
+
codeblock: "code",
|
|
2971
|
+
tooltip: "tooltip",
|
|
2972
|
+
"hero/marketing gradient": "marketing"
|
|
2973
|
+
};
|
|
2974
|
+
function looksLikeCleanHeading(text) {
|
|
2975
|
+
if (!/^[a-z0-9][a-z0-9 -]*$/.test(text)) return false;
|
|
2976
|
+
const wordCount = text.split(/\s+/).filter(Boolean).length;
|
|
2977
|
+
return wordCount > 0 && wordCount <= 3;
|
|
2978
|
+
}
|
|
2934
2979
|
function normalizeTokenGroupComment(comment) {
|
|
2935
2980
|
const text = comment.trim().replace(/^\/\/\s*/, "").replace(/^\/\*+\s*/, "").replace(/\s*\*+\/$/, "").trim().toLowerCase();
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
transitions: "transitions",
|
|
2944
|
-
colors: "colors",
|
|
2945
|
-
surfaces: "surfaces",
|
|
2946
|
-
text: "text",
|
|
2947
|
-
borders: "borders",
|
|
2948
|
-
shadows: "shadows",
|
|
2949
|
-
focus: "focus",
|
|
2950
|
-
scrollbar: "scrollbar",
|
|
2951
|
-
"component heights": "component-sizing",
|
|
2952
|
-
"appshell layout": "layout",
|
|
2953
|
-
codeblock: "code",
|
|
2954
|
-
tooltip: "tooltip",
|
|
2955
|
-
"hero/marketing gradient": "marketing"
|
|
2956
|
-
};
|
|
2957
|
-
return mappings[text] ?? text.replace(/\s+/g, "-");
|
|
2981
|
+
if (text in TOKEN_GROUP_COMMENT_MAPPINGS) {
|
|
2982
|
+
return TOKEN_GROUP_COMMENT_MAPPINGS[text];
|
|
2983
|
+
}
|
|
2984
|
+
if (!looksLikeCleanHeading(text)) {
|
|
2985
|
+
return null;
|
|
2986
|
+
}
|
|
2987
|
+
return text.replace(/\s+/g, "-");
|
|
2958
2988
|
}
|
|
2959
2989
|
|
|
2960
2990
|
// src/tokens/scss.ts
|
|
@@ -3021,7 +3051,7 @@ function parseScssTokens(content, filePath = "tokens.scss") {
|
|
|
3021
3051
|
const tokens = [];
|
|
3022
3052
|
const seenNames = /* @__PURE__ */ new Set();
|
|
3023
3053
|
let currentCategory = "other";
|
|
3024
|
-
let
|
|
3054
|
+
let currentCategoryIsCurated = false;
|
|
3025
3055
|
const scssVars = parseScssVariables(content);
|
|
3026
3056
|
const varDeclRegex = /^\s*(--[\w-]+)\s*:\s*(.+?)\s*;/;
|
|
3027
3057
|
const sectionCommentRegex = /^\s*\/\/\s+([A-Z].+)$/;
|
|
@@ -3031,7 +3061,9 @@ function parseScssTokens(content, filePath = "tokens.scss") {
|
|
|
3031
3061
|
const normalized = normalizeTokenGroupComment(commentMatch[0]);
|
|
3032
3062
|
if (normalized) {
|
|
3033
3063
|
currentCategory = normalized;
|
|
3034
|
-
|
|
3064
|
+
currentCategoryIsCurated = true;
|
|
3065
|
+
} else {
|
|
3066
|
+
currentCategoryIsCurated = false;
|
|
3035
3067
|
}
|
|
3036
3068
|
continue;
|
|
3037
3069
|
}
|
|
@@ -3047,7 +3079,7 @@ function parseScssTokens(content, filePath = "tokens.scss") {
|
|
|
3047
3079
|
tokens.push({
|
|
3048
3080
|
name,
|
|
3049
3081
|
value: cleanValue || void 0,
|
|
3050
|
-
category:
|
|
3082
|
+
category: currentCategoryIsCurated ? currentCategory : inferTokenGroup(name),
|
|
3051
3083
|
description
|
|
3052
3084
|
});
|
|
3053
3085
|
}
|
|
@@ -3058,7 +3090,7 @@ function parseScssTokens(content, filePath = "tokens.scss") {
|
|
|
3058
3090
|
tokens.push({
|
|
3059
3091
|
name,
|
|
3060
3092
|
value,
|
|
3061
|
-
category:
|
|
3093
|
+
category: currentCategoryIsCurated ? currentCategory : inferTokenGroup(name)
|
|
3062
3094
|
});
|
|
3063
3095
|
}
|
|
3064
3096
|
const cssVarValues = /* @__PURE__ */ new Map();
|
|
@@ -5057,11 +5089,15 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5057
5089
|
const textByNode = indexTextChildrenByNodeId(ix);
|
|
5058
5090
|
const classTokensByNode = indexClassTokensByNode(ix);
|
|
5059
5091
|
const reimplTargets = canonicalReimplTargets(sources, mappings);
|
|
5092
|
+
const canonicalDirectoryResolvedNodes = indexCanonicalDirectoryResolvedNodes(ix, sources);
|
|
5093
|
+
const shadowRenderRootNodeIds = indexShadowRenderRootNodeIds(ix);
|
|
5060
5094
|
const findings = [];
|
|
5061
5095
|
const seenImportFixes = /* @__PURE__ */ new Set();
|
|
5062
5096
|
for (const node of ix.byKind("usage_node")) {
|
|
5063
5097
|
if (isCanonicalSourceImplementationFile(node.file, sources)) continue;
|
|
5064
5098
|
if (node.element.includes(".")) continue;
|
|
5099
|
+
if (canonicalDirectoryResolvedNodes.has(node.id)) continue;
|
|
5100
|
+
if (shadowRenderRootNodeIds.has(node.id)) continue;
|
|
5065
5101
|
const props = propsByNode.get(node.id) ?? [];
|
|
5066
5102
|
const textChildren = textByNode.get(node.id) ?? [];
|
|
5067
5103
|
const importsForFile = importsByFileAndLocal.get(node.file);
|
|
@@ -5309,6 +5345,18 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5309
5345
|
}
|
|
5310
5346
|
return findings;
|
|
5311
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
|
+
}
|
|
5312
5360
|
function findBuiltInRawHtmlMatch(node, props, textChildren, inputType) {
|
|
5313
5361
|
if (isIconOnlyButton(node, props, textChildren)) {
|
|
5314
5362
|
return { canonical: "IconButton", tier: "icon-only" };
|
|
@@ -5587,7 +5635,27 @@ function sourceLabel(source) {
|
|
|
5587
5635
|
function canonicalImportPath(source) {
|
|
5588
5636
|
if (source.kind === "npm") return source.specifier;
|
|
5589
5637
|
if (source.kind === "registry") return source.importPath;
|
|
5590
|
-
return void 0;
|
|
5638
|
+
return source.include?.length ? source.path : void 0;
|
|
5639
|
+
}
|
|
5640
|
+
function indexCanonicalDirectoryResolvedNodes(ix, sources) {
|
|
5641
|
+
const out = /* @__PURE__ */ new Set();
|
|
5642
|
+
const directoryPaths = sources.filter(
|
|
5643
|
+
(source) => source.kind === "directory"
|
|
5644
|
+
).map((source) => normalizePath(source.path));
|
|
5645
|
+
if (directoryPaths.length === 0) return out;
|
|
5646
|
+
for (const fact of ix.byKind("usage_component")) {
|
|
5647
|
+
const module = componentIdModule(fact.componentId);
|
|
5648
|
+
if (module === void 0) continue;
|
|
5649
|
+
const normalizedModule = normalizePath(module);
|
|
5650
|
+
if (directoryPaths.some((path) => isPathInSource(normalizedModule, path))) {
|
|
5651
|
+
out.add(fact.nodeId);
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5654
|
+
return out;
|
|
5655
|
+
}
|
|
5656
|
+
function componentIdModule(componentId2) {
|
|
5657
|
+
const hash = componentId2.indexOf("#");
|
|
5658
|
+
return hash > 0 ? componentId2.slice(0, hash) : void 0;
|
|
5591
5659
|
}
|
|
5592
5660
|
function isCanonicalSourceImplementationFile(file, sources) {
|
|
5593
5661
|
const normalizedFile = normalizePath(file);
|
|
@@ -5615,9 +5683,95 @@ function isIdentifier(value) {
|
|
|
5615
5683
|
return /^[A-Za-z_$][\w$]*$/.test(value);
|
|
5616
5684
|
}
|
|
5617
5685
|
|
|
5618
|
-
// src/
|
|
5619
|
-
|
|
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";
|
|
5620
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";
|
|
5621
5775
|
function ruleComponentsForbiddenPropValue(ix) {
|
|
5622
5776
|
const findings = [];
|
|
5623
5777
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -5647,8 +5801,8 @@ function ruleComponentsForbiddenPropValue(ix) {
|
|
|
5647
5801
|
} : void 0;
|
|
5648
5802
|
findings.push(
|
|
5649
5803
|
makeFinding({
|
|
5650
|
-
ruleId:
|
|
5651
|
-
ruleVersion:
|
|
5804
|
+
ruleId: RULE_ID4,
|
|
5805
|
+
ruleVersion: RULE_VERSION4,
|
|
5652
5806
|
severity: policy.severity,
|
|
5653
5807
|
message: buildMessage(
|
|
5654
5808
|
node.element,
|
|
@@ -5692,8 +5846,8 @@ function buildMessage(element, prop, value, because, replacement) {
|
|
|
5692
5846
|
}
|
|
5693
5847
|
|
|
5694
5848
|
// src/rules/components-unknown-prop.ts
|
|
5695
|
-
var
|
|
5696
|
-
var
|
|
5849
|
+
var RULE_ID5 = "components/unknown-prop";
|
|
5850
|
+
var RULE_VERSION5 = "1";
|
|
5697
5851
|
function ruleComponentsUnknownProp(ix) {
|
|
5698
5852
|
const policy = ix.policy.jsxUnknownPropsForbidden();
|
|
5699
5853
|
if (!policy) return [];
|
|
@@ -5714,8 +5868,8 @@ function ruleComponentsUnknownProp(ix) {
|
|
|
5714
5868
|
if (knownNames.has(propUsage.prop)) continue;
|
|
5715
5869
|
findings.push(
|
|
5716
5870
|
makeFinding({
|
|
5717
|
-
ruleId:
|
|
5718
|
-
ruleVersion:
|
|
5871
|
+
ruleId: RULE_ID5,
|
|
5872
|
+
ruleVersion: RULE_VERSION5,
|
|
5719
5873
|
severity: policy.severity,
|
|
5720
5874
|
message: `Unknown prop "${propUsage.prop}" on <${node.element}>. Allowed props: ${formatPropList(knownNames)}.`,
|
|
5721
5875
|
location: node.location,
|
|
@@ -5747,7 +5901,7 @@ function formatPropList(names) {
|
|
|
5747
5901
|
// src/rules/composition-pattern.ts
|
|
5748
5902
|
var CARDINALITY_RULE_ID = "composition/cardinality";
|
|
5749
5903
|
var CO_OCCURRENCE_RULE_ID = "composition/co-occurrence";
|
|
5750
|
-
var
|
|
5904
|
+
var RULE_VERSION6 = "1";
|
|
5751
5905
|
function ruleCompositionCardinality(ix) {
|
|
5752
5906
|
return evaluate(ix, CARDINALITY_RULE_ID, "cardinality");
|
|
5753
5907
|
}
|
|
@@ -5815,7 +5969,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
5815
5969
|
const extras = selected.slice(constraint.max);
|
|
5816
5970
|
return makeFinding({
|
|
5817
5971
|
ruleId: ctx.ruleId,
|
|
5818
|
-
ruleVersion:
|
|
5972
|
+
ruleVersion: RULE_VERSION6,
|
|
5819
5973
|
severity: ctx.severity,
|
|
5820
5974
|
message: cardinalityMaxMessage(pattern, constraint.max, count),
|
|
5821
5975
|
location: extras[0].location,
|
|
@@ -5826,7 +5980,7 @@ function checkCardinality(ctx, region, pattern, constraint, selected) {
|
|
|
5826
5980
|
if (constraint.min !== void 0 && count < constraint.min) {
|
|
5827
5981
|
return makeFinding({
|
|
5828
5982
|
ruleId: ctx.ruleId,
|
|
5829
|
-
ruleVersion:
|
|
5983
|
+
ruleVersion: RULE_VERSION6,
|
|
5830
5984
|
severity: ctx.severity,
|
|
5831
5985
|
message: cardinalityMinMessage(pattern, constraint.min, count),
|
|
5832
5986
|
location: region.container.location,
|
|
@@ -5847,7 +6001,7 @@ function checkCoOccurrence(ctx, region, pattern, constraint, selected) {
|
|
|
5847
6001
|
if (satisfied) return null;
|
|
5848
6002
|
return makeFinding({
|
|
5849
6003
|
ruleId: ctx.ruleId,
|
|
5850
|
-
ruleVersion:
|
|
6004
|
+
ruleVersion: RULE_VERSION6,
|
|
5851
6005
|
severity: ctx.severity,
|
|
5852
6006
|
message: coOccurrenceMessage(pattern, constraint),
|
|
5853
6007
|
location: region.container.location,
|
|
@@ -5899,8 +6053,8 @@ function coOccurrenceMessage(pattern, constraint) {
|
|
|
5899
6053
|
}
|
|
5900
6054
|
|
|
5901
6055
|
// src/rules/jsx-preferred-component.ts
|
|
5902
|
-
var
|
|
5903
|
-
var
|
|
6056
|
+
var RULE_ID6 = "components/preferred-component";
|
|
6057
|
+
var RULE_VERSION7 = "1";
|
|
5904
6058
|
function ruleJsxPreferredComponent(ix) {
|
|
5905
6059
|
const policies = ix.policy.jsxComponentPreferred();
|
|
5906
6060
|
if (policies.length === 0) return [];
|
|
@@ -5916,8 +6070,8 @@ function ruleJsxPreferredComponent(ix) {
|
|
|
5916
6070
|
if (!replacement) continue;
|
|
5917
6071
|
findings.push(
|
|
5918
6072
|
makeFinding({
|
|
5919
|
-
ruleId:
|
|
5920
|
-
ruleVersion:
|
|
6073
|
+
ruleId: RULE_ID6,
|
|
6074
|
+
ruleVersion: RULE_VERSION7,
|
|
5921
6075
|
severity: policy.severity,
|
|
5922
6076
|
message: `<${node.element}> should use <${replacement}>.`,
|
|
5923
6077
|
location: node.location,
|
|
@@ -5954,8 +6108,8 @@ function componentNameFromId(componentId2) {
|
|
|
5954
6108
|
}
|
|
5955
6109
|
|
|
5956
6110
|
// src/rules/jsx-preferred-import-path.ts
|
|
5957
|
-
var
|
|
5958
|
-
var
|
|
6111
|
+
var RULE_ID7 = "imports/preferred-path";
|
|
6112
|
+
var RULE_VERSION8 = "1";
|
|
5959
6113
|
function ruleJsxPreferredImportPath(ix) {
|
|
5960
6114
|
const policies = ix.policy.jsxImportPathPreferred();
|
|
5961
6115
|
if (policies.length === 0) return [];
|
|
@@ -5975,8 +6129,8 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
5975
6129
|
seen.add(key);
|
|
5976
6130
|
findings.push(
|
|
5977
6131
|
makeFinding({
|
|
5978
|
-
ruleId:
|
|
5979
|
-
ruleVersion:
|
|
6132
|
+
ruleId: RULE_ID7,
|
|
6133
|
+
ruleVersion: RULE_VERSION8,
|
|
5980
6134
|
severity: policy.severity,
|
|
5981
6135
|
message: `Import from "${usage.source}" should use "${policy.to}".`,
|
|
5982
6136
|
location: usage.location,
|
|
@@ -6011,10 +6165,10 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6011
6165
|
}
|
|
6012
6166
|
|
|
6013
6167
|
// src/rules/props-invalid-value.ts
|
|
6014
|
-
var
|
|
6015
|
-
var
|
|
6168
|
+
var RULE_ID8 = "props/invalid-value";
|
|
6169
|
+
var RULE_VERSION9 = "1";
|
|
6016
6170
|
function rulePropsInvalidValue(ix) {
|
|
6017
|
-
const policy = ix.policy.ruleConfig(
|
|
6171
|
+
const policy = ix.policy.ruleConfig(RULE_ID8);
|
|
6018
6172
|
if (!policy?.enabled) return [];
|
|
6019
6173
|
const findings = [];
|
|
6020
6174
|
const componentByNode = indexComponentByNodeId(ix);
|
|
@@ -6043,8 +6197,8 @@ function rulePropsInvalidValue(ix) {
|
|
|
6043
6197
|
} : void 0;
|
|
6044
6198
|
findings.push(
|
|
6045
6199
|
makeFinding({
|
|
6046
|
-
ruleId:
|
|
6047
|
-
ruleVersion:
|
|
6200
|
+
ruleId: RULE_ID8,
|
|
6201
|
+
ruleVersion: RULE_VERSION9,
|
|
6048
6202
|
severity: policy.severity ?? "warn",
|
|
6049
6203
|
message: buildMessage2(node.element, propUsage.prop, propUsage.value, allowedValues),
|
|
6050
6204
|
location: propUsage.location ?? node.location,
|
|
@@ -6220,14 +6374,15 @@ function srgbGamma(linear) {
|
|
|
6220
6374
|
}
|
|
6221
6375
|
|
|
6222
6376
|
// src/rules/styles-no-raw-color.ts
|
|
6223
|
-
var
|
|
6224
|
-
var
|
|
6377
|
+
var RULE_ID9 = "styles/no-raw-color";
|
|
6378
|
+
var RULE_VERSION10 = "1";
|
|
6225
6379
|
function ruleStylesNoRawColor(ix) {
|
|
6226
6380
|
const policy = ix.policy.rawColorPolicy();
|
|
6227
6381
|
if (!policy) return [];
|
|
6228
6382
|
const colorTokens = ix.tokens.byCategory("color");
|
|
6229
6383
|
const preferLabel = policy.prefer === "token" ? "design token" : "CSS variable";
|
|
6230
6384
|
const findings = [];
|
|
6385
|
+
const advisorySeverity = policy.severity === "error" ? "warn" : policy.severity;
|
|
6231
6386
|
for (const decl of ix.byKind("style_declaration")) {
|
|
6232
6387
|
if (decl.property.startsWith("--") && decl.declaredTokenSource === true) continue;
|
|
6233
6388
|
const color = detectRawColor(decl.value);
|
|
@@ -6238,8 +6393,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6238
6393
|
const evidenceIds = resolution ? [decl.id, policy.id, resolution.token.id] : [decl.id, policy.id];
|
|
6239
6394
|
findings.push(
|
|
6240
6395
|
makeFinding({
|
|
6241
|
-
ruleId:
|
|
6242
|
-
ruleVersion:
|
|
6396
|
+
ruleId: RULE_ID9,
|
|
6397
|
+
ruleVersion: RULE_VERSION10,
|
|
6243
6398
|
severity: policy.severity,
|
|
6244
6399
|
message: colorMessage(color, `\`${decl.property}\``, resolution, preferLabel),
|
|
6245
6400
|
location: decl.location,
|
|
@@ -6267,6 +6422,37 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6267
6422
|
}
|
|
6268
6423
|
const componentByNode = indexComponentByNodeId(ix);
|
|
6269
6424
|
for (const inline of ix.byKind("usage_inline_style")) {
|
|
6425
|
+
if (inline.valueKind === "dynamic-raw") {
|
|
6426
|
+
const node2 = readUsageNode(ix, inline.nodeId);
|
|
6427
|
+
if (!node2) continue;
|
|
6428
|
+
const componentEvidenceId2 = componentByNode.get(node2.id)?.id;
|
|
6429
|
+
const evidenceIds2 = componentEvidenceId2 ? [node2.id, componentEvidenceId2, inline.id, policy.id] : [node2.id, inline.id, policy.id];
|
|
6430
|
+
findings.push(
|
|
6431
|
+
makeFinding({
|
|
6432
|
+
ruleId: RULE_ID9,
|
|
6433
|
+
ruleVersion: RULE_VERSION10,
|
|
6434
|
+
severity: advisorySeverity,
|
|
6435
|
+
message: `Runtime-constructed raw color \`${inline.value}\` on inline \`${inline.property}\`. Use a ${preferLabel} instead.`,
|
|
6436
|
+
location: node2.location,
|
|
6437
|
+
evidence: ix.evidence(evidenceIds2),
|
|
6438
|
+
fingerprintIdentity: {
|
|
6439
|
+
source: "usage_inline_style_dynamic",
|
|
6440
|
+
file: node2.file,
|
|
6441
|
+
nodePath: node2.nodePath,
|
|
6442
|
+
element: node2.element,
|
|
6443
|
+
property: inline.property,
|
|
6444
|
+
value: inline.value
|
|
6445
|
+
},
|
|
6446
|
+
attributes: {
|
|
6447
|
+
property: inline.property,
|
|
6448
|
+
rawValue: inline.value,
|
|
6449
|
+
source: "jsx",
|
|
6450
|
+
dynamic: true
|
|
6451
|
+
}
|
|
6452
|
+
})
|
|
6453
|
+
);
|
|
6454
|
+
continue;
|
|
6455
|
+
}
|
|
6270
6456
|
if (inline.valueKind === "css-variable") continue;
|
|
6271
6457
|
const color = detectRawColor(inline.value);
|
|
6272
6458
|
if (!color) continue;
|
|
@@ -6280,8 +6466,8 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6280
6466
|
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6281
6467
|
findings.push(
|
|
6282
6468
|
makeFinding({
|
|
6283
|
-
ruleId:
|
|
6284
|
-
ruleVersion:
|
|
6469
|
+
ruleId: RULE_ID9,
|
|
6470
|
+
ruleVersion: RULE_VERSION10,
|
|
6285
6471
|
severity: policy.severity,
|
|
6286
6472
|
message: colorMessage(color, `inline \`${inline.property}\``, resolution, preferLabel),
|
|
6287
6473
|
location: node.location,
|
|
@@ -6307,8 +6493,68 @@ function ruleStylesNoRawColor(ix) {
|
|
|
6307
6493
|
})
|
|
6308
6494
|
);
|
|
6309
6495
|
}
|
|
6496
|
+
for (const prop of ix.byKind("usage_prop_resolved")) {
|
|
6497
|
+
if (prop.resolution !== "static") continue;
|
|
6498
|
+
if (typeof prop.value !== "string") continue;
|
|
6499
|
+
if (!SVG_PAINT_ATTRS.has(prop.prop)) continue;
|
|
6500
|
+
const color = detectRawColor(prop.value);
|
|
6501
|
+
if (!color) continue;
|
|
6502
|
+
if (isExemptColor(color, policy.except)) continue;
|
|
6503
|
+
const node = readUsageNode(ix, prop.nodeId);
|
|
6504
|
+
if (!node) continue;
|
|
6505
|
+
const componentEvidenceId = componentByNode.get(node.id)?.id;
|
|
6506
|
+
const cssProperty = SVG_PAINT_CSS_PROPERTY[prop.prop] ?? prop.prop;
|
|
6507
|
+
const resolution = resolveColorToken(cssProperty, color, colorTokens);
|
|
6508
|
+
const fixEmission = resolution?.kind === "exact" ? buildTokenFix(cssProperty, prop.value, color, resolution, ix) : void 0;
|
|
6509
|
+
const baseEvidence = componentEvidenceId ? [node.id, componentEvidenceId, prop.id, policy.id] : [node.id, prop.id, policy.id];
|
|
6510
|
+
const evidenceIds = resolution ? [...baseEvidence, resolution.token.id] : baseEvidence;
|
|
6511
|
+
findings.push(
|
|
6512
|
+
makeFinding({
|
|
6513
|
+
ruleId: RULE_ID9,
|
|
6514
|
+
ruleVersion: RULE_VERSION10,
|
|
6515
|
+
severity: advisorySeverity,
|
|
6516
|
+
message: colorMessage(color, `\`${prop.prop}\``, resolution, preferLabel),
|
|
6517
|
+
location: prop.location ?? node.location,
|
|
6518
|
+
evidence: ix.evidence(evidenceIds),
|
|
6519
|
+
fingerprintIdentity: {
|
|
6520
|
+
source: "usage_prop_resolved",
|
|
6521
|
+
file: node.file,
|
|
6522
|
+
nodePath: node.nodePath,
|
|
6523
|
+
element: node.element,
|
|
6524
|
+
property: prop.prop,
|
|
6525
|
+
value: color
|
|
6526
|
+
},
|
|
6527
|
+
fix: fixEmission?.fix,
|
|
6528
|
+
attributes: {
|
|
6529
|
+
property: prop.prop,
|
|
6530
|
+
rawValue: prop.value,
|
|
6531
|
+
color,
|
|
6532
|
+
source: "jsx",
|
|
6533
|
+
suggestedToken: resolution?.token.name,
|
|
6534
|
+
...resolution ? { tokenMatch: resolution.kind } : {},
|
|
6535
|
+
...fixEmission?.downgradeReason ? { downgradeReason: fixEmission.downgradeReason } : {}
|
|
6536
|
+
}
|
|
6537
|
+
})
|
|
6538
|
+
);
|
|
6539
|
+
}
|
|
6310
6540
|
return findings;
|
|
6311
6541
|
}
|
|
6542
|
+
var SVG_PAINT_ATTRS = /* @__PURE__ */ new Set([
|
|
6543
|
+
"fill",
|
|
6544
|
+
"stroke",
|
|
6545
|
+
"color",
|
|
6546
|
+
"stop-color",
|
|
6547
|
+
"stopColor",
|
|
6548
|
+
"flood-color",
|
|
6549
|
+
"floodColor",
|
|
6550
|
+
"lighting-color",
|
|
6551
|
+
"lightingColor"
|
|
6552
|
+
]);
|
|
6553
|
+
var SVG_PAINT_CSS_PROPERTY = {
|
|
6554
|
+
stopColor: "stop-color",
|
|
6555
|
+
floodColor: "flood-color",
|
|
6556
|
+
lightingColor: "lighting-color"
|
|
6557
|
+
};
|
|
6312
6558
|
function resolveColorToken(property, raw, tokens) {
|
|
6313
6559
|
const exact = findTokenMatch(property, raw, tokens);
|
|
6314
6560
|
if (exact) return { kind: "exact", token: exact.token, ambiguous: exact.ambiguous };
|
|
@@ -6416,8 +6662,8 @@ function escapeRegExp(value) {
|
|
|
6416
6662
|
}
|
|
6417
6663
|
|
|
6418
6664
|
// src/rules/styles-no-raw-dimensions.ts
|
|
6419
|
-
var
|
|
6420
|
-
var
|
|
6665
|
+
var RULE_ID10 = "styles/no-raw-dimensions";
|
|
6666
|
+
var RULE_VERSION11 = "1";
|
|
6421
6667
|
function ruleStylesNoRawDimensions(ix) {
|
|
6422
6668
|
const policy = ix.policy.rawDimensionPolicy();
|
|
6423
6669
|
if (!policy) return [];
|
|
@@ -6437,8 +6683,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6437
6683
|
const fixEmission = dimensionFix(decl.property, resolution, ix);
|
|
6438
6684
|
findings.push(
|
|
6439
6685
|
makeFinding({
|
|
6440
|
-
ruleId:
|
|
6441
|
-
ruleVersion:
|
|
6686
|
+
ruleId: RULE_ID10,
|
|
6687
|
+
ruleVersion: RULE_VERSION11,
|
|
6442
6688
|
severity: policy.severity,
|
|
6443
6689
|
message: dimensionMessage(decl.value, decl.property, preferLabel, resolution, false),
|
|
6444
6690
|
location: decl.location,
|
|
@@ -6479,8 +6725,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6479
6725
|
const fixEmission = dimensionFix(inline.property, resolution, ix);
|
|
6480
6726
|
findings.push(
|
|
6481
6727
|
makeFinding({
|
|
6482
|
-
ruleId:
|
|
6483
|
-
ruleVersion:
|
|
6728
|
+
ruleId: RULE_ID10,
|
|
6729
|
+
ruleVersion: RULE_VERSION11,
|
|
6484
6730
|
severity: policy.severity,
|
|
6485
6731
|
message: dimensionMessage(inline.value, inline.property, preferLabel, resolution, true),
|
|
6486
6732
|
location: node.location,
|
|
@@ -6750,8 +6996,8 @@ function parseLengthParts(raw) {
|
|
|
6750
6996
|
}
|
|
6751
6997
|
|
|
6752
6998
|
// src/rules/styles-no-raw-spacing.ts
|
|
6753
|
-
var
|
|
6754
|
-
var
|
|
6999
|
+
var RULE_ID11 = "styles/no-raw-spacing";
|
|
7000
|
+
var RULE_VERSION12 = "1";
|
|
6755
7001
|
function ruleStylesNoRawSpacing(ix) {
|
|
6756
7002
|
const findings = [];
|
|
6757
7003
|
const lookupCache = /* @__PURE__ */ new Map();
|
|
@@ -6793,8 +7039,8 @@ function checkDeclaration(ix, decl, lookupFor) {
|
|
|
6793
7039
|
if (!checked) return null;
|
|
6794
7040
|
const fixEmission = buildSpacingFix(decl.property, checked, ix);
|
|
6795
7041
|
return makeFinding({
|
|
6796
|
-
ruleId:
|
|
6797
|
-
ruleVersion:
|
|
7042
|
+
ruleId: RULE_ID11,
|
|
7043
|
+
ruleVersion: RULE_VERSION12,
|
|
6798
7044
|
severity: policy.severity,
|
|
6799
7045
|
message: spacingMessage(decl.property, decl.value, allowed, scale.unit, checked),
|
|
6800
7046
|
location: decl.location,
|
|
@@ -6848,8 +7094,8 @@ function checkInlineStyle(ix, inline, componentByNode, lookupFor) {
|
|
|
6848
7094
|
const componentEvidenceId = componentByNode.get(node.id)?.id;
|
|
6849
7095
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
6850
7096
|
return makeFinding({
|
|
6851
|
-
ruleId:
|
|
6852
|
-
ruleVersion:
|
|
7097
|
+
ruleId: RULE_ID11,
|
|
7098
|
+
ruleVersion: RULE_VERSION12,
|
|
6853
7099
|
severity: policy.severity,
|
|
6854
7100
|
message: spacingMessage(inline.property, inline.value, allowed, scale.unit, checked),
|
|
6855
7101
|
location: node.location,
|
|
@@ -6910,8 +7156,8 @@ function spacingMessage(property, value, allowed, unit, checked) {
|
|
|
6910
7156
|
}
|
|
6911
7157
|
|
|
6912
7158
|
// src/rules/styles-no-raw-typography.ts
|
|
6913
|
-
var
|
|
6914
|
-
var
|
|
7159
|
+
var RULE_ID12 = "styles/no-raw-typography";
|
|
7160
|
+
var RULE_VERSION13 = "1";
|
|
6915
7161
|
var FONT_SIZE_PROPERTIES = /* @__PURE__ */ new Set(["font-size", "fontsize"]);
|
|
6916
7162
|
function ruleStylesNoRawTypography(ix) {
|
|
6917
7163
|
const policy = ix.policy.fontSizeScale();
|
|
@@ -6929,8 +7175,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
6929
7175
|
const fixEmission = buildFix(decl.property, checked, ix);
|
|
6930
7176
|
findings.push(
|
|
6931
7177
|
makeFinding({
|
|
6932
|
-
ruleId:
|
|
6933
|
-
ruleVersion:
|
|
7178
|
+
ruleId: RULE_ID12,
|
|
7179
|
+
ruleVersion: RULE_VERSION13,
|
|
6934
7180
|
severity: policy.severity,
|
|
6935
7181
|
message: typographyMessage(decl.property, decl.value, allowed, scale.unit, checked),
|
|
6936
7182
|
location: decl.location,
|
|
@@ -6976,8 +7222,8 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
6976
7222
|
const evidenceIds = componentEvidenceId ? [node.id, componentEvidenceId, inline.id, policy.id, scale.id] : [node.id, inline.id, policy.id, scale.id];
|
|
6977
7223
|
findings.push(
|
|
6978
7224
|
makeFinding({
|
|
6979
|
-
ruleId:
|
|
6980
|
-
ruleVersion:
|
|
7225
|
+
ruleId: RULE_ID12,
|
|
7226
|
+
ruleVersion: RULE_VERSION13,
|
|
6981
7227
|
severity: policy.severity,
|
|
6982
7228
|
message: typographyMessage(inline.property, inline.value, allowed, scale.unit, checked),
|
|
6983
7229
|
location: node.location,
|
|
@@ -7079,15 +7325,15 @@ function typographyMessage(property, value, allowed, unit, checked) {
|
|
|
7079
7325
|
}
|
|
7080
7326
|
|
|
7081
7327
|
// src/rules/theme-no-theme-coupled-literal.ts
|
|
7082
|
-
var
|
|
7083
|
-
var
|
|
7328
|
+
var RULE_ID13 = "theme/no-theme-coupled-literal";
|
|
7329
|
+
var RULE_VERSION14 = "1";
|
|
7084
7330
|
var COLOR_LITERAL_GLOBAL = /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?|oklab|oklch|lab|lch)\(\s*[\d.%\s,\/]+\s*\)/g;
|
|
7085
7331
|
var WHITE_RGBA = /^rgba?\(\s*255\s*,\s*255\s*,\s*255/i;
|
|
7086
7332
|
var BLACK_RGBA = /^rgba?\(\s*0\s*,\s*0\s*,\s*0/i;
|
|
7087
7333
|
var BOX_SHADOW_PROP = /^(?:-webkit-|-moz-)?box-shadow$/i;
|
|
7088
7334
|
var NEUTRAL_SHADOW = /^(?:none|inherit|initial|unset|revert|revert-layer)\s*$/i;
|
|
7089
7335
|
function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
7090
|
-
const policy = ix.policy.ruleConfig(
|
|
7336
|
+
const policy = ix.policy.ruleConfig(RULE_ID13);
|
|
7091
7337
|
if (!policy?.enabled) return [];
|
|
7092
7338
|
const findings = [];
|
|
7093
7339
|
for (const decl of ix.byKind("style_declaration")) {
|
|
@@ -7096,8 +7342,8 @@ function ruleThemeNoThemeCoupledLiteral(ix) {
|
|
|
7096
7342
|
if (!hit) continue;
|
|
7097
7343
|
findings.push(
|
|
7098
7344
|
makeFinding({
|
|
7099
|
-
ruleId:
|
|
7100
|
-
ruleVersion:
|
|
7345
|
+
ruleId: RULE_ID13,
|
|
7346
|
+
ruleVersion: RULE_VERSION14,
|
|
7101
7347
|
severity: policy.severity ?? "warn",
|
|
7102
7348
|
message: hit.message,
|
|
7103
7349
|
location: decl.location,
|
|
@@ -7187,8 +7433,8 @@ function isTokenFile(filePath) {
|
|
|
7187
7433
|
}
|
|
7188
7434
|
|
|
7189
7435
|
// src/rules/tailwind-arbitrary-color.ts
|
|
7190
|
-
var
|
|
7191
|
-
var
|
|
7436
|
+
var RULE_ID14 = "tailwind/arbitrary-color";
|
|
7437
|
+
var RULE_VERSION15 = "1";
|
|
7192
7438
|
var COLOR_UTILITIES = /* @__PURE__ */ new Set([
|
|
7193
7439
|
"accent",
|
|
7194
7440
|
"bg",
|
|
@@ -7230,8 +7476,8 @@ function ruleTailwindArbitraryColor(ix) {
|
|
|
7230
7476
|
const evidenceIds = node ? [node.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
7231
7477
|
findings.push(
|
|
7232
7478
|
makeFinding({
|
|
7233
|
-
ruleId:
|
|
7234
|
-
ruleVersion:
|
|
7479
|
+
ruleId: RULE_ID14,
|
|
7480
|
+
ruleVersion: RULE_VERSION15,
|
|
7235
7481
|
severity: policy.severity,
|
|
7236
7482
|
message: `\`${klass.raw}\` uses arbitrary raw color ${color}. Use a ${policy.prefer === "token" ? "design token" : "CSS variable"} instead.`,
|
|
7237
7483
|
location: klass.location,
|
|
@@ -7262,8 +7508,8 @@ function fingerprintIdentity(klass, color) {
|
|
|
7262
7508
|
}
|
|
7263
7509
|
|
|
7264
7510
|
// src/rules/tailwind-arbitrary-spacing.ts
|
|
7265
|
-
var
|
|
7266
|
-
var
|
|
7511
|
+
var RULE_ID15 = "tailwind/arbitrary-spacing";
|
|
7512
|
+
var RULE_VERSION16 = "1";
|
|
7267
7513
|
var SPACING_UTILITY_TO_PROPERTY = /* @__PURE__ */ new Map([
|
|
7268
7514
|
["m", "margin"],
|
|
7269
7515
|
["mt", "margin"],
|
|
@@ -7309,8 +7555,8 @@ function ruleTailwindArbitrarySpacing(ix) {
|
|
|
7309
7555
|
const fix = buildFix2(klass);
|
|
7310
7556
|
findings.push(
|
|
7311
7557
|
makeFinding({
|
|
7312
|
-
ruleId:
|
|
7313
|
-
ruleVersion:
|
|
7558
|
+
ruleId: RULE_ID15,
|
|
7559
|
+
ruleVersion: RULE_VERSION16,
|
|
7314
7560
|
severity: policy.severity,
|
|
7315
7561
|
message: `\`${klass.raw}\` is an arbitrary spacing value not on the project's spacing scale.`,
|
|
7316
7562
|
location: klass.location,
|
|
@@ -7441,8 +7687,8 @@ function matchesTokenGlob(value, pattern) {
|
|
|
7441
7687
|
}
|
|
7442
7688
|
|
|
7443
7689
|
// src/rules/tailwind-forbidden-palette.ts
|
|
7444
|
-
var
|
|
7445
|
-
var
|
|
7690
|
+
var RULE_ID16 = "tailwind/forbidden-palette";
|
|
7691
|
+
var RULE_VERSION17 = "1";
|
|
7446
7692
|
function ruleTailwindForbiddenPalette(ix) {
|
|
7447
7693
|
const deny = ix.policy.tailwindPaletteDeny();
|
|
7448
7694
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
@@ -7467,8 +7713,8 @@ function ruleTailwindForbiddenPalette(ix) {
|
|
|
7467
7713
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
7468
7714
|
findings.push(
|
|
7469
7715
|
makeFinding({
|
|
7470
|
-
ruleId:
|
|
7471
|
-
ruleVersion:
|
|
7716
|
+
ruleId: RULE_ID16,
|
|
7717
|
+
ruleVersion: RULE_VERSION17,
|
|
7472
7718
|
severity: policy.severity,
|
|
7473
7719
|
message: messageFor(klass, token, deniedPattern, allow?.patterns ?? []),
|
|
7474
7720
|
location: klass.location,
|
|
@@ -7515,8 +7761,8 @@ function buildFix3(klass) {
|
|
|
7515
7761
|
}
|
|
7516
7762
|
|
|
7517
7763
|
// src/rules/tailwind-off-scale-spacing-token.ts
|
|
7518
|
-
var
|
|
7519
|
-
var
|
|
7764
|
+
var RULE_ID17 = "tailwind/off-scale-spacing-token";
|
|
7765
|
+
var RULE_VERSION18 = "1";
|
|
7520
7766
|
function ruleTailwindOffScaleSpacingToken(ix) {
|
|
7521
7767
|
const resolvedByKey = indexResolvedTailwindTokens(ix);
|
|
7522
7768
|
const findings = [];
|
|
@@ -7542,8 +7788,8 @@ function ruleTailwindOffScaleSpacingToken(ix) {
|
|
|
7542
7788
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id, scale.id] : [klass.id, resolved.id, policy.id, scale.id];
|
|
7543
7789
|
findings.push(
|
|
7544
7790
|
makeFinding({
|
|
7545
|
-
ruleId:
|
|
7546
|
-
ruleVersion:
|
|
7791
|
+
ruleId: RULE_ID17,
|
|
7792
|
+
ruleVersion: RULE_VERSION18,
|
|
7547
7793
|
severity: policy.severity,
|
|
7548
7794
|
message: `\`${klass.raw}\` resolves to ${formatResolvedValue(signedValue, scale.unit)}, which is not on the project's spacing scale [${allowed.join(", ")}].`,
|
|
7549
7795
|
location: klass.location,
|
|
@@ -7592,8 +7838,8 @@ function buildFix4(raw, utility) {
|
|
|
7592
7838
|
}
|
|
7593
7839
|
|
|
7594
7840
|
// src/rules/tailwind-raw-color-via-token.ts
|
|
7595
|
-
var
|
|
7596
|
-
var
|
|
7841
|
+
var RULE_ID18 = "tailwind/raw-color-via-token";
|
|
7842
|
+
var RULE_VERSION19 = "1";
|
|
7597
7843
|
function ruleTailwindRawColorViaToken(ix) {
|
|
7598
7844
|
const allow = ix.policy.tailwindPaletteAllow();
|
|
7599
7845
|
if (!allow || allow.patterns.length === 0) return [];
|
|
@@ -7613,8 +7859,8 @@ function ruleTailwindRawColorViaToken(ix) {
|
|
|
7613
7859
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, allow.id] : [klass.id, resolved.id, allow.id];
|
|
7614
7860
|
findings.push(
|
|
7615
7861
|
makeFinding({
|
|
7616
|
-
ruleId:
|
|
7617
|
-
ruleVersion:
|
|
7862
|
+
ruleId: RULE_ID18,
|
|
7863
|
+
ruleVersion: RULE_VERSION19,
|
|
7618
7864
|
severity: allow.severity,
|
|
7619
7865
|
message: `\`${klass.raw}\` resolves to raw color \`${color}\` (${sourceLabel2(resolved.resolved.source)}). Project policy requires \`${allow.patterns.join("`, `")}\` palette tokens.`,
|
|
7620
7866
|
location: klass.location,
|
|
@@ -7647,8 +7893,8 @@ function sourceLabel2(source) {
|
|
|
7647
7893
|
}
|
|
7648
7894
|
|
|
7649
7895
|
// src/rules/tailwind-unknown-class.ts
|
|
7650
|
-
var
|
|
7651
|
-
var
|
|
7896
|
+
var RULE_ID19 = "tailwind/unknown-class";
|
|
7897
|
+
var RULE_VERSION20 = "1";
|
|
7652
7898
|
var UNIVERSAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
7653
7899
|
"white",
|
|
7654
7900
|
"black",
|
|
@@ -7669,8 +7915,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
7669
7915
|
const evidenceIds2 = node2 ? [node2.id, klass.id, policy.id] : [klass.id, policy.id];
|
|
7670
7916
|
findings.push(
|
|
7671
7917
|
makeFinding({
|
|
7672
|
-
ruleId:
|
|
7673
|
-
ruleVersion:
|
|
7918
|
+
ruleId: RULE_ID19,
|
|
7919
|
+
ruleVersion: RULE_VERSION20,
|
|
7674
7920
|
severity: policy.severity,
|
|
7675
7921
|
message: `\`${klass.raw}\` could not be parsed as a known Tailwind utility.`,
|
|
7676
7922
|
location: klass.location,
|
|
@@ -7704,8 +7950,8 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
7704
7950
|
const evidenceIds = node ? [node.id, klass.id, resolved.id, policy.id] : [klass.id, resolved.id, policy.id];
|
|
7705
7951
|
findings.push(
|
|
7706
7952
|
makeFinding({
|
|
7707
|
-
ruleId:
|
|
7708
|
-
ruleVersion:
|
|
7953
|
+
ruleId: RULE_ID19,
|
|
7954
|
+
ruleVersion: RULE_VERSION20,
|
|
7709
7955
|
severity: policy.severity,
|
|
7710
7956
|
message: `\`${klass.raw}\` did not resolve to any known palette token.`,
|
|
7711
7957
|
location: klass.location,
|
|
@@ -7733,12 +7979,12 @@ function ruleTailwindUnknownClass(ix) {
|
|
|
7733
7979
|
}
|
|
7734
7980
|
|
|
7735
7981
|
// src/rules/tokens-require-dual-fallback.ts
|
|
7736
|
-
var
|
|
7737
|
-
var
|
|
7982
|
+
var RULE_ID20 = "tokens/require-dual-fallback";
|
|
7983
|
+
var RULE_VERSION21 = "1";
|
|
7738
7984
|
var CSS_VAR_WITH_OPTIONAL_FALLBACK = /var\(\s*(--[A-Za-z0-9_-]+)(\s*,\s*[^)]*)?\)/g;
|
|
7739
7985
|
var SASS_FILE = /\.(scss|sass)$/i;
|
|
7740
7986
|
function ruleTokensRequireDualFallback(ix) {
|
|
7741
|
-
const policy = ix.policy.ruleConfig(
|
|
7987
|
+
const policy = ix.policy.ruleConfig(RULE_ID20);
|
|
7742
7988
|
if (!policy?.enabled) return [];
|
|
7743
7989
|
const tokensByCssVariable = indexTokensByCssVariable(ix.tokens.list());
|
|
7744
7990
|
const findings = [];
|
|
@@ -7773,8 +8019,8 @@ function ruleTokensRequireDualFallback(ix) {
|
|
|
7773
8019
|
) : void 0;
|
|
7774
8020
|
findings.push(
|
|
7775
8021
|
makeFinding({
|
|
7776
|
-
ruleId:
|
|
7777
|
-
ruleVersion:
|
|
8022
|
+
ruleId: RULE_ID20,
|
|
8023
|
+
ruleVersion: RULE_VERSION21,
|
|
7778
8024
|
severity: policy.severity ?? "warn",
|
|
7779
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}.`,
|
|
7780
8026
|
location: decl.location,
|
|
@@ -7814,8 +8060,8 @@ function indexTokensByCssVariable(tokens) {
|
|
|
7814
8060
|
}
|
|
7815
8061
|
|
|
7816
8062
|
// src/rules/tokens-css-vars-must-be-defined.ts
|
|
7817
|
-
var
|
|
7818
|
-
var
|
|
8063
|
+
var RULE_ID21 = "tokens/css-vars-must-be-defined";
|
|
8064
|
+
var RULE_VERSION22 = "1";
|
|
7819
8065
|
var CSS_VAR_REFERENCE = /var\(\s*(--[A-Za-z0-9_-]+)/gi;
|
|
7820
8066
|
function ruleTokensCssVarsMustBeDefined(ix) {
|
|
7821
8067
|
const policy = ix.policy.cssVarsMustBeDefined();
|
|
@@ -7842,8 +8088,8 @@ function ruleTokensCssVarsMustBeDefined(ix) {
|
|
|
7842
8088
|
seen.add(tokenName);
|
|
7843
8089
|
findings.push(
|
|
7844
8090
|
makeFinding({
|
|
7845
|
-
ruleId:
|
|
7846
|
-
ruleVersion:
|
|
8091
|
+
ruleId: RULE_ID21,
|
|
8092
|
+
ruleVersion: RULE_VERSION22,
|
|
7847
8093
|
severity: policy.severity,
|
|
7848
8094
|
message: `var(${tokenName}) is not in the contract token vocabulary. Use a token defined in your contract's token source files.`,
|
|
7849
8095
|
location: decl.location,
|
|
@@ -7923,6 +8169,8 @@ function isDenyEligible(finding, failOnWarnings) {
|
|
|
7923
8169
|
// src/rules/tiers.ts
|
|
7924
8170
|
var RULE_TIER = {
|
|
7925
8171
|
// ---- Tier A — contract vocabulary -------------------------------------
|
|
8172
|
+
"components/shadow-component": "contract",
|
|
8173
|
+
// canonical-component identity drift
|
|
7926
8174
|
"components/prefer-library": "contract",
|
|
7927
8175
|
// canonical-component bypass (FUI1004)
|
|
7928
8176
|
"tokens/css-vars-must-be-defined": "contract",
|
|
@@ -7968,6 +8216,7 @@ function hygieneTierRuleIds() {
|
|
|
7968
8216
|
return Object.keys(RULE_TIER).filter((id) => !isContractTierRule(id));
|
|
7969
8217
|
}
|
|
7970
8218
|
var VOCABULARY_ORDER = [
|
|
8219
|
+
"components/shadow-component",
|
|
7971
8220
|
"components/prefer-library",
|
|
7972
8221
|
"tokens/css-vars-must-be-defined"
|
|
7973
8222
|
];
|
|
@@ -8028,6 +8277,11 @@ var RULES = [
|
|
|
8028
8277
|
version: "1",
|
|
8029
8278
|
run: ruleComponentsPreferLibrary
|
|
8030
8279
|
},
|
|
8280
|
+
{
|
|
8281
|
+
id: "components/shadow-component",
|
|
8282
|
+
version: "1",
|
|
8283
|
+
run: ruleComponentsShadowComponent
|
|
8284
|
+
},
|
|
8031
8285
|
{
|
|
8032
8286
|
id: "styles/no-raw-color",
|
|
8033
8287
|
version: "1",
|
|
@@ -8622,6 +8876,133 @@ function readPreferLibraryRule(config) {
|
|
|
8622
8876
|
const raw = config?.rules?.["components/prefer-library"];
|
|
8623
8877
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
8624
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
|
+
}
|
|
8625
9006
|
export {
|
|
8626
9007
|
AGENT_CONTEXT_RELATIVE_PATH,
|
|
8627
9008
|
AGENT_FORMAT_SCHEMA_VERSION,
|
|
@@ -8672,6 +9053,7 @@ export {
|
|
|
8672
9053
|
bridgeSourceViolations,
|
|
8673
9054
|
budgetBar,
|
|
8674
9055
|
buildAgentFormat,
|
|
9056
|
+
buildComponentKey,
|
|
8675
9057
|
buildRegistryArtifact,
|
|
8676
9058
|
buildRegistryFile,
|
|
8677
9059
|
buildRegistryInstallPlan,
|
|
@@ -8698,6 +9080,7 @@ export {
|
|
|
8698
9080
|
checkStoryExclusion,
|
|
8699
9081
|
clampMaxPasses,
|
|
8700
9082
|
classifyComplexity,
|
|
9083
|
+
classifyIdentity,
|
|
8701
9084
|
collectRegistryInstallDependencies,
|
|
8702
9085
|
colorSimilarity,
|
|
8703
9086
|
compareByVocabularyRank,
|
|
@@ -8793,6 +9176,7 @@ export {
|
|
|
8793
9176
|
hashRegistryFileContent,
|
|
8794
9177
|
hexToRgb,
|
|
8795
9178
|
hygieneTierRuleIds,
|
|
9179
|
+
indexComponentIdentityUsage,
|
|
8796
9180
|
inferTokenCategory,
|
|
8797
9181
|
inferTokenGroup,
|
|
8798
9182
|
isColorLike,
|
|
@@ -8819,6 +9203,8 @@ export {
|
|
|
8819
9203
|
makeClassNameDynamicFact,
|
|
8820
9204
|
makeClassNameLiteralFact,
|
|
8821
9205
|
makeComponentCapabilityFact,
|
|
9206
|
+
makeComponentDefinitionFact,
|
|
9207
|
+
makeComponentIdentityFact,
|
|
8822
9208
|
makeComponentMetadataFact,
|
|
8823
9209
|
makeContractTokenFact,
|
|
8824
9210
|
makeFinding,
|
|
@@ -8943,6 +9329,7 @@ export {
|
|
|
8943
9329
|
ruleA11yRequiredAccessibleName,
|
|
8944
9330
|
ruleComponentsForbiddenPropValue,
|
|
8945
9331
|
ruleComponentsPreferLibrary,
|
|
9332
|
+
ruleComponentsShadowComponent,
|
|
8946
9333
|
ruleComponentsUnknownProp,
|
|
8947
9334
|
ruleJsxPreferredComponent,
|
|
8948
9335
|
ruleJsxPreferredImportPath,
|