@usefragments/core 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HXGE3O2F.js → chunk-AOG4FTV6.js} +108 -6
- package/dist/chunk-AOG4FTV6.js.map +1 -0
- package/dist/{chunk-57QDBEHQ.js → chunk-ZHS52OT4.js} +3 -9
- package/dist/chunk-ZHS52OT4.js.map +1 -0
- package/dist/codes/index.d.ts +2 -2
- package/dist/codes/index.js +2 -2
- package/dist/compiled-types/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/{governance-BLsyk56o.d.ts → governance-B88uR3Zq.d.ts} +218 -1
- package/dist/{index-h_yWj15D.d.ts → index-DbkPE46t.d.ts} +40 -0
- package/dist/index.d.ts +429 -28
- package/dist/index.js +326 -33
- package/dist/index.js.map +1 -1
- package/dist/react-types.d.ts +1 -1
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/package.json +1 -1
- package/src/agent-format.test.ts +1 -0
- package/src/canonical-bridge.ts +46 -0
- package/src/canonical-direction.ts +118 -0
- package/src/codes/codes.ts +9 -0
- package/src/contract/preimage.test.ts +40 -0
- package/src/contract/preimage.ts +20 -0
- package/src/effective-governance-inputs.test.ts +52 -0
- package/src/effective-governance-inputs.ts +106 -0
- package/src/facts/builders.ts +25 -1
- package/src/facts/compile.ts +20 -2
- package/src/facts/fact-index.ts +4 -0
- package/src/facts/types.ts +25 -6
- package/src/governance-integrity.test.ts +22 -0
- package/src/governance-integrity.ts +10 -4
- package/src/governance.test.ts +67 -0
- package/src/governance.ts +76 -0
- package/src/index.ts +27 -4
- package/src/rules/__tests__/fix-emission-invariant.test.ts +35 -4
- package/src/rules/components-prefer-library.test.ts +103 -0
- package/src/rules/components-prefer-library.ts +30 -4
- package/src/rules/fix-availability.ts +1 -0
- package/src/rules/index.ts +7 -0
- package/src/rules/jsx-preferred-import-path.ts +45 -9
- package/src/rules/rules.test.ts +202 -6
- package/src/rules/spacing-resolution.ts +2 -2
- package/src/rules/styles-no-raw-color.test.ts +6 -1
- package/src/rules/styles-no-raw-color.ts +3 -2
- package/src/rules/styles-no-raw-dimensions.ts +5 -7
- package/src/rules/styles-no-raw-spacing.ts +5 -2
- package/src/rules/styles-no-raw-typography.ts +7 -4
- package/src/rules/taxonomy.test.ts +3 -0
- package/src/rules/tiers.ts +2 -0
- package/src/rules/token-candidates.ts +36 -0
- package/src/rules/tokens-require-dual-fallback.ts +2 -1
- package/src/rules/tokens-upstream-drift.test.ts +111 -0
- package/src/rules/tokens-upstream-drift.ts +57 -0
- package/src/rules/utils.ts +22 -14
- package/src/schema.ts +13 -0
- package/src/schemas/index.ts +5 -8
- package/src/token-types.ts +71 -1
- package/src/types.ts +6 -0
- package/dist/chunk-57QDBEHQ.js.map +0 -1
- package/dist/chunk-HXGE3O2F.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
bridgeSourceViolations,
|
|
50
50
|
byCode,
|
|
51
51
|
byRuleId,
|
|
52
|
+
canonicalBridgeV1Schema,
|
|
52
53
|
canonicalJson,
|
|
53
54
|
compileComponentFacts,
|
|
54
55
|
compileGlobalGovernanceFacts,
|
|
@@ -111,7 +112,7 @@ import {
|
|
|
111
112
|
ownedImportsEqual,
|
|
112
113
|
resolveComponentGovernance,
|
|
113
114
|
scaleGovernanceRecordSchema
|
|
114
|
-
} from "./chunk-
|
|
115
|
+
} from "./chunk-AOG4FTV6.js";
|
|
115
116
|
import {
|
|
116
117
|
AGENT_FORMAT_SCHEMA_VERSION,
|
|
117
118
|
agentErrorEnvelopeSchema,
|
|
@@ -136,7 +137,7 @@ import {
|
|
|
136
137
|
suppressionDirectiveSchema,
|
|
137
138
|
validatorResultSchema,
|
|
138
139
|
violationSchema
|
|
139
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-ZHS52OT4.js";
|
|
140
141
|
import {
|
|
141
142
|
OWNED_PACKAGE_IDENTITY_EPOCH,
|
|
142
143
|
OWNED_PACKAGE_IDENTITY_SOURCE_SHA256,
|
|
@@ -1687,6 +1688,14 @@ var tokenConfigSchema = z4.object({
|
|
|
1687
1688
|
// vocabulary seeds the known-token set (#7c). A packages-only tokens block
|
|
1688
1689
|
// is valid — see the superRefine below.
|
|
1689
1690
|
packages: z4.array(z4.string().min(1)).optional(),
|
|
1691
|
+
aliases: z4.record(z4.string().min(1)).optional(),
|
|
1692
|
+
upstream: z4.array(
|
|
1693
|
+
z4.object({
|
|
1694
|
+
packageName: z4.string().min(1),
|
|
1695
|
+
version: z4.string().min(1).optional(),
|
|
1696
|
+
digest: z4.string().regex(/^[a-f0-9]{64}$/u).optional()
|
|
1697
|
+
})
|
|
1698
|
+
).optional(),
|
|
1690
1699
|
exclude: z4.array(repoRelativePathSchema).optional(),
|
|
1691
1700
|
themeSelectors: z4.record(z4.string()).optional(),
|
|
1692
1701
|
enabled: z4.boolean().optional(),
|
|
@@ -4775,13 +4784,20 @@ function isExemptColor(value, except) {
|
|
|
4775
4784
|
}
|
|
4776
4785
|
return false;
|
|
4777
4786
|
}
|
|
4778
|
-
function tokenReference(
|
|
4779
|
-
if (
|
|
4780
|
-
|
|
4781
|
-
|
|
4787
|
+
function tokenReference(token) {
|
|
4788
|
+
if (token.referenceFormat === "css-var") {
|
|
4789
|
+
const cssVarName = [token.name, ...token.sourceNames ?? []].find(
|
|
4790
|
+
(name) => name.startsWith("--")
|
|
4791
|
+
);
|
|
4792
|
+
return cssVarName ? `var(${cssVarName})` : token.name;
|
|
4793
|
+
}
|
|
4794
|
+
if (token.referenceFormat === "scss-var") {
|
|
4795
|
+
return [token.name, ...token.sourceNames ?? []].find((name) => name.startsWith("$")) ?? (token.name.startsWith("--") ? `$${token.name.slice(2)}` : token.name);
|
|
4796
|
+
}
|
|
4797
|
+
return token.sourceNames?.[0] ?? token.name;
|
|
4782
4798
|
}
|
|
4783
4799
|
function isApplicableTokenReference(referenceFormat) {
|
|
4784
|
-
return referenceFormat ===
|
|
4800
|
+
return referenceFormat === "css-var" || referenceFormat === "scss-var";
|
|
4785
4801
|
}
|
|
4786
4802
|
|
|
4787
4803
|
// src/rules/a11y-required-accessible-name.ts
|
|
@@ -4938,6 +4954,77 @@ function isEnforceableHtmlEquivalent(args) {
|
|
|
4938
4954
|
return builtIn !== null && builtIn.canonical === args.canonical;
|
|
4939
4955
|
}
|
|
4940
4956
|
|
|
4957
|
+
// src/canonical-direction.ts
|
|
4958
|
+
function projectCanonicalDirectionConflicts(ix, canonicalSources) {
|
|
4959
|
+
const npmSources = canonicalSources.filter(
|
|
4960
|
+
(source) => source.kind === "npm"
|
|
4961
|
+
);
|
|
4962
|
+
if (npmSources.length === 0) return [];
|
|
4963
|
+
const identityByComponent = new Map(
|
|
4964
|
+
ix.byKind("component_identity").map((identity) => [identity.componentKey, identity])
|
|
4965
|
+
);
|
|
4966
|
+
const conflicts = [];
|
|
4967
|
+
for (const definition of ix.byKind("component_definition")) {
|
|
4968
|
+
if (!definition.exported || definition.renderRoot.resolution !== "canonical") continue;
|
|
4969
|
+
const target = npmTarget(definition.renderRoot.canonical, npmSources);
|
|
4970
|
+
if (!target) continue;
|
|
4971
|
+
const identity = identityByComponent.get(definition.componentKey);
|
|
4972
|
+
if (identity?.decisionId && decisionTargetsExport(identity.canonicalTarget, target.exportName)) {
|
|
4973
|
+
if (identity.state === "shadow") continue;
|
|
4974
|
+
if (identity.state === "variant" && identity.confidence === "confirmed") {
|
|
4975
|
+
conflicts.push({
|
|
4976
|
+
...target,
|
|
4977
|
+
wrapperComponentKey: definition.componentKey,
|
|
4978
|
+
wrapperFile: definition.file,
|
|
4979
|
+
wrapperExportName: definition.exportName,
|
|
4980
|
+
state: "sanctioned"
|
|
4981
|
+
});
|
|
4982
|
+
continue;
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
conflicts.push({
|
|
4986
|
+
...target,
|
|
4987
|
+
wrapperComponentKey: definition.componentKey,
|
|
4988
|
+
wrapperFile: definition.file,
|
|
4989
|
+
wrapperExportName: definition.exportName,
|
|
4990
|
+
state: "unresolved"
|
|
4991
|
+
});
|
|
4992
|
+
}
|
|
4993
|
+
return conflicts.sort(
|
|
4994
|
+
(left, right) => left.packageName.localeCompare(right.packageName) || left.exportName.localeCompare(right.exportName) || left.wrapperComponentKey.localeCompare(right.wrapperComponentKey)
|
|
4995
|
+
);
|
|
4996
|
+
}
|
|
4997
|
+
function canonicalDirectionConflictsTarget(conflicts, importPath, exportName) {
|
|
4998
|
+
if (!importPath) return false;
|
|
4999
|
+
return conflicts.some(
|
|
5000
|
+
(conflict) => ownedImportMatchesRoot(importPath, conflict.packageName) && conflict.exportName === exportName
|
|
5001
|
+
);
|
|
5002
|
+
}
|
|
5003
|
+
function npmTarget(canonicalTarget, sources) {
|
|
5004
|
+
const separator = canonicalTarget.lastIndexOf("#");
|
|
5005
|
+
if (separator <= 0 || separator === canonicalTarget.length - 1) return null;
|
|
5006
|
+
const importPath = canonicalTarget.slice(0, separator);
|
|
5007
|
+
const imported = canonicalTarget.slice(separator + 1).split(".").at(-1);
|
|
5008
|
+
if (!imported) return null;
|
|
5009
|
+
for (const source of sources) {
|
|
5010
|
+
if (!ownedImportMatchesRoot(importPath, source.specifier)) continue;
|
|
5011
|
+
const subpath = packageSubpath(importPath, source.specifier);
|
|
5012
|
+
const exportName = imported === "default" ? subpath.split("/").filter(Boolean).at(-1) : imported;
|
|
5013
|
+
if (!exportName) continue;
|
|
5014
|
+
if (source.exclude?.includes(exportName)) continue;
|
|
5015
|
+
if (source.include && !source.include.includes(exportName)) continue;
|
|
5016
|
+
return { packageName: source.specifier, exportName };
|
|
5017
|
+
}
|
|
5018
|
+
return null;
|
|
5019
|
+
}
|
|
5020
|
+
function packageSubpath(importPath, packageName) {
|
|
5021
|
+
return importPath === packageName ? "" : importPath.slice(packageName.length + 1);
|
|
5022
|
+
}
|
|
5023
|
+
function decisionTargetsExport(target, exportName) {
|
|
5024
|
+
if (!target) return true;
|
|
5025
|
+
return target === exportName || target.split("#").at(-1)?.split(".").at(-1) === exportName;
|
|
5026
|
+
}
|
|
5027
|
+
|
|
4941
5028
|
// src/rules/components-prefer-library.ts
|
|
4942
5029
|
var RULE_ID2 = "components/prefer-library";
|
|
4943
5030
|
var RULE_VERSION2 = "1";
|
|
@@ -5081,8 +5168,12 @@ function findClassNameReimpl(node, classTokensByNode, targets) {
|
|
|
5081
5168
|
function ruleComponentsPreferLibrary(ix) {
|
|
5082
5169
|
const policy = ix.policy.ruleConfig(RULE_ID2);
|
|
5083
5170
|
if (!policy?.enabled) return [];
|
|
5084
|
-
const
|
|
5085
|
-
const
|
|
5171
|
+
const configuredSources = canonicalSourcesFromPolicy(policy.options?.canonicalSources);
|
|
5172
|
+
const conflicts = projectCanonicalDirectionConflicts(ix, configuredSources);
|
|
5173
|
+
const sources = configuredSources.map((source) => suppressConflictedExports(source, conflicts));
|
|
5174
|
+
const mappings = canonicalMappingsFromPolicy(policy.options?.canonicalMappings).filter(
|
|
5175
|
+
(mapping) => !canonicalDirectionConflictsTarget(conflicts, mapping.importPath, mapping.name)
|
|
5176
|
+
);
|
|
5086
5177
|
if (sources.length === 0 && mappings.length === 0) return [];
|
|
5087
5178
|
const importsByFileAndLocal = indexImportsByFileAndLocal(ix);
|
|
5088
5179
|
const propsByNode = indexPropsByNodeId(ix);
|
|
@@ -5345,6 +5436,16 @@ function ruleComponentsPreferLibrary(ix) {
|
|
|
5345
5436
|
}
|
|
5346
5437
|
return findings;
|
|
5347
5438
|
}
|
|
5439
|
+
function suppressConflictedExports(source, conflicts) {
|
|
5440
|
+
if (source.kind !== "npm") return source;
|
|
5441
|
+
const names = conflicts.filter((conflict) => ownedImportMatchesRoot(source.specifier, conflict.packageName)).map((conflict) => conflict.exportName);
|
|
5442
|
+
if (names.length === 0) return source;
|
|
5443
|
+
const blocked = new Set(names);
|
|
5444
|
+
if (source.include) {
|
|
5445
|
+
return { ...source, include: source.include.filter((name) => !blocked.has(name)) };
|
|
5446
|
+
}
|
|
5447
|
+
return { ...source, exclude: [.../* @__PURE__ */ new Set([...source.exclude ?? [], ...names])].sort() };
|
|
5448
|
+
}
|
|
5348
5449
|
function indexShadowRenderRootNodeIds(ix) {
|
|
5349
5450
|
const identities = ix.byKind("component_identity");
|
|
5350
5451
|
if (identities.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -6117,6 +6218,18 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6117
6218
|
const seen = /* @__PURE__ */ new Set();
|
|
6118
6219
|
for (const usage of ix.byKind("usage_import")) {
|
|
6119
6220
|
const matches = policies.filter((policy) => {
|
|
6221
|
+
if (policy.bridge) {
|
|
6222
|
+
if (policy.bridge.implementationFiles.some((file) => sameRepoPath(file, usage.file))) {
|
|
6223
|
+
return false;
|
|
6224
|
+
}
|
|
6225
|
+
if (!ownedImportMatchesRoot(usage.source, policy.from)) return false;
|
|
6226
|
+
if (ownedImportsEqual(usage.source, policy.from)) {
|
|
6227
|
+
return usage.imported === policy.bridge.underlyingExportName;
|
|
6228
|
+
}
|
|
6229
|
+
const subpath = usage.source.slice(policy.from.length + 1);
|
|
6230
|
+
const leaf = subpath.split("/").at(-1);
|
|
6231
|
+
return leaf === policy.bridge.underlyingExportName && (usage.imported === "default" || usage.imported === policy.bridge.underlyingExportName);
|
|
6232
|
+
}
|
|
6120
6233
|
if (!ownedImportsEqual(policy.from, usage.source)) return false;
|
|
6121
6234
|
if (policy.imported !== void 0 && policy.imported !== usage.imported) return false;
|
|
6122
6235
|
return true;
|
|
@@ -6132,7 +6245,7 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6132
6245
|
ruleId: RULE_ID7,
|
|
6133
6246
|
ruleVersion: RULE_VERSION8,
|
|
6134
6247
|
severity: policy.severity,
|
|
6135
|
-
message: `Import from "${usage.source}" should use "${policy.to}".`,
|
|
6248
|
+
message: policy.bridge ? `Import ${policy.bridge.underlyingExportName} through the confirmed local ${policy.bridge.localExportName} wrapper from "${policy.to}".` : `Import from "${usage.source}" should use "${policy.to}".`,
|
|
6136
6249
|
location: usage.location,
|
|
6137
6250
|
evidence: ix.evidence([usage.id, policy.id]),
|
|
6138
6251
|
fingerprintIdentity: {
|
|
@@ -6143,7 +6256,7 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6143
6256
|
line: usage.location.line,
|
|
6144
6257
|
column: usage.location.column
|
|
6145
6258
|
},
|
|
6146
|
-
fix: {
|
|
6259
|
+
fix: policy.bridge ? void 0 : {
|
|
6147
6260
|
kind: "replaceImport",
|
|
6148
6261
|
title: policy.because ?? `Replace import path with "${policy.to}"`,
|
|
6149
6262
|
from: usage.source,
|
|
@@ -6155,7 +6268,14 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6155
6268
|
suggestedImport: policy.to,
|
|
6156
6269
|
imported: usage.imported,
|
|
6157
6270
|
local: usage.local,
|
|
6158
|
-
rawValue: usage.source
|
|
6271
|
+
rawValue: usage.source,
|
|
6272
|
+
...policy.bridge ? {
|
|
6273
|
+
canonicalBridge: true,
|
|
6274
|
+
canonicalTarget: policy.bridge.componentKey,
|
|
6275
|
+
suggestedComponent: policy.bridge.localExportName,
|
|
6276
|
+
implementationFiles: policy.bridge.implementationFiles,
|
|
6277
|
+
decisionSource: policy.bridge.decisionSource
|
|
6278
|
+
} : {}
|
|
6159
6279
|
}
|
|
6160
6280
|
})
|
|
6161
6281
|
);
|
|
@@ -6163,6 +6283,12 @@ function ruleJsxPreferredImportPath(ix) {
|
|
|
6163
6283
|
}
|
|
6164
6284
|
return findings;
|
|
6165
6285
|
}
|
|
6286
|
+
function sameRepoPath(left, right) {
|
|
6287
|
+
return normalizeRepoPath(left) === normalizeRepoPath(right);
|
|
6288
|
+
}
|
|
6289
|
+
function normalizeRepoPath(path) {
|
|
6290
|
+
return path.replace(/\\/gu, "/").replace(/^\.\//u, "");
|
|
6291
|
+
}
|
|
6166
6292
|
|
|
6167
6293
|
// src/rules/props-invalid-value.ts
|
|
6168
6294
|
var RULE_ID8 = "props/invalid-value";
|
|
@@ -6373,13 +6499,36 @@ function srgbGamma(linear) {
|
|
|
6373
6499
|
return clamped <= 31308e-7 ? 12.92 * clamped : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
|
|
6374
6500
|
}
|
|
6375
6501
|
|
|
6502
|
+
// src/rules/token-candidates.ts
|
|
6503
|
+
function normalizedTokenValue(token) {
|
|
6504
|
+
const value = token.value.trim();
|
|
6505
|
+
return token.category === "color" ? normalizeColor(value) : value.replace(/\s+/g, " ");
|
|
6506
|
+
}
|
|
6507
|
+
function localTokenCandidates(ix, category) {
|
|
6508
|
+
const all = ix.tokens.list();
|
|
6509
|
+
const upstreamByName = /* @__PURE__ */ new Map();
|
|
6510
|
+
for (const token of all) {
|
|
6511
|
+
if (token.role !== "upstream") continue;
|
|
6512
|
+
const matches = upstreamByName.get(token.name) ?? [];
|
|
6513
|
+
matches.push(token);
|
|
6514
|
+
upstreamByName.set(token.name, matches);
|
|
6515
|
+
}
|
|
6516
|
+
return all.filter((token) => {
|
|
6517
|
+
if (token.role === "upstream" || category !== void 0 && token.category !== category) {
|
|
6518
|
+
return false;
|
|
6519
|
+
}
|
|
6520
|
+
const matches = upstreamByName.get(token.upstreamName ?? token.name) ?? [];
|
|
6521
|
+
return matches.length !== 1 || normalizedTokenValue(token) === normalizedTokenValue(matches[0]);
|
|
6522
|
+
});
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6376
6525
|
// src/rules/styles-no-raw-color.ts
|
|
6377
6526
|
var RULE_ID9 = "styles/no-raw-color";
|
|
6378
6527
|
var RULE_VERSION10 = "1";
|
|
6379
6528
|
function ruleStylesNoRawColor(ix) {
|
|
6380
6529
|
const policy = ix.policy.rawColorPolicy();
|
|
6381
6530
|
if (!policy) return [];
|
|
6382
|
-
const colorTokens = ix
|
|
6531
|
+
const colorTokens = localTokenCandidates(ix, "color");
|
|
6383
6532
|
const preferLabel = policy.prefer === "token" ? "design token" : "CSS variable";
|
|
6384
6533
|
const findings = [];
|
|
6385
6534
|
const advisorySeverity = policy.severity === "error" ? "warn" : policy.severity;
|
|
@@ -6633,7 +6782,7 @@ function normalizeColor2(value) {
|
|
|
6633
6782
|
return lower;
|
|
6634
6783
|
}
|
|
6635
6784
|
function buildTokenFix(property, rawValue, rawColor, match, ix) {
|
|
6636
|
-
const reference = tokenReference(match.token
|
|
6785
|
+
const reference = tokenReference(match.token);
|
|
6637
6786
|
const value = replaceRawColor(rawValue, rawColor, reference);
|
|
6638
6787
|
return emitFix(
|
|
6639
6788
|
{
|
|
@@ -6668,8 +6817,8 @@ function ruleStylesNoRawDimensions(ix) {
|
|
|
6668
6817
|
const policy = ix.policy.rawDimensionPolicy();
|
|
6669
6818
|
if (!policy) return [];
|
|
6670
6819
|
const appliesTo = new Set(policy.appliesTo);
|
|
6671
|
-
const spacingTokens = ix
|
|
6672
|
-
const radiusTokens = ix
|
|
6820
|
+
const spacingTokens = localTokenCandidates(ix, "spacing");
|
|
6821
|
+
const radiusTokens = localTokenCandidates(ix, "radius");
|
|
6673
6822
|
const preferLabel = policy.prefer === "token" ? "design token" : "CSS variable";
|
|
6674
6823
|
const findings = [];
|
|
6675
6824
|
for (const decl of ix.byKind("style_declaration")) {
|
|
@@ -6880,9 +7029,7 @@ function normalizeDimension(value) {
|
|
|
6880
7029
|
return `${parsed.value}${parsed.unit ?? "px"}`;
|
|
6881
7030
|
}
|
|
6882
7031
|
function tokenVar(token) {
|
|
6883
|
-
|
|
6884
|
-
const cssVarName = token.name.startsWith("--") ? token.name : `--${token.name.replace(/\./g, "-")}`;
|
|
6885
|
-
return `var(${cssVarName})`;
|
|
7032
|
+
return tokenReference(token);
|
|
6886
7033
|
}
|
|
6887
7034
|
|
|
6888
7035
|
// src/rules/spacing-resolution.ts
|
|
@@ -6920,7 +7067,7 @@ function resolveSpacingValue(input) {
|
|
|
6920
7067
|
continue;
|
|
6921
7068
|
}
|
|
6922
7069
|
anyViolation = true;
|
|
6923
|
-
const reference = tokenReference(exactToken
|
|
7070
|
+
const reference = tokenReference(exactToken);
|
|
6924
7071
|
if (!normalized.deterministicFix) allValuePreserving = false;
|
|
6925
7072
|
suggestedParts.push(reference);
|
|
6926
7073
|
if (!firstViolation) {
|
|
@@ -6952,7 +7099,7 @@ function resolveSpacingValue(input) {
|
|
|
6952
7099
|
} else {
|
|
6953
7100
|
const nearestToken = input.tokens.get(Math.abs(nearest));
|
|
6954
7101
|
suggestedToken = nearestToken?.name;
|
|
6955
|
-
suggestedPart = nearestToken ? tokenReference(nearestToken
|
|
7102
|
+
suggestedPart = nearestToken ? tokenReference(nearestToken) : input.bareNumberFix ? `${nearest}` : `${nearest}${input.scale.unit}`;
|
|
6956
7103
|
suggestedParts.push(suggestedPart);
|
|
6957
7104
|
if (!normalized.deterministicFix) reliableSuggestion = false;
|
|
6958
7105
|
}
|
|
@@ -7004,7 +7151,7 @@ function ruleStylesNoRawSpacing(ix) {
|
|
|
7004
7151
|
const lookupFor = (scale) => {
|
|
7005
7152
|
let lookup = lookupCache.get(scale.name);
|
|
7006
7153
|
if (!lookup) {
|
|
7007
|
-
lookup = buildSpacingTokenLookup(ix
|
|
7154
|
+
lookup = buildSpacingTokenLookup(localTokenCandidates(ix, "spacing"), scale);
|
|
7008
7155
|
lookupCache.set(scale.name, lookup);
|
|
7009
7156
|
}
|
|
7010
7157
|
return lookup;
|
|
@@ -7138,7 +7285,7 @@ function buildSpacingFix(property, checked, ix) {
|
|
|
7138
7285
|
deterministic: true
|
|
7139
7286
|
},
|
|
7140
7287
|
{
|
|
7141
|
-
symbols: tokenSymbolsInText(checked.suggestedValue),
|
|
7288
|
+
symbols: checked.matchedToken ? [checked.matchedToken] : tokenSymbolsInText(checked.suggestedValue),
|
|
7142
7289
|
editKind: "replaceStyleValue",
|
|
7143
7290
|
valuePreserving: checked.deterministicFix
|
|
7144
7291
|
},
|
|
@@ -7254,7 +7401,7 @@ function ruleStylesNoRawTypography(ix) {
|
|
|
7254
7401
|
}
|
|
7255
7402
|
function buildTypographyTokenLookup(ix, scale) {
|
|
7256
7403
|
const out = /* @__PURE__ */ new Map();
|
|
7257
|
-
for (const token of ix
|
|
7404
|
+
for (const token of localTokenCandidates(ix, "typography")) {
|
|
7258
7405
|
const parsed = parseLengthValue(token.value);
|
|
7259
7406
|
if (!parsed || parsed.unit === null) continue;
|
|
7260
7407
|
const normalized = normalizeLengthForScale(parsed, scale);
|
|
@@ -7276,7 +7423,7 @@ function checkFontSize(raw, allowed, scale, tokens, bareNumberFix = false) {
|
|
|
7276
7423
|
if (matchesScale(normalized.value, allowed)) {
|
|
7277
7424
|
if (!exactToken) return null;
|
|
7278
7425
|
return {
|
|
7279
|
-
suggestedValue: tokenReference(exactToken
|
|
7426
|
+
suggestedValue: tokenReference(exactToken),
|
|
7280
7427
|
fixEmittable: true,
|
|
7281
7428
|
deterministicFix: normalized.deterministicFix,
|
|
7282
7429
|
reason: "token-equivalent",
|
|
@@ -7288,7 +7435,7 @@ function checkFontSize(raw, allowed, scale, tokens, bareNumberFix = false) {
|
|
|
7288
7435
|
return { fixEmittable: false, deterministicFix: false, reason: "off-scale" };
|
|
7289
7436
|
}
|
|
7290
7437
|
const nearestToken = tokens.get(Math.abs(nearest));
|
|
7291
|
-
const suggestedValue = nearestToken ? tokenReference(nearestToken
|
|
7438
|
+
const suggestedValue = nearestToken ? tokenReference(nearestToken) : bareNumberFix ? `${nearest}` : `${nearest}${scale.unit}`;
|
|
7292
7439
|
return {
|
|
7293
7440
|
suggestedValue,
|
|
7294
7441
|
fixEmittable: normalized.deterministicFix,
|
|
@@ -7307,7 +7454,7 @@ function buildFix(property, checked, ix) {
|
|
|
7307
7454
|
deterministic: true
|
|
7308
7455
|
},
|
|
7309
7456
|
{
|
|
7310
|
-
symbols: tokenSymbolsInText(checked.suggestedValue),
|
|
7457
|
+
symbols: checked.matchedToken ? [checked.matchedToken] : tokenSymbolsInText(checked.suggestedValue),
|
|
7311
7458
|
editKind: "replaceStyleValue",
|
|
7312
7459
|
valuePreserving: checked.deterministicFix
|
|
7313
7460
|
},
|
|
@@ -7986,7 +8133,7 @@ var SASS_FILE = /\.(scss|sass)$/i;
|
|
|
7986
8133
|
function ruleTokensRequireDualFallback(ix) {
|
|
7987
8134
|
const policy = ix.policy.ruleConfig(RULE_ID20);
|
|
7988
8135
|
if (!policy?.enabled) return [];
|
|
7989
|
-
const tokensByCssVariable = indexTokensByCssVariable(ix
|
|
8136
|
+
const tokensByCssVariable = indexTokensByCssVariable(localTokenCandidates(ix));
|
|
7990
8137
|
const findings = [];
|
|
7991
8138
|
for (const decl of ix.byKind("style_declaration")) {
|
|
7992
8139
|
if (!SASS_FILE.test(decl.file)) continue;
|
|
@@ -8135,6 +8282,57 @@ function skipToCloseParen(value, openParenIndex) {
|
|
|
8135
8282
|
return value.length;
|
|
8136
8283
|
}
|
|
8137
8284
|
|
|
8285
|
+
// src/rules/tokens-upstream-drift.ts
|
|
8286
|
+
var RULE_ID22 = "tokens/upstream-drift";
|
|
8287
|
+
var RULE_VERSION23 = "1";
|
|
8288
|
+
function ruleTokensUpstreamDrift(ix) {
|
|
8289
|
+
const local = ix.tokens.list().filter((token) => token.role !== "upstream");
|
|
8290
|
+
const upstream = ix.tokens.list().filter((token) => token.role === "upstream");
|
|
8291
|
+
const upstreamByName = /* @__PURE__ */ new Map();
|
|
8292
|
+
for (const token of upstream) {
|
|
8293
|
+
const matches = upstreamByName.get(token.name) ?? [];
|
|
8294
|
+
matches.push(token);
|
|
8295
|
+
upstreamByName.set(token.name, matches);
|
|
8296
|
+
}
|
|
8297
|
+
const findings = [];
|
|
8298
|
+
for (const token of local) {
|
|
8299
|
+
const upstreamName = token.upstreamName ?? token.name;
|
|
8300
|
+
const matches = (upstreamByName.get(upstreamName) ?? []).sort(
|
|
8301
|
+
(a, b) => (a.sourceIdentity ?? "").localeCompare(b.sourceIdentity ?? "")
|
|
8302
|
+
);
|
|
8303
|
+
if (matches.length !== 1) continue;
|
|
8304
|
+
const expected = matches[0];
|
|
8305
|
+
if (normalizedTokenValue(token) === normalizedTokenValue(expected)) continue;
|
|
8306
|
+
const location = token.location ?? {
|
|
8307
|
+
file: token.sourceIdentity ?? "tokens",
|
|
8308
|
+
line: 1,
|
|
8309
|
+
column: 1
|
|
8310
|
+
};
|
|
8311
|
+
findings.push(
|
|
8312
|
+
makeFinding({
|
|
8313
|
+
ruleId: RULE_ID22,
|
|
8314
|
+
ruleVersion: RULE_VERSION23,
|
|
8315
|
+
severity: "warn",
|
|
8316
|
+
message: `Local token ${token.name} is ${token.value}, but upstream ${upstreamName} is ${expected.value}.`,
|
|
8317
|
+
location,
|
|
8318
|
+
evidence: ix.evidence([token.id, expected.id]),
|
|
8319
|
+
fingerprintIdentity: {
|
|
8320
|
+
local: token.name,
|
|
8321
|
+
upstream: upstreamName,
|
|
8322
|
+
localSource: token.sourceIdentity,
|
|
8323
|
+
upstreamSource: expected.sourceIdentity
|
|
8324
|
+
},
|
|
8325
|
+
attributes: {
|
|
8326
|
+
local: { name: token.name, value: token.value, location: token.location },
|
|
8327
|
+
upstream: { name: expected.name, value: expected.value, location: expected.location },
|
|
8328
|
+
autoFix: false
|
|
8329
|
+
}
|
|
8330
|
+
})
|
|
8331
|
+
);
|
|
8332
|
+
}
|
|
8333
|
+
return findings;
|
|
8334
|
+
}
|
|
8335
|
+
|
|
8138
8336
|
// src/rules/emit-gate.ts
|
|
8139
8337
|
var BLOCKING_RULE_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
8140
8338
|
"styles/no-raw-color",
|
|
@@ -8175,6 +8373,8 @@ var RULE_TIER = {
|
|
|
8175
8373
|
// canonical-component bypass (FUI1004)
|
|
8176
8374
|
"tokens/css-vars-must-be-defined": "contract",
|
|
8177
8375
|
// token drift (FUI2015)
|
|
8376
|
+
"tokens/upstream-drift": "contract",
|
|
8377
|
+
// authored local/upstream value drift (FUI2017)
|
|
8178
8378
|
// ---- Tier B — generic hygiene -----------------------------------------
|
|
8179
8379
|
"components/unknown-prop": "hygiene",
|
|
8180
8380
|
"components/forbidden-prop-value": "hygiene",
|
|
@@ -8218,7 +8418,8 @@ function hygieneTierRuleIds() {
|
|
|
8218
8418
|
var VOCABULARY_ORDER = [
|
|
8219
8419
|
"components/shadow-component",
|
|
8220
8420
|
"components/prefer-library",
|
|
8221
|
-
"tokens/css-vars-must-be-defined"
|
|
8421
|
+
"tokens/css-vars-must-be-defined",
|
|
8422
|
+
"tokens/upstream-drift"
|
|
8222
8423
|
];
|
|
8223
8424
|
function vocabularyRank(ruleId) {
|
|
8224
8425
|
const index = VOCABULARY_ORDER.indexOf(ruleId);
|
|
@@ -8342,6 +8543,11 @@ var RULES = [
|
|
|
8342
8543
|
version: "1",
|
|
8343
8544
|
run: ruleTokensCssVarsMustBeDefined
|
|
8344
8545
|
},
|
|
8546
|
+
{
|
|
8547
|
+
id: "tokens/upstream-drift",
|
|
8548
|
+
version: "1",
|
|
8549
|
+
run: ruleTokensUpstreamDrift
|
|
8550
|
+
},
|
|
8345
8551
|
{
|
|
8346
8552
|
id: "theme/no-theme-coupled-literal",
|
|
8347
8553
|
version: "1",
|
|
@@ -8431,19 +8637,23 @@ function evaluateGovernanceIntegrity(input) {
|
|
|
8431
8637
|
policyFamily.reason = "no governance policy resolved";
|
|
8432
8638
|
}
|
|
8433
8639
|
const componentsConfig = configs.get("components/prefer-library");
|
|
8434
|
-
const
|
|
8640
|
+
const confirmedBridges = input.policy?.canonicalBridges ?? [];
|
|
8641
|
+
const componentsArmed = confirmedBridges.length > 0 || componentsConfig?.enabled === true && hasEffectiveComponentVocabulary(componentsConfig.options);
|
|
8435
8642
|
const componentsFamily = {
|
|
8436
8643
|
id: "components",
|
|
8437
8644
|
armed: componentsArmed,
|
|
8438
|
-
rules: [
|
|
8645
|
+
rules: [
|
|
8646
|
+
...confirmedBridges.length > 0 ? ["imports/preferred-path"] : [],
|
|
8647
|
+
...componentsConfig?.enabled === true ? ["components/prefer-library"] : []
|
|
8648
|
+
]
|
|
8439
8649
|
};
|
|
8440
8650
|
if (!componentsArmed) {
|
|
8441
8651
|
if (componentsConfig?.enabled === true) {
|
|
8442
8652
|
componentsFamily.reason = "components/prefer-library enabled but no effective canonical source";
|
|
8443
|
-
componentsFamily.remediation = "add govern.
|
|
8653
|
+
componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
|
|
8444
8654
|
} else {
|
|
8445
8655
|
componentsFamily.reason = "components/prefer-library not enabled";
|
|
8446
|
-
componentsFamily.remediation = "add govern.
|
|
8656
|
+
componentsFamily.remediation = "add govern.canonicalBridges for local wrappers, govern.canonicalSources, or designSystem.path/packageName";
|
|
8447
8657
|
}
|
|
8448
8658
|
}
|
|
8449
8659
|
const cssVarsActive = input.cssVarsActive ?? policyDeclaresCssVars(input.policy);
|
|
@@ -8529,6 +8739,74 @@ function evaluateGovernanceIntegrity(input) {
|
|
|
8529
8739
|
};
|
|
8530
8740
|
}
|
|
8531
8741
|
|
|
8742
|
+
// src/canonical-bridge.ts
|
|
8743
|
+
function canonicalBridgeContractMappings(bridges) {
|
|
8744
|
+
return (bridges ?? []).map((bridge) => ({
|
|
8745
|
+
component: `${bridge.underlying.packageName}#${bridge.underlying.exportName}`,
|
|
8746
|
+
canonical: bridge.local.componentKey,
|
|
8747
|
+
status: "confirmed",
|
|
8748
|
+
importPath: bridge.local.moduleSpecifier,
|
|
8749
|
+
bridge: {
|
|
8750
|
+
underlyingPackageName: bridge.underlying.packageName,
|
|
8751
|
+
underlyingExportName: bridge.underlying.exportName,
|
|
8752
|
+
localComponentKey: bridge.local.componentKey,
|
|
8753
|
+
localExportName: bridge.local.exportName,
|
|
8754
|
+
implementationFiles: [...bridge.local.implementationFiles],
|
|
8755
|
+
decisionSource: bridge.decision.source
|
|
8756
|
+
}
|
|
8757
|
+
}));
|
|
8758
|
+
}
|
|
8759
|
+
|
|
8760
|
+
// src/effective-governance-inputs.ts
|
|
8761
|
+
var EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA = "effective-governance-inputs:v1";
|
|
8762
|
+
function compileEffectiveGovernanceInputs(options) {
|
|
8763
|
+
const projection = {
|
|
8764
|
+
schema: EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA,
|
|
8765
|
+
policySource: options.policySource,
|
|
8766
|
+
config: {
|
|
8767
|
+
path: options.configPath ? normalizePath2(options.configPath) : null,
|
|
8768
|
+
digest: options.configDigest
|
|
8769
|
+
},
|
|
8770
|
+
components: {
|
|
8771
|
+
canonicalSources: sortRecords(options.policy?.canonicalSources ?? []),
|
|
8772
|
+
canonicalBridges: sortRecords(options.policy?.canonicalBridges ?? []),
|
|
8773
|
+
definitionFiles: normalizePaths(options.componentDefinitionFiles)
|
|
8774
|
+
},
|
|
8775
|
+
tokens: {
|
|
8776
|
+
declared: normalizePaths(options.tokens.declared),
|
|
8777
|
+
loadedDefinitions: options.tokens.loadedDefinitions,
|
|
8778
|
+
diagnostics: [...options.tokens.diagnostics].sort(),
|
|
8779
|
+
...options.tokens.catalog ? {
|
|
8780
|
+
catalog: {
|
|
8781
|
+
...options.tokens.catalog,
|
|
8782
|
+
configuredSources: normalizePaths(options.tokens.catalog.configuredSources),
|
|
8783
|
+
parsedSources: normalizePaths(options.tokens.catalog.parsedSources),
|
|
8784
|
+
definitions: sortRecords(options.tokens.catalog.definitions),
|
|
8785
|
+
diagnostics: sortRecords(options.tokens.catalog.diagnostics),
|
|
8786
|
+
references: sortRecords(options.tokens.catalog.references),
|
|
8787
|
+
...options.tokens.catalog.sourceStates ? { sourceStates: sortRecords(options.tokens.catalog.sourceStates) } : {}
|
|
8788
|
+
}
|
|
8789
|
+
} : {}
|
|
8790
|
+
},
|
|
8791
|
+
eligibility: {
|
|
8792
|
+
include: normalizePaths(options.eligibility.include),
|
|
8793
|
+
exclude: normalizePaths(options.eligibility.exclude)
|
|
8794
|
+
}
|
|
8795
|
+
};
|
|
8796
|
+
return { ...projection, inputsHash: contractHash(projection) };
|
|
8797
|
+
}
|
|
8798
|
+
function normalizePaths(values) {
|
|
8799
|
+
return [...new Set(values.map(normalizePath2))].sort();
|
|
8800
|
+
}
|
|
8801
|
+
function normalizePath2(value) {
|
|
8802
|
+
return value.replace(/\\/gu, "/").replace(/^\.\//u, "");
|
|
8803
|
+
}
|
|
8804
|
+
function sortRecords(values) {
|
|
8805
|
+
return [...values].sort(
|
|
8806
|
+
(left, right) => canonicalPreimage(left).localeCompare(canonicalPreimage(right))
|
|
8807
|
+
);
|
|
8808
|
+
}
|
|
8809
|
+
|
|
8532
8810
|
// src/contract/preimage.ts
|
|
8533
8811
|
var CONTRACT_DOMAINS = ["components", "tokens", "canonicalMap", "policy"];
|
|
8534
8812
|
var CONTRACT_PREIMAGE_SCHEMA = "fcid-preimage:v1";
|
|
@@ -8584,6 +8862,15 @@ function projectCanonicalMap(mappings) {
|
|
|
8584
8862
|
component: mapping.component,
|
|
8585
8863
|
canonical: mapping.canonical,
|
|
8586
8864
|
importPath: mapping.importPath ? projectV1OwnedImportIdentity(mapping.importPath) : void 0,
|
|
8865
|
+
bridge: mapping.bridge ? {
|
|
8866
|
+
underlyingPackageName: projectV1OwnedImportIdentity(
|
|
8867
|
+
mapping.bridge.underlyingPackageName
|
|
8868
|
+
),
|
|
8869
|
+
underlyingExportName: mapping.bridge.underlyingExportName,
|
|
8870
|
+
localComponentKey: projectV1OwnedComponentId(mapping.bridge.localComponentKey),
|
|
8871
|
+
localExportName: mapping.bridge.localExportName,
|
|
8872
|
+
implementationFiles: [...mapping.bridge.implementationFiles].sort()
|
|
8873
|
+
} : void 0,
|
|
8587
8874
|
resolves: mapping.resolves ? sortCanonical(mapping.resolves) : void 0,
|
|
8588
8875
|
propMapping: mapping.propMapping ? sortCanonical(
|
|
8589
8876
|
mapping.propMapping.map((entry) => ({
|
|
@@ -9016,6 +9303,7 @@ export {
|
|
|
9016
9303
|
DEFAULTS,
|
|
9017
9304
|
DEFAULT_ENHANCED_STYLE_PROPERTIES,
|
|
9018
9305
|
DEFAULT_STYLE_PROPERTIES,
|
|
9306
|
+
EFFECTIVE_GOVERNANCE_INPUTS_SCHEMA,
|
|
9019
9307
|
EVIDENCE_ORDER,
|
|
9020
9308
|
EXPLAIN_URL_BASE,
|
|
9021
9309
|
FRAGMENTS_INTERNAL_RULE_IDS,
|
|
@@ -9070,6 +9358,9 @@ export {
|
|
|
9070
9358
|
byRuleId,
|
|
9071
9359
|
calculateDeltaE,
|
|
9072
9360
|
canBlock,
|
|
9361
|
+
canonicalBridgeContractMappings,
|
|
9362
|
+
canonicalBridgeV1Schema,
|
|
9363
|
+
canonicalDirectionConflictsTarget,
|
|
9073
9364
|
canonicalJson,
|
|
9074
9365
|
canonicalPreimage,
|
|
9075
9366
|
canonicalizeOwnedComponentId,
|
|
@@ -9092,6 +9383,7 @@ export {
|
|
|
9092
9383
|
compareStylesWithTokens,
|
|
9093
9384
|
compileBlock,
|
|
9094
9385
|
compileComponentFacts,
|
|
9386
|
+
compileEffectiveGovernanceInputs,
|
|
9095
9387
|
compileFragment,
|
|
9096
9388
|
compileGlobalGovernanceFacts,
|
|
9097
9389
|
compileRecipe,
|
|
@@ -9287,6 +9579,7 @@ export {
|
|
|
9287
9579
|
parseTokenFile,
|
|
9288
9580
|
parseTokens,
|
|
9289
9581
|
portableRepoPathError,
|
|
9582
|
+
projectCanonicalDirectionConflicts,
|
|
9290
9583
|
projectContractPreimage,
|
|
9291
9584
|
projectV1OwnedComponentId,
|
|
9292
9585
|
projectV1OwnedImportIdentity,
|