@usefragments/core 1.0.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.
Files changed (228) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-243QYRUF.js +2145 -0
  3. package/dist/chunk-243QYRUF.js.map +1 -0
  4. package/dist/chunk-3LLRNCPX.js +111 -0
  5. package/dist/chunk-3LLRNCPX.js.map +1 -0
  6. package/dist/chunk-EIYNNS77.js +48 -0
  7. package/dist/chunk-EIYNNS77.js.map +1 -0
  8. package/dist/chunk-JNBFJ34I.js +326 -0
  9. package/dist/chunk-JNBFJ34I.js.map +1 -0
  10. package/dist/chunk-R3X7UBHU.js +228 -0
  11. package/dist/chunk-R3X7UBHU.js.map +1 -0
  12. package/dist/chunk-SH4KPIYH.js +393 -0
  13. package/dist/chunk-SH4KPIYH.js.map +1 -0
  14. package/dist/chunk-V4VQB57N.js +56 -0
  15. package/dist/chunk-V4VQB57N.js.map +1 -0
  16. package/dist/chunk-X34IA4LR.js +319 -0
  17. package/dist/chunk-X34IA4LR.js.map +1 -0
  18. package/dist/chunk-YF65VYRY.js +493 -0
  19. package/dist/chunk-YF65VYRY.js.map +1 -0
  20. package/dist/codes/index.d.ts +40 -0
  21. package/dist/codes/index.js +23 -0
  22. package/dist/codes/index.js.map +1 -0
  23. package/dist/compiled-types/index.d.ts +5 -0
  24. package/dist/compiled-types/index.js +1 -0
  25. package/dist/compiled-types/index.js.map +1 -0
  26. package/dist/generate/index.d.ts +47 -0
  27. package/dist/generate/index.js +11 -0
  28. package/dist/generate/index.js.map +1 -0
  29. package/dist/governance-BOa3KyiJ.d.ts +3279 -0
  30. package/dist/graph/index.d.ts +66 -0
  31. package/dist/graph/index.js +17 -0
  32. package/dist/graph/index.js.map +1 -0
  33. package/dist/index.d.ts +11388 -0
  34. package/dist/index.js +8711 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/package-identity.d.ts +42 -0
  37. package/dist/package-identity.js +23 -0
  38. package/dist/package-identity.js.map +1 -0
  39. package/dist/preview/index.d.ts +484 -0
  40. package/dist/preview/index.js +1129 -0
  41. package/dist/preview/index.js.map +1 -0
  42. package/dist/react-types.d.ts +22 -0
  43. package/dist/react-types.js +1 -0
  44. package/dist/react-types.js.map +1 -0
  45. package/dist/registry.d.ts +2367 -0
  46. package/dist/registry.js +62 -0
  47. package/dist/registry.js.map +1 -0
  48. package/dist/schemas/index.d.ts +2584 -0
  49. package/dist/schemas/index.js +55 -0
  50. package/dist/schemas/index.js.map +1 -0
  51. package/dist/severity.d.ts +18 -0
  52. package/dist/severity.js +27 -0
  53. package/dist/severity.js.map +1 -0
  54. package/dist/test-utils.d.ts +14 -0
  55. package/dist/test-utils.js +63 -0
  56. package/dist/test-utils.js.map +1 -0
  57. package/dist/topology/index.d.ts +48 -0
  58. package/dist/topology/index.js +7 -0
  59. package/dist/topology/index.js.map +1 -0
  60. package/dist/types-xJ2xyp_G.d.ts +119 -0
  61. package/package.json +120 -0
  62. package/src/__tests__/contract-parser.test.ts +614 -0
  63. package/src/__tests__/dtcg-generators.test.ts +439 -0
  64. package/src/__tests__/dtcg-parser.test.ts +884 -0
  65. package/src/__tests__/preview-runtime.test.tsx +111 -0
  66. package/src/__tests__/schema.test.ts +31 -0
  67. package/src/__tests__/severity.test.ts +34 -0
  68. package/src/__tests__/style-comparison.test.ts +662 -0
  69. package/src/__tests__/tokens-compat.test.ts +51 -0
  70. package/src/agent-format.test.ts +105 -0
  71. package/src/agent-format.ts +141 -0
  72. package/src/bundle.ts +149 -0
  73. package/src/codes/__tests__/codes.test.ts +73 -0
  74. package/src/codes/codes.ts +463 -0
  75. package/src/codes/index.ts +7 -0
  76. package/src/codes/source-finding-bridge.ts +113 -0
  77. package/src/compiled-types/index.ts +293 -0
  78. package/src/component-contract.ts +231 -0
  79. package/src/component-discovery.ts +80 -0
  80. package/src/composition.test.ts +262 -0
  81. package/src/composition.ts +318 -0
  82. package/src/config-paths.test.ts +78 -0
  83. package/src/config-paths.ts +47 -0
  84. package/src/config.ts +18 -0
  85. package/src/conform-prove.test.ts +274 -0
  86. package/src/conform-prove.ts +326 -0
  87. package/src/conform.ts +160 -0
  88. package/src/constants.ts +114 -0
  89. package/src/context.ts +2 -0
  90. package/src/contract/fixture.ts +110 -0
  91. package/src/contract/hash.test.ts +86 -0
  92. package/src/contract/hash.ts +195 -0
  93. package/src/contract/index.ts +43 -0
  94. package/src/contract/preimage.test.ts +254 -0
  95. package/src/contract/preimage.ts +365 -0
  96. package/src/contract/stamp.test.ts +100 -0
  97. package/src/contract/stamp.ts +105 -0
  98. package/src/contract-parser.ts +141 -0
  99. package/src/defineFragment.test.ts +257 -0
  100. package/src/defineFragment.ts +293 -0
  101. package/src/dtcg-generators.ts +324 -0
  102. package/src/dtcg-parser.ts +1 -0
  103. package/src/dtcg.ts +237 -0
  104. package/src/facts/__snapshots__/facts.test.ts.snap +124 -0
  105. package/src/facts/builders.ts +835 -0
  106. package/src/facts/compile.ts +422 -0
  107. package/src/facts/fact-index.ts +394 -0
  108. package/src/facts/facts.test.ts +494 -0
  109. package/src/facts/ids.ts +125 -0
  110. package/src/facts/index.ts +119 -0
  111. package/src/facts/types.ts +614 -0
  112. package/src/figma.ts +263 -0
  113. package/src/fragment-types.ts +214 -0
  114. package/src/generate/generate.test.ts +594 -0
  115. package/src/generate/index.ts +438 -0
  116. package/src/governance-integrity.test.ts +186 -0
  117. package/src/governance-integrity.ts +282 -0
  118. package/src/governance.test.ts +300 -0
  119. package/src/governance.ts +605 -0
  120. package/src/governance.type-test.ts +75 -0
  121. package/src/graph/engine.ts +446 -0
  122. package/src/graph/graph.test.ts +746 -0
  123. package/src/graph/index.ts +32 -0
  124. package/src/graph/serialization.ts +55 -0
  125. package/src/graph/types.ts +178 -0
  126. package/src/index.ts +891 -0
  127. package/src/local-canonical.ts +102 -0
  128. package/src/mcp-snapshot.ts +222 -0
  129. package/src/package-identity-match.ts +33 -0
  130. package/src/package-identity.generated.ts +233 -0
  131. package/src/package-identity.test.ts +99 -0
  132. package/src/package-identity.ts +157 -0
  133. package/src/performance-presets.ts +142 -0
  134. package/src/preview/comparison-artifact.test.ts +228 -0
  135. package/src/preview/comparison-artifact.ts +181 -0
  136. package/src/preview/comparison-context.ts +93 -0
  137. package/src/preview/comparison-evidence.ts +112 -0
  138. package/src/preview/comparison-format.ts +21 -0
  139. package/src/preview/comparison-presentation.ts +187 -0
  140. package/src/preview/comparison-references.test.ts +430 -0
  141. package/src/preview/comparison-references.ts +72 -0
  142. package/src/preview/comparison-score.ts +59 -0
  143. package/src/preview/comparison-styles.ts +197 -0
  144. package/src/preview/comparison.test.ts +559 -0
  145. package/src/preview/comparison.ts +488 -0
  146. package/src/preview/index.ts +10 -0
  147. package/src/preview/types.ts +309 -0
  148. package/src/preview/validation.test.ts +290 -0
  149. package/src/preview/validation.ts +356 -0
  150. package/src/preview-runtime.tsx +144 -0
  151. package/src/raw-html-canonical.test.ts +41 -0
  152. package/src/raw-html-canonical.ts +179 -0
  153. package/src/react-types.ts +21 -0
  154. package/src/registry-install-plan.ts +344 -0
  155. package/src/registry.test.ts +245 -0
  156. package/src/registry.ts +369 -0
  157. package/src/rules/a11y-required-accessible-name.ts +93 -0
  158. package/src/rules/color-math.test.ts +72 -0
  159. package/src/rules/color-math.ts +192 -0
  160. package/src/rules/components-forbidden-prop-value.ts +108 -0
  161. package/src/rules/components-prefer-library.ts +943 -0
  162. package/src/rules/components-unknown-prop.ts +94 -0
  163. package/src/rules/composition-pattern-schema.ts +125 -0
  164. package/src/rules/composition-pattern.test.ts +320 -0
  165. package/src/rules/composition-pattern.ts +271 -0
  166. package/src/rules/emit-gate.test.ts +42 -0
  167. package/src/rules/emit-gate.ts +60 -0
  168. package/src/rules/finding.ts +54 -0
  169. package/src/rules/index.ts +235 -0
  170. package/src/rules/jsx-preferred-component.ts +68 -0
  171. package/src/rules/jsx-preferred-import-path.ts +67 -0
  172. package/src/rules/presets.ts +57 -0
  173. package/src/rules/props-invalid-value.ts +115 -0
  174. package/src/rules/rules.test.ts +3636 -0
  175. package/src/rules/spacing-resolution.ts +184 -0
  176. package/src/rules/styles-no-raw-color.ts +329 -0
  177. package/src/rules/styles-no-raw-dimensions.ts +357 -0
  178. package/src/rules/styles-no-raw-spacing.ts +236 -0
  179. package/src/rules/styles-no-raw-typography.ts +237 -0
  180. package/src/rules/tailwind-arbitrary-color.test.ts +128 -0
  181. package/src/rules/tailwind-arbitrary-color.ts +93 -0
  182. package/src/rules/tailwind-arbitrary-spacing.test.ts +157 -0
  183. package/src/rules/tailwind-arbitrary-spacing.ts +120 -0
  184. package/src/rules/tailwind-forbidden-palette.test.ts +116 -0
  185. package/src/rules/tailwind-forbidden-palette.ts +107 -0
  186. package/src/rules/tailwind-off-scale-spacing-token.test.ts +100 -0
  187. package/src/rules/tailwind-off-scale-spacing-token.ts +108 -0
  188. package/src/rules/tailwind-raw-color-via-token.test.ts +108 -0
  189. package/src/rules/tailwind-raw-color-via-token.ts +82 -0
  190. package/src/rules/tailwind-rule-utils.ts +106 -0
  191. package/src/rules/tailwind-unknown-class.test.ts +157 -0
  192. package/src/rules/tailwind-unknown-class.ts +121 -0
  193. package/src/rules/taxonomy.test.ts +238 -0
  194. package/src/rules/theme-no-theme-coupled-literal.ts +132 -0
  195. package/src/rules/tiers.ts +111 -0
  196. package/src/rules/tokens-css-vars-must-be-defined.test.ts +244 -0
  197. package/src/rules/tokens-css-vars-must-be-defined.ts +156 -0
  198. package/src/rules/tokens-require-dual-fallback.ts +78 -0
  199. package/src/rules/types.ts +27 -0
  200. package/src/rules/utils.test.ts +110 -0
  201. package/src/rules/utils.ts +599 -0
  202. package/src/schema.ts +427 -0
  203. package/src/schemas/index.ts +275 -0
  204. package/src/schemas/schemas.test.ts +124 -0
  205. package/src/severity.ts +54 -0
  206. package/src/storyAdapter.test.ts +571 -0
  207. package/src/storyAdapter.ts +761 -0
  208. package/src/storyFilters.test.ts +350 -0
  209. package/src/storyFilters.ts +253 -0
  210. package/src/storybook-csf.ts +11 -0
  211. package/src/style-comparison.ts +423 -0
  212. package/src/test-utils.ts +68 -0
  213. package/src/token-parser.ts +2 -0
  214. package/src/token-types.ts +287 -0
  215. package/src/tokens/categories.ts +131 -0
  216. package/src/tokens/color.ts +193 -0
  217. package/src/tokens/design-token-parser.test.ts +159 -0
  218. package/src/tokens/design-token-parser.ts +402 -0
  219. package/src/tokens/dtcg.ts +428 -0
  220. package/src/tokens/index.ts +130 -0
  221. package/src/tokens/lookup.ts +19 -0
  222. package/src/tokens/scss.ts +171 -0
  223. package/src/tokens/tailwind-v4.ts +187 -0
  224. package/src/tokens/types.ts +43 -0
  225. package/src/topology/index.ts +9 -0
  226. package/src/topology/resolve-area.test.ts +174 -0
  227. package/src/topology/resolve-area.ts +215 -0
  228. package/src/types.ts +1057 -0
@@ -0,0 +1,78 @@
1
+ import type { FactIndex } from "../facts/index.js";
2
+
3
+ import { makeFinding } from "./finding.js";
4
+ import type { Finding } from "./types.js";
5
+ import { cssVariableNames } from "./utils.js";
6
+
7
+ export const RULE_ID = "tokens/require-dual-fallback";
8
+ export const RULE_VERSION = "1";
9
+
10
+ const CSS_VAR_WITH_OPTIONAL_FALLBACK = /var\(\s*(--[A-Za-z0-9_-]+)(\s*,\s*[^)]*)?\)/g;
11
+
12
+ /**
13
+ * The dual-fallback convention — `var(--token, $token)` — is only meaningful in
14
+ * a Sass pipeline, where the `$token` half resolves at build time. In plain CSS
15
+ * (`.css`/`.module.css`), inline styles, or CSS-in-JS, a `$token` literal is
16
+ * invalid and would silently break the declaration, so the rule (and its
17
+ * autofix) must never touch those files.
18
+ */
19
+ const SASS_FILE = /\.(scss|sass)$/i;
20
+
21
+ export function ruleTokensRequireDualFallback(ix: FactIndex): Finding[] {
22
+ const policy = ix.policy.ruleConfig(RULE_ID);
23
+ if (!policy?.enabled) return [];
24
+
25
+ const knownTokenVars = cssVariableNames(ix.tokens.list());
26
+ const findings: Finding[] = [];
27
+
28
+ for (const decl of ix.byKind("style_declaration")) {
29
+ if (!SASS_FILE.test(decl.file)) continue;
30
+ CSS_VAR_WITH_OPTIONAL_FALLBACK.lastIndex = 0;
31
+ let match: RegExpExecArray | null;
32
+ while ((match = CSS_VAR_WITH_OPTIONAL_FALLBACK.exec(decl.value)) !== null) {
33
+ const tokenName = match[1];
34
+ const hasFallback = match[2] !== undefined;
35
+ if (hasFallback) continue;
36
+ if (!knownTokenVars.has(tokenName) && !tokenName.startsWith("--fui-")) continue;
37
+
38
+ const rawValue = match[0];
39
+ const scssVar = tokenName.replace(/^--/, "$");
40
+ const replacement = `var(${tokenName}, ${scssVar})`;
41
+ const nextValue = decl.value.replace(rawValue, replacement);
42
+
43
+ findings.push(
44
+ makeFinding({
45
+ ruleId: RULE_ID,
46
+ ruleVersion: RULE_VERSION,
47
+ severity: policy.severity ?? "warn",
48
+ message: `var(${tokenName}) is missing an SCSS fallback. Use var(${tokenName}, ${scssVar}).`,
49
+ location: decl.location,
50
+ evidence: ix.evidence([decl.id, policy.id]),
51
+ fingerprintIdentity: {
52
+ file: decl.file,
53
+ selector: decl.selector,
54
+ declarationPath: decl.declarationPath,
55
+ property: decl.property,
56
+ tokenName,
57
+ },
58
+ fix: {
59
+ kind: "replaceStyleValue",
60
+ title: `Add fallback for ${tokenName}`,
61
+ property: decl.property,
62
+ value: nextValue,
63
+ deterministic: true,
64
+ },
65
+ attributes: {
66
+ property: decl.property,
67
+ rawValue: decl.value,
68
+ tokenName,
69
+ suggestedValue: nextValue,
70
+ source: "css",
71
+ },
72
+ }),
73
+ );
74
+ }
75
+ }
76
+
77
+ return findings;
78
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Rule output — `Finding` and `FindingFix`.
3
+ *
4
+ * Findings are emitted by pure rule functions over `FactIndex`. Every finding
5
+ * carries:
6
+ * - `ruleId` and `ruleVersion` so Cloud can group, version, and re-run.
7
+ * - `fingerprint`, a deterministic hash of (ruleId + identity tuple). Two
8
+ * scans of unchanged source produce the same fingerprint, so Cloud can
9
+ * dedupe and preserve event history.
10
+ * - `evidence`, a non-empty list of fact IDs that justify the finding.
11
+ * Phase 5 contract: a rule must never emit a finding without evidence.
12
+ * - `location`, the source span the user should look at. Optional only for
13
+ * facts that are not source-anchored (none in Phase 5).
14
+ *
15
+ * Fix records are pure descriptions; applying them to source is a Phase 6
16
+ * concern that lives in the CLI.
17
+ */
18
+
19
+ export type {
20
+ Finding,
21
+ FindingFix,
22
+ FindingReplaceClassTokenFix,
23
+ FindingReplaceComponentFix,
24
+ FindingReplaceImportFix,
25
+ FindingReplacePropValueFix,
26
+ FindingReplaceStyleValueFix,
27
+ } from "../schemas/index.js";
@@ -0,0 +1,110 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { FactIndex, makeUsageNodeFact } from "../index.js";
4
+ import type { UsageNodeFact } from "../index.js";
5
+ import { regionsByPrefix, type Region } from "./utils.js";
6
+
7
+ /**
8
+ * Build a `usage_node` fact and add it to the index. Tests assert container /
9
+ * member *relationships*, never literal `nodePath` strings — segments are
10
+ * opaque (per the usage-node-fact contract).
11
+ */
12
+ function node(
13
+ ix: FactIndex,
14
+ nodePath: string,
15
+ element: string,
16
+ file = "src/App.tsx"
17
+ ): UsageNodeFact {
18
+ const fact = makeUsageNodeFact({
19
+ file,
20
+ nodePath,
21
+ element,
22
+ location: { file, line: 1, column: 1 },
23
+ });
24
+ ix.add(fact);
25
+ return fact;
26
+ }
27
+
28
+ function regionFor(regions: Region[], container: UsageNodeFact): Region {
29
+ const region = regions.find((r) => r.container.id === container.id);
30
+ if (!region) throw new Error(`no region for container ${container.nodePath}`);
31
+ return region;
32
+ }
33
+
34
+ const memberIds = (region: Region) => new Set(region.members.map((m) => m.id));
35
+
36
+ describe("regionsByPrefix", () => {
37
+ // ACCEPTANCE §1 — sibling grouping under a container.
38
+ it("groups sibling children under their container", () => {
39
+ const ix = new FactIndex();
40
+ const n0 = node(ix, "0:0", "ButtonGroup");
41
+ const n1 = node(ix, "0:0/0", "Button");
42
+ const n2 = node(ix, "0:0/1", "Button");
43
+
44
+ const regions = regionsByPrefix(ix);
45
+ const members = memberIds(regionFor(regions, n0));
46
+ expect(members.has(n1.id)).toBe(true);
47
+ expect(members.has(n2.id)).toBe(true);
48
+
49
+ // n1 and n2 are siblings: neither contains the other.
50
+ expect(regionFor(regions, n1).members).toHaveLength(0);
51
+ expect(regionFor(regions, n2).members).toHaveLength(0);
52
+ });
53
+
54
+ // ACCEPTANCE §2 — opaque conditional segment stays in-region.
55
+ it("keeps an opaque conditional (expr:) child in the container's region", () => {
56
+ const ix = new FactIndex();
57
+ const n0 = node(ix, "0:0", "ButtonGroup");
58
+ const n1 = node(ix, "0:0/0", "Button");
59
+ const n2 = node(ix, "0:0/expr:0/0", "Button"); // {show && <Button/>}
60
+
61
+ const members = memberIds(regionFor(regionsByPrefix(ix), n0));
62
+ expect(members.has(n1.id)).toBe(true);
63
+ // `expr:0` is one opaque segment, so `0:0` is still a strict prefix.
64
+ expect(members.has(n2.id)).toBe(true);
65
+ });
66
+
67
+ it("compares whole segments — distinct conditional branches stay separate", () => {
68
+ const ix = new FactIndex();
69
+ const branchA = node(ix, "0:0/expr:0/0", "Button");
70
+ const branchB = node(ix, "0:0/expr:1/0", "Button");
71
+
72
+ const regions = regionsByPrefix(ix);
73
+ expect(memberIds(regionFor(regions, branchA)).has(branchB.id)).toBe(false);
74
+ expect(memberIds(regionFor(regions, branchB)).has(branchA.id)).toBe(false);
75
+ });
76
+
77
+ it("includes all descendants, not only direct children", () => {
78
+ const ix = new FactIndex();
79
+ const root = node(ix, "0:0", "Card");
80
+ const child = node(ix, "0:0/0", "ButtonGroup");
81
+ const grandchild = node(ix, "0:0/0/0", "Button");
82
+
83
+ const members = memberIds(regionFor(regionsByPrefix(ix), root));
84
+ expect(members.has(child.id)).toBe(true);
85
+ expect(members.has(grandchild.id)).toBe(true);
86
+ });
87
+
88
+ it("scopes regions by file — identical paths in different files do not merge", () => {
89
+ const ix = new FactIndex();
90
+ const a0 = node(ix, "0:0", "ButtonGroup", "src/A.tsx");
91
+ const a1 = node(ix, "0:0/0", "Button", "src/A.tsx");
92
+ const b1 = node(ix, "0:0/0", "Button", "src/B.tsx");
93
+
94
+ const members = memberIds(regionFor(regionsByPrefix(ix), a0));
95
+ expect(members.has(a1.id)).toBe(true);
96
+ expect(members.has(b1.id)).toBe(false);
97
+ });
98
+
99
+ it("scopes regions by rootIndex — separate root trees in one file do not merge", () => {
100
+ const ix = new FactIndex();
101
+ const r0 = node(ix, "0:0", "ButtonGroup");
102
+ const r0child = node(ix, "0:0/0", "Button");
103
+ // Same segments as r0child but a different leading root index.
104
+ const r1child = node(ix, "1:0/0", "Button");
105
+
106
+ const members = memberIds(regionFor(regionsByPrefix(ix), r0));
107
+ expect(members.has(r0child.id)).toBe(true);
108
+ expect(members.has(r1child.id)).toBe(false);
109
+ });
110
+ });