synthesisui 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/doctor.js +175 -69
- package/dist/doctor/scan.js +14 -12
- package/dist/index.js +5 -3
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -252,6 +252,14 @@ export async function doctor(opts) {
|
|
|
252
252
|
r.findings = r.findings.filter((f) => !explained.has(`${r.file}:${f.line}:${f.literal.toLowerCase()}`));
|
|
253
253
|
}
|
|
254
254
|
const d = diagnose(reports);
|
|
255
|
+
// Nine releases in one evening added a section each, every one justified on
|
|
256
|
+
// its own, and nobody read the whole. The result was 151 lines carrying about
|
|
257
|
+
// eight lines of meaning. Detail is now something you ask for.
|
|
258
|
+
const verbose = opts.verbose === true;
|
|
259
|
+
const say = (line) => {
|
|
260
|
+
if (verbose)
|
|
261
|
+
console.log(line);
|
|
262
|
+
};
|
|
255
263
|
console.log(section("Doctor"));
|
|
256
264
|
console.log(body(hasSystem
|
|
257
265
|
? `${table.name ?? table.slug} v${table.version ?? "?"} - ${table.byName.size} tokens, ${d.scanned} files read`
|
|
@@ -266,10 +274,18 @@ export async function doctor(opts) {
|
|
|
266
274
|
for (const r of reports) {
|
|
267
275
|
if (!r.setAside)
|
|
268
276
|
continue;
|
|
269
|
-
|
|
277
|
+
for (const a of r.setAside) {
|
|
278
|
+
aside.set(a.reason, (aside.get(a.reason) ?? 0) + a.count);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const asideTotal = [...aside.values()].reduce((n, v) => n + v, 0);
|
|
282
|
+
if (verbose) {
|
|
283
|
+
for (const [reason, count] of aside) {
|
|
284
|
+
console.log(body(`set aside: ${count} value(s) in ${reason}`));
|
|
285
|
+
}
|
|
270
286
|
}
|
|
271
|
-
|
|
272
|
-
console.log(body(`set aside
|
|
287
|
+
else if (asideTotal > 0) {
|
|
288
|
+
console.log(body(`set aside ${asideTotal} value(s) a token could never hold (--verbose for why)`));
|
|
273
289
|
}
|
|
274
290
|
// 0 of 0 is not a perfect score, it is an empty measurement - printing a
|
|
275
291
|
// full bar there would be the report's first lie.
|
|
@@ -280,18 +296,18 @@ export async function doctor(opts) {
|
|
|
280
296
|
console.log(body(` ${d.tokenUses} from the system, ${d.findings.length} by hand`));
|
|
281
297
|
}
|
|
282
298
|
if (d.findings.length > 0) {
|
|
283
|
-
|
|
299
|
+
say(section("Drift"));
|
|
284
300
|
const order = ["color", "radius", "spacing", "font"].filter((k) => d.counts[k] > 0);
|
|
285
301
|
for (const kind of order) {
|
|
286
|
-
|
|
302
|
+
say(body(`${d.counts[kind]} ${KIND_LABEL[kind]}`));
|
|
287
303
|
}
|
|
288
304
|
// What a person actually acts on first: the value repeated everywhere.
|
|
289
305
|
// One decision here retires dozens of sites, and a list sorted by file
|
|
290
306
|
// never tells you that.
|
|
291
307
|
const repeats = d.repeats.slice(0, 5);
|
|
292
308
|
if (repeats.length > 0) {
|
|
293
|
-
|
|
294
|
-
|
|
309
|
+
say("");
|
|
310
|
+
say(body("Most repeated"));
|
|
295
311
|
const w = Math.max(...repeats.map((r) => r.literal.length));
|
|
296
312
|
for (const r of repeats) {
|
|
297
313
|
const where = `${r.count}\u00d7 in ${r.files} file${r.files === 1 ? "" : "s"}`;
|
|
@@ -301,17 +317,17 @@ export async function doctor(opts) {
|
|
|
301
317
|
: near
|
|
302
318
|
? ` → nearest is ${near.name} (${near.value})`
|
|
303
319
|
: "";
|
|
304
|
-
|
|
320
|
+
say(` ${r.literal.padEnd(w)} ${where}${named}`);
|
|
305
321
|
}
|
|
306
322
|
}
|
|
307
323
|
// Loudest files first: drift concentrates, and the fix is usually one
|
|
308
324
|
// shared component rather than two hundred call sites.
|
|
309
325
|
const files = [...d.files].sort((a, b) => b.findings.length - a.findings.length);
|
|
310
|
-
const shownFiles =
|
|
311
|
-
|
|
326
|
+
const shownFiles = verbose ? files : files.slice(0, 8);
|
|
327
|
+
say("");
|
|
312
328
|
for (const f of shownFiles) {
|
|
313
|
-
|
|
314
|
-
const shown =
|
|
329
|
+
say(body(`${f.file}`));
|
|
330
|
+
const shown = verbose ? f.findings : f.findings.slice(0, 3);
|
|
315
331
|
for (const x of shown) {
|
|
316
332
|
// A dead end with a neighbour is not a dead end. Only for lengths -
|
|
317
333
|
// "nearly the same blue" is the guess this tool must never make.
|
|
@@ -321,15 +337,15 @@ export async function doctor(opts) {
|
|
|
321
337
|
: near
|
|
322
338
|
? `→ nearest is ${near.name} (${near.value})`
|
|
323
339
|
: "→ no token holds this value yet";
|
|
324
|
-
|
|
340
|
+
say(` ${String(x.line).padStart(4)} ${x.literal} ${named}`);
|
|
325
341
|
}
|
|
326
342
|
if (f.findings.length > shown.length) {
|
|
327
|
-
|
|
343
|
+
say(` +${f.findings.length - shown.length} more`);
|
|
328
344
|
}
|
|
329
|
-
|
|
345
|
+
say("");
|
|
330
346
|
}
|
|
331
347
|
if (files.length > shownFiles.length) {
|
|
332
|
-
|
|
348
|
+
say(body(`+${files.length - shownFiles.length} more files. Run with --all to see everything.`));
|
|
333
349
|
}
|
|
334
350
|
}
|
|
335
351
|
// Every other section asks whether the code obeys the system. This one asks
|
|
@@ -339,19 +355,19 @@ export async function doctor(opts) {
|
|
|
339
355
|
const conflicts = documents.flatMap((doc) => findSelfConflicts(doc));
|
|
340
356
|
const conflictsInUse = conflicts.filter((c) => used.has(c.component));
|
|
341
357
|
if (conflictsInUse.length > 0) {
|
|
342
|
-
|
|
343
|
-
|
|
358
|
+
say(section("Your system contradicts itself"));
|
|
359
|
+
say(body(conflictsInUse.length === 1
|
|
344
360
|
? "One component says one thing in prose and another in its recipe."
|
|
345
361
|
: `${conflictsInUse.length} components say one thing in prose and another in their recipe.`));
|
|
346
|
-
|
|
362
|
+
say("");
|
|
347
363
|
for (const c of conflictsInUse) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
364
|
+
say(body(`ds-${c.component}`));
|
|
365
|
+
say(` "${c.law}"`);
|
|
366
|
+
say(` but ${c.where} binds ${c.value}`);
|
|
367
|
+
say("");
|
|
352
368
|
}
|
|
353
|
-
|
|
354
|
-
|
|
369
|
+
say(body("Whoever wrote the law and whoever wrote the recipe disagree."));
|
|
370
|
+
say(body("Until they do not, no code here can be correct."));
|
|
355
371
|
}
|
|
356
372
|
// The other way a system fails itself: a recipe that names a SHELF where the
|
|
357
373
|
// system has a ROLE. The value is legitimate, the reference resolves, the CSS
|
|
@@ -360,22 +376,24 @@ export async function doctor(opts) {
|
|
|
360
376
|
.flatMap((doc) => findFrozenBindings(doc))
|
|
361
377
|
.filter((f) => used.has(f.component));
|
|
362
378
|
if (frozen.length > 0) {
|
|
363
|
-
|
|
364
|
-
|
|
379
|
+
say(section("These will not follow your other scheme"));
|
|
380
|
+
say(body(frozen.length === 1
|
|
365
381
|
? "One recipe names a primitive where a role holds the same value."
|
|
366
382
|
: `${frozen.length} recipes name a primitive where a role holds the same value.`));
|
|
367
|
-
|
|
383
|
+
say("");
|
|
368
384
|
for (const f of frozen) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
385
|
+
say(body(`ds-${f.component} · ${f.where}`));
|
|
386
|
+
say(` binds ${f.wrote}`);
|
|
387
|
+
say(` {color.semantic.${f.role}} holds that, and becomes ${f.becomes}`);
|
|
388
|
+
say("");
|
|
373
389
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
390
|
+
say(body("The value resolves and the CSS compiles, so nothing"));
|
|
391
|
+
say(body("complains - the surface just stays put when the scheme"));
|
|
392
|
+
say(body("moves around it."));
|
|
377
393
|
}
|
|
378
394
|
const frozenAt = new Set(frozen.map((f) => `${f.component}:${f.where.split(" · ").pop()}`));
|
|
395
|
+
let offSystemCount = 0;
|
|
396
|
+
let lawKeepingCount = 0;
|
|
379
397
|
if (overrides.length > 0) {
|
|
380
398
|
// An override on a property the component's own law FORBIDS, written as a
|
|
381
399
|
// reset, is not drift - it is the author keeping a promise the recipe
|
|
@@ -404,6 +422,7 @@ export async function doctor(opts) {
|
|
|
404
422
|
};
|
|
405
423
|
const drifting = overrides.filter((o) => lawKept(o) === null);
|
|
406
424
|
const correcting = overrides.length - drifting.length;
|
|
425
|
+
lawKeepingCount = correcting;
|
|
407
426
|
/**
|
|
408
427
|
* Two very different acts wearing one label. Choosing a DIFFERENT token
|
|
409
428
|
* from the system is a decision inside the vocabulary - the recipe says
|
|
@@ -429,18 +448,19 @@ export async function doctor(opts) {
|
|
|
429
448
|
// REMOVES what the recipe sets, and no token can hold "no transform".
|
|
430
449
|
// Marking it as a raw value asked for something that cannot exist.
|
|
431
450
|
const offSystem = drifting.filter((o) => !onSystem(o) && !isReset(o.wrote)).length;
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
451
|
+
offSystemCount = offSystem;
|
|
452
|
+
say(section("Overruled"));
|
|
453
|
+
say(body(`${drifting.length} place${drifting.length === 1 ? "" : "s"} where the code takes a component`));
|
|
454
|
+
say(body("the system defines, and then overrules it locally."));
|
|
455
|
+
say("");
|
|
456
|
+
say(body(`${offSystem} of them leave the system entirely; the rest pick a different token.`));
|
|
437
457
|
if (correcting > 0) {
|
|
438
|
-
|
|
439
|
-
|
|
458
|
+
say("");
|
|
459
|
+
say(body(correcting === 1
|
|
440
460
|
? "One more overrules it to KEEP a law the recipe breaks - marked below."
|
|
441
461
|
: `${correcting} more overrule it to KEEP a law the recipe breaks - marked below.`));
|
|
442
462
|
}
|
|
443
|
-
|
|
463
|
+
say("");
|
|
444
464
|
const byFile = new Map();
|
|
445
465
|
for (const o of overrides) {
|
|
446
466
|
const list = byFile.get(o.file);
|
|
@@ -449,20 +469,20 @@ export async function doctor(opts) {
|
|
|
449
469
|
else
|
|
450
470
|
byFile.set(o.file, [o]);
|
|
451
471
|
}
|
|
452
|
-
const shown =
|
|
472
|
+
const shown = verbose ? [...byFile] : [...byFile].slice(0, 6);
|
|
453
473
|
for (const [file, list] of shown) {
|
|
454
|
-
|
|
455
|
-
for (const o of
|
|
456
|
-
|
|
474
|
+
say(body(file));
|
|
475
|
+
for (const o of verbose ? list : list.slice(0, 3)) {
|
|
476
|
+
say(` ${String(o.line).padStart(4)} ds-${o.component} · ${o.prop}: ${o.wrote}`);
|
|
457
477
|
// Always say what is being overruled. A finding that cannot name it is
|
|
458
478
|
// indistinguishable from a bug in the reader's eyes, and one of these
|
|
459
479
|
// (`.ds-card:hover { transform: none }`) was a real, deliberate call.
|
|
460
480
|
if (o.recipe)
|
|
461
|
-
|
|
481
|
+
say(` the recipe binds ${o.recipe}`);
|
|
462
482
|
else if (o.where)
|
|
463
|
-
|
|
483
|
+
say(` the recipe binds it ${o.where}`);
|
|
464
484
|
if (lawKept(o) === null && !onSystem(o)) {
|
|
465
|
-
|
|
485
|
+
say(isReset(o.wrote)
|
|
466
486
|
? " ↑ removes it rather than replacing it"
|
|
467
487
|
: " ↑ a raw value, not a token");
|
|
468
488
|
}
|
|
@@ -471,21 +491,21 @@ export async function doctor(opts) {
|
|
|
471
491
|
// that as an infraction is the same unfairness as the law case above.
|
|
472
492
|
if (frozenAt.has(`${o.component}:${o.prop}`) &&
|
|
473
493
|
/var\(\s*--ds-color-semantic-/.test(o.wrote)) {
|
|
474
|
-
|
|
494
|
+
say(" ✓ the recipe is frozen here; yours names a role");
|
|
475
495
|
}
|
|
476
496
|
const kept = lawKept(o);
|
|
477
497
|
if (kept) {
|
|
478
|
-
|
|
479
|
-
|
|
498
|
+
say(` ✓ but the law says: "${kept}"`);
|
|
499
|
+
say(" this override keeps it - the recipe does not");
|
|
480
500
|
}
|
|
481
501
|
}
|
|
482
|
-
if (!
|
|
483
|
-
|
|
502
|
+
if (!verbose && list.length > 3) {
|
|
503
|
+
say(` +${list.length - 3} more`);
|
|
484
504
|
}
|
|
485
|
-
|
|
505
|
+
say("");
|
|
486
506
|
}
|
|
487
507
|
if (byFile.size > shown.length) {
|
|
488
|
-
|
|
508
|
+
say(body(`+${byFile.size - shown.length} more files. Run with --all.`));
|
|
489
509
|
}
|
|
490
510
|
}
|
|
491
511
|
// The system's own words, for the components this project actually uses.
|
|
@@ -496,28 +516,114 @@ export async function doctor(opts) {
|
|
|
496
516
|
.filter(([name]) => (recipes.get(name)?.usage.length ?? 0) > 0)
|
|
497
517
|
.sort((a, b) => b[1] - a[1]);
|
|
498
518
|
if (inUse.length > 0) {
|
|
499
|
-
|
|
500
|
-
const shownComps = opts.laws ||
|
|
519
|
+
say(section("What your system says about what you use"));
|
|
520
|
+
const shownComps = opts.laws || verbose ? inUse : inUse.slice(0, 4);
|
|
501
521
|
for (const [name, count] of shownComps) {
|
|
502
522
|
const laws = recipes.get(name)?.usage ?? [];
|
|
503
|
-
const shown = opts.laws ||
|
|
504
|
-
|
|
523
|
+
const shown = opts.laws || verbose ? laws : laws.slice(0, 2);
|
|
524
|
+
say(body(`ds-${name} · ${count} place${count === 1 ? "" : "s"}`));
|
|
505
525
|
for (const law of shown)
|
|
506
|
-
|
|
526
|
+
say(` ${law}`);
|
|
507
527
|
if (laws.length > shown.length) {
|
|
508
|
-
|
|
528
|
+
say(` +${laws.length - shown.length} more`);
|
|
509
529
|
}
|
|
510
|
-
|
|
530
|
+
say("");
|
|
511
531
|
}
|
|
512
532
|
if (inUse.length > shownComps.length) {
|
|
513
|
-
|
|
514
|
-
|
|
533
|
+
say(body(`+${inUse.length - shownComps.length} more components carry laws. Run with --laws.`));
|
|
534
|
+
say("");
|
|
515
535
|
}
|
|
516
536
|
}
|
|
517
|
-
|
|
537
|
+
say(section("What this means"));
|
|
518
538
|
for (const line of verdict(d, hasSystem, overrides.length, conflictsInUse.length))
|
|
519
|
-
|
|
520
|
-
|
|
539
|
+
say(line);
|
|
540
|
+
say("");
|
|
541
|
+
const plan = [];
|
|
542
|
+
for (const c of conflictsInUse) {
|
|
543
|
+
plan.push({
|
|
544
|
+
rank: 0,
|
|
545
|
+
what: `the ds-${c.component} contradiction`,
|
|
546
|
+
size: `${used.get(c.component) ?? 0} places`,
|
|
547
|
+
cheap: false,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
for (const f of frozen) {
|
|
551
|
+
plan.push({
|
|
552
|
+
rank: 1,
|
|
553
|
+
what: `the frozen ds-${f.component} ${f.where.split(" · ").pop()}`,
|
|
554
|
+
size: `${used.get(f.component) ?? 0} places`,
|
|
555
|
+
cheap: true,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (offSystemCount > 0) {
|
|
559
|
+
plan.push({
|
|
560
|
+
rank: 2,
|
|
561
|
+
what: `the ${offSystemCount} override${offSystemCount === 1 ? "" : "s"} that left the system`,
|
|
562
|
+
size: `${offSystemCount} place${offSystemCount === 1 ? "" : "s"}`,
|
|
563
|
+
cheap: false,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
for (const r of d.repeats.filter((x) => x.token).slice(0, 3)) {
|
|
567
|
+
plan.push({
|
|
568
|
+
rank: 3,
|
|
569
|
+
what: `${r.literal} → ${r.token}`,
|
|
570
|
+
size: `${r.files} file${r.files === 1 ? "" : "s"}`,
|
|
571
|
+
cheap: true,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
for (const r of d.repeats.filter((x) => !x.token).slice(0, 2)) {
|
|
575
|
+
const near = nearestToken(table, r.literal);
|
|
576
|
+
plan.push({
|
|
577
|
+
rank: 4,
|
|
578
|
+
what: near
|
|
579
|
+
? `${r.literal} - name it, or snap to ${near.name}`
|
|
580
|
+
: `${r.literal} - the system has no name for it`,
|
|
581
|
+
size: `${r.files} file${r.files === 1 ? "" : "s"}`,
|
|
582
|
+
cheap: false,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
plan.sort((a, b) => a.rank - b.rank);
|
|
586
|
+
if (!verbose && !hasSystem) {
|
|
587
|
+
// The cold run is the whole pitch: someone with no system, one command, a
|
|
588
|
+
// number they did not have. The summary must not swallow it - it is the
|
|
589
|
+
// only path here that has to persuade rather than inform.
|
|
590
|
+
console.log("");
|
|
591
|
+
for (const line of verdict(d, false, 0, 0))
|
|
592
|
+
console.log(line);
|
|
593
|
+
console.log("");
|
|
594
|
+
}
|
|
595
|
+
else if (!verbose) {
|
|
596
|
+
if (conflictsInUse.length + frozen.length > 0) {
|
|
597
|
+
console.log("");
|
|
598
|
+
console.log(body(`${conflictsInUse.length + frozen.length} problem(s) in the SYSTEM - not fixable from this repo`));
|
|
599
|
+
for (const c of conflictsInUse) {
|
|
600
|
+
console.log(` ds-${c.component} the law forbids ${c.forbids}, the recipe binds it`);
|
|
601
|
+
}
|
|
602
|
+
for (const f of frozen) {
|
|
603
|
+
console.log(` ds-${f.component} ${f.where.split(" · ").pop()} frozen at a primitive, will not follow the other scheme`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (d.findings.length > 0 || overrides.length > 0) {
|
|
607
|
+
console.log("");
|
|
608
|
+
console.log(body(`${d.findings.length} value(s) by hand · ${d.named} already have a name`));
|
|
609
|
+
console.log(body(`${overrides.length - lawKeepingCount} override(s) · ${offSystemCount} left the system` +
|
|
610
|
+
(lawKeepingCount > 0
|
|
611
|
+
? ` · ${lawKeepingCount} more kept a law`
|
|
612
|
+
: "")));
|
|
613
|
+
}
|
|
614
|
+
if (plan.length > 0) {
|
|
615
|
+
console.log("");
|
|
616
|
+
console.log(body("Where to start"));
|
|
617
|
+
const w = Math.min(52, Math.max(...plan.slice(0, 5).map((j) => j.what.length)));
|
|
618
|
+
plan.slice(0, 5).forEach((j, i) => {
|
|
619
|
+
const what = j.what.length > w ? `${j.what.slice(0, w - 1)}…` : j.what.padEnd(w);
|
|
620
|
+
console.log(` ${i + 1}. ${what} ${j.size}${j.cheap ? " (cheap)" : ""}`);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
console.log("");
|
|
624
|
+
console.log(body("synthesisui doctor --verbose every finding, file by file"));
|
|
625
|
+
console.log("");
|
|
626
|
+
}
|
|
521
627
|
if (opts.strict) {
|
|
522
628
|
const mine = d.findings.length > 0 || overrides.length > 0;
|
|
523
629
|
// The two system lenses are deliberately NOT in here by default. A team
|
package/dist/doctor/scan.js
CHANGED
|
@@ -60,16 +60,21 @@ const IDIOM = new Set(["0", "0px", "1px", "9999px", "100%", "50%"]);
|
|
|
60
60
|
export function scanSource(file, source, table) {
|
|
61
61
|
const findings = [];
|
|
62
62
|
let tokenUses = 0;
|
|
63
|
-
|
|
63
|
+
// Reason by reason. Rolling two into "A or B" was the one place the report
|
|
64
|
+
// still lumped things it had told apart everywhere else.
|
|
65
|
+
const aside = new Map();
|
|
66
|
+
const setAside = (reason) => aside.set(reason, (aside.get(reason) ?? 0) + 1);
|
|
64
67
|
if (RENDERS_IMAGE.test(source)) {
|
|
65
68
|
return {
|
|
66
69
|
file,
|
|
67
70
|
findings: [],
|
|
68
71
|
tokenUses: 0,
|
|
69
|
-
setAside:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
setAside: [
|
|
73
|
+
{
|
|
74
|
+
reason: "renders to an image, where CSS variables do not exist",
|
|
75
|
+
count: (source.match(COLOR) ?? []).length,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
73
78
|
};
|
|
74
79
|
}
|
|
75
80
|
let svgDepth = 0;
|
|
@@ -101,7 +106,7 @@ export function scanSource(file, source, table) {
|
|
|
101
106
|
if (literal.includes("$") || literal.includes("{"))
|
|
102
107
|
return;
|
|
103
108
|
if (col >= 0 && inFallback(col)) {
|
|
104
|
-
|
|
109
|
+
setAside("a token's own fallback");
|
|
105
110
|
return;
|
|
106
111
|
}
|
|
107
112
|
const key = `${kind}:${literal}`;
|
|
@@ -123,7 +128,7 @@ export function scanSource(file, source, table) {
|
|
|
123
128
|
(before.match(/<\/svg>/g) ?? []).length;
|
|
124
129
|
// inside an <svg>, a colour on fill= or stroke= is paint, not surface
|
|
125
130
|
if (depthHere > 0 && SVG_PAINT.test(before)) {
|
|
126
|
-
|
|
131
|
+
setAside("SVG artwork");
|
|
127
132
|
continue;
|
|
128
133
|
}
|
|
129
134
|
push("color", m[0], m.index ?? -1);
|
|
@@ -150,12 +155,9 @@ export function scanSource(file, source, table) {
|
|
|
150
155
|
file,
|
|
151
156
|
findings,
|
|
152
157
|
tokenUses,
|
|
153
|
-
...(aside > 0
|
|
158
|
+
...(aside.size > 0
|
|
154
159
|
? {
|
|
155
|
-
setAside: {
|
|
156
|
-
reason: "SVG artwork or a token's own fallback",
|
|
157
|
-
count: aside,
|
|
158
|
-
},
|
|
160
|
+
setAside: [...aside].map(([reason, count]) => ({ reason, count })),
|
|
159
161
|
}
|
|
160
162
|
: null),
|
|
161
163
|
};
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ Usage - deterministic, FREE:
|
|
|
26
26
|
synthesisui upgrade <slug> update an installed DS + regenerate your components + migration brief
|
|
27
27
|
synthesisui use <slug> "<intent>" print a ready-to-paste agent prompt to build/modify on-system
|
|
28
28
|
synthesisui clean [--force] strip create-next-app boilerplate (dry run without --force)
|
|
29
|
-
synthesisui doctor [paths…] [--
|
|
29
|
+
synthesisui doctor [paths…] [--verbose] audit for DRIFT: every design value written by
|
|
30
30
|
hand, the token your system already has for it, and the
|
|
31
31
|
laws your system carries for what you use
|
|
32
32
|
|
|
@@ -54,7 +54,7 @@ Options:
|
|
|
54
54
|
--force clean: apply the changes (without it, dry run)
|
|
55
55
|
--strict doctor: exit 1 when drift is found in THIS repo (for CI)
|
|
56
56
|
--strict-system doctor: also exit 1 when the system itself is inconsistent
|
|
57
|
-
--
|
|
57
|
+
--verbose doctor: every finding, file by file (default is a summary)
|
|
58
58
|
--laws doctor: show every usage law, not just the busiest components
|
|
59
59
|
--out <path> output path for the generated template (default: <pagesDir>/<file>)
|
|
60
60
|
-h, --help this help
|
|
@@ -129,7 +129,9 @@ async function main() {
|
|
|
129
129
|
scopes: args,
|
|
130
130
|
strict: flags.strict === true || flags["strict-system"] === true,
|
|
131
131
|
strictSystem: flags["strict-system"] === true,
|
|
132
|
-
|
|
132
|
+
// --all is the old spelling; it keeps working silently so a script
|
|
133
|
+
// written last week does not break.
|
|
134
|
+
verbose: flags.verbose === true || flags.all === true,
|
|
133
135
|
laws: flags.laws === true,
|
|
134
136
|
});
|
|
135
137
|
break;
|
package/package.json
CHANGED