@jxsuite/compiler 0.0.1

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/src/shared.js ADDED
@@ -0,0 +1,690 @@
1
+ /**
2
+ * Shared.js — Shared compiler utilities
3
+ *
4
+ * Detection, scope resolution, HTML building, CSS extraction, and naming utilities used across all
5
+ * compilation targets (static, client, element, server).
6
+ */
7
+
8
+ import { camelToKebab, toCSSText, RESERVED_KEYS } from "@jxsuite/runtime";
9
+
10
+ // Re-export runtime utilities used by submodules
11
+ export { camelToKebab, toCSSText, RESERVED_KEYS };
12
+
13
+ // CDN defaults
14
+ export const DEFAULT_REACTIVITY_SRC = "https://esm.sh/@vue/reactivity@3.5.32";
15
+ export const DEFAULT_LIT_HTML_SRC = "https://esm.sh/lit-html@3.3.0";
16
+
17
+ // ─── Schema keywords ─────────────────────────────────────────────────────────
18
+
19
+ /**
20
+ * Schema-only keywords used to detect pure type definitions (Shape 2b). An object with ONLY these
21
+ * keys and no `default` is a type def, not a signal.
22
+ */
23
+ export const SCHEMA_KEYWORDS = new Set([
24
+ "type",
25
+ "enum",
26
+ "minimum",
27
+ "maximum",
28
+ "minLength",
29
+ "maxLength",
30
+ "pattern",
31
+ "format",
32
+ "items",
33
+ "properties",
34
+ "required",
35
+ "description",
36
+ "title",
37
+ "$comment",
38
+ ]);
39
+
40
+ // ─── Detection ────────────────────────────────────────────────────────────────
41
+
42
+ /**
43
+ * Returns true if a $src path points to a .class.json schema-defined class.
44
+ *
45
+ * @param {any} src
46
+ * @returns {boolean}
47
+ */
48
+ export function isClassJsonSrc(src) {
49
+ return typeof src === "string" && src.endsWith(".class.json");
50
+ }
51
+
52
+ /**
53
+ * Returns true if an object contains only schema keywords (no `default`, no `$prototype`).
54
+ *
55
+ * @param {any} obj
56
+ * @returns {boolean}
57
+ */
58
+ export function isSchemaOnly(obj) {
59
+ for (const k of Object.keys(obj)) {
60
+ if (!SCHEMA_KEYWORDS.has(k)) return false;
61
+ }
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * Returns true if a string contains a ${} template expression.
67
+ *
68
+ * @param {any} val
69
+ * @returns {boolean}
70
+ */
71
+ export function isTemplateString(val) {
72
+ return typeof val === "string" && val.includes("${");
73
+ }
74
+
75
+ /**
76
+ * Determine whether a node (or any of its descendants) requires client-side JavaScript.
77
+ *
78
+ * @param {any} def
79
+ * @returns {boolean}
80
+ */
81
+ export function isDynamic(def) {
82
+ if (!def || typeof def !== "object") return false;
83
+
84
+ if (def.state) {
85
+ for (const [k, d] of Object.entries(def.state)) {
86
+ // Skip injected context (read-only, not reactive)
87
+ if (k === "$site" || k === "$page") continue;
88
+ // Skip timing: "compiler" entries — resolved at build time, baked into static HTML
89
+ if (
90
+ d &&
91
+ typeof d === "object" &&
92
+ !Array.isArray(d) &&
93
+ /** @type {any} */ (d).timing === "compiler"
94
+ )
95
+ continue;
96
+ if (typeof d !== "object" || d === null || Array.isArray(d)) return true;
97
+ if (/** @type {any} */ (d).$prototype) return true;
98
+ if ("default" in /** @type {any} */ (d)) return true;
99
+ if (isSchemaOnly(d)) continue;
100
+ return true;
101
+ }
102
+ }
103
+
104
+ if (def.$switch) return true;
105
+ if (def.children?.$prototype === "Array") return true;
106
+
107
+ if (Array.isArray(def.children)) {
108
+ if (def.children.some(/** @param {any} c */ (c) => isDynamic(c))) return true;
109
+ }
110
+
111
+ for (const [key, val] of Object.entries(def)) {
112
+ if (RESERVED_KEYS.has(key)) continue;
113
+ if (
114
+ val !== null &&
115
+ typeof val === "object" &&
116
+ typeof (/** @type {any} */ (val).$ref) === "string"
117
+ )
118
+ return true;
119
+ if (isTemplateString(val)) return true;
120
+ }
121
+
122
+ if (def.style && typeof def.style === "object") {
123
+ for (const val of Object.values(def.style)) {
124
+ if (isTemplateString(val)) return true;
125
+ }
126
+ }
127
+
128
+ if (def.attributes && typeof def.attributes === "object") {
129
+ for (const val of Object.values(def.attributes)) {
130
+ if (isTemplateString(val)) return true;
131
+ }
132
+ }
133
+
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Shallow variant of isDynamic — checks only this node's own properties, not its children.
139
+ *
140
+ * @param {any} def
141
+ * @returns {boolean}
142
+ */
143
+ export function isNodeDynamic(def) {
144
+ if (!def || typeof def !== "object") return false;
145
+
146
+ if (def.$switch) return true;
147
+ if (def.children?.$prototype === "Array") return true;
148
+
149
+ for (const [key, val] of Object.entries(def)) {
150
+ if (RESERVED_KEYS.has(key)) continue;
151
+ if (
152
+ val !== null &&
153
+ typeof val === "object" &&
154
+ typeof (/** @type {any} */ (val).$ref) === "string"
155
+ )
156
+ return true;
157
+ if (isTemplateString(val)) return true;
158
+ }
159
+
160
+ if (def.style && typeof def.style === "object") {
161
+ for (const val of Object.values(def.style)) {
162
+ if (isTemplateString(val)) return true;
163
+ }
164
+ }
165
+
166
+ if (def.attributes && typeof def.attributes === "object") {
167
+ for (const val of Object.values(def.attributes)) {
168
+ if (isTemplateString(val)) return true;
169
+ }
170
+ }
171
+
172
+ return false;
173
+ }
174
+
175
+ /**
176
+ * Returns true if any node in the tree will need dynamic handling.
177
+ *
178
+ * @param {any} def
179
+ * @returns {boolean}
180
+ */
181
+ export function hasAnyIsland(def) {
182
+ if (!def || typeof def !== "object") return false;
183
+ if (isDynamic(def)) return true;
184
+ if (Array.isArray(def.children))
185
+ return def.children.some(/** @param {any} c */ (c) => hasAnyIsland(c));
186
+ return false;
187
+ }
188
+
189
+ // ─── Scope / value resolution ─────────────────────────────────────────────────
190
+
191
+ /**
192
+ * @param {any} raw
193
+ * @param {any} [parentScope]
194
+ * @param {Record<string, any>} [scopeDefs]
195
+ * @param {Record<string, any>} [media]
196
+ * @returns {{ scope: any; scopeDefs: Record<string, any>; media: Record<string, any> }}
197
+ */
198
+ export function createCompileContext(raw, parentScope = null, scopeDefs = {}, media = {}) {
199
+ const scope = raw?.state
200
+ ? buildInitialScope(raw.state, parentScope)
201
+ : (parentScope ?? Object.create(null));
202
+ return { scope, scopeDefs, media };
203
+ }
204
+
205
+ /**
206
+ * @param {Record<string, any>} [defs]
207
+ * @param {any} [parentScope]
208
+ * @returns {any}
209
+ */
210
+ export function buildInitialScope(defs = {}, parentScope = null) {
211
+ const scope = Object.create(parentScope ?? null);
212
+
213
+ for (const [key, def] of Object.entries(defs)) {
214
+ if (typeof def !== "object" || def === null || Array.isArray(def)) {
215
+ setOwnScopeValue(scope, key, cloneValue(def));
216
+ continue;
217
+ }
218
+ if ("default" in def) {
219
+ setOwnScopeValue(scope, key, cloneValue(def.default));
220
+ continue;
221
+ }
222
+ if (!def.$prototype && !isSchemaOnly(def)) {
223
+ setOwnScopeValue(scope, key, cloneValue(def));
224
+ }
225
+ }
226
+
227
+ for (const [key, def] of Object.entries(defs)) {
228
+ if (typeof def === "string" && isTemplateString(def)) {
229
+ defineLazyScopeValue(scope, key, () => evaluateStaticTemplate(def, scope));
230
+ continue;
231
+ }
232
+ if (!def || typeof def !== "object") continue;
233
+ if (def.$prototype === "Function") {
234
+ if (def.body) {
235
+ const fn = new Function("state", ...(def.parameters ?? def.arguments ?? []), def.body);
236
+ if (def.body.includes("return")) {
237
+ defineLazyScopeValue(scope, key, () => fn(scope));
238
+ } else {
239
+ setOwnScopeValue(scope, key, fn);
240
+ }
241
+ } else if (!def.body?.includes("return")) {
242
+ setOwnScopeValue(scope, key, () => {});
243
+ }
244
+ continue;
245
+ }
246
+ if (def.$prototype === "LocalStorage" || def.$prototype === "SessionStorage") {
247
+ setOwnScopeValue(scope, key, cloneValue(def.default ?? null));
248
+ }
249
+ }
250
+
251
+ return scope;
252
+ }
253
+
254
+ /**
255
+ * @param {any} scope
256
+ * @param {string} key
257
+ * @param {any} value
258
+ */
259
+ export function setOwnScopeValue(scope, key, value) {
260
+ Object.defineProperty(scope, key, {
261
+ value,
262
+ enumerable: true,
263
+ configurable: true,
264
+ writable: true,
265
+ });
266
+ }
267
+
268
+ /**
269
+ * @param {any} scope
270
+ * @param {string} key
271
+ * @param {() => any} getter
272
+ */
273
+ export function defineLazyScopeValue(scope, key, getter) {
274
+ Object.defineProperty(scope, key, {
275
+ enumerable: true,
276
+ configurable: true,
277
+ get: getter,
278
+ });
279
+ }
280
+
281
+ /**
282
+ * @param {any} value
283
+ * @param {any} scope
284
+ * @returns {any}
285
+ */
286
+ export function resolveStaticValue(value, scope) {
287
+ if (isRefObject(value)) return resolveRefValue(value.$ref, scope);
288
+ if (isTemplateString(value)) return evaluateStaticTemplate(value, scope);
289
+ return value;
290
+ }
291
+
292
+ /**
293
+ * @param {any} value
294
+ * @returns {boolean}
295
+ */
296
+ export function isRefObject(value) {
297
+ return value !== null && typeof value === "object" && typeof value.$ref === "string";
298
+ }
299
+
300
+ /**
301
+ * @param {any} refValue
302
+ * @param {any} scope
303
+ * @returns {any}
304
+ */
305
+ export function resolveRefValue(refValue, scope) {
306
+ if (typeof refValue !== "string") return refValue;
307
+ if (refValue.startsWith("$map/")) {
308
+ const parts = refValue.split("/");
309
+ const key = parts[1];
310
+ const base = scope.$map?.[key] ?? scope["$map/" + key];
311
+ return parts.length > 2 ? getPathValue(base, parts.slice(2).join("/")) : base;
312
+ }
313
+ if (refValue.startsWith("#/state/")) {
314
+ const sub = refValue.slice("#/state/".length);
315
+ const slash = sub.indexOf("/");
316
+ if (slash < 0) return scope[sub];
317
+ return getPathValue(scope[sub.slice(0, slash)], sub.slice(slash + 1));
318
+ }
319
+ return scope[refValue] ?? null;
320
+ }
321
+
322
+ /**
323
+ * @param {string} str
324
+ * @param {any} scope
325
+ * @returns {any}
326
+ */
327
+ export function evaluateStaticTemplate(str, scope) {
328
+ const fn = new Function("state", "$map", `return \`${str}\``);
329
+ return fn(scope, scope?.$map);
330
+ }
331
+
332
+ /**
333
+ * @param {any} base
334
+ * @param {string} path
335
+ * @returns {any}
336
+ */
337
+ export function getPathValue(base, path) {
338
+ if (!path) return base;
339
+ return path
340
+ .split("/")
341
+ .reduce(
342
+ (/** @type {any} */ acc, /** @type {string} */ key) => (acc == null ? undefined : acc[key]),
343
+ base,
344
+ );
345
+ }
346
+
347
+ /**
348
+ * @param {any} value
349
+ * @returns {any}
350
+ */
351
+ export function cloneValue(value) {
352
+ if (value === null || typeof value !== "object") return value;
353
+ return JSON.parse(JSON.stringify(value));
354
+ }
355
+
356
+ // ─── HTML building ────────────────────────────────────────────────────────────
357
+
358
+ /**
359
+ * Build an HTML attribute string from a static element definition.
360
+ *
361
+ * @param {any} def
362
+ * @param {any} scope
363
+ * @returns {string}
364
+ */
365
+ export function buildAttrs(def, scope) {
366
+ let out = "";
367
+
368
+ const id = resolveStaticValue(def.id, scope);
369
+ const className = resolveStaticValue(def.className, scope);
370
+ const hidden = resolveStaticValue(def.hidden, scope);
371
+ const tabIndex = resolveStaticValue(def.tabIndex, scope);
372
+ const title = resolveStaticValue(def.title, scope);
373
+ const lang = resolveStaticValue(def.lang, scope);
374
+ const dir = resolveStaticValue(def.dir, scope);
375
+
376
+ if (id) out += ` id="${escapeHtml(id)}"`;
377
+ if (className) out += ` class="${escapeHtml(className)}"`;
378
+ if (hidden) out += " hidden";
379
+ if (tabIndex !== undefined && tabIndex !== null)
380
+ out += ` tabindex="${escapeHtml(String(tabIndex))}"`;
381
+ if (title) out += ` title="${escapeHtml(title)}"`;
382
+ if (lang) out += ` lang="${escapeHtml(lang)}"`;
383
+ if (dir) out += ` dir="${escapeHtml(dir)}"`;
384
+
385
+ if (def.style) {
386
+ // Collect properties that have @media overrides — these must NOT be inline
387
+ // because inline styles (specificity 1,0,0,0) always beat stylesheet @media rules.
388
+ const mediaOverriddenProps = new Set();
389
+ for (const [k, v] of Object.entries(def.style)) {
390
+ if (k.startsWith("@") && v && typeof v === "object") {
391
+ for (const prop of Object.keys(/** @type {Record<string, any>} */ (v))) {
392
+ if (
393
+ !prop.startsWith(":") &&
394
+ !prop.startsWith(".") &&
395
+ !prop.startsWith("&") &&
396
+ !prop.startsWith("[")
397
+ ) {
398
+ mediaOverriddenProps.add(prop);
399
+ }
400
+ }
401
+ }
402
+ }
403
+
404
+ const inline = Object.entries(def.style)
405
+ .filter(
406
+ ([k, v]) =>
407
+ !k.startsWith(":") &&
408
+ !k.startsWith(".") &&
409
+ !k.startsWith("&") &&
410
+ !k.startsWith("[") &&
411
+ !k.startsWith("@") &&
412
+ !mediaOverriddenProps.has(k) &&
413
+ v !== null &&
414
+ typeof v !== "object",
415
+ )
416
+ .map(([k, v]) => {
417
+ const value = resolveStaticValue(v, scope);
418
+ return value == null ? null : `${camelToKebab(k)}: ${value}`;
419
+ })
420
+ .filter(Boolean)
421
+ .join("; ");
422
+ if (inline) out += ` style="${inline}"`;
423
+ }
424
+
425
+ if (def.attributes) {
426
+ for (const [k, v] of Object.entries(def.attributes)) {
427
+ const value = resolveStaticValue(v, scope);
428
+ if (
429
+ value !== null &&
430
+ value !== undefined &&
431
+ (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
432
+ ) {
433
+ out += ` ${k}="${escapeHtml(String(value))}"`;
434
+ }
435
+ }
436
+ }
437
+
438
+ return out;
439
+ }
440
+
441
+ /**
442
+ * Build the inner HTML (textContent or children) for a node.
443
+ *
444
+ * @param {any} def
445
+ * @param {any} raw
446
+ * @param {{ scope: any; scopeDefs: Record<string, any>; media: Record<string, any> }} context
447
+ * @param {(def: any, raw: any, context: any) => string} childCompiler
448
+ * @returns {string}
449
+ */
450
+ export function buildInner(def, raw, context, childCompiler) {
451
+ const source = raw ?? def;
452
+
453
+ if (source.textContent !== undefined) {
454
+ const value = resolveStaticValue(source.textContent, context.scope);
455
+ return value == null ? "" : escapeHtml(String(value));
456
+ }
457
+ if (source.innerHTML) return resolveStaticValue(source.innerHTML, context.scope) ?? "";
458
+ if (Array.isArray(source.children)) {
459
+ const rawChildren = raw?.children;
460
+ return source.children
461
+ .map((/** @type {any} */ c, /** @type {number} */ i) => {
462
+ const childRaw = rawChildren?.[i] ?? c;
463
+ return childCompiler(c, childRaw, context);
464
+ })
465
+ .join("\n ");
466
+ }
467
+ return "";
468
+ }
469
+
470
+ // ─── CSS extraction ───────────────────────────────────────────────────────────
471
+
472
+ /**
473
+ * Walk the entire document tree and collect all static nested CSS rules.
474
+ *
475
+ * @param {any} doc
476
+ * @param {Record<string, any>} [mediaQueries]
477
+ * @returns {string}
478
+ */
479
+ export function compileStyles(doc, mediaQueries = {}) {
480
+ /** @type {string[]} */
481
+ const rules = [];
482
+ collectStyles(doc, rules, mediaQueries, "");
483
+ if (rules.length === 0) return "";
484
+ return `<style>\n${rules.join("\n")}\n</style>`;
485
+ }
486
+
487
+ /**
488
+ * @param {any} def
489
+ * @param {string[]} rules
490
+ * @param {Record<string, any>} mediaQueries
491
+ * @param {string} [_parentSel]
492
+ */
493
+ export function collectStyles(def, rules, mediaQueries, _parentSel = "") {
494
+ if (!def || typeof def !== "object") return;
495
+
496
+ const selector = def.id
497
+ ? `#${def.id}`
498
+ : def.className
499
+ ? `.${def.className.split(" ")[0]}`
500
+ : (def.tagName ?? "*");
501
+
502
+ if (def.style) {
503
+ // Collect properties that have @media overrides — these are excluded from
504
+ // inline styles in buildAttrs(), so we emit them as base CSS rules here.
505
+ const mediaOverriddenProps = new Set();
506
+ for (const [k, v] of Object.entries(def.style)) {
507
+ if (k.startsWith("@") && v && typeof v === "object") {
508
+ for (const p of Object.keys(/** @type {Record<string, any>} */ (v))) {
509
+ if (
510
+ !p.startsWith(":") &&
511
+ !p.startsWith(".") &&
512
+ !p.startsWith("&") &&
513
+ !p.startsWith("[")
514
+ ) {
515
+ mediaOverriddenProps.add(p);
516
+ }
517
+ }
518
+ }
519
+ }
520
+
521
+ // Emit base CSS rules for media-overridden properties
522
+ if (mediaOverriddenProps.size > 0) {
523
+ const baseDecls = [];
524
+ for (const p of mediaOverriddenProps) {
525
+ const v = def.style[p];
526
+ if (v !== null && v !== undefined && typeof v !== "object") {
527
+ baseDecls.push(`${camelToKebab(p)}: ${v}`);
528
+ }
529
+ }
530
+ if (baseDecls.length > 0) {
531
+ rules.push(`${selector} { ${baseDecls.join("; ")} }`);
532
+ }
533
+ }
534
+
535
+ for (const [prop, val] of Object.entries(def.style)) {
536
+ if (prop.startsWith("@")) {
537
+ const query = prop.startsWith("@--")
538
+ ? (mediaQueries[prop.slice(1)] ?? prop.slice(1))
539
+ : prop.slice(1);
540
+ rules.push(`@media ${query} { ${selector} { ${toCSSText(/** @type {any} */ (val))} } }`);
541
+ for (const [sel, nestedRules] of Object.entries(/** @type {Record<string, any>} */ (val))) {
542
+ if (
543
+ !(
544
+ sel.startsWith(":") ||
545
+ sel.startsWith(".") ||
546
+ sel.startsWith("&") ||
547
+ sel.startsWith("[")
548
+ )
549
+ )
550
+ continue;
551
+ const resolved = sel.startsWith("&") ? sel.replace("&", selector) : `${selector}${sel}`;
552
+ rules.push(
553
+ `@media ${query} { ${resolved} { ${toCSSText(/** @type {any} */ (nestedRules))} } }`,
554
+ );
555
+ }
556
+ } else if (
557
+ prop.startsWith(":") ||
558
+ prop.startsWith(".") ||
559
+ prop.startsWith("&") ||
560
+ prop.startsWith("[")
561
+ ) {
562
+ const resolved = prop.startsWith("&") ? prop.replace("&", selector) : `${selector}${prop}`;
563
+ rules.push(`${resolved} { ${toCSSText(/** @type {any} */ (val))} }`);
564
+ }
565
+ }
566
+ }
567
+
568
+ if (Array.isArray(def.children)) {
569
+ def.children.forEach((/** @type {any} */ c) => {
570
+ collectStyles(c, rules, mediaQueries, selector);
571
+ });
572
+ }
573
+ }
574
+
575
+ // ─── Utilities ────────────────────────────────────────────────────────────────
576
+
577
+ /**
578
+ * HTML-escape a string for safe attribute and text content embedding.
579
+ *
580
+ * @param {string} str
581
+ * @returns {string}
582
+ */
583
+ export function escapeHtml(str) {
584
+ return String(str)
585
+ .replace(/&/g, "&amp;")
586
+ .replace(/</g, "&lt;")
587
+ .replace(/>/g, "&gt;")
588
+ .replace(/"/g, "&quot;")
589
+ .replace(/'/g, "&#39;");
590
+ }
591
+
592
+ /**
593
+ * Convert a page title to a valid custom element tag name.
594
+ *
595
+ * @param {string} title
596
+ * @returns {string}
597
+ */
598
+ export function titleToTagName(title) {
599
+ const slug = title
600
+ .toLowerCase()
601
+ .replace(/[^a-z0-9]+/g, "-")
602
+ .replace(/^-|-$/g, "");
603
+ return slug.includes("-") ? slug : `jx-${slug}`;
604
+ }
605
+
606
+ /**
607
+ * @param {string} tagName
608
+ * @returns {string}
609
+ */
610
+ export function tagNameToClassName(tagName) {
611
+ return tagName
612
+ .split("-")
613
+ .map((/** @type {string} */ s) => s.charAt(0).toUpperCase() + s.slice(1))
614
+ .join("");
615
+ }
616
+
617
+ /**
618
+ * Recursively collect unique $src values from $prototype: "Function" entries.
619
+ *
620
+ * @param {any} doc
621
+ * @returns {string[]}
622
+ */
623
+ export function collectSrcImports(doc) {
624
+ /** @type {Set<string>} */
625
+ const srcs = new Set();
626
+ _walkSrc(doc, srcs);
627
+ return [...srcs];
628
+ }
629
+
630
+ /**
631
+ * @param {any} def
632
+ * @param {Set<string>} srcs
633
+ */
634
+ function _walkSrc(def, srcs) {
635
+ if (!def || typeof def !== "object") return;
636
+ if (def.state) {
637
+ for (const d of Object.values(def.state)) {
638
+ if (
639
+ d &&
640
+ typeof d === "object" &&
641
+ /** @type {any} */ (d).$prototype === "Function" &&
642
+ /** @type {any} */ (d).$src
643
+ ) {
644
+ srcs.add(/** @type {any} */ (d).$src);
645
+ }
646
+ }
647
+ }
648
+ if (Array.isArray(def.children)) {
649
+ def.children.forEach((/** @type {any} */ c) => _walkSrc(c, srcs));
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Recursively collect all `timing: "server"` entries from the document tree.
655
+ *
656
+ * @param {any} doc
657
+ * @returns {any[]}
658
+ */
659
+ export function collectServerEntries(doc) {
660
+ /** @type {Map<string, any>} */
661
+ const entries = new Map();
662
+ _walkServerEntries(doc, entries);
663
+ return [...entries.values()];
664
+ }
665
+
666
+ /**
667
+ * @param {any} def
668
+ * @param {Map<string, any>} entries
669
+ */
670
+ function _walkServerEntries(def, entries) {
671
+ if (!def || typeof def !== "object") return;
672
+ if (def.state) {
673
+ for (const [key, d] of Object.entries(def.state)) {
674
+ const entry = /** @type {any} */ (d);
675
+ if (
676
+ entry &&
677
+ typeof entry === "object" &&
678
+ entry.timing === "server" &&
679
+ entry.$src &&
680
+ entry.$export &&
681
+ !entry.$prototype
682
+ ) {
683
+ entries.set(entry.$export, { key, exportName: entry.$export, src: entry.$src });
684
+ }
685
+ }
686
+ }
687
+ if (Array.isArray(def.children)) {
688
+ def.children.forEach((/** @type {any} */ c) => _walkServerEntries(c, entries));
689
+ }
690
+ }