synthesisui 0.16.309 → 0.16.311
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.
- package/dist/commands/component.js +2 -34
- package/dist/commands/gaps.js +6 -2
- package/dist/commands/import.js +23 -20
- package/dist/commands/template.js +35 -2
- package/dist/doctor/gap-triage.js +21 -6
- package/dist/doctor/style-ledger.js +30 -4
- package/dist/their-tongue.js +40 -0
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { body, section, snippet } from "../output.js";
|
|
|
9
9
|
import { findCollision, reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
10
10
|
import { fetchComponent, RegistryError } from "../registry.js";
|
|
11
11
|
import { flavourResolver } from "../styles-flavour.js";
|
|
12
|
-
import { inTheirTongue,
|
|
12
|
+
import { inTheirTongue, projectTongue } from "../their-tongue.js";
|
|
13
13
|
import { readCensus, unreadComment, unreadForComponent, } from "../unread-for-component.js";
|
|
14
14
|
/**
|
|
15
15
|
* Writes the shared `cn.ts` next to the components, built from THIS project's
|
|
@@ -98,7 +98,7 @@ export async function component(slug, name, opts) {
|
|
|
98
98
|
* apagaria a cor. Um projeto de destino chega sem mapa no `.lock`, nada é traduzido, e a folha
|
|
99
99
|
* continua sendo o caminho - o comando DIZ qual dos dois aconteceu.
|
|
100
100
|
*/
|
|
101
|
-
const tongue = await
|
|
101
|
+
const tongue = await projectTongue(root, slug);
|
|
102
102
|
const spoken = tongue ? inTheirTongue(res.css, tongue) : null;
|
|
103
103
|
const css = spoken ? spoken.css : res.css;
|
|
104
104
|
/**
|
|
@@ -325,35 +325,3 @@ export async function component(slug, name, opts) {
|
|
|
325
325
|
}
|
|
326
326
|
console.log("");
|
|
327
327
|
}
|
|
328
|
-
/**
|
|
329
|
-
* O VOCABULÁRIO DESTE REPOSITÓRIO, do que o `add` já deixou na pasta - ver `tongueOf`.
|
|
330
|
-
*
|
|
331
|
-
* `null` quando não há mapa: é a resposta de um projeto de destino, de um repositório que nunca
|
|
332
|
-
* buildou, ou de um install feito por um CLI anterior a 0.16.290. Nos três casos nada é traduzido e
|
|
333
|
-
* a folha instalada continua sendo o caminho, que é o comportamento de sempre.
|
|
334
|
-
*/
|
|
335
|
-
async function tongueFor(root, slug) {
|
|
336
|
-
const dir = join(root, "_synthesisui", "ds", slug);
|
|
337
|
-
const lock = await readFile(join(dir, ".lock"), "utf8").catch(() => null);
|
|
338
|
-
if (!lock)
|
|
339
|
-
return null;
|
|
340
|
-
let map = [];
|
|
341
|
-
let version = 0;
|
|
342
|
-
try {
|
|
343
|
-
const parsed = JSON.parse(lock);
|
|
344
|
-
map = parsed.tokenMap ?? [];
|
|
345
|
-
version = parsed.version ?? 0;
|
|
346
|
-
}
|
|
347
|
-
catch {
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
if (map.length === 0)
|
|
351
|
-
return null;
|
|
352
|
-
/**
|
|
353
|
-
* O VALOR COMPILADO MORA NA PASTA DA VERSÃO - a folha da raiz é um re-export de uma linha, escrito
|
|
354
|
-
* assim de propósito para que o `@import` dele nunca mude entre updates (ver `add.ts`). É de lá
|
|
355
|
-
* que sai o literal para as variáveis que não têm nome dele.
|
|
356
|
-
*/
|
|
357
|
-
const installed = await readFile(join(dir, `v${version}`, "tokens.css"), "utf8").catch(() => "");
|
|
358
|
-
return tongueOf(map, installed);
|
|
359
|
-
}
|
package/dist/commands/gaps.js
CHANGED
|
@@ -26,8 +26,12 @@ export async function gaps(opts) {
|
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
let ledger;
|
|
29
|
+
/** A régua única - ver `describeValueRuler`. Ausente num censo medido antes de 0.16.308. */
|
|
30
|
+
let values;
|
|
29
31
|
try {
|
|
30
|
-
|
|
32
|
+
const census = JSON.parse(raw);
|
|
33
|
+
ledger = census.ledger;
|
|
34
|
+
values = census.values?.classes ?? undefined;
|
|
31
35
|
}
|
|
32
36
|
catch {
|
|
33
37
|
console.log(body(`${path} is not readable JSON.`));
|
|
@@ -42,7 +46,7 @@ export async function gaps(opts) {
|
|
|
42
46
|
console.log(body(`This measurement was written by a tool version that did not count style fragments yet, so the numbers do not exist rather than being zero. Run \`synthesisui import --dry\` again with ${opts.cli} to measure.`));
|
|
43
47
|
return;
|
|
44
48
|
}
|
|
45
|
-
for (const line of describeTriage(triageLedger(ledger, opts.cli)))
|
|
49
|
+
for (const line of describeTriage(triageLedger(ledger, opts.cli), values))
|
|
46
50
|
console.log(body(line));
|
|
47
51
|
/**
|
|
48
52
|
* E O QUE A CAMADA GLOBAL JÁ LEVOU - o desconto, sem o qual este comando repete a mentira que ele
|
package/dist/commands/import.js
CHANGED
|
@@ -1849,7 +1849,29 @@ export async function takeCensus(root, opts) {
|
|
|
1849
1849
|
* AS LEIS DAS PÁGINAS, ditas onde a arquitetura já é dita - governam o sistema, não uma peça,
|
|
1850
1850
|
* então `applies` é vazio e elas chegam a todo prompt. Ver `pageLaws`.
|
|
1851
1851
|
*/
|
|
1852
|
-
|
|
1852
|
+
/**
|
|
1853
|
+
* A CONTABILIDADE POR VALOR, contada AQUI porque o material é daqui: os tokens de classe que
|
|
1854
|
+
* vestem cada componente admitido (os nós do sketch e as camadas condicionais cruas), contra
|
|
1855
|
+
* os tokens que o CSS dele declara. Ver `Census.values` e `doctor/value-ledger.ts` - é a
|
|
1856
|
+
* reconciliação do item 11, e a soma fecha com `seen` por construção.
|
|
1857
|
+
*
|
|
1858
|
+
* ANTES do relatório do ledger de propósito: "quanto vocês entenderam?" tem UMA resposta (dono,
|
|
1859
|
+
* 26/08), e é esta régua - a mesma que a tela usa. A frase do ledger passa a liderar com ela.
|
|
1860
|
+
*/
|
|
1861
|
+
const valueByComponent = {};
|
|
1862
|
+
for (const [lookName, look] of Object.entries(looks)) {
|
|
1863
|
+
const tokens = [];
|
|
1864
|
+
for (const node of look.sketch ?? []) {
|
|
1865
|
+
tokens.push(...(node.classes ?? "").split(/\s+/).filter(Boolean));
|
|
1866
|
+
}
|
|
1867
|
+
for (const layer of look.rawLayers ?? [])
|
|
1868
|
+
tokens.push(...layer.classes);
|
|
1869
|
+
if (tokens.length > 0) {
|
|
1870
|
+
valueByComponent[lookName] = accountClasses(tokens, declaredValues);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
const valuesTotal = sumAccounts(Object.values(valueByComponent));
|
|
1874
|
+
const ledgerLines = describeLedger(ledger, valuesTotal);
|
|
1853
1875
|
if (ledgerLines.length > 0) {
|
|
1854
1876
|
say("");
|
|
1855
1877
|
say(section("Every style fragment, accounted for"));
|
|
@@ -2138,25 +2160,6 @@ export async function takeCensus(root, opts) {
|
|
|
2138
2160
|
const target = c?.canonical ?? (c?.bucket === "exclusive" ? c.name : null);
|
|
2139
2161
|
return target ? safePartName(target) : null;
|
|
2140
2162
|
}, (pkg) => versions[pkg]);
|
|
2141
|
-
/**
|
|
2142
|
-
* A CONTABILIDADE POR VALOR, contada AQUI porque o material é daqui: os tokens de classe que
|
|
2143
|
-
* vestem cada componente admitido (os nós do sketch e as camadas condicionais cruas), contra
|
|
2144
|
-
* os tokens que o CSS dele declara. Ver `Census.values` e `doctor/value-ledger.ts` - é a
|
|
2145
|
-
* reconciliação do item 11, e a soma fecha com `seen` por construção.
|
|
2146
|
-
*/
|
|
2147
|
-
const valueByComponent = {};
|
|
2148
|
-
for (const [lookName, look] of Object.entries(looks)) {
|
|
2149
|
-
const tokens = [];
|
|
2150
|
-
for (const node of look.sketch ?? []) {
|
|
2151
|
-
tokens.push(...(node.classes ?? "").split(/\s+/).filter(Boolean));
|
|
2152
|
-
}
|
|
2153
|
-
for (const layer of look.rawLayers ?? [])
|
|
2154
|
-
tokens.push(...layer.classes);
|
|
2155
|
-
if (tokens.length > 0) {
|
|
2156
|
-
valueByComponent[lookName] = accountClasses(tokens, declaredValues);
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
const valuesTotal = sumAccounts(Object.values(valueByComponent));
|
|
2160
2163
|
return {
|
|
2161
2164
|
census: 1,
|
|
2162
2165
|
project: {
|
|
@@ -2,6 +2,7 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { readProjectConfig, resolveRegistry } from "../config.js";
|
|
4
4
|
import { fetchTemplate } from "../registry.js";
|
|
5
|
+
import { inTheirTongue, projectTongue } from "../their-tongue.js";
|
|
5
6
|
/**
|
|
6
7
|
* Materializes a whole page from a DS template into the project (hybrid
|
|
7
8
|
* codegen-first): the server codegens deterministic files, we write them, and
|
|
@@ -27,14 +28,46 @@ export async function template(slug, name, opts) {
|
|
|
27
28
|
const defaultDir = join("templates", asName ?? name);
|
|
28
29
|
const pageRel = opts.out ?? join(defaultDir, pageFile.filename);
|
|
29
30
|
const pageDir = dirname(join(root, pageRel));
|
|
31
|
+
/**
|
|
32
|
+
* NENHUMA MATERIALIZAÇÃO VAZA VOCABULÁRIO INTERNO (INV-VOLTA-02) - a mesma porta do
|
|
33
|
+
* `component`. Uma página inteira saía com `var(--ds-*)` cru enquanto um componente avulso
|
|
34
|
+
* falava a língua dele; a promessa é uma só. Sem mapa no `.lock`, nada é traduzido e a folha
|
|
35
|
+
* instalada continua sendo o caminho - e a saída diz qual dos dois aconteceu.
|
|
36
|
+
*/
|
|
37
|
+
const tongue = await projectTongue(root, slug);
|
|
38
|
+
const speak = (code) => (tongue ? inTheirTongue(code, tongue) : null);
|
|
39
|
+
let named = 0;
|
|
40
|
+
let inlined = 0;
|
|
41
|
+
const still = new Set();
|
|
30
42
|
await mkdir(pageDir, { recursive: true });
|
|
31
|
-
|
|
43
|
+
const spokenPage = speak(pageFile.code);
|
|
44
|
+
if (spokenPage) {
|
|
45
|
+
named += spokenPage.named;
|
|
46
|
+
inlined += spokenPage.inlined;
|
|
47
|
+
for (const l of spokenPage.left)
|
|
48
|
+
still.add(l);
|
|
49
|
+
}
|
|
50
|
+
await writeFile(join(root, pageRel), spokenPage ? spokenPage.css : pageFile.code, "utf8");
|
|
32
51
|
console.log(`✓ wrote ${pageRel} (${slug} v${generated.version})`);
|
|
33
52
|
for (const f of siblings) {
|
|
34
53
|
const rel = join(dirname(pageRel), f.filename);
|
|
35
|
-
|
|
54
|
+
const spoken = speak(f.code);
|
|
55
|
+
if (spoken) {
|
|
56
|
+
named += spoken.named;
|
|
57
|
+
inlined += spoken.inlined;
|
|
58
|
+
for (const l of spoken.left)
|
|
59
|
+
still.add(l);
|
|
60
|
+
}
|
|
61
|
+
await writeFile(join(root, rel), spoken ? spoken.css : f.code, "utf8");
|
|
36
62
|
console.log(`✓ wrote ${rel}`);
|
|
37
63
|
}
|
|
64
|
+
if (named > 0 || inlined > 0) {
|
|
65
|
+
console.log(` ${named} reference${named === 1 ? "" : "s"} now speak${named === 1 ? "s" : ""} the name YOUR code gives the value${inlined > 0 ? `, and ${inlined} carr${inlined === 1 ? "ies" : "y"} the value because your code names no token for it` : ""}.`);
|
|
66
|
+
if (still.size > 0) {
|
|
67
|
+
const sample = [...still].sort().slice(0, 3).join(", ");
|
|
68
|
+
console.log(` ${still.size} still point${still.size === 1 ? "s" : ""} at our stylesheet (${sample}${still.size > 3 ? ", …" : ""}), so tokens.css is still needed here.`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
38
71
|
console.log("");
|
|
39
72
|
console.log("Next steps:");
|
|
40
73
|
console.log(` • use it in a route, e.g. ${join(config.pagesDir, "page.tsx")}:`);
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* decisão de escrever é de quem lê - a esteira opinando sobre o nosso backlog no terminal do
|
|
21
21
|
* cliente é ela falando de um assunto que não é dela (dono, 05/08).
|
|
22
22
|
*/
|
|
23
|
+
import { describeValueRuler } from "./style-ledger.js";
|
|
23
24
|
/**
|
|
24
25
|
* QUEM É DONO DE CADA FORMA, hoje.
|
|
25
26
|
*
|
|
@@ -151,8 +152,10 @@ export function triageLedger(ledger, triagedBy) {
|
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
154
|
/** `0.16.133` é mais velha que `0.16.134`. Compara número por número, e o que não é número não
|
|
154
|
-
* desempata nada - uma pré-release não é motivo para dizer que a medição está velha.
|
|
155
|
-
|
|
155
|
+
* desempata nada - uma pré-release não é motivo para dizer que a medição está velha.
|
|
156
|
+
* Exportada porque é a TERCEIRA cópia desta pergunta (cli-version.ts e reinterpret.ts na web têm
|
|
157
|
+
* as outras) e a auditoria de 26/08 a encontrou fora do twin-drift - agora ele a amarra. */
|
|
158
|
+
export function isOlder(measured, current) {
|
|
156
159
|
const parts = (v) => v.split(".").map((p) => Number.parseInt(p, 10));
|
|
157
160
|
const a = parts(measured);
|
|
158
161
|
const b = parts(current);
|
|
@@ -173,11 +176,23 @@ function isOlder(measured, current) {
|
|
|
173
176
|
* do não interpretado ser o portão funcionando é a informação mais importante da tela, e ela vem
|
|
174
177
|
* antes de qualquer lista de tarefa.
|
|
175
178
|
*/
|
|
176
|
-
export function describeTriage(t
|
|
179
|
+
export function describeTriage(t,
|
|
180
|
+
/**
|
|
181
|
+
* A RÉGUA ÚNICA (ver `describeValueRuler`): quando o censo carrega o ledger de valores, o
|
|
182
|
+
* percentual que abre esta tela é o MESMO da tela do sistema e do fim do `import`. Era aqui
|
|
183
|
+
* que nascia a segunda resposta - "348 fragmentos, 36%" contra os 91% reais do codelevel
|
|
184
|
+
* (26/08) - e o agente do dono repassou a errada ao cliente.
|
|
185
|
+
*/
|
|
186
|
+
values) {
|
|
177
187
|
const unread = t.counted - t.interpreted;
|
|
178
|
-
const lines =
|
|
179
|
-
|
|
180
|
-
|
|
188
|
+
const lines = values
|
|
189
|
+
? [
|
|
190
|
+
...describeValueRuler(values),
|
|
191
|
+
`The ${unread} style fragments not interpreted are sorted below by what to do about them.`,
|
|
192
|
+
]
|
|
193
|
+
: [
|
|
194
|
+
`${t.counted} style fragments accounted for, ${t.interpreted} interpreted (${t.percent}%). The ${unread} below are sorted by what to do about them.`,
|
|
195
|
+
];
|
|
181
196
|
if (t.stale) {
|
|
182
197
|
lines.push("", `MEASURED BY CLI ${t.measuredBy}, TRIAGED BY ${t.triagedBy}. Re-run \`import\` before concluding anything from the numbers below - a gap here may already be closed, and the work already done.`);
|
|
183
198
|
}
|
|
@@ -278,14 +278,27 @@ function blank() {
|
|
|
278
278
|
* nomeia cada lacuna com o número do repo dela. Um percentual sem a contagem atrás é número de
|
|
279
279
|
* marketing, e um zero sem motivo lê como falha nossa.
|
|
280
280
|
*/
|
|
281
|
-
export function describeLedger(l
|
|
281
|
+
export function describeLedger(l,
|
|
282
|
+
/**
|
|
283
|
+
* A RÉGUA ÚNICA - o ledger de valores por declaração (ver `Census.values.classes`). Quando ela
|
|
284
|
+
* existe, "quanto vocês entenderam?" tem UMA resposta, e é esta: a mesma que a tela usa. O
|
|
285
|
+
* total de fragmentos continua dito - é a promessa de contabilidade -, mas sem um segundo
|
|
286
|
+
* percentual competindo (o agente do dono liderou com "36%" quando a leitura real era 91%,
|
|
287
|
+
* medido no codelevel em 26/08).
|
|
288
|
+
*/
|
|
289
|
+
values) {
|
|
282
290
|
const total = sum(l.counted);
|
|
283
291
|
if (total === 0)
|
|
284
292
|
return [];
|
|
285
293
|
const read = sum(l.interpreted);
|
|
286
|
-
const lines =
|
|
287
|
-
|
|
288
|
-
|
|
294
|
+
const lines = values
|
|
295
|
+
? [
|
|
296
|
+
...describeValueRuler(values),
|
|
297
|
+
`And every one of the ${total} style fragments in your files is accounted for - the ${total - read} not interpreted are listed below with the file and line where each lives.`,
|
|
298
|
+
]
|
|
299
|
+
: [
|
|
300
|
+
`${total} style fragments in your files, and every one is accounted for: ${read} interpreted (${Math.round((read / total) * 100)}%), ${total - read} listed below with the file and line where each lives.`,
|
|
301
|
+
];
|
|
289
302
|
/**
|
|
290
303
|
* POR FORMA, ANTES DOS GRUPOS. O total responde "quanto"; esta tabela responde "de que jeito o
|
|
291
304
|
* projeto escreve estilo", que é a pergunta que decide qual leitor vale construir. Sem ela, 56%
|
|
@@ -319,3 +332,16 @@ export function describeLedger(l) {
|
|
|
319
332
|
function sum(counts) {
|
|
320
333
|
return Object.values(counts).reduce((n, k) => n + k, 0);
|
|
321
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* A frase da régua única, com todo denominador em palavras - `import` e `gaps` a imprimem
|
|
337
|
+
* IDÊNTICA, porque duas variações da mesma frase viram duas respostas na cabeça de quem lê.
|
|
338
|
+
* A conta é a da tela: decisões = vistas - estrutura; fechadas = interpretadas + respondidas.
|
|
339
|
+
*/
|
|
340
|
+
export function describeValueRuler(values) {
|
|
341
|
+
const decisions = values.seen - values.structure;
|
|
342
|
+
const closed = values.interpreted + values.answered;
|
|
343
|
+
const percent = decisions > 0 ? Math.round((closed / decisions) * 100) : 0;
|
|
344
|
+
return [
|
|
345
|
+
`Of the ${values.seen} class declarations on your components, ${values.structure} are structure (layout plumbing, not design decisions). Of the ${decisions} design decisions, ${closed} are interpreted${values.answered > 0 ? ` (${values.answered} of them answered by you)` : ""} - ${percent}%.`,
|
|
346
|
+
];
|
|
347
|
+
}
|
package/dist/their-tongue.js
CHANGED
|
@@ -45,3 +45,43 @@ export function tongueOf(map, installedCss) {
|
|
|
45
45
|
}
|
|
46
46
|
return { names, values };
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* NENHUMA MATERIALIZAÇÃO VAZA VOCABULÁRIO INTERNO - a regra da camada, num lugar só (dono, 26/08).
|
|
50
|
+
*
|
|
51
|
+
* Toda saída que escreve código no repositório dele passa por ESTA porta: carrega o vocabulário do
|
|
52
|
+
* projeto (`projectTongue`) e traduz (`inTheirTongue`). O `template` entregava páginas inteiras com
|
|
53
|
+
* `--ds-*` cru enquanto o `component` traduzia - a mesma promessa, duas implementações, uma delas
|
|
54
|
+
* ausente. Um mecanismo novo de materialização que chamar esta função já nasce coberto; um que não
|
|
55
|
+
* chamar é reprovado pelo gate (ver contracts/camada-6-volta.md, INV-VOLTA-02).
|
|
56
|
+
*
|
|
57
|
+
* `null` quando não há mapa: projeto de destino, repositório que nunca buildou, ou install anterior
|
|
58
|
+
* a 0.16.290. Nos três casos nada é traduzido e a folha instalada continua sendo o caminho - e quem
|
|
59
|
+
* chama DIZ qual dos dois aconteceu.
|
|
60
|
+
*/
|
|
61
|
+
export async function projectTongue(root, slug) {
|
|
62
|
+
const { readFile } = await import("node:fs/promises");
|
|
63
|
+
const { join } = await import("node:path");
|
|
64
|
+
const dir = join(root, "_synthesisui", "ds", slug);
|
|
65
|
+
const lock = await readFile(join(dir, ".lock"), "utf8").catch(() => null);
|
|
66
|
+
if (!lock)
|
|
67
|
+
return null;
|
|
68
|
+
let map = [];
|
|
69
|
+
let version = 0;
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(lock);
|
|
72
|
+
map = parsed.tokenMap ?? [];
|
|
73
|
+
version = parsed.version ?? 0;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (map.length === 0)
|
|
79
|
+
return null;
|
|
80
|
+
/**
|
|
81
|
+
* O VALOR COMPILADO MORA NA PASTA DA VERSÃO - a folha da raiz é um re-export de uma linha, escrito
|
|
82
|
+
* assim de propósito para que o `@import` dele nunca mude entre updates (ver `add.ts`). É de lá
|
|
83
|
+
* que sai o literal para as variáveis que não têm nome dele.
|
|
84
|
+
*/
|
|
85
|
+
const installed = await readFile(join(dir, `v${version}`, "tokens.css"), "utf8").catch(() => "");
|
|
86
|
+
return tongueOf(map, installed);
|
|
87
|
+
}
|
package/package.json
CHANGED