synthesisui 0.16.473 → 0.16.474

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,4 +1,5 @@
1
1
  import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import { importMark, writeSyncMark } from "../last-sync.js";
2
3
  import { readStyleSheet } from "../doctor/scss-lens.js";
3
4
  import { tsTokenObjects, tsTokensAsVars } from "../doctor/ts-tokens-lens.js";
4
5
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
@@ -5234,6 +5235,12 @@ export async function runImport(opts) {
5234
5235
  * mais longa do produto ficar mais longa no lugar onde ela já é mais difícil de ler.
5235
5236
  */
5236
5237
  if (payload?.slug) {
5238
+ /**
5239
+ * O PONTO DE PARTIDA DO PRIMEIRO `sync` - ver `importMark`. Sem ele, o que o agente cria depois do import nunca é
5240
+ * anunciado (bateria, 28/09: 16 componentes no b7, nenhum dito). Só aqui, com o sistema nascido: um `--dry` não
5241
+ * envia nada, e uma marca sem envio mentiria sobre o que a plataforma tem.
5242
+ */
5243
+ await writeSyncMark(root, importMark(census, opts.cli));
5237
5244
  /**
5238
5245
  * O QUE O CSS DELE AINDA IMPORTA - e este aviso sai do CLI, não da skill.
5239
5246
  *
@@ -485,7 +485,12 @@ export const COUNTED_DIFFERENTLY = "this run counts a value as named only when Y
485
485
  * `:root { --q-stone: #44403c; … }` de uma linha atribui ao token. Um hook anterior manda trocar a definição pelo
486
486
  * próprio nome, e quem obedece apaga a cor.
487
487
  */
488
- export const CHECKER_SINCE = "0.16.473";
488
+ /**
489
+ * 0.16.473 -> 0.16.474 em 28/09, e o passo 1 dá **SIM** (a bateria, b5): o que o fim do turno diz de um componente novo
490
+ * passa a medir as regras da folha global que ele veste pelo `className`. Um hook anterior diz "no colour or spacing of
491
+ * its own to measure" sobre o componente de um projeto que estiliza por classe global.
492
+ */
493
+ export const CHECKER_SINCE = "0.16.474";
489
494
  /**
490
495
  * A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
491
496
  *
package/dist/last-sync.js CHANGED
@@ -45,6 +45,26 @@ components) {
45
45
  }
46
46
  return out;
47
47
  }
48
+ /**
49
+ * A MARCA QUE O IMPORT DEIXA - o ponto de partida do primeiro `sync`. Bateria, 28/09 (b7): um projeto sem componente
50
+ * ganhou 16 ao longo de cinco telas e nenhum foi anunciado, porque só o `sync` gravava marca e `whatGrew` sem marca
51
+ * anterior não anuncia nada. Os mesmos campos que o `sync` grava, lidos do mesmo censo - nunca uma segunda conta.
52
+ * `components` é o que o censo admitiu, que é o que o import envia.
53
+ */
54
+ export function importMark(census, cli) {
55
+ const sum = (r) => Object.values(r ?? {}).reduce((n, v) => n + v, 0);
56
+ const readings = fingerprintReadings(census.components);
57
+ return {
58
+ at: new Date().toISOString(),
59
+ components: Object.keys(readings).length,
60
+ declarations: census.coverage?.declarations ?? 0,
61
+ seen: sum(census.ledger?.counted),
62
+ interpreted: sum(census.ledger?.interpreted),
63
+ unread: Object.fromEntries((census.ledger?.unread ?? []).map((g) => [g.reason ?? "unknown", g.uses ?? 0])),
64
+ ...(cli ? { cli } : {}),
65
+ readings,
66
+ };
67
+ }
48
68
  const path = (root) => join(root, "_synthesisui", LAST_SYNC_FILE);
49
69
  export async function readSyncMark(root) {
50
70
  try {
package/dist/what-grew.js CHANGED
@@ -16,12 +16,16 @@ before) {
16
16
  return { born: [], gone };
17
17
  const { table } = await loadSystem(root).catch(() => ({ table: null }));
18
18
  const context = [];
19
+ /** As folhas GLOBAIS (não `.module.*`) - ver `globalRulesFor`. */
20
+ const globals = [];
19
21
  if (table) {
20
22
  for await (const file of walkAll([root])) {
21
23
  const rel = relative(root, file);
22
24
  if (!/\.(css|scss|sass|less)$/i.test(rel))
23
25
  continue;
24
26
  const src = await readFile(file, "utf8").catch(() => "");
27
+ if (src && !/\.module\.[a-z]+$/i.test(rel))
28
+ globals.push({ rel, src });
25
29
  const report = src ? scanSource(rel, src, table) : null;
26
30
  if (report?.ownDeclared?.length)
27
31
  context.push(report);
@@ -49,6 +53,17 @@ before) {
49
53
  if (css)
50
54
  reports.push(scanSource(sheet, css, table));
51
55
  }
56
+ /**
57
+ * E AS REGRAS GLOBAIS QUE ELE VESTE - bateria, 28/09 (b5): o componente de um projeto que estiliza por classe global
58
+ * não importa folha nenhuma, e saía "no colour or spacing of its own to measure".
59
+ */
60
+ const worn = classesWorn(src);
61
+ if (worn.size > 0)
62
+ for (const g of globals) {
63
+ const rules = globalRulesFor(g.src, worn);
64
+ if (rules)
65
+ reports.push(scanSource(g.rel, rules, table));
66
+ }
52
67
  const d = diagnose(reports, context);
53
68
  /** Classes utilitárias numa string qualquer do arquivo - `className="…"`, `cn("…")`, `cva("…")` (f11, 28/09). */
54
69
  const utilities = !d.reach.measured &&
@@ -117,3 +132,26 @@ export async function takeGrowthNote(root) {
117
132
  return null;
118
133
  }
119
134
  }
135
+ /** As classes escritas por extenso no `className` - `className="a b"`, `'a'` ou `` `a b` `` sem interpolação. */
136
+ function classesWorn(src) {
137
+ const out = new Set();
138
+ for (const m of src.matchAll(/className=(?:"([^"]*)"|'([^']*)'|\{`([^`$]*)`\})/g))
139
+ for (const c of (m[1] ?? m[2] ?? m[3] ?? "").split(/\s+/))
140
+ if (c)
141
+ out.add(c);
142
+ return out;
143
+ }
144
+ /**
145
+ * AS REGRAS DE UMA FOLHA GLOBAL CUJO SELETOR NOMEIA UMA DAS CLASSES QUE ELE VESTE, recortadas como CSS - só o bloco
146
+ * mais interno (`[^{}]`), então uma regra dentro de `@media` também entra. `null` quando nenhuma casa.
147
+ */
148
+ function globalRulesFor(css, worn) {
149
+ const out = [];
150
+ for (const m of css.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
151
+ const selector = (m[1] ?? "").trim();
152
+ const names = [...selector.matchAll(/\.([A-Za-z_][\w-]*)/g)].map((x) => x[1]);
153
+ if (names.some((n) => worn.has(n)))
154
+ out.push(`${selector} { ${(m[2] ?? "").trim()} }`);
155
+ }
156
+ return out.length > 0 ? `${out.join("\n")}\n` : null;
157
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.473",
3
+ "version": "0.16.474",
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": {