synthesisui 0.16.97 → 0.16.98

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.
@@ -123,7 +123,14 @@ function frontierClasses(node, sketch) {
123
123
  export function resolveAnatomy(read, declared, deps,
124
124
  /** Their name → the slug it reaches in this system. See `RefResolver`. */
125
125
  resolve,
126
- /** What the component returns, when the skill said and it is not a plain tag. */
126
+ /**
127
+ * What the component returns, when the skill said and it is not a plain tag.
128
+ *
129
+ * A STRING or an object: `{ name, at }` / `{ name, classes }` carries what the
130
+ * PARENT paints on the root at the call site. `MetricCard` returns
131
+ * `<Card className="flex-1 dark:bg-darkgray-300">` and that override had
132
+ * nowhere to travel (not-expressed.md, dono, 01/08).
133
+ */
127
134
  root,
128
135
  /** This component's sketch, so a node can name its classes by INDEX instead
129
136
  * of retyping them - see `AnatomyRead.at`. */
@@ -290,15 +297,22 @@ sketch) {
290
297
  * through the crosswalk exactly like a child frontier does.
291
298
  */
292
299
  let rootOut;
293
- const rootRaw = String(root ?? "").trim();
300
+ const rootObj = typeof root === "object" && root ? root : null;
301
+ const rootRaw = String((rootObj ? rootObj.name : root) ?? "").trim();
302
+ // The parent's own override on the root it returns, by index or as written.
303
+ const rootPainted = rootObj
304
+ ? (frontierClasses(rootObj, sketch).classes ??
305
+ "")
306
+ : "";
307
+ const paints = rootPainted ? { classes: rootPainted } : {};
294
308
  if (rootRaw && /^[A-Z]/.test(rootRaw)) {
295
309
  if (rootRaw.includes(".")) {
296
- rootOut = { from: rootRaw };
310
+ rootOut = { from: rootRaw, ...paints };
297
311
  }
298
312
  else {
299
313
  const slug = resolve?.(rootRaw) ?? safePartName(rootRaw);
300
314
  if (slug)
301
- rootOut = { ref: slug, name: rootRaw };
315
+ rootOut = { ref: slug, name: rootRaw, ...paints };
302
316
  }
303
317
  }
304
318
  if (coerced > 0) {
@@ -393,19 +393,64 @@ async function resolveAtNodes(root, entries) {
393
393
  // better than refusing to validate at all.
394
394
  return entries;
395
395
  }
396
- /** Their spelling → the sketch, matched the way the contract kebabs a name. */
397
- const sketchFor = (name) => {
398
- if (looks[name]?.sketch)
399
- return looks[name].sketch;
396
+ /** Their spelling → the look, matched the way the contract kebabs a name. */
397
+ const lookFor = (name) => {
398
+ if (looks[name])
399
+ return looks[name];
400
400
  const slug = name.replace(/[^a-z0-9]/gi, "").toLowerCase();
401
401
  for (const [theirs, look] of Object.entries(looks)) {
402
402
  if (theirs.replace(/[^a-z0-9]/gi, "").toLowerCase() === slug)
403
- return look.sketch;
403
+ return look;
404
404
  }
405
405
  return undefined;
406
406
  };
407
407
  return entries.map((entry) => {
408
- const sketch = sketchFor(String(entry.name ?? ""));
408
+ const look = lookFor(String(entry.name ?? ""));
409
+ const sketch = look?.sketch;
410
+ /**
411
+ * THE STYLES THE IMPORT WILL MERGE - so the floor measured is the floor the
412
+ * component arrives with.
413
+ *
414
+ * The reading sends an ANATOMY: a tree of `at` indices, deliberately without
415
+ * the classes, because retyping them is the loss the index exists to prevent.
416
+ * The styles are the census's measured `base` and `layers`, merged at import.
417
+ * So the validator was judging a recipe whose look had not arrived yet, and
418
+ * all 35 components came back BELOW THE FLOOR with the same four lines - a
419
+ * verdict about a payload nobody will ever send (not-expressed.md, dono,
420
+ * 01/08).
421
+ *
422
+ * Merged UNDER what the payload carries: a base the reader authored on
423
+ * purpose still wins over the measurement.
424
+ */
425
+ const recipe0 = entry.recipe;
426
+ const withLook = recipe0
427
+ ? {
428
+ ...recipe0,
429
+ ...(look?.base && Object.keys(look.base).length > 0
430
+ ? { base: { ...look.base, ...(recipe0.base ?? {}) } }
431
+ : {}),
432
+ ...(look?.layers?.length
433
+ ? {
434
+ layers: [
435
+ ...look.layers,
436
+ ...(recipe0.layers ?? []),
437
+ ],
438
+ }
439
+ : {}),
440
+ /**
441
+ * THE ROOT TAG TRAVELS, so the validator can name the KIND.
442
+ *
443
+ * It falls back to `surface` when the payload declares none, and every
444
+ * one of 35 components got the surface checklist - including a Button,
445
+ * two fields, a control and two pills (not-expressed.md, dono, 01/08).
446
+ * The kind is arithmetic from the name and the root tag; the tag is
447
+ * here and the arithmetic lives on the side that owns the contract, so
448
+ * this sends the evidence rather than a second implementation of it.
449
+ */
450
+ ...(look?.rootTag ? { rootTag: look.rootTag } : {}),
451
+ }
452
+ : recipe0;
453
+ entry = { ...entry, recipe: withLook };
409
454
  if (!sketch)
410
455
  return entry;
411
456
  const walk = (nodes) => {
@@ -1135,6 +1135,44 @@ export function transcribe(classes, declared) {
1135
1135
  * Absent or unfound, the old behaviour stands exactly - which keeps every
1136
1136
  * single-component file reading byte-identically.
1137
1137
  */
1138
+ /**
1139
+ * AN EXPORT THAT IS AN ALIAS - follow it to the function it wraps.
1140
+ *
1141
+ * `export const Chat = Object.assign(React.forwardRef(ChatContainerComponent), …)`
1142
+ * declares the NAME the census knows and none of the markup: the styles live in
1143
+ * `ChatContainerComponent`, declared above. Measured on a real library, EIGHT of
1144
+ * 36 exports are this shape - seven `forwardRef(Fn)` and one
1145
+ * `Object.assign(forwardRef(Fn))` - and Chat arrived with an empty look, which
1146
+ * forced the one hand-read of the whole run (not-expressed.md, dono, 01/08).
1147
+ *
1148
+ * They used to work by accident: with no body brace to find, the reader fell
1149
+ * back to the first `return (` in the file, which for a one-component file is
1150
+ * the right one. Removing that fallback - correctly, because it let a helper
1151
+ * borrow a sibling's return - would have broken all eight. So the alias is
1152
+ * followed on purpose instead: the first capitalised identifier inside the call
1153
+ * is the function this name IS.
1154
+ */
1155
+ function aliasTarget(source, name) {
1156
+ const decl = new RegExp(`(?:const|let|var)\\s+${name}\\s*(?::[^=]*)?=\\s*([^;\\n]{0,200})`).exec(source);
1157
+ if (!decl)
1158
+ return null;
1159
+ const rhs = decl[1];
1160
+ // Only a WRAPPER call aliases: `forwardRef`, `memo`, `Object.assign`,
1161
+ // `styled(...)`. A plain arrow is the component itself and has no target.
1162
+ if (!/^(?:React\.)?(?:forwardRef|memo|Object\.assign|styled)\b/.test(rhs))
1163
+ return null;
1164
+ for (const m of rhs.matchAll(/\b([A-Z][A-Za-z0-9_]*)\b/g)) {
1165
+ const candidate = m[1];
1166
+ if (/^(React|Object|Component)$/.test(candidate))
1167
+ continue;
1168
+ if (candidate === name)
1169
+ continue;
1170
+ // It has to be declared in this file, or it is a type argument or an import.
1171
+ if (new RegExp(`(?:function|const|let|var)\\s+${candidate}\\b`).test(source))
1172
+ return candidate;
1173
+ }
1174
+ return null;
1175
+ }
1138
1176
  function returnAt(source, name) {
1139
1177
  if (name) {
1140
1178
  const decl = new RegExp(`(?:function|const|let|var)\\s+${name}\\b|${name}\\s*[:=]\\s*(?:function|\\()`).exec(source);
@@ -1150,6 +1188,16 @@ function returnAt(source, name) {
1150
1188
  const arrow = /=>\s*\(/.exec(source.slice(decl.index, decl.index + 400));
1151
1189
  if (arrow)
1152
1190
  return decl.index + arrow.index;
1191
+ /**
1192
+ * AN ALIAS POINTS AT THE FUNCTION THAT HAS THE MARKUP - follow it once.
1193
+ * `const Chat = Object.assign(forwardRef(ChatContainerComponent), …)`.
1194
+ */
1195
+ const target = aliasTarget(source, name);
1196
+ if (target) {
1197
+ const viaAlias = ownReturn(source, source.search(new RegExp(`(?:function|const|let|var)\\s+${target}\\b`)));
1198
+ if (viaAlias !== -1)
1199
+ return viaAlias;
1200
+ }
1153
1201
  /**
1154
1202
  * FOUND THE NAME AND IT RETURNS NO JSX IN PARENTHESES - so say nothing.
1155
1203
  *
@@ -1200,13 +1248,36 @@ export function definitionSpan(source, name) {
1200
1248
  if (!decl)
1201
1249
  return null;
1202
1250
  const brace = bodyBrace(source, decl.index);
1203
- if (brace === -1)
1251
+ if (brace === -1) {
1252
+ // The export is an alias; the body belongs to what it wraps.
1253
+ const target = aliasTarget(source, name);
1254
+ if (target)
1255
+ return definitionSpan(source, target);
1204
1256
  return null;
1257
+ }
1205
1258
  const end = matchingBrace(source, brace);
1206
1259
  return { from: decl.index, to: end === -1 ? source.length : end + 1 };
1207
1260
  }
1208
1261
  /** The `{` that opens a function body declared at `declAt`, or -1. */
1209
1262
  function bodyBrace(source, declAt) {
1263
+ /**
1264
+ * A BODY INLINE INSIDE A WRAPPER: `forwardRef<A, B>((props, ref) => { … })`.
1265
+ *
1266
+ * Toggle is written this way and the plain walk below cannot find it - the
1267
+ * wrapper's own parenthesis never closes before the body starts, so it looked
1268
+ * for a `{` that is not there and the component read empty. There is no named
1269
+ * function to alias to either: the body IS the argument.
1270
+ */
1271
+ const head = source.slice(declAt, declAt + 400);
1272
+ const wrapper = /=\s*(?:React\.)?(?:forwardRef|memo|styled\([^)]*\))\s*(?:<[^>(]*>)?\s*\(/.exec(head);
1273
+ if (wrapper) {
1274
+ const arrow = /(?:=>|function\s*[A-Za-z0-9_]*\s*\([^)]*\))\s*\{/.exec(source.slice(declAt + wrapper.index));
1275
+ if (arrow) {
1276
+ const at = declAt + wrapper.index + arrow.index + arrow[0].length - 1;
1277
+ if (source[at] === "{")
1278
+ return at;
1279
+ }
1280
+ }
1210
1281
  let i = declAt;
1211
1282
  let paren = 0;
1212
1283
  let opened = false;
@@ -614,10 +614,21 @@ depth appears anyway.
614
614
  "Modal": {
615
615
  "root": "BaseDialog.Root",
616
616
  "anatomy": [ … ]
617
+ },
618
+ "MetricCard": {
619
+ "root": { "name": "Card", "at": 1 },
620
+ "anatomy": [ … ]
617
621
  }
618
622
  }
619
623
  \`\`\`
620
624
 
625
+ **The root takes what the PARENT paints on it.** When the returned root carries an override at
626
+ the call site - \`MetricCard\` returns \`<Card className="flex-1 dark:bg-darkgray-300">\` - send the
627
+ object form and point \`at\` the sketch node: \`{ "name": "Card", "at": 1 }\`. The string form still
628
+ works and means "no override". Sending the root as an anatomy node instead would make it a leaf
629
+ and orphan the content inside it, which is why it is a field on the root rather than a node
630
+ (not-expressed.md, dono, 01/08).
631
+
621
632
  **Say what the component RETURNS when it is not a plain tag.** Measured on a real library
622
633
  (dono, 01/08): 14 of 23 components return one of their own - \`ArticleCard\` returns a \`<Card>\`,
623
634
  \`Button\` and \`Text\` return a \`<Component>\` - and 17 of 23 have no style of their own at all,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.97",
3
+ "version": "0.16.98",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {