synthesisui 0.16.374 → 0.16.376
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/doctor/apply-fix.js +120 -6
- package/package.json +1 -1
package/dist/doctor/apply-fix.js
CHANGED
|
@@ -51,6 +51,8 @@ export function planFix(d, read) {
|
|
|
51
51
|
const next = new Map();
|
|
52
52
|
/** Linhas por arquivo, mutadas em memória: dois achados no mesmo arquivo compõem. */
|
|
53
53
|
const lines = new Map();
|
|
54
|
+
/** A mesma chave, congelada antes da primeira troca - ver o skip de `not-written`. */
|
|
55
|
+
const originals = new Map();
|
|
54
56
|
/**
|
|
55
57
|
* DE BAIXO PARA CIMA no arquivo não é necessário - a troca não muda a contagem de linhas - mas a
|
|
56
58
|
* ORDEM por arquivo é, para dois achados na mesma linha não se atropelarem: o segundo procura o seu
|
|
@@ -119,20 +121,84 @@ export function planFix(d, read) {
|
|
|
119
121
|
continue;
|
|
120
122
|
}
|
|
121
123
|
lines.set(f.file, raw.split("\n"));
|
|
124
|
+
/**
|
|
125
|
+
* A LINHA COMO ELA ESTAVA ANTES DE QUALQUER TROCA DESTA RODADA - e é ela que separa dois
|
|
126
|
+
* skips que hoje se dizem com a mesma frase. Ver o bloco de `swapped === null`.
|
|
127
|
+
*/
|
|
128
|
+
originals.set(f.file, raw.split("\n"));
|
|
122
129
|
}
|
|
123
130
|
const body = lines.get(f.file);
|
|
124
131
|
if (!body)
|
|
125
132
|
continue;
|
|
126
133
|
const idx = f.line - 1;
|
|
127
134
|
const current = body[idx];
|
|
135
|
+
/**
|
|
136
|
+
* A DECLARAÇÃO DE UM TOKEN NÃO É UM USO DELE - e trocar ali APAGA o token do app inteiro.
|
|
137
|
+
*
|
|
138
|
+
* O QUE ACONTECIA: `--color-brand-blue: #4f46e5;` é onde o token NASCE. O scan vê o literal, o
|
|
139
|
+
* sistema tem um nome para aquele valor - o próprio `--color-brand-blue` -, e a troca escrevia
|
|
140
|
+
* `--color-brand-blue: var(--color-brand-blue)`. Isso é um ciclo de custom property, e a regra do
|
|
141
|
+
* CSS é dura: a propriedade fica inválida no elemento E em todos os descendentes. Como esses
|
|
142
|
+
* tokens moram em `:root`, o app inteiro perde a cor.
|
|
143
|
+
*
|
|
144
|
+
* MEDIDO em 05/09, sobre uma cópia do repositório do dono: **26 declarações** viraram
|
|
145
|
+
* auto-referência numa passada. E cada uma delas é o destino de dezenas de trocas que o mesmo
|
|
146
|
+
* comando acabou de fazer - o conserto apagava aquilo que ele mesmo tinha acabado de apontar.
|
|
147
|
+
*
|
|
148
|
+
* É A MESMA FAMÍLIA DO CICLO DOS EASINGS que o dono encontrou em 04/09, e desta vez produzida
|
|
149
|
+
* pelo nosso comando de conserto. Sem erro no console: o navegador simplesmente descarta.
|
|
150
|
+
*
|
|
151
|
+
* A REGRA É ESTREITA DE PROPÓSITO. Apontar um token DELE para outro token DELE
|
|
152
|
+
* (`--brand-hover: #4f46e5` -> `var(--color-brand-blue)`) continua valendo e é bom. Só a
|
|
153
|
+
* auto-referência é fatal, e só ela é recusada.
|
|
154
|
+
*
|
|
155
|
+
* É GERAL: toda folha declara token com valor literal - 149 no repositório dele, 180 no
|
|
156
|
+
* frontend-hub.
|
|
157
|
+
*/
|
|
158
|
+
const declaring = /^\s*(--[a-z0-9-]+)\s*:/i.exec(current ?? "");
|
|
159
|
+
if (declaring && declaring[1] === name) {
|
|
160
|
+
skipped.push({
|
|
161
|
+
file: f.file,
|
|
162
|
+
line: f.line,
|
|
163
|
+
literal: f.literal,
|
|
164
|
+
because: "self-reference",
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
128
168
|
const swapped = current === undefined ? null : swap(current, f.literal, name);
|
|
129
169
|
if (swapped === null) {
|
|
130
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* DOIS MOTIVOS DIFERENTES, E A TELA DIZIA UM SÓ - e o que ela dizia era falso quase sempre.
|
|
172
|
+
*
|
|
173
|
+
* O QUE O DONO VIU em 05/09: *"13 lines changed since the scan and were left alone - run it
|
|
174
|
+
* again"*, num repositório onde ele não tinha editado nada. Medido: **13 de 13** eram classe
|
|
175
|
+
* utilitária. A linha era `"transition-all duration-300 ease-[var(--ease-out-soft)]"`, o scan
|
|
176
|
+
* derivou `300ms` de `duration-300` - corretamente -, e o `swap` procura o texto `300ms`, que
|
|
177
|
+
* nunca esteve escrito ali. `grep 300ms` no arquivo devolve zero.
|
|
178
|
+
*
|
|
179
|
+
* O comentário que estava aqui já sabia disso: *"o arquivo mudou desde a medição, OU o literal
|
|
180
|
+
* já foi trocado por outro achado"*. A frase impressa contava só o primeiro caso, e mandava
|
|
181
|
+
* rodar de novo - o que não muda nada, porque nada mudou.
|
|
182
|
+
*
|
|
183
|
+
* A LINHA ORIGINAL DESEMPATA. Sem o literal nela, o valor veio de forma abreviada e trocá-lo é
|
|
184
|
+
* outra reescrita (`duration-[var(--duration-base)]`), não uma troca de literal. Com o literal
|
|
185
|
+
* nela, alguma coisa mexeu na linha depois - e aí rodar de novo é o conselho certo.
|
|
186
|
+
*
|
|
187
|
+
* É GERAL: `duration-*` é Tailwind puro - 15 arquivos no repositório dele, 37 no frontend-hub.
|
|
188
|
+
*/
|
|
189
|
+
/**
|
|
190
|
+
* A LINHA SUMIU É OUTRA COISA - e é `moved` de verdade: um arquivo que encolheu desde a
|
|
191
|
+
* medição mudou, e rodar de novo é o conselho certo. `not-written` só quando a linha ESTÁ lá
|
|
192
|
+
* e o valor não está escrito nela.
|
|
193
|
+
*/
|
|
194
|
+
const before = originals.get(f.file)?.[idx];
|
|
131
195
|
skipped.push({
|
|
132
196
|
file: f.file,
|
|
133
197
|
line: f.line,
|
|
134
198
|
literal: f.literal,
|
|
135
|
-
because:
|
|
199
|
+
because: before === undefined || before.includes(f.literal)
|
|
200
|
+
? "moved"
|
|
201
|
+
: "not-written",
|
|
136
202
|
});
|
|
137
203
|
continue;
|
|
138
204
|
}
|
|
@@ -176,6 +242,10 @@ export async function readerFor(root, files) {
|
|
|
176
242
|
export function describeFix(result, dry) {
|
|
177
243
|
const { applied, skipped } = result;
|
|
178
244
|
const moved = skipped.filter((s) => s.because === "moved").length;
|
|
245
|
+
/** O valor veio de forma abreviada - ver `not-written` em `planFix`. */
|
|
246
|
+
const notWritten = skipped.filter((s) => s.because === "not-written").length;
|
|
247
|
+
/** A linha declara o próprio token - ver `self-reference` em `planFix`. */
|
|
248
|
+
const selfRef = skipped.filter((s) => s.because === "self-reference").length;
|
|
179
249
|
const coincidence = skipped.filter((s) => s.because === "cross-family").length;
|
|
180
250
|
const unread = skipped.filter((s) => s.because === "unreadable").length;
|
|
181
251
|
const decisions = skipped.filter((s) => s.because === "no-token").length;
|
|
@@ -187,23 +257,67 @@ export function describeFix(result, dry) {
|
|
|
187
257
|
? `Nothing to apply. All ${decisions} findings are values your system has no name for - those are design decisions, not fixes.`
|
|
188
258
|
: relative > 0
|
|
189
259
|
? `Nothing to apply. All ${relative} findings are lengths in \`em\`, which follow the element's font size - swapping them can move the layout, so that call is yours.`
|
|
190
|
-
:
|
|
260
|
+
: /**
|
|
261
|
+
* E QUANDO NADA É APLICÁVEL, O MOTIVO SAI AQUI TAMBÉM - senão a saída inteira é
|
|
262
|
+
* "Nothing to apply." e a pessoa não tem como saber que o valor existe, tem nome, e só
|
|
263
|
+
* não está escrito como texto.
|
|
264
|
+
*/
|
|
265
|
+
notWritten > 0
|
|
266
|
+
? `Nothing to apply. All ${notWritten} findings are written as a shorthand - a utility class like \`duration-300\` carries the value in its name, so there is no literal on the line to replace. Changing them is a different rewrite.`
|
|
267
|
+
: /**
|
|
268
|
+
* E quando tudo o que havia eram as linhas onde os tokens nascem, "Nothing to apply."
|
|
269
|
+
* sozinho manda a pessoa procurar um defeito. O comando passou por ali de propósito.
|
|
270
|
+
*/
|
|
271
|
+
selfRef > 0
|
|
272
|
+
? `Nothing to apply. ${selfRef === 1 ? "The one finding is" : `All ${selfRef} findings are`} on the line where the token itself is declared - replacing there would point it at itself, and the browser would drop it.`
|
|
273
|
+
: "Nothing to apply.");
|
|
191
274
|
return lines;
|
|
192
275
|
}
|
|
193
276
|
lines.push(`${dry ? "Would replace" : "Replaced"} ${applied.length} hand-written value${applied.length === 1 ? "" : "s"} with the token your system already has, across ${result.files} file${result.files === 1 ? "" : "s"}.`);
|
|
194
277
|
const byToken = new Map();
|
|
195
278
|
for (const a of applied)
|
|
196
279
|
byToken.set(a.token, (byToken.get(a.token) ?? 0) + 1);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
280
|
+
const ranked = [...byToken.entries()].sort((a, b) => b[1] - a[1]);
|
|
281
|
+
const SHOWN = 6;
|
|
282
|
+
for (const [token, n] of ranked.slice(0, SHOWN))
|
|
200
283
|
lines.push(` var(${token}) · ${n} time${n === 1 ? "" : "s"}`);
|
|
284
|
+
/**
|
|
285
|
+
* O QUE O CORTE ESCONDEU, DITO - e é a mesma lei que a linha abaixo já obedece.
|
|
286
|
+
*
|
|
287
|
+
* O QUE O DONO VIU em 05/09: `Would replace 143 ... across 20 files` e seis linhas somando 81. As
|
|
288
|
+
* outras 62 ocorrências não apareciam em lugar nenhum, e ele aprovaria a escrita sem saber que
|
|
289
|
+
* existiam.
|
|
290
|
+
*
|
|
291
|
+
* O comentário três linhas abaixo já enuncia a regra - *"DITO SEMPRE QUE ACONTECE, senão o número
|
|
292
|
+
* some da conta sem explicação"* -, aplicada ao `relative` e ao `moved` e não ao próprio corte
|
|
293
|
+
* desta lista. Terceira vez nesta família num dia: o `gaps` (#1327) e a tela de admin (#1304).
|
|
294
|
+
*/
|
|
295
|
+
const rest = ranked.slice(SHOWN);
|
|
296
|
+
if (rest.length > 0) {
|
|
297
|
+
const restUses = rest.reduce((n, [, uses]) => n + uses, 0);
|
|
298
|
+
lines.push(` and ${restUses} more across ${rest.length} other token${rest.length === 1 ? "" : "s"}, not listed here`);
|
|
299
|
+
}
|
|
201
300
|
/**
|
|
202
301
|
* DITO SEMPRE QUE ACONTECE - senão o número de "trocado" some da conta sem explicação, e a pessoa
|
|
203
302
|
* conclui que o comando falhou onde ele se recusou de propósito.
|
|
204
303
|
*/
|
|
205
304
|
if (relative > 0)
|
|
206
305
|
lines.push(` ${relative} more ${relative === 1 ? "is a length" : "are lengths"} in \`em\`, which follow the element's font size - your system names the same number, but the pixel may differ. Left for you to decide.`);
|
|
306
|
+
/**
|
|
307
|
+
* O VALOR QUE NÃO ESTÁ ESCRITO - a frase que substituiu uma afirmação falsa sobre o repositório.
|
|
308
|
+
*
|
|
309
|
+
* Isto saía como *"N lines changed since the scan - run it again"*, e na tela do dono nada tinha
|
|
310
|
+
* mudado: 13 de 13 eram `duration-300` e afins. Mandar rodar de novo não muda nada, e mandar
|
|
311
|
+
* alguém procurar uma edição que ela não fez é pior que calar.
|
|
312
|
+
*/
|
|
313
|
+
/**
|
|
314
|
+
* DITO, e não calado: é a linha onde o token NASCE, e a pessoa precisa saber que o comando passou
|
|
315
|
+
* por ali de propósito - senão ela procura por que aquele valor não foi trocado.
|
|
316
|
+
*/
|
|
317
|
+
if (selfRef > 0)
|
|
318
|
+
lines.push(` ${selfRef} ${selfRef === 1 ? "is" : "are"} the line where the token itself is declared - replacing there would point it at itself and the browser would drop it. Left alone.`);
|
|
319
|
+
if (notWritten > 0)
|
|
320
|
+
lines.push(` ${notWritten} more ${notWritten === 1 ? "is" : "are"} written as a shorthand - a utility class like \`duration-300\` carries the value in its name, so there is no literal on the line to replace. Changing ${notWritten === 1 ? "it" : "those"} is a different rewrite. Left alone.`);
|
|
207
321
|
if (moved > 0)
|
|
208
322
|
lines.push(`${moved} line${moved === 1 ? "" : "s"} changed since the scan and ${moved === 1 ? "was" : "were"} left alone - run it again.`);
|
|
209
323
|
if (coincidence > 0)
|
package/package.json
CHANGED