synthesisui 0.16.379 → 0.16.381
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/doctor.js +4 -1
- package/dist/doctor/apply-fix.js +145 -21
- package/dist/doctor/fragments.js +84 -5
- package/dist/install-marks.js +1 -1
- package/package.json +1 -1
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/dist/doctor/fragments.js
CHANGED
|
@@ -22,8 +22,28 @@ const CLASS_LITERAL = /className=(?:"([^"]{0,400})"|\{\s*"([^"]{0,400})"\s*\})/g
|
|
|
22
22
|
const CLASS_TEMPLATE = /className=\{\s*`([^`]{0,400})`\s*\}/g;
|
|
23
23
|
/** `className={algo}` sem literal nenhum: não existe na fonte. */
|
|
24
24
|
const CLASS_RUNTIME = /className=\{\s*([A-Za-z_$][\w$.[\]]{0,60})\s*\}/g;
|
|
25
|
-
/**
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* A ABERTURA de `cn("…", …)` e amigos - o FIM sai por parêntese balanceado, e não por regex.
|
|
27
|
+
*
|
|
28
|
+
* O QUE ACONTECIA: o corpo era `([^)]{0,600})`, que para no PRIMEIRO `)`. Basta uma chamada aninhada
|
|
29
|
+
* - `cn("flex", buttonVariants({ size }), "p-4")` - para tudo o que vem depois dela desaparecer da
|
|
30
|
+
* conta. Não é o teto de 600: é o parêntese.
|
|
31
|
+
*
|
|
32
|
+
* MEDIDO EM TRÊS POPULAÇÕES (05/09, classes que o coletor não via por causa do corte):
|
|
33
|
+
*
|
|
34
|
+
* codelevel/packages/ui 21 de 33 chamadas truncadas de 83
|
|
35
|
+
* frontend-hub/packages/ui 41 de 17 chamadas truncadas de 78
|
|
36
|
+
* frontend-hub/apps/web-dashboard 1 de 1 chamada truncada de 47
|
|
37
|
+
*
|
|
38
|
+
* E o que sumia não era resto: `shadow-[0_10px_30px_-10px_rgba(…)]`, `data-[starting-style]:…`,
|
|
39
|
+
* `bg-[var(--color-lightgray-300)]` - as classes mais ricas do repositório, que são exatamente as
|
|
40
|
+
* que o cliente quer ver contadas. Num ledger cuja promessa é que nada é descartado em silêncio,
|
|
41
|
+
* elas não eram lidas E não eram "não lidas": não existiam.
|
|
42
|
+
*
|
|
43
|
+
* É O MESMO PRECEDENTE do `sx` e do `styled` três blocos abaixo, e pela mesma razão: um regex com
|
|
44
|
+
* teto escolhe a formatação do arquivo de outra pessoa.
|
|
45
|
+
*/
|
|
46
|
+
const CALL_OPEN = /\b(?:cn|clsx|classNames|cx|twMerge|twJoin|classcat)\(/g;
|
|
27
47
|
const STRING_IN_CALL = /"([^"]{0,300})"|'([^']{0,300})'/g;
|
|
28
48
|
/** `style={{ … }}`. */
|
|
29
49
|
const INLINE = /style=\{\{([^}]{0,400})\}\}/g;
|
|
@@ -72,6 +92,7 @@ const MODULE_REF = /\$\{\s*[A-Za-z_$][\w$]*\.[A-Za-z_$][\w$]*\s*\}/g;
|
|
|
72
92
|
*
|
|
73
93
|
* status === "success" ? "bg-success-500" : ... "success" é OPERANDO da comparação
|
|
74
94
|
* // ... to prevent the "short width" issue "short width" está num COMENTÁRIO
|
|
95
|
+
* cn(iconVariants({ animation: … : "none" }), …) "none" é ARGUMENTO de outra chamada
|
|
75
96
|
*
|
|
76
97
|
* MEDIDO EM 11/08 no repo real, sobre as 26 declarações que o censo listava como `value-not-read`:
|
|
77
98
|
*
|
|
@@ -93,6 +114,38 @@ function blankComments(body) {
|
|
|
93
114
|
function isComparedTo(body, index) {
|
|
94
115
|
return /(?:===|!==|==|!=)\s*$/.test(body.slice(0, index));
|
|
95
116
|
}
|
|
117
|
+
/** Um utilitário de classe abrindo parêntese, para saber QUEM abriu a chamada mais interna. */
|
|
118
|
+
const OPENS_A_CLASS_CALL = /\b(?:cn|clsx|classNames|cx|twMerge|twJoin|classcat)\($/;
|
|
119
|
+
/**
|
|
120
|
+
* O LITERAL É ARGUMENTO DE OUTRA CHAMADA, e argumento de outra chamada não é classe.
|
|
121
|
+
*
|
|
122
|
+
* `cn(iconVariants({ animation: shouldAnimate ? animation : "none" }), className)` põe `"none"`
|
|
123
|
+
* dentro do `cn`, mas ele não veste nada: é o valor que a função de variantes recebe para escolher a
|
|
124
|
+
* classe. A esteira listava `none` como uma classe que não conseguiu ler, e o cliente ia procurar no
|
|
125
|
+
* código dele um estilo que ele nunca escreveu - o mesmo custo do operando de `===` logo acima.
|
|
126
|
+
*
|
|
127
|
+
* MEDIDO EM TRÊS POPULAÇÕES (05/09, sobre os literais que já passam pelos dois guards anteriores):
|
|
128
|
+
*
|
|
129
|
+
* codelevel/packages/ui 3 de 83 `"none"` em Card, Icon, Typography
|
|
130
|
+
* frontend-hub/packages/ui 0 de 123
|
|
131
|
+
* frontend-hub/apps/web-dashboard 0 de 68
|
|
132
|
+
*
|
|
133
|
+
* QUEM ABRIU A CHAMADA DECIDE, e não o simples fato de haver uma. Um utilitário de classe aninhado
|
|
134
|
+
* (`cn(clsx("flex"))`) continua vestindo, e o que está dentro dele É classe - sem esta metade, o
|
|
135
|
+
* conserto de um falso positivo criaria um falso negativo. Nas três populações essa forma dá N=0
|
|
136
|
+
* hoje; ela está aqui porque aninhar utilitário é forma de qualquer projeto, e o `CALL_LITERAL` só
|
|
137
|
+
* casa a chamada de fora.
|
|
138
|
+
*/
|
|
139
|
+
function isArgumentOfAnotherCall(body, index) {
|
|
140
|
+
const open = [];
|
|
141
|
+
for (let i = 0; i < index; i++) {
|
|
142
|
+
if (body[i] === "(")
|
|
143
|
+
open.push(!OPENS_A_CLASS_CALL.test(body.slice(0, i + 1)));
|
|
144
|
+
else if (body[i] === ")")
|
|
145
|
+
open.pop();
|
|
146
|
+
}
|
|
147
|
+
return open[open.length - 1] === true;
|
|
148
|
+
}
|
|
96
149
|
export function fragmentsOfSource(file, source) {
|
|
97
150
|
const out = [];
|
|
98
151
|
const at = (index) => source.slice(0, index).split("\n").length;
|
|
@@ -127,15 +180,28 @@ export function fragmentsOfSource(file, source) {
|
|
|
127
180
|
* OS LITERAIS DENTRO DE UMA CHAMADA. `cn("flex p-4", cond && "gap-2")` tem dois, e os dois são
|
|
128
181
|
* classe escrita à mão - a forma mais comum do repo dele (113 arquivos).
|
|
129
182
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
183
|
+
/**
|
|
184
|
+
* SÓ A CHAMADA MAIS EXTERNA - senão um utilitário aninhado conta os literais dele duas vezes.
|
|
185
|
+
*
|
|
186
|
+
* Enquanto o corpo parava no primeiro `)`, um `clsx(` dentro de um `cn(` era uma segunda match e
|
|
187
|
+
* ninguém percebia. Com o corpo inteiro, o de fora já cobre o de dentro.
|
|
188
|
+
*/
|
|
189
|
+
let until = 0;
|
|
190
|
+
for (const call of source.matchAll(CALL_OPEN)) {
|
|
191
|
+
const from = (call.index ?? 0) + call[0].length;
|
|
192
|
+
if (from < until)
|
|
193
|
+
continue;
|
|
194
|
+
until = closingParen(source, from);
|
|
195
|
+
const body = blankComments(source.slice(from, until));
|
|
196
|
+
const base = from;
|
|
133
197
|
for (const s of body.matchAll(STRING_IN_CALL)) {
|
|
134
198
|
const text = s[1] ?? s[2] ?? "";
|
|
135
199
|
if (!LOOKS_LIKE_CLASS.test(text))
|
|
136
200
|
continue;
|
|
137
201
|
if (isComparedTo(body, s.index ?? 0))
|
|
138
202
|
continue;
|
|
203
|
+
if (isArgumentOfAnotherCall(body, s.index ?? 0))
|
|
204
|
+
continue;
|
|
139
205
|
push("class", base + (s.index ?? 0), text);
|
|
140
206
|
}
|
|
141
207
|
}
|
|
@@ -151,6 +217,19 @@ export function fragmentsOfSource(file, source) {
|
|
|
151
217
|
}
|
|
152
218
|
return out;
|
|
153
219
|
}
|
|
220
|
+
/** O fim da chamada aberta em `from`, contando parênteses. Sem fechamento, o fim do arquivo. */
|
|
221
|
+
function closingParen(source, from) {
|
|
222
|
+
let depth = 1;
|
|
223
|
+
let i = from;
|
|
224
|
+
while (i < source.length && depth > 0) {
|
|
225
|
+
if (source[i] === "(")
|
|
226
|
+
depth += 1;
|
|
227
|
+
else if (source[i] === ")")
|
|
228
|
+
depth -= 1;
|
|
229
|
+
i += 1;
|
|
230
|
+
}
|
|
231
|
+
return depth === 0 ? i - 1 : source.length;
|
|
232
|
+
}
|
|
154
233
|
/** O fim de um objeto aberto com duas chaves - `sx={{ … }}` fecha em duas. */
|
|
155
234
|
function balanced(source, from, close) {
|
|
156
235
|
let depth = 2;
|
package/dist/install-marks.js
CHANGED
|
@@ -680,7 +680,7 @@ export const CHECKER_SINCE = "0.16.378";
|
|
|
680
680
|
* posição de conteúdo. Um censo medido antes desta versão conta como deriva o que a folha do app
|
|
681
681
|
* nem alcança - e o número do cliente é a promessa.
|
|
682
682
|
*/
|
|
683
|
-
export const READER_SINCE = "0.16.
|
|
683
|
+
export const READER_SINCE = "0.16.381";
|
|
684
684
|
/**
|
|
685
685
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
686
686
|
*
|
package/package.json
CHANGED