@jxsuite/compiler 0.19.0 → 0.21.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.
@@ -65,10 +65,10 @@ const WRAPPER_TAGS = new Set([
65
65
  * Convert a Jx node to an array of mdast nodes. Returns an array because wrapper unwrapping can
66
66
  * produce multiple children.
67
67
  *
68
- * @param {any} node
69
- * @param {Map<string, any>} componentDefs
70
- * @param {any} [scope] - Current resolution scope
71
- * @returns {any[]}
68
+ * @param {JxElement | string} node
69
+ * @param {Map<string, JxElement>} componentDefs
70
+ * @param {Record<string, unknown> | null} [scope] - Current resolution scope
71
+ * @returns {MdastNode[]}
72
72
  */
73
73
  function nodeToMdast(node, componentDefs, scope) {
74
74
  // Bare text
@@ -153,8 +153,8 @@ function nodeToMdast(node, componentDefs, scope) {
153
153
  return [{ type: "inlineCode", value: text ?? "" }];
154
154
 
155
155
  case "link": {
156
- const href = node.attributes?.href ?? "";
157
- const title = node.attributes?.title ?? null;
156
+ const href = String(node.attributes?.href ?? "");
157
+ const title = /** @type {string | null} */ (node.attributes?.title ?? null);
158
158
  const children =
159
159
  text != null
160
160
  ? [{ type: "text", value: text }]
@@ -163,9 +163,9 @@ function nodeToMdast(node, componentDefs, scope) {
163
163
  }
164
164
 
165
165
  case "image": {
166
- const src = node.attributes?.src ?? "";
167
- const alt = node.attributes?.alt ?? "";
168
- const title = node.attributes?.title ?? null;
166
+ const src = String(node.attributes?.src ?? "");
167
+ const alt = String(node.attributes?.alt ?? "");
168
+ const title = /** @type {string | null} */ (node.attributes?.title ?? null);
169
169
  return [{ type: "image", url: src, alt, title }];
170
170
  }
171
171
 
@@ -199,10 +199,16 @@ function nodeToMdast(node, componentDefs, scope) {
199
199
  case "code": {
200
200
  // pre > code → fenced code block
201
201
  const codeChild = Array.isArray(node.children)
202
- ? node.children.find((/** @type {any} */ c) => c?.tagName === "code")
202
+ ? node.children.find(
203
+ (/** @type {JxElement | string} */ c) =>
204
+ /** @type {JxMutableNode} */ (c)?.tagName === "code",
205
+ )
203
206
  : null;
204
- const value = codeChild?.textContent ?? text ?? "";
205
- const lang = codeChild?.className?.replace("language-", "") ?? null;
207
+ const value = /** @type {JxElement | undefined} */ (codeChild)?.textContent ?? text ?? "";
208
+ const lang = /** @type {JxElement | undefined} */ (codeChild)?.className?.replace(
209
+ "language-",
210
+ "",
211
+ ) ?? null;
206
212
  return [{ type: "code", lang, value }];
207
213
  }
208
214
 
@@ -250,10 +256,10 @@ function isInlineType(type) {
250
256
  /**
251
257
  * Convert a node's children to mdast nodes (block context).
252
258
  *
253
- * @param {any} node
254
- * @param {Map<string, any>} componentDefs
255
- * @param {any} [scope]
256
- * @returns {any[]}
259
+ * @param {JxElement} node
260
+ * @param {Map<string, JxElement>} componentDefs
261
+ * @param {Record<string, unknown> | null} [scope]
262
+ * @returns {MdastNode[]}
257
263
  */
258
264
  function convertChildren(node, componentDefs, scope) {
259
265
  if (node.textContent != null) {
@@ -262,16 +268,18 @@ function convertChildren(node, componentDefs, scope) {
262
268
  return [];
263
269
  }
264
270
  if (!Array.isArray(node.children)) return [];
265
- return node.children.flatMap((/** @type {any} */ c) => nodeToMdast(c, componentDefs, scope));
271
+ return node.children.flatMap((/** @type {JxElement | string} */ c) =>
272
+ nodeToMdast(c, componentDefs, scope),
273
+ );
266
274
  }
267
275
 
268
276
  /**
269
277
  * Convert children in inline context — same as convertChildren but for inline content.
270
278
  *
271
- * @param {any} node
272
- * @param {Map<string, any>} componentDefs
273
- * @param {any} [scope]
274
- * @returns {any[]}
279
+ * @param {JxElement} node
280
+ * @param {Map<string, JxElement>} componentDefs
281
+ * @param {Record<string, unknown> | null} [scope]
282
+ * @returns {MdastNode[]}
275
283
  */
276
284
  function convertChildrenInline(node, componentDefs, scope) {
277
285
  if (node.textContent != null) {
@@ -280,7 +288,9 @@ function convertChildrenInline(node, componentDefs, scope) {
280
288
  return [];
281
289
  }
282
290
  if (!Array.isArray(node.children)) return [];
283
- return node.children.flatMap((/** @type {any} */ c) => nodeToMdast(c, componentDefs, scope));
291
+ return node.children.flatMap((/** @type {JxElement | string} */ c) =>
292
+ nodeToMdast(c, componentDefs, scope),
293
+ );
284
294
  }
285
295
 
286
296
  // ─── Component inlining ─────────────────────────────────────────────────────
@@ -288,10 +298,10 @@ function convertChildrenInline(node, componentDefs, scope) {
288
298
  /**
289
299
  * Inline a custom element by resolving its component definition.
290
300
  *
291
- * @param {any} node - The element instance (with $props, children, etc.)
301
+ * @param {JxElement} node - The element instance (with $props, children, etc.)
292
302
  * @param {string} tag - The tagName
293
- * @param {Map<string, any>} componentDefs
294
- * @returns {any[]}
303
+ * @param {Map<string, JxElement>} componentDefs
304
+ * @returns {MdastNode[]}
295
305
  */
296
306
  function inlineComponent(node, tag, componentDefs) {
297
307
  const def = componentDefs.get(tag);
@@ -314,10 +324,10 @@ function inlineComponent(node, tag, componentDefs) {
314
324
  ) {
315
325
  stateDefs[key] = { ...existing, default: value };
316
326
  } else {
317
- stateDefs[key] = value;
327
+ stateDefs[key] = /** @type {JxStateDefinition} */ (value);
318
328
  }
319
329
  } else {
320
- stateDefs[key] = value;
330
+ stateDefs[key] = /** @type {JxStateDefinition} */ (value);
321
331
  }
322
332
  }
323
333
 
@@ -331,10 +341,15 @@ function inlineComponent(node, tag, componentDefs) {
331
341
 
332
342
  // Convert to mdast, passing instance's own children as potential slot content
333
343
  const instanceChildren = node.children;
334
- return resolved.flatMap((/** @type {any} */ child) => {
344
+ return resolved.flatMap((/** @type {JxElement | string} */ child) => {
335
345
  // Replace slot elements with instance children
336
- if (child?.tagName === "slot" && Array.isArray(instanceChildren)) {
337
- return instanceChildren.flatMap((/** @type {any} */ c) => nodeToMdast(c, componentDefs));
346
+ if (
347
+ /** @type {JxMutableNode} */ (child)?.tagName === "slot" &&
348
+ Array.isArray(instanceChildren)
349
+ ) {
350
+ return instanceChildren.flatMap((/** @type {JxElement | string} */ c) =>
351
+ nodeToMdast(c, componentDefs),
352
+ );
338
353
  }
339
354
  return nodeToMdast(child, componentDefs, scope);
340
355
  });
@@ -343,35 +358,37 @@ function inlineComponent(node, tag, componentDefs) {
343
358
  /**
344
359
  * Deep-resolve template expressions in a node tree.
345
360
  *
346
- * @param {any} nodes
347
- * @param {any} scope
348
- * @returns {any[]}
361
+ * @param {(JxElement | string)[]} nodes
362
+ * @param {Record<string, unknown>} scope
363
+ * @returns {(JxElement | string)[]}
349
364
  */
350
365
  function deepResolve(nodes, scope) {
351
366
  if (!Array.isArray(nodes)) return [];
352
- return nodes.map((/** @type {any} */ node) => resolveNode(node, scope));
367
+ return nodes.map((/** @type {JxElement | string} */ node) => resolveNode(node, scope));
353
368
  }
354
369
 
355
370
  /**
356
371
  * Resolve template expressions in a single node.
357
372
  *
358
- * @param {any} node
359
- * @param {any} scope
360
- * @returns {any}
373
+ * @param {JxElement | string} node
374
+ * @param {Record<string, unknown>} scope
375
+ * @returns {JxElement | string}
361
376
  */
362
377
  function resolveNode(node, scope) {
363
378
  if (typeof node === "string") {
364
- return isTemplateString(node) ? (evaluateStaticTemplate(node, scope) ?? node) : node;
379
+ return isTemplateString(node) ? String(evaluateStaticTemplate(node, scope) ?? node) : node;
365
380
  }
366
381
  if (!node || typeof node !== "object") return node;
367
382
 
368
383
  const result = { ...node };
369
384
 
370
385
  if (typeof result.textContent === "string" && isTemplateString(result.textContent)) {
371
- result.textContent = evaluateStaticTemplate(result.textContent, scope) ?? result.textContent;
386
+ result.textContent = String(
387
+ evaluateStaticTemplate(result.textContent, scope) ?? result.textContent,
388
+ );
372
389
  }
373
390
  if (typeof result.innerHTML === "string" && isTemplateString(result.innerHTML)) {
374
- result.innerHTML = evaluateStaticTemplate(result.innerHTML, scope) ?? result.innerHTML;
391
+ result.innerHTML = String(evaluateStaticTemplate(result.innerHTML, scope) ?? result.innerHTML);
375
392
  }
376
393
  if (result.attributes) {
377
394
  result.attributes = { ...result.attributes };
@@ -393,23 +410,23 @@ function resolveNode(node, scope) {
393
410
  /**
394
411
  * Expand a $prototype: "Array" descriptor into concrete mdast nodes.
395
412
  *
396
- * @param {any} arrayDef
397
- * @param {Map<string, any>} componentDefs
398
- * @param {any} [scope]
399
- * @returns {any[]}
413
+ * @param {JxElement} arrayDef
414
+ * @param {Map<string, JxElement>} componentDefs
415
+ * @param {Record<string, unknown> | null} [scope]
416
+ * @returns {MdastNode[]}
400
417
  */
401
418
  function expandArray(arrayDef, componentDefs, scope) {
402
- const itemsRef = arrayDef.items?.$ref;
419
+ const itemsRef = /** @type {JxMutableNode} */ (arrayDef).items?.$ref;
403
420
  if (!itemsRef || !scope) return [];
404
421
 
405
422
  // Resolve the items array from scope
406
423
  const items = resolveRef(itemsRef, scope);
407
424
  if (!Array.isArray(items)) return [];
408
425
 
409
- const mapTemplate = arrayDef.map;
426
+ const mapTemplate = /** @type {JxMutableNode} */ (arrayDef).map;
410
427
  if (!mapTemplate) return [];
411
428
 
412
- return items.flatMap((/** @type {any} */ item, /** @type {number} */ index) => {
429
+ return items.flatMap((/** @type {JxMutableNode} */ item, /** @type {number} */ index) => {
413
430
  // Create a scope with $map values
414
431
  const mapScope = Object.create(scope);
415
432
  mapScope.item = item;
@@ -424,8 +441,8 @@ function expandArray(arrayDef, componentDefs, scope) {
424
441
  /**
425
442
  * Resolve $map/ references in a map template node.
426
443
  *
427
- * @param {any} node
428
- * @param {any} item
444
+ * @param {JxMutableNode} node
445
+ * @param {Record<string, unknown>} item
429
446
  * @returns {any}
430
447
  */
431
448
  function resolveMapNode(node, item) {
@@ -436,8 +453,8 @@ function resolveMapNode(node, item) {
436
453
 
437
454
  // Resolve $ref values
438
455
  for (const [key, value] of Object.entries(result)) {
439
- if (value && typeof value === "object" && value.$ref) {
440
- const ref = value.$ref;
456
+ if (value && typeof value === "object" && /** @type {JxMutableNode} */ (value).$ref) {
457
+ const ref = /** @type {string} */ (/** @type {JxMutableNode} */ (value).$ref);
441
458
  if (ref.startsWith("$map/")) {
442
459
  const path = ref.slice("$map/".length);
443
460
  result[key] = resolvePath(
@@ -457,7 +474,9 @@ function resolveMapNode(node, item) {
457
474
  }
458
475
 
459
476
  if (Array.isArray(result.children)) {
460
- result.children = result.children.map((/** @type {any} */ c) => resolveMapNode(c, item));
477
+ result.children = result.children.map((c) =>
478
+ resolveMapNode(/** @type {JxMutableNode} */ (/** @type {unknown} */ (c)), item),
479
+ );
461
480
  }
462
481
 
463
482
  return result;
@@ -466,13 +485,13 @@ function resolveMapNode(node, item) {
466
485
  /**
467
486
  * Resolve a dot/slash-separated path on an object.
468
487
  *
469
- * @param {any} obj
488
+ * @param {unknown} obj
470
489
  * @param {string} path
471
490
  * @returns {any}
472
491
  */
473
492
  function resolvePath(obj, path) {
474
493
  const parts = path.split(/[/.]/);
475
- let current = obj;
494
+ let current = /** @type {JxMutableNode} */ (obj);
476
495
  for (const part of parts) {
477
496
  if (current == null) return undefined;
478
497
  current = current[part];
@@ -484,8 +503,8 @@ function resolvePath(obj, path) {
484
503
  * Resolve a $ref string against a scope.
485
504
  *
486
505
  * @param {string} ref
487
- * @param {any} scope
488
- * @returns {any}
506
+ * @param {Record<string, unknown>} scope
507
+ * @returns {unknown}
489
508
  */
490
509
  function resolveRef(ref, scope) {
491
510
  if (ref.startsWith("#/state/")) {
@@ -499,10 +518,10 @@ function resolveRef(ref, scope) {
499
518
  /**
500
519
  * Convert a table element to mdast table node.
501
520
  *
502
- * @param {any} node
503
- * @param {Map<string, any>} componentDefs
504
- * @param {any} [scope]
505
- * @returns {any[]}
521
+ * @param {JxElement} node
522
+ * @param {Map<string, JxElement>} componentDefs
523
+ * @param {Record<string, unknown> | null} [scope]
524
+ * @returns {MdastNode[]}
506
525
  */
507
526
  function convertTable(node, componentDefs, scope) {
508
527
  // Flatten thead/tbody wrappers to get rows
@@ -516,8 +535,8 @@ function convertTable(node, componentDefs, scope) {
516
535
  /**
517
536
  * Resolve text content, handling template strings if a scope is available.
518
537
  *
519
- * @param {any} value
520
- * @param {any} [scope]
538
+ * @param {unknown} value
539
+ * @param {Record<string, unknown> | null} [scope]
521
540
  * @returns {string | null}
522
541
  */
523
542
  function resolveText(value, scope) {
@@ -539,10 +558,10 @@ function resolveText(value, scope) {
539
558
  * markdown content.
540
559
  *
541
560
  * @param {string} html
542
- * @returns {any[]}
561
+ * @returns {MdastNode[]}
543
562
  */
544
563
  function htmlToMdast(html) {
545
- /** @type {any[]} */
564
+ /** @type {MdastNode[]} */
546
565
  const nodes = [];
547
566
 
548
567
  // Simple top-level block parser
@@ -594,7 +613,7 @@ function splitHtmlBlocks(html) {
594
613
  * Parse a single HTML element string into mdast node(s).
595
614
  *
596
615
  * @param {string} html
597
- * @returns {any[] | null}
616
+ * @returns {MdastNode[] | null}
598
617
  */
599
618
  function parseHtmlElement(html) {
600
619
  // Heading
@@ -679,10 +698,10 @@ function parseHtmlElement(html) {
679
698
  * Parse inline HTML content to mdast inline nodes.
680
699
  *
681
700
  * @param {string} html
682
- * @returns {any[]}
701
+ * @returns {MdastNode[]}
683
702
  */
684
703
  function parseInlineHtml(html) {
685
- /** @type {any[]} */
704
+ /** @type {MdastNode[]} */
686
705
  const nodes = [];
687
706
  let pos = 0;
688
707
 
@@ -813,10 +832,10 @@ function findMatchingClose(html, start, tag) {
813
832
  * Parse <li> elements from list HTML.
814
833
  *
815
834
  * @param {string} html
816
- * @returns {any[]}
835
+ * @returns {MdastNode[]}
817
836
  */
818
837
  function parseListItems(html) {
819
- /** @type {any[]} */
838
+ /** @type {MdastNode[]} */
820
839
  const items = [];
821
840
  const liPattern = /<li(?:\s[^>]*)?>([\s\S]*?)<\/li>/gi;
822
841
  let m;
@@ -835,16 +854,16 @@ function parseListItems(html) {
835
854
  * Parse an HTML table to mdast table node.
836
855
  *
837
856
  * @param {string} html
838
- * @returns {any[]}
857
+ * @returns {MdastNode[]}
839
858
  */
840
859
  function parseHtmlTable(html) {
841
- /** @type {any[]} */
860
+ /** @type {MdastNode[]} */
842
861
  const rows = [];
843
862
  const trPattern = /<tr(?:\s[^>]*)?>([\s\S]*?)<\/tr>/gi;
844
863
  let m;
845
864
  while ((m = trPattern.exec(html)) !== null) {
846
865
  const cellPattern = /<(?:th|td)(?:\s[^>]*)?>([\s\S]*?)<\/(?:th|td)>/gi;
847
- /** @type {any[]} */
866
+ /** @type {MdastNode[]} */
848
867
  const cells = [];
849
868
  let c;
850
869
  while ((c = cellPattern.exec(m[1])) !== null) {
@@ -888,8 +907,8 @@ function decodeHtmlEntities(str) {
888
907
  /**
889
908
  * Compile a fully-resolved Jx document to clean markdown.
890
909
  *
891
- * @param {any} doc - Resolved Jx document (post layout, context, prototypes, templates)
892
- * @param {Map<string, any>} [componentDefs] - Component definitions for inlining
910
+ * @param {JxDocument} doc - Resolved Jx document (post layout, context, prototypes, templates)
911
+ * @param {Map<string, JxElement>} [componentDefs] - Component definitions for inlining
893
912
  * @returns {{ content: string }}
894
913
  */
895
914
  export function compileMarkdown(doc, componentDefs = new Map()) {
@@ -901,14 +920,14 @@ export function compileMarkdown(doc, componentDefs = new Map()) {
901
920
  const scope = doc.state ? buildInitialScope(doc.state, null) : null;
902
921
 
903
922
  // Convert to mdast
904
- const mdastChildren = doc.children.flatMap((/** @type {any} */ child) =>
923
+ const mdastChildren = doc.children.flatMap((/** @type {JxElement | string} */ child) =>
905
924
  nodeToMdast(child, componentDefs, scope),
906
925
  );
907
926
 
908
927
  // Clean up: ensure block-level structure (no bare inline nodes at root)
909
- /** @type {any[]} */
928
+ /** @type {MdastNode[]} */
910
929
  const cleaned = [];
911
- /** @type {any[]} */
930
+ /** @type {MdastNode[]} */
912
931
  let inlineBuf = [];
913
932
 
914
933
  const flushInline = () => {
@@ -928,7 +947,9 @@ export function compileMarkdown(doc, componentDefs = new Map()) {
928
947
  }
929
948
  flushInline();
930
949
 
931
- const mdast = /** @type {any} */ ({ type: "root", children: cleaned });
950
+ const mdast = /** @type {import("mdast").Root} */ (
951
+ /** @type {unknown} */ ({ type: "root", children: cleaned })
952
+ );
932
953
 
933
954
  const md = unified()
934
955
  .use(remarkGfm)
@@ -24,7 +24,7 @@ import { collectServerEntries } from "../shared.js";
24
24
  */
25
25
  export async function compileServer(sourcePath, opts = {}) {
26
26
  const { baseUrl = "/_jx/server" } = opts;
27
- const doc = await $RefParser.dereference(sourcePath);
27
+ const doc = /** @type {JxElement} */ (await $RefParser.dereference(sourcePath));
28
28
  const entries = collectServerEntries(doc);
29
29
  if (entries.length === 0) return null;
30
30
 
@@ -18,15 +18,20 @@ import { emitElementModule } from "./compile-element.js";
18
18
  /**
19
19
  * Compile a static document to HTML, with dynamic subtrees as islands.
20
20
  *
21
- * @param {any} raw - Raw JSON document (with $ref pointers preserved)
22
- * @param {any} opts
21
+ * @param {JxMutableNode | Record<string, any>} raw - Raw JSON document (with $ref pointers
22
+ * preserved)
23
+ * @param {Record<string, unknown>} opts
23
24
  * @returns {{ html: string; files: { path: string; content: string; tagName: string }[] }}
24
25
  */
25
26
  export function compileStaticPage(raw, opts) {
26
- const { title, reactivitySrc, litHtmlSrc } = opts;
27
+ const { title, reactivitySrc, litHtmlSrc } = /** @type {JxMutableNode} */ (opts);
27
28
 
28
29
  const rootContext = createCompileContext(raw, null, raw.state ?? {}, raw.$media ?? {});
29
- const styleBlock = compileStyles(raw, raw.$media ?? {}, opts.projectStyle ?? null);
30
+ const styleBlock = compileStyles(
31
+ raw,
32
+ raw.$media ?? {},
33
+ /** @type {JxStyle | null} */ (opts.projectStyle ?? null),
34
+ );
30
35
  /** @type {{ def: any; tagName: string; className: string }[]} */
31
36
  const islands = [];
32
37
  const bodyContent = compileNode(raw, false, raw, rootContext, islands);
@@ -83,9 +88,9 @@ export function compileStaticPage(raw, opts) {
83
88
  * Compile a single Jx node to an HTML string. Dynamic nodes become hydration islands; static nodes
84
89
  * become plain HTML.
85
90
  *
86
- * @param {any} def
91
+ * @param {JxMutableNode} def
87
92
  * @param {boolean} dynamic
88
- * @param {any} raw
93
+ * @param {JxMutableNode} raw
89
94
  * @param {any} context
90
95
  * @param {{ def: any; tagName: string; className: string }[]} islands
91
96
  * @returns {string}
@@ -127,8 +132,8 @@ function compileNode(def, dynamic, raw, context, islands) {
127
132
  * Build the inner HTML (textContent or children) for a node. For children, emit islands only for
128
133
  * those that are actually dynamic.
129
134
  *
130
- * @param {any} def
131
- * @param {any} raw
135
+ * @param {JxMutableNode} def
136
+ * @param {JxMutableNode} raw
132
137
  * @param {any} context
133
138
  * @param {{ def: any; tagName: string; className: string }[]} islands
134
139
  * @returns {string}
@@ -141,14 +146,20 @@ function buildInnerWithIslands(def, raw, context, islands) {
141
146
  return value == null ? "" : escapeHtml(String(value));
142
147
  }
143
148
  if (source.innerHTML)
144
- return resolveStaticValue(source.innerHTML, context.scope) ?? source.innerHTML;
149
+ return (
150
+ /** @type {string} */ (resolveStaticValue(source.innerHTML, context.scope)) ??
151
+ source.innerHTML
152
+ );
145
153
  if (Array.isArray(source.children)) {
146
154
  const rawChildren = raw?.children;
147
155
  return source.children
148
- .map((/** @type {any} */ c, /** @type {number} */ i) => {
149
- const childDynamic = isNodeDynamic(c);
150
- const childRaw = rawChildren?.[i] ?? c;
151
- return compileNode(c, childDynamic, childRaw, context, islands);
156
+ .map((c, /** @type {number} */ i) => {
157
+ const child = /** @type {JxMutableNode} */ (/** @type {unknown} */ (c));
158
+ const childDynamic = isNodeDynamic(child);
159
+ const childRaw = /** @type {JxMutableNode} */ (
160
+ /** @type {unknown} */ (rawChildren?.[i] ?? c)
161
+ );
162
+ return compileNode(child, childDynamic, childRaw, context, islands);
152
163
  })
153
164
  .join("\n ");
154
165
  }