synthesisui 0.16.296 → 0.16.298

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.
@@ -47,8 +47,9 @@ import { body, paint, section } from "../output.js";
47
47
  import { outsideScope } from "../outside-scope.js";
48
48
  import { phase, startProgress } from "../progress.js";
49
49
  import { repoStateOf } from "../repo-state.js";
50
- import { runtimeDeclaredVars } from "../runtime-vars.js";
50
+ import { runtimeDeclaredVars, runtimeDeclaredVarsIn } from "../runtime-vars.js";
51
51
  import { detectStack, resolveDeps, stackVersions } from "../stack.js";
52
+ import { wiredSlugs, wiringWarning } from "../wired-slugs.js";
52
53
  import { placeInWorkspace } from "../workspace-place.js";
53
54
  import { add } from "./add.js";
54
55
  import { walk, walkAll } from "./doctor.js";
@@ -1945,6 +1946,27 @@ export async function takeCensus(root, opts) {
1945
1946
  */
1946
1947
  for (const name of runtimeDeclaredVars(sources))
1947
1948
  declaredNames.add(name);
1949
+ /**
1950
+ * E FORA DO ESCOPO TAMBÉM - senão o conserto acima não alcança um monorepo.
1951
+ *
1952
+ * Com um escopo estreitado este `root` É a pasta do escopo, então `sources` tem o escopo e mais
1953
+ * nada. As fontes deste repositório são declaradas nos apps e CONSUMIDAS dentro da biblioteca - a
1954
+ * divisão que o próprio Next recomenda, e não um acidente de organização.
1955
+ *
1956
+ * MEDIDO: com o leitor acima já publicado, a segunda corrida ainda reportou as quatro fontes como
1957
+ * mortas. A promessa escrita acima é *"a name is only broken if NOTHING declares it, anywhere"*, e
1958
+ * "anywhere" tem que incluir os roots que a pessoa nomeou em uso.
1959
+ */
1960
+ const usageRoots = (opts?.usage ?? []).map((u) => u.path);
1961
+ if (usageRoots.length > 0) {
1962
+ const outside = await runtimeDeclaredVarsIn(usageRoots, {
1963
+ readdir: (p) => readdir(p, { withFileTypes: true, encoding: "utf8" }),
1964
+ readFile: (p) => readFile(p, "utf8"),
1965
+ join,
1966
+ });
1967
+ for (const name of outside)
1968
+ declaredNames.add(name);
1969
+ }
1948
1970
  /**
1949
1971
  * HOW SOMEBODY CALLS THE COMPONENT THEY ALREADY HAVE.
1950
1972
  *
@@ -2092,6 +2114,17 @@ export async function takeCensus(root, opts) {
2092
2114
  : {}),
2093
2115
  ...(animations.size > 0 ? { animations: [...animations].sort() } : {}),
2094
2116
  ...(brokenRefs.length > 0 ? { brokenRefs } : {}),
2117
+ /**
2118
+ * OS SISTEMAS QUE O CSS DELE JÁ CHAMA PELO NOME - ver `wiredSlugs`.
2119
+ *
2120
+ * Existe porque um import de 24/08 deixou o repositório do dono SEM COMPILAR: o CSS dele
2121
+ * carrega `_synthesisui/ds/codelevel/`, o sistema nasceu como `codelevel-ds`, e o `next build`
2122
+ * dele terminou em exit code 1 com `Can't resolve`. Não é cor errada - é o app parado.
2123
+ *
2124
+ * O campo é a metade medida da resposta; a outra é a pergunta do nome saber usá-lo. Aditivo e
2125
+ * omitido quando não há nada cabeado, que é o caso de todo primeiro import limpo.
2126
+ */
2127
+ ...(wiredSlugs(css).length > 0 ? { wiredSlugs: wiredSlugs(css) } : {}),
2095
2128
  /**
2096
2129
  * OS NOMES DISPUTADOS - ver `name-claim.ts`.
2097
2130
  *
@@ -3974,6 +4007,26 @@ export async function runImport(opts) {
3974
4007
  * mais longa do produto ficar mais longa no lugar onde ela já é mais difícil de ler.
3975
4008
  */
3976
4009
  if (payload?.slug) {
4010
+ /**
4011
+ * A FIAÇÃO DELE CONTRA O SLUG QUE NASCEU - e este aviso sai do CLI, não da skill.
4012
+ *
4013
+ * MEDIDO em 24/08: o `globals.css` dele importa `_synthesisui/ds/codelevel/`, o nome escolhido
4014
+ * derivou `codelevel-ds`, ninguém mencionou, e o `next build` dele terminou em exit code 1 com
4015
+ * `Can't resolve`. O aplicativo do cliente parou de compilar por causa de um comando que a
4016
+ * plataforma conduziu do começo ao fim.
4017
+ *
4018
+ * AQUI e não só no playbook porque o playbook é conselho e isto é fato: o slug chegou do
4019
+ * servidor, a pasta vai ser escrita com ele, e a divergência é aritmética. Um aviso que depende
4020
+ * de um agente ter lido o capítulo certo é um aviso que falta exatamente quando ele não leu -
4021
+ * foi assim que este defeito passou.
4022
+ */
4023
+ const warning = wiringWarning(census.wiredSlugs ?? [], payload.slug);
4024
+ if (warning) {
4025
+ console.log("");
4026
+ console.log(section("Your CSS points somewhere else"));
4027
+ for (const line of warning.split("\n"))
4028
+ console.log(body(line));
4029
+ }
3977
4030
  try {
3978
4031
  await add(payload.slug, {
3979
4032
  registry: opts.registry,
@@ -47,3 +47,56 @@ export function runtimeDeclaredVars(sources) {
47
47
  }
48
48
  return out;
49
49
  }
50
+ /**
51
+ * E A DECLARAÇÃO PODE MORAR FORA DO ESCOPO - o caso que o conserto de 24/08 não alcançou.
52
+ *
53
+ * MEDIDO no `codelevel` na segunda corrida, já com o leitor acima publicado: as quatro fontes
54
+ * CONTINUARAM sendo reportadas como mortas. A causa não era o leitor - era o que chega nele.
55
+ *
56
+ * `--scope packages/ui` faz o `root` do censo ser a pasta do escopo, então a varredura vê
57
+ * `packages/ui` e mais nada. As fontes são declaradas em `apps/landing/app/fonts.ts` e
58
+ * `apps/web/app/fonts.ts`, e USADAS dentro de `packages/ui`. Essa divisão não é acidente de
59
+ * organização: num monorepo, o app carrega a fonte e a biblioteca a consome, e é a forma
60
+ * recomendada pelo próprio Next.
61
+ *
62
+ * O código já promete o contrário do que fazia - *"a name is only broken if NOTHING declares it,
63
+ * anywhere"* está escrito no ponto que monta a lista. "Anywhere" tem que incluir os roots de
64
+ * `--usage`, que a pessoa nomeou justamente para dizer onde o vocabulário é consumido.
65
+ *
66
+ * SÓ LEITURA, e só de arquivos de código: nada aqui escreve, e um root de uso já é lido para
67
+ * evidência - a diferença é que agora ele também pode DECLARAR.
68
+ */
69
+ export async function runtimeDeclaredVarsIn(roots, read) {
70
+ const out = new Set();
71
+ const seen = new Set();
72
+ const walk = async (dir, depth) => {
73
+ /**
74
+ * O TETO DE PROFUNDIDADE é o que mantém isto barato num monorepo grande. Uma declaração de
75
+ * fonte mora no diretório de app (`app/fonts.ts`, `src/app/fonts.ts`, `styles/fonts.ts`) -
76
+ * quatro níveis cobrem as três formas com folga, e o custo não cresce com o tamanho do repo.
77
+ */
78
+ if (depth < 0 || seen.has(dir))
79
+ return;
80
+ seen.add(dir);
81
+ const entries = await read.readdir(dir).catch(() => []);
82
+ for (const e of entries) {
83
+ if (e.name.startsWith(".") || e.name === "node_modules")
84
+ continue;
85
+ const full = read.join(dir, e.name);
86
+ if (e.isDirectory()) {
87
+ await walk(full, depth - 1);
88
+ continue;
89
+ }
90
+ if (!/\.(ts|tsx|js|jsx|mjs)$/i.test(e.name))
91
+ continue;
92
+ const src = await read.readFile(full).catch(() => "");
93
+ if (!src)
94
+ continue;
95
+ for (const name of runtimeDeclaredVars([{ file: full, source: src }]))
96
+ out.add(name);
97
+ }
98
+ };
99
+ for (const r of roots)
100
+ await walk(r, 4);
101
+ return out;
102
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * OS SISTEMAS QUE O CSS DELE JÁ CHAMA PELO NOME - e o build que quebra quando o nome não bate.
3
+ *
4
+ * O QUE ACONTECEU, medido no repositório do dono em 24/08: o `globals.css` dele carrega
5
+ *
6
+ * @import "../../../../_synthesisui/ds/codelevel/tokens.css";
7
+ * @import "../../../../_synthesisui/ds/codelevel/theme.css";
8
+ *
9
+ * O import daquela corrida nasceu com o slug `codelevel-ds`, então o `add` escreveu
10
+ * `_synthesisui/ds/codelevel-ds/` e os dois `@import` passaram a apontar para uma pasta que não
11
+ * existe. O `next build` dele terminou em **exit code 1**:
12
+ *
13
+ * Can't resolve '../../../../_synthesisui/ds/codelevel/tokens.css'
14
+ *
15
+ * Não é uma cor errada nem um token faltando: o aplicativo dele **não compila**. É o pior desfecho
16
+ * possível de uma esteira que existe para devolver o código dele funcionando, e ele sai de um
17
+ * comando que a plataforma conduziu do começo ao fim.
18
+ *
19
+ * POR QUE O SLUG PODE DIVERGIR: o nome é decisão dele, feita numa pergunta, e o slug deriva do
20
+ * nome. "CodeLevel" dá `codelevel`; "CodeLevel DS" dá `codelevel-ds`. As duas respostas são
21
+ * legítimas - o que não é legítimo é ninguém dizer que a segunda quebra o CSS que já está lá.
22
+ *
23
+ * ENTÃO A MEDIÇÃO VEM ANTES DA PERGUNTA. Este leitor responde "quais slugs o CSS deste repositório
24
+ * já espera encontrar", e o censo carrega a resposta. Com ela na mão, a pergunta do nome deixa de
25
+ * ser um campo em branco: existe um slug que faz a fiação resolver, e ele é dito em voz alta.
26
+ *
27
+ * DIZER, E NÃO DECIDIR. A plataforma não escolhe o nome do sistema dele nem reescreve o CSS dele -
28
+ * é o mesmo padrão do `add`, que imprime o `@import` e não o cola. O que ela deve é nunca deixá-lo
29
+ * escolher às cegas uma resposta que apaga o build.
30
+ */
31
+ /**
32
+ * O caminho que a nossa própria instalação usa. Qualquer profundidade de `../`, porque a folha dele
33
+ * pode estar em qualquer nível - a do dono está quatro acima.
34
+ */
35
+ const WIRED = /_synthesisui\/ds\/([a-z0-9][a-z0-9-]*)\//gi;
36
+ /**
37
+ * Os slugs que o CSS dele já referencia, em ordem de aparição e sem repetir.
38
+ *
39
+ * Recebe o CSS já concatenado porque é isso que o censo tem em mão - uma segunda varredura de
40
+ * arquivos seria uma segunda medição, livre para discordar da primeira.
41
+ */
42
+ export function wiredSlugs(css) {
43
+ const out = [];
44
+ for (const m of css.matchAll(WIRED))
45
+ if (!out.includes(m[1].toLowerCase()))
46
+ out.push(m[1].toLowerCase());
47
+ return out;
48
+ }
49
+ /**
50
+ * O QUE DIZER A ELE QUANDO O SISTEMA JÁ NASCEU COM OUTRO NOME.
51
+ *
52
+ * Roda DEPOIS do envio, com o slug que o servidor devolveu - então não é previsão, é fato: a pasta
53
+ * vai ser escrita com este nome e os `@import` dele apontam para outro.
54
+ *
55
+ * `null` quando o CSS não cabeia nada (todo primeiro import limpo) e quando o slug que nasceu já é
56
+ * um dos cabeados. Um aviso que aparece sempre é um aviso que ninguém lê.
57
+ *
58
+ * A frase carrega as três coisas que a decisão precisa: o que o CSS espera, o que existe agora, e o
59
+ * que acontece se ele não fizer nada. Sem a terceira isto é trivia - e foi a terceira que faltou na
60
+ * corrida que parou o build dele.
61
+ *
62
+ * DIZER, E NÃO CONSERTAR. O CSS é dele; reescrevê-lo por conta própria seria a plataforma editando
63
+ * o repositório de alguém sem pedir. É o mesmo padrão do `add`, que imprime o `@import` e não o
64
+ * cola.
65
+ */
66
+ export function wiringWarning(
67
+ /**
68
+ * A LISTA, e não o CSS - porque o censo já a carrega (`census.wiredSlugs`) e o CSS bruto não
69
+ * viaja nele. Recontar aqui seria uma segunda medição, livre para discordar da primeira.
70
+ */
71
+ wired, slugThatWasBorn) {
72
+ if (wired.length === 0)
73
+ return null;
74
+ if (wired.includes(slugThatWasBorn.toLowerCase()))
75
+ return null;
76
+ return [
77
+ `Your CSS imports _synthesisui/ds/${wired[0]}/, and this system is "${slugThatWasBorn}".`,
78
+ `Those @import lines point at a folder that does not exist, and the build fails on them -`,
79
+ `not a missing colour, a build that stops. Point them at ${slugThatWasBorn} to fix it:`,
80
+ "",
81
+ ` @import ".../_synthesisui/ds/${slugThatWasBorn}/tokens.css";`,
82
+ ` @import ".../_synthesisui/ds/${slugThatWasBorn}/theme.css";`,
83
+ "",
84
+ "Yours to change - we do not edit your CSS.",
85
+ ].join("\n");
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.296",
3
+ "version": "0.16.298",
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": {