@yschimke/compose-design-map 1.43.1 → 1.45.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/design-map.mjs +89 -9
- package/package.json +1 -1
package/design-map.mjs
CHANGED
|
@@ -408,6 +408,33 @@ export function variantSeeds(preview) {
|
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
/** The name a variant render goes by, for a report and for the design-map `state` slot. */
|
|
411
|
+
/**
|
|
412
|
+
* How a folded variant names itself in the reference diagnostics: `<parentId> [<axis>=<value> …]`.
|
|
413
|
+
*
|
|
414
|
+
* Not the bare `componentId` — that is the PARENT's id for a VARIANT role, so reporting a variant's
|
|
415
|
+
* stated absence under it would read as a finding about a parent that may carry a perfectly good
|
|
416
|
+
* reference.
|
|
417
|
+
*
|
|
418
|
+
* Not [variantName] either, though that is what the variant is called elsewhere: it narrows to the
|
|
419
|
+
* `state` alone whenever there is one, so two variants of a parent sharing a state and differing
|
|
420
|
+
* only in `props` produce the SAME name. These labels are map keys, so a collision silently drops
|
|
421
|
+
* one of the two stated absences — losing exactly the record this diagnostic exists to keep. The
|
|
422
|
+
* full seed vector is what distinguishes them, so the label is built from that.
|
|
423
|
+
*/
|
|
424
|
+
export function variantAbsenceId(preview) {
|
|
425
|
+
const parent = preview.catalog?.componentId ?? "(unnamed)";
|
|
426
|
+
const axes = variantSeeds(preview)
|
|
427
|
+
.map((seed) => `${seed.key}=${seed.raw}`)
|
|
428
|
+
.join(" ");
|
|
429
|
+
// `state` and `props` are both optional, so a variant CAN declare `noReference` and name no axis
|
|
430
|
+
// at all. Falling back to the bare parent id then publishes "Button — <reason>" for a parent that
|
|
431
|
+
// may hold a perfectly good reference: it reads as a finding about the parent, and says nothing
|
|
432
|
+
// about which variant the reason belongs to. The function name is what distinguishes such a
|
|
433
|
+
// variant, so it stands in for the axes it did not give.
|
|
434
|
+
const label = axes || preview.functionName || captureIdentity(preview).subject;
|
|
435
|
+
return label ? `${parent} [${label}]` : parent;
|
|
436
|
+
}
|
|
437
|
+
|
|
411
438
|
function variantName(preview, seeds) {
|
|
412
439
|
const catalog = preview.catalog;
|
|
413
440
|
const cell = preview.overrides?.name;
|
|
@@ -529,29 +556,80 @@ export function projectDesignMap(previews, opts = {}) {
|
|
|
529
556
|
* however many captures it publishes.
|
|
530
557
|
*/
|
|
531
558
|
const unmappedIds = new Map();
|
|
559
|
+
/**
|
|
560
|
+
* Stated absences, keyed by a COLLISION-SAFE identity and carrying the display label separately.
|
|
561
|
+
*
|
|
562
|
+
* A component keys on its own id and a variant on its capture subject, each behind its own
|
|
563
|
+
* prefix. Both are free-form strings from different namespaces — a `@CatalogComponent(id = …)`
|
|
564
|
+
* may legally be spelled like a capture subject — so sharing one map without tagging the domain
|
|
565
|
+
* is the same collision one namespace over. Never keyed on the rendered label. The label is built by joining `key=value` pairs, and
|
|
566
|
+
* discovery splits an annotation prop at its FIRST `=` only, so a value may legally contain both
|
|
567
|
+
* a space and an `=`: `props = ["a=b c=d"]` is one prop, and renders identically to the two props
|
|
568
|
+
* `a=b` and `c=d`. Keying on that string would silently drop one of two distinct absences — the
|
|
569
|
+
* same data loss this diagnostic exists to prevent, one level subtler.
|
|
570
|
+
*/
|
|
532
571
|
const statedAbsentIds = new Map();
|
|
572
|
+
/** Capture subjects whose absence is stated — a variant is named by subject, not by component. */
|
|
573
|
+
const referencelessSubjects = new Set();
|
|
574
|
+
/** Component ids whose absence is stated, for the componentId-keyed ambiguity filter below. */
|
|
575
|
+
const statedAbsentComponentIds = new Set();
|
|
533
576
|
for (const preview of previews) {
|
|
534
577
|
const catalog = preview.catalog;
|
|
535
|
-
if (!catalog || catalog.
|
|
578
|
+
if (!catalog || catalog.reference) continue;
|
|
536
579
|
if (isVariantCapture(preview)) continue;
|
|
580
|
+
// A `@CatalogVariant` can now state its own kit correspondence, so its absence is reported
|
|
581
|
+
// like a component's. Scanning components alone meant folding a render under a parent silently
|
|
582
|
+
// dropped its stated absence from this accounting — a catalog could lose an audit signal by
|
|
583
|
+
// restructuring, which is exactly what `statedAbsent` exists to prevent. `--strict` counts a
|
|
584
|
+
// variant's stated absence the same as a component's: someone looked, and wrote down what they
|
|
585
|
+
// found, wherever the render sits.
|
|
586
|
+
//
|
|
587
|
+
// Reported under the variant's own label, not its parent's id (`componentId` is the PARENT for
|
|
588
|
+
// a VARIANT), or a folded variant's reason would be reported against a parent that may have a
|
|
589
|
+
// perfectly good reference of its own. The label is for reading; the KEY is the capture
|
|
590
|
+
// subject, which cannot collide — see the map's own note above.
|
|
591
|
+
if (catalog.role === "VARIANT") {
|
|
592
|
+
if (!catalog.noReference) continue; // silence under a parent is the parent's business
|
|
593
|
+
const subject = captureIdentity(preview).subject;
|
|
594
|
+
statedAbsentIds.set(`subject:${subject}`, {
|
|
595
|
+
label: variantAbsenceId(preview),
|
|
596
|
+
reason: catalog.noReference,
|
|
597
|
+
});
|
|
598
|
+
// The ambiguity filter below matches on componentId, which for a VARIANT is the PARENT's --
|
|
599
|
+
// so a variant's own stated absence could not suppress its own ambiguous-mode record, and a
|
|
600
|
+
// parent carrying a good reference left it unsuppressed. `--strict --allow-stated-absence`
|
|
601
|
+
// then failed on precisely the case that flag exists to accept. A variant render has its own
|
|
602
|
+
// capture subject, so record that instead of trying to name it by component.
|
|
603
|
+
referencelessSubjects.add(subject);
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (catalog.role !== "COMPONENT") continue;
|
|
537
607
|
const id = catalog.componentId;
|
|
538
|
-
if (catalog.noReference)
|
|
539
|
-
|
|
608
|
+
if (catalog.noReference) {
|
|
609
|
+
statedAbsentIds.set(`component:${id}`, { label: id, reason: catalog.noReference });
|
|
610
|
+
statedAbsentComponentIds.add(id);
|
|
611
|
+
} else if (!statedAbsentIds.has(id)) unmappedIds.set(id, true);
|
|
540
612
|
}
|
|
541
613
|
/** Components carrying neither a reference nor a stated reason for its absence. */
|
|
542
|
-
const unmapped = [...unmappedIds.keys()].filter((id) => !
|
|
614
|
+
const unmapped = [...unmappedIds.keys()].filter((id) => !statedAbsentComponentIds.has(id));
|
|
543
615
|
/**
|
|
544
616
|
* Components whose reference is absent for a STATED reason. Reported apart from `unmapped`
|
|
545
617
|
* because they are the opposite situation: someone looked, and what they found is that the kit
|
|
546
618
|
* has nothing live to point at. Rolling the two together is what made a retired pattern read as
|
|
547
619
|
* neglect.
|
|
548
620
|
*/
|
|
549
|
-
const statedAbsent = [...statedAbsentIds].map((
|
|
550
|
-
componentId,
|
|
621
|
+
const statedAbsent = [...statedAbsentIds.values()].map(({ label, reason }) => ({
|
|
622
|
+
componentId: label,
|
|
551
623
|
reason,
|
|
552
624
|
}));
|
|
553
|
-
/**
|
|
554
|
-
|
|
625
|
+
/**
|
|
626
|
+
* Every COMPONENT that reaches no reference, however its absence was spelled — the set the
|
|
627
|
+
* ambiguity filter tests `componentIds` against. A variant's key is a capture subject rather than
|
|
628
|
+
* a component id, so it is deliberately absent here and suppressed through
|
|
629
|
+
* [referencelessSubjects] instead; putting subjects in this set would only add entries no
|
|
630
|
+
* componentId can ever equal.
|
|
631
|
+
*/
|
|
632
|
+
const referencelessIds = new Set([...unmapped, ...statedAbsentComponentIds]);
|
|
555
633
|
|
|
556
634
|
for (const preview of previews) {
|
|
557
635
|
const catalog = preview.catalog;
|
|
@@ -614,7 +692,9 @@ export function projectDesignMap(previews, opts = {}) {
|
|
|
614
692
|
// absence already reported above, and under --strict it would be a second, unfixable failure
|
|
615
693
|
// for the same component.
|
|
616
694
|
ambiguousMode: selection.ambiguous.filter(
|
|
617
|
-
(a) =>
|
|
695
|
+
(a) =>
|
|
696
|
+
!referencelessSubjects.has(a.subject) &&
|
|
697
|
+
(!a.componentIds.length || a.componentIds.some((id) => !referencelessIds.has(id))),
|
|
618
698
|
),
|
|
619
699
|
variantRenders: declarations.reduce((n, d) => n + d.renders.length, 0),
|
|
620
700
|
withSet: components.filter((c) => c.refSet).length,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yschimke/compose-design-map",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"description": "Project a compose-preview discovery manifest into design-parity's design-map.json, plus a sidecar of unresolved variant declarations. Dependency-free.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|