synthesisui 0.16.214 → 0.16.215

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.
@@ -13,7 +13,7 @@ import { describeGate, gateComponent, groupSkips, SCREENS_FOR_SYSTEM, } from "..
13
13
  import { emptyTally, internalSpecifiers, scanComponentsInto, tallyToInventory, } from "../doctor/components-scan.js";
14
14
  import { countShape, describeCoverage, summarizeCoverage, } from "../doctor/coverage.js";
15
15
  import { crosswalk, floorSize, isLibrary, observedRules, useLiveCatalogue, } from "../doctor/crosswalk.js";
16
- import { keyframeOffsets, moduleImports, partialCandidates, readModuleCss, readModuleUsage, sheetImports, transcribeModule, } from "../doctor/css-modules.js";
16
+ import { keyframeOffsets, moduleImports, partialCandidates, readGlobalClasses, readModuleCss, readModuleUsage, sheetImports, transcribeModule, } from "../doctor/css-modules.js";
17
17
  import { dataContract } from "../doctor/data-contract.js";
18
18
  import { reconcile, scanDefinitions, } from "../doctor/definitions-scan.js";
19
19
  import { fragmentsOfSource, fragmentsOfStylesheet, judgeFragments, } from "../doctor/fragments.js";
@@ -227,21 +227,27 @@ into) {
227
227
  for (const candidate of partialCandidates(spec))
228
228
  imported.add(join(dirname(sheet.file), candidate));
229
229
  let css = "";
230
+ const globals = [];
230
231
  for (const { file, body } of sheets) {
231
232
  css += `\n${body}`;
232
- if (!into || !body)
233
+ if (!body)
233
234
  continue;
234
235
  const partial = basename(file).startsWith("_");
236
+ const kind = /\.module\./.test(file) || (partial && imported.has(file))
237
+ ? "scoped"
238
+ : partial
239
+ ? "dead"
240
+ : "global";
241
+ if (kind === "global")
242
+ globals.push({ file: relative(root, file), body });
243
+ if (!into)
244
+ continue;
235
245
  into.push({
236
246
  file: relative(root, file),
237
- fragments: fragmentsOfStylesheet(relative(root, file), body, /\.module\./.test(file) || (partial && imported.has(file))
238
- ? "scoped"
239
- : partial
240
- ? "dead"
241
- : "global"),
247
+ fragments: fragmentsOfStylesheet(relative(root, file), body, kind),
242
248
  });
243
249
  }
244
- return css;
250
+ return { css, globals };
245
251
  }
246
252
  /**
247
253
  * EVERY distinct design value, commonest first - not just the repeated ones.
@@ -378,7 +384,7 @@ export async function takeCensus(root, opts) {
378
384
  useLiveCatalogue(fetched.ok ? asCatalogueTable(fetched.index) : null);
379
385
  /** Todo arquivo de estilo do escopo, para o ledger - ver o parâmetro `into`. */
380
386
  const styleFiles = [];
381
- const css = await harvestOwnCss(root, styleFiles);
387
+ const { css, globals: globalSheets } = await harvestOwnCss(root, styleFiles);
382
388
  const table = buildTable({ css, source: "yours" });
383
389
  const schemes = parseSchemeBlocks(css);
384
390
  const reports = [];
@@ -407,6 +413,34 @@ export async function takeCensus(root, opts) {
407
413
  if (!declaredValues.has(m[1]))
408
414
  declaredValues.set(m[1], m[2].trim());
409
415
  }
416
+ /**
417
+ * AS REGRAS DE CLASSE DAS FOLHAS GLOBAIS - ver `readGlobalClasses`. Uma regra que um
418
+ * componente admitido veste é a receita dele; `claimed` guarda quais foram vestidas,
419
+ * porque é isso que o juiz do ledger precisa para chamar a classe e a regra de LIDAS.
420
+ */
421
+ const globalClasses = readGlobalClasses(globalSheets, declaredValues);
422
+ const globalClassesClaimed = new Set();
423
+ /** Por arquivo, as classes cuja regra a `<style>` island já entregou - ver o juiz. */
424
+ const islandClassesRead = new Map();
425
+ /** A regra global que uma lista de classes veste, mesclada - e a reivindicação. */
426
+ const globalWear = (classes) => {
427
+ const worn = {
428
+ base: {},
429
+ dark: {},
430
+ states: {},
431
+ };
432
+ for (const cls of classes) {
433
+ const rule = globalClasses.get(cls);
434
+ if (!rule)
435
+ continue;
436
+ globalClassesClaimed.add(cls);
437
+ Object.assign(worn.base, rule.base);
438
+ Object.assign(worn.dark, rule.dark);
439
+ for (const [state, block] of Object.entries(rule.states))
440
+ worn.states[state] = { ...worn.states[state], ...block };
441
+ }
442
+ return worn;
443
+ };
410
444
  const looks = {};
411
445
  /**
412
446
  * The directory shape, gathered while the files are already being walked - so
@@ -622,7 +656,13 @@ export async function takeCensus(root, opts) {
622
656
  * the root tag are per component, scoped by name.
623
657
  */
624
658
  for (const extra of found.slice(1)) {
625
- const et = transcribe(rootClasses(src, extra.name), declaredValues);
659
+ const ecls = rootClasses(src, extra.name);
660
+ const et = transcribe(ecls, declaredValues);
661
+ // A raiz do segundo componente também veste folha global - mesma regra.
662
+ const eglobal = globalWear(ecls);
663
+ et.base = { ...eglobal.base, ...et.base };
664
+ et.dark = { ...eglobal.dark, ...et.dark };
665
+ et.states = { ...eglobal.states, ...et.states };
626
666
  const etag = rootTag(src, extra.name);
627
667
  // ITS OWN MARKUP, sliced at the definition boundary - the whole file's
628
668
  // sketch belonged to the first component and the second read an empty
@@ -641,7 +681,14 @@ export async function takeCensus(root, opts) {
641
681
  };
642
682
  }
643
683
  }
644
- const t = transcribe(rootClasses(src, found[0].name), declaredValues);
684
+ const rootCls = rootClasses(src, found[0].name);
685
+ const t = transcribe(rootCls, declaredValues);
686
+ /**
687
+ * A REGRA DE CLASSE GLOBAL QUE A RAIZ VESTE - `clsx("root", …)` contra
688
+ * `.root { isolation: isolate }` numa folha global. É a camada mais fraca de
689
+ * todas: no cascade dele, qualquer coisa dita no elemento ganha da folha.
690
+ */
691
+ const fromGlobal = globalWear(rootCls);
645
692
  /**
646
693
  * The transcription carries `dark` too, and spreading it put the key back after
647
694
  * the conditional below decided to leave it out. Two writers for one field is
@@ -739,6 +786,7 @@ export async function takeCensus(root, opts) {
739
786
  */
740
787
  const styledHere = readStyledComponents(src).find((c) => c.name === found[0].name);
741
788
  const base = {
789
+ ...fromGlobal.base,
742
790
  ...inlineStyle,
743
791
  ...styledHere?.base,
744
792
  ...fromModule?.base,
@@ -746,12 +794,14 @@ export async function takeCensus(root, opts) {
746
794
  ...t.base,
747
795
  };
748
796
  const states = {
797
+ ...fromGlobal.states,
749
798
  ...styledHere?.states,
750
799
  ...fromModule?.states,
751
800
  ...v.base.states,
752
801
  ...t.states,
753
802
  };
754
803
  const dark = {
804
+ ...fromGlobal.dark,
755
805
  ...styledHere?.dark,
756
806
  ...fromModule?.dark,
757
807
  ...t.dark,
@@ -820,9 +870,37 @@ export async function takeCensus(root, opts) {
820
870
  if (mine.length === 0)
821
871
  continue;
822
872
  islandStyle[index] = Object.assign(islandStyle[index] ?? {}, ...mine.map((rule) => rule.declarations));
873
+ /**
874
+ * A CLASSE FOI LIDA - pela island, e o juiz do ledger precisa saber,
875
+ * senão o `dot` cuja regra o censo carrega segue acusado de valor
876
+ * perdido (era 3 das 6 ocorrências finais da fila, 12/08).
877
+ */
878
+ const claimed = islandClassesRead.get(rel) ?? new Set();
879
+ claimed.add(cls);
880
+ islandClassesRead.set(rel, claimed);
823
881
  }
824
882
  }
825
883
  }
884
+ /**
885
+ * A REGRA DE CLASSE GLOBAL QUE UM NÓ INTERNO VESTE viaja pela MESMA costura do
886
+ * island: por índice do sketch, resolvida em part quando a anatomia nomeia o nó.
887
+ * A raiz (índice 0) já entrou no `base` acima; o island e as utilities do próprio
888
+ * nó ganham dela, porque no cascade dele a folha global é a camada mais fraca.
889
+ */
890
+ for (const [index, node] of sketch.entries()) {
891
+ if (index === 0)
892
+ continue;
893
+ for (const cls of (node.classes ?? "").split(/\s+/)) {
894
+ const rule = cls ? globalClasses.get(cls) : undefined;
895
+ if (!rule)
896
+ continue;
897
+ globalClassesClaimed.add(cls);
898
+ islandStyle[index] = {
899
+ ...rule.base,
900
+ ...(islandStyle[index] ?? {}),
901
+ };
902
+ }
903
+ }
826
904
  if (island.foreign.length > 0) {
827
905
  /**
828
906
  * WHOSE MARKUP IS BEING PATCHED, measured rather than guessed: the opaque
@@ -1503,7 +1581,11 @@ export async function takeCensus(root, opts) {
1503
1581
  * Montado aqui porque é o primeiro momento em que as três coisas existem juntas: os fragmentos
1504
1582
  * vistos, os tokens declarados, e quais arquivos de estilo pertencem a componente admitido.
1505
1583
  */
1506
- const ledger = buildLedger(opts?.cli ?? "unknown", judgeFragments([...fragments.flat(), ...styleFiles.flatMap((s) => s.fragments)], declaredValues, admittedStyles));
1584
+ const ledger = buildLedger(opts?.cli ?? "unknown", judgeFragments([...fragments.flat(), ...styleFiles.flatMap((s) => s.fragments)], declaredValues, admittedStyles, {
1585
+ wornGlobal: globalClassesClaimed,
1586
+ islandRead: islandClassesRead,
1587
+ refused: new Set(skips.map((s) => s.file)),
1588
+ }));
1507
1589
  const coverageLines = describeCoverage(coverage, composition);
1508
1590
  if (coverageLines.length > 0) {
1509
1591
  say("");
@@ -683,3 +683,50 @@ function tokenRefFor(name) {
683
683
  return `{typography.families.${bare.slice(5)}}`;
684
684
  return `var(${name})`;
685
685
  }
686
+ /**
687
+ * AS REGRAS DE CLASSE DE UMA FOLHA GLOBAL, prontas para o componente que as veste.
688
+ *
689
+ * Uma folha global vale para a página inteira - por isso ela nunca teve leitor de
690
+ * componente. Mas uma regra de CLASSE que um componente admitido veste literalmente é
691
+ * a receita DELE: o `.root { isolation: isolate }` de um repo real pertence ao
692
+ * RootWrapper que escreve `clsx("root", …)`, e era a última ocorrência de
693
+ * `value-not-read` (12/08). Medido antes de construir: a massa da família é regra de
694
+ * ELEMENTO (106 de 115 declarações no packages/ui), e regra de classe vestida existe
695
+ * UMA - este leitor lê exatamente o que existe, e o resto continua declarado.
696
+ *
697
+ * Reusa o MESMO leitor de regra do module (`readModuleCss`), então estados e a face
698
+ * dark vêm de graça - um segundo parser seria a segunda fonte de verdade que este
699
+ * arquivo recusa em todo lugar. Fica de fora, nomeado: classe que só aparece como
700
+ * descendente (`.a .b` - o contexto é do pai) e seletor composto, que `readModuleCss`
701
+ * já reporta em `unslotted`.
702
+ */
703
+ export function readGlobalClasses(globals, declared) {
704
+ const resolve = (block) => {
705
+ const out = {};
706
+ for (const [prop, value] of Object.entries(block)) {
707
+ out[prop] = value.replace(/var\(\s*(--[a-zA-Z0-9_-]+)\s*(?:,[^)]*)?\)/g, (whole, name) => declared.has(name) ? tokenRefFor(name) : whole);
708
+ }
709
+ return out;
710
+ };
711
+ const out = new Map();
712
+ for (const sheet of globals) {
713
+ const read = readModuleCss(sheet.body);
714
+ const dependent = new Set(Object.values(read.classes).flatMap((c) => c.children));
715
+ for (const [name, cls] of Object.entries(read.classes)) {
716
+ /** O filho de `.a .b` só existe naquele contexto - vesti-lo solto pintaria errado. */
717
+ if (dependent.has(name))
718
+ continue;
719
+ const states = {};
720
+ for (const [state, block] of Object.entries(cls.states))
721
+ states[state] = resolve(block);
722
+ // A última folha ganha, que é como o cascade dele resolve duas regras iguais.
723
+ out.set(name, {
724
+ ...cls,
725
+ base: resolve(cls.base),
726
+ dark: resolve(cls.dark),
727
+ states,
728
+ });
729
+ }
730
+ }
731
+ return out;
732
+ }
@@ -260,8 +260,60 @@ kind) {
260
260
  * fatos diferentes que, somados num número só, fizeram 36% parecer defeito de leitura quando era
261
261
  * o portão funcionando (dono, 04/08).
262
262
  */
263
- export function judgeFragments(seen, declared, admitted) {
263
+ /**
264
+ * O VOCABULÁRIO DE ESTRUTURA - o que a anatomia carrega como forma e direção do nó,
265
+ * nunca como declaração de estilo. É o conjunto que `arrangement()` e a derivação de
266
+ * forma já leem; crescer isto sem crescer aquilo voltaria a ser acusação.
267
+ */
268
+ const STRUCTURE = new Set([
269
+ "flex",
270
+ "inline-flex",
271
+ "grid",
272
+ "inline-grid",
273
+ "flex-row",
274
+ "flex-col",
275
+ "flex-row-reverse",
276
+ "flex-col-reverse",
277
+ "block",
278
+ "inline-block",
279
+ "inline",
280
+ "contents",
281
+ ]);
282
+ export function judgeFragments(seen, declared, admitted,
283
+ /**
284
+ * AS CLASSES DE FOLHA GLOBAL QUE UM COMPONENTE ADMITIDO VESTIU - ver
285
+ * `readGlobalClasses` e a reivindicação em `import.ts`. Vestida, a regra é a
286
+ * receita do componente e as duas metades do fato são LIDAS: a declaração na
287
+ * folha e a classe no elemento.
288
+ */
289
+ /**
290
+ * O QUE FOI LIDO POR OUTRO CAMINHO E QUEM SAIU PELO PORTÃO - as três chegaram
291
+ * juntas em 12/08, medindo os últimos textos da fila de `value-not-read`:
292
+ *
293
+ * wornGlobal classes de folha global que um admitido vestiu (`page-shell`) -
294
+ * a regra está no look; a classe e a regra são LIDAS
295
+ * islandRead por arquivo, as classes cuja regra mora na `<style>` island que o
296
+ * censo JÁ lê (`dot` do TypingIndicator) - mesmo fato, outra folha
297
+ * refused arquivos de componente que o portão recusou de propósito: a classe
298
+ * deles é `component-not-admitted` (by-design), nunca lacuna de valor
299
+ */
300
+ elsewhere) {
301
+ const wornGlobal = elsewhere?.wornGlobal ?? new Set();
302
+ const islandRead = elsewhere?.islandRead ?? new Map();
303
+ const refused = elsewhere?.refused ?? new Set();
264
304
  return seen.map((f) => {
305
+ /**
306
+ * A REGRA DE CLASSE GLOBAL QUE ALGUÉM VESTE FOI LIDA - antes do desvio de
307
+ * `binding`, porque lida é mais forte que ligada. `.root { isolation: isolate }`
308
+ * era a última ocorrência de `value-not-read` do repo real (12/08).
309
+ */
310
+ if (f.shape === "css" && f.reason === "shape-not-read") {
311
+ const cls = /^\.([A-Za-z][\w-]*)\s*\{/.exec(f.text)?.[1];
312
+ if (cls && wornGlobal.has(cls)) {
313
+ const { reason: _clear, ...rest } = f;
314
+ return { ...rest, read: true };
315
+ }
316
+ }
265
317
  /**
266
318
  * UMA DECLARAÇÃO SEM LEITOR CUJO VALOR É UM TOKEN QUE ELES JÁ DECLARAM.
267
319
  *
@@ -320,9 +372,36 @@ export function judgeFragments(seen, declared, admitted) {
320
372
  Object.values(t.states ?? {}).reduce((n, b) => n + Object.keys(b).length, 0) +
321
373
  (t.layers ?? []).reduce((n, l) => n + Object.keys(l.style).length, 0) >
322
374
  0;
323
- return read
324
- ? { ...f, read: true }
325
- : { ...f, read: false, reason: "value-not-read" };
375
+ if (read)
376
+ return { ...f, read: true };
377
+ /**
378
+ * ZERO DECLARAÇÕES NÃO É SEMPRE VALOR PERDIDO - duas formas de lista que a
379
+ * esteira lê por OUTRO caminho, medidas no repo real em 12/08 (eram os 3
380
+ * últimos textos da fila de `value-not-read`):
381
+ *
382
+ * flex flex-col arranjo viaja como FORMA do nó (Lei 12, `arrangement()`),
383
+ * e o transcribe devolve zero de propósito
384
+ * root a regra está numa folha GLOBAL que um admitido veste -
385
+ * o leitor de classe global a entrega no look
386
+ *
387
+ * Só quando TODO token tem um desses destinos: um token desconhecido no meio
388
+ * mantém o fragmento na fila, que é onde ele conta.
389
+ */
390
+ const tokens = literal.split(/\s+/).filter(Boolean);
391
+ const readElsewhere = tokens.length > 0 &&
392
+ tokens.every((tk) => STRUCTURE.has(tk) ||
393
+ wornGlobal.has(tk) ||
394
+ islandRead.get(f.file)?.has(tk));
395
+ if (readElsewhere)
396
+ return { ...f, read: true };
397
+ /** A classe de um componente recusado é o portão desenhado, não valor perdido. */
398
+ if (refused.has(f.file))
399
+ return {
400
+ ...f,
401
+ read: false,
402
+ reason: "component-not-admitted",
403
+ };
404
+ return { ...f, read: false, reason: "value-not-read" };
326
405
  }
327
406
  case "inline": {
328
407
  const read = Object.keys(readInlineStyle(`style={{${f.text}}}`)).length > 0;
@@ -448,6 +448,17 @@ const PLACE_KEYWORD = {
448
448
  property: "overflow+textOverflow+whiteSpace",
449
449
  value: "overflow:hidden|textOverflow:ellipsis|whiteSpace:nowrap",
450
450
  },
451
+ /**
452
+ * ACESSIBILIDADE E ROLAGEM SÃO DECLARAÇÕES TAMBÉM - dois dos três utilitários que
453
+ * seguravam a fila de `value-not-read` do repo real depois de tudo lido (12/08):
454
+ * `sr-only` no título do Modal e `scrollbar-gutter-stable` no chat. Valores
455
+ * PUBLICADOS do Tailwind, transcritos verbatim - nada inventado.
456
+ */
457
+ "sr-only": {
458
+ property: "position+width+height+padding+margin+overflow+clip+whiteSpace+borderWidth",
459
+ value: "position:absolute|width:1px|height:1px|padding:0|margin:-1px|overflow:hidden|clip:rect(0, 0, 0, 0)|whiteSpace:nowrap|borderWidth:0",
460
+ },
461
+ "scrollbar-gutter-stable": { property: "scrollbarGutter", value: "stable" },
451
462
  };
452
463
  /**
453
464
  * MOVEMENT IS A DECLARATION TOO, and 91 utilities of it were being dropped.
@@ -715,6 +726,14 @@ function readUtilityCore(utility, declared) {
715
726
  * frames themselves travel beside the reading - see `BUILTIN_KEYFRAMES` - because a
716
727
  * recipe cannot point at a keyframe the system does not define.
717
728
  */
729
+ /**
730
+ * `cursor-help`, `cursor-not-allowed` - o cursor É uma decisão de interação, e o
731
+ * nome do Tailwind é o valor CSS verbatim em toda a escala publicada. O gatilho do
732
+ * MetricCard real diz "isto tem explicação" com ele (12/08).
733
+ */
734
+ const cursor = /^cursor-([a-z-]+)$/.exec(core);
735
+ if (cursor)
736
+ return { property: "cursor", value: cursor[1] };
718
737
  const animate = /^animate-([a-z][\w-]*)$/.exec(core);
719
738
  if (animate && animate[1] !== "none") {
720
739
  return {
@@ -1900,7 +1919,7 @@ export function returnAt(source, name) {
1900
1919
  * body and no `return` to find - `const X = () => (<div/>)`. That is a
1901
1920
  * real shape and it gets the concise position.
1902
1921
  */
1903
- const arrow = /=>\s*\(/.exec(source.slice(decl.index, decl.index + 400));
1922
+ const arrow = /=>\s*(?:\(|<[A-Za-z])/.exec(source.slice(decl.index, decl.index + 400));
1904
1923
  if (arrow)
1905
1924
  return decl.index + arrow.index;
1906
1925
  /**
@@ -1924,7 +1943,7 @@ export function returnAt(source, name) {
1924
1943
  return -1;
1925
1944
  }
1926
1945
  }
1927
- return source.search(/return\s*\(/);
1946
+ return source.search(/return\s*(?:\(|<[A-Za-z])/);
1928
1947
  }
1929
1948
  /**
1930
1949
  * THE COMPONENT'S OWN `return (`, not the first one after its name.
@@ -2072,7 +2091,14 @@ function ownReturn(source, declAt) {
2072
2091
  }
2073
2092
  else if (depth === 1 &&
2074
2093
  ch === "r" &&
2075
- /^return\s*\(/.test(source.slice(j))) {
2094
+ /**
2095
+ * `return (` OU `return <div` - o parêntese é estilo, não gramática. O extrator
2096
+ * exigia o parêntese e um `return <div …>;` de linha única não tinha raiz: o
2097
+ * RootWrapper real vestia a última ocorrência de `value-not-read` num look que
2098
+ * nem existia (12/08; 41 arquivos assim no dashboard dele). `<[A-Za-z]` para uma
2099
+ * comparação `return a < b` não virar marcação.
2100
+ */
2101
+ /^return\s*(?:\(|<[A-Za-z])/.test(source.slice(j))) {
2076
2102
  return j;
2077
2103
  }
2078
2104
  }
@@ -100,7 +100,7 @@ export const CHECKER_SINCE = "0.16.202";
100
100
  *
101
101
  * Um censo medido antes disto carrega as duas leituras erradas.
102
102
  */
103
- export const READER_SINCE = "0.16.214";
103
+ export const READER_SINCE = "0.16.215";
104
104
  /**
105
105
  * O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
106
106
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.214",
3
+ "version": "0.16.215",
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": {