synthesisui 0.16.128 → 0.16.130
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-read.js +9 -0
- package/dist/commands/import.js +65 -13
- package/dist/doctor/coverage.js +19 -1
- package/dist/doctor/sketch.js +86 -0
- package/package.json +1 -1
package/dist/anatomy-read.js
CHANGED
|
@@ -288,6 +288,15 @@ sketch) {
|
|
|
288
288
|
if (measured?.behaviourProps &&
|
|
289
289
|
Object.keys(measured.behaviourProps).length > 0)
|
|
290
290
|
node_.behaviourProps = measured.behaviourProps;
|
|
291
|
+
/**
|
|
292
|
+
* O QUE ELE RECEBE E O QUE ELE REPETE, pela mesma consulta. As duas peças que fazem uma
|
|
293
|
+
* receita de TELA carregar a fiação de dados, e não só a casca - ver `SketchNode.passes`
|
|
294
|
+
* e `SketchNode.repeats`.
|
|
295
|
+
*/
|
|
296
|
+
if (measured?.passes && Object.keys(measured.passes).length > 0)
|
|
297
|
+
node_.passes = measured.passes;
|
|
298
|
+
if (measured?.repeats)
|
|
299
|
+
node_.repeats = measured.repeats;
|
|
291
300
|
}
|
|
292
301
|
else {
|
|
293
302
|
if (node.when)
|
package/dist/commands/import.js
CHANGED
|
@@ -261,6 +261,8 @@ export async function takeCensus(root, opts) {
|
|
|
261
261
|
const tally = emptyTally();
|
|
262
262
|
const internal = await internalSpecifiers(root);
|
|
263
263
|
const defined = [];
|
|
264
|
+
/** Nome → arquivo e fonte, para toda leitura por componente - ver o preenchimento abaixo. */
|
|
265
|
+
const sourceOf = new Map();
|
|
264
266
|
/** What the gate turned away, with the reason. Never dropped in silence. */
|
|
265
267
|
const skips = [];
|
|
266
268
|
// Kept for the reference check below, which needs every file at once: a name
|
|
@@ -411,6 +413,21 @@ export async function takeCensus(root, opts) {
|
|
|
411
413
|
componentFiles += 1;
|
|
412
414
|
}
|
|
413
415
|
defined.push(...found);
|
|
416
|
+
/**
|
|
417
|
+
* O ARQUIVO E A FONTE DE CADA COMPONENTE QUE GANHA UM LOOK.
|
|
418
|
+
*
|
|
419
|
+
* O contrato de dados era estampado a partir de `defined`, a lista que o PORTÃO de
|
|
420
|
+
* componente aceita - 36 numa biblioteca e 29 num app de 690 arquivos. Então 242 receitas
|
|
421
|
+
* do dashboard dele e 19 do web-review chegavam sem `api` nenhum: sem props, sem
|
|
422
|
+
* derivação, sem tipo de eixo e sem o especificador de import, embora 41% e 61% dos
|
|
423
|
+
* arquivos declarem um `<Nome>Props` (04/08).
|
|
424
|
+
*
|
|
425
|
+
* É a mesma classe de defeito da elegibilidade, um nível ao lado: uma leitura pendurada
|
|
426
|
+
* numa lista feita para biblioteca. O look já nasce aqui, com o arquivo e a fonte na mão,
|
|
427
|
+
* e é aqui que a leitura tem que se pendurar.
|
|
428
|
+
*/
|
|
429
|
+
for (const d of found)
|
|
430
|
+
sourceOf.set(d.name, { file: rel, source: src });
|
|
414
431
|
// THE LOOK, from their own class names. One transcription per file, keyed
|
|
415
432
|
// by the component it defines - the root element's classes are the
|
|
416
433
|
// component's own look, and everything below it is a part this slice does
|
|
@@ -1076,7 +1093,21 @@ export async function takeCensus(root, opts) {
|
|
|
1076
1093
|
console.log(body(paint.faint(` and ${unreadableByComponent.length - 8} more components`)));
|
|
1077
1094
|
}
|
|
1078
1095
|
}
|
|
1079
|
-
|
|
1096
|
+
/**
|
|
1097
|
+
* QUANTOS DECIDEM ARRANJO EM VEZ DE APARÊNCIA - a mesma conta que a plataforma faz para
|
|
1098
|
+
* marcar `composition: true`: uma receita sem uma única declaração nunca prometeu cor.
|
|
1099
|
+
* Dito ANTES de importar, porque "36 de 271 saíram com aparência" faz parecer que 235 se
|
|
1100
|
+
* perderam, e eles não se perderam (04/08).
|
|
1101
|
+
*/
|
|
1102
|
+
const composition = Object.values(looks).filter((look) => {
|
|
1103
|
+
const l = look;
|
|
1104
|
+
return (Object.keys(l.base ?? {}).length === 0 &&
|
|
1105
|
+
Object.keys(l.dark ?? {}).length === 0 &&
|
|
1106
|
+
Object.keys(l.states ?? {}).length === 0 &&
|
|
1107
|
+
Object.keys(l.parts ?? {}).length === 0 &&
|
|
1108
|
+
(l.layers ?? []).length === 0);
|
|
1109
|
+
}).length;
|
|
1110
|
+
const coverageLines = describeCoverage(coverage, composition);
|
|
1080
1111
|
if (coverageLines.length > 0) {
|
|
1081
1112
|
console.log("");
|
|
1082
1113
|
console.log(section("What this version reads, on your files"));
|
|
@@ -1225,12 +1256,23 @@ export async function takeCensus(root, opts) {
|
|
|
1225
1256
|
* as not exported. See `publicApi`.
|
|
1226
1257
|
*/
|
|
1227
1258
|
const api = await publicApi(root);
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1259
|
+
/**
|
|
1260
|
+
* TODO NOME QUE TEM LEITURA, e não só os que o inventário de USO lista.
|
|
1261
|
+
*
|
|
1262
|
+
* O laço iterava `components` - o inventário de uso - e pendurava o arquivo em `defined`. Duas
|
|
1263
|
+
* listas feitas para biblioteca, e num app nenhuma das duas alcança uma tela: 242 receitas do
|
|
1264
|
+
* dashboard dele ficaram sem `api` (04/08). Agora a leitura acompanha o LOOK, que é onde o
|
|
1265
|
+
* resto da leitura daquele componente já vive, e o inventário continua sendo inventário.
|
|
1266
|
+
*/
|
|
1267
|
+
const named = [
|
|
1268
|
+
...new Set([...components.map((c) => c.name), ...Object.keys(looks)]),
|
|
1269
|
+
];
|
|
1270
|
+
for (const name of named) {
|
|
1271
|
+
const c = components.find((x) => x.name === name);
|
|
1272
|
+
const at = sourceOf.get(name);
|
|
1273
|
+
const file = at?.file;
|
|
1274
|
+
const source = at?.source;
|
|
1275
|
+
const required = source ? requiredProps(source, name) : [];
|
|
1234
1276
|
/**
|
|
1235
1277
|
* THE COMPONENT'S DATA CONTRACT - its props, its derivations, the copy an option
|
|
1236
1278
|
* carries, and the type that names an axis's options.
|
|
@@ -1242,11 +1284,11 @@ export async function takeCensus(root, opts) {
|
|
|
1242
1284
|
* Stamped whether or not the folder is a package: a `src/components` with no
|
|
1243
1285
|
* manifest of its own has no specifier to offer and its components still have props.
|
|
1244
1286
|
*/
|
|
1245
|
-
const span = source ? definitionSpan(source,
|
|
1287
|
+
const span = source ? definitionSpan(source, name) : null;
|
|
1246
1288
|
const contract = source
|
|
1247
|
-
? dataContract(source, span ? source.slice(span.from, span.to) : source,
|
|
1289
|
+
? dataContract(source, span ? source.slice(span.from, span.to) : source, name, (c?.declaredAxes ?? looks[name]?.declaredAxes ?? {}))
|
|
1248
1290
|
: null;
|
|
1249
|
-
|
|
1291
|
+
const stamped = {
|
|
1250
1292
|
/**
|
|
1251
1293
|
* SILENCE WHEN THE ENTRY WAS NOT READ. An unreadable entry gives an empty export
|
|
1252
1294
|
* set, and calling that "internal" would have marked every component internal at
|
|
@@ -1254,7 +1296,7 @@ export async function takeCensus(root, opts) {
|
|
|
1254
1296
|
*/
|
|
1255
1297
|
...(!api?.read
|
|
1256
1298
|
? {}
|
|
1257
|
-
: api.exports.has(
|
|
1299
|
+
: api.exports.has(name)
|
|
1258
1300
|
? { from: api.from }
|
|
1259
1301
|
: { exported: false }),
|
|
1260
1302
|
/**
|
|
@@ -1280,8 +1322,18 @@ export async function takeCensus(root, opts) {
|
|
|
1280
1322
|
: {}),
|
|
1281
1323
|
};
|
|
1282
1324
|
// An api with nothing in it is not a fact - it is an empty object on 36 components.
|
|
1283
|
-
|
|
1284
|
-
|
|
1325
|
+
/**
|
|
1326
|
+
* ONDE ELE MORA: no LOOK, que é por componente, e também na entrada do inventário quando
|
|
1327
|
+
* existe uma. A entrada é o que a plataforma sempre leu; o look é o que alcança as 242 que
|
|
1328
|
+
* o inventário não tem. Uma leitura, dois endereços, e a plataforma prefere o look.
|
|
1329
|
+
*/
|
|
1330
|
+
if (Object.keys(stamped).length === 0)
|
|
1331
|
+
continue;
|
|
1332
|
+
if (c)
|
|
1333
|
+
c.api = stamped;
|
|
1334
|
+
const look = looks[name];
|
|
1335
|
+
if (look)
|
|
1336
|
+
look.api = stamped;
|
|
1285
1337
|
}
|
|
1286
1338
|
const brokenRefs = findBrokenRefs(sources, declaredNames);
|
|
1287
1339
|
const conventions = detectConventions(sources);
|
package/dist/doctor/coverage.js
CHANGED
|
@@ -105,12 +105,30 @@ export function summarizeCoverage(counts, components, read, declarations) {
|
|
|
105
105
|
* then names every shape that is not fully read, with the reason. No percentages without
|
|
106
106
|
* the count behind them: "75%" of an unknown total is a marketing number.
|
|
107
107
|
*/
|
|
108
|
-
export function describeCoverage(c
|
|
108
|
+
export function describeCoverage(c,
|
|
109
|
+
/**
|
|
110
|
+
* QUANTOS SÃO COMPOSIÇÃO, quando quem chama sabe - ver `componentRecipe.composition`.
|
|
111
|
+
*
|
|
112
|
+
* Numa biblioteca é zero e a frase não aparece. Num app é a maioria, e dizer só "36 de 271
|
|
113
|
+
* saíram com aparência" faz parecer que 235 se perderam. Eles não se perderam: decidem
|
|
114
|
+
* arranjo e fluxo, e é isso que a receita deles carrega. Medido no dashboard dele: 235 de
|
|
115
|
+
* 271 (04/08).
|
|
116
|
+
*/
|
|
117
|
+
composition) {
|
|
109
118
|
if (c.components === 0)
|
|
110
119
|
return [];
|
|
111
120
|
const lines = [];
|
|
112
121
|
const pct = Math.round((c.read / c.components) * 100);
|
|
113
122
|
lines.push(`${c.read} of ${c.components} components came out with a look - ${pct}%, ${c.declarations} declarations in total. This is measured on your files, not an estimate.`);
|
|
123
|
+
if (composition && composition > 0) {
|
|
124
|
+
lines.push(
|
|
125
|
+
/**
|
|
126
|
+
* DIZ O QUE FOI MEDIDO, e não o que a plataforma vai concluir. Este número é "não tem
|
|
127
|
+
* look próprio nos seus arquivos"; a receita final é decidida depois, com a paleta em
|
|
128
|
+
* mão, e as duas contas não são a mesma. Prometer a segunda aqui seria inventar precisão.
|
|
129
|
+
*/
|
|
130
|
+
` and ${composition} have no look of their own in your files - a screen that composes others, in a grid, passing data down. Those arrive as COMPOSITION recipes: the tree, the slots, and what each child receives, with no palette to grade. Nothing was lost; it is a different kind of recipe.`);
|
|
131
|
+
}
|
|
114
132
|
for (const { shape, files, examples } of c.counts) {
|
|
115
133
|
const share = Math.round((files / c.components) * 100);
|
|
116
134
|
const head = ` ${files} (${share}%) ${shape.label}`;
|
package/dist/doctor/sketch.js
CHANGED
|
@@ -351,6 +351,82 @@ export function conditionalPropsOf(body) {
|
|
|
351
351
|
out[m[1]] = m[2].trim();
|
|
352
352
|
return Object.keys(out).length > 0 ? out : null;
|
|
353
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Toda prop de um elemento, com o valor como eles escreveram - ver `SketchNode.passes`.
|
|
356
|
+
*
|
|
357
|
+
* Quatro formas atravessam, porque as quatro são evidência do que a tela liga: a string
|
|
358
|
+
* literal (a cópia dele), o identificador ou caminho (a fiação), o literal numérico ou
|
|
359
|
+
* booleano, e a expressão composta curta. Duas não atravessam: função inline e objeto inline.
|
|
360
|
+
*/
|
|
361
|
+
export function passedProps(body,
|
|
362
|
+
/** O que `behaviourProps` já levou, para cada fato viver num lugar só. */
|
|
363
|
+
taken) {
|
|
364
|
+
const out = {};
|
|
365
|
+
const SKIP = /^(className|class|key|ref|style|children)$/;
|
|
366
|
+
for (const m of body.matchAll(/(\w+)=(?:"([^"]{0,80})"|'([^']{0,80})'|\{([^{}]{1,80})\})/g)) {
|
|
367
|
+
const prop = m[1];
|
|
368
|
+
if (SKIP.test(prop) || taken?.[prop] || out[prop])
|
|
369
|
+
continue;
|
|
370
|
+
const literal = m[2] ?? m[3];
|
|
371
|
+
if (literal != null) {
|
|
372
|
+
out[prop] = literal;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const value = (m[4] ?? "").replace(/\s+/g, " ").trim();
|
|
376
|
+
// Um corpo de função é código que chama o que a receita não tem; um objeto é estilo.
|
|
377
|
+
if (!value || /=>|\bfunction\b/.test(value) || value.startsWith("{"))
|
|
378
|
+
continue;
|
|
379
|
+
out[prop] = value;
|
|
380
|
+
}
|
|
381
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* A expressão que um `map` percorre, quando este elemento está dentro de um - ver `repeats`.
|
|
385
|
+
*
|
|
386
|
+
* Mesma forma de `guardAt`: uma varredura para trás até a chamada que envolve este offset, e
|
|
387
|
+
* o alvo é o que vem antes do `.map(`. Nada é avaliado; a expressão é evidência de que este nó
|
|
388
|
+
* é uma LISTA.
|
|
389
|
+
*/
|
|
390
|
+
export function repeatOf(source, at) {
|
|
391
|
+
const MAP = /([A-Za-z_$][\w$.?[\]]{0,60})\s*\.map\s*\(/g;
|
|
392
|
+
MAP.lastIndex = 0;
|
|
393
|
+
let found = null;
|
|
394
|
+
for (const m of source.matchAll(MAP)) {
|
|
395
|
+
const open = (m.index ?? 0) + m[0].length - 1;
|
|
396
|
+
if (open > at)
|
|
397
|
+
break;
|
|
398
|
+
const end = closeOf(source, open);
|
|
399
|
+
if (end === -1 || end <= at)
|
|
400
|
+
continue;
|
|
401
|
+
found = m[1].replace(/\?$/, "");
|
|
402
|
+
}
|
|
403
|
+
return found;
|
|
404
|
+
}
|
|
405
|
+
/** O índice do fecha-parêntese que corresponde ao abre em `open`, ou -1. */
|
|
406
|
+
function closeOf(source, open) {
|
|
407
|
+
let depth = 0;
|
|
408
|
+
let quote = "";
|
|
409
|
+
for (let i = open; i < source.length; i++) {
|
|
410
|
+
const c = source[i];
|
|
411
|
+
if (quote) {
|
|
412
|
+
if (c === "\\")
|
|
413
|
+
i += 1;
|
|
414
|
+
else if (c === quote)
|
|
415
|
+
quote = "";
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (c === '"' || c === "'" || c === "`")
|
|
419
|
+
quote = c;
|
|
420
|
+
else if (c === "(")
|
|
421
|
+
depth += 1;
|
|
422
|
+
else if (c === ")") {
|
|
423
|
+
depth -= 1;
|
|
424
|
+
if (depth === 0)
|
|
425
|
+
return i;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return -1;
|
|
429
|
+
}
|
|
354
430
|
export function sketchOf(source, name) {
|
|
355
431
|
/**
|
|
356
432
|
* READ THE IMPORTS BEFORE SLICING - they live at the top of the file, and the
|
|
@@ -433,6 +509,16 @@ export function sketchOf(source, name) {
|
|
|
433
509
|
const conditional = conditionalPropsOf(event.body);
|
|
434
510
|
if (conditional)
|
|
435
511
|
node.behaviourProps = conditional;
|
|
512
|
+
/**
|
|
513
|
+
* O QUE ELE RECEBE E O QUE ELE REPETE - ver `passes` e `repeats`. As duas peças que faltavam
|
|
514
|
+
* para a receita de uma TELA carregar a fiação, e não só a casca.
|
|
515
|
+
*/
|
|
516
|
+
const given = passedProps(event.body, conditional);
|
|
517
|
+
if (given)
|
|
518
|
+
node.passes = given;
|
|
519
|
+
const list = repeatOf(source, event.at);
|
|
520
|
+
if (list)
|
|
521
|
+
node.repeats = list;
|
|
436
522
|
// What has to be true for this element to exist at all - see `guardAt`.
|
|
437
523
|
const guard = guardAt(source, event.at);
|
|
438
524
|
if (guard) {
|
package/package.json
CHANGED