synthesisui 0.16.272 → 0.16.274
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/anatomy-from-sketch.js +30 -2
- package/dist/commands/import.js +42 -0
- package/dist/index.js +54 -10
- package/dist/install-marks.js +1 -1
- package/dist/outside-scope.js +103 -0
- package/package.json +1 -1
|
@@ -608,8 +608,12 @@ versionOf) {
|
|
|
608
608
|
* one is a hole somebody has to explain.
|
|
609
609
|
*/
|
|
610
610
|
const expects = EXPECTS[lower];
|
|
611
|
+
/**
|
|
612
|
+
* E O MESMO ÍNDICE NO SLOT DE UM NÓ INTERNO, pela mesma razão: `measuredAt` só alcança um nó que
|
|
613
|
+
* saiba de onde veio. Aqui o `at` é o do PRÓPRIO nó, porque é o conteúdo dele que o slot ocupa.
|
|
614
|
+
*/
|
|
611
615
|
const withSlot = node.slot
|
|
612
|
-
? [...inside, { as: "slot", ...(expects ? { expects } : {}) }]
|
|
616
|
+
? [...inside, { as: "slot", at: index, ...(expects ? { expects } : {}) }]
|
|
613
617
|
: inside;
|
|
614
618
|
if (withSlot.length > 0) {
|
|
615
619
|
built.children = withSlot;
|
|
@@ -655,7 +659,31 @@ versionOf) {
|
|
|
655
659
|
const rootNode = sketch[top];
|
|
656
660
|
if (rootNode?.slot) {
|
|
657
661
|
const only = EXPECTS[String(rootNode.tag ?? "").toLowerCase()];
|
|
658
|
-
|
|
662
|
+
/**
|
|
663
|
+
* O ÍNDICE DA RAIZ VIAJA COM O SLOT - e sem ele TODO fato de call-site da raiz sumia.
|
|
664
|
+
*
|
|
665
|
+
* `measuredAt` (anatomy-read.ts) começa com `if (typeof node.at !== "number") return out` e só
|
|
666
|
+
* depois lê `passes`, `contentFrom`, `behaviourProps`, `repeats` e `text`. Um nó sintetizado sem
|
|
667
|
+
* `at` não tem como recuperar nada disso.
|
|
668
|
+
*
|
|
669
|
+
* Medido em 21/08 no repositório do dono, e o que se perdia era ACESSIBILIDADE QUE ELE ESCREVEU:
|
|
670
|
+
*
|
|
671
|
+
* Toast <div role="status" aria-live="polite">
|
|
672
|
+
* TooltipContent <span role="tooltip">
|
|
673
|
+
* ThemeToggle <button type="button" role="switch" aria-label=…>
|
|
674
|
+
* Card <Tag onMouseMove=… onMouseLeave=…> (o tilt inteiro)
|
|
675
|
+
*
|
|
676
|
+
* A lei 10 diz que acessibilidade SUPOSTA é pior que nenhuma. Isto era o inverso e é pior ainda:
|
|
677
|
+
* ele escreveu, o censo colheu, e a receita entregava uma div muda ao agente dele.
|
|
678
|
+
*
|
|
679
|
+
* A raiz continua consumida - ela É o componente. O que muda é que o fato dela deixa de morrer com
|
|
680
|
+
* ela: viaja no slot que representa o conteúdo dela, que é o nó que sobra da raiz.
|
|
681
|
+
*/
|
|
682
|
+
read.push({
|
|
683
|
+
as: "slot",
|
|
684
|
+
at: top,
|
|
685
|
+
...(only ? { expects: only } : {}),
|
|
686
|
+
});
|
|
659
687
|
}
|
|
660
688
|
return { read, notes, libraries };
|
|
661
689
|
}
|
package/dist/commands/import.js
CHANGED
|
@@ -42,7 +42,9 @@ import { mergeCensus } from "../merge-census.js";
|
|
|
42
42
|
import { claimName } from "../name-claim.js";
|
|
43
43
|
import { namingQueue } from "../naming-queue.js";
|
|
44
44
|
import { body, paint, section } from "../output.js";
|
|
45
|
+
import { outsideScope } from "../outside-scope.js";
|
|
45
46
|
import { phase, startProgress } from "../progress.js";
|
|
47
|
+
import { repoStateOf } from "../repo-state.js";
|
|
46
48
|
import { detectStack, resolveDeps, stackVersions } from "../stack.js";
|
|
47
49
|
import { placeInWorkspace } from "../workspace-place.js";
|
|
48
50
|
import { walk, walkAll } from "./doctor.js";
|
|
@@ -380,6 +382,17 @@ export async function takeCensus(root, opts) {
|
|
|
380
382
|
*/
|
|
381
383
|
const say = opts?.quiet ? () => { } : (line) => console.log(line);
|
|
382
384
|
const scopeLabel = opts?.scopeLabel;
|
|
385
|
+
/**
|
|
386
|
+
* O QUE O ESCOPO DEIXOU DE FORA, medido no sistema de arquivos - ver `outside-scope.ts`.
|
|
387
|
+
*
|
|
388
|
+
* Os caminhos que o leitor REALMENTE olhou são o escopo mais os de uso: `--scope packages/ui
|
|
389
|
+
* --usage apps/web` lê os dois, então nenhum dos dois é "fora". Sem `scopeLabel` a função devolve
|
|
390
|
+
* `null` por conta, porque aí ele apontou para a raiz.
|
|
391
|
+
*/
|
|
392
|
+
const outside = await outsideScope(root, [
|
|
393
|
+
...(scopeLabel ? [scopeLabel] : []),
|
|
394
|
+
...(opts?.usage ?? []).map((u) => u.label),
|
|
395
|
+
]).catch(() => null);
|
|
383
396
|
/**
|
|
384
397
|
* THE LIVE CATALOGUE, once, before anything is matched.
|
|
385
398
|
*
|
|
@@ -2150,6 +2163,15 @@ export async function takeCensus(root, opts) {
|
|
|
2150
2163
|
* acima o tempo todo, gravado por este mesmo objeto - os dois chegam juntos e só um sobrevivia.
|
|
2151
2164
|
*/
|
|
2152
2165
|
...(scopeLabel ? { scope: scopeLabel } : {}),
|
|
2166
|
+
/**
|
|
2167
|
+
* O DENOMINADOR DO FORA - ver `outside-scope.ts`.
|
|
2168
|
+
*
|
|
2169
|
+
* `reachability` e `coverage` contam o dentro. Esta é a única linha que diz o que o escopo
|
|
2170
|
+
* EXCLUIU, e ela existe porque a garantia honesta sobre o que ninguém abriu não é "leu tudo" - é
|
|
2171
|
+
* "sabe exatamente o que não abriu". `null` quando não há escopo: aí o leitor olhou a raiz e a
|
|
2172
|
+
* categoria não existe.
|
|
2173
|
+
*/
|
|
2174
|
+
...(outside ? { outsideScope: outside } : {}),
|
|
2153
2175
|
...((opts?.usage ?? []).length > 0
|
|
2154
2176
|
? { usage: (opts?.usage ?? []).map((u) => u.label) }
|
|
2155
2177
|
: {}),
|
|
@@ -3667,6 +3689,10 @@ export async function runImport(opts) {
|
|
|
3667
3689
|
console.log("");
|
|
3668
3690
|
return;
|
|
3669
3691
|
}
|
|
3692
|
+
/** O que a máquina dele fez até agora - ver o corpo do POST abaixo. */
|
|
3693
|
+
const repoAtImport = opts.cli
|
|
3694
|
+
? await repoStateOf(root, "", opts.cli).catch(() => null)
|
|
3695
|
+
: null;
|
|
3670
3696
|
phase(3, 3, "Sending v1 and the v2 proposal");
|
|
3671
3697
|
const res = await fetch(`${base}/api/onboarding/import`, {
|
|
3672
3698
|
method: "POST",
|
|
@@ -3674,10 +3700,26 @@ export async function runImport(opts) {
|
|
|
3674
3700
|
"content-type": "application/json",
|
|
3675
3701
|
Authorization: `Bearer ${token}`,
|
|
3676
3702
|
},
|
|
3703
|
+
/**
|
|
3704
|
+
* O ESTADO DO REPO VAI NO IMPORT TAMBÉM - e não só no `sync`.
|
|
3705
|
+
*
|
|
3706
|
+
* Medido em 21/08: `repo_state` era NULL no sistema que acabou de nascer, porque este corpo tinha
|
|
3707
|
+
* três campos e o do `sync` tem seis. A consequência é que TRÊS classes de causa ficam cegas num
|
|
3708
|
+
* import - `command-failed`, `command-flooded` e `command-skipped-check` leem `repo.runs`, e sem
|
|
3709
|
+
* ele o cliente roda, algo quebra no comando, e a plataforma não sabe.
|
|
3710
|
+
*
|
|
3711
|
+
* O import É a primeira sincronização: quando ele roda, o `ledger.jsonl` já tem as execuções
|
|
3712
|
+
* anteriores no disco - 16 delas, no caso medido, e zero enviadas.
|
|
3713
|
+
*
|
|
3714
|
+
* O SLUG AINDA NÃO EXISTE aqui, e é por isso que ele vai vazio: `repoStateOf` usa o slug só para
|
|
3715
|
+
* ler `_synthesisui/ds/<slug>/.lock`, que num import não existe de qualquer forma. Todo campo do
|
|
3716
|
+
* payload é opcional, então o que falta simplesmente não viaja - em vez de o conjunto todo faltar.
|
|
3717
|
+
*/
|
|
3677
3718
|
body: JSON.stringify({
|
|
3678
3719
|
census,
|
|
3679
3720
|
name: chosen,
|
|
3680
3721
|
...(opts.group ? { group: opts.group } : {}),
|
|
3722
|
+
...(repoAtImport ? { repo: repoAtImport } : {}),
|
|
3681
3723
|
}),
|
|
3682
3724
|
}).catch(() => null);
|
|
3683
3725
|
if (!res || !res.ok) {
|
package/dist/index.js
CHANGED
|
@@ -640,7 +640,15 @@ function scrub(message) {
|
|
|
640
640
|
.replace(/(?:\.{0,2}\/)[^\s'"`)]{2,}/g, "<path>")
|
|
641
641
|
.trim();
|
|
642
642
|
}
|
|
643
|
-
async function recordRun(command, started, ok, error,
|
|
643
|
+
async function recordRun(command, started, ok, error,
|
|
644
|
+
/**
|
|
645
|
+
* O QUE A SAÍDA MEDIU: quantos bytes, e a LINHA MAIS LARGA.
|
|
646
|
+
*
|
|
647
|
+
* A largura é o eixo novo. Em 21/08 a tabela do import saiu ilegível na tela do dono com 27 947
|
|
648
|
+
* bytes - abaixo de qualquer teto de inundação - porque o problema nunca foi tamanho, foi uma linha
|
|
649
|
+
* que não cabe.
|
|
650
|
+
*/
|
|
651
|
+
written) {
|
|
644
652
|
try {
|
|
645
653
|
const root = resolve(typeof parseFlags(process.argv.slice(2)).flags.dir === "string"
|
|
646
654
|
? parseFlags(process.argv.slice(2)).flags.dir
|
|
@@ -681,7 +689,22 @@ async function recordRun(command, started, ok, error, bytes) {
|
|
|
681
689
|
ms: Date.now() - started,
|
|
682
690
|
/** Só a primeira linha, cortada: uma stack carrega caminhos do projeto de alguém. */
|
|
683
691
|
...(message ? { error: message.split("\n")[0].slice(0, 200) } : {}),
|
|
684
|
-
...(
|
|
692
|
+
...(written && written.bytes > 0 ? { bytes: written.bytes } : {}),
|
|
693
|
+
/**
|
|
694
|
+
* A LARGURA VIAJA COM A DO TERMINAL, e não sozinha.
|
|
695
|
+
*
|
|
696
|
+
* `widest: 180` não diz nada; `widest: 180` num terminal de 120 diz que sessenta colunas foram
|
|
697
|
+
* redesenhadas em cima de si mesmas. Sem `columns` a plataforma teria que supor uma largura, e
|
|
698
|
+
* supor aqui é o que faz um alarme falso em toda máquina com janela grande.
|
|
699
|
+
*/
|
|
700
|
+
...(written && written.widest > 0
|
|
701
|
+
? {
|
|
702
|
+
widest: written.widest,
|
|
703
|
+
...(process.stdout.columns
|
|
704
|
+
? { columns: process.stdout.columns }
|
|
705
|
+
: {}),
|
|
706
|
+
}
|
|
707
|
+
: {}),
|
|
685
708
|
...(frame ? { where: frame } : {}),
|
|
686
709
|
});
|
|
687
710
|
}
|
|
@@ -706,23 +729,44 @@ async function recordRun(command, started, ok, error, bytes) {
|
|
|
706
729
|
function countingStdout() {
|
|
707
730
|
const original = process.stdout.write.bind(process.stdout);
|
|
708
731
|
let bytes = 0;
|
|
732
|
+
let widest = 0;
|
|
733
|
+
/**
|
|
734
|
+
* E A LINHA MAIS LARGA, medida junto - o eixo que nunca foi medido.
|
|
735
|
+
*
|
|
736
|
+
* Em 21/08 a tabela de fechamento do import saiu ILEGÍVEL na tela do dono: uma célula sobrescrevendo
|
|
737
|
+
* a outra, palavras cortadas no meio (`"19 com os eixos declaas partes aninhadas"`). A saída tinha
|
|
738
|
+
* 27 947 bytes - bem abaixo do teto de inundação de 64 KB -, então nenhuma medição nossa viu nada.
|
|
739
|
+
*
|
|
740
|
+
* Bytes não é a pergunta. A pergunta é se a linha CABE. Comparar a mais larga com
|
|
741
|
+
* `process.stdout.columns` transforma "a tabela quebrou" num número que a plataforma recebe, e é a
|
|
742
|
+
* primeira coisa que a esteira mede sobre a PRÓPRIA saída em vez de sobre o repositório dele.
|
|
743
|
+
*
|
|
744
|
+
* Códigos ANSI são retirados antes de contar: cor não ocupa coluna, e contá-la acusaria toda linha
|
|
745
|
+
* pintada.
|
|
746
|
+
*/
|
|
709
747
|
process.stdout.write = ((chunk, ...rest) => {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
748
|
+
const text = typeof chunk === "string"
|
|
749
|
+
? chunk
|
|
750
|
+
: Buffer.isBuffer(chunk)
|
|
751
|
+
? chunk.toString("utf8")
|
|
752
|
+
: "";
|
|
753
|
+
bytes += Buffer.byteLength(text);
|
|
754
|
+
for (const line of text.split("\n")) {
|
|
755
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI é exatamente o que se retira
|
|
756
|
+
const visible = line.replace(/\u001b\[[0-9;]*m/g, "");
|
|
757
|
+
if (visible.length > widest)
|
|
758
|
+
widest = visible.length;
|
|
759
|
+
}
|
|
716
760
|
return original(chunk, ...rest);
|
|
717
761
|
});
|
|
718
|
-
return () => bytes;
|
|
762
|
+
return () => ({ bytes, widest });
|
|
719
763
|
}
|
|
720
764
|
async function guarded() {
|
|
721
765
|
const started = Date.now();
|
|
722
766
|
const command = parseFlags(process.argv.slice(2)).positionals[0] ?? "";
|
|
723
767
|
/** `help` e a invocação vazia não são trabalho sobre um projeto, e registrá-las seria só ruído. */
|
|
724
768
|
const worth = command && command !== "help";
|
|
725
|
-
const written = worth ? countingStdout() : () => 0;
|
|
769
|
+
const written = worth ? countingStdout() : () => ({ bytes: 0, widest: 0 });
|
|
726
770
|
try {
|
|
727
771
|
await main();
|
|
728
772
|
if (worth)
|
package/dist/install-marks.js
CHANGED
|
@@ -253,7 +253,7 @@ export const CHECKER_SINCE = "0.16.250";
|
|
|
253
253
|
* publicado antes deste código existir, e uma marca nele calaria o aviso para quem o instalou. Mesma
|
|
254
254
|
* lição de algumas horas antes, na mesma sessão.
|
|
255
255
|
*/
|
|
256
|
-
export const READER_SINCE = "0.16.
|
|
256
|
+
export const READER_SINCE = "0.16.274";
|
|
257
257
|
/**
|
|
258
258
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
259
259
|
*
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* O QUE O ESCOPO EXCLUIU - o único denominador que a esteira não tinha.
|
|
5
|
+
*
|
|
6
|
+
* O QUÊ. `census.reachability` diz *"22 de 22 arquivos alcançados"* e `census.coverage` diz *"60
|
|
7
|
+
* componentes, 59 lidos"*. As duas contas são honestas e as duas são sobre o DENTRO: quando alguém
|
|
8
|
+
* roda `--scope packages/ui`, tudo fora dali é invisível por construção, e nada conta o que ficou
|
|
9
|
+
* fora.
|
|
10
|
+
*
|
|
11
|
+
* POR QUÊ. O dono perguntou, com razão, se a gente consegue garantir que nenhuma informação dele se
|
|
12
|
+
* perde. Para tudo o que o leitor TOCA a resposta agora é sim, com portão. Para o que ele nunca abriu,
|
|
13
|
+
* a resposta honesta não é "sim" - é *"a gente sabe exatamente o que não abriu, e diz"*. É a única
|
|
14
|
+
* garantia que a física permite: não se conta o que não se olhou, mas se conta o que se deixou de
|
|
15
|
+
* olhar.
|
|
16
|
+
*
|
|
17
|
+
* COMO. Contagem de sistema de arquivos, determinística e barata. Nenhum parse: só quantos arquivos
|
|
18
|
+
* com a extensão do leitor existem fora do escopo, e em que diretórios de topo.
|
|
19
|
+
*
|
|
20
|
+
* O QUE ISTO NÃO PROMETE: dizer o que existe DENTRO daqueles arquivos. Isso exigiria lê-los, que é
|
|
21
|
+
* exatamente o que o escopo pediu para não fazer. A linha diz o tamanho da porta fechada, não o que
|
|
22
|
+
* há atrás dela - e é por isso que ela é uma oferta ("aponte para cá também"), nunca um alarme.
|
|
23
|
+
*/
|
|
24
|
+
/** As extensões que os leitores da esteira realmente abrem. */
|
|
25
|
+
const READ = new Set([".tsx", ".jsx", ".ts", ".js", ".css", ".vue", ".svelte"]);
|
|
26
|
+
/** Pastas que nenhum cliente quer contadas - e a lista é sobre RUÍDO, não sobre leitura. */
|
|
27
|
+
const SKIP = new Set([
|
|
28
|
+
"node_modules",
|
|
29
|
+
"dist",
|
|
30
|
+
"build",
|
|
31
|
+
"out",
|
|
32
|
+
".next",
|
|
33
|
+
".turbo",
|
|
34
|
+
".git",
|
|
35
|
+
"coverage",
|
|
36
|
+
"_synthesisui",
|
|
37
|
+
"storybook-static",
|
|
38
|
+
]);
|
|
39
|
+
async function countIn(dir, root, inside, tally, depth = 0) {
|
|
40
|
+
/** Um monorepo fundo não vale um passeio infinito; oito níveis cobrem qualquer layout real. */
|
|
41
|
+
if (depth > 8)
|
|
42
|
+
return;
|
|
43
|
+
/**
|
|
44
|
+
* O TIPO ESCRITO À MÃO, e não inferido de `readdir`: a sobrecarga que o TS escolhe sem argumento
|
|
45
|
+
* devolve `Dirent<Buffer>`, e aí `entry.name` deixa de ser string. Inferir de uma sobrecarga é
|
|
46
|
+
* pedir que o compilador adivinhe qual das cinco eu quis.
|
|
47
|
+
*/
|
|
48
|
+
let entries;
|
|
49
|
+
try {
|
|
50
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
if (entry.name.startsWith(".") || SKIP.has(entry.name))
|
|
57
|
+
continue;
|
|
58
|
+
const path = join(dir, entry.name);
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
await countIn(path, root, inside, tally, depth + 1);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const dot = entry.name.lastIndexOf(".");
|
|
64
|
+
if (dot < 0 || !READ.has(entry.name.slice(dot)))
|
|
65
|
+
continue;
|
|
66
|
+
const rel = relative(root, path);
|
|
67
|
+
if (inside(rel))
|
|
68
|
+
continue;
|
|
69
|
+
/** O diretório de topo é a unidade que uma pessoa reconhece: `apps/web`, não a pasta folha. */
|
|
70
|
+
const top = rel.split(sep).slice(0, 2).join("/");
|
|
71
|
+
tally.set(top, (tally.get(top) ?? 0) + 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* MEDE O FORA. `scopes` são os caminhos que o import realmente leu, relativos à raiz.
|
|
76
|
+
*
|
|
77
|
+
* Devolve `null` quando não há escopo declarado: sem `--scope` o leitor olhou a raiz, então "fora do
|
|
78
|
+
* escopo" não é uma categoria que existe - e emitir uma linha de zero ensinaria o cliente a ignorar a
|
|
79
|
+
* seção quando ela tiver algo.
|
|
80
|
+
*/
|
|
81
|
+
export async function outsideScope(root, scopes) {
|
|
82
|
+
const declared = scopes.map((s) => s.replace(/^\.\//, "").replace(/\/$/, ""));
|
|
83
|
+
if (declared.length === 0)
|
|
84
|
+
return null;
|
|
85
|
+
const inside = (rel) => declared.some((s) => rel === s || rel.startsWith(`${s}/`));
|
|
86
|
+
const tally = new Map();
|
|
87
|
+
await countIn(root, root, inside, tally);
|
|
88
|
+
const places = [...tally]
|
|
89
|
+
.map(([path, files]) => ({ path, files }))
|
|
90
|
+
.sort((a, b) => b.files - a.files);
|
|
91
|
+
const files = places.reduce((n, p) => n + p.files, 0);
|
|
92
|
+
if (files === 0)
|
|
93
|
+
return null;
|
|
94
|
+
const named = places
|
|
95
|
+
.slice(0, 3)
|
|
96
|
+
.map((p) => `${p.path} (${p.files})`)
|
|
97
|
+
.join(", ");
|
|
98
|
+
return {
|
|
99
|
+
files,
|
|
100
|
+
places,
|
|
101
|
+
said: `${files} more readable file${files === 1 ? "" : "s"} live outside what you pointed at - ${named}${places.length > 3 ? `, and ${places.length - 3} more place${places.length - 3 === 1 ? "" : "s"}` : ""}. Nothing was read there, so nothing about them is in this measurement: point at them too if their design belongs in this system.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
package/package.json
CHANGED