@hueest/xray 0.6.0 → 0.7.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/LICENSE +21 -0
- package/README.md +14 -12
- package/dist/client.d.ts +42 -51
- package/dist/client.js +134 -141
- package/dist/core.d.ts +34 -44
- package/dist/core.js +27 -33
- package/dist/css-escape-DJ-BsrNm.js +47 -0
- package/dist/hud.d.ts +7 -8
- package/dist/hud.js +5 -5
- package/dist/index.d.ts +251 -284
- package/dist/index.js +292 -338
- package/dist/measure-CUQxB-Ic.d.ts +524 -0
- package/dist/{plate-BwJUgHDc.js → plate-CXIh5_OB.js} +52 -59
- package/dist/react.core-BS2yvqeh.js +301 -0
- package/dist/react.core-BkJoCgWJ.d.ts +115 -0
- package/dist/react.d.ts +11 -13
- package/dist/react.dev.d.ts +4 -6
- package/dist/react.dev.js +54 -61
- package/dist/react.js +21 -21
- package/dist/{project-BAXKpnfF.js → serialize-Cj-EHvMv.js} +872 -1026
- package/package.json +8 -6
- package/virtual.d.ts +25 -32
- package/dist/css-escape-N7bOusGW.js +0 -52
- package/dist/react.core-DeEli0Lp.js +0 -307
- package/dist/react.core-sJfIcaXX.d.ts +0 -102
- package/dist/serialize-BqbQVxYl.d.ts +0 -564
package/dist/index.d.ts
CHANGED
|
@@ -1,39 +1,34 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
|
-
|
|
3
2
|
//#region src/diagnostics.d.ts
|
|
4
3
|
/**
|
|
5
|
-
* Dev-only capture diagnostics: a small, serializable record of why a Plate may
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Dev-only capture diagnostics: a small, serializable record of why a Plate may be incomplete or
|
|
5
|
+
* lower-fidelity, plus the one shared formatter both the browser console (`client.ts`) and the Vite
|
|
6
|
+
* server output (`index.ts`) print.
|
|
8
7
|
*
|
|
9
|
-
* Motivation: the serializer silently swallows a handful of capture omissions
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* caller can log them — without the core performing any console side effects.
|
|
8
|
+
* Motivation: the serializer silently swallows a handful of capture omissions (an unreadable
|
|
9
|
+
* cross-origin stylesheet, skipped pseudo-elements, off-screen bones dropped at the fold, an
|
|
10
|
+
* over-the-limit subtree). Without these records, "why does this skeleton look wrong?" has no
|
|
11
|
+
* answer short of reading the serializer source. Diagnostics surface those omissions as data the
|
|
12
|
+
* capture returns, so a caller can log them; the core performs no console side effects.
|
|
15
13
|
*
|
|
16
|
-
* Strictly DEV-ONLY (
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* byte-identical to a capture that produced no diagnostics).
|
|
14
|
+
* Strictly DEV-ONLY (ADR 0024, ADR 0008): this type is NOT a public package export and MUST NEVER
|
|
15
|
+
* reach the persisted Plate JSON, the `GatedPlate`, or the production adapter. It rides on the
|
|
16
|
+
* TRANSIENT `ViewIR.diagnostics` field only, which `captureView` populates and the dev client
|
|
17
|
+
* strips before posting (so committed `plates/<name>.json` stays byte-identical to a capture that
|
|
18
|
+
* produced no diagnostics).
|
|
22
19
|
*
|
|
23
|
-
* Centralized on purpose:
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* so their warning text cannot drift.
|
|
20
|
+
* Centralized on purpose: invalid posted/committed plates (ADR 0023) reuse the same messaging, so
|
|
21
|
+
* the code table and formatter stay reusable. Browser and server share `formatDiagnostic` so their
|
|
22
|
+
* warning text cannot drift.
|
|
27
23
|
*
|
|
28
24
|
* @module
|
|
29
25
|
*/
|
|
30
26
|
/**
|
|
31
|
-
* One aggregated capture omission. Serializable on purpose: NO DOM nodes, NO
|
|
32
|
-
*
|
|
33
|
-
* optional
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* node.
|
|
27
|
+
* One aggregated capture omission. Serializable on purpose: NO DOM nodes, NO CSS bodies, NO
|
|
28
|
+
* captured text — only a stable `code`, a human message, an optional aggregate `count`, and an
|
|
29
|
+
* optional short `detail` (e.g. a plate name or a node count, never user content). `count`
|
|
30
|
+
* aggregates occurrences: there is at most one record per code per capture, never one per skipped
|
|
31
|
+
* selector or node.
|
|
37
32
|
*/
|
|
38
33
|
interface CaptureDiagnostic {
|
|
39
34
|
code: DiagnosticCode;
|
|
@@ -42,59 +37,59 @@ interface CaptureDiagnostic {
|
|
|
42
37
|
detail?: string;
|
|
43
38
|
}
|
|
44
39
|
/**
|
|
45
|
-
* The closed set of capture-omission codes. Each maps to one silent site in the
|
|
46
|
-
*
|
|
47
|
-
*
|
|
40
|
+
* The closed set of capture-omission codes. Each maps to one silent site in the serializer (or an
|
|
41
|
+
* existing oversized-capture warning folded into the same vocabulary). Kept as a const union so the
|
|
42
|
+
* message table is exhaustive.
|
|
48
43
|
*/
|
|
49
44
|
type DiagnosticCode = 'unreadable-stylesheet' | 'unreadable-import' | 'skipped-dynamic-pseudo' | 'skipped-pseudo-element' | 'unsupported-rule' | 'dropped-tag' | 'dropped-subset' | 'dropped-cropped' | 'too-large' | 'invalid-plate-file';
|
|
50
45
|
//#endregion
|
|
51
46
|
//#region src/plate.d.ts
|
|
52
|
-
type LeafKind = 'text' | 'text-block' | 'media' | 'box';
|
|
53
47
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
48
|
+
* `hidden` is EXPLICIT-ONLY (a `data-xr-bone-kind` hint or a walker `ctx.bone({ kind: 'hidden'
|
|
49
|
+
* })`), never inferred: a bone that holds its measured box in the layout but paints nothing — the
|
|
50
|
+
* space-preserving complement to `data-xr-ignore`, which drops the box entirely.
|
|
51
|
+
*/
|
|
52
|
+
type LeafKind = 'text-line' | 'text-block' | 'media' | 'box' | 'hidden';
|
|
53
|
+
/**
|
|
54
|
+
* The shipped structural capture of a component's rendered DOM — the TREE artifact a Skeleton
|
|
55
|
+
* renders from (see the glossary: xray-js.dev/glossary). A Plate carries structure ONLY (ADR 0026):
|
|
56
|
+
* its scoped CSS is an INDEPENDENT sibling artifact — the plugin serves it through Vite's own CSS
|
|
57
|
+
* pipeline (`virtual:xray/plates/<name>.css`, plus the shared `virtual:xray/base.css`, and an
|
|
58
|
+
* `export const css` on the virtual JS module for dev/programmatic reads), and the
|
|
59
|
+
* framework-neutral `core.ts` path hands it back separately (`captureElement` → `{ plate, css }`;
|
|
60
|
+
* `renderPlateHtml(plate, css)` receives it explicitly). Consumers decide how to render either
|
|
61
|
+
* part. Still self-contained in ADR 0003's sense: both artifacts originate from the capture and
|
|
62
|
+
* depend on nothing in the consumer's build. Built from the RenderNode tree at plugin load (see
|
|
63
|
+
* chunk.ts), so the adapter injects strings instead of walking a tree on the latency-critical first
|
|
64
|
+
* paint — and no per-framework serializer ships to the client (ADR 0008).
|
|
69
65
|
*/
|
|
70
66
|
interface Plate {
|
|
71
67
|
v: number;
|
|
72
68
|
/** Capture name; comes from the virtual-module specifier, never typed at a call site. */
|
|
73
69
|
name: string;
|
|
74
70
|
/**
|
|
75
|
-
* The root's children, pre-serialized (ADR 0008): a stitch-free run is one
|
|
76
|
-
*
|
|
77
|
-
*
|
|
71
|
+
* The root's children, pre-serialized (ADR 0008): a stitch-free run is one HTML `string`; a
|
|
72
|
+
* stitch is `{ r: name }`; a stitch-bearing element is `{ c: className, k: chunk[] }`. Null until
|
|
73
|
+
* first capture.
|
|
78
74
|
*/
|
|
79
75
|
chunks: Chunk[] | null;
|
|
80
76
|
/** The root node's own content classes (its inherited font context), if any. */
|
|
81
77
|
rootCls?: string;
|
|
82
78
|
/**
|
|
83
|
-
* Child plates named by the chunks' `{ r }` stitches, resolved through the
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
79
|
+
* Child plates named by the chunks' `{ r }` stitches, resolved through the import graph (ADR
|
|
80
|
+
* 0006). Populated by the loader's generated imports, never serialized into the plates JSON;
|
|
81
|
+
* absent in production when there are no stitches. In dev the live store takes precedence, so
|
|
82
|
+
* this need not be present.
|
|
87
83
|
*/
|
|
88
84
|
refs?: Record<string, Plate>;
|
|
89
85
|
}
|
|
90
86
|
/**
|
|
91
|
-
* One serialized child of a plate root (or of a stitch-bearing node): a
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* mounts a child plate at each stitch, and repeats each template (ADR 0008).
|
|
87
|
+
* One serialized child of a plate root (or of a stitch-bearing node): a stitch-free HTML run
|
|
88
|
+
* (`string`), a stitch to a child plate (`{ r: name }`), a stitch-bearing element kept real so its
|
|
89
|
+
* inner stitches can mount (`{ c: className, k: chunk[] }`), or a templated uniform run (`{ t:
|
|
90
|
+
* html, n }`): the cell's HTML once plus a repeat count the adapter expands to N copies (Template,
|
|
91
|
+
* ADR 0022). The framework-neutral render contract: an adapter injects the strings, mounts a child
|
|
92
|
+
* plate at each stitch, and repeats each template (ADR 0008).
|
|
98
93
|
*/
|
|
99
94
|
type Chunk = string | {
|
|
100
95
|
r: string;
|
|
@@ -106,13 +101,12 @@ type Chunk = string | {
|
|
|
106
101
|
n: number;
|
|
107
102
|
};
|
|
108
103
|
/**
|
|
109
|
-
* A plate after the in-browser Serialize (emit +
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* load (`serializePlate`). Never shipped to the client.
|
|
104
|
+
* A plate after the in-browser Serialize (emit + gate + classify) but before chunk serialization:
|
|
105
|
+
* the build-time RenderNode tree plus its scoped CSS. Produced by `serializePlateIR` (the
|
|
106
|
+
* structural part of `StoredPlate`) and the single-View `capture`, then serialized into the shipped
|
|
107
|
+
* `Plate` (chunks) at load (`serializePlate`). Never shipped to the client.
|
|
114
108
|
*/
|
|
115
|
-
interface
|
|
109
|
+
interface GatedPlate {
|
|
116
110
|
v: number;
|
|
117
111
|
name: string;
|
|
118
112
|
tree: RenderNode | null;
|
|
@@ -124,37 +118,38 @@ interface PlateNode {
|
|
|
124
118
|
/** Present on paint leaves; absent on layout containers. */
|
|
125
119
|
leaf?: LeafKind;
|
|
126
120
|
/**
|
|
127
|
-
* A stitch: render the named child plate's skeleton here instead of own
|
|
128
|
-
*
|
|
129
|
-
*
|
|
121
|
+
* A stitch: render the named child plate's skeleton here instead of own structure (ADR 0006).
|
|
122
|
+
* Mutually exclusive with `leaf`/`kids`. Resolved against `Plate.refs` (prod) or the live store
|
|
123
|
+
* (dev).
|
|
130
124
|
*/
|
|
131
125
|
ref?: string;
|
|
132
126
|
/**
|
|
133
|
-
* A templated uniform run (
|
|
134
|
-
*
|
|
135
|
-
*
|
|
127
|
+
* A templated uniform run (Template, ADR 0022): render this node's subtree `count` times. The
|
|
128
|
+
* serializer emits the subtree's HTML once plus the count; the adapter expands it to N copies at
|
|
129
|
+
* render. Absent on ordinary nodes.
|
|
136
130
|
*/
|
|
137
131
|
count?: number;
|
|
138
132
|
kids?: PlateNode[];
|
|
139
133
|
}
|
|
140
134
|
/**
|
|
141
|
-
* A node in the shipped tree, after classify (ADR 0007). Carries the
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* `nodeKey`) is gone. `cls` is a space-joined class-token list.
|
|
135
|
+
* A node in the shipped tree, after classify (ADR 0007). Carries the content-addressed style
|
|
136
|
+
* classes it needs instead of an id — the CSS targets those classes, not a per-node `data-xr`
|
|
137
|
+
* value, so no per-node scoping key is needed. `cls` is a space-joined class-token list.
|
|
145
138
|
*/
|
|
146
139
|
interface RenderNode {
|
|
147
140
|
leaf?: LeafKind;
|
|
148
141
|
ref?: string;
|
|
149
142
|
cls?: string;
|
|
150
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* A templated uniform run (Template, ADR 0022): serialize this subtree once + a count the adapter
|
|
145
|
+
* repeats.
|
|
146
|
+
*/
|
|
151
147
|
count?: number;
|
|
152
148
|
kids?: RenderNode[];
|
|
153
149
|
}
|
|
154
150
|
/**
|
|
155
|
-
* A captured style rule in id form — selectors are generated
|
|
156
|
-
*
|
|
157
|
-
* targets the plate root.
|
|
151
|
+
* A captured style rule in id form — selectors are generated downstream, so Serialize can remap
|
|
152
|
+
* node ids without string surgery. `ids: []` targets the plate root.
|
|
158
153
|
*/
|
|
159
154
|
interface PlateRule {
|
|
160
155
|
ids: number[];
|
|
@@ -168,12 +163,10 @@ interface PlateRule {
|
|
|
168
163
|
order: number;
|
|
169
164
|
}
|
|
170
165
|
/**
|
|
171
|
-
* One capture of a component within one viewport view — `[min, max)` in px,
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* framework-neutral per-View capture shape, exported for consumers reading the
|
|
176
|
-
* id-form projection directly.
|
|
166
|
+
* One capture of a component within one viewport view — `[min, max)` in px, either side open-ended
|
|
167
|
+
* when undefined. The id-form `{ tree, rules }` shape `emit` projects a measured View into (ADR
|
|
168
|
+
* 0004). The disk/wire shape is `StoredPlate` (below); this is the framework-neutral per-View
|
|
169
|
+
* capture shape, exported for consumers reading the id-form projection directly.
|
|
177
170
|
*/
|
|
178
171
|
interface ViewCapture {
|
|
179
172
|
/** Viewport width at capture time. */
|
|
@@ -183,37 +176,32 @@ interface ViewCapture {
|
|
|
183
176
|
tree: PlateNode;
|
|
184
177
|
rules: PlateRule[];
|
|
185
178
|
/**
|
|
186
|
-
* TRANSIENT, dev-only capture diagnostics (why this view may be lower
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* envelope keeps it in a SIBLING field that never reaches the stored artifact
|
|
193
|
-
* (ADR 0008). Absent in production (the prod adapter ships no capture). See
|
|
194
|
-
* diagnostics.ts and ADR 0008.
|
|
179
|
+
* TRANSIENT, dev-only capture diagnostics (why this view may be lower fidelity). Captured in the
|
|
180
|
+
* browser (mirrored onto `ViewIR.diagnostics`); the dev client logs it and posts it alongside the
|
|
181
|
+
* `StoredPlate` (so the Vite server can print the same text). It MUST NEVER be persisted: it is
|
|
182
|
+
* not part of the committed `StoredPlate` disk shape, the validator does not carry it, and the
|
|
183
|
+
* POST envelope keeps it in a SIBLING field that never reaches the stored artifact (ADR 0008).
|
|
184
|
+
* Absent in production (the prod adapter ships no capture). See diagnostics.ts and ADR 0008.
|
|
195
185
|
*/
|
|
196
186
|
diagnostics?: CaptureDiagnostic[];
|
|
197
187
|
}
|
|
198
188
|
/**
|
|
199
|
-
* The committed `plates/<name>.json` shape
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* at module-load, leaving the Tailwind/Bundle passthrough a clean gen-side seam.
|
|
207
|
-
* Replaces `PlateFile` (+ `ViewCapture`) on disk and on the `/__xray` wire.
|
|
189
|
+
* The committed `plates/<name>.json` shape (ADR 0022): the whole Plate already projected in the
|
|
190
|
+
* browser (the in-browser Serialize folds emit + the multi-View gating + classify in one session),
|
|
191
|
+
* so disk holds ONE gated, content-addressed tree + css rather than per-View captures assembled at
|
|
192
|
+
* load. It is a `GatedPlate` (the structural `{tree, css}` `serializePlate` consumes) PLUS the
|
|
193
|
+
* `breakpoints` the HUD coverage reads — the analysis-only geometry never crosses the wire (ADR
|
|
194
|
+
* 0021). The gen-side runs `serializePlate(stored) → Plate` (chunks) at module-load, leaving the
|
|
195
|
+
* Tailwind/Bundle passthrough a clean gen-side seam.
|
|
208
196
|
*/
|
|
209
|
-
interface StoredPlate extends
|
|
197
|
+
interface StoredPlate extends GatedPlate {
|
|
210
198
|
/** Width thresholds (view boundaries); the HUD coverage derives its bands from these. */
|
|
211
199
|
breakpoints: number[];
|
|
212
200
|
}
|
|
213
201
|
/**
|
|
214
|
-
* A dev-only sticky boolean toggle (get/set/subscribe), persisted per tab.
|
|
215
|
-
*
|
|
216
|
-
*
|
|
202
|
+
* A dev-only sticky boolean toggle (get/set/subscribe), persisted per tab. Used for both "light
|
|
203
|
+
* box" (render every `<Skeleton>` regardless of readiness) and "capture" (whether browsing
|
|
204
|
+
* re-captures plates).
|
|
217
205
|
*/
|
|
218
206
|
interface ToggleStore {
|
|
219
207
|
get: () => boolean;
|
|
@@ -221,19 +209,17 @@ interface ToggleStore {
|
|
|
221
209
|
subscribe: (onChange: () => void) => () => void;
|
|
222
210
|
}
|
|
223
211
|
/**
|
|
224
|
-
* Dev-only store of the freshest plate per name, updated in place when a
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* boundary and re-fire the capture, a feedback loop.
|
|
212
|
+
* Dev-only store of the freshest plate per name, updated in place when a capture lands.
|
|
213
|
+
* `<Skeleton>` reads through it so a new plate re-renders the site rather than reloading the module
|
|
214
|
+
* — which would remount the capture boundary and re-fire the capture, a feedback loop.
|
|
228
215
|
*/
|
|
229
216
|
interface PlateStore {
|
|
230
217
|
get: (name: string) => Plate | undefined;
|
|
231
218
|
subscribe: (name: string, onChange: () => void) => () => void;
|
|
232
219
|
/**
|
|
233
|
-
* Register a plate imported via the ref graph if the store has none yet — so a
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* live capture.
|
|
220
|
+
* Register a plate imported via the ref graph if the store has none yet — so a stitch resolves
|
|
221
|
+
* even before its child is re-captured, and survives a parent hot-swap (whose pushed plate
|
|
222
|
+
* carries no resolved `refs`). Never overwrites a live capture.
|
|
237
223
|
*/
|
|
238
224
|
seed: (name: string, plate: Plate) => void;
|
|
239
225
|
}
|
|
@@ -243,34 +229,31 @@ interface Span {
|
|
|
243
229
|
max?: number;
|
|
244
230
|
}
|
|
245
231
|
/**
|
|
246
|
-
* Dev-only Fixture store (ADR 0015): stored render-INPUT `data` per Plate,
|
|
247
|
-
*
|
|
248
|
-
*
|
|
232
|
+
* Dev-only Fixture store (ADR 0015): stored render-INPUT `data` per Plate, recorded to and replayed
|
|
233
|
+
* from a `plates/<name>.fixture.json` sidecar. Entirely dev-only — devalue and this store never
|
|
234
|
+
* reach the production bundle.
|
|
235
|
+
*
|
|
236
|
+
* Two cooperating halves, both flowing through the render-prop `data` seam (ADR 0014):
|
|
249
237
|
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* it). `has(name)` reports whether a Fixture exists (HUD coverage marker), and
|
|
258
|
-
* `read(name)` returns the parsed Fixture `data` for Replay to substitute in
|
|
259
|
-
* front of the render prop.
|
|
238
|
+
* - The dev adapter (`react.dev.tsx`) calls `register(name, data)` at `loading=false` with the live
|
|
239
|
+
* `data` it is about to render, so the latest recordable input for each mounted Plate is always
|
|
240
|
+
* in scope for Record.
|
|
241
|
+
* - `record(name)` serializes that registered live `data` with devalue and POSTs it to the sidecar
|
|
242
|
+
* (the HUD button and the `first-ready`/`always` modes call it). `has(name)` reports whether a
|
|
243
|
+
* Fixture exists (HUD coverage marker), and `read(name)` returns the parsed Fixture `data` for
|
|
244
|
+
* Replay to substitute in front of the render prop.
|
|
260
245
|
*
|
|
261
|
-
* The store holds DECODED values (the dev client owns the devalue parse, since it
|
|
262
|
-
*
|
|
263
|
-
* devalue string.
|
|
246
|
+
* The store holds DECODED values (the dev client owns the devalue parse, since it has the live
|
|
247
|
+
* runtime); only the on-disk sidecar and the wire carry the opaque devalue string.
|
|
264
248
|
*/
|
|
265
249
|
interface FixtureStore {
|
|
266
250
|
/**
|
|
267
|
-
* Record the latest live `data` the dev adapter is about to render for `name`,
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* mounted, the live value is the most-recently-registered still-mounted instance.
|
|
251
|
+
* Record the latest live `data` the dev adapter is about to render for `name`, so Record always
|
|
252
|
+
* has the current recordable input in scope. A `register` with the same value is cheap; the
|
|
253
|
+
* adapter calls it on every ready render. Returns a disposer the adapter runs on unmount so an
|
|
254
|
+
* unmounted Skeleton's `data` never lingers in the store: an unmounted instance must never be
|
|
255
|
+
* recorded or shadow a still-mounted one (ADR 0015). With several Skeletons of the same plate
|
|
256
|
+
* name mounted, the live value is the most-recently-registered still-mounted instance.
|
|
274
257
|
*/
|
|
275
258
|
register: (name: string, data: unknown) => () => void;
|
|
276
259
|
/** True once a recorded Fixture exists for `name` (drives the HUD coverage marker). */
|
|
@@ -280,25 +263,22 @@ interface FixtureStore {
|
|
|
280
263
|
data: unknown;
|
|
281
264
|
} | undefined;
|
|
282
265
|
/**
|
|
283
|
-
* Serialize the registered live `data` for `name` with devalue and POST it to
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* registered (a plain-children `<Skeleton>` has none).
|
|
266
|
+
* Serialize the registered live `data` for `name` with devalue and POST it to the sidecar.
|
|
267
|
+
* Returns a promise that resolves once the write is server-confirmed (so the HUD can surface a
|
|
268
|
+
* failed Record). Throws loudly when the live `data` is unrecordable (devalue's contract) or no
|
|
269
|
+
* live `data` was registered (a plain-children `<Skeleton>` has none).
|
|
288
270
|
*/
|
|
289
271
|
record: (name: string) => Promise<void>;
|
|
290
272
|
/**
|
|
291
|
-
* Record every Skeleton that currently has live `data` registered (the HUD's
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* rejected write surfaces to the HUD.
|
|
273
|
+
* Record every Skeleton that currently has live `data` registered (the HUD's single "Record
|
|
274
|
+
* fixture" button — one click captures the inputs of all mounted render-prop Skeletons). Resolves
|
|
275
|
+
* once all writes are server-confirmed; a rejected write surfaces to the HUD.
|
|
295
276
|
*/
|
|
296
277
|
recordAll: () => Promise<void>;
|
|
297
278
|
/**
|
|
298
|
-
* Seed the store from the on-disk sidecars on boot: a `{ <name>: devalueString }`
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* session survives a re-seed.
|
|
279
|
+
* Seed the store from the on-disk sidecars on boot: a `{ <name>: devalueString }` map (the opaque
|
|
280
|
+
* strings the server serves). The client decodes each with devalue. Existing entries are not
|
|
281
|
+
* clobbered, so a Fixture recorded this session survives a re-seed.
|
|
302
282
|
*/
|
|
303
283
|
seed: (encoded: Record<string, string>) => void;
|
|
304
284
|
/** React subscription so the HUD re-renders when a Fixture is recorded or seeded. */
|
|
@@ -310,10 +290,10 @@ interface PlateCoverage {
|
|
|
310
290
|
spans: Span[];
|
|
311
291
|
}
|
|
312
292
|
/**
|
|
313
|
-
* Dev-only per-plate coverage, sourced from the committed plate files (not the
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
293
|
+
* Dev-only per-plate coverage, sourced from the committed plate files (not the session): the
|
|
294
|
+
* breakpoints detected so far and which view spans are captured. The HUD renders every band —
|
|
295
|
+
* covered ones lit, the rest dim — and the capture-all sweep derives its target widths from the
|
|
296
|
+
* breakpoints.
|
|
317
297
|
*/
|
|
318
298
|
interface CoverageStore {
|
|
319
299
|
get: () => ReadonlyMap<string, PlateCoverage>;
|
|
@@ -321,9 +301,9 @@ interface CoverageStore {
|
|
|
321
301
|
subscribe: (onChange: () => void) => () => void;
|
|
322
302
|
}
|
|
323
303
|
/**
|
|
324
|
-
* Dev-only capture hook, installed by the dev client before the app boots.
|
|
325
|
-
*
|
|
326
|
-
*
|
|
304
|
+
* Dev-only capture hook, installed by the dev client before the app boots. Absent in production
|
|
305
|
+
* builds, so `<Skeleton>` carries zero capture weight. `captured` returns a dispose callback (stop
|
|
306
|
+
* observing this site).
|
|
327
307
|
*/
|
|
328
308
|
interface CaptureHook {
|
|
329
309
|
captured: (name: string, el: Element, opts?: {
|
|
@@ -349,20 +329,18 @@ interface CaptureHook {
|
|
|
349
329
|
/** Dev-only per-plate breakpoints + captured spans, from disk; fed by the bootstrap. */
|
|
350
330
|
coverage?: CoverageStore;
|
|
351
331
|
/**
|
|
352
|
-
* Replay toggle (ADR 0015): when active, the dev adapter substitutes a recorded
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
* `missing-only`) is carried alongside in `replayMode`.
|
|
332
|
+
* Replay toggle (ADR 0015): when active, the dev adapter substitutes a recorded Fixture's `data`
|
|
333
|
+
* for the live `data` at `loading=false`. The boolean is the coarse on/off the HUD owns per tab;
|
|
334
|
+
* the per-mode policy (`prefer` vs `missing-only`) is carried alongside in `replayMode`.
|
|
356
335
|
*/
|
|
357
336
|
replay?: ToggleStore;
|
|
358
337
|
/** Dev-only Replay policy: how a Fixture substitutes for live `data` (ADR 0015). */
|
|
359
338
|
replayMode?: 'prefer' | 'missing-only';
|
|
360
339
|
/**
|
|
361
|
-
* Dev-only Record policy (ADR 0015): when the dev adapter records the live
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
* render.
|
|
340
|
+
* Dev-only Record policy (ADR 0015): when the dev adapter records the live `data` it is about to
|
|
341
|
+
* render. `'manual'` (the default) records nothing automatically — only the HUD button does;
|
|
342
|
+
* `'first-ready'` records once when a Skeleton goes ready and has no Fixture yet; `'always'`
|
|
343
|
+
* records on every ready render.
|
|
366
344
|
*/
|
|
367
345
|
recordMode?: 'manual' | 'first-ready' | 'always';
|
|
368
346
|
/** Dev-only Fixture record/replay store (ADR 0015); never present in production. */
|
|
@@ -374,40 +352,36 @@ declare global {
|
|
|
374
352
|
//#endregion
|
|
375
353
|
//#region src/chunk.d.ts
|
|
376
354
|
/**
|
|
377
|
-
* Distinct plate names referenced by the tree's stitches, in first-seen order.
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* `mergeViews`/`addCapture` removal (the multi-View merge/gate now lives in the
|
|
383
|
-
* in-browser `serializePlateIR`, project.ts).
|
|
355
|
+
* Distinct plate names referenced by the tree's stitches, in first-seen order. Walks a `RenderNode`
|
|
356
|
+
* tree (the stored, post-classify shape), so it drives the gen-side stitch import graph straight
|
|
357
|
+
* off a `StoredPlate.tree` (`renderPlateModule`, index.ts). The one node-side primitive needed at
|
|
358
|
+
* build time; multi-View gating happens in the in-browser `serializePlateIR` (serialize.ts, ADR
|
|
359
|
+
* 0022).
|
|
384
360
|
*/
|
|
385
361
|
declare function collectRefs(tree: RenderNode | null): string[];
|
|
386
362
|
//#endregion
|
|
387
363
|
//#region src/name.d.ts
|
|
388
364
|
/**
|
|
389
|
-
* Plate-name validation, shared by the Vite plugin (`index.ts`) and the
|
|
390
|
-
*
|
|
365
|
+
* Plate-name validation, shared by the Vite plugin (`index.ts`) and the Node-only POST validator
|
|
366
|
+
* (`validate.ts`).
|
|
391
367
|
*
|
|
392
|
-
* Lives in its own dependency-free module so `validate.ts` can reuse it without
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
* ref-graph emitter all gate on.
|
|
368
|
+
* Lives in its own dependency-free module so `validate.ts` can reuse it without importing the
|
|
369
|
+
* plugin entry (which would be a cycle), and so the regex stays the single source of truth for what
|
|
370
|
+
* a plate name may be — the same rule the loader, the file watcher, the coverage reader, the POST
|
|
371
|
+
* endpoint, and the ref-graph emitter all gate on.
|
|
397
372
|
*
|
|
398
373
|
* @module
|
|
399
374
|
*/
|
|
400
375
|
/**
|
|
401
376
|
* Names come from import specifiers and POST bodies — keep them inside the plates dir.
|
|
402
377
|
*
|
|
403
|
-
* Each path segment is `\w+(-\w+)*` — word chars with single interior hyphens,
|
|
404
|
-
* never
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
* distinct prefixes (else e.g. `a/-b` and `a-/b` would both encode to `a---b`).
|
|
378
|
+
* Each path segment is `\w+(-\w+)*` — word chars with single interior hyphens, never doubled and
|
|
379
|
+
* never at a segment edge — and segments are joined by `/`. The class-prefix encoder
|
|
380
|
+
* (`classify.ts`) renders `/` as `--` to keep nested names readable, and that prefix is the
|
|
381
|
+
* isolation boundary across stitched plates (ADR 0007), so the encoding must be injective. Banning
|
|
382
|
+
* a literal `--` and any edge hyphen guarantees no `-` ever abuts a `/`, so every `--` in the
|
|
383
|
+
* prefix came from exactly one `/` — making distinct names always map to distinct prefixes (else
|
|
384
|
+
* e.g. `a/-b` and `a-/b` would both encode to `a---b`).
|
|
411
385
|
*
|
|
412
386
|
* @internal
|
|
413
387
|
*/
|
|
@@ -418,32 +392,38 @@ declare function isValidPlateName(name: string): boolean;
|
|
|
418
392
|
* Options for `xrayVitePlugin()`.
|
|
419
393
|
*
|
|
420
394
|
* Fixed conventions are intentionally not configurable: Plate names come from
|
|
421
|
-
* `virtual:xray/plates/<name>`, files live under `platesDir`, and responsive
|
|
422
|
-
*
|
|
423
|
-
* page evaluates.
|
|
395
|
+
* `virtual:xray/plates/<name>`, files live under `platesDir`, and responsive breakpoints are
|
|
396
|
+
* derived from captured CSS plus the `matchMedia` queries the page evaluates.
|
|
424
397
|
*/
|
|
425
398
|
interface XrayOptions {
|
|
426
|
-
/**
|
|
399
|
+
/**
|
|
400
|
+
* Directory where Plate JSON files are written and read, relative to the Vite root. Default:
|
|
401
|
+
* `plates`.
|
|
402
|
+
*/
|
|
427
403
|
platesDir?: string;
|
|
428
404
|
/** Mount the dev HUD for coverage, Capture, Light Box, and capture-all controls. Default: `false`. */
|
|
429
405
|
hud?: boolean;
|
|
430
406
|
/**
|
|
431
|
-
* Initial state of the browse-time Capture toggle in dev. Keep this off for
|
|
432
|
-
*
|
|
433
|
-
* Capture on per tab. Default: `false`.
|
|
407
|
+
* Initial state of the browse-time Capture toggle in dev. Keep this off for normal browsing and
|
|
408
|
+
* let the HUD, capture-all flow, or `?xray-capture` turn Capture on per tab. Default: `false`.
|
|
434
409
|
*/
|
|
435
410
|
capture?: boolean;
|
|
436
|
-
/**
|
|
411
|
+
/**
|
|
412
|
+
* Settle delay in milliseconds before capturing a ready Skeleton; override one boundary with the
|
|
413
|
+
* `delay` prop. Default: `200`.
|
|
414
|
+
*/
|
|
437
415
|
delay?: number;
|
|
438
|
-
/**
|
|
416
|
+
/**
|
|
417
|
+
* Upper bound in milliseconds on waiting for a busy subtree to go quiet before capturing anyway.
|
|
418
|
+
* Default: `5000`.
|
|
419
|
+
*/
|
|
439
420
|
settleCap?: number;
|
|
440
421
|
/**
|
|
441
|
-
* Collect-stage limits (ADR 0022): refuse to capture a boundary whose measured
|
|
442
|
-
*
|
|
443
|
-
* `
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
* boundary with the flat `maxNodes`/`maxBreadth`/`maxDepth` `<Skeleton>` props.
|
|
422
|
+
* Collect-stage limits (ADR 0022): refuse to capture a boundary whose measured tree exceeds any
|
|
423
|
+
* axis — too big means the Skeleton sits too high in the tree. `maxNodes` caps total surviving
|
|
424
|
+
* nodes (default `4096`), `maxBreadth` the widest level (default `512`), `maxDepth` the nesting
|
|
425
|
+
* depth (default `32`). Omit `maxNodes` and it derives as `round(maxBreadth × maxDepth / 4)`.
|
|
426
|
+
* Override one boundary with the flat `maxNodes`/`maxBreadth`/`maxDepth` `<Skeleton>` props.
|
|
447
427
|
*/
|
|
448
428
|
collect?: {
|
|
449
429
|
maxNodes?: number;
|
|
@@ -452,31 +432,30 @@ interface XrayOptions {
|
|
|
452
432
|
};
|
|
453
433
|
/**
|
|
454
434
|
* Dev-only Fixtures (ADR 0015): record a `<Skeleton>`'s render-input `data` to a
|
|
455
|
-
* `plates/<name>.fixture.json` sidecar and replay it in place of live data so
|
|
456
|
-
*
|
|
457
|
-
*
|
|
435
|
+
* `plates/<name>.fixture.json` sidecar and replay it in place of live data so capture is
|
|
436
|
+
* deterministic. Boot state only — the HUD is per-tab authority, just like `capture`. Entirely
|
|
437
|
+
* dev-only; nothing here ships in production.
|
|
458
438
|
*/
|
|
459
439
|
fixtures?: {
|
|
460
440
|
/**
|
|
461
|
-
* When to record the live `data`: `'manual'` (HUD button only — the default,
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
441
|
+
* When to record the live `data`: `'manual'` (HUD button only — the default, so live app data
|
|
442
|
+
* is never silently committed), `'first-ready'` (record once the first time a Skeleton goes
|
|
443
|
+
* ready with no Fixture yet), or `'always'` (record on every ready render). Default:
|
|
444
|
+
* `'manual'`.
|
|
465
445
|
*/
|
|
466
446
|
record?: 'manual' | 'first-ready' | 'always';
|
|
467
447
|
/**
|
|
468
|
-
* Whether to replay a recorded Fixture in place of live `data`: `false` (off,
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
* capturable). Default: `false`.
|
|
448
|
+
* Whether to replay a recorded Fixture in place of live `data`: `false` (off, the default),
|
|
449
|
+
* `'prefer'` (always use the Fixture when one exists), or `'missing-only'` (use the Fixture
|
|
450
|
+
* only when live `data` is nullish — `missing ≡ nullish`, so an empty array/object is present
|
|
451
|
+
* and stays capturable). Default: `false`.
|
|
473
452
|
*/
|
|
474
453
|
replay?: false | 'prefer' | 'missing-only';
|
|
475
454
|
};
|
|
476
455
|
}
|
|
477
456
|
/**
|
|
478
|
-
* Create the Vite plugins that resolve committed Plates and, during dev,
|
|
479
|
-
*
|
|
457
|
+
* Create the Vite plugins that resolve committed Plates and, during dev, capture new Plates from
|
|
458
|
+
* rendered `<Skeleton>` boundaries.
|
|
480
459
|
*
|
|
481
460
|
* Typical setup:
|
|
482
461
|
*
|
|
@@ -489,8 +468,10 @@ interface XrayOptions {
|
|
|
489
468
|
* ```
|
|
490
469
|
*
|
|
491
470
|
* Returns two plugins:
|
|
471
|
+
*
|
|
492
472
|
* - `xray:data` resolves `virtual:xray/plates/<name>` from committed Plate files in dev and build.
|
|
493
|
-
* - `xray:dev` runs only during `vite serve`; it injects the capture client, receives Captures,
|
|
473
|
+
* - `xray:dev` runs only during `vite serve`; it injects the capture client, receives Captures,
|
|
474
|
+
* writes Plate files, and hot-updates the page.
|
|
494
475
|
*/
|
|
495
476
|
declare function xrayVitePlugin(options?: XrayOptions): Plugin[];
|
|
496
477
|
interface CaptureRequest {
|
|
@@ -507,29 +488,26 @@ interface CaptureResponse {
|
|
|
507
488
|
interface PlateBroadcaster {
|
|
508
489
|
send(payload: unknown): void;
|
|
509
490
|
}
|
|
510
|
-
/** The subset of Vite's logger
|
|
491
|
+
/** The subset of Vite's logger used here; it preserves the formatted string verbatim. */
|
|
511
492
|
interface ServerLogger {
|
|
512
493
|
warn(message: string): void;
|
|
513
494
|
}
|
|
514
495
|
interface CaptureServer {
|
|
515
496
|
ws: PlateBroadcaster;
|
|
516
497
|
/**
|
|
517
|
-
* Vite's logger (`server.config.logger`). When present its `warn` prints the
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
* used, so server output matches the browser console exactly.
|
|
498
|
+
* Vite's logger (`server.config.logger`). When present its `warn` prints the diagnostic text to
|
|
499
|
+
* the server output; otherwise `console.warn` is the fallback. Either way the SAME shared
|
|
500
|
+
* formatter (diagnostics.ts) is used, so server output matches the browser console exactly.
|
|
521
501
|
*/
|
|
522
502
|
config?: {
|
|
523
503
|
logger?: ServerLogger;
|
|
524
504
|
};
|
|
525
505
|
/**
|
|
526
|
-
* The subset of Vite's dev server needed to keep BOTH of a plate's virtual
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
* `
|
|
530
|
-
*
|
|
531
|
-
* hard `vite` import at the type level beyond the `Plugin` type already
|
|
532
|
-
* used elsewhere).
|
|
506
|
+
* The subset of Vite's dev server needed to keep BOTH of a plate's virtual modules fresh after a
|
|
507
|
+
* plate file changes (capture POST, hand edit, git checkout). Optional — absent in the unit tests
|
|
508
|
+
* that stub a minimal `server`. `getModuleById`/`invalidateModule`/`reloadModule` mirror
|
|
509
|
+
* `ViteDevServer`'s real shape (a subset, so this module stays free of a hard `vite` import at
|
|
510
|
+
* the type level beyond the `Plugin` type already used elsewhere).
|
|
533
511
|
*/
|
|
534
512
|
moduleGraph?: {
|
|
535
513
|
getModuleById(id: string): unknown;
|
|
@@ -538,10 +516,9 @@ interface CaptureServer {
|
|
|
538
516
|
reloadModule?(mod: unknown): Promise<void>;
|
|
539
517
|
}
|
|
540
518
|
/**
|
|
541
|
-
* Per-plate breakpoints + derived coverage bands, read from the committed
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
* widths in one session, so its coverage is complete).
|
|
519
|
+
* Per-plate breakpoints + derived coverage bands, read from the committed StoredPlate files. The
|
|
520
|
+
* `spans` derive from each plate's `breakpoints` (ADR 0022: a StoredPlate sweeps all widths in one
|
|
521
|
+
* session, so its coverage is complete).
|
|
545
522
|
*
|
|
546
523
|
* @internal
|
|
547
524
|
*/
|
|
@@ -550,53 +527,47 @@ declare function readCoverage(dir: string): Record<string, {
|
|
|
550
527
|
spans: Span[];
|
|
551
528
|
}>;
|
|
552
529
|
/**
|
|
553
|
-
* All recorded Fixtures as `{ <name>: <devalueString> }`, read from the
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
* these distinct from Plate files (`isValidPlateName` rejects the dotted name, so
|
|
530
|
+
* All recorded Fixtures as `{ <name>: <devalueString> }`, read from the `<name>.fixture.json`
|
|
531
|
+
* sidecars (ADR 0015). The map values are the OPAQUE devalue strings exactly as written — the node
|
|
532
|
+
* side never parses them; the browser dev client owns the devalue decode. The `.fixture.json`
|
|
533
|
+
* suffix keeps these distinct from Plate files (`isValidPlateName` rejects the dotted name, so
|
|
558
534
|
* `readCoverage` never picks them up — Fixtures never leak into Plate coverage).
|
|
559
535
|
*
|
|
560
536
|
* @internal
|
|
561
537
|
*/
|
|
562
538
|
declare function readFixtures(dir: string): Record<string, string>;
|
|
563
539
|
/**
|
|
564
|
-
* Write a recorded Fixture sidecar (ADR 0015). Reuses the capture endpoint's
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
* so the persisted Plate stays purely structural.
|
|
540
|
+
* Write a recorded Fixture sidecar (ADR 0015). Reuses the capture endpoint's front-door checks
|
|
541
|
+
* (method, content type, declared/streamed size cap) and the `isValidPlateName` gate, but treats
|
|
542
|
+
* the devalue `data` payload as OPAQUE TEXT: it is validated only as a string and written verbatim,
|
|
543
|
+
* never eval'd or parsed server-side. The sidecar lives NEXT TO the Plate but is never merged into
|
|
544
|
+
* it, so the persisted Plate stays purely structural.
|
|
570
545
|
*
|
|
571
546
|
* @internal
|
|
572
547
|
*/
|
|
573
548
|
declare function handleFixturePost(req: CaptureRequest, res: CaptureResponse, fixturePath: (name: string) => string): void;
|
|
574
549
|
/**
|
|
575
|
-
* Validate a posted `StoredPlate` envelope, write it AS-IS, and hot-swap the
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
* `
|
|
579
|
-
*
|
|
580
|
-
* never reaches the stored artifact (ADR 0008).
|
|
550
|
+
* Validate a posted `StoredPlate` envelope, write it AS-IS, and hot-swap the plate in place. The
|
|
551
|
+
* dev client posts a finished StoredPlate (the whole Plate already projected in the browser, ADR
|
|
552
|
+
* 0022) inside a `{ plate, diagnostics? }` envelope; there is NO server-side accumulation/merge.
|
|
553
|
+
* `diagnostics` is a SIBLING field the server logs and then drops; it never reaches the stored
|
|
554
|
+
* artifact (ADR 0008).
|
|
581
555
|
*
|
|
582
556
|
* @internal
|
|
583
557
|
*/
|
|
584
558
|
declare function handleCapturePost(req: CaptureRequest, res: CaptureResponse, server: CaptureServer, platePath: (name: string) => string): void;
|
|
585
559
|
/**
|
|
586
|
-
* Render a plate as the virtual module's source. Each stitch (`ref` node) becomes
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
* import itself); an unknown child resolves to its own empty plate, never a
|
|
592
|
-
* build break.
|
|
560
|
+
* Render a plate as the virtual module's source. Each stitch (`ref` node) becomes a real import of
|
|
561
|
+
* that child's virtual module, wired into a `refs` map on the default export — so the bundler
|
|
562
|
+
* builds the actual reference graph and chunks the transitive closure a page uses, with no runtime
|
|
563
|
+
* registry (ADR 0006). A stitchless plate is just its JSON. Self-references are dropped (a module
|
|
564
|
+
* can't import itself); an unknown child resolves to its own empty plate, never a build break.
|
|
593
565
|
*
|
|
594
|
-
* `name` defaults to the plate's own `name`, but the loader passes the
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
* (`collectRefs` reads `stored.tree`, a RenderNode, unchanged by the cutover).
|
|
566
|
+
* `name` defaults to the plate's own `name`, but the loader passes the PATH-derived name so a
|
|
567
|
+
* hand-edited StoredPlate whose `name` field drifted from its filename still serializes and
|
|
568
|
+
* self-references under the canonical name (`collectRefs` reads `stored.tree`, a RenderNode).
|
|
598
569
|
*
|
|
599
|
-
* Two-artifact module shape (
|
|
570
|
+
* Two-artifact module shape (ADR 0026):
|
|
600
571
|
*
|
|
601
572
|
* ```js
|
|
602
573
|
* import "virtual:xray/base.css"
|
|
@@ -605,29 +576,25 @@ declare function handleCapturePost(req: CaptureRequest, res: CaptureResponse, se
|
|
|
605
576
|
* export const css = "<the plate's scoped css string>"
|
|
606
577
|
* ```
|
|
607
578
|
*
|
|
608
|
-
* The default export is the TREE artifact (`Plate` carries no css field
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
* unused named export out of built chunks (verified empirically in the plan's
|
|
615
|
-
* scratch app; the side-effect css imports do not pin it).
|
|
579
|
+
* The default export is the TREE artifact (`Plate` carries no css field); the page's styling flows
|
|
580
|
+
* through Vite's own CSS pipeline via the two side-effect imports (dev inject + HMR, build
|
|
581
|
+
* extraction into the chunk's `.css` asset). `export const css` is the plate's css as data, for dev
|
|
582
|
+
* tooling / programmatic consumers that want the string itself — production adapters import only
|
|
583
|
+
* the default, so Rollup TREE-SHAKES the unused named export out of built chunks (verified
|
|
584
|
+
* empirically; the side-effect css imports do not pin it).
|
|
616
585
|
*
|
|
617
|
-
* BASE_CSS rides as a JS-level import of the SHARED `virtual:xray/base.css`
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
* the plate's css module so a plate rule still beats a base rule at equal
|
|
627
|
-
* specificity by source order (the old inline `${BASE_CSS}\n${css}` order).
|
|
586
|
+
* BASE_CSS rides as a JS-level import of the SHARED `virtual:xray/base.css` module (not an
|
|
587
|
+
* `@import` inside each plate's css): Vite dedupes JS module imports by id, so the base rules reach
|
|
588
|
+
* the page exactly once no matter how many plates (nested/stitched included — every plate module
|
|
589
|
+
* emits this import, module identity collapses them) a page mounts, and at build a multi-chunk app
|
|
590
|
+
* gets it hoisted/shared rather than copied per chunk. An `@import` inside each plate's `.css`
|
|
591
|
+
* would instead be INLINED per importer by Vite's postcss-import at build, duplicating BASE_CSS
|
|
592
|
+
* into every plate's asset — the opposite of the dedupe this design locks in. Base rides before the
|
|
593
|
+
* plate's css module so a plate rule still beats a base rule at equal specificity by source order
|
|
594
|
+
* (the `${BASE_CSS}\n${css}` composition order).
|
|
628
595
|
*
|
|
629
596
|
* @internal
|
|
630
597
|
*/
|
|
631
|
-
declare function renderPlateModule(merged:
|
|
598
|
+
declare function renderPlateModule(merged: GatedPlate, name?: string): string;
|
|
632
599
|
//#endregion
|
|
633
600
|
export { type LeafKind, type Plate, type PlateNode, type PlateRule, type StoredPlate, type ViewCapture, XrayOptions, collectRefs, handleCapturePost, handleFixturePost, isValidPlateName, readCoverage, readFixtures, renderPlateModule, xrayVitePlugin };
|