synthesisui 0.16.376 → 0.16.378

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.
@@ -890,7 +890,19 @@ export async function doctor(opts) {
890
890
  * "not drift" era a nossa palavra para o motivo, e ela não diz o motivo. As duas razões reais
891
891
  * cabem na linha: ou nenhum token poderia segurar aquele valor, ou ele já vem de um.
892
892
  */
893
- console.log(body(`${plural(asideTotal, "value")} left out of the count - a token could never hold them, or they already come from one` +
893
+ /**
894
+ * E EM 05/09 ELAS VIRARAM TRÊS, senão esta linha volta a esconder metade.
895
+ *
896
+ * Os dois motivos novos não cabem em nenhum dos dois antigos: um token PODERIA segurar aquele
897
+ * valor, e é justamente por isso que escrevê-lo ali destrói. Uma cor dentro do e-mail que o app
898
+ * monta como texto, ou dentro de um hex escrito numa frase da página, não é pintada pela folha
899
+ * do app - `var()` não resolve num cliente de e-mail, e uma frase não é pintura nenhuma.
900
+ *
901
+ * Esta é a linha que o cliente lê SEM `--verbose`, e ela é o resumo inteiro para quem não pede
902
+ * detalhe. Sem a terceira metade, 61 valores no repositório dele saem da conta sob um motivo
903
+ * que não é o deles.
904
+ */
905
+ console.log(body(`${plural(asideTotal, "value")} left out of the count - a token could never hold them, they already come from one, or the page never paints them` +
894
906
  (skippedProjects.length > 0
895
907
  ? `, and ${plural(skippedProjects.length, "nested project")} that speak their own systems`
896
908
  : "") +
@@ -155,7 +155,26 @@ export function planFix(d, read) {
155
155
  * É GERAL: toda folha declara token com valor literal - 149 no repositório dele, 180 no
156
156
  * frontend-hub.
157
157
  */
158
- const declaring = /^\s*(--[a-z0-9-]+)\s*:/i.exec(current ?? "");
158
+ /**
159
+ * SEM ÂNCORA NO INÍCIO DA LINHA - e a âncora era um buraco de verdade.
160
+ *
161
+ * A primeira versão deste guard casava `^\s*--nome:`, o que só enxerga CSS formatado com uma
162
+ * declaração por linha. Numa folha minificada a linha inteira é
163
+ * `.pulse-page{--violet:#6755ff;--green:#79e39e;background:#080a10}` - a declaração não começa a
164
+ * linha, o guard não a via, e o ciclo passaria batido exatamente onde ninguém vai reler o diff.
165
+ *
166
+ * Medido no repositório do dono: `apps/landing/app/styles/pulse.css` é assim, e ele declara
167
+ * `--violet` e `--green` no meio de uma linha. Naquele repositório o ciclo não chega a nascer
168
+ * porque nenhum daqueles nomes coincide com um token do sistema - mas isso é sorte da população,
169
+ * não proteção.
170
+ *
171
+ * Percorre TODAS as declarações da linha e recusa a que se apontaria para si mesma. As outras
172
+ * seguem trocáveis: numa linha minificada, um `background:#fbfaf6` ao lado de um `--violet:...`
173
+ * continua sendo um uso legítimo.
174
+ */
175
+ const declaring = [
176
+ ...(current ?? "").matchAll(/(--[a-z0-9-]+)\s*:\s*([^;}]*)/gi),
177
+ ].find(([, , value]) => value?.includes(f.literal));
159
178
  if (declaring && declaring[1] === name) {
160
179
  skipped.push({
161
180
  file: f.file,
@@ -269,6 +269,97 @@ function fallbackSpans(line, table) {
269
269
  }
270
270
  return out;
271
271
  }
272
+ /**
273
+ * ONDE A CASCATA DO APP NÃO ALCANÇA - e trocar por `var()` ali APAGA a cor.
274
+ *
275
+ * O QUE ACONTECIA: `apps/web/lib/email.ts` monta o e-mail de login como TEXTO, e o `--fix` trocou
276
+ * `background:#6366f1` por `background:var(--color-brand-indigo)` no botão "Sign in". Aquele HTML é
277
+ * entregue a um cliente de e-mail, onde o `:root` do app não existe: a custom property não resolve,
278
+ * a declaração fica inválida, e o `background` volta para `transparent`. O botão de entrar some
279
+ * sobre um card quase preto - e é a PRIMEIRA tela que um usuário novo vê.
280
+ *
281
+ * É A MESMA FAMÍLIA DE `RENDERS_IMAGE`, que já recusa o arquivo que desenha para uma imagem pelo
282
+ * mesmo motivo declarado: "where CSS variables do not exist". Faltava a metade que sai como HTML.
283
+ *
284
+ * O SINAL É DA LINGUAGEM, NÃO DO REPOSITÓRIO: em `.ts`/`.tsx`/`.js`/`.jsx`, `style="..."` com uma
285
+ * declaração dentro NÃO é JSX - o React tipa `style` como objeto (`style={{}}`). Um atributo HTML
286
+ * escrito com aspas só existe ali dentro de uma string, isto é, marcação montada como texto: um
287
+ * e-mail, um PDF, um `srcdoc`, um sanitizador. Nenhum deles herda a folha do app.
288
+ *
289
+ * `.vue` e `.svelte` ficam FORA de propósito: neles `style="..."` é atributo de template real, o
290
+ * elemento é do app, e a cascata alcança. Recusar ali cegaria o produto para dois frameworks
291
+ * inteiros.
292
+ *
293
+ * MEDIDO em 05/09, em 4 escopos: `codelevel-monorepo` 7 linhas (`apps/web/lib/email.ts`, 2 delas já
294
+ * trocadas pelo `--fix` no repositório dele) · `frontend-hub/packages/ui` 3 linhas
295
+ * (`SignalUI/.../Sanitize.ts`, que remonta HTML de editor de texto) · `synthesisui-hub/apps/web`
296
+ * 18 linhas (`src/lib/email.ts` - o nosso próprio produto tem a forma) · `frontend-hub/apps/web-
297
+ * dashboard` 0. Três de quatro: a forma é geral, não é peculiaridade de um repositório.
298
+ */
299
+ const HTML_STYLE_ATTR = /style\s*=\s*"([^"]*)"/g;
300
+ /** Só onde `style="..."` não pode ser um elemento do app - ver `HTML_STYLE_ATTR`. */
301
+ const ASSEMBLES_MARKUP = /\.(?:tsx?|jsx?|mjs|cjs)$/i;
302
+ function markupTextSpans(file, line) {
303
+ if (!ASSEMBLES_MARKUP.test(file))
304
+ return [];
305
+ const out = [];
306
+ for (const m of line.matchAll(HTML_STYLE_ATTR)) {
307
+ if (!m[1].includes(":"))
308
+ continue;
309
+ const start = (m.index ?? 0) + m[0].indexOf('"') + 1;
310
+ out.push([start, start + m[1].length]);
311
+ }
312
+ return out;
313
+ }
314
+ /**
315
+ * O QUE A PÁGINA DIZ NÃO É O QUE ELA PINTA - e o `--fix` reescreveu uma frase.
316
+ *
317
+ * O QUE ACONTECIA: a landing dele demonstra a própria ferramenta num terminal falso, e a frase era
318
+ * `#6b6b78 is already --color-ink-500`. O scanner viu um hex, o sistema nomeia aquele valor, e a
319
+ * troca deixou `var(--color-ink-500) is already --color-ink-500` - uma tautologia, na frase que
320
+ * anuncia o que o produto faz. Não foi estilo mudado: foi CONTEÚDO.
321
+ *
322
+ * A LEI JÁ EXISTIA e esta é a borda dela - `fix` muda VALOR, nunca FORMA. Um literal em posição de
323
+ * conteúdo não é nem valor nem forma: é texto que uma pessoa lê.
324
+ *
325
+ * A REGIÃO É DERIVADA DA MARCAÇÃO, não de nome de arquivo: entre um `>` e o `<` seguinte está o que
326
+ * o elemento mostra. E dentro dela o literal ainda precisa estar CRU - `{x > 1 ? "#fff" : "#000"}`
327
+ * põe valores numa região que se parece com conteúdo, e as aspas são o que os separa de uma frase.
328
+ *
329
+ * UMA EXPRESSÃO NÃO É UMA FRASE, e esta borda custou um achado real. `<style>{`...`}</style>` é a
330
+ * folha que o app injeta de verdade: a cascata ALCANÇA, `var()` resolve, e o `120ms` escrito ali é
331
+ * deriva como qualquer outra. Medido em `apps/web/src/components/dashboard/live-specimen.tsx`, o
332
+ * guard sem esta borda derrubava 24 achados para 23. O sinal é da linguagem: uma chave logo depois
333
+ * do `>` abre uma expressão JSX, e o que vem dentro dela é código.
334
+ *
335
+ * `<style>html,body{...}</style>` DENTRO DE UMA STRING continua sendo conteúdo - lá a chave não
336
+ * encosta no `>`, e o documento é outro (um `srcdoc` de iframe, medido no `frontend-hub`), onde a
337
+ * folha do app também não chega.
338
+ *
339
+ * MEDIDO em 05/09, em 4 escopos: `codelevel-monorepo` 1 (a frase da landing) · `frontend-hub/apps/
340
+ * web-dashboard` 1 (o CSS do iframe) · `synthesisui-hub/apps/web` 14, entre elas a nossa PRÓPRIA
341
+ * demonstração na home, `<Line tone="ok">#2563eb -> --ds-color-semantic-info</Line>` · `frontend-
342
+ * hub/packages/ui` 0. O preço de errar é o produto reescrevendo a página de vendas de quem o
343
+ * instalou - inclusive a nossa.
344
+ */
345
+ function contentSpans(line) {
346
+ const out = [];
347
+ let open = line.indexOf(">");
348
+ while (open !== -1) {
349
+ const close = line.indexOf("<", open + 1);
350
+ if (close === -1)
351
+ break;
352
+ if (close > open + 1 && line[open + 1] !== "{")
353
+ out.push([open + 1, close]);
354
+ open = line.indexOf(">", close + 1);
355
+ }
356
+ return out;
357
+ }
358
+ /** Um valor entre aspas é um valor, mesmo numa região de conteúdo. */
359
+ function quotedAt(line, col, len) {
360
+ const q = /["'`]/;
361
+ return q.test(line[col - 1] ?? "") || q.test(line[col + len] ?? "");
362
+ }
272
363
  /**
273
364
  * The line that DEFINES a token is not a line that drifted from it.
274
365
  *
@@ -403,6 +494,12 @@ function scanCore(file, source, table) {
403
494
  // shadow) is one decision, so it is reported once.
404
495
  const spans = fallbackSpans(line, table);
405
496
  const inFallback = (at) => spans.some(([a, b]) => at >= a && at <= b);
497
+ /** Ver `markupTextSpans`: HTML montado como texto sai do alcance da folha do app. */
498
+ const markup = markupTextSpans(file, line);
499
+ const inMarkup = (at) => markup.some(([a, b]) => at >= a && at <= b);
500
+ /** Ver `contentSpans`: entre `>` e `<` está o que o elemento MOSTRA. */
501
+ const content = contentSpans(line);
502
+ const inContent = (at) => content.some(([a, b]) => at >= a && at <= b);
406
503
  const seen = new Set();
407
504
  // NOT named `at`: that is the line number in this scope, and shadowing it
408
505
  // put column positions into the report as line numbers.
@@ -413,6 +510,14 @@ function scanCore(file, source, table) {
413
510
  setAside("a token's own fallback");
414
511
  return;
415
512
  }
513
+ if (col >= 0 && inMarkup(col)) {
514
+ setAside("markup assembled as text, where the app's variables do not reach");
515
+ return;
516
+ }
517
+ if (col >= 0 && inContent(col) && !quotedAt(line, col, literal.length)) {
518
+ setAside("text the page shows, not a value it paints");
519
+ return;
520
+ }
416
521
  const key = `${kind}:${literal}`;
417
522
  if (seen.has(key))
418
523
  return;
@@ -475,7 +580,14 @@ function scanCore(file, source, table) {
475
580
  // A stack already reading from the system is the point, not a problem.
476
581
  if (stack.startsWith("var("))
477
582
  continue;
478
- push("font", stack);
583
+ /**
584
+ * A COLUNA VIAJA AQUI TAMBÉM - sem ela os guards por posição são cegos para uma família.
585
+ *
586
+ * `font` era o único `push` sem coluna, então um `font-family` dentro de um `style="..."` de
587
+ * e-mail passava pelos dois guards novos e pelo de fallback. Um valor não deixa de estar fora
588
+ * do alcance da folha por ser uma pilha de fontes.
589
+ */
590
+ push("font", stack, (m.index ?? 0) + m[0].indexOf(stack));
479
591
  }
480
592
  for (const m of line.matchAll(MOTION_PROP)) {
481
593
  const value = m[1];
@@ -242,7 +242,15 @@ export const MATERIALISER_SINCE = "0.16.370";
242
242
  * `line 12 oklch(...) -> --color-mint-500`. Um hook pinado antes desta versão aconselha OUTRA
243
243
  * coisa - nada -, que é a régua desta marca.
244
244
  */
245
- export const CHECKER_SINCE = "0.16.367";
245
+ /**
246
+ * 0.16.367 -> 0.16.378 em 05/09: o hook parava de cobrar onde o conselho DESTRÓI. Ele lê `scanSource`
247
+ * direto, então um hook pinado antes olha `style="background:#6366f1"` dentro do e-mail de login que
248
+ * o app monta como texto e manda escrever `var(--color-brand-indigo)` - no cliente de e-mail a
249
+ * custom property não resolve, a declaração fica inválida e o botão "Sign in" perde o fundo. E olha
250
+ * `#6b6b78 is already --color-ink-500` numa frase da landing e manda tokenizar TEXTO. Um hook
251
+ * pinado antes desta versão aconselha OUTRA coisa, que é a régua desta marca.
252
+ */
253
+ export const CHECKER_SINCE = "0.16.378";
246
254
  /**
247
255
  * A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
248
256
  *
@@ -665,7 +673,14 @@ export const CHECKER_SINCE = "0.16.367";
665
673
  * `codelevel-ui` e 23 no `frontend-hub`, e ZERO no `observed` das três populações. Um censo tirado
666
674
  * antes disto não tem aqueles valores na régua, e só um `sync` os traz.
667
675
  */
668
- export const READER_SINCE = "0.16.367";
676
+ /**
677
+ * 0.16.367 -> 0.16.378 em 05/09: o censo produzido MUDA. O `import` roda `scanSource` sobre cada
678
+ * arquivo e passa os relatórios por `diagnose`, então duas formas que nunca foram valor de design
679
+ * saem do `observed`: marcação montada como texto (o e-mail, o sanitizador de editor) e literal em
680
+ * posição de conteúdo. Um censo medido antes desta versão conta como deriva o que a folha do app
681
+ * nem alcança - e o número do cliente é a promessa.
682
+ */
683
+ export const READER_SINCE = "0.16.378";
669
684
  /**
670
685
  * O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
671
686
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.376",
3
+ "version": "0.16.378",
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": {