synthesisui 0.16.449 → 0.16.451

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.
@@ -1,10 +1,11 @@
1
1
  import { readFile, stat, writeFile } from "node:fs/promises";
2
2
  import { join, relative, resolve } from "node:path";
3
3
  import { changedSince } from "../changed-files.js";
4
- import { emptyTally, internalSpecifiers, scanComponentsInto, tallyToInventory, } from "../doctor/components-scan.js";
4
+ import { emptyTally, exportedNames, internalSpecifiers, scanComponentsInto, tallyToInventory, } from "../doctor/components-scan.js";
5
5
  import { checkContracts } from "../doctor/contract-check.js";
6
6
  import { appendEvent, ledgerPath } from "../doctor/ledger.js";
7
7
  import { diagnose, nameToWrite, scanSource } from "../doctor/scan.js";
8
+ import { declarados, falaDaPrateleira, naPrateleira } from "../doctor/shelf.js";
8
9
  import { governs, ungovernedIn } from "../governed.js";
9
10
  import { readMode } from "../mode.js";
10
11
  import { namesRemoved, previousText, rulesTouchedBy, } from "../rule-touched.js";
@@ -361,7 +362,14 @@ async function report(root, filePath, mode) {
361
362
  * parte do relatório que RECUSA, e uma recusa escondida embaixo de uma lista de valores é uma
362
363
  * recusa que ninguém lê.
363
364
  */
365
+ /**
366
+ * A PRATELEIRA VEM ANTES DOS VALORES, e a ordem é o ponto: trocar um hex por
367
+ * um token num componente que não precisava existir é polir o que devia ser
368
+ * apagado. Se a peça já está no sistema, essa é a primeira coisa a saber.
369
+ */
370
+ const prateleira = falaDaPrateleira(rel, naPrateleira(exportedNames(src), declarados(documents)), table.name ?? table.slug ?? "this system");
364
371
  const lines = [
372
+ ...(prateleira.length > 0 ? [...prateleira, ""] : []),
365
373
  ...(composed.length > 0 ? [...composed, ""] : []),
366
374
  ...(broke.length > 0 ? [...broke, ""] : []),
367
375
  `${rel} - checked against ${table.name ?? table.slug}.`,
@@ -2417,7 +2417,23 @@ export async function takeCensus(root, opts) {
2417
2417
  * e e' exatamente o caso que fazia o `canvas` ser escolhido por casamento de palavra.
2418
2418
  * Ver `root-ground.ts` para a medicao do `codelevel` que motivou isto.
2419
2419
  */
2420
- const rootGround = rootGroundOf(globalSheets.map((g) => ({ file: g.file, css: g.body })));
2420
+ const rootGround = rootGroundOf(
2421
+ /**
2422
+ * SÓ AS FOLHAS DENTRO DO ESCOPO - corrigido em 21/09, na primeira corrida real.
2423
+ *
2424
+ * A versao de estreia leu TODAS as folhas colhidas e o `codelevel` respondeu com
2425
+ * `_local/bkp/scrollytelling/styles.css`: um backup morto, fora do escopo declarado
2426
+ * (`packages/ui`), que declara `html, body { background: var(--bg) }` com tokens que
2427
+ * o projeto de hoje nao tem. A regra certa estava tres arquivos ao lado e perdeu para
2428
+ * a ordem da varredura.
2429
+ *
2430
+ * `g.outside` ja existia e ja era contado logo acima, em `deForaDoEscopo` - eu e que
2431
+ * nao o usei. Um chao lido de codigo morto e pior que nenhum: ele tem a confianca de
2432
+ * uma medicao e a procedencia de um arquivo que ninguem executa.
2433
+ */
2434
+ globalSheets
2435
+ .filter((g) => !g.outside)
2436
+ .map((g) => ({ file: g.file, css: g.body })));
2421
2437
  const declaredSchemes = {
2422
2438
  base: Object.fromEntries(schemes.base),
2423
2439
  light: Object.fromEntries(schemes.light),
@@ -274,6 +274,40 @@ function importedNames(source, internal = []) {
274
274
  */
275
275
  const BOUND = /(?:const|let|var|function|class)\s+([A-Z][A-Za-z0-9_]*)|[{,]\s*\w+\s*:\s*([A-Z][A-Za-z0-9_]*)\s*(?:=[^,}]*)?\s*[,}]/g;
276
276
  const EXPORTED_NAME = /export\s+(?:default\s+)?(?:async\s+)?(?:function|class|const|let|var)\s+([A-Z][A-Za-z0-9_]*)|export\s+default\s+([A-Z][A-Za-z0-9_]*)|export\s*\{([^}]*)\}/g;
277
+ /**
278
+ * OS COMPONENTES QUE ESTE ARQUIVO EXPORTA - a metade que faltava para o hook
279
+ * enxergar a PRATELEIRA, e não só os valores.
280
+ *
281
+ * O hook já dizia "este valor tem nome no sistema". Ele nunca dizia "esta PEÇA
282
+ * tem recipe no sistema", e o agente do CodeLevel mediu o efeito disso em
283
+ * 22/09: escreveu cinco componentes do zero num dia, com pelo menos quatro
284
+ * deles já existindo no índice - *"a ferramenta estava ligada o dia inteiro e
285
+ * eu não fiz uma pergunta a ela"*.
286
+ *
287
+ * Reusa a mesma regex de `localOnlyNames`, que é quem já sabe ler as quatro
288
+ * formas de exportar. Duas leituras do mesmo formato discordariam um dia.
289
+ */
290
+ export function exportedNames(source) {
291
+ const nomes = new Set();
292
+ EXPORTED_NAME.lastIndex = 0;
293
+ for (const m of source.matchAll(EXPORTED_NAME)) {
294
+ if (m[1])
295
+ nomes.add(m[1]);
296
+ if (m[2])
297
+ nomes.add(m[2]);
298
+ if (m[3]) {
299
+ for (const cru of m[3].split(",")) {
300
+ const nome = cru
301
+ .trim()
302
+ .split(/\s+as\s+/)[0]
303
+ ?.trim();
304
+ if (nome && /^[A-Z]/.test(nome))
305
+ nomes.add(nome);
306
+ }
307
+ }
308
+ }
309
+ return nomes;
310
+ }
277
311
  export function localOnlyNames(source) {
278
312
  const exported = new Set();
279
313
  EXPORTED_NAME.lastIndex = 0;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * A PRATELEIRA - "esta peça já existe no sistema", dito sem ninguém perguntar.
3
+ *
4
+ * O PROBLEMA, medido em 22/09 no CodeLevel. O agente escreveu cinco componentes
5
+ * do zero num dia; pelo menos quatro já existiam no índice do sistema. Ele
6
+ * mesmo contou o porquê: *"o AGENTS.md manda consultar o índice antes de
7
+ * escrever UI (...) não consultei uma única vez. Também não chamei nenhuma das
8
+ * ferramentas dele"*. E fechou: *"a ferramenta estava ligada o dia inteiro e eu
9
+ * não fiz uma pergunta a ela"*.
10
+ *
11
+ * As duas metades do produto têm caminhos diferentes, e é isso que explica o
12
+ * resultado: a GUARDA roda no hook, sem pedir licença, e funcionou 209 de 209.
13
+ * A BIBLIOTECA é MCP - `list_components`, `describe_component` -, e MCP é PULL:
14
+ * só acontece se o agente resolver perguntar. Ele não resolveu nenhuma vez.
15
+ *
16
+ * Isto põe a biblioteca no caminho de push.
17
+ *
18
+ * ─────────────────────────────────────────────────────────────────────────
19
+ * A DISCIPLINA, e ela é a parte importante deste arquivo.
20
+ *
21
+ * Casar NOME é a família de defeito que este repositório já pagou caro: nome
22
+ * vencendo a leitura foi o erro do importador. Então aqui:
23
+ *
24
+ * IGUALDADE EXATA, depois de normalizar. Nada de substring, prefixo,
25
+ * distância de edição ou plural. `Card` casa com `card`; `CardHeader` não
26
+ * casa com `card`, e é melhor perder o aviso do que dar um errado.
27
+ *
28
+ * É PERGUNTA, NUNCA VEREDITO. O texto não diz "você errou": diz que os dois
29
+ * nomes coincidem e pergunta se são a mesma coisa.
30
+ *
31
+ * A CHECAGEM DECLARA A PRÓPRIA FRAQUEZA na saída. Quem lê é um agente, e um
32
+ * agente que recebe "isto já existe" como fato vai apagar trabalho certo.
33
+ */
34
+ /** `StatCard` → `stat-card`; `DSCard` → `ds-card`. */
35
+ export function normalizar(nome) {
36
+ return nome
37
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
38
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
39
+ .toLowerCase();
40
+ }
41
+ /** Os componentes que o sistema declara, de um documento de design system. */
42
+ export function declarados(documents) {
43
+ const nomes = new Set();
44
+ for (const doc of documents) {
45
+ const comps = doc?.components;
46
+ if (comps)
47
+ for (const k of Object.keys(comps))
48
+ nomes.add(normalizar(k));
49
+ const blocos = doc?.blocks;
50
+ if (blocos)
51
+ for (const k of Object.keys(blocos))
52
+ nomes.add(normalizar(k));
53
+ }
54
+ return nomes;
55
+ }
56
+ /**
57
+ * O que este arquivo define E o sistema já declara. Vazio é a resposta normal.
58
+ *
59
+ * `ds-` some do nome declarado antes de comparar: o sistema guarda `card` e a
60
+ * classe emitida é `ds-card`, e um repositório que chama a peça dele de `DsCard`
61
+ * está falando do mesmo componente.
62
+ */
63
+ export function naPrateleira(exportados, sistema) {
64
+ const achados = [];
65
+ for (const nome of exportados) {
66
+ const n = normalizar(nome);
67
+ const semPrefixo = n.startsWith("ds-") ? n.slice(3) : n;
68
+ if (sistema.has(n))
69
+ achados.push({ escrito: nome, declarado: n });
70
+ else if (sistema.has(semPrefixo))
71
+ achados.push({ escrito: nome, declarado: semPrefixo });
72
+ }
73
+ return achados;
74
+ }
75
+ /** O texto que o agente lê. Pergunta, com a fraqueza declarada. */
76
+ export function falaDaPrateleira(rel, achados, sistema) {
77
+ if (achados.length === 0)
78
+ return [];
79
+ return [
80
+ `${rel} - ${sistema} already declares ${achados.length === 1 ? "a component" : "components"} with ${achados.length === 1 ? "this name" : "these names"}:`,
81
+ ...achados.map((a) => ` you wrote ${a.escrito} · the system has ${a.declarado}`),
82
+ "",
83
+ "If they are the same thing, materialize the system's instead of keeping yours - it arrives already wearing the tokens, and it stays in step when the system changes.",
84
+ /**
85
+ * A FRAQUEZA VAI JUNTO. Esta checagem compara NOME, e nome colide: um
86
+ * `Card` de dashboard e um `card` de baralho são palavras iguais e coisas
87
+ * diferentes. Sem esta linha, um agente obediente apaga trabalho certo.
88
+ */
89
+ "This check compares NAMES only, and names collide. If yours is a different thing, keep it and say so in your summary.",
90
+ ];
91
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.449",
3
+ "version": "0.16.451",
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": {