synthesisui 0.16.344 → 0.16.348

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.
@@ -24,7 +24,7 @@ import { accountExportsInto, emptyExportAccount, exportInvariantHolds, } from ".
24
24
  import { fragmentsOfSource, fragmentsOfStylesheet, judgeFragments, } from "../doctor/fragments.js";
25
25
  import { useFrameworkMajor } from "../doctor/framework-palette.js";
26
26
  import { describeConvention, describeRemainder, detectConventions, } from "../doctor/idiom.js";
27
- import { pageLaws } from "../doctor/page-laws.js";
27
+ import { componentsPagesReachFor, pageLaws } from "../doctor/page-laws.js";
28
28
  import { callSiteNode, placeLayers, } from "../doctor/place-layers.js";
29
29
  import { publicApi, requiredProps } from "../doctor/public-api.js";
30
30
  import { aliasesOf, describeReachability, edgesIn, reachabilityOf, } from "../doctor/reachability.js";
@@ -60,6 +60,25 @@ import { harvestWorkspaceCss } from "../workspace-css.js";
60
60
  import { placeInWorkspace } from "../workspace-place.js";
61
61
  import { add } from "./add.js";
62
62
  import { walk, walkAll } from "./doctor.js";
63
+ /**
64
+ * UMA PÁGINA DELE, como ele a montou - ver `Census.pages`.
65
+ *
66
+ * Não é "uma rota do Next": é qualquer arquivo que o portão de componente recusou por ser tela e
67
+ * não peça (`route` ou `screen`), em qualquer stack. O que a identifica é o veredito do portão,
68
+ * nunca o nome do arquivo - e é isso que a faz valer no projeto de alguém que a gente nunca viu.
69
+ */
70
+ /**
71
+ * QUANTAS PÁGINAS VIAJAM NO CENSO.
72
+ *
73
+ * O teto existe pela mesma razão que o de `skipped`: num monorepo o número estoura e o censo
74
+ * viraria um dump do repositório. Medido em 01/09, o `frontend-hub` tem 145 arquivos de página e
75
+ * o nosso app 65, então 400 cabe as duas populações inteiras e o corte só aparece em repositório
76
+ * bem maior que os que a gente mede.
77
+ *
78
+ * E QUANDO ELE CORTAR, `pagesTotal` diz quantas eram. Uma tela que contasse `pages.length` diria
79
+ * 400 e pareceria completa, que é a forma mais barata de um corte silencioso mentir.
80
+ */
81
+ export const PAGES_MAX = 400;
63
82
  /**
64
83
  * How many distinct values travel, PER KIND.
65
84
  *
@@ -472,7 +491,8 @@ export async function takeCensus(root, opts) {
472
491
  const sourceOf = new Map();
473
492
  /** Nome → o que a peça é, quando o portão soube dizer. Ver `CensusLook.kind`. */
474
493
  const kindOf = new Map();
475
- /** A raiz e a composição de cada página, para as leis - ver `pageLaws`. */
494
+ /** Como ele monta cada página - as leis saem daqui, e o censo carrega o dado. Ver `pageLaws`
495
+ * e `Census.pages`. */
476
496
  const pages = [];
477
497
  /** Todo fragmento de estilo visto, para o ledger - ver `buildLedger`. */
478
498
  const fragments = [];
@@ -753,12 +773,22 @@ export async function takeCensus(root, opts) {
753
773
  const sketch = sketchOf(src, d.name);
754
774
  const root = sketch[0];
755
775
  pages.push({
776
+ file: rel,
756
777
  root: root?.classes
757
778
  ? transcribe(root.classes.split(/\s+/).filter(Boolean), declaredValues).base
758
779
  : {},
780
+ /**
781
+ * `from` VEM DO SKETCH e não de uma segunda leitura do arquivo: o nó já carrega o
782
+ * especificador que o import dele declara, e reabrir o arquivo para descobrir o
783
+ * mesmo fato é a falha de esteira que o dono nomeou em 01/08.
784
+ */
759
785
  composes: sketch
760
786
  .filter((n) => /^[A-Z]/.test(n.tag))
761
- .map((n) => n.tag.split(".")[0]),
787
+ .map((n) => ({
788
+ name: n.tag.split(".")[0],
789
+ ...(n.from ? { from: n.from } : {}),
790
+ depth: n.depth,
791
+ })),
762
792
  });
763
793
  }
764
794
  skips.push({
@@ -2061,7 +2091,15 @@ export async function takeCensus(root, opts) {
2061
2091
  for (const line of ledgerLines.slice(0, 24))
2062
2092
  say(body(line));
2063
2093
  }
2064
- const pageRules = pageLaws(pages);
2094
+ /**
2095
+ * As leis contam NOMES, e o censo carrega a peça inteira. O `map` fica aqui e não em
2096
+ * `page-laws.ts` para o módulo das leis continuar sem saber o que é um censo - ele responde
2097
+ * "no que estas páginas concordam" e nada mais.
2098
+ */
2099
+ const pageRules = pageLaws(pages.map((page) => ({
2100
+ root: page.root ?? {},
2101
+ composes: page.composes.map((c) => c.name),
2102
+ })));
2065
2103
  if (pageRules.length > 0) {
2066
2104
  say("");
2067
2105
  say(section("What your pages agree on"));
@@ -2070,6 +2108,32 @@ export async function takeCensus(root, opts) {
2070
2108
  say(body(paint.faint(` ${law.evidence}`)));
2071
2109
  }
2072
2110
  }
2111
+ /**
2112
+ * AS PEÇAS QUE AS PÁGINAS DELE ALCANÇAM, ditas em voz alta - ver `componentsPagesReachFor`.
2113
+ *
2114
+ * Isto existe porque o campo `pages` do censo nasceria SEM LEITOR: a plataforma vai consumi-lo
2115
+ * num planejador que ainda não existe, e um dado guardado que ninguém mostra é a mesma coisa
2116
+ * que não medir. Aqui ele vira uma frase que já vale sozinha - o desenvolvedor descobre quais
2117
+ * das peças dele são a espinha das telas dele, que é uma pergunta que ninguém respondia.
2118
+ *
2119
+ * O NÚMERO VEM JUNTO SEMPRE, e ele é páginas e não usos - sem o denominador em palavras a lista
2120
+ * lê como ranking de importância em vez de contagem de espalhamento.
2121
+ */
2122
+ const spread = componentsPagesReachFor(pages, (from) => {
2123
+ if (!from)
2124
+ return true;
2125
+ if (internal.some((prefix) => from.startsWith(prefix)))
2126
+ return true;
2127
+ return frontierKind(from) === "own";
2128
+ });
2129
+ if (spread.length > 0) {
2130
+ say("");
2131
+ say(section("The components your pages are built from"));
2132
+ say(body(`${spread.length} of your components hold up ${pages.length} ${pages.length === 1 ? "page" : "pages"} - a new page starts from these.`));
2133
+ for (const piece of spread.slice(0, 8)) {
2134
+ say(body(paint.faint(` ${piece.name} - on ${piece.pages} of your ${pages.length} pages`)));
2135
+ }
2136
+ }
2073
2137
  if (architectures.length > 0) {
2074
2138
  say("");
2075
2139
  say(section("How this project is organised"));
@@ -2397,6 +2461,17 @@ export async function takeCensus(root, opts) {
2397
2461
  * Omitido quando não há nenhuma, que é o caso de todo primeiro import.
2398
2462
  */
2399
2463
  ...(declaredForms.length > 0 ? { declaredForms } : {}),
2464
+ /**
2465
+ * COMO ELE MONTA UMA PÁGINA - ver `Census.pages`. Omitido quando não há página nenhuma, que é
2466
+ * o caso de toda biblioteca: ela não compõe telas dentro de si, e um array vazio aqui leria
2467
+ * como "medimos e ele não monta nada".
2468
+ */
2469
+ ...(pages.length > 0
2470
+ ? {
2471
+ pages: pages.slice(0, PAGES_MAX),
2472
+ ...(pages.length > PAGES_MAX ? { pagesTotal: pages.length } : {}),
2473
+ }
2474
+ : {}),
2400
2475
  /**
2401
2476
  * A FOLHA DELE ENTRA POR ÚLTIMO, e a ordem é a resposta a quem manda.
2402
2477
  *
@@ -202,7 +202,17 @@ function declarations(body) {
202
202
  */
203
203
  let flat = body;
204
204
  for (let guard = 0; guard < 12; guard++) {
205
- const next = flat.replace(/\{[^{}]*\}/g, "");
205
+ /**
206
+ * O CABEÇALHO SAI JUNTO COM O BLOCO, e não depois - senão ele fica grudado na declaração
207
+ * seguinte e come a declaração inteira. Tirando só `{ … }`, o corpo de
208
+ *
209
+ * .panel { color: red; &:hover { color: blue; } padding: 24px; }
210
+ *
211
+ * virava `color: red; &:hover padding: 24px;`, e o pedaço do meio tem `:` no `:hover`:
212
+ * a propriedade lida era `&` e o `padding` do cartão dele MORRIA - a decisão estava no
213
+ * código, contada como interpretada, e não chegava à receita.
214
+ */
215
+ const next = flat.replace(/[^;{}]*\{[^{}]*\}/g, "");
206
216
  if (next === flat)
207
217
  break;
208
218
  flat = next;
@@ -458,7 +468,76 @@ function looksLikeTypeArg(source, at) {
458
468
  }
459
469
  export function scanTags(source) {
460
470
  const out = [];
471
+ /**
472
+ * QUANTAS TAGS ESTÃO ABERTAS - o que diz se estamos em CÓDIGO ou em CONTEÚDO.
473
+ *
474
+ * A distinção existe por causa das aspas, e ela é o que separa duas coisas escritas igual:
475
+ * `.replaceAll("<task_complete/>", …)` é código, e a tag ali é texto; `Don't` é conteúdo, e
476
+ * o apóstrofo ali é prosa. Parear aspas nos dois lugares custou marcação REAL na primeira
477
+ * tentativa - o `KeapCRMDialog` deles perdeu quatro `<Skeleton>` legítimos porque
478
+ * `height="70px"` desalinhou o par (medido, 01/09).
479
+ */
480
+ let open = 0;
461
481
  for (let i = 0; i < source.length; i++) {
482
+ /**
483
+ * COMENTÁRIO NÃO É MARCAÇÃO, e vale nos dois lados: `{/* … *\/}` mora no conteúdo e
484
+ * `//<AddIcon />` mora no código.
485
+ *
486
+ * O QUE O CLIENTE PERDE SEM ISTO: uma linha que ele comentou vira um nó da anatomia. No
487
+ * `frontend-hub`, `icon, //<AddIcon iconColor={…} />` faz o `SidebarButton` nascer com um
488
+ * ícone que a marcação não tem - e como o nó fantasma cai em `depth` 0, ele vira a RAIZ e
489
+ * desloca todo `at` abaixo dele, que é o índice pelo qual a anatomia acha o nó medido.
490
+ * Um `{/* … <StudioButtonNextStep …/> … *\/}` entrega um COMPONENTE inteiro que ele apagou.
491
+ *
492
+ * Medido em 01/09: 19 de 378 componentes e 2 de 95 páginas do app dele, 15 de 170
493
+ * componentes e 3 de 56 páginas do nosso. A biblioteca dele dá 0 de 36 - o defeito
494
+ * atravessou porque não aparece onde a esteira é mais olhada.
495
+ */
496
+ if (source[i] === "/" && source[i + 1] === "*") {
497
+ const close = source.indexOf("*/", i + 2);
498
+ i = close === -1 ? source.length : close + 1;
499
+ continue;
500
+ }
501
+ /** `https://…` não abre comentário, e o `://` é o que diz isso sem saber o contexto. */
502
+ if (source[i] === "/" && source[i + 1] === "/" && source[i - 1] !== ":") {
503
+ const nl = source.indexOf("\n", i + 2);
504
+ i = nl === -1 ? source.length : nl;
505
+ continue;
506
+ }
507
+ /**
508
+ * LITERAL DE TEXTO, só em código - com nenhuma tag aberta. Dentro de uma tag as aspas são
509
+ * atributo e o laço abaixo já as consome inteiras; dentro do conteúdo elas são prosa.
510
+ */
511
+ if (open === 0 &&
512
+ (source[i] === '"' || source[i] === "'" || source[i] === "`")) {
513
+ const quote = source[i];
514
+ /**
515
+ * ASPAS NÃO ATRAVESSAM LINHA - é regra da linguagem, e é ela que separa um literal de
516
+ * uma aspa solta dentro de outra coisa.
517
+ *
518
+ * Sem esse limite, o `["']` de uma EXPRESSÃO REGULAR abria uma string que só fechava
519
+ * páginas adiante: `cleanContent.replace(/^["']|["']$/g, "")` apagou os 19 nós do
520
+ * `ToolResultView` deles - o componente inteiro (medido, 01/09). Crase é a exceção,
521
+ * porque template literal atravessa linha de propósito.
522
+ */
523
+ const limit = quote === "`"
524
+ ? source.length
525
+ : source.indexOf("\n", i + 1) + 1 || source.length;
526
+ let j = i + 1;
527
+ while (j < limit) {
528
+ if (source[j] === "\\")
529
+ j += 2;
530
+ else if (source[j] === quote)
531
+ break;
532
+ else
533
+ j += 1;
534
+ }
535
+ /** Sem par até o limite não é literal - é aspa solta, e engoli-la custaria marcação. */
536
+ if (j < limit && source[j] === quote) {
537
+ i = j;
538
+ continue;
539
+ }
540
+ }
462
541
  if (source[i] !== "<")
463
542
  continue;
464
543
  const closing = source[i + 1] === "/";
@@ -498,6 +577,7 @@ export function scanTags(source) {
498
577
  const body = source.slice(nameAt + name.length, end);
499
578
  if (closing) {
500
579
  out.push({ at: i, kind: "close", tag: name, body: "" });
580
+ open = Math.max(0, open - 1);
501
581
  }
502
582
  else {
503
583
  out.push({ at: i, kind: "open", tag: name, body });
@@ -505,6 +585,9 @@ export function scanTags(source) {
505
585
  if (body.trimEnd().endsWith("/")) {
506
586
  out.push({ at: i + 1, kind: "close", tag: name, body: "" });
507
587
  }
588
+ else {
589
+ open += 1;
590
+ }
508
591
  }
509
592
  i = end;
510
593
  }
@@ -197,6 +197,48 @@ function backtick(source, from) {
197
197
  * contagem: um global com regra de CLASSE é forma que nenhum leitor cobre hoje - 399 declarações
198
198
  * no repo dele, e a única lacuna que estava calada em vez de declarada (04/08).
199
199
  */
200
+ /**
201
+ * O SELETOR DE UMA DECLARAÇÃO, resolvido contra os blocos que a contêm.
202
+ *
203
+ * O QUE O CLIENTE GANHA: uma declaração que ele escreveu dentro de `&:hover` chega aqui como
204
+ * `.card:hover` - o nome inteiro, o mesmo que o navegador aplica. Sem isto ela chegava como
205
+ * `&:hover`, e um `&` sem pai não tem a quem se ligar: nenhum leitor deste lado ou do outro
206
+ * consegue dizer de que elemento aquela decisão é.
207
+ *
208
+ * UMA AT-RULE É CONTEXTO, NUNCA PAI. `@media (…) { .card { padding } }` é uma decisão do `.card`
209
+ * sob uma condição, e não de um elemento chamado `@media`. A at-rule só responde pelo seletor
210
+ * quando não há nenhum dentro dela - que é como `@utility text-grad { … }` do Tailwind 4 e o
211
+ * `0%` de um `@keyframes` seguem chegando exatamente como chegavam.
212
+ */
213
+ function selectorOf(open) {
214
+ let selector = "";
215
+ let atRule = "";
216
+ for (const block of open) {
217
+ if (!block)
218
+ continue;
219
+ if (block.startsWith("@")) {
220
+ if (!selector)
221
+ atRule = block;
222
+ continue;
223
+ }
224
+ selector = selector ? resolveNesting(block, selector) : block;
225
+ }
226
+ return selector || atRule;
227
+ }
228
+ /**
229
+ * O `&` DO FILHO TROCADO PELO PAI, e o filho sem `&` pendurado como descendente - as duas
230
+ * regras do CSS aninhado. Uma lista de pais vira `:is(a, b)`, que é o que o próprio CSS faz:
231
+ * escrever `a, b:hover` diria outra coisa, e diria errado.
232
+ */
233
+ function resolveNesting(child, parent) {
234
+ const base = parent.includes(",") ? `:is(${parent})` : parent;
235
+ return child
236
+ .split(",")
237
+ .map((part) => part.trim())
238
+ .filter(Boolean)
239
+ .map((part) => part.includes("&") ? part.replaceAll("&", base) : `${base} ${part}`)
240
+ .join(", ");
241
+ }
200
242
  export function fragmentsOfStylesheet(file, css,
201
243
  /**
202
244
  * COMO ESTA FOLHA CHEGA NO BUILD DELES, e isso vem do GRAFO DE IMPORTS e nunca do nome do
@@ -212,31 +254,54 @@ export function fragmentsOfStylesheet(file, css,
212
254
  */
213
255
  kind) {
214
256
  const out = [];
215
- let selector = "";
257
+ /**
258
+ * OS BLOCOS ABERTOS, E NÃO O ÚLTIMO SELETOR VISTO.
259
+ *
260
+ * O QUE O CLIENTE VIVIA COM UMA VARIÁVEL SÓ: depois de um bloco aninhado, TODA declaração que
261
+ * vinha a seguir ficava com o seletor do bloco que já tinha fechado. No `.card { color: red;
262
+ * &:hover { color: blue } padding: 8px }`, o `padding` dele era registrado como sendo do
263
+ * `:hover` - uma decisão de repouso lida como decisão de estado. Medido no repositório vivo em
264
+ * 01/09, sobre 724 folhas e 19 374 declarações: **440 (2,3%) ficavam com o seletor de um bloco
265
+ * já fechado, e 942 (4,9%) saíam com um `&` que não tinha pai a que se referir**.
266
+ */
267
+ const open = [];
216
268
  for (const [i, raw] of css.split("\n").entries()) {
217
269
  const line = raw.replace(/\/\*.*?\*\//g, "");
218
270
  for (const piece of line.matchAll(PIECES)) {
219
271
  const text = (piece[1] ?? "").trim();
220
272
  if (piece[2] === "{") {
221
- selector = text;
273
+ open.push(text);
222
274
  continue;
223
275
  }
276
+ /**
277
+ * O BLOCO SÓ FECHA DEPOIS QUE A DECLARAÇÃO DELE FOI LIDA. `.a { color: red }` numa linha
278
+ * só termina a declaração no `}`, sem `;` - desempilhar antes de ler perdia a última
279
+ * declaração de todo bloco escrito assim. Medido no repositório vivo quando aconteceu:
280
+ * 19 declarações a menos no censo do `frontend-hub`, num conserto que só devia mexer em
281
+ * QUAL seletor cada uma tem.
282
+ */
283
+ const closes = piece[2] === "}";
224
284
  /**
225
285
  * Sem terminador não há declaração: `padding: 8px` no fim de uma linha pode ser a primeira
226
286
  * metade de um valor que continua na próxima.
227
287
  */
228
- if (!piece[2] || !text)
288
+ if (!piece[2] || !text) {
289
+ if (closes)
290
+ open.pop();
229
291
  continue;
292
+ }
230
293
  const m = DECLARATION.exec(text);
231
- if (!m)
232
- continue;
233
294
  /**
234
295
  * UM TOKEN NÃO É UM FRAGMENTO DE COMPONENTE. `--color-ocean-500: #…` é a paleta, e ela tem
235
296
  * o seu próprio caminho e o seu próprio relatório; contá-la aqui inflaria o total com o que
236
297
  * já está coberto em outro lugar.
237
298
  */
238
- if (m[1].startsWith("--"))
299
+ if (!m || m[1].startsWith("--")) {
300
+ if (closes)
301
+ open.pop();
239
302
  continue;
303
+ }
304
+ const selector = selectorOf(open);
240
305
  out.push({
241
306
  shape: "css",
242
307
  file,
@@ -249,6 +314,8 @@ kind) {
249
314
  ? { reason: "sheet-not-imported" }
250
315
  : {}),
251
316
  });
317
+ if (closes)
318
+ open.pop();
252
319
  }
253
320
  }
254
321
  return out;
@@ -302,6 +369,24 @@ const STRUCTURE = new Set([
302
369
  function targetClass(text) {
303
370
  const brace = text.indexOf("{");
304
371
  const selector = brace === -1 ? text : text.slice(0, brace);
372
+ /**
373
+ * `@utility x` É A CLASSE `.x` - ela é a forma do Tailwind 4 de declarar um utilitário, e não
374
+ * traz o ponto que este casamento procura.
375
+ *
376
+ * O QUE ELE VIA SEM ISTO: a declaração CHEGAVA ao componente e o relatório dizia que não. O
377
+ * `readGlobalClasses` lê `@utility` desde 24/08 e a entrega no look de quem veste a classe -
378
+ * medido em 01/09 pela cadeia real, o `Scene` recebe `perspective: 900px` da `@utility
379
+ * scene-3d`. Mas o julgamento procurava um ponto no seletor, não achava, e mandava as mesmas
380
+ * declarações para o ledger como forma sem leitor. No censo do `codelevel`: **68 declarações
381
+ * em 22 utilities anunciadas como não interpretadas**, com as duas que um componente veste
382
+ * (`scene-3d` no `Scene`, `stage-3d` no `Stage`) já dentro da receita dele.
383
+ *
384
+ * Um número que acusa lacuna onde não há custa o mesmo que um que a esconde: manda alguém
385
+ * consertar o que não está quebrado. Quase custou um leitor inteiro escrito duas vezes.
386
+ */
387
+ const utility = /^@utility\s+([a-z][\w-]*)/i.exec(selector.trim())?.[1];
388
+ if (utility)
389
+ return utility;
305
390
  for (const group of selector.split(",")) {
306
391
  const classes = group.match(/\.[A-Za-z][\w-]*/g);
307
392
  /** Sem o ponto: o nome que `globalClasses` indexa. */
@@ -104,3 +104,41 @@ export function pageLaws(pages) {
104
104
  function topOf(composed) {
105
105
  return [...composed.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? "";
106
106
  }
107
+ /**
108
+ * OS COMPONENTES QUE AS PÁGINAS DELE MAIS ALCANÇAM - fato contado, e nunca inferência de papel.
109
+ *
110
+ * O QUE ISTO DESTRAVA: a paleta de um planejador de página que abre com os componentes que ELE usa, na ordem
111
+ * em que ele usa, em vez de uma lista que a gente escreveu. Medido em 01/09, uma página do
112
+ * `frontend-hub` monta `ContentWrapper` em 20 delas, `PageHeader` em 14 e `HorizontalMenu` em 8 -
113
+ * e essas três são a gramática de layout daquele projeto, escrita em componentes.
114
+ *
115
+ * O QUE ELA NÃO DIZ, de propósito: que esses componentes SÃO layout. Isso é leitura de papel e ela
116
+ * exigiria provar que o componente envolve os outros; o que está medido aqui é quantas páginas a
117
+ * alcançam, e é só isso que o texto pode afirmar. Chamar frequência de papel seria a plataforma
118
+ * batizando o vocabulário dele, que a decisão 3 proíbe.
119
+ *
120
+ * CONTA PÁGINAS E NÃO USOS: uma página que repete o mesmo `Card` oito vezes não faz dele o
121
+ * esqueleto do projeto - faz dele o conteúdo daquela página. O sinal é ESPALHAMENTO.
122
+ *
123
+ * SÓ OS COMPONENTES DELE. Sem o filtro a lista abriria com `motion.div` e `LucideSearch`, que são de
124
+ * terceiros e não estão no sistema dele para serem oferecidas. Quem chama decide o que é dele,
125
+ * porque a régua que desempata `workspace:` contra semver mora no manifesto - ver `frontierKind`.
126
+ */
127
+ export function componentsPagesReachFor(pages, theirs) {
128
+ const spread = new Map();
129
+ for (const page of pages) {
130
+ const seen = new Set();
131
+ for (const piece of page.composes) {
132
+ if (!theirs(piece.from))
133
+ continue;
134
+ if (seen.has(piece.name))
135
+ continue;
136
+ seen.add(piece.name);
137
+ spread.set(piece.name, (spread.get(piece.name) ?? 0) + 1);
138
+ }
139
+ }
140
+ return [...spread.entries()]
141
+ .filter(([, n]) => n >= AGREE)
142
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
143
+ .map(([name, n]) => ({ name, pages: n }));
144
+ }
@@ -144,7 +144,15 @@
144
144
  * terminal dizendo qual arquivo e como tomar a versão nova por escolha. Antes desta versão a
145
145
  * reescrita era incondicional e o único aviso era prosa depois do fato.
146
146
  */
147
- export const MATERIALISER_SINCE = "0.16.319";
147
+ /**
148
+ * 0.16.319 -> 0.16.345 em 01/09 (`markup-only.spec`): o scanner de tags parou de ler comentário e
149
+ * literal de texto como marcação, e isso chega ao ARQUIVO que o `materialize` escreve.
150
+ *
151
+ * O sketch alimenta a anatomia e a anatomia alimenta o codegen, então um bloco JSX que ele
152
+ * comentou vinha sendo escrito na pasta dele como componente de verdade. Medido em 14 arquivos
153
+ * das duas populações o nó fantasma era a RAIZ - o elemento cujas classes viram a `base`.
154
+ */
155
+ export const MATERIALISER_SINCE = "0.16.345";
148
156
  /**
149
157
  * A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
150
158
  *
@@ -484,7 +492,47 @@ export const CHECKER_SINCE = "0.16.308";
484
492
  * parte do repositório. O corpus dourado NÃO se moveu por isso: nenhum dos 29 apps é medido com
485
493
  * escopo, e o hash parado aqui é ausência de cobertura, nunca ausência de efeito.
486
494
  */
487
- export const READER_SINCE = "0.16.341";
495
+ /**
496
+ * 0.16.341 -> 0.16.345 em 01/09 (`markup-only.spec`): o leitor deixou de tratar COMENTÁRIO e
497
+ * LITERAL DE TEXTO como marcação.
498
+ *
499
+ * O QUE O CLIENTE VIVIA: uma linha que ele comentou virava um elemento da anatomia dele. No
500
+ * `frontend-hub`, `icon, //<AddIcon iconColor={…} />` faz o `SidebarButton` nascer com um ícone
501
+ * que a marcação não tem; um `{/* … <StudioButtonNextStep …/> … *\/}` entrega um COMPONENTE
502
+ * inteiro que ele apagou; e `.replaceAll("<task_complete/>", …)` entrega uma tag que só existe
503
+ * dentro de uma string.
504
+ *
505
+ * A marca sobe porque o SKETCH é medido na máquina dele e viaja gravado no censo: o `sync`
506
+ * remede com `takeCensus`, então quem rodar recebe a anatomia sem os nós fantasma. Sem a marca,
507
+ * o `align` não chamaria ninguém.
508
+ *
509
+ * QUANTOS: 735 arquivos das duas populações, 47 mudaram, 111 nós saíram - os 111 verificados
510
+ * contra o AST do compilador como comentário ou literal, e zero marcação real perdida. Em 14
511
+ * arquivos o nó fantasma era a RAIZ, que é o que vira a `base` da receita.
512
+ *
513
+ * QUEM NÃO É AFETADO: quem não deixa código comentado na marcação. A biblioteca dele
514
+ * (`packages/ui`) dá 0 de 36 - e foi por isso que o defeito atravessou, porque ele não aparece
515
+ * onde a esteira é mais olhada. O corpus dourado não se movia por isso: nenhum dos 29 apps
516
+ * tinha a forma, e a fixture `markup-with-commented-out-code` entra no mesmo PR para o portão
517
+ * deixar de ser cego.
518
+ */
519
+ /**
520
+ * 0.16.345 -> 0.16.346 em 01/09 (`INV-COLETA-12`): o censo passou a carregar COMO ELE MONTA UMA
521
+ * PÁGINA - a sequência de peças de cada uma, com a origem de cada peça.
522
+ *
523
+ * O QUE ELE GANHA COM O `sync`: a plataforma passa a saber quais das peças DELE sustentam as telas
524
+ * dele. Sem isso, qualquer coisa que precise partir de uma página só tem o esqueleto que NÓS
525
+ * escrevemos - três layouts padrão contra os componentes que ele de fato usa.
526
+ *
527
+ * O campo é NOVO, então todo censo já guardado está sem ele: a marca sobe porque a ausência aqui
528
+ * não se distingue de "este projeto não tem páginas", e só o remedir separa os dois. O `sync`
529
+ * refaz com `takeCensus`.
530
+ *
531
+ * QUEM NÃO É AFETADO: quem apontou a leitura para uma biblioteca. Ela não compõe telas dentro de
532
+ * si, o campo fica ausente, e ausente ali continua sendo a resposta certa - o `codelevel-ui` dá
533
+ * 0 de 0 contra 145 arquivos de página do `frontend-hub`.
534
+ */
535
+ export const READER_SINCE = "0.16.348";
488
536
  /**
489
537
  * O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
490
538
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.344",
3
+ "version": "0.16.348",
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": {