synthesisui 0.16.378 → 0.16.380
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/align.js +47 -2
- package/dist/commands/doctor.js +4 -1
- package/dist/doctor/apply-fix.js +145 -21
- package/package.json +1 -1
package/dist/commands/align.js
CHANGED
|
@@ -154,10 +154,55 @@ opts = {}) {
|
|
|
154
154
|
* medido em 11/08, 12 de 36 componentes têm árvore diferente entre o leitor de 07/08 e o de hoje.
|
|
155
155
|
*/
|
|
156
156
|
const measuredBy = await censusMeasuredBy(root);
|
|
157
|
-
|
|
157
|
+
/**
|
|
158
|
+
* A RÉGUA QUE DIZ "VOCÊ ESTÁ ATRÁS" MORA DENTRO DA VERSÃO QUE ESTÁ ATRÁS - e por isso o silêncio
|
|
159
|
+
* daqui nunca significou que estava tudo em dia.
|
|
160
|
+
*
|
|
161
|
+
* O QUE ACONTECIA, medido em 05/09 no repositório do dono: o `connect` prega a versão do dia em
|
|
162
|
+
* que rodou, e os hooks dele chamam `synthesisui@0.16.370` LITERAL. Abrindo o tarball publicado
|
|
163
|
+
* daquela versão, `READER_SINCE = "0.16.367"`. O censo dele foi medido pela 0.16.376, e o leitor
|
|
164
|
+
* de hoje mudou na 0.16.378. Ele ESTÁ atrás - e a comparação acima, feita com a régua congelada de
|
|
165
|
+
* 0.16.367, responde que não: `isOlderCli("0.16.376", "0.16.367")` é falso, e este check cala.
|
|
166
|
+
*
|
|
167
|
+
* É a armadilha inteira: quem prega uma versão passa a medir a própria defasagem com a régua do
|
|
168
|
+
* dia em que pregou. O aviso só existe na versão que ele não tem, e um check que cala lê como
|
|
169
|
+
* "está em dia" - "ausência de evidência não é evidência", agora com a nossa assinatura.
|
|
170
|
+
*
|
|
171
|
+
* O SINAL QUE FUNCIONA OFFLINE, e é o que fecha a porta em vez de vigiá-la: comparar as duas
|
|
172
|
+
* coisas que este processo JÁ conhece. Se o CLI que está rodando é mais velho que o CLI que
|
|
173
|
+
* MEDIU, então ele está julgando uma medição feita por uma versão que sabe mais do que ele - e
|
|
174
|
+
* nada que ele afirme sobre estar em dia pode ser verdade. Ele não precisa saber o que veio
|
|
175
|
+
* depois; ele só precisa saber que veio.
|
|
176
|
+
*
|
|
177
|
+
* É GERAL e não depende de rede: o `align` não faz uma única chamada HTTP, de propósito, e
|
|
178
|
+
* continua sem fazer. As duas versões estão em disco - uma é a que está executando, a outra está
|
|
179
|
+
* carimbada no censo.
|
|
180
|
+
*/
|
|
181
|
+
if (measuredBy && cli && isOlderCli(cli, measuredBy))
|
|
182
|
+
out.push({
|
|
183
|
+
/**
|
|
184
|
+
* A FRASE NÃO CITA UMA FRASE QUE A TELA NÃO IMPRIME.
|
|
185
|
+
*
|
|
186
|
+
* A primeira redação dizia que o *"you are up to date"* deste check não é uma resposta - e
|
|
187
|
+
* este comando nunca escreve essa linha. Ele CALA quando não encontra desalinho, e é o
|
|
188
|
+
* silêncio que era falso. Citar uma frase inexistente manda a pessoa procurar na tela algo
|
|
189
|
+
* que não está lá, que é o defeito de 08/08 outra vez em escala menor.
|
|
190
|
+
*/
|
|
191
|
+
says: `this check is running on CLI ${cli}, and the measurement in this repo was read by ${measuredBy} - a newer one. It cannot see what changed in the readers after ${cli}, so its silence about the measurement being current is not an answer. ${cli} is the version pinned the day \`connect\` last ran here.`,
|
|
192
|
+
run: "npx synthesisui@latest connect",
|
|
193
|
+
});
|
|
194
|
+
else if (behind(measuredBy, cli, READER_SINCE))
|
|
158
195
|
out.push({
|
|
159
196
|
says: `the measurement stored in this repo was read by an older reader, so what the platform knows about your components is what that reader could see. A re-measure is the only thing that reaches it - the fix lives on this machine, not on the server.`,
|
|
160
|
-
|
|
197
|
+
/**
|
|
198
|
+
* `connect` VEM PRIMEIRO, e a plataforma já dizia isso enquanto esta linha dizia outra coisa.
|
|
199
|
+
*
|
|
200
|
+
* `readerBehind` em `apps/web/src/lib/ds/reader-behind.ts` devolve os DOIS passos, com o
|
|
201
|
+
* motivo escrito: sem o `connect`, o `sync` pode medir com o leitor velho, reenviar o mesmo
|
|
202
|
+
* censo, e ensinar que sincronizar não adianta - "o pior resultado possível de todos". Esta
|
|
203
|
+
* linha mandava só `sync`, e é ela que roda na máquina dele a cada sessão.
|
|
204
|
+
*/
|
|
205
|
+
run: "npx synthesisui@latest connect && npx synthesisui sync",
|
|
161
206
|
});
|
|
162
207
|
/**
|
|
163
208
|
* O ESCOPO, que é o desalinho mais caro e o mais silencioso: sem ele o `sync` mede o repo inteiro
|
package/dist/commands/doctor.js
CHANGED
|
@@ -621,7 +621,10 @@ async function runFix(root, d, writing) {
|
|
|
621
621
|
await appendEvent(root, {
|
|
622
622
|
kind: "fix",
|
|
623
623
|
at: new Date().toISOString(),
|
|
624
|
-
|
|
624
|
+
/** ESCRITAS, e não achados - ver `Applied.occurrences`: um achado de folha minificada vale
|
|
625
|
+
* por sete trocas, e o ledger que contasse achados registraria um ato menor do que o que
|
|
626
|
+
* aconteceu no repositório dele. */
|
|
627
|
+
named: result.applied.reduce((n, a) => n + a.occurrences, 0),
|
|
625
628
|
});
|
|
626
629
|
console.log("");
|
|
627
630
|
console.log(body(paint.dim("Read the diff before committing - it is your code, and this replaced literals with the tokens your own system declares.")));
|
package/dist/doctor/apply-fix.js
CHANGED
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
* confiança:
|
|
19
19
|
*
|
|
20
20
|
* 1. tocar uma linha que mudou desde a medição - se o texto não bate, pula e conta
|
|
21
|
-
* 2.
|
|
22
|
-
*
|
|
21
|
+
* 2. sair da linha que o achado nomeia - a troca acontece naquela linha e em nenhuma outra.
|
|
22
|
+
* DENTRO dela, todas as ocorrências daquele valor são trocadas, e o recibo conta ESCRITAS,
|
|
23
|
+
* não achados: o scan emite um achado por (arquivo, linha, valor), e numa folha minificada
|
|
24
|
+
* esse um vale por sete
|
|
23
25
|
* 3. escrever em arquivo que o scanner marcou como fora (gerado, vendored) - esses nunca
|
|
24
26
|
* chegam aqui, porque o walk já os pula
|
|
25
27
|
*/
|
|
@@ -27,17 +29,117 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
27
29
|
import { join } from "node:path";
|
|
28
30
|
import { nameToWrite } from "./scan.js";
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
32
|
+
* ONDE O VALOR DE UMA DECLARAÇÃO DESTE MESMO TOKEN COMEÇA E TERMINA, nesta linha.
|
|
31
33
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
34
|
+
* Só os intervalos do token que a troca ESCREVERIA: é dentro deles, e só ali, que trocar produz
|
|
35
|
+
* `--x: var(--x)`. Todo o resto da linha - inclusive a declaração de OUTRO token dele - é uso
|
|
36
|
+
* legítimo, e continua trocável.
|
|
35
37
|
*/
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
function selfRanges(line, name) {
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const m of line.matchAll(/(--[a-z0-9-]+)\s*:\s*([^;}]*)/gi)) {
|
|
41
|
+
if (m[1] !== name)
|
|
42
|
+
continue;
|
|
43
|
+
const at = (m.index ?? 0) + m[0].length - m[2].length;
|
|
44
|
+
out.push([at, at + m[2].length]);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A TROCA NA LINHA, EM TODAS AS VEZES QUE O VALOR APARECE NELA - e era aqui que o número mentia.
|
|
50
|
+
*
|
|
51
|
+
* O QUE ACONTECIA: o scan emite UM achado por (arquivo, linha, valor), e esta função trocava a
|
|
52
|
+
* PRIMEIRA ocorrência. Numa folha minificada a linha inteira é o arquivo: em
|
|
53
|
+
* `apps/landing/app/styles/pulse.css` a linha 7 escreve `#6b6b78` sete vezes, o comando trocava uma,
|
|
54
|
+
* contava uma, e deixava seis - sem dizer. A medição seguinte reencontrava o mesmo achado, e o número
|
|
55
|
+
* que o comando prometeu passava a não bater com o número que a próxima leitura mostra.
|
|
56
|
+
*
|
|
57
|
+
* MEDIDO EM DUAS POPULAÇÕES (05/09, pelo caminho real do `doctor --fix --write`, antes e depois):
|
|
58
|
+
*
|
|
59
|
+
* codelevel 114 achados aplicados · escrevia 114 · agora escreve **126** · discrepância 5 -> 0
|
|
60
|
+
* frontend-hub 1010 achados aplicados · escrevia 1010 · agora escreve **1012** · já era 0
|
|
61
|
+
*
|
|
62
|
+
* E A DIFERENÇA QUE ISTO FECHA: no codelevel o comando anunciava 114 trocas e a medição seguinte
|
|
63
|
+
* mostrava só 109 achados a menos - **5 sem explicação**, que são estes: cinco linhas onde o valor
|
|
64
|
+
* aparecia mais de uma vez, o achado continuava lá depois do conserto, e o cliente que rodasse os
|
|
65
|
+
* dois comandos em sequência via os dois números. Agora a queda é 114 de 114.
|
|
66
|
+
*
|
|
67
|
+
* O TETO É MAIOR QUE O EFEITO DE HOJE, e vale dizer: no codelevel, 39 dos 208 achados NOMEADOS
|
|
68
|
+
* escondem 235 ocorrências, 234 delas num único arquivo minificado - a maior parte em achados que o
|
|
69
|
+
* `--fix` ainda não aplica porque apontam para tokens nossos, que só resolvem depois da fiação. No
|
|
70
|
+
* dia em que ele fizer a fiação, são essas que passam a ser escritas.
|
|
71
|
+
*
|
|
72
|
+
* Não é forma de um repositório: é toda folha que alguém minifica, e todo arquivo que escreve o mesmo
|
|
73
|
+
* valor duas vezes na mesma linha.
|
|
74
|
+
*
|
|
75
|
+
* O GUARD DA AUTO-REFERÊNCIA PASSA A VALER POR OCORRÊNCIA, e não pela linha inteira. Numa linha que
|
|
76
|
+
* DECLARA o token e também o USA, recusar as duas coisas deixava o uso sem conserto por causa da
|
|
77
|
+
* vizinha; agora a declaração fica intacta e o uso é trocado. Quando toda ocorrência da linha é a
|
|
78
|
+
* própria declaração, o resultado é o de sempre: nada trocado, e o motivo dito.
|
|
79
|
+
*
|
|
80
|
+
* E O VALOR TEM QUE TERMINAR ONDE ELE TERMINA - ver `wholeValue`.
|
|
81
|
+
*/
|
|
82
|
+
/**
|
|
83
|
+
* A OCORRÊNCIA É O VALOR INTEIRO, E NÃO O COMEÇO DE OUTRO - e sem isto a troca escreve cor inválida.
|
|
84
|
+
*
|
|
85
|
+
* `#4f46e5` é um prefixo de `#4f46e50b`, que é a MESMA cor com alpha. Trocando pelo prefixo, a linha
|
|
86
|
+
* fica `var(--color-brand-blue)0b`: o navegador descarta a declaração inteira e o elemento perde a
|
|
87
|
+
* cor - o mesmo estrago do ciclo, por outro caminho. Vale igual para comprimento: `1rem` é prefixo de
|
|
88
|
+
* `1remx` e sufixo de `11rem`.
|
|
89
|
+
*
|
|
90
|
+
* NÃO É HIPÓTESE: `apps/landing/app/styles/pulse.css` escreve 8 valores em `#rrggbbaa`, e o
|
|
91
|
+
* `--fix` só não os atingia porque trocava a PRIMEIRA ocorrência da linha e ela calhava de ser um hex
|
|
92
|
+
* puro. Sorte da população, não proteção - e trocando todas, a sorte acaba.
|
|
93
|
+
*/
|
|
94
|
+
function wholeValue(line, at, literal) {
|
|
95
|
+
const before = at > 0 ? line[at - 1] : "";
|
|
96
|
+
const after = line[at + literal.length] ?? "";
|
|
97
|
+
return !/[0-9a-z#]/i.test(before) && !/[0-9a-z]/i.test(after);
|
|
98
|
+
}
|
|
99
|
+
/** Onde este valor está escrito INTEIRO nesta linha - a mesma pergunta que a troca faz. */
|
|
100
|
+
function wholeValuesIn(line, literal) {
|
|
101
|
+
const out = [];
|
|
102
|
+
for (let at = line.indexOf(literal); at !== -1;) {
|
|
103
|
+
if (wholeValue(line, at, literal))
|
|
104
|
+
out.push(at);
|
|
105
|
+
at = line.indexOf(literal, at + literal.length);
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function swapAll(line, literal, name) {
|
|
110
|
+
const guarded = selfRanges(line, name);
|
|
111
|
+
let out = "";
|
|
112
|
+
let from = 0;
|
|
113
|
+
let count = 0;
|
|
114
|
+
let blocked = 0;
|
|
115
|
+
for (;;) {
|
|
116
|
+
const at = line.indexOf(literal, from);
|
|
117
|
+
if (at === -1)
|
|
118
|
+
break;
|
|
119
|
+
const declares = guarded.some(([start, end]) => at >= start && at + literal.length <= end);
|
|
120
|
+
/**
|
|
121
|
+
* A BORDA NÃO CONTA COMO AUTO-REFERÊNCIA - senão o relatório dá o motivo de outro caso.
|
|
122
|
+
*
|
|
123
|
+
* `blocked` é só a declaração do próprio token, porque é ele que decide a frase impressa quando
|
|
124
|
+
* nada foi trocado. Um prefixo de valor maior não é uma recusa: é um valor que não está escrito
|
|
125
|
+
* naquela linha, e cai no motivo que já existe para isso.
|
|
126
|
+
*/
|
|
127
|
+
if (!wholeValue(line, at, literal)) {
|
|
128
|
+
out += line.slice(from, at + literal.length);
|
|
129
|
+
from = at + literal.length;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (declares) {
|
|
133
|
+
blocked++;
|
|
134
|
+
out += line.slice(from, at + literal.length);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
count++;
|
|
138
|
+
out += `${line.slice(from, at)}var(${name})`;
|
|
139
|
+
}
|
|
140
|
+
from = at + literal.length;
|
|
141
|
+
}
|
|
142
|
+
return { line: out + line.slice(from), count, blocked };
|
|
41
143
|
}
|
|
42
144
|
/**
|
|
43
145
|
* Aplica o que tem token, em memória, e devolve o conteúdo novo por arquivo.
|
|
@@ -53,6 +155,15 @@ export function planFix(d, read) {
|
|
|
53
155
|
const lines = new Map();
|
|
54
156
|
/** A mesma chave, congelada antes da primeira troca - ver o skip de `not-written`. */
|
|
55
157
|
const originals = new Map();
|
|
158
|
+
/**
|
|
159
|
+
* O MESMO VALOR, NA MESMA LINHA, APONTADO DUAS VEZES É UM CONSERTO SÓ - e não uma linha que mudou.
|
|
160
|
+
*
|
|
161
|
+
* A troca cobre TODAS as ocorrências daquela linha de uma vez, então um segundo achado idêntico não
|
|
162
|
+
* encontra mais o literal e cairia no skip de `moved`, que imprime *"linhas mudaram desde o scan -
|
|
163
|
+
* rode de novo"* num repositório onde ninguém mexeu em nada. Nada é perdido de vista: as duas
|
|
164
|
+
* escritas já estão contadas no `occurrences` do primeiro.
|
|
165
|
+
*/
|
|
166
|
+
const done = new Set();
|
|
56
167
|
/**
|
|
57
168
|
* DE BAIXO PARA CIMA no arquivo não é necessário - a troca não muda a contagem de linhas - mas a
|
|
58
169
|
* ORDEM por arquivo é, para dois achados na mesma linha não se atropelarem: o segundo procura o seu
|
|
@@ -172,10 +283,15 @@ export function planFix(d, read) {
|
|
|
172
283
|
* seguem trocáveis: numa linha minificada, um `background:#fbfaf6` ao lado de um `--violet:...`
|
|
173
284
|
* continua sendo um uso legítimo.
|
|
174
285
|
*/
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
286
|
+
const already = `${f.file}:${f.line}:${f.literal}`;
|
|
287
|
+
if (done.has(already))
|
|
288
|
+
continue;
|
|
289
|
+
const swapped = current === undefined ? null : swapAll(current, f.literal, name);
|
|
290
|
+
/**
|
|
291
|
+
* TODA OCORRÊNCIA DA LINHA ERA A PRÓPRIA DECLARAÇÃO - o caso que o guard sempre recusou, dito
|
|
292
|
+
* com a mesma frase de antes. O que mudou é que uma linha que declara E usa deixou de cair aqui.
|
|
293
|
+
*/
|
|
294
|
+
if (swapped !== null && swapped.count === 0 && swapped.blocked > 0) {
|
|
179
295
|
skipped.push({
|
|
180
296
|
file: f.file,
|
|
181
297
|
line: f.line,
|
|
@@ -184,8 +300,7 @@ export function planFix(d, read) {
|
|
|
184
300
|
});
|
|
185
301
|
continue;
|
|
186
302
|
}
|
|
187
|
-
|
|
188
|
-
if (swapped === null) {
|
|
303
|
+
if (swapped === null || swapped.count === 0) {
|
|
189
304
|
/**
|
|
190
305
|
* DOIS MOTIVOS DIFERENTES, E A TELA DIZIA UM SÓ - e o que ela dizia era falso quase sempre.
|
|
191
306
|
*
|
|
@@ -215,19 +330,21 @@ export function planFix(d, read) {
|
|
|
215
330
|
file: f.file,
|
|
216
331
|
line: f.line,
|
|
217
332
|
literal: f.literal,
|
|
218
|
-
because: before === undefined || before
|
|
333
|
+
because: before === undefined || wholeValuesIn(before, f.literal).length > 0
|
|
219
334
|
? "moved"
|
|
220
335
|
: "not-written",
|
|
221
336
|
});
|
|
222
337
|
continue;
|
|
223
338
|
}
|
|
224
|
-
body[idx] = swapped;
|
|
339
|
+
body[idx] = swapped.line;
|
|
225
340
|
applied.push({
|
|
226
341
|
file: f.file,
|
|
227
342
|
line: f.line,
|
|
228
343
|
literal: f.literal,
|
|
229
344
|
token: name,
|
|
345
|
+
occurrences: swapped.count,
|
|
230
346
|
});
|
|
347
|
+
done.add(already);
|
|
231
348
|
}
|
|
232
349
|
for (const [file, body] of lines) {
|
|
233
350
|
const touched = applied.some((a) => a.file === file);
|
|
@@ -292,10 +409,17 @@ export function describeFix(result, dry) {
|
|
|
292
409
|
: "Nothing to apply.");
|
|
293
410
|
return lines;
|
|
294
411
|
}
|
|
295
|
-
|
|
412
|
+
/**
|
|
413
|
+
* O NÚMERO É O DE ESCRITAS, e não o de achados - ver `Applied.occurrences`.
|
|
414
|
+
*
|
|
415
|
+
* Um achado numa linha minificada vale por sete trocas. Contando achados, o recibo dizia 114 onde o
|
|
416
|
+
* comando escreveu 349, e a medição seguinte não batia com nenhum dos dois números.
|
|
417
|
+
*/
|
|
418
|
+
const uses = applied.reduce((n, a) => n + a.occurrences, 0);
|
|
419
|
+
lines.push(`${dry ? "Would replace" : "Replaced"} ${uses} hand-written value${uses === 1 ? "" : "s"} with the token your system already has, across ${result.files} file${result.files === 1 ? "" : "s"}.`);
|
|
296
420
|
const byToken = new Map();
|
|
297
421
|
for (const a of applied)
|
|
298
|
-
byToken.set(a.token, (byToken.get(a.token) ?? 0) +
|
|
422
|
+
byToken.set(a.token, (byToken.get(a.token) ?? 0) + a.occurrences);
|
|
299
423
|
const ranked = [...byToken.entries()].sort((a, b) => b[1] - a[1]);
|
|
300
424
|
const SHOWN = 6;
|
|
301
425
|
for (const [token, n] of ranked.slice(0, SHOWN))
|
package/package.json
CHANGED