synthesisui 0.16.335 → 0.16.337
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/import.js +83 -12
- package/dist/doctor/architecture.js +103 -3
- package/dist/doctor/transcribe.js +36 -1
- package/dist/install-marks.js +23 -1
- package/dist/merge-census.js +35 -0
- package/package.json +1 -1
package/dist/commands/import.js
CHANGED
|
@@ -5,7 +5,7 @@ import { applyAnatomyPatch, hasEdits, } from "../anatomy-patch.js";
|
|
|
5
5
|
import { resolveAnatomy, resolveFlatParts, safePartName, } from "../anatomy-read.js";
|
|
6
6
|
import { readCredentials, readToken, resolveRegistry, sameRegistry, } from "../config.js";
|
|
7
7
|
import { declaredElsewhere } from "../declared-elsewhere.js";
|
|
8
|
-
import { architectureRule, componentHome, describeArchitecture, describeChoice, detectArchitectures, homeLine, proposeNewHome, } from "../doctor/architecture.js";
|
|
8
|
+
import { architectureGap, architectureRule, componentHome, describeArchitecture, describeChoice, describeGap, detectArchitectures, homeLine, proposeNewHome, } from "../doctor/architecture.js";
|
|
9
9
|
import { findBrokenRefs } from "../doctor/broken-refs.js";
|
|
10
10
|
import { nestingRules, propRules, readDefinitionProps, readNesting, readRuntime, } from "../doctor/call-sites.js";
|
|
11
11
|
import { asCatalogueTable, describeFallback, fetchCatalogue, } from "../doctor/catalogue-fetch.js";
|
|
@@ -388,6 +388,21 @@ const MAX_GLOBAL_CLASSES = 300;
|
|
|
388
388
|
* dois com folga e continua sendo um teto de verdade, alinhado com `DISTINCT_CAP` do ledger.
|
|
389
389
|
*/
|
|
390
390
|
const SKIPPED_CAP = 4096;
|
|
391
|
+
/**
|
|
392
|
+
* UM ARQUIVO QUE MOSTRA UM COMPONENTE NÃO É UM COMPONENTE - a mesma exclusão nos três lugares.
|
|
393
|
+
*
|
|
394
|
+
* Stories and tests compose components to SHOW them; counting those as the product's own
|
|
395
|
+
* composition is the same lie the colour census told before they were set aside.
|
|
396
|
+
*
|
|
397
|
+
* E ela vale igual para a CONTAGEM que a organização declara. Medido em 30/08 em
|
|
398
|
+
* `codelevel-monorepo/packages/ui`: 75 arquivos `.tsx`, dos quais 38 são `*.stories.tsx` e 37 são
|
|
399
|
+
* componente. Dizer "we read 75 component files" a quem tem 37 é metade do número inventada por
|
|
400
|
+
* nós - e a frase existe justamente para o caso em que o cliente vai conferir o número na mão.
|
|
401
|
+
*
|
|
402
|
+
* UMA constante, e não três literais: a terceira cópia já existia antes desta, e uma delas
|
|
403
|
+
* envelhecer sozinha significa dois denominadores diferentes para a mesma palavra na mesma saída.
|
|
404
|
+
*/
|
|
405
|
+
const SHOWS_A_COMPONENT = /(\.(spec|test|stories)\.[a-z]+$|__tests__\/|(^|\/)\.storybook\/)/;
|
|
391
406
|
export async function takeCensus(root, opts) {
|
|
392
407
|
/**
|
|
393
408
|
* O RELATÓRIO SÓ SAI QUANDO ALGUÉM O PEDIU - `say` cala quando o chamador é uma RE-medição.
|
|
@@ -552,6 +567,8 @@ export async function takeCensus(root, opts) {
|
|
|
552
567
|
* naming how the project is organised costs no second pass. See `architecture.ts`.
|
|
553
568
|
*/
|
|
554
569
|
const dirs = new Map();
|
|
570
|
+
/** Uma entrada nova da árvore, com os três contadores em zero. */
|
|
571
|
+
const freshDir = () => ({ dirs: new Set(), files: 0, direct: 0 });
|
|
555
572
|
/** Parent → child → the files that put one inside the other. */
|
|
556
573
|
const nesting = new Map();
|
|
557
574
|
/**
|
|
@@ -621,17 +638,37 @@ export async function takeCensus(root, opts) {
|
|
|
621
638
|
continue;
|
|
622
639
|
const rel = relative(root, file);
|
|
623
640
|
progress.step(rel);
|
|
624
|
-
|
|
641
|
+
/**
|
|
642
|
+
* A ORGANIZAÇÃO SÓ CONTA COMPONENTE - `SHOWS_A_COMPONENT`, a mesma exclusão de baixo.
|
|
643
|
+
*
|
|
644
|
+
* Este bloco roda onze linhas ANTES do filtro, e por isso contava story e teste como código
|
|
645
|
+
* do produto: 75 arquivos onde o cliente tem 37 (`codelevel-monorepo/packages/ui`, 30/08). O
|
|
646
|
+
* número que a organização declara é o que ele confere na mão, então ele conta o que ele
|
|
647
|
+
* conta quando confere.
|
|
648
|
+
*/
|
|
649
|
+
if (/\.(tsx|jsx|vue|svelte)$/i.test(rel) && !SHOWS_A_COMPONENT.test(rel)) {
|
|
625
650
|
const parts = rel.split("/");
|
|
626
651
|
// Every ancestor learns the name of the child directly under it, which is all
|
|
627
652
|
// the shape detector needs and is one pass rather than a tree walk.
|
|
628
653
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
629
654
|
const at = parts.slice(0, i).join("/");
|
|
630
|
-
const entry = dirs.get(at) ??
|
|
655
|
+
const entry = dirs.get(at) ?? freshDir();
|
|
631
656
|
entry.dirs.add(parts[i]);
|
|
632
657
|
entry.files += 1;
|
|
633
658
|
dirs.set(at, entry);
|
|
634
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* A PASTA-MÃE, CREDITADA PELO ARQUIVO QUE ELA SEGURA - ver `DirEntry.direct`.
|
|
662
|
+
*
|
|
663
|
+
* O laço acima nunca roda para um arquivo na RAIZ do escopo (`parts.length - 1 === 0`), e
|
|
664
|
+
* era isso que fazia um projeto de quatro componentes soltos - uma forma real de biblioteca
|
|
665
|
+
* pequena - não produzir entrada nenhuma e ficar em silêncio absoluto sobre a própria
|
|
666
|
+
* organização. Aqui a raiz é uma pasta como as outras.
|
|
667
|
+
*/
|
|
668
|
+
const home = parts.slice(0, -1).join("/");
|
|
669
|
+
const entry = dirs.get(home) ?? freshDir();
|
|
670
|
+
entry.direct += 1;
|
|
671
|
+
dirs.set(home, entry);
|
|
635
672
|
}
|
|
636
673
|
sources.push({ file: rel, source: src });
|
|
637
674
|
readSignalsInto(signals, importTally, rel, src);
|
|
@@ -639,7 +676,7 @@ export async function takeCensus(root, opts) {
|
|
|
639
676
|
// Stories and tests compose components to SHOW them; counting those as the
|
|
640
677
|
// product's own composition is the same lie the colour census told before
|
|
641
678
|
// they were set aside.
|
|
642
|
-
if (
|
|
679
|
+
if (!SHOWS_A_COMPONENT.test(rel)) {
|
|
643
680
|
scanComponentsInto(tally, rel, src, internal);
|
|
644
681
|
/**
|
|
645
682
|
* The other half: what this file EXPORTS, with the axes its types declare. A
|
|
@@ -1282,11 +1319,24 @@ export async function takeCensus(root, opts) {
|
|
|
1282
1319
|
}
|
|
1283
1320
|
}
|
|
1284
1321
|
progress.done();
|
|
1285
|
-
|
|
1322
|
+
/** A ÁRVORE MEDIDA, uma vez - as três leituras abaixo saem todas dela. */
|
|
1323
|
+
const dirEntries = [...dirs.entries()].map(([path, v]) => ({
|
|
1286
1324
|
path: path || ".",
|
|
1287
1325
|
dirs: [...v.dirs],
|
|
1288
1326
|
files: v.files,
|
|
1289
|
-
|
|
1327
|
+
direct: v.direct,
|
|
1328
|
+
}));
|
|
1329
|
+
const architectures = detectArchitectures(dirEntries);
|
|
1330
|
+
/**
|
|
1331
|
+
* A LACUNA, quando nada foi reconhecido - ver `architectureGap` (`INV-COB-08`). Nenhuma
|
|
1332
|
+
* decisão aqui: este arquivo IMPRIME e GRAVA o que aquela camada decidiu.
|
|
1333
|
+
*/
|
|
1334
|
+
const gap = architectureGap(dirEntries, architectures);
|
|
1335
|
+
/**
|
|
1336
|
+
* ONDE O PRÓXIMO COMPONENTE VAI - derivado do escopo que ele apontou, e por isso a
|
|
1337
|
+
* pergunta sobrevive a não reconhecermos organização nenhuma.
|
|
1338
|
+
*/
|
|
1339
|
+
const newHome = proposeNewHome(dirEntries, scopeLabel);
|
|
1290
1340
|
const d = diagnose(reports);
|
|
1291
1341
|
const allComposed = tallyToInventory(tally, 400);
|
|
1292
1342
|
/**
|
|
@@ -1419,7 +1469,7 @@ export async function takeCensus(root, opts) {
|
|
|
1419
1469
|
}
|
|
1420
1470
|
edges.set(file, edgesIn(src));
|
|
1421
1471
|
const rel = join(u.label, relative(u.path, file));
|
|
1422
|
-
if (
|
|
1472
|
+
if (SHOWS_A_COMPONENT.test(rel)) {
|
|
1423
1473
|
continue;
|
|
1424
1474
|
}
|
|
1425
1475
|
usageSeen.add(rel);
|
|
@@ -2007,6 +2057,16 @@ export async function takeCensus(root, opts) {
|
|
|
2007
2057
|
say(body(paint.faint(` ${describeArchitecture(a)}`)));
|
|
2008
2058
|
}
|
|
2009
2059
|
}
|
|
2060
|
+
else if (gap) {
|
|
2061
|
+
/**
|
|
2062
|
+
* A MESMA SEÇÃO, NO MESMO LUGAR DA SAÍDA - `INV-COB-08`. Uma leitura que rodou e não
|
|
2063
|
+
* reconheceu nada tinha esta seção inteira desaparecendo, e a ausência lê como "não
|
|
2064
|
+
* medimos nada aqui" em vez de "medimos, e a régua é nossa".
|
|
2065
|
+
*/
|
|
2066
|
+
say("");
|
|
2067
|
+
say(section("How this project is organised"));
|
|
2068
|
+
say(body(describeGap(gap, scopeLabel)));
|
|
2069
|
+
}
|
|
2010
2070
|
if (skips.length > 0) {
|
|
2011
2071
|
say("");
|
|
2012
2072
|
say(body(describeGate(scoped.length, skips.length)));
|
|
@@ -2492,13 +2552,24 @@ export async function takeCensus(root, opts) {
|
|
|
2492
2552
|
line: homeLine(a),
|
|
2493
2553
|
rule: architectureRule(a, scopeLabel),
|
|
2494
2554
|
})),
|
|
2495
|
-
...(proposeNewHome(architectures, scopeLabel)
|
|
2496
|
-
? {
|
|
2497
|
-
newComponentHome: proposeNewHome(architectures, scopeLabel),
|
|
2498
|
-
}
|
|
2499
|
-
: {}),
|
|
2500
2555
|
}
|
|
2501
2556
|
: {}),
|
|
2557
|
+
/**
|
|
2558
|
+
* A LEITURA QUE RODOU E NÃO RECONHECEU NADA - `INV-COB-08`, ver `architectureGap`.
|
|
2559
|
+
*
|
|
2560
|
+
* Só os DOIS números medidos. Sem nome de forma, sem regra e sem caminho: nada foi
|
|
2561
|
+
* reconhecido, então qualquer um dos três seria adivinhação nossa gravada como fato dele.
|
|
2562
|
+
*/
|
|
2563
|
+
...(gap ? { architectureGap: gap } : {}),
|
|
2564
|
+
/**
|
|
2565
|
+
* FORA DO `if` DAS FORMAS, e é isso que mantém a pergunta viva - `INV-COB-08`.
|
|
2566
|
+
*
|
|
2567
|
+
* "Daqui em diante vai num lugar novo" é derivada do escopo que ele apontou e não depende
|
|
2568
|
+
* de reconhecermos organização nenhuma. Presa dentro do bloco acima, ela sumia junto com o
|
|
2569
|
+
* resto justamente para quem mais precisa dela: o projeto sobre o qual não temos nada a
|
|
2570
|
+
* dizer é o único cujo agente não recebe regra de onde criar componente.
|
|
2571
|
+
*/
|
|
2572
|
+
...(newHome ? { newComponentHome: newHome } : {}),
|
|
2502
2573
|
...(skips.length > 0
|
|
2503
2574
|
? {
|
|
2504
2575
|
/**
|
|
@@ -165,13 +165,26 @@ export function homeLine(a) {
|
|
|
165
165
|
* ela a pergunta é sobre o passado.
|
|
166
166
|
*
|
|
167
167
|
* O caminho é DERIVADO, não inventado: fica dentro do escopo que a pessoa já apontou como
|
|
168
|
-
* fonte de verdade, e usa `src/` só quando
|
|
168
|
+
* fonte de verdade, e usa `src/` só quando a árvore medida mostra que o escopo tem `src`.
|
|
169
169
|
* Sem nome de sistema dentro dele de propósito - quem quer isso digita, e digitar um nome
|
|
170
170
|
* é mais honesto do que a gente adivinhar a grafia dele.
|
|
171
171
|
*/
|
|
172
|
-
export function proposeNewHome(
|
|
172
|
+
export function proposeNewHome(
|
|
173
|
+
/**
|
|
174
|
+
* A ÁRVORE MEDIDA, e não a lista do que foi reconhecido - a porta fechada.
|
|
175
|
+
*
|
|
176
|
+
* `hasSrc` era lido da lista de formas, e com ela VAZIA - que é exatamente o caso em que
|
|
177
|
+
* nenhuma organização conhecida apareceu - a resposta era sempre "não tem `src`": num
|
|
178
|
+
* projeto cujos 75 arquivos moram todos sob `src/`, a proposta mandava o cliente para uma
|
|
179
|
+
* pasta na raiz. Duas entradas (a árvore e as formas) podem discordar; uma não pode, e as
|
|
180
|
+
* formas saem da árvore aqui dentro.
|
|
181
|
+
*/
|
|
182
|
+
entries, scope) {
|
|
183
|
+
const found = detectArchitectures(entries);
|
|
173
184
|
const base = scope ? scope.replace(/\/+$/, "") : "";
|
|
174
|
-
const hasSrc =
|
|
185
|
+
const hasSrc = entries.some((e) => e.path === "src" ||
|
|
186
|
+
e.path.startsWith("src/") ||
|
|
187
|
+
(isRoot(e.path) && e.dirs.includes("src")));
|
|
175
188
|
const trunk = `${base ? `${base}/` : ""}${hasSrc ? "src/components" : "components"}`;
|
|
176
189
|
const fresh = `${trunk}/{atoms, molecules, organisms}`;
|
|
177
190
|
/** Se a proposta é o que já existe, ela não é uma pasta nova - e oferecê-la seria repetir a opção 1. */
|
|
@@ -233,3 +246,90 @@ export function describeChoice(found) {
|
|
|
233
246
|
const list = options.map(say).join(", ");
|
|
234
247
|
return `${options.length} shapes govern real code, and none of them is a mistake: ${list}.${notes.length > 0 ? ` Smaller corners, reported and not asked about: ${notes.map(say).join(", ")}.` : ""} Pick which one has PRIORITY - it becomes the rule every prompt reads, and the others stay true without deciding.`;
|
|
235
248
|
}
|
|
249
|
+
/** A raiz do escopo, que a caminhada escreve como `.` e o mapa guarda como "". */
|
|
250
|
+
function isRoot(path) {
|
|
251
|
+
return path === "." || path === "";
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* A ORGANIZAÇÃO QUE NINGUÉM SOUBE NOMEAR - declarada, nunca calada (`INV-COB-08`).
|
|
255
|
+
*
|
|
256
|
+
* O QUE O CLIENTE GANHA: quando a leitura roda e nenhuma das organizações conhecidas
|
|
257
|
+
* aparece, ele continua recebendo uma resposta - quantos arquivos de componente foram
|
|
258
|
+
* medidos, e em quantas pastas eles estão - com a frase dizendo que o limite é a nossa
|
|
259
|
+
* régua e não o projeto dele. Sem isto a seção inteira sobre organização SOME da saída, e
|
|
260
|
+
* silêncio é indistinguível de "não medimos nada aqui": ele fica sem saber que a régua
|
|
261
|
+
* rodou, sem o número que ela mediu, e sem a chance de dizer onde o próximo componente vai.
|
|
262
|
+
*
|
|
263
|
+
* MEDIÇÃO de 30/08 - a população e a régua de enumeração de escopo estão escritas UMA vez, em
|
|
264
|
+
* `contracts/camada-4-cobertura.md` (`INV-COB-08`), e não são recopiadas aqui: 6 de 43 escopos
|
|
265
|
+
* com componentes (14%) não casam nenhuma organização conhecida, e o maior deles tem 37
|
|
266
|
+
* arquivos. No banco de produção, na mesma data, há 1 sistema com censo de import guardado, e
|
|
267
|
+
* ele é um dos mudos - 1 de 1. Foto daquelas populações naquele dia, nunca invariante.
|
|
268
|
+
*
|
|
269
|
+
* O QUE ESTE BLOCO NÃO FAZ, de propósito: reconhecer uma organização nova. Uma sexta forma é
|
|
270
|
+
* outra entrega. Aqui a ausência de reconhecimento vira DECLARAÇÃO, nunca um palpite.
|
|
271
|
+
*/
|
|
272
|
+
/**
|
|
273
|
+
* ABAIXO DISTO NÃO HÁ ORGANIZAÇÃO A DECLARAR - há conteúdo.
|
|
274
|
+
*
|
|
275
|
+
* MEDIÇÃO de 30/08 - população e régua de enumeração de escopo em
|
|
276
|
+
* `contracts/camada-4-cobertura.md` (`INV-COB-08`): dos 6 escopos mudos, 4 têm um ou dois
|
|
277
|
+
* arquivos de componente ao todo. Um ou dois arquivos não organizam nada, e dizer "não sei
|
|
278
|
+
* nomear a sua organização" sobre eles é ruído com cara de lacuna. Os 2 que sobram são os que a
|
|
279
|
+
* frase existe para alcançar - 37 arquivos em 37 pastas, e 7 arquivos em 1 pasta. O 3 é lido
|
|
280
|
+
* dessa foto, e não é regra do produto.
|
|
281
|
+
*
|
|
282
|
+
* O piso é ancorado por LITERAIS no spec (2 cala, 3 fala) e não por esta constante: um teste
|
|
283
|
+
* escrito como `GAP_MIN_FILES - 1` acompanha a constante para onde ela for, e com ela em zero o
|
|
284
|
+
* produto imprimiria "We read 0 component files in 0 folders" - o zero pelado da lei 14 - com a
|
|
285
|
+
* suíte inteira verde.
|
|
286
|
+
*/
|
|
287
|
+
export const GAP_MIN_FILES = 3;
|
|
288
|
+
/**
|
|
289
|
+
* A LEITURA QUE RODOU E NÃO RECONHECEU NADA - o estado `unknown` desta leitura.
|
|
290
|
+
*
|
|
291
|
+
* Devolve `null` quando há forma reconhecida (aí quem fala é `describeChoice`) e quando o
|
|
292
|
+
* escopo é pequeno demais para ter organização (`GAP_MIN_FILES`).
|
|
293
|
+
*/
|
|
294
|
+
export function architectureGap(entries, found) {
|
|
295
|
+
if (found.length > 0)
|
|
296
|
+
return null;
|
|
297
|
+
/**
|
|
298
|
+
* SOMAR `direct`, e nunca `files`: a caminhada credita cada arquivo a TODOS os ancestrais,
|
|
299
|
+
* então somar `files` conta o mesmo arquivo uma vez por nível. `direct` é o arquivo na pasta
|
|
300
|
+
* onde ele está, uma vez só - a mesma conta que o `find` acima faz na máquina dele.
|
|
301
|
+
*/
|
|
302
|
+
let files = 0;
|
|
303
|
+
let folders = 0;
|
|
304
|
+
for (const e of entries) {
|
|
305
|
+
if (e.direct <= 0)
|
|
306
|
+
continue;
|
|
307
|
+
files += e.direct;
|
|
308
|
+
folders += 1;
|
|
309
|
+
}
|
|
310
|
+
if (files < GAP_MIN_FILES)
|
|
311
|
+
return null;
|
|
312
|
+
return { files, folders };
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* A FRASE, no tom das outras desta camada: o número medido, o motivo, e nenhuma acusação.
|
|
316
|
+
*
|
|
317
|
+
* A ordem é a da dúvida de quem lê - o que medimos, de quem é o limite, e o que isso custa
|
|
318
|
+
* a ele hoje. A segunda parte não é gentileza: sem ela a frase lê como um defeito do
|
|
319
|
+
* repositório dele, e o defeito é da régua.
|
|
320
|
+
*/
|
|
321
|
+
export function describeGap(g,
|
|
322
|
+
/**
|
|
323
|
+
* ONDE MEDIMOS, quando a leitura foi apontada para uma parte do repositório.
|
|
324
|
+
*
|
|
325
|
+
* "here" não resolve para nada na cabeça de quem rodou `--scope packages/ui` da raiz de um
|
|
326
|
+
* monorepo, e num import de dois lugares a mesma seção sai duas vezes falando de pastas
|
|
327
|
+
* diferentes. Sem rótulo a redação continua a mesma: uma leitura da raiz não tem outro
|
|
328
|
+
* lugar a que se referir.
|
|
329
|
+
*/
|
|
330
|
+
where) {
|
|
331
|
+
const files = `${g.files} component file${g.files === 1 ? "" : "s"}`;
|
|
332
|
+
const folders = `${g.folders} folder${g.folders === 1 ? "" : "s"}`;
|
|
333
|
+
const at = where ? `under \`${where}\`` : "here";
|
|
334
|
+
return `We read ${files} in ${folders} ${at}, and none of the ways of organising code this version can name fits them. That does not mean your project has no structure; it means ours does not have a name for yours yet. Nothing about where code lives was written into your rules, so an agent reading your \`CLAUDE.md\` guesses where a new component goes until you tell it.`;
|
|
335
|
+
}
|
|
@@ -2220,12 +2220,47 @@ function ownReturn(source, declAt) {
|
|
|
2220
2220
|
}
|
|
2221
2221
|
return -1;
|
|
2222
2222
|
}
|
|
2223
|
+
/**
|
|
2224
|
+
* A RAIZ POLIMÓRFICA RESOLVIDA PARA O ELEMENTO QUE ELA REALMENTE É (T21, dono, 26/08).
|
|
2225
|
+
*
|
|
2226
|
+
* O QUE O CLIENTE VIA: o blueprint do botão dele anunciando a raiz como `Tag` - o nome de uma
|
|
2227
|
+
* variável local que só existe dentro do arquivo dele, apresentado como se fosse um elemento. O
|
|
2228
|
+
* código é `const Tag = (as ?? "button") as ElementType`, e a raiz é um `<button>`.
|
|
2229
|
+
*
|
|
2230
|
+
* A LEI 13 pede a ORIGEM, e um identificador local não tem nenhuma: não há especificador de import
|
|
2231
|
+
* atrás de `Tag`. Onde o próprio arquivo declara o DEFAULT, ele é a origem - e lê-lo é derivar, não
|
|
2232
|
+
* supor.
|
|
2233
|
+
*
|
|
2234
|
+
* SÓ O QUE É DERIVÁVEL, e a fronteira é o literal. Medido nos dois repositórios vivos em 30/08:
|
|
2235
|
+
*
|
|
2236
|
+
* 4 declarações polimórficas no `codelevel`, 0 no `frontend-hub`
|
|
2237
|
+
* Button.tsx `const Tag = (as ?? "button") as ElementType` -> button
|
|
2238
|
+
* Card.tsx `const Tag = (as ?? "div") as ElementType` -> div
|
|
2239
|
+
* Typography `const Tag: ElementType = as ?? MAPA[variante]` -> fica `Tag`
|
|
2240
|
+
* SkillNode `const Icon = icon ?? MAPA[estado]` -> fica `Icon`
|
|
2241
|
+
*
|
|
2242
|
+
* As duas últimas definem a regra: o default vem de um mapa indexado em runtime, então não existe
|
|
2243
|
+
* UM elemento para resolver. Escolher um seria inventar a raiz do componente dele - e a raiz decide
|
|
2244
|
+
* a marcação que a gente devolve. Sem literal, o nome fica como está.
|
|
2245
|
+
*
|
|
2246
|
+
* (A primeira contagem deste dia disse TRÊS: a régua exigia o literal na mesma linha e não via o
|
|
2247
|
+
* `SkillNode`. Remedida com a régua larga, que é a que responde "quantos a forma alcança".)
|
|
2248
|
+
*/
|
|
2249
|
+
const POLYMORPHIC_DEFAULT = /(?:const|let)\s+NAME\s*(?::[^=]*)?=\s*\(?\s*[A-Za-z_$][\w$]*\s*(?:\?\?|\|\|)\s*["']([a-z][a-z0-9-]*)["']/;
|
|
2223
2250
|
export function rootTag(source, name) {
|
|
2224
2251
|
const ret = returnAt(source, name);
|
|
2225
2252
|
if (ret === -1)
|
|
2226
2253
|
return null;
|
|
2227
2254
|
const m = /<([A-Za-z][A-Za-z0-9.]*)/.exec(source.slice(ret));
|
|
2228
|
-
|
|
2255
|
+
if (!m)
|
|
2256
|
+
return null;
|
|
2257
|
+
const tag = m[1];
|
|
2258
|
+
// Só um identificador local pode ser uma raiz polimórfica: uma tag HTML já é o elemento, e um
|
|
2259
|
+
// nome com ponto (`Tabs.Root`) é membro de um objeto importado, não uma variável com default.
|
|
2260
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(tag))
|
|
2261
|
+
return tag;
|
|
2262
|
+
const declared = new RegExp(POLYMORPHIC_DEFAULT.source.replace("NAME", tag)).exec(source);
|
|
2263
|
+
return declared ? declared[1] : tag;
|
|
2229
2264
|
}
|
|
2230
2265
|
export function rootClasses(source, name) {
|
|
2231
2266
|
const ret = returnAt(source, name);
|
package/dist/install-marks.js
CHANGED
|
@@ -361,13 +361,35 @@ export const CHECKER_SINCE = "0.16.308";
|
|
|
361
361
|
* componentes que devolvem um Fragment recuperam as classes que hoje caem no vazio, e 386 de 386
|
|
362
362
|
* looks legados leem exatamente como liam.
|
|
363
363
|
*
|
|
364
|
+
* 0.16.334 -> 0.16.336 em 30/08 (T21-CLI): `rootTag` passou a resolver a raiz polimórfica para o
|
|
365
|
+
* elemento que ela é - `const Tag = (as ?? "button")` grava `button`, e não mais o nome da variável
|
|
366
|
+
* local. Um censo já gravado carrega `rootTag: "Tag"` e não há como derivar o elemento a partir
|
|
367
|
+
* dele: o default mora no CÓDIGO, que só a remedição lê. Por isso a marca sobe.
|
|
368
|
+
*
|
|
369
|
+
* Medido nos dois repositórios vivos: 4 declarações da forma no `codelevel` (2 resolvem para um
|
|
370
|
+
* elemento, 2 têm default vindo de um mapa em runtime e ficam com o nome), 0 no `frontend-hub`.
|
|
371
|
+
*
|
|
364
372
|
* 0.16.328 -> 0.16.329 em 28/08: `rootClasses` passou a ler só o `className` do PRÓPRIO elemento.
|
|
365
373
|
* Antes ele ancorava no primeiro `className=` do texto da tag de abertura, então a classe de um JSX
|
|
366
374
|
* escrito dentro de uma prop virava classe da raiz - e o `className` da raiz, quando vinha depois,
|
|
367
375
|
* nunca era alcançado. Medido: 1134 componentes, 77 mudaram, e a receita que o cliente recebe muda
|
|
368
376
|
* com eles. Uma medição anterior não produz o conserto, então a marca sobe.
|
|
369
377
|
*/
|
|
370
|
-
|
|
378
|
+
/**
|
|
379
|
+
* 0.16.336 -> 0.16.337 em 30/08 (INV-COB-08): a contagem de arquivos por organização deixou de
|
|
380
|
+
* incluir story, spec e teste. Um censo já gravado carrega o número inflado - `atomic@src/lib/SignalUI`
|
|
381
|
+
* dizia 69 arquivos onde há 38 - e esse número viaja para o CLAUDE.md do cliente como a EVIDÊNCIA da
|
|
382
|
+
* regra (`architectureRule().evidence`, "holding N component files"). Não há como corrigi-lo a partir
|
|
383
|
+
* do censo: saber quais daqueles arquivos eram story exige reler o disco, e só a remedição lê. Por
|
|
384
|
+
* isso a marca sobe.
|
|
385
|
+
*
|
|
386
|
+
* MEDIÇÃO de 30/08 - população e régua de enumeração de escopo em
|
|
387
|
+
* `contracts/camada-4-cobertura.md` (`INV-COB-08`): `frontend-hub/packages/ui` sai de 69 para 38
|
|
388
|
+
* (mesma forma, mesmo lugar); `codelevel/packages/ui` sai de 75 para 37. O ranking não mudou em
|
|
389
|
+
* nenhum dos 43 escopos medidos, então ninguém recebe uma organização DIFERENTE - só o número
|
|
390
|
+
* honesto.
|
|
391
|
+
*/
|
|
392
|
+
export const READER_SINCE = "0.16.337";
|
|
371
393
|
/**
|
|
372
394
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
373
395
|
*
|
package/dist/merge-census.js
CHANGED
|
@@ -119,6 +119,32 @@ export function mergeCensus(list) {
|
|
|
119
119
|
let brokenRefs = first.brokenRefs ?? undefined;
|
|
120
120
|
let conventions = first.conventions ?? undefined;
|
|
121
121
|
let architectures = first.architectures ?? undefined;
|
|
122
|
+
/**
|
|
123
|
+
* A LACUNA DE ORGANIZAÇÃO, SOMADA - `INV-COB-08`, ver `architectureGap` em `import.ts`.
|
|
124
|
+
*
|
|
125
|
+
* A declaração de qualquer escopo SOBREVIVE à fusão. Ela diz "medimos aqui e não soubemos
|
|
126
|
+
* nomear"; deixá-la de fora porque o outro escopo foi reconhecido devolve o silêncio que a
|
|
127
|
+
* invariante existe para eliminar, um passo adiante - e o cliente que rodou com dois escopos
|
|
128
|
+
* é justamente quem tem mais lugar onde a resposta pode sumir.
|
|
129
|
+
*
|
|
130
|
+
* SOMADOS, e não o primeiro que aparece, porque os dois são CONTAGENS e contagem é aditiva:
|
|
131
|
+
* o total descreve exatamente as partes que ficaram sem nome, e nenhum arquivo contado aqui
|
|
132
|
+
* pertence a um escopo reconhecido. É o oposto do caso da cobertura logo abaixo, onde somar
|
|
133
|
+
* percentuais produziria um número que não descreve medição nenhuma.
|
|
134
|
+
*/
|
|
135
|
+
let gapFiles = first.architectureGap
|
|
136
|
+
?.files;
|
|
137
|
+
let gapFolders = first.architectureGap
|
|
138
|
+
?.folders;
|
|
139
|
+
/**
|
|
140
|
+
* ONDE O PRÓXIMO COMPONENTE VAI - o PRIMEIRO escopo vence, a mesma regra de `scope`.
|
|
141
|
+
*
|
|
142
|
+
* É um caminho, não uma contagem: dois caminhos não somam, e escolher o do escopo seguinte
|
|
143
|
+
* mandaria o agente para um lugar fora do escopo que o censo declara como o dele.
|
|
144
|
+
*/
|
|
145
|
+
const newComponentHome = list
|
|
146
|
+
.map((c) => c.newComponentHome)
|
|
147
|
+
.find((h) => typeof h === "string" && h.length > 0);
|
|
122
148
|
let components = first.components ?? undefined;
|
|
123
149
|
let defined = first.defined ?? undefined;
|
|
124
150
|
const stack = [...(first.project.stack ?? [])];
|
|
@@ -177,6 +203,11 @@ export function mergeCensus(list) {
|
|
|
177
203
|
brokenRefs = concat(brokenRefs, c.brokenRefs);
|
|
178
204
|
conventions = concat(conventions, c.conventions);
|
|
179
205
|
architectures = concat(architectures, c.architectures);
|
|
206
|
+
const gap = c.architectureGap;
|
|
207
|
+
if (gap) {
|
|
208
|
+
gapFiles = (gapFiles ?? 0) + (gap.files ?? 0);
|
|
209
|
+
gapFolders = (gapFolders ?? 0) + (gap.folders ?? 0);
|
|
210
|
+
}
|
|
180
211
|
components = concat(components, c.components);
|
|
181
212
|
defined = concat(defined, c.defined);
|
|
182
213
|
for (const s of c.project.stack ?? [])
|
|
@@ -260,6 +291,10 @@ export function mergeCensus(list) {
|
|
|
260
291
|
out.conventions = conventions;
|
|
261
292
|
if (architectures && architectures.length > 0)
|
|
262
293
|
out.architectures = architectures;
|
|
294
|
+
if (gapFiles !== undefined && gapFolders !== undefined)
|
|
295
|
+
out.architectureGap = { files: gapFiles, folders: gapFolders };
|
|
296
|
+
if (newComponentHome)
|
|
297
|
+
out.newComponentHome = newComponentHome;
|
|
263
298
|
if (components && components.length > 0)
|
|
264
299
|
out.components = components;
|
|
265
300
|
if (defined && defined.length > 0)
|
package/package.json
CHANGED