@jxsuite/compiler 0.23.1 → 0.24.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.
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Shared.js — Shared compiler utilities
3
4
  *
@@ -7,6 +8,16 @@
7
8
 
8
9
  import { camelToKebab, toCSSText, RESERVED_KEYS } from "@jxsuite/runtime";
9
10
  import { compileExpression, isMutating, evaluateExpression } from "@jxsuite/runtime/expression";
11
+ import type { ExpressionNode } from "@jxsuite/runtime/expression";
12
+ import type {
13
+ JxElement,
14
+ JxMutableNode,
15
+ JxStyle,
16
+ JxStateDefinition,
17
+ JxPrototypeDef,
18
+ JxStateObject,
19
+ JxMappedArray,
20
+ } from "@jxsuite/schema/types";
10
21
 
11
22
  // Re-export runtime utilities used by submodules
12
23
  export {
@@ -17,6 +28,7 @@ export {
17
28
  isMutating,
18
29
  evaluateExpression,
19
30
  };
31
+ export type { ExpressionNode };
20
32
 
21
33
  // CDN defaults
22
34
  export const DEFAULT_REACTIVITY_SRC = "https://esm.sh/@vue/reactivity@3.5.32";
@@ -53,7 +65,7 @@ export const SCHEMA_KEYWORDS = new Set([
53
65
  * @param {unknown} src
54
66
  * @returns {boolean}
55
67
  */
56
- export function isClassJsonSrc(src) {
68
+ export function isClassJsonSrc(src: unknown) {
57
69
  return typeof src === "string" && src.endsWith(".class.json");
58
70
  }
59
71
 
@@ -63,7 +75,7 @@ export function isClassJsonSrc(src) {
63
75
  * @param {Record<string, unknown>} obj
64
76
  * @returns {boolean}
65
77
  */
66
- export function isSchemaOnly(obj) {
78
+ export function isSchemaOnly(obj: Record<string, unknown>) {
67
79
  for (const k of Object.keys(obj)) {
68
80
  if (!SCHEMA_KEYWORDS.has(k)) return false;
69
81
  }
@@ -76,7 +88,7 @@ export function isSchemaOnly(obj) {
76
88
  * @param {unknown} val
77
89
  * @returns {boolean}
78
90
  */
79
- export function isTemplateString(val) {
91
+ export function isTemplateString(val: unknown) {
80
92
  return typeof val === "string" && val.includes("${");
81
93
  }
82
94
 
@@ -86,7 +98,7 @@ export function isTemplateString(val) {
86
98
  * @param {JxElement | JxMutableNode | string} def
87
99
  * @returns {boolean}
88
100
  */
89
- export function isDynamic(def) {
101
+ export function isDynamic(def: JxElement | JxMutableNode | string) {
90
102
  if (!def || typeof def !== "object") return false;
91
103
 
92
104
  if (def.state) {
@@ -98,11 +110,11 @@ export function isDynamic(def) {
98
110
  d &&
99
111
  typeof d === "object" &&
100
112
  !Array.isArray(d) &&
101
- /** @type {JxPrototypeDef} */ (d).timing === "compiler"
113
+ (d as JxPrototypeDef).timing === "compiler"
102
114
  )
103
115
  continue;
104
116
  if (typeof d !== "object" || d === null || Array.isArray(d)) return true;
105
- if (/** @type {JxPrototypeDef} */ (d).$prototype) return true;
117
+ if ((d as JxPrototypeDef).$prototype) return true;
106
118
  if ("default" in /** @type {object} */ (d)) return true;
107
119
  if (isSchemaOnly(d)) continue;
108
120
  return true;
@@ -112,7 +124,7 @@ export function isDynamic(def) {
112
124
  if (def.$switch) return true;
113
125
  if (
114
126
  !Array.isArray(def.children) &&
115
- /** @type {JxMappedArray | undefined} */ (def.children)?.$prototype === "Array"
127
+ (def.children as JxMappedArray | undefined)?.$prototype === "Array"
116
128
  )
117
129
  return true;
118
130
 
@@ -122,11 +134,7 @@ export function isDynamic(def) {
122
134
 
123
135
  for (const [key, val] of Object.entries(def)) {
124
136
  if (RESERVED_KEYS.has(key)) continue;
125
- if (
126
- val !== null &&
127
- typeof val === "object" &&
128
- typeof (/** @type {JxMutableNode} */ (val).$ref) === "string"
129
- )
137
+ if (val !== null && typeof val === "object" && typeof (val as JxMutableNode).$ref === "string")
130
138
  return true;
131
139
  if (isTemplateString(val)) return true;
132
140
  }
@@ -152,23 +160,19 @@ export function isDynamic(def) {
152
160
  * @param {JxElement | JxMutableNode | string} def
153
161
  * @returns {boolean}
154
162
  */
155
- export function isNodeDynamic(def) {
163
+ export function isNodeDynamic(def: JxElement | JxMutableNode | string) {
156
164
  if (!def || typeof def !== "object") return false;
157
165
 
158
166
  if (def.$switch) return true;
159
167
  if (
160
168
  !Array.isArray(def.children) &&
161
- /** @type {JxMappedArray | undefined} */ (def.children)?.$prototype === "Array"
169
+ (def.children as JxMappedArray | undefined)?.$prototype === "Array"
162
170
  )
163
171
  return true;
164
172
 
165
173
  for (const [key, val] of Object.entries(def)) {
166
174
  if (RESERVED_KEYS.has(key)) continue;
167
- if (
168
- val !== null &&
169
- typeof val === "object" &&
170
- typeof (/** @type {JxMutableNode} */ (val).$ref) === "string"
171
- )
175
+ if (val !== null && typeof val === "object" && typeof (val as JxMutableNode).$ref === "string")
172
176
  return true;
173
177
  if (isTemplateString(val)) return true;
174
178
  }
@@ -194,10 +198,10 @@ export function isNodeDynamic(def) {
194
198
  * @param {JxElement | JxMutableNode | string} def
195
199
  * @returns {boolean}
196
200
  */
197
- export function hasAnyIsland(def) {
201
+ export function hasAnyIsland(def: JxElement | JxMutableNode | string): boolean {
198
202
  if (!def || typeof def !== "object") return false;
199
203
  if (isDynamic(def)) return true;
200
- if (Array.isArray(def.children)) return def.children.some((c) => hasAnyIsland(c));
204
+ if (Array.isArray(def.children)) return def.children.some((c): boolean => hasAnyIsland(c));
201
205
  return false;
202
206
  }
203
207
 
@@ -214,7 +218,12 @@ export function hasAnyIsland(def) {
214
218
  * media: Record<string, string>;
215
219
  * }}
216
220
  */
217
- export function createCompileContext(raw, parentScope = null, scopeDefs = {}, media = {}) {
221
+ export function createCompileContext(
222
+ raw: JxElement | JxMutableNode | null,
223
+ parentScope: Record<string, unknown> | null = null,
224
+ scopeDefs: Record<string, unknown> = {},
225
+ media: Record<string, string> = {},
226
+ ) {
218
227
  const scope = raw?.state
219
228
  ? buildInitialScope(raw.state, parentScope)
220
229
  : (parentScope ?? Object.create(null));
@@ -226,7 +235,10 @@ export function createCompileContext(raw, parentScope = null, scopeDefs = {}, me
226
235
  * @param {Record<string, unknown> | null} [parentScope]
227
236
  * @returns {Record<string, unknown>}
228
237
  */
229
- export function buildInitialScope(defs = {}, parentScope = null) {
238
+ export function buildInitialScope(
239
+ defs: Record<string, JxStateDefinition> = {},
240
+ parentScope: Record<string, unknown> | null = null,
241
+ ) {
230
242
  const scope = Object.create(parentScope ?? null);
231
243
 
232
244
  for (const [key, def] of Object.entries(defs)) {
@@ -234,7 +246,7 @@ export function buildInitialScope(defs = {}, parentScope = null) {
234
246
  setOwnScopeValue(scope, key, cloneValue(def));
235
247
  continue;
236
248
  }
237
- const d = /** @type {JxStateObject & JxPrototypeDef} */ (def);
249
+ const d = def as JxStateObject & JxPrototypeDef;
238
250
  if ("default" in d) {
239
251
  setOwnScopeValue(scope, key, cloneValue(d.default));
240
252
  continue;
@@ -250,11 +262,11 @@ export function buildInitialScope(defs = {}, parentScope = null) {
250
262
  continue;
251
263
  }
252
264
  if (!def || typeof def !== "object") continue;
253
- const d = /** @type {JxStateObject & JxPrototypeDef} */ (def);
265
+ const d = def as JxStateObject & JxPrototypeDef;
254
266
  if ("$expression" in d) {
255
- const node = /** @type {any} */ (d).$expression;
267
+ const node = (d as Record<string, unknown>).$expression as ExpressionNode;
256
268
  if (isMutating(node.operator)) {
257
- setOwnScopeValue(scope, key, (/** @type {any} */ s, /** @type {any} */ event) =>
269
+ setOwnScopeValue(scope, key, (s: Record<string, any>, event: Event) =>
258
270
  evaluateExpression(node, s, event),
259
271
  );
260
272
  } else {
@@ -288,7 +300,7 @@ export function buildInitialScope(defs = {}, parentScope = null) {
288
300
  * @param {string} key
289
301
  * @param {unknown} value
290
302
  */
291
- export function setOwnScopeValue(scope, key, value) {
303
+ export function setOwnScopeValue(scope: Record<string, unknown>, key: string, value: unknown) {
292
304
  Object.defineProperty(scope, key, {
293
305
  value,
294
306
  enumerable: true,
@@ -302,7 +314,11 @@ export function setOwnScopeValue(scope, key, value) {
302
314
  * @param {string} key
303
315
  * @param {() => unknown} getter
304
316
  */
305
- export function defineLazyScopeValue(scope, key, getter) {
317
+ export function defineLazyScopeValue(
318
+ scope: Record<string, unknown>,
319
+ key: string,
320
+ getter: () => unknown,
321
+ ) {
306
322
  Object.defineProperty(scope, key, {
307
323
  enumerable: true,
308
324
  configurable: true,
@@ -315,9 +331,9 @@ export function defineLazyScopeValue(scope, key, getter) {
315
331
  * @param {Record<string, unknown>} scope
316
332
  * @returns {unknown}
317
333
  */
318
- export function resolveStaticValue(value, scope) {
319
- if (isRefObject(value)) return resolveRefValue(/** @type {JxMutableNode} */ (value).$ref, scope);
320
- if (isTemplateString(value)) return evaluateStaticTemplate(/** @type {string} */ (value), scope);
334
+ export function resolveStaticValue(value: unknown, scope: Record<string, unknown> | null) {
335
+ if (isRefObject(value)) return resolveRefValue((value as JxMutableNode).$ref, scope!);
336
+ if (isTemplateString(value)) return evaluateStaticTemplate(value as string, scope!);
321
337
  return value;
322
338
  }
323
339
 
@@ -325,11 +341,9 @@ export function resolveStaticValue(value, scope) {
325
341
  * @param {unknown} value
326
342
  * @returns {boolean}
327
343
  */
328
- export function isRefObject(value) {
344
+ export function isRefObject(value: unknown) {
329
345
  return (
330
- value !== null &&
331
- typeof value === "object" &&
332
- typeof (/** @type {JxMutableNode} */ (value).$ref) === "string"
346
+ value !== null && typeof value === "object" && typeof (value as JxMutableNode).$ref === "string"
333
347
  );
334
348
  }
335
349
 
@@ -338,13 +352,12 @@ export function isRefObject(value) {
338
352
  * @param {Record<string, unknown>} scope
339
353
  * @returns {unknown}
340
354
  */
341
- export function resolveRefValue(refValue, scope) {
355
+ export function resolveRefValue(refValue: unknown, scope: Record<string, unknown>) {
342
356
  if (typeof refValue !== "string") return refValue;
343
357
  if (refValue.startsWith("$map/")) {
344
358
  const parts = refValue.split("/");
345
359
  const key = parts[1];
346
- const base = /** @type {Record<string, unknown> | undefined} */ (scope.$map)?.[key] ??
347
- scope["$map/" + key];
360
+ const base = (scope.$map as Record<string, unknown> | undefined)?.[key] ?? scope["$map/" + key];
348
361
  return parts.length > 2 ? getPathValue(base, parts.slice(2).join("/")) : base;
349
362
  }
350
363
  if (refValue.startsWith("#/state/")) {
@@ -361,7 +374,7 @@ export function resolveRefValue(refValue, scope) {
361
374
  * @param {Record<string, unknown>} scope
362
375
  * @returns {unknown}
363
376
  */
364
- export function evaluateStaticTemplate(str, scope) {
377
+ export function evaluateStaticTemplate(str: string, scope: Record<string, unknown>) {
365
378
  try {
366
379
  const singleExprMatch = str.match(/^\$\{(.+)\}$/s);
367
380
  if (singleExprMatch) {
@@ -380,24 +393,21 @@ export function evaluateStaticTemplate(str, scope) {
380
393
  * @param {string} path
381
394
  * @returns {unknown}
382
395
  */
383
- export function getPathValue(base, path) {
396
+ export function getPathValue(base: unknown, path: string) {
384
397
  if (!path) return base;
385
- return path.split("/").reduce(
386
- /**
387
- * @param {Record<string, unknown>} acc
388
- * @param {string} key
389
- * @returns {Record<string, unknown>}
390
- */
391
- (acc, key) => (/** @type {Record<string, unknown>} */ (acc == null ? undefined : acc[key])),
392
- /** @type {Record<string, unknown>} */ (base),
393
- );
398
+ return path
399
+ .split("/")
400
+ .reduce<unknown>(
401
+ (acc, key: string) => (acc == null ? undefined : (acc as Record<string, unknown>)[key]),
402
+ base,
403
+ );
394
404
  }
395
405
 
396
406
  /**
397
407
  * @param {unknown} value
398
408
  * @returns {unknown}
399
409
  */
400
- export function cloneValue(value) {
410
+ export function cloneValue(value: unknown) {
401
411
  if (value === null || typeof value !== "object") return value;
402
412
  return JSON.parse(JSON.stringify(value));
403
413
  }
@@ -410,7 +420,7 @@ export function cloneValue(value) {
410
420
  * @param {JxElement | JxMutableNode} def
411
421
  * @param {Record<string, unknown>} scope @returns {string}
412
422
  */
413
- export function buildAttrs(def, scope) {
423
+ export function buildAttrs(def: JxElement | JxMutableNode, scope: Record<string, unknown> | null) {
414
424
  let out = "";
415
425
 
416
426
  const id = resolveStaticValue(def.id, scope);
@@ -492,7 +502,16 @@ export function buildAttrs(def, scope) {
492
502
  * @param {(def: any, raw: any, context: any) => string} childCompiler
493
503
  * @returns {string}
494
504
  */
495
- export function buildInner(def, raw, context, childCompiler) {
505
+ export function buildInner(
506
+ def: JxElement,
507
+ raw: JxElement | null,
508
+ context: {
509
+ scope: Record<string, unknown> | null;
510
+ scopeDefs: Record<string, unknown>;
511
+ media: Record<string, string>;
512
+ },
513
+ childCompiler: (def: unknown, raw: unknown, context: unknown) => string,
514
+ ) {
496
515
  const source = raw ?? def;
497
516
 
498
517
  if (source.textContent !== undefined) {
@@ -500,12 +519,12 @@ export function buildInner(def, raw, context, childCompiler) {
500
519
  return value == null ? "" : escapeHtml(String(value));
501
520
  }
502
521
  if (source.innerHTML)
503
- return /** @type {string} */ (resolveStaticValue(source.innerHTML, context.scope)) ?? "";
522
+ return (resolveStaticValue(source.innerHTML, context.scope) as string) ?? "";
504
523
  if (Array.isArray(source.children)) {
505
524
  const rawChildren = raw?.children;
506
525
  return source.children
507
- .map((/** @type {JxElement | JxMutableNode | string} */ c, /** @type {number} */ i) => {
508
- const childRaw = /** @type {(JxElement | string)[] | undefined} */ (rawChildren)?.[i] ?? c;
526
+ .map((c: JxElement | JxMutableNode | string, i: number) => {
527
+ const childRaw = (rawChildren as (JxElement | string)[] | undefined)?.[i] ?? c;
509
528
  return childCompiler(c, childRaw, context);
510
529
  })
511
530
  .join("\n ");
@@ -523,15 +542,18 @@ export function buildInner(def, raw, context, childCompiler) {
523
542
  * @param {JxStyle | null} [projectStyle]
524
543
  * @returns {string}
525
544
  */
526
- export function compileStyles(doc, mediaQueries = {}, projectStyle = null) {
527
- /** @type {string[]} */
528
- const rules = [];
545
+ export function compileStyles(
546
+ doc: JxElement | JxMutableNode,
547
+ mediaQueries: Record<string, string> = {},
548
+ projectStyle: JxStyle | null = null,
549
+ ) {
550
+ const rules: string[] = [];
529
551
 
530
552
  // Emit project-level (site-wide) styles — CSS custom properties go on :root,
531
553
  // everything else on body. Project-level style is implicitly :root, so a
532
554
  // flat object like { "--bg": "#000", "margin": "0" } is the expected format.
533
555
  if (projectStyle && typeof projectStyle === "object") {
534
- function emitProjectRules(/** @type {string} */ selector, /** @type {JxMutableNode} */ obj) {
556
+ function emitProjectRules(selector: string, obj: Record<string, unknown>) {
535
557
  const props = toCSSText(obj);
536
558
  if (props) rules.push(`${selector} { ${props} }`);
537
559
  for (const [key, val] of Object.entries(obj)) {
@@ -560,13 +582,13 @@ export function compileStyles(doc, mediaQueries = {}, projectStyle = null) {
560
582
  : key.startsWith(":") || key.startsWith(".") || key.startsWith("[")
561
583
  ? `${selector}${key}`
562
584
  : `${selector} ${key}`;
563
- emitProjectRules(resolved, /** @type {Record<string, unknown>} */ (val));
585
+ emitProjectRules(resolved, val as Record<string, unknown>);
564
586
  }
565
587
  }
566
588
 
567
589
  for (const [key, val] of Object.entries(projectStyle)) {
568
590
  if (key.startsWith(":") || key.startsWith(".") || key.startsWith("[")) {
569
- emitProjectRules(key, /** @type {Record<string, unknown>} */ (val));
591
+ emitProjectRules(key, val as Record<string, unknown>);
570
592
  } else if (
571
593
  val !== null &&
572
594
  typeof val === "object" &&
@@ -574,7 +596,7 @@ export function compileStyles(doc, mediaQueries = {}, projectStyle = null) {
574
596
  !key.startsWith("@") &&
575
597
  !key.startsWith("--")
576
598
  ) {
577
- emitProjectRules(key, val);
599
+ emitProjectRules(key, val as Record<string, unknown>);
578
600
  } else if (key.startsWith("@")) {
579
601
  // @media block at top level
580
602
  const atRule = key.startsWith("@--")
@@ -582,15 +604,13 @@ export function compileStyles(doc, mediaQueries = {}, projectStyle = null) {
582
604
  : key.startsWith("@(")
583
605
  ? `@media ${key.slice(1)}`
584
606
  : key;
585
- rules.push(`${atRule} { body { ${toCSSText(/** @type {object} */ (val))} } }`);
607
+ rules.push(`${atRule} { body { ${toCSSText(val as Record<string, unknown>)} } }`);
586
608
  }
587
609
  }
588
610
  // Collect CSS custom properties into :root {}
589
- /** @type {Record<string, unknown>} */
590
- const rootProps = {};
611
+ const rootProps: Record<string, unknown> = {};
591
612
  // Collect direct CSS properties into body {}
592
- /** @type {Record<string, unknown>} */
593
- const bodyProps = {};
613
+ const bodyProps: Record<string, unknown> = {};
594
614
  for (const [key, val] of Object.entries(projectStyle)) {
595
615
  if (key.startsWith(":") || key.startsWith(".") || key.startsWith("[") || key.startsWith("@"))
596
616
  continue;
@@ -625,7 +645,12 @@ export function compileStyles(doc, mediaQueries = {}, projectStyle = null) {
625
645
  * @param {string[]} rules
626
646
  * @param {Record<string, string>} mediaQueries
627
647
  */
628
- function emitNestedElement(selector, obj, rules, mediaQueries) {
648
+ function emitNestedElement(
649
+ selector: string,
650
+ obj: Record<string, unknown>,
651
+ rules: string[],
652
+ mediaQueries: Record<string, string>,
653
+ ) {
629
654
  const props = toCSSText(obj);
630
655
  if (props) rules.push(`${selector} { ${props} }`);
631
656
  for (const [key, val] of Object.entries(obj)) {
@@ -656,7 +681,7 @@ function emitNestedElement(selector, obj, rules, mediaQueries) {
656
681
  : key.startsWith(":") || key.startsWith(".") || key.startsWith("[")
657
682
  ? `${selector}${key}`
658
683
  : `${selector} ${key}`;
659
- emitNestedElement(resolved, /** @type {Record<string, unknown>} */ (val), rules, mediaQueries);
684
+ emitNestedElement(resolved, val as Record<string, unknown>, rules, mediaQueries);
660
685
  }
661
686
  }
662
687
 
@@ -668,11 +693,11 @@ function emitNestedElement(selector, obj, rules, mediaQueries) {
668
693
  * @param {{ n: number }} [counter]
669
694
  */
670
695
  export function collectStyles(
671
- def,
672
- rules,
673
- mediaQueries,
674
- _parentSel = "",
675
- counter = { n: 0 },
696
+ def: JxElement | JxMutableNode | string,
697
+ rules: string[],
698
+ mediaQueries: Record<string, string>,
699
+ _parentSel: string = "",
700
+ counter: { n: number } = { n: 0 },
676
701
  prefix = "jx",
677
702
  ) {
678
703
  if (!def || typeof def !== "object") return;
@@ -748,7 +773,7 @@ export function collectStyles(
748
773
  }
749
774
 
750
775
  if (Array.isArray(def.children)) {
751
- def.children.forEach((/** @type {JxElement | JxMutableNode | string} */ c) => {
776
+ def.children.forEach((c: JxElement | JxMutableNode | string) => {
752
777
  collectStyles(c, rules, mediaQueries, selector, counter, prefix);
753
778
  });
754
779
  }
@@ -762,7 +787,7 @@ export function collectStyles(
762
787
  * @param {string} str
763
788
  * @returns {string}
764
789
  */
765
- export function escapeHtml(str) {
790
+ export function escapeHtml(str: string) {
766
791
  return String(str)
767
792
  .replace(/&/g, "&amp;")
768
793
  .replace(/</g, "&lt;")
@@ -777,7 +802,7 @@ export function escapeHtml(str) {
777
802
  * @param {string} title
778
803
  * @returns {string}
779
804
  */
780
- export function titleToTagName(title) {
805
+ export function titleToTagName(title: string) {
781
806
  const slug = title
782
807
  .toLowerCase()
783
808
  .replace(/[^a-z0-9]+/g, "-")
@@ -789,10 +814,10 @@ export function titleToTagName(title) {
789
814
  * @param {string} tagName
790
815
  * @returns {string}
791
816
  */
792
- export function tagNameToClassName(tagName) {
817
+ export function tagNameToClassName(tagName: string) {
793
818
  return tagName
794
819
  .split("-")
795
- .map((/** @type {string} */ s) => s.charAt(0).toUpperCase() + s.slice(1))
820
+ .map((s: string) => s.charAt(0).toUpperCase() + s.slice(1))
796
821
  .join("");
797
822
  }
798
823
 
@@ -802,9 +827,8 @@ export function tagNameToClassName(tagName) {
802
827
  * @param {JxElement} doc
803
828
  * @returns {string[]}
804
829
  */
805
- export function collectSrcImports(doc) {
806
- /** @type {Set<string>} */
807
- const srcs = new Set();
830
+ export function collectSrcImports(doc: JxElement) {
831
+ const srcs: Set<string> = new Set();
808
832
  _walkSrc(doc, srcs);
809
833
  return [...srcs];
810
834
  }
@@ -813,24 +837,22 @@ export function collectSrcImports(doc) {
813
837
  * @param {JxElement | JxMutableNode | string} def
814
838
  * @param {Set<string>} srcs
815
839
  */
816
- function _walkSrc(def, srcs) {
840
+ function _walkSrc(def: JxElement | JxMutableNode | string, srcs: Set<string>) {
817
841
  if (!def || typeof def !== "object") return;
818
842
  if (def.state) {
819
843
  for (const d of Object.values(def.state)) {
820
844
  if (
821
845
  d &&
822
846
  typeof d === "object" &&
823
- /** @type {JxMutableNode} */ (d).$prototype === "Function" &&
824
- /** @type {JxMutableNode} */ (d).$src
847
+ (d as JxMutableNode).$prototype === "Function" &&
848
+ (d as JxMutableNode).$src
825
849
  ) {
826
- srcs.add(/** @type {string} */ (/** @type {JxMutableNode} */ (d).$src));
850
+ srcs.add((d as JxMutableNode).$src as string);
827
851
  }
828
852
  }
829
853
  }
830
854
  if (Array.isArray(def.children)) {
831
- def.children.forEach((/** @type {JxElement | JxMutableNode | string} */ c) =>
832
- _walkSrc(c, srcs),
833
- );
855
+ def.children.forEach((c: JxElement | JxMutableNode | string) => _walkSrc(c, srcs));
834
856
  }
835
857
  }
836
858
 
@@ -840,7 +862,7 @@ function _walkSrc(def, srcs) {
840
862
  * @param {JxElement} doc
841
863
  * @returns {{ key: string; exportName: string; src: string }[]}
842
864
  */
843
- export function collectServerEntries(doc) {
865
+ export function collectServerEntries(doc: JxElement) {
844
866
  /** @type {Map<string, { key: string; exportName: string; src: string }>} */
845
867
  const entries = new Map();
846
868
  _walkServerEntries(doc, entries);
@@ -851,11 +873,14 @@ export function collectServerEntries(doc) {
851
873
  * @param {JxElement | JxMutableNode | string} def
852
874
  * @param {Map<string, { key: string; exportName: string; src: string }>} entries
853
875
  */
854
- function _walkServerEntries(def, entries) {
876
+ function _walkServerEntries(
877
+ def: JxElement | JxMutableNode | string,
878
+ entries: Map<string, { key: string; exportName: string; src: string }>,
879
+ ) {
855
880
  if (!def || typeof def !== "object") return;
856
881
  if (def.state) {
857
882
  for (const [key, d] of Object.entries(def.state)) {
858
- const entry = /** @type {JxMutableNode} */ (d);
883
+ const entry = d as JxMutableNode;
859
884
  if (
860
885
  entry &&
861
886
  typeof entry === "object" &&
@@ -869,16 +894,23 @@ function _walkServerEntries(def, entries) {
869
894
  }
870
895
  }
871
896
  if (Array.isArray(def.children)) {
872
- def.children.forEach((/** @type {JxElement | JxMutableNode | string} */ c) =>
873
- _walkServerEntries(c, entries),
874
- );
897
+ def.children.forEach((c: JxElement | JxMutableNode | string) => _walkServerEntries(c, entries));
875
898
  }
876
899
  }
877
900
 
878
901
  // ─── Component pre-rendering ─────────────────────────────────────────────────
879
902
 
880
- /** @type {Set<string>} */
881
- const SELF_CLOSING = new Set(["input", "br", "hr", "img", "meta", "link", "area", "col", "source"]);
903
+ const SELF_CLOSING: Set<string> = new Set([
904
+ "input",
905
+ "br",
906
+ "hr",
907
+ "img",
908
+ "meta",
909
+ "link",
910
+ "area",
911
+ "col",
912
+ "source",
913
+ ]);
882
914
 
883
915
  /**
884
916
  * Recursively render a Jx node tree to static HTML for pre-rendering.
@@ -888,7 +920,11 @@ const SELF_CLOSING = new Set(["input", "br", "hr", "img", "meta", "link", "area"
888
920
  * @param {string | null} [slotContent] - HTML to substitute for `<slot>` elements
889
921
  * @returns {string}
890
922
  */
891
- export function renderStaticNode(node, scope, slotContent = null) {
923
+ export function renderStaticNode(
924
+ node: JxElement | JxMutableNode | string,
925
+ scope: Record<string, unknown> | null,
926
+ slotContent: string | null = null,
927
+ ): string {
892
928
  if (typeof node === "string") {
893
929
  if (isTemplateString(node) && scope) {
894
930
  const val = evaluateStaticTemplate(node, scope);
@@ -898,8 +934,8 @@ export function renderStaticNode(node, scope, slotContent = null) {
898
934
  }
899
935
  if (typeof node === "number" || typeof node === "boolean") return escapeHtml(String(node));
900
936
  if (Array.isArray(node))
901
- return node
902
- .map((/** @type {JxElement | JxMutableNode | string} */ c) =>
937
+ return (node as (JxElement | JxMutableNode | string)[])
938
+ .map((c: JxElement | JxMutableNode | string): string =>
903
939
  renderStaticNode(c, scope, slotContent),
904
940
  )
905
941
  .join("\n");
@@ -923,12 +959,10 @@ export function renderStaticNode(node, scope, slotContent = null) {
923
959
  inner = val != null ? escapeHtml(String(val)) : "";
924
960
  } else if (node.innerHTML) {
925
961
  const val = resolveStaticValue(node.innerHTML, scope);
926
- inner = val != null ? String(val) : /** @type {string} */ (node.innerHTML);
962
+ inner = val != null ? String(val) : (node.innerHTML as string);
927
963
  } else if (Array.isArray(node.children)) {
928
964
  inner = node.children
929
- .map((/** @type {JxElement | JxMutableNode | string} */ c) =>
930
- renderStaticNode(c, scope, slotContent),
931
- )
965
+ .map((c: JxElement | JxMutableNode | string) => renderStaticNode(c, scope, slotContent))
932
966
  .join("\n");
933
967
  }
934
968
 
@@ -944,9 +978,12 @@ export function renderStaticNode(node, scope, slotContent = null) {
944
978
  * @param {string | null} [slotContent] - HTML to substitute for `<slot>` elements
945
979
  * @returns {string} The pre-rendered innerHTML
946
980
  */
947
- export function preRenderComponentHtml(doc, propsOverride = null, slotContent = null) {
948
- /** @type {Record<string, JxStateDefinition>} */
949
- let stateDefs = doc.state ?? {};
981
+ export function preRenderComponentHtml(
982
+ doc: JxElement,
983
+ propsOverride: Record<string, unknown> | null = null,
984
+ slotContent: string | null = null,
985
+ ) {
986
+ let stateDefs: Record<string, JxStateDefinition> = doc.state ?? {};
950
987
  if (propsOverride) {
951
988
  stateDefs = { ...stateDefs };
952
989
  for (const [key, value] of Object.entries(propsOverride)) {
@@ -960,19 +997,17 @@ export function preRenderComponentHtml(doc, propsOverride = null, slotContent =
960
997
  ) {
961
998
  stateDefs[key] = { .../** @type {JxStateObject} */ (existing), default: value };
962
999
  } else {
963
- stateDefs[key] = /** @type {JxStateDefinition} */ (value);
1000
+ stateDefs[key] = value as JxStateDefinition;
964
1001
  }
965
1002
  } else {
966
- stateDefs[key] = /** @type {JxStateDefinition} */ (value);
1003
+ stateDefs[key] = value as JxStateDefinition;
967
1004
  }
968
1005
  }
969
1006
  }
970
1007
  const scope = buildInitialScope(stateDefs, null);
971
1008
  if (!Array.isArray(doc.children)) return "";
972
1009
  return doc.children
973
- .map((/** @type {JxElement | JxMutableNode | string} */ c) =>
974
- renderStaticNode(c, scope, slotContent),
975
- )
1010
+ .map((c: JxElement | JxMutableNode | string) => renderStaticNode(c, scope, slotContent))
976
1011
  .join("\n");
977
1012
  }
978
1013
 
@@ -985,7 +1020,7 @@ export function preRenderComponentHtml(doc, propsOverride = null, slotContent =
985
1020
  * @param {JxElement} doc - Component JSON definition
986
1021
  * @returns {boolean}
987
1022
  */
988
- export function isComponentFullyStatic(doc) {
1023
+ export function isComponentFullyStatic(doc: JxElement) {
989
1024
  return _isStaticNode(doc);
990
1025
  }
991
1026
 
@@ -993,7 +1028,7 @@ export function isComponentFullyStatic(doc) {
993
1028
  * @param {JxElement | string | (JxElement | string)[]} node
994
1029
  * @returns {boolean}
995
1030
  */
996
- function _isStaticNode(node) {
1031
+ function _isStaticNode(node: JxElement | string | (JxElement | string)[]) {
997
1032
  if (!node || typeof node !== "object") return true;
998
1033
  if (Array.isArray(node)) return node.every(_isStaticNode);
999
1034
 
@@ -1006,7 +1041,7 @@ function _isStaticNode(node) {
1006
1041
  if (node.state) {
1007
1042
  for (const def of Object.values(node.state)) {
1008
1043
  if (!def || typeof def !== "object") continue;
1009
- const d = /** @type {JxMutableNode} */ (def);
1044
+ const d = def as JxMutableNode;
1010
1045
  if (d.$prototype) return false;
1011
1046
  if (d.$ref) return false;
1012
1047
  }
@@ -1038,13 +1073,16 @@ function _isStaticNode(node) {
1038
1073
  * @param {Record<string, string>} [mediaQueries] - Project media query definitions
1039
1074
  * @returns {string} CSS text, or empty string if no styles
1040
1075
  */
1041
- export function buildComponentCSS(tagName, styleDef, doc = null, mediaQueries = {}) {
1042
- /** @type {string[]} */
1043
- const rules = [];
1076
+ export function buildComponentCSS(
1077
+ tagName: string,
1078
+ styleDef: JxStyle | null | undefined,
1079
+ doc: JxElement | null = null,
1080
+ mediaQueries: Record<string, string> = {},
1081
+ ) {
1082
+ const rules: string[] = [];
1044
1083
 
1045
1084
  if (styleDef && typeof styleDef === "object") {
1046
- /** @type {string[]} */
1047
- const decls = [];
1085
+ const decls: string[] = [];
1048
1086
  for (const [prop, value] of Object.entries(styleDef)) {
1049
1087
  if (
1050
1088
  prop.startsWith(":") ||
@@ -1069,7 +1107,7 @@ export function buildComponentCSS(tagName, styleDef, doc = null, mediaQueries =
1069
1107
  : prop.startsWith("@(")
1070
1108
  ? `@media ${prop.slice(1)}`
1071
1109
  : prop;
1072
- rules.push(`${atRule} { ${tagName} { ${toCSSText(/** @type {object} */ (val))} } }`);
1110
+ rules.push(`${atRule} { ${tagName} { ${toCSSText(val as Record<string, unknown>)} } }`);
1073
1111
  } else if (
1074
1112
  prop.startsWith(":") ||
1075
1113
  prop.startsWith(".") ||
@@ -1077,7 +1115,7 @@ export function buildComponentCSS(tagName, styleDef, doc = null, mediaQueries =
1077
1115
  prop.startsWith("[")
1078
1116
  ) {
1079
1117
  const resolved = prop.startsWith("&") ? prop.replace("&", tagName) : `${tagName}${prop}`;
1080
- rules.push(`${resolved} { ${toCSSText(/** @type {object} */ (val))} }`);
1118
+ rules.push(`${resolved} { ${toCSSText(val as Record<string, unknown>)} }`);
1081
1119
  }
1082
1120
  }
1083
1121
  }