synthesisui 0.16.349 → 0.16.354
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/census-pages.js +66 -0
- package/dist/commands/import.js +61 -29
- package/dist/doctor/fragments.js +35 -0
- package/dist/doctor/transcribe.js +132 -2
- package/dist/doctor/variant-read.js +28 -2
- package/dist/install-marks.js +85 -1
- package/dist/merge-census.js +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMO ELE MONTA UMA PÁGINA - a régua única, num módulo só.
|
|
3
|
+
*
|
|
4
|
+
* O que o cliente ganha: o planejador de página oferece o esqueleto que ELE já usa - as peças
|
|
5
|
+
* dele, na ordem em que ele as escreve - em vez dos três layouts que NÓS escrevemos. Ver
|
|
6
|
+
* `INV-COLETA-12` em `contracts/camada-1-coleta.md`.
|
|
7
|
+
*
|
|
8
|
+
* POR QUE ISTO NÃO MORA EM `import.ts`: três lugares precisam da mesma resposta - a varredura do
|
|
9
|
+
* escopo, a varredura da evidência (`--usage`, que é onde as páginas moram em todo monorepo) e a
|
|
10
|
+
* FUSÃO de dois escopos medidos. Duas cópias da transcrição seriam duas réguas no dia em que uma
|
|
11
|
+
* fosse editada, e o teto declarado num arquivo e furado no outro seria um teto que não existe.
|
|
12
|
+
*/
|
|
13
|
+
import { sketchOf } from "./doctor/sketch.js";
|
|
14
|
+
import { transcribe } from "./doctor/transcribe.js";
|
|
15
|
+
/**
|
|
16
|
+
* QUANTAS PÁGINAS VIAJAM NO CENSO.
|
|
17
|
+
*
|
|
18
|
+
* O teto existe pela mesma razão que o de `skipped`: num monorepo o número estoura e o censo
|
|
19
|
+
* viraria um dump do repositório. Medido em 01/09, o `frontend-hub` tem 145 arquivos de página e
|
|
20
|
+
* o nosso app 65, então 400 cabe as duas populações inteiras e o corte só aparece em repositório
|
|
21
|
+
* bem maior que os que a gente mede.
|
|
22
|
+
*
|
|
23
|
+
* E QUANDO ELE CORTAR, `pagesTotal` diz quantas eram. Uma tela que contasse `pages.length` diria
|
|
24
|
+
* 400 e pareceria completa, que é a forma mais barata de um corte silencioso mentir.
|
|
25
|
+
*/
|
|
26
|
+
export const PAGES_MAX = 400;
|
|
27
|
+
/**
|
|
28
|
+
* UMA PÁGINA DELE, TRANSCRITA - a moldura da raiz e as peças que ela compõe.
|
|
29
|
+
*
|
|
30
|
+
* `from` VEM DO SKETCH e não de uma segunda leitura do arquivo: o nó já carrega o especificador
|
|
31
|
+
* que o import dele declara, e reabrir o arquivo para descobrir o mesmo fato é a falha de
|
|
32
|
+
* esteira que o dono nomeou em 01/08.
|
|
33
|
+
*/
|
|
34
|
+
export function pageCompositionOf(rel, src, name, declaredValues) {
|
|
35
|
+
const sketch = sketchOf(src, name);
|
|
36
|
+
const at = sketch[0];
|
|
37
|
+
return {
|
|
38
|
+
file: rel,
|
|
39
|
+
root: at?.classes
|
|
40
|
+
? transcribe(at.classes.split(/\s+/).filter(Boolean), declaredValues).base
|
|
41
|
+
: {},
|
|
42
|
+
composes: sketch
|
|
43
|
+
.filter((n) => /^[A-Z]/.test(n.tag))
|
|
44
|
+
.map((n) => ({
|
|
45
|
+
name: n.tag.split(".")[0],
|
|
46
|
+
...(n.from ? { from: n.from } : {}),
|
|
47
|
+
depth: n.depth,
|
|
48
|
+
})),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* AS PÁGINAS DE DOIS ESCOPOS MEDIDOS, SOMADAS - e o teto reaplicado sobre a soma.
|
|
53
|
+
*
|
|
54
|
+
* Cada escopo mediu arquivos DIFERENTES, então isto é soma e não desempate: nenhuma regra do
|
|
55
|
+
* primeiro-escopo-vence se aplica a um conjunto disjunto. O que se preserva é o denominador -
|
|
56
|
+
* `pagesTotal` conta quantas foram VISTAS em cada medição, inclusive as que aquela medição já
|
|
57
|
+
* havia cortado, senão a soma de dois cortes leria como o total do projeto.
|
|
58
|
+
*/
|
|
59
|
+
export function mergePages(list) {
|
|
60
|
+
const all = list.flatMap((c) => c.pages ?? []);
|
|
61
|
+
if (all.length === 0)
|
|
62
|
+
return {};
|
|
63
|
+
const seen = list.reduce((n, c) => n + (c.pagesTotal ?? (c.pages ?? []).length), 0);
|
|
64
|
+
const pages = all.slice(0, PAGES_MAX);
|
|
65
|
+
return { pages, ...(seen > pages.length ? { pagesTotal: seen } : {}) };
|
|
66
|
+
}
|
package/dist/commands/import.js
CHANGED
|
@@ -3,6 +3,7 @@ import { basename, dirname, join, relative, sep } from "node:path";
|
|
|
3
3
|
import { anatomyFromSketch } from "../anatomy-from-sketch.js";
|
|
4
4
|
import { applyAnatomyPatch, hasEdits, } from "../anatomy-patch.js";
|
|
5
5
|
import { resolveAnatomy, resolveFlatParts, safePartName, } from "../anatomy-read.js";
|
|
6
|
+
import { PAGES_MAX, pageCompositionOf } from "../census-pages.js";
|
|
6
7
|
import { readCredentials, readToken, resolveRegistry, sameRegistry, } from "../config.js";
|
|
7
8
|
import { declaredElsewhere } from "../declared-elsewhere.js";
|
|
8
9
|
import { architectureGap, architectureRule, componentHome, describeArchitecture, describeChoice, describeGap, detectArchitectures, homeLine, packagingOf, proposeNewHome, resolvesAs, } from "../doctor/architecture.js";
|
|
@@ -68,17 +69,12 @@ import { walk, walkAll } from "./doctor.js";
|
|
|
68
69
|
* nunca o nome do arquivo - e é isso que a faz valer no projeto de alguém que a gente nunca viu.
|
|
69
70
|
*/
|
|
70
71
|
/**
|
|
71
|
-
*
|
|
72
|
+
* O TETO DE PÁGINAS, re-exportado de onde a régua inteira mora - ver `census-pages.ts`.
|
|
72
73
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* o nosso app 65, então 400 cabe as duas populações inteiras e o corte só aparece em repositório
|
|
76
|
-
* bem maior que os que a gente mede.
|
|
77
|
-
*
|
|
78
|
-
* E QUANDO ELE CORTAR, `pagesTotal` diz quantas eram. Uma tela que contasse `pages.length` diria
|
|
79
|
-
* 400 e pareceria completa, que é a forma mais barata de um corte silencioso mentir.
|
|
74
|
+
* Ele vive lá porque a FUSÃO de dois escopos precisa reaplicá-lo sobre a soma, e um teto
|
|
75
|
+
* declarado num arquivo e furado no outro é um teto que não existe.
|
|
80
76
|
*/
|
|
81
|
-
export
|
|
77
|
+
export { PAGES_MAX };
|
|
82
78
|
/**
|
|
83
79
|
* How many distinct values travel, PER KIND.
|
|
84
80
|
*
|
|
@@ -770,26 +766,7 @@ export async function takeCensus(root, opts) {
|
|
|
770
766
|
* que falta para alguém escrever a próxima (04/08).
|
|
771
767
|
*/
|
|
772
768
|
if (verdict.why === "route" || verdict.why === "screen") {
|
|
773
|
-
|
|
774
|
-
const root = sketch[0];
|
|
775
|
-
pages.push({
|
|
776
|
-
file: rel,
|
|
777
|
-
root: root?.classes
|
|
778
|
-
? transcribe(root.classes.split(/\s+/).filter(Boolean), declaredValues).base
|
|
779
|
-
: {},
|
|
780
|
-
/**
|
|
781
|
-
* `from` VEM DO SKETCH e não de uma segunda leitura do arquivo: o nó já carrega o
|
|
782
|
-
* especificador que o import dele declara, e reabrir o arquivo para descobrir o
|
|
783
|
-
* mesmo fato é a falha de esteira que o dono nomeou em 01/08.
|
|
784
|
-
*/
|
|
785
|
-
composes: sketch
|
|
786
|
-
.filter((n) => /^[A-Z]/.test(n.tag))
|
|
787
|
-
.map((n) => ({
|
|
788
|
-
name: n.tag.split(".")[0],
|
|
789
|
-
...(n.from ? { from: n.from } : {}),
|
|
790
|
-
depth: n.depth,
|
|
791
|
-
})),
|
|
792
|
-
});
|
|
769
|
+
pages.push(pageCompositionOf(rel, src, d.name, declaredValues));
|
|
793
770
|
}
|
|
794
771
|
skips.push({
|
|
795
772
|
name: d.name,
|
|
@@ -1456,6 +1433,11 @@ export async function takeCensus(root, opts) {
|
|
|
1456
1433
|
* not `reports`: an app's classes are choices made from a scale, and folding
|
|
1457
1434
|
* them into the census would put the average back where the declaration goes.
|
|
1458
1435
|
*
|
|
1436
|
+
* A ÚNICA COISA QUE ESTE LAÇO ESCREVE ALÉM DA CONTAGEM É `pages`, e a razão é que uma página
|
|
1437
|
+
* não é uma escolha de estilo: é a COMPOSIÇÃO dela - quais peças, em que ordem, sob que
|
|
1438
|
+
* moldura. Ela não entra em `defined`, não declara token e não vota em escala. Ver o bloco
|
|
1439
|
+
* `AS PÁGINAS MORAM AQUI` mais abaixo, com o número das duas populações.
|
|
1440
|
+
*
|
|
1459
1441
|
* Deliberately its own tally so the LIBRARY QUESTION is asked of the scope
|
|
1460
1442
|
* alone. Fold the app in first and every export has a count, `isLibrary` reads
|
|
1461
1443
|
* false, and the crosswalk starts substituting the components it was just
|
|
@@ -1548,6 +1530,41 @@ export async function takeCensus(root, opts) {
|
|
|
1548
1530
|
// source - so measuring nesting only on the defined loop reported zero
|
|
1549
1531
|
// companions on a 128-component library (e2e, 01/08).
|
|
1550
1532
|
readNesting(rel, src, nesting);
|
|
1533
|
+
/**
|
|
1534
|
+
* AS PÁGINAS MORAM AQUI, E ATÉ AGORA O CENSO NÃO GUARDAVA NENHUMA.
|
|
1535
|
+
*
|
|
1536
|
+
* `Census.pages` é como ELE monta uma tela - a moldura da raiz e a ordem das peças -, e é
|
|
1537
|
+
* o único dado a partir do qual alguém escreve a próxima página no vocabulário dele. Ele
|
|
1538
|
+
* só era colhido na varredura do ESCOPO, e o escopo é a biblioteca: uma `packages/ui` não
|
|
1539
|
+
* tem `app/` nem `pages/`, então a resposta era sempre zero. Medido em 01/09 nas duas
|
|
1540
|
+
* populações, com o mesmo `--scope`/`--usage` que o `import` e o `sync` passam:
|
|
1541
|
+
*
|
|
1542
|
+
* codelevel 0 páginas guardadas · 9 existem (apps/web 3, apps/landing 6)
|
|
1543
|
+
* frontend-hub 0 páginas guardadas · 156 existem (apps/web-dashboard)
|
|
1544
|
+
*
|
|
1545
|
+
* Isto NÃO afrouxa a lei da passagem de evidência. Uma página não entra em `defined`, não
|
|
1546
|
+
* doa token e não vota em escala: o que ela doa é COMPOSIÇÃO - quais peças dele seguram
|
|
1547
|
+
* quais telas -, que é exatamente a classe de fato que `--usage` existe para trazer.
|
|
1548
|
+
*
|
|
1549
|
+
* A régua é a mesma do escopo, de propósito: o portão recusa a rota (`route`/`screen`) e
|
|
1550
|
+
* é essa recusa que a identifica como página. Só o que o portão RECUSA por ser tela vira
|
|
1551
|
+
* página, então nada que já é componente é contado duas vezes.
|
|
1552
|
+
*/
|
|
1553
|
+
if (/\.(tsx|jsx)$/i.test(rel)) {
|
|
1554
|
+
for (const d of scanDefinitions(rel, src)) {
|
|
1555
|
+
const verdict = gateComponent({
|
|
1556
|
+
name: d.name,
|
|
1557
|
+
file: rel,
|
|
1558
|
+
source: src,
|
|
1559
|
+
internal: [...shared, ...uInternal],
|
|
1560
|
+
});
|
|
1561
|
+
if (verdict.ok)
|
|
1562
|
+
continue;
|
|
1563
|
+
if (verdict.why !== "route" && verdict.why !== "screen")
|
|
1564
|
+
continue;
|
|
1565
|
+
pages.push(pageCompositionOf(rel, src, d.name, declaredValues));
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1551
1568
|
// Which PROJECT composes it - the credibility panel's number, per root.
|
|
1552
1569
|
for (const m of src.matchAll(/<([A-Z][A-Za-z0-9_]*)/g)) {
|
|
1553
1570
|
const at = projectsOf.get(m[1]) ?? new Set();
|
|
@@ -2000,6 +2017,21 @@ export async function takeCensus(root, opts) {
|
|
|
2000
2017
|
]),
|
|
2001
2018
|
islandRead: islandClassesRead,
|
|
2002
2019
|
refused: new Set(skips.map((s) => s.file)),
|
|
2020
|
+
/**
|
|
2021
|
+
* AS ANIMAÇÕES QUE O CENSO CAPTUROU - ver o veredito em `judgeFragments`.
|
|
2022
|
+
*
|
|
2023
|
+
* `keyframes` é o objeto que já viaja no censo e que o compilador emite. Um frame de uma
|
|
2024
|
+
* delas chegou por essa porta, então cobrá-lo como lacuna conta a mesma coisa duas vezes.
|
|
2025
|
+
* A LISTA É A MESMA FUSÃO QUE O CENSO GRAVA, e não a metade de componente.
|
|
2026
|
+
*
|
|
2027
|
+
* `keyframes` sozinho é o que os componentes declaram; `globalKeyframes` é a folha dele, e
|
|
2028
|
+
* é ali que os 35 do codelevel moram - o censo funde os dois na hora de escrever. Ler só o
|
|
2029
|
+
* primeiro aqui declarava capturado um conjunto VAZIO e o conserto não movia nada: a
|
|
2030
|
+
* primeira medição depois de ligar isto deu exatamente os mesmos 130 de antes.
|
|
2031
|
+
*
|
|
2032
|
+
* Se a captura falhar, o frame volta a ser lacuna sozinho, que é a resposta certa.
|
|
2033
|
+
*/
|
|
2034
|
+
keyframes: new Set(Object.keys({ ...globalKeyframes, ...keyframes })),
|
|
2003
2035
|
},
|
|
2004
2036
|
/**
|
|
2005
2037
|
* AS FORMAS QUE ELE DECLAROU - e é aqui que a declaração dele muda o veredito.
|
package/dist/doctor/fragments.js
CHANGED
|
@@ -302,12 +302,24 @@ kind) {
|
|
|
302
302
|
continue;
|
|
303
303
|
}
|
|
304
304
|
const selector = selectorOf(open);
|
|
305
|
+
/**
|
|
306
|
+
* DE QUAL ANIMAÇÃO ESTE FRAME É - ver `Fragment.inKeyframe`.
|
|
307
|
+
*
|
|
308
|
+
* `selectorOf` devolve `0%`, porque o bloco do frame é um seletor comum e o at-rule só
|
|
309
|
+
* vence quando não há nenhum. O nome fica na pilha e é a única coisa que liga esta
|
|
310
|
+
* declaração ao keyframe que o censo capturou.
|
|
311
|
+
*/
|
|
312
|
+
const inKeyframe = open
|
|
313
|
+
.map((b) => /^@keyframes\s+([^\s{]+)/.exec(b)?.[1])
|
|
314
|
+
.filter(Boolean)
|
|
315
|
+
.pop();
|
|
305
316
|
out.push({
|
|
306
317
|
shape: "css",
|
|
307
318
|
file,
|
|
308
319
|
line: i + 1,
|
|
309
320
|
text: `${selector ? `${selector} ` : ""}{ ${m[1]}: ${m[2].trim()} }`,
|
|
310
321
|
read: false,
|
|
322
|
+
...(inKeyframe ? { inKeyframe } : {}),
|
|
311
323
|
...(kind === "global"
|
|
312
324
|
? { reason: "shape-not-read" }
|
|
313
325
|
: kind === "dead"
|
|
@@ -440,7 +452,30 @@ forms = []) {
|
|
|
440
452
|
const wornGlobal = elsewhere?.wornGlobal ?? new Set();
|
|
441
453
|
const islandRead = elsewhere?.islandRead ?? new Map();
|
|
442
454
|
const refused = elsewhere?.refused ?? new Set();
|
|
455
|
+
const captured = elsewhere?.keyframes ?? new Set();
|
|
443
456
|
return seen.map((f) => {
|
|
457
|
+
/**
|
|
458
|
+
* UM FRAME DE UM KEYFRAME CAPTURADO CHEGOU - a mesma família de `wornGlobal` logo abaixo, e a
|
|
459
|
+
* mesma família de `INV-INTERP-15`: a régua acusava um fato que já estava no censo.
|
|
460
|
+
*
|
|
461
|
+
* `0% { opacity: 0 }` não tem leitor de RECEITA e nunca vai ter - um frame não é a decisão de
|
|
462
|
+
* um componente, é um instante de uma animação. Mas a animação inteira viaja em
|
|
463
|
+
* `census.keyframes`, com todos os seus frames, e o compilador a emite. Cobrar o frame como
|
|
464
|
+
* lacuna é contar duas vezes a mesma coisa e chamar a segunda de perda.
|
|
465
|
+
*
|
|
466
|
+
* MEDIDO EM 02/09, nas duas populações e com o mesmo `--scope`/`--usage` do produto:
|
|
467
|
+
*
|
|
468
|
+
* codelevel 35 keyframes capturados · 87 de 105 declarações sem leitor eram frames (83%)
|
|
469
|
+
* frontend-hub 1 keyframe capturado · 0 de 6 eram frames (0%)
|
|
470
|
+
*
|
|
471
|
+
* As duas discordam por larga margem, e é isso que decide o desenho: a pergunta não é sobre
|
|
472
|
+
* volume, é sobre a porta por onde a declaração passou. O nome tem que estar CAPTURADO - um
|
|
473
|
+
* frame de uma animação que o censo não colheu continua sendo lacuna, porque ali ela é real.
|
|
474
|
+
*/
|
|
475
|
+
if (f.inKeyframe && captured.has(f.inKeyframe)) {
|
|
476
|
+
const { reason: _unread, ...rest } = f;
|
|
477
|
+
return { ...rest, read: true };
|
|
478
|
+
}
|
|
444
479
|
/**
|
|
445
480
|
* A REGRA DE CLASSE GLOBAL QUE ALGUÉM VESTE FOI LIDA - antes do desvio de
|
|
446
481
|
* `binding`, porque lida é mais forte que ligada. `.root { isolation: isolate }`
|
|
@@ -677,6 +677,24 @@ export function readUtility(raw, declared) {
|
|
|
677
677
|
if (property)
|
|
678
678
|
return { property, value: `${Number(pct.toFixed(4))}%` };
|
|
679
679
|
}
|
|
680
|
+
/**
|
|
681
|
+
* `-translate-x-1/2` TAMBÉM É UMA FRAÇÃO, e ela tem que ser lida AQUI pelo motivo exato que o
|
|
682
|
+
* comentário acima já dava: depois do split de alpha ela chega como `translate-x-1` e volta
|
|
683
|
+
* `0.25rem` - *silently wrong, which is worse than unread*.
|
|
684
|
+
*
|
|
685
|
+
* E isto não é hipótese: o primeiro rascunho do leitor de `translate` (02/09) tratava a fração
|
|
686
|
+
* dentro de `readUtilityCore`, depois do split, e produziu `translateX(-0.25rem)` para o
|
|
687
|
+
* `-translate-x-1/2` com que ele centra elementos. O spec pegou. É a mesma armadilha, uma função
|
|
688
|
+
* de transform depois.
|
|
689
|
+
*/
|
|
690
|
+
const shift = /^(-?)translate-([xy])-(\d+)\/(\d+)$/.exec(utility);
|
|
691
|
+
if (shift) {
|
|
692
|
+
const pct = (Number(shift[3]) / Number(shift[4])) * 100;
|
|
693
|
+
return {
|
|
694
|
+
property: "transform",
|
|
695
|
+
value: `translate${shift[2] === "x" ? "X" : "Y"}(${shift[1]}${Number(pct.toFixed(4))}%)`,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
680
698
|
const read = readUtilityCore(utility, declared);
|
|
681
699
|
return read ? withAlpha(read, utility.split("/")[1]) : read;
|
|
682
700
|
}
|
|
@@ -1139,6 +1157,63 @@ function readUtilityCore(utility, declared) {
|
|
|
1139
1157
|
* so two on the same element would overwrite - none of their 3 co-occur, and saying
|
|
1140
1158
|
* one of them is better than saying neither.
|
|
1141
1159
|
*/
|
|
1160
|
+
/**
|
|
1161
|
+
* O EIXO FAZ PARTE DA FUNÇÃO, e não do argumento - `rotate-x-45` é `rotateX(45deg)`.
|
|
1162
|
+
*
|
|
1163
|
+
* O QUE ISTO CONSERTA, e é pior que não-lido: o teste abaixo é `^-?rotate-(.+)$`, que capturava
|
|
1164
|
+
* `x-45` e montava **`rotate(x-45deg)`** - uma declaração que o navegador descarta inteira,
|
|
1165
|
+
* gravada na receita como se fosse a decisão dele. `skew-x-6` e `scale-x-95` caíam fora de
|
|
1166
|
+
* qualquer leitor e viravam lacuna, que é o lado seguro de errar; `rotate-x` era o inseguro.
|
|
1167
|
+
*
|
|
1168
|
+
* MEDIDO EM 02/09 COM A RÉGUA DO PRODUTO - as classes que o censo realmente carrega, e não um
|
|
1169
|
+
* grep: **0 usos nas duas populações** para as seis formas por eixo. E o grep ingênuo mentia
|
|
1170
|
+
* feio: ele dava 114 e 16 para `perspective-` e 60 e 78 para `origin-`, e NENHUM era classe de
|
|
1171
|
+
* componente - era o CSS `perspective:` e `transform-origin:` dentro das `@utility` dele.
|
|
1172
|
+
*
|
|
1173
|
+
* ENTÃO POR QUE LER, com N=0? Pelo mesmo motivo que `peer-<estado>/<nome>:` entrou no
|
|
1174
|
+
* `INV-INTERP-17`: isto não é forma de repositório nenhum, é a GRAMÁTICA que o Tailwind declara,
|
|
1175
|
+
* e nela o eixo é sufixo da função. Derivar da gramática cobre o cliente que a gente nunca viu -
|
|
1176
|
+
* e o que decide o caso é que a alternativa aqui não era silêncio, era CSS inválido.
|
|
1177
|
+
*
|
|
1178
|
+
* O QUE NÃO ENTROU, declarado: `origin-*`, `perspective-*`, `transform-gpu` e `translate-z-*`.
|
|
1179
|
+
* Os três primeiros são propriedades PRÓPRIAS (`transformOrigin`, `perspective`, `willChange`) e
|
|
1180
|
+
* não funções de `transform`, então não são sufixo desta família: entram como leitor novo, com
|
|
1181
|
+
* medição própria, no dia em que uma população as escrever como classe.
|
|
1182
|
+
*/
|
|
1183
|
+
const axial = /^(-?)(rotate|skew|scale)-([xyz])-(.+)$/.exec(core);
|
|
1184
|
+
if (axial) {
|
|
1185
|
+
/**
|
|
1186
|
+
* QUANDO ESTE PADRÃO CASA, ELE DECIDE - e nunca deixa cair no leitor genérico abaixo.
|
|
1187
|
+
*
|
|
1188
|
+
* É a metade que fecha a porta de verdade. O primeiro rascunho só ACRESCENTAVA a leitura por
|
|
1189
|
+
* eixo, e `rotate-x-banana` continuava passando para `^-?rotate-(.+)$` e voltando
|
|
1190
|
+
* `rotate(x-bananadeg)` - o mesmo CSS inválido, agora por um caminho a mais. Um `return null`
|
|
1191
|
+
* aqui manda a classe para a lista de não lidas, que é a resposta certa: nunca inventamos, e
|
|
1192
|
+
* uma lacuna dita vale mais que um valor que o navegador descarta.
|
|
1193
|
+
*
|
|
1194
|
+
* E O SINAL SÓ EXISTE ONDE O TAILWIND O DEFINE: `-rotate-y-90` e `-skew-y-3` são válidos;
|
|
1195
|
+
* `-scale-x-95` não é, e aceitá-lo seria ler uma decisão que ele não tem como ter escrito.
|
|
1196
|
+
*/
|
|
1197
|
+
const [, sign, kind, ax, arg] = axial;
|
|
1198
|
+
const fn = `${kind}${ax.toUpperCase()}`;
|
|
1199
|
+
const arbitrary = /^\[(.+)\]$/.exec(arg);
|
|
1200
|
+
if (kind === "scale") {
|
|
1201
|
+
if (sign)
|
|
1202
|
+
return null;
|
|
1203
|
+
const factor = arbitrary
|
|
1204
|
+
? arbitrary[1]
|
|
1205
|
+
: /^\d+$/.test(arg)
|
|
1206
|
+
? String(Number(arg) / 100)
|
|
1207
|
+
: null;
|
|
1208
|
+
return factor
|
|
1209
|
+
? { property: "transform", value: `${fn}(${factor})` }
|
|
1210
|
+
: null;
|
|
1211
|
+
}
|
|
1212
|
+
if (!arbitrary && !/^\d+(\.\d+)?$/.test(arg))
|
|
1213
|
+
return null;
|
|
1214
|
+
const deg = arbitrary ? arbitrary[1] : `${arg}deg`;
|
|
1215
|
+
return { property: "transform", value: `${fn}(${sign}${deg})` };
|
|
1216
|
+
}
|
|
1142
1217
|
const rotate = /^-?rotate-(.+)$/.exec(core);
|
|
1143
1218
|
if (rotate) {
|
|
1144
1219
|
const arbitrary = /^\[(.+)\]$/.exec(rotate[1]);
|
|
@@ -1148,6 +1223,57 @@ function readUtilityCore(utility, declared) {
|
|
|
1148
1223
|
value: `rotate(${core.startsWith("-") ? `-${deg}` : deg})`,
|
|
1149
1224
|
};
|
|
1150
1225
|
}
|
|
1226
|
+
/**
|
|
1227
|
+
* `-translate-y-px`, `translate-x-1/2`, `translate-y-[140%]` - o deslocamento estático.
|
|
1228
|
+
*
|
|
1229
|
+
* A MESMA FAMÍLIA DE `rotate` E `scale`, e era a única das três sem leitor. Medido em 02/09, por
|
|
1230
|
+
* CAMADA e com a régua do produto:
|
|
1231
|
+
*
|
|
1232
|
+
* codelevel 22 camadas com classe de transform · 5 chegavam (23%) · 17 perdidas
|
|
1233
|
+
* frontend-hub 2 camadas · 2 chegavam (100%) · 0 perdidas
|
|
1234
|
+
*
|
|
1235
|
+
* As duas populações discordam porque escrevem coisas diferentes: o `frontend-hub` usa
|
|
1236
|
+
* `scale-95` e `rotate-180`, que já eram lidos; o `codelevel` usa `translate`, e nele as formas
|
|
1237
|
+
* perdidas eram `-translate-y-px` (6), `-translate-x-1/2` (3), `-translate-y-0.5` (2),
|
|
1238
|
+
* `-translate-y-1/2` (2), `translate-y-0`, `translate-y-[140%]` e as duas do grupo nomeado.
|
|
1239
|
+
*
|
|
1240
|
+
* O QUE ELE VÊ SEM ISTO: o card dele sobe 1px no hover no navegador e não sobe no Canvas, e o
|
|
1241
|
+
* elemento que ele centrou com `-translate-x-1/2` aparece deslocado meia largura.
|
|
1242
|
+
*
|
|
1243
|
+
* A ESCALA É A DELE PRIMEIRO, exatamente como em `gap-sm`: `--spacing-<passo>` declarado vence a
|
|
1244
|
+
* fórmula numérica. A FRAÇÃO é percentual por definição do Tailwind (`1/2` = 50%), e não passo de
|
|
1245
|
+
* escala - convertê-la pela escala daria um valor que nada no CSS dele significa.
|
|
1246
|
+
*
|
|
1247
|
+
* DUAS NA MESMA CAMADA AINDA SE SOBRESCREVEM, e isso não é novo nem calado: as três funções
|
|
1248
|
+
* pousam em `transform` inteiro, o comentário de `rotate` já declarava isso, e medido em 02/09 é
|
|
1249
|
+
* **1 de 18** camadas no `codelevel` e 0 no `frontend-hub`. Dizer uma continua sendo melhor que
|
|
1250
|
+
* dizer nenhuma.
|
|
1251
|
+
*/
|
|
1252
|
+
const translate = /^-?translate-([xy])-(.+)$/.exec(core);
|
|
1253
|
+
if (translate) {
|
|
1254
|
+
const axis = translate[1] === "x" ? "X" : "Y";
|
|
1255
|
+
const arg = translate[2];
|
|
1256
|
+
const neg = core.startsWith("-");
|
|
1257
|
+
const own = `--spacing-${arg}`;
|
|
1258
|
+
const arbitrary = /^\[(.+)\]$/.exec(arg);
|
|
1259
|
+
/** A FRAÇÃO NÃO CHEGA AQUI - `readUtility` a lê antes do split de alpha, e o comentário lá
|
|
1260
|
+
* explica por que ela não pode esperar por este ponto. */
|
|
1261
|
+
const size = arbitrary
|
|
1262
|
+
? arbitrary[1]
|
|
1263
|
+
: arg === "full"
|
|
1264
|
+
? "100%"
|
|
1265
|
+
: declared.has(own)
|
|
1266
|
+
? `{spacing.${arg}}`
|
|
1267
|
+
: (SPACING[arg] ?? numericSpacing(arg));
|
|
1268
|
+
if (size)
|
|
1269
|
+
return {
|
|
1270
|
+
property: "transform",
|
|
1271
|
+
value: `translate${axis}(${neg ? `-${size}` : size})`,
|
|
1272
|
+
...(!arbitrary && arg !== "full" && declared.has(own)
|
|
1273
|
+
? { token: own }
|
|
1274
|
+
: {}),
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1151
1277
|
const scale = /^scale-(.+)$/.exec(core);
|
|
1152
1278
|
if (scale) {
|
|
1153
1279
|
const arbitrary = /^\[(.+)\]$/.exec(scale[1]);
|
|
@@ -1648,8 +1774,12 @@ function slotFor(out, modifiers) {
|
|
|
1648
1774
|
let at;
|
|
1649
1775
|
let unslotted = false;
|
|
1650
1776
|
for (const mod of rest) {
|
|
1651
|
-
/**
|
|
1652
|
-
|
|
1777
|
+
/**
|
|
1778
|
+
* `group-hover:`, `group-data-[checked]:` - the PARENT's state. E o NOME do grupo depois da
|
|
1779
|
+
* barra não é parte do estado: ver a régua gêmea em `variant-read.ts`, com a medição das duas
|
|
1780
|
+
* populações e a prova de que descartar o nome não junta nada.
|
|
1781
|
+
*/
|
|
1782
|
+
const group = /^group-(.+?)(?:\/[A-Za-z0-9_-]+)?$/.exec(mod);
|
|
1653
1783
|
const bare = group ? group[1] : mod;
|
|
1654
1784
|
const state = STATE_SPELLING[bare] ?? dataState(bare);
|
|
1655
1785
|
if (state) {
|
|
@@ -73,8 +73,34 @@ const MODIFIER_STATE = {
|
|
|
73
73
|
"aria-invalid": "invalid",
|
|
74
74
|
"aria-busy": "loading",
|
|
75
75
|
};
|
|
76
|
-
/**
|
|
77
|
-
|
|
76
|
+
/**
|
|
77
|
+
* O NOME DO GRUPO NÃO É PARTE DO ESTADO - `group-hover/tt:` é "quando o grupo chamado `tt` está em
|
|
78
|
+
* hover", e o estado ali é `hover`.
|
|
79
|
+
*
|
|
80
|
+
* O TAILWIND NOMEIA GRUPOS desde a v3, e é o que se escreve quando dois grupos se aninham: o pai
|
|
81
|
+
* declara `group/tt` e o filho reage com `group-hover/tt:`. A régua capturava `(.+)` depois do
|
|
82
|
+
* `group-`, então ela procurava o estado `hover/tt` na tabela e não achava - e o estilo inteiro
|
|
83
|
+
* daquela condição era declarado perdido.
|
|
84
|
+
*
|
|
85
|
+
* MEDIDO EM 02/09, e AS DUAS POPULAÇÕES SÃO OPOSTAS (`INV-GERAL-07`):
|
|
86
|
+
*
|
|
87
|
+
* codelevel 138 usos NOMEADOS em 39 arquivos · 0 anônimos
|
|
88
|
+
* frontend-hub 0 nomeados · 192 anônimos
|
|
89
|
+
*
|
|
90
|
+
* Uma régua escrita para qualquer uma das duas quebraria na outra por inteiro. O que decide não é
|
|
91
|
+
* a população: é a gramática que o Tailwind declara, e nela o nome é um IDENTIFICADOR opcional
|
|
92
|
+
* depois de uma barra. `peer-<estado>/<nome>:` tem a mesma forma e entra pela mesma porta - 0 usos
|
|
93
|
+
* nas duas populações hoje, e cobrir os dois é derivar da gramática em vez de fixar o que um
|
|
94
|
+
* repositório escreve.
|
|
95
|
+
*
|
|
96
|
+
* DESCARTAR O NOME É SEGURO, e isto foi medido antes de ser decidido: o nome só distinguiria algo
|
|
97
|
+
* se dois grupos diferentes aparecessem na MESMA lista de classes. No `codelevel` são 2 nomes no
|
|
98
|
+
* repositório inteiro (`tt` 96, `btn` 42), 7 arquivos usam os dois, e **em 0 dos 10 atributos
|
|
99
|
+
* `className` medidos eles coexistem**. Cada lista de classes pertence a um elemento, e ali o nome
|
|
100
|
+
* é derivável do contexto. Se um dia coexistirem, as duas condições produzem o MESMO `within` e
|
|
101
|
+
* viram duas camadas distinguíveis pelas outras classes - nada é somado em silêncio.
|
|
102
|
+
*/
|
|
103
|
+
const RELATIONAL = /^(?:group|peer)-(.+?)(?:\/[A-Za-z0-9_-]+)?$/;
|
|
78
104
|
/**
|
|
79
105
|
* `data-[state=open]:`, `data-[checked]:`, `data-[side=top]:` - como @base-ui e Radix escrevem uma
|
|
80
106
|
* condição.
|
package/dist/install-marks.js
CHANGED
|
@@ -532,7 +532,91 @@ export const CHECKER_SINCE = "0.16.308";
|
|
|
532
532
|
* si, o campo fica ausente, e ausente ali continua sendo a resposta certa - o `codelevel-ui` dá
|
|
533
533
|
* 0 de 0 contra 145 arquivos de página do `frontend-hub`.
|
|
534
534
|
*/
|
|
535
|
-
|
|
535
|
+
/**
|
|
536
|
+
* 0.16.349 -> 0.16.350 em 01/09 (`INV-COLETA-12`, a metade do MONOREPO): as páginas do app dele
|
|
537
|
+
* passam a chegar ao censo mesmo quando a leitura foi apontada para a biblioteca.
|
|
538
|
+
*
|
|
539
|
+
* O QUE ELE GANHA COM O `sync`: a linha *"N of your components hold up M pages"* - quais peças
|
|
540
|
+
* dele são a espinha das telas dele. Quem roda `--scope packages/ui --usage apps/web` recebia
|
|
541
|
+
* ZERO página e o relatório calava as duas seções que falam delas.
|
|
542
|
+
*
|
|
543
|
+
* A CORREÇÃO DE UMA FRASE QUE ESTAVA AQUI: "ausente continua sendo a resposta certa para quem
|
|
544
|
+
* apontou a leitura para uma biblioteca" só valia para quem NÃO passou `--usage`. As páginas
|
|
545
|
+
* moram no app, e a varredura da evidência lia cada um desses arquivos para contagem e lei e
|
|
546
|
+
* passava direto pela composição. Medido em 01/09, com o mesmo `--scope`/`--usage` que o
|
|
547
|
+
* `import` e o `sync` passam:
|
|
548
|
+
*
|
|
549
|
+
* codelevel 0 páginas guardadas · 9 existem (apps/web 3, apps/landing 6)
|
|
550
|
+
* frontend-hub 0 páginas guardadas · 156 existem (apps/web-dashboard)
|
|
551
|
+
*
|
|
552
|
+
* QUEM NÃO É AFETADO: quem mede um app inteiro sem escopo - esse caminho já carregava as páginas
|
|
553
|
+
* desde 0.16.348. E quem aponta para uma biblioteca sem `--usage`: ela não compõe telas dentro de
|
|
554
|
+
* si, o campo segue ausente, e ali a ausência é a resposta certa de verdade.
|
|
555
|
+
*/
|
|
556
|
+
/**
|
|
557
|
+
* 0.16.350 -> 0.16.351 em 02/09 (`INV-COLETA-16`): um frame de uma animação que o censo CAPTUROU
|
|
558
|
+
* deixa de ser contado como declaração sem leitor.
|
|
559
|
+
*
|
|
560
|
+
* O QUE ELE GANHA COM O `sync`: o número de cobertura para de contar como perdido o que a
|
|
561
|
+
* plataforma leu inteiro. `0% { opacity: 0 }` nunca vai ter leitor de RECEITA - um frame não é a
|
|
562
|
+
* decisão de um componente -, mas a animação viaja em `census.keyframes` e o compilador a emite.
|
|
563
|
+
*
|
|
564
|
+
* codelevel 35 keyframes capturados · shape-not-read 130 -> 18 · CSS lido 35% -> 88%
|
|
565
|
+
* frontend-hub 1 keyframe capturado · shape-not-read 6 -> 6 · nada muda
|
|
566
|
+
*
|
|
567
|
+
* QUEM NÃO É AFETADO: quem não escreve `@keyframes`. E quem escreve um que a captura NÃO alcançou -
|
|
568
|
+
* ali o frame continua sendo lacuna, porque ali a perda é real.
|
|
569
|
+
*/
|
|
570
|
+
/**
|
|
571
|
+
* 0.16.351 -> 0.16.352 em 02/09 (`INV-INTERP-17`): o GRUPO NOMEADO do Tailwind passa a ser lido.
|
|
572
|
+
*
|
|
573
|
+
* O QUE ELE GANHA COM O `sync`: a condição que só existe quando o grupo em volta reage. `group/tt`
|
|
574
|
+
* no pai e `group-hover/tt:` no filho é como se escreve isso desde a v3 do Tailwind, e a régua
|
|
575
|
+
* procurava o estado `hover/tt` na tabela - não achava, e declarava PERDIDA a condição inteira.
|
|
576
|
+
*
|
|
577
|
+
* MEDIDO NO `codelevel`: 4 classes em `unplaced` viram 2 camadas condicionais com `opacity: 1` na
|
|
578
|
+
* receita do `TooltipContent`. O tooltip dele aparece no hover no navegador e não aparecia na
|
|
579
|
+
* nossa.
|
|
580
|
+
*
|
|
581
|
+
* codelevel 138 usos NOMEADOS · 0 anônimos · unplaced 4 -> 0
|
|
582
|
+
* frontend-hub 0 nomeados · 192 anônimos · unplaced 0 -> 0 (a forma anônima não regride)
|
|
583
|
+
*
|
|
584
|
+
* QUEM NÃO É AFETADO: quem escreve só a forma anônima (`group-hover:`), que já era lida. O nome do
|
|
585
|
+
* grupo é descartado com prova: em 0 dos 10 atributos `className` medidos dois grupos coexistem.
|
|
586
|
+
*/
|
|
587
|
+
/**
|
|
588
|
+
* 0.16.352 -> 0.16.353 em 02/09 (`INV-INTERP-18`): `translate` passa a ser lido.
|
|
589
|
+
*
|
|
590
|
+
* O QUE ELE GANHA COM O `sync`: o deslocamento estático que ele declara. `rotate` e `scale` já
|
|
591
|
+
* eram lidos; `translate` era a única das três funções de transform sem leitor, e a camada inteira
|
|
592
|
+
* que dependia dela era descartada.
|
|
593
|
+
*
|
|
594
|
+
* codelevel 5 de 22 camadas chegavam (23%) -> 30 de 30
|
|
595
|
+
* frontend-hub 2 de 2 (100%) -> 2 de 2 · ele escreve `scale`/`rotate`, que já eram lidos
|
|
596
|
+
*
|
|
597
|
+
* E UM CASO PIOR QUE NÃO-LIDO: `-translate-x-1/2`, com que ele centra elementos, chegava ao leitor
|
|
598
|
+
* de tamanho como `translate-x-1` e voltava `0.25rem`. Quem já importou tem essa receita gravada -
|
|
599
|
+
* o elemento aparece deslocado meia largura em vez de centrado.
|
|
600
|
+
*
|
|
601
|
+
* QUEM NÃO É AFETADO: quem não escreve `translate-*`.
|
|
602
|
+
*/
|
|
603
|
+
/**
|
|
604
|
+
* 0.16.353 -> 0.16.354 em 02/09 (`INV-INTERP-19`): o EIXO passa a fazer parte da função, e a régua
|
|
605
|
+
* para de emitir CSS inválido.
|
|
606
|
+
*
|
|
607
|
+
* O QUE ELE GANHA COM O `sync`: `rotate-x-45` virava **`rotate(x-45deg)`**, uma declaração que o
|
|
608
|
+
* navegador descarta inteira e que ficava GRAVADA na receita como se fosse a decisão dele. Agora é
|
|
609
|
+
* `rotateX(45deg)`, e o que não é ângulo vira lacuna em vez de valor inventado.
|
|
610
|
+
*
|
|
611
|
+
* A MARCA SOBE APESAR DE N=0 nas duas populações, e é uma escolha declarada: quem TEM a forma tem
|
|
612
|
+
* um valor inválido gravado, e é a marca que faz o `align` chamá-lo a re-medir. Errar para o lado
|
|
613
|
+
* de subir custa um `sync` que não precisava; errar para o outro deixa o valor inválido para
|
|
614
|
+
* sempre, e "0 nas duas que eu medi" não é "0 no mundo" (`INV-GERAL-07`).
|
|
615
|
+
*
|
|
616
|
+
* QUEM NÃO É AFETADO: quem não escreve `rotate-x`, `skew-x/y` ou `scale-x/y`. As formas SEM eixo -
|
|
617
|
+
* `rotate-180`, `scale-95` - não se movem, e há asserção disso.
|
|
618
|
+
*/
|
|
619
|
+
export const READER_SINCE = "0.16.354";
|
|
536
620
|
/**
|
|
537
621
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
538
622
|
*
|
package/dist/merge-census.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mergePages } from "./census-pages.js";
|
|
1
2
|
import { gapReason } from "./doctor/architecture.js";
|
|
2
3
|
const asRecord = (v) => (v ?? {});
|
|
3
4
|
/** Nome do escopo para as mensagens - `.` quando a medição não escopou nada. */
|
|
@@ -296,6 +297,25 @@ export function mergeCensus(list) {
|
|
|
296
297
|
out.conventions = conventions;
|
|
297
298
|
if (architectures && architectures.length > 0)
|
|
298
299
|
out.architectures = architectures;
|
|
300
|
+
/**
|
|
301
|
+
* AS PÁGINAS DOS ESCOPOS SOMAM - ver `mergePages` em `census-pages.ts`.
|
|
302
|
+
*
|
|
303
|
+
* `out` nasce de um spread do PRIMEIRO censo e depois é montado campo a campo, então `pages`
|
|
304
|
+
* do primeiro escopo viajava de carona e a dos SEGUINTES caía inteira, sem uma linha dizendo
|
|
305
|
+
* isso. Com um escopo só o defeito não aparece (a fusão de uma lista devolve o próprio censo),
|
|
306
|
+
* e é por isso que ele sobreviveu: o caminho que o expõe é `--scope A --scope B`, que existe
|
|
307
|
+
* justamente para o sistema que mora em dois pacotes.
|
|
308
|
+
*
|
|
309
|
+
* `pagesTotal` também precisava ser recontado: herdado do primeiro, ele descrevia uma medição
|
|
310
|
+
* e era lido como o total do projeto.
|
|
311
|
+
*/
|
|
312
|
+
const merged = mergePages(list);
|
|
313
|
+
delete out.pages;
|
|
314
|
+
delete out.pagesTotal;
|
|
315
|
+
if (merged.pages)
|
|
316
|
+
out.pages = merged.pages;
|
|
317
|
+
if (merged.pagesTotal)
|
|
318
|
+
out.pagesTotal = merged.pagesTotal;
|
|
299
319
|
if (gaps.length > 0)
|
|
300
320
|
out.architectureGaps = gaps;
|
|
301
321
|
if (newComponentHome)
|
package/package.json
CHANGED