@invariant-ui/cli 0.0.0-stage → 0.2.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 (190) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +250 -2
  3. package/dist/agent-kit/apply.js +232 -0
  4. package/dist/agent-kit/guidance.js +236 -0
  5. package/dist/agent-kit/index.js +5 -0
  6. package/dist/agent-kit/integrations.js +121 -0
  7. package/dist/agent-kit/plugin.js +115 -0
  8. package/dist/artifact-generator/index.js +75 -0
  9. package/dist/browser-verify/driver.js +2 -0
  10. package/dist/browser-verify/index.js +4 -0
  11. package/dist/browser-verify/playwright.js +82 -0
  12. package/dist/browser-verify/runner.js +175 -0
  13. package/dist/browser-verify/spec.js +103 -0
  14. package/dist/cli/args.js +77 -0
  15. package/dist/cli/baseline.js +64 -0
  16. package/dist/cli/cli.js +121 -0
  17. package/dist/cli/commands-browser.js +104 -0
  18. package/dist/cli/commands-discover.js +312 -0
  19. package/dist/cli/commands-enforcement.js +35 -0
  20. package/dist/cli/commands-governance.js +179 -0
  21. package/dist/cli/commands-hook.js +269 -0
  22. package/dist/cli/commands-patterns.js +324 -0
  23. package/dist/cli/commands-setup.js +485 -0
  24. package/dist/cli/commands-validate.js +157 -0
  25. package/dist/cli/commands.js +517 -0
  26. package/dist/cli/config.js +197 -0
  27. package/dist/cli/diff.js +66 -0
  28. package/dist/cli/enforcement-log.js +83 -0
  29. package/dist/cli/enforcement-report.js +96 -0
  30. package/dist/cli/errors.js +10 -0
  31. package/dist/cli/format.js +22 -0
  32. package/dist/cli/index.js +5 -0
  33. package/dist/cli/io.js +2 -0
  34. package/dist/cli/main.js +31 -0
  35. package/dist/cli/pipeline.js +262 -0
  36. package/dist/cli/publish.js +77 -0
  37. package/dist/cli/report.js +127 -0
  38. package/dist/cli/starter-rules.js +57 -0
  39. package/dist/cli/store.js +60 -0
  40. package/dist/cli/version.js +5 -0
  41. package/dist/cli/watch.js +68 -0
  42. package/dist/cli/workflow.js +52 -0
  43. package/dist/compiler-angular/adapter.js +115 -0
  44. package/dist/compiler-angular/analyze.js +222 -0
  45. package/dist/compiler-angular/classes.js +446 -0
  46. package/dist/compiler-angular/declared.js +285 -0
  47. package/dist/compiler-angular/emit.js +183 -0
  48. package/dist/compiler-angular/index.js +4 -0
  49. package/dist/compiler-angular/template.js +142 -0
  50. package/dist/compiler-core/adapter.js +2 -0
  51. package/dist/compiler-core/adapters/inventory.js +312 -0
  52. package/dist/compiler-core/compile.js +148 -0
  53. package/dist/compiler-core/component-facts.js +123 -0
  54. package/dist/compiler-core/discovery/docs.js +66 -0
  55. package/dist/compiler-core/discovery/entries.js +270 -0
  56. package/dist/compiler-core/discovery/exports.js +207 -0
  57. package/dist/compiler-core/discovery/graph.js +59 -0
  58. package/dist/compiler-core/discovery/index.js +103 -0
  59. package/dist/compiler-core/discovery/packages.js +148 -0
  60. package/dist/compiler-core/discovery/schema.js +144 -0
  61. package/dist/compiler-core/discovery/storybook.js +232 -0
  62. package/dist/compiler-core/discovery/support.js +82 -0
  63. package/dist/compiler-core/discovery/tokens.js +186 -0
  64. package/dist/compiler-core/discovery/tooling.js +146 -0
  65. package/dist/compiler-core/fs.js +89 -0
  66. package/dist/compiler-core/glob.js +3 -0
  67. package/dist/compiler-core/index.js +16 -0
  68. package/dist/compiler-core/ir-builder.js +68 -0
  69. package/dist/compiler-core/json-schema.js +11 -0
  70. package/dist/compiler-core/testing/index.js +81 -0
  71. package/dist/compiler-core/ts/ast.js +166 -0
  72. package/dist/compiler-core/ts/members.js +163 -0
  73. package/dist/compiler-core/ts/program.js +81 -0
  74. package/dist/compiler-core/ts/static-eval.js +145 -0
  75. package/dist/compiler-core/ts/tsconfig-paths.js +60 -0
  76. package/dist/compiler-core/walk.js +46 -0
  77. package/dist/compiler-ir/index.js +4 -0
  78. package/dist/compiler-ir/ir.js +67 -0
  79. package/dist/compiler-ir/json-schema.js +6 -0
  80. package/dist/compiler-ir/validate.js +116 -0
  81. package/dist/compiler-react/adapter.js +147 -0
  82. package/dist/compiler-react/analyze.js +199 -0
  83. package/dist/compiler-react/behavior.js +75 -0
  84. package/dist/compiler-react/detect.js +558 -0
  85. package/dist/compiler-react/emit.js +188 -0
  86. package/dist/compiler-react/index.js +2 -0
  87. package/dist/compiler-react/props.js +157 -0
  88. package/dist/compiler-react/styling.js +138 -0
  89. package/dist/compiler-react/usage.js +116 -0
  90. package/dist/compiler-react/variants.js +111 -0
  91. package/dist/compiler-storybook/adapter.js +253 -0
  92. package/dist/compiler-storybook/csf.js +521 -0
  93. package/dist/compiler-storybook/emit.js +202 -0
  94. package/dist/compiler-storybook/index-json.js +41 -0
  95. package/dist/compiler-storybook/index.js +5 -0
  96. package/dist/compiler-storybook/mdx.js +120 -0
  97. package/dist/compiler-tokens/adapter.js +158 -0
  98. package/dist/compiler-tokens/css.js +59 -0
  99. package/dist/compiler-tokens/index.js +5 -0
  100. package/dist/compiler-tokens/json-lines.js +106 -0
  101. package/dist/compiler-tokens/json-tokens.js +52 -0
  102. package/dist/compiler-vue/adapter.js +107 -0
  103. package/dist/compiler-vue/analyze.js +344 -0
  104. package/dist/compiler-vue/declared.js +343 -0
  105. package/dist/compiler-vue/emit.js +129 -0
  106. package/dist/compiler-vue/index.js +5 -0
  107. package/dist/compiler-vue/script.js +472 -0
  108. package/dist/compiler-vue/sfc.js +44 -0
  109. package/dist/compiler-vue/template.js +73 -0
  110. package/dist/contract/archetype-checks.js +153 -0
  111. package/dist/contract/components.js +102 -0
  112. package/dist/contract/conflicts.js +39 -0
  113. package/dist/contract/contract.js +46 -0
  114. package/dist/contract/evidence.js +84 -0
  115. package/dist/contract/fact-path.js +39 -0
  116. package/dist/contract/facts.js +62 -0
  117. package/dist/contract/index.js +16 -0
  118. package/dist/contract/integrity.js +21 -0
  119. package/dist/contract/json-schema.js +11 -0
  120. package/dist/contract/knowledge.js +95 -0
  121. package/dist/contract/pattern-checks.js +254 -0
  122. package/dist/contract/patterns.js +225 -0
  123. package/dist/contract/primitives.js +81 -0
  124. package/dist/contract/sources.js +30 -0
  125. package/dist/contract/tokens.js +41 -0
  126. package/dist/contract/validate.js +253 -0
  127. package/dist/contract-query/archetype.js +188 -0
  128. package/dist/contract-query/index.js +6 -0
  129. package/dist/contract-query/patterns.js +298 -0
  130. package/dist/contract-query/search.js +132 -0
  131. package/dist/contract-query/system.js +148 -0
  132. package/dist/contract-query/views.js +288 -0
  133. package/dist/governance/decisions.js +229 -0
  134. package/dist/governance/index.js +5 -0
  135. package/dist/governance/patterns-file.js +156 -0
  136. package/dist/governance/patterns.js +510 -0
  137. package/dist/governance/policies.js +148 -0
  138. package/dist/mcp/index.js +7 -0
  139. package/dist/mcp/loader.js +70 -0
  140. package/dist/mcp/protocol.js +27 -0
  141. package/dist/mcp/resources.js +138 -0
  142. package/dist/mcp/server.js +177 -0
  143. package/dist/mcp/stdio.js +28 -0
  144. package/dist/mcp/tools.js +267 -0
  145. package/dist/reconciliation/code.js +62 -0
  146. package/dist/reconciliation/components.js +401 -0
  147. package/dist/reconciliation/conflicts.js +126 -0
  148. package/dist/reconciliation/facts.js +41 -0
  149. package/dist/reconciliation/index.js +7 -0
  150. package/dist/reconciliation/reconcile.js +134 -0
  151. package/dist/reconciliation/resolve.js +158 -0
  152. package/dist/reconciliation/review.js +81 -0
  153. package/dist/reconciliation/storybook.js +370 -0
  154. package/dist/reconciliation/tokens.js +179 -0
  155. package/dist/reconciliation/util.js +55 -0
  156. package/dist/semantic-engine/additive.js +51 -0
  157. package/dist/semantic-engine/compounds.js +91 -0
  158. package/dist/semantic-engine/documents.js +157 -0
  159. package/dist/semantic-engine/enrich.js +42 -0
  160. package/dist/semantic-engine/index.js +7 -0
  161. package/dist/semantic-engine/infer.js +38 -0
  162. package/dist/semantic-engine/tokens.js +120 -0
  163. package/dist/shared/canonical.js +65 -0
  164. package/dist/shared/diagnostic.js +78 -0
  165. package/dist/shared/glob.js +98 -0
  166. package/dist/shared/index.js +4 -0
  167. package/dist/shared/json.js +2 -0
  168. package/dist/validation/card.js +50 -0
  169. package/dist/validation/discovery/arrangements.js +162 -0
  170. package/dist/validation/discovery/discover.js +70 -0
  171. package/dist/validation/discovery/index.js +8 -0
  172. package/dist/validation/discovery/model.js +127 -0
  173. package/dist/validation/discovery/promote.js +24 -0
  174. package/dist/validation/discovery/reviews.js +63 -0
  175. package/dist/validation/discovery/shapes.js +47 -0
  176. package/dist/validation/discovery/similar-files.js +146 -0
  177. package/dist/validation/discovery/util.js +47 -0
  178. package/dist/validation/index.js +11 -0
  179. package/dist/validation/interaction-facts.js +341 -0
  180. package/dist/validation/interaction-workflows.js +633 -0
  181. package/dist/validation/literals.js +251 -0
  182. package/dist/validation/pattern-rules.js +608 -0
  183. package/dist/validation/policy.js +241 -0
  184. package/dist/validation/report.js +163 -0
  185. package/dist/validation/rules.js +269 -0
  186. package/dist/validation/scan.js +295 -0
  187. package/dist/validation/text.js +34 -0
  188. package/dist/validation/validate-code.js +513 -0
  189. package/dist/validation/validate-usage.js +196 -0
  190. package/package.json +38 -4
@@ -0,0 +1,202 @@
1
+ import { excerpt, lineOf } from "../compiler-core/index.js";
2
+ export const CAPABILITIES = ["group.*", "story.*", "doc.*"];
3
+ /** Writes one story file: its meta (a `story-group`) and each story. */
4
+ export function emitCsf(builder, csf, index, indexPath) {
5
+ const at = (node, method = "ast") => builder.evidence({
6
+ sourceRef: `${csf.file}#${lineOf(node)}`,
7
+ location: { type: "file", path: csf.file, line: lineOf(node) },
8
+ method,
9
+ original: excerpt(node)
10
+ });
11
+ let groupRef;
12
+ if (csf.group) {
13
+ groupRef = builder.entity(`storybook:${csf.file}`, "story-group", csf.group.title ?? csf.file);
14
+ emitGroup(builder, groupRef, csf, csf.group, at);
15
+ if (index && indexPath) {
16
+ const entries = index.filter((e) => e.importPath.replace(/^\.\//, "") === indexPath);
17
+ const evidence = builder.evidence({
18
+ sourceRef: "storybook-static/index.json",
19
+ location: { type: "file", path: "storybook-static/index.json" },
20
+ method: "structured-parse"
21
+ });
22
+ builder.fact(groupRef, "group.index.entries", entries.map((e) => ({
23
+ id: e.id,
24
+ type: e.type,
25
+ ...(e.title ? { title: e.title } : {}),
26
+ ...(e.name ? { name: e.name } : {})
27
+ })), evidence);
28
+ }
29
+ }
30
+ for (const story of csf.stories)
31
+ emitStory(builder, csf, story, groupRef, at);
32
+ }
33
+ function emitGroup(builder, ref, csf, group, at) {
34
+ const node = at(group.node);
35
+ const put = (path, value, evidence = node, origin = "extracted", completeness) => builder.fact(ref, path, value, evidence, { origin, ...(completeness ? { completeness } : {}) });
36
+ put("group.file", csf.file);
37
+ if (group.title)
38
+ put("group.title", group.title);
39
+ else
40
+ put("group.titleAuto", true);
41
+ if (csf.selectionUncertain)
42
+ put("group.selectionUncertain", true);
43
+ if (csf.excluded.length > 0)
44
+ put("group.excludedExports", csf.excluded);
45
+ if (group.component) {
46
+ const ev = at(group.componentNode ?? group.node, "compiler-api");
47
+ const c = group.component;
48
+ put("group.component", {
49
+ text: c.text,
50
+ via: c.via,
51
+ ...(c.file ? { file: c.file } : {}),
52
+ ...(c.name ? { name: c.name } : {}),
53
+ ...(c.external ? { external: c.external } : {}),
54
+ ...(c.unresolvedModule ? { unresolvedModule: c.unresolvedModule } : {})
55
+ }, ev);
56
+ }
57
+ emitAnnotations(builder, ref, "group", group, at);
58
+ }
59
+ function emitStory(builder, csf, story, groupRef, at) {
60
+ const ref = builder.entity(`storybook:${csf.file}#${story.exportName}`, "story", story.name ?? story.exportName);
61
+ const node = at(story.node);
62
+ const put = (path, value, evidence = node, origin = "extracted") => builder.fact(ref, path, value, evidence, { origin });
63
+ put("story.exportName", story.exportName);
64
+ put("story.file", csf.file);
65
+ put("story.shape", story.shape);
66
+ if (story.name)
67
+ put("story.name", story.name);
68
+ if (groupRef)
69
+ put("story.group", groupRef);
70
+ if (story.description)
71
+ put("story.description", story.description, node, "authored");
72
+ put("story.hasRender", story.hasRender);
73
+ if (story.play) {
74
+ put("story.play.present", true);
75
+ if (story.play.calls.length > 0)
76
+ put("story.play.calls", story.play.calls);
77
+ if (story.play.steps.length > 0)
78
+ put("story.play.steps", story.play.steps);
79
+ }
80
+ if (story.composes.length > 0)
81
+ put("story.composes", story.composes, at(story.node, "compiler-api"));
82
+ emitAnnotations(builder, ref, "story", story, at);
83
+ }
84
+ function emitAnnotations(builder, ref, prefix, a, at) {
85
+ const put = (path, value, evidence, options = {}) => builder.fact(ref, `${prefix}.${path}`, value, evidence, {
86
+ origin: options.origin ?? "extracted",
87
+ ...(options.completeness ? { completeness: options.completeness } : {})
88
+ });
89
+ if (a.args.value && typeof a.args.value === "object" && !Array.isArray(a.args.value)) {
90
+ for (const [name, value] of Object.entries(a.args.value)) {
91
+ const node = a.argNodes.get(name) ?? a.argsNode;
92
+ if (node)
93
+ put(`args.${name}`, value, at(node));
94
+ }
95
+ }
96
+ if (a.args.unresolved.length > 0 && a.argsNode)
97
+ put("argsUnresolved", [...new Set(a.args.unresolved)].sort(), at(a.argsNode), { completeness: "partial" });
98
+ if (a.args.spies.length > 0 && a.argsNode)
99
+ put("argsSpies", [...new Set(a.args.spies)].sort(), at(a.argsNode));
100
+ for (const [prop, info] of Object.entries(a.argTypes)) {
101
+ const node = a.argTypeNodes.get(prop);
102
+ if (node)
103
+ emitArgType(put, `argTypes.${prop}`, info, at(node));
104
+ }
105
+ const firstArgType = [...a.argTypeNodes.values()][0];
106
+ if (a.argTypesUnresolved.length > 0 && firstArgType)
107
+ put("argTypesUnresolved", [...new Set(a.argTypesUnresolved)].sort(), at(firstArgType), {
108
+ completeness: "partial"
109
+ });
110
+ const p = a.parameters;
111
+ if (a.nodes.parameters && p.keys.length > 0) {
112
+ const ev = at(a.nodes.parameters);
113
+ put("parameters.keys", p.keys, ev);
114
+ if (p.a11y !== undefined)
115
+ put("parameters.a11y", p.a11y, ev);
116
+ if (p.layout)
117
+ put("parameters.layout", p.layout, ev);
118
+ if (p.controls !== undefined)
119
+ put("parameters.controls", p.controls, ev);
120
+ if (p.docsDescription?.component)
121
+ put("docs.description", p.docsDescription.component, ev, { origin: "authored" });
122
+ }
123
+ if (a.nodes.tags && a.tags && a.tags.length > 0)
124
+ put("tags", a.tags, at(a.nodes.tags));
125
+ if (a.nodes.decorators)
126
+ put("hasDecorators", true, at(a.nodes.decorators));
127
+ if (a.nodes.loaders)
128
+ put("hasLoaders", true, at(a.nodes.loaders));
129
+ }
130
+ function emitArgType(put, base, info, ev) {
131
+ put(`${base}.declared`, true, ev);
132
+ if (info.control !== undefined)
133
+ put(`${base}.control`, info.control, ev);
134
+ if (info.options)
135
+ put(`${base}.options`, info.options, ev, {
136
+ completeness: info.optionsUnresolved ? "partial" : "complete"
137
+ });
138
+ else if (info.optionsUnresolved)
139
+ put(`${base}.optionsUnresolved`, true, ev, { completeness: "partial" });
140
+ // A description is prose a person wrote in the story file.
141
+ if (info.description)
142
+ put(`${base}.description`, info.description, ev, { origin: "authored" });
143
+ if (info.defaultSummary !== undefined)
144
+ put(`${base}.defaultSummary`, info.defaultSummary, ev);
145
+ if (info.mappingKeys)
146
+ put(`${base}.mappingKeys`, info.mappingKeys, ev);
147
+ if (info.action)
148
+ put(`${base}.action`, info.action, ev);
149
+ if (info.hidden)
150
+ put(`${base}.hidden`, true, ev);
151
+ if (info.category)
152
+ put(`${base}.category`, info.category, ev);
153
+ }
154
+ /** Writes an MDX documentation page. Prose is authored; imports and blocks are extracted. */
155
+ export function emitMdx(builder, doc) {
156
+ const ref = builder.entity(`storybook:${doc.path}`, "doc-page", doc.title ?? doc.path);
157
+ const structure = builder.evidence({
158
+ sourceRef: doc.path,
159
+ location: { type: "file", path: doc.path, line: 1 },
160
+ method: "document-parse"
161
+ });
162
+ const putStructure = (path, value, evidence = structure) => builder.fact(ref, path, value, evidence, { origin: "extracted" });
163
+ putStructure("doc.kind", "storybook-mdx");
164
+ putStructure("doc.file", doc.path);
165
+ if (doc.title)
166
+ builder.fact(ref, "doc.title", doc.title, structure, { origin: "authored" });
167
+ if (doc.metaTitle)
168
+ builder.fact(ref, "doc.meta.title", doc.metaTitle, structure, { origin: "authored" });
169
+ if (doc.metaOf) {
170
+ const ev = builder.evidence({
171
+ sourceRef: `${doc.path}#meta`,
172
+ location: { type: "file", path: doc.path, line: doc.metaOf.line },
173
+ method: "document-parse"
174
+ });
175
+ putStructure("doc.meta.of", { identifier: doc.metaOf.identifier, ...(doc.metaOf.file ? { file: doc.metaOf.file } : {}) }, ev);
176
+ }
177
+ if (doc.blocks.length > 0)
178
+ putStructure("doc.blocks", doc.blocks);
179
+ if (doc.storyRefs.length > 0) {
180
+ const first = doc.storyRefs[0];
181
+ const ev = builder.evidence({
182
+ sourceRef: `${doc.path}#stories`,
183
+ location: { type: "file", path: doc.path, line: first?.line ?? 1 },
184
+ method: "document-parse"
185
+ });
186
+ putStructure("doc.stories", doc.storyRefs.map(({ block, exportName, file }) => ({
187
+ block,
188
+ exportName,
189
+ ...(file ? { file } : {})
190
+ })), ev);
191
+ }
192
+ for (const [i, section] of doc.sections.entries()) {
193
+ const ev = builder.evidence({
194
+ sourceRef: `${doc.path}#${section.line}`,
195
+ location: { type: "file", path: doc.path, line: section.line },
196
+ method: "document-parse",
197
+ original: section.heading
198
+ });
199
+ builder.fact(ref, `doc.sections.s${i + 1}`, { heading: section.heading, level: section.level, text: section.text }, ev, { origin: "authored" });
200
+ }
201
+ }
202
+ //# sourceMappingURL=emit.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Reads a built Storybook's `index.json`. Its format is not documented as public API, so parsing is
3
+ * tolerant (`entries` or the older `stories`) and uses only the fields below. It is optional evidence
4
+ * about which story files a build contained, never a source of story identity.
5
+ */
6
+ export function parseBuiltIndex(text) {
7
+ let parsed;
8
+ try {
9
+ parsed = JSON.parse(text);
10
+ }
11
+ catch {
12
+ return undefined;
13
+ }
14
+ if (typeof parsed !== "object" || parsed === null)
15
+ return undefined;
16
+ const record = parsed;
17
+ const format = record.entries !== undefined ? "entries" : record.stories !== undefined ? "stories" : undefined;
18
+ const raw = format === "entries" ? record.entries : record.stories;
19
+ if (!format || typeof raw !== "object" || raw === null)
20
+ return undefined;
21
+ const values = Array.isArray(raw) ? raw : Object.values(raw);
22
+ const entries = [];
23
+ for (const value of values) {
24
+ if (typeof value !== "object" || value === null)
25
+ continue;
26
+ const e = value;
27
+ const importPath = typeof e.importPath === "string" ? e.importPath : undefined;
28
+ if (!importPath || typeof e.id !== "string")
29
+ continue;
30
+ entries.push({
31
+ id: e.id,
32
+ type: typeof e.type === "string" ? e.type : "story",
33
+ ...(typeof e.title === "string" ? { title: e.title } : {}),
34
+ ...(typeof e.name === "string" ? { name: e.name } : {}),
35
+ importPath,
36
+ tags: Array.isArray(e.tags) ? e.tags.filter((t) => typeof t === "string") : []
37
+ });
38
+ }
39
+ return { entries: entries.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)), format };
40
+ }
41
+ //# sourceMappingURL=index-json.js.map
@@ -0,0 +1,5 @@
1
+ export { ADAPTER_ID, ADAPTER_VERSION, storybookAdapter } from "./adapter.js";
2
+ export { extractCsf } from "./csf.js";
3
+ export { parseBuiltIndex } from "./index-json.js";
4
+ export { parseMdx } from "./mdx.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,120 @@
1
+ import { posix } from "../compiler-core/index.js";
2
+ const BLOCKS = new Set([
3
+ "Meta",
4
+ "Canvas",
5
+ "Story",
6
+ "Controls",
7
+ "ArgTypes",
8
+ "Source",
9
+ "Primary",
10
+ "Stories",
11
+ "Description",
12
+ "Subtitle",
13
+ "Title",
14
+ "Markdown",
15
+ "Unstyled",
16
+ "ColorPalette",
17
+ "IconGallery",
18
+ "Typeset"
19
+ ]);
20
+ const MAX_SECTION_TEXT = 500;
21
+ /**
22
+ * Reads a Storybook MDX page with line-based scanning. It does not compile or evaluate MDX: it records
23
+ * the imports, the doc blocks used and what they reference, and the prose outline.
24
+ */
25
+ export function parseMdx(path, text, projectFiles) {
26
+ const lines = text.split("\n");
27
+ const imports = readImports(path, text, projectFiles);
28
+ const doc = { path, storyRefs: [], blocks: [], sections: [] };
29
+ const blocks = new Set();
30
+ let fence;
31
+ let current;
32
+ lines.forEach((raw, index) => {
33
+ const line = index + 1;
34
+ const fenceMatch = /^\s*(```+|~~~+)/.exec(raw);
35
+ if (fenceMatch) {
36
+ const marker = fenceMatch[1].slice(0, 3);
37
+ fence = fence === undefined ? marker : fence === marker ? undefined : fence;
38
+ return;
39
+ }
40
+ if (fence !== undefined)
41
+ return;
42
+ for (const match of raw.matchAll(/<([A-Z][A-Za-z]*)\b([^>]*?)\/?>/g)) {
43
+ const block = match[1];
44
+ if (!BLOCKS.has(block))
45
+ continue;
46
+ blocks.add(block);
47
+ const attributes = match[2];
48
+ const of = /\bof=\{\s*([A-Za-z_$][\w$]*)(?:\.([A-Za-z_$][\w$]*))?\s*\}/.exec(attributes);
49
+ if (block === "Meta") {
50
+ const title = /\btitle=(?:"([^"]*)"|'([^']*)')/.exec(attributes);
51
+ if (title)
52
+ doc.metaTitle = (title[1] ?? title[2]);
53
+ if (of) {
54
+ const imported = imports.get(of[1]);
55
+ doc.metaOf = {
56
+ identifier: of[1],
57
+ ...(imported?.file ? { file: imported.file } : {}),
58
+ line
59
+ };
60
+ }
61
+ }
62
+ else if (of?.[2]) {
63
+ const imported = imports.get(of[1]);
64
+ doc.storyRefs.push({
65
+ block,
66
+ exportName: of[2],
67
+ ...(imported?.file ? { file: imported.file } : {}),
68
+ line
69
+ });
70
+ }
71
+ }
72
+ if (/^\s*(?:import|export)\s/.test(raw) || /^\s*<[A-Za-z]/.test(raw))
73
+ return;
74
+ const heading = /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(raw);
75
+ if (heading) {
76
+ current = { heading: heading[2], level: heading[1].length, line, text: "" };
77
+ doc.sections.push(current);
78
+ return;
79
+ }
80
+ if (current && raw.trim() && current.text.length < MAX_SECTION_TEXT) {
81
+ current.text = `${current.text}${current.text ? " " : ""}${raw.trim()}`.slice(0, MAX_SECTION_TEXT);
82
+ }
83
+ });
84
+ doc.blocks = [...blocks].sort();
85
+ const title = doc.metaTitle ?? doc.sections.find((s) => s.level === 1)?.heading;
86
+ if (title)
87
+ doc.title = title;
88
+ return doc;
89
+ }
90
+ function readImports(path, text, projectFiles) {
91
+ const out = new Map();
92
+ const resolve = (spec) => {
93
+ if (!spec.startsWith("."))
94
+ return undefined;
95
+ const base = posix.normalize(posix.join(posix.dirname(path), spec));
96
+ const stripped = base.replace(/\.[cm]?[jt]sx?$/, "");
97
+ return [
98
+ base,
99
+ ...[".stories.tsx", ".stories.ts", ".stories.jsx", ".stories.js", ".tsx", ".ts", ".jsx", ".js"].map((e) => stripped + e),
100
+ ...[".tsx", ".ts", ".jsx", ".js"].map((e) => `${base}${e}`)
101
+ ].find((c) => projectFiles.has(c));
102
+ };
103
+ for (const match of text.matchAll(/import\s+([^;]+?)\s+from\s+["']([^"']+)["']/g)) {
104
+ const clause = match[1];
105
+ const file = resolve(match[2]);
106
+ const namespace = /\*\s+as\s+([A-Za-z_$][\w$]*)/.exec(clause);
107
+ if (namespace)
108
+ out.set(namespace[1], { ...(file ? { file } : {}) });
109
+ const def = /^([A-Za-z_$][\w$]*)\s*(?:,|$)/.exec(clause);
110
+ if (def)
111
+ out.set(def[1], { ...(file ? { file } : {}) });
112
+ for (const named of /\{([^}]*)\}/.exec(clause)?.[1]?.split(",") ?? []) {
113
+ const alias = /(?:[\w$]+\s+as\s+)?([\w$]+)\s*$/.exec(named.trim());
114
+ if (alias?.[1])
115
+ out.set(alias[1], { ...(file ? { file } : {}) });
116
+ }
117
+ }
118
+ return out;
119
+ }
120
+ //# sourceMappingURL=mdx.js.map
@@ -0,0 +1,158 @@
1
+ import { hashText, IrBuilder, scanCssCustomProperties } from "../compiler-core/index.js";
2
+ import { hashJson } from "../shared/index.js";
3
+ import { classifyScope, valueKind } from "./css.js";
4
+ import { parseJsonWithLines } from "./json-lines.js";
5
+ import { extractJsonTokens } from "./json-tokens.js";
6
+ export const ADAPTER_ID = "design-tokens";
7
+ export const ADAPTER_VERSION = "0.1.0";
8
+ /**
9
+ * Extracts design tokens from CSS custom properties and from W3C-Design-Tokens-shaped or Style
10
+ * Dictionary-shaped JSON. It records names, raw values, references and theme modes exactly as written.
11
+ * It does not decide token categories or semantic names: those are interpretation, not extraction.
12
+ */
13
+ export const designTokensAdapter = {
14
+ id: ADAPTER_ID,
15
+ version: ADAPTER_VERSION,
16
+ type: "tokens",
17
+ async discover({ project }) {
18
+ const groups = new Map();
19
+ for (const file of project.tokenFiles) {
20
+ if (!isExtractable(file))
21
+ continue;
22
+ groups.set(file.package ?? "", [...(groups.get(file.package ?? "") ?? []), file]);
23
+ }
24
+ const used = new Set();
25
+ const sources = [];
26
+ for (const [packageId, files] of [...groups].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
27
+ const pkg = project.packages.find((p) => p.id === packageId);
28
+ const slug = (pkg?.name ?? pkg?.dir ?? "root")
29
+ .toLowerCase()
30
+ .replace(/[^a-z0-9._-]+/g, "-")
31
+ .replace(/^[^a-z0-9]+|[-.]+$/g, "") || "root";
32
+ let id = `design-tokens-${slug}`;
33
+ for (let n = 2; used.has(id); n++)
34
+ id = `design-tokens-${slug}-${n}`;
35
+ used.add(id);
36
+ sources.push({
37
+ id,
38
+ type: "tokens",
39
+ location: pkg?.dir ?? ".",
40
+ config: { files: files.map((f) => f.path) },
41
+ inputHash: hashJson(files.map((f) => [f.path, f.contentHash]))
42
+ });
43
+ }
44
+ return { sources, diagnostics: [] };
45
+ },
46
+ async extract({ fs, project, source, clock }, config) {
47
+ const builder = new IrBuilder({
48
+ source: {
49
+ id: source.id,
50
+ type: source.type,
51
+ location: source.location,
52
+ inputHash: source.inputHash,
53
+ adapter: { name: ADAPTER_ID, version: ADAPTER_VERSION }
54
+ },
55
+ extractor: { name: ADAPTER_ID, version: ADAPTER_VERSION },
56
+ capabilities: ["name", "description", "deprecated", "value.*", "modes.*", "extensions.*"],
57
+ clock
58
+ });
59
+ let partial = false;
60
+ for (const file of project.tokenFiles.filter((f) => config.files.includes(f.path))) {
61
+ if (file.kind === "css-custom-properties") {
62
+ partial = (await extractCss({ fs, builder, file })) || partial;
63
+ }
64
+ else {
65
+ partial = (await extractJson({ fs, builder, file })) || partial;
66
+ }
67
+ }
68
+ return { ir: builder.build(partial ? "partial" : "complete") };
69
+ },
70
+ async validate({ ir }) {
71
+ return ir.entities.length === 0
72
+ ? [
73
+ {
74
+ code: "tokens.none-extracted",
75
+ severity: "warning",
76
+ message: `No tokens were extracted for ${ir.source.id}`,
77
+ sourceId: ir.source.id
78
+ }
79
+ ]
80
+ : [];
81
+ }
82
+ };
83
+ function isExtractable(file) {
84
+ return (file.kind === "css-custom-properties" ||
85
+ (file.kind === "json-tokens" && (file.format === "dtcg-like" || file.format === "style-dictionary-like")));
86
+ }
87
+ async function extractCss(input) {
88
+ const { fs, builder, file } = input;
89
+ const text = await fs.readFile(file.path);
90
+ if (hashText(text) !== file.contentHash)
91
+ throw new Error(`${file.path} changed after discovery`);
92
+ for (const variable of scanCssCustomProperties(text)) {
93
+ const scope = classifyScope(variable.chain);
94
+ const name = variable.name.slice(2);
95
+ const ref = scope.kind === "other"
96
+ ? `css:${file.path}#${variable.name}@${variable.scope}`
97
+ : `css:${file.path}#${variable.name}`;
98
+ builder.entity(ref, "token", name);
99
+ const evidence = builder.evidence({
100
+ sourceRef: `${file.path}#${variable.name}`,
101
+ location: { type: "file", path: file.path, line: variable.line },
102
+ method: "structured-parse",
103
+ original: `${variable.name}: ${variable.value};`
104
+ });
105
+ builder.fact(ref, "name", name, evidence);
106
+ builder.fact(ref, "extensions.css.name", variable.name, evidence);
107
+ // Per-scope facts live under the scope they describe, so a root and a dark definition never
108
+ // contradict each other; only two definitions in the same scope can conflict.
109
+ const base = scope.kind === "theme"
110
+ ? `extensions.css.modes.${scope.mode}`
111
+ : scope.kind === "root"
112
+ ? "extensions.css.root"
113
+ : "extensions.css";
114
+ builder.fact(ref, `${base}.rawValue`, variable.value, evidence);
115
+ builder.fact(ref, `${base}.valueKind`, valueKind(variable.value), evidence);
116
+ if (scope.kind === "other") {
117
+ builder.fact(ref, "extensions.css.scopeKind", "other", evidence);
118
+ builder.fact(ref, "extensions.css.scope", variable.scope, evidence);
119
+ continue;
120
+ }
121
+ if (scope.kind === "theme")
122
+ builder.fact(ref, `${base}.via`, scope.via, evidence);
123
+ const prefix = scope.kind === "theme" ? `modes.${scope.mode}` : "value";
124
+ if (variable.aliasOf)
125
+ builder.fact(ref, `${prefix}.aliasOf`, variable.aliasOf.slice(2), evidence);
126
+ else
127
+ builder.fact(ref, `${prefix}.value`, variable.value, evidence);
128
+ }
129
+ return file.cssVariablesTruncated === true;
130
+ }
131
+ async function extractJson(input) {
132
+ const { fs, builder, file } = input;
133
+ const text = await fs.readFile(file.path);
134
+ if (hashText(text) !== file.contentHash)
135
+ throw new Error(`${file.path} changed after discovery`);
136
+ for (const token of extractJsonTokens(parseJsonWithLines(text))) {
137
+ const ref = `token:${file.path}#${token.name}`;
138
+ builder.entity(ref, "token", token.name);
139
+ const evidence = builder.evidence({
140
+ sourceRef: `${file.path}#${token.name}`,
141
+ location: { type: "file", path: file.path, line: token.line },
142
+ method: "structured-parse"
143
+ });
144
+ builder.fact(ref, "name", token.name, evidence);
145
+ if (token.aliasOf)
146
+ builder.fact(ref, "value.aliasOf", token.aliasOf, evidence);
147
+ else
148
+ builder.fact(ref, "value.value", token.value, evidence);
149
+ if (token.type)
150
+ builder.fact(ref, `extensions.${token.format}.type`, token.type, evidence);
151
+ if (token.description)
152
+ builder.fact(ref, "description", token.description, evidence, { origin: "authored" });
153
+ if (token.deprecated)
154
+ builder.fact(ref, "deprecated", token.deprecated, evidence, { origin: "authored" });
155
+ }
156
+ return false;
157
+ }
158
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1,59 @@
1
+ const ROOT = /^(?::root|html|:host|body)$/;
2
+ const THEME_ATTR = /^(?:html|:root|body)?\[data-(?:theme|mode|color-scheme|color-mode)(?:[~^$*|]?=)?["']?([\w-]+)["']?\]$/;
3
+ const THEME_CLASS = /^(?:html|:root|body)?\.(dark|light)$/;
4
+ const THEME_PREFIX_CLASS = /^(?:html|:root|body)?\.theme-([\w-]+)$/;
5
+ const MEDIA_SCHEME = /^@media\b.*prefers-color-scheme\s*:\s*(dark|light)\b/i;
6
+ /**
7
+ * Classifies the rule chain around a custom property. Only well-known theme conventions are treated as
8
+ * themes: a data-theme-style attribute, `.dark`/`.light`/`.theme-*` classes, and
9
+ * `@media (prefers-color-scheme)` around a root selector. Any other selector is `other`, which usually
10
+ * means a component-private variable, and is never presented as a theme or default value.
11
+ */
12
+ export function classifyScope(chain) {
13
+ const selector = [...chain].reverse().find((entry) => !entry.startsWith("@"));
14
+ if (selector === undefined)
15
+ return { kind: "other" };
16
+ const media = chain
17
+ .map((entry) => MEDIA_SCHEME.exec(entry)?.[1]?.toLowerCase())
18
+ .find((m) => m !== undefined);
19
+ const parts = selector.split(",").map((p) => p.trim());
20
+ const classified = parts.map((part) => {
21
+ if (ROOT.test(part))
22
+ return media ? { kind: "theme", mode: media, via: "media" } : { kind: "root" };
23
+ const match = THEME_ATTR.exec(part) ?? THEME_CLASS.exec(part) ?? THEME_PREFIX_CLASS.exec(part);
24
+ if (match?.[1])
25
+ return { kind: "theme", mode: match[1].toLowerCase(), via: "selector" };
26
+ return { kind: "other" };
27
+ });
28
+ const first = classified[0];
29
+ if (!first)
30
+ return { kind: "other" };
31
+ const same = classified.every((c) => c.kind === first.kind && (c.kind !== "theme" || (first.kind === "theme" && c.mode === first.mode)));
32
+ return same ? first : { kind: "other" };
33
+ }
34
+ const COLOR_FN = /^(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|color-mix)\(/i;
35
+ const LENGTH = /^-?(?:\d+\.?\d*|\.\d+)(?:px|rem|em|%|vh|vw|vmin|vmax|ch|ex|pt|cm|mm|in)$/i;
36
+ const TIME = /^-?(?:\d+\.?\d*|\.\d+)(?:ms|s)$/i;
37
+ const NUMBER = /^-?(?:\d+\.?\d*|\.\d+)$/;
38
+ /** Syntactic classification of a raw value. It says what the text looks like, nothing about design intent. */
39
+ export function valueKind(raw) {
40
+ const value = raw.trim();
41
+ if (/^var\(/i.test(value))
42
+ return "reference";
43
+ if (/^#[0-9a-f]{3,8}$/i.test(value) || COLOR_FN.test(value))
44
+ return "color";
45
+ if (LENGTH.test(value))
46
+ return "length";
47
+ if (TIME.test(value))
48
+ return "time";
49
+ if (NUMBER.test(value))
50
+ return "number";
51
+ if (/^(?:calc|min|max|clamp)\(/i.test(value))
52
+ return "expression";
53
+ if (/^url\(/i.test(value))
54
+ return "url";
55
+ if (/^["']/.test(value))
56
+ return "string";
57
+ return "other";
58
+ }
59
+ //# sourceMappingURL=css.js.map
@@ -0,0 +1,5 @@
1
+ export { ADAPTER_ID, ADAPTER_VERSION, designTokensAdapter } from "./adapter.js";
2
+ export { classifyScope, valueKind } from "./css.js";
3
+ export { parseJsonWithLines } from "./json-lines.js";
4
+ export { extractJsonTokens } from "./json-tokens.js";
5
+ //# sourceMappingURL=index.js.map