@jxsuite/compiler 0.23.0 → 0.23.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/dist/compiler.js CHANGED
@@ -329,7 +329,7 @@ ${y.join(`,
329
329
  }
330
330
  </script>
331
331
  ${H}
332
- <script type="module" src="/${W}"></script>
332
+ <script type="module" src="./${W}"></script>
333
333
  </head>
334
334
  <body>
335
335
  ${I}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/compiler",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "description": "Jx static HTML compiler, island detector, and site builder",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,8 +38,8 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@apidevtools/json-schema-ref-parser": "^15.3.5",
41
- "@jxsuite/parser": "^0.23.0",
42
- "@jxsuite/runtime": "^0.23.0",
41
+ "@jxsuite/parser": "^0.23.1",
42
+ "@jxsuite/runtime": "^0.23.1",
43
43
  "remark-gfm": "^4.0.1",
44
44
  "remark-stringify": "^11.0.0",
45
45
  "sharp": "^0.34.5",
@@ -37,6 +37,7 @@ import {
37
37
  collectServerEntries,
38
38
  renderStaticNode,
39
39
  resolveStaticValue,
40
+ resolveRefValue,
40
41
  DEFAULT_REACTIVITY_SRC,
41
42
  DEFAULT_LIT_HTML_SRC,
42
43
  } from "../shared.js";
@@ -435,7 +436,9 @@ async function compilePage(
435
436
  expandComponents(layoutDoc, componentDefs);
436
437
 
437
438
  // Strip resolved timing: "compiler" state entries — they're now baked into the tree
438
- // and keeping them would cause isDynamic() to misclassify the page as dynamic
439
+ // and keeping them would cause isDynamic() to misclassify the page as dynamic.
440
+ // Also strip resolved content arrays (from ContentCollection) that have been
441
+ // baked into unrolled map templates.
439
442
  if (layoutDoc.state) {
440
443
  for (const [key, def] of Object.entries(layoutDoc.state)) {
441
444
  if (key === "$site" || key === "$page") continue;
@@ -446,6 +449,8 @@ async function compilePage(
446
449
  /** @type {JxMutableNode} */ (def).timing === "compiler"
447
450
  ) {
448
451
  delete layoutDoc.state[key];
452
+ } else if (Array.isArray(def)) {
453
+ delete layoutDoc.state[key];
449
454
  }
450
455
  }
451
456
  }
@@ -576,6 +581,69 @@ function isBareSpecifier(s) {
576
581
  );
577
582
  }
578
583
 
584
+ /**
585
+ * Deep-clone a map template, resolving template strings and $ref values against the given scope.
586
+ *
587
+ * @param {JxMutableNode} template
588
+ * @param {Record<string, unknown>} scope
589
+ * @returns {JxMutableNode}
590
+ */
591
+ function expandMapTemplate(template, scope) {
592
+ if (!template || typeof template !== "object") return template;
593
+ const node = /** @type {JxMutableNode} */ ({});
594
+ for (const [k, v] of Object.entries(template)) {
595
+ if (k === "children" && Array.isArray(v)) {
596
+ node.children = v.map((child) => {
597
+ if (typeof child === "string") return child;
598
+ return expandMapTemplate(/** @type {JxMutableNode} */ (child), scope);
599
+ });
600
+ } else if (k === "style" && v && typeof v === "object") {
601
+ node.style = { ...v };
602
+ for (const [sk, sv] of Object.entries(node.style)) {
603
+ if (typeof sv === "string" && isTemplateString(sv)) {
604
+ node.style[sk] = evaluateMapTemplate(sv, scope) ?? sv;
605
+ }
606
+ }
607
+ } else if (k === "attributes" && v && typeof v === "object") {
608
+ node.attributes = { ...v };
609
+ for (const [ak, av] of Object.entries(node.attributes)) {
610
+ if (typeof av === "string" && isTemplateString(av)) {
611
+ node.attributes[ak] = evaluateMapTemplate(av, scope) ?? av;
612
+ }
613
+ }
614
+ } else if (typeof v === "string" && isTemplateString(v)) {
615
+ node[k] = evaluateMapTemplate(v, scope) ?? v;
616
+ } else {
617
+ node[k] = v;
618
+ }
619
+ }
620
+ return node;
621
+ }
622
+
623
+ /**
624
+ * Evaluate a template string in the context of a mapped array item. Exposes `item`, `index`,
625
+ * `state`, and `$map` as local variables.
626
+ *
627
+ * @param {string} str
628
+ * @param {Record<string, unknown>} scope
629
+ * @returns {unknown}
630
+ */
631
+ function evaluateMapTemplate(str, scope) {
632
+ try {
633
+ const item = /** @type {Record<string, unknown>} */ (scope.$map)?.item;
634
+ const index = /** @type {Record<string, unknown>} */ (scope.$map)?.index;
635
+ const singleExprMatch = str.match(/^\$\{(.+)\}$/s);
636
+ if (singleExprMatch) {
637
+ const fn = new Function("state", "$map", "item", "index", `return (${singleExprMatch[1]})`);
638
+ return fn(scope, scope.$map, item, index);
639
+ }
640
+ const fn = new Function("state", "$map", "item", "index", `return \`${str}\``);
641
+ return fn(scope, scope.$map, item, index);
642
+ } catch {
643
+ return null;
644
+ }
645
+ }
646
+
579
647
  /**
580
648
  * Recursively resolve template strings in a document tree against a scope. Mutates the document in
581
649
  * place — evaluates ${...} in innerHTML, textContent, style values, and attribute values.
@@ -617,6 +685,34 @@ function resolveDocTemplates(node, scope) {
617
685
  }
618
686
  }
619
687
  const rawChildren = /** @type {unknown} */ (node.children);
688
+ if (
689
+ rawChildren &&
690
+ typeof rawChildren === "object" &&
691
+ !Array.isArray(rawChildren) &&
692
+ /** @type {JxMutableNode} */ (rawChildren).$prototype === "Array"
693
+ ) {
694
+ const arrayDef = /** @type {JxMutableNode} */ (rawChildren);
695
+ const itemsSrc = arrayDef.items;
696
+ let items = null;
697
+ if (itemsSrc && typeof itemsSrc === "object" && /** @type {JxMutableNode} */ (itemsSrc).$ref) {
698
+ const ref = /** @type {string} */ (/** @type {JxMutableNode} */ (itemsSrc).$ref);
699
+ items = resolveRefValue(ref, scope);
700
+ } else if (Array.isArray(itemsSrc)) {
701
+ items = itemsSrc;
702
+ }
703
+ if (Array.isArray(items) && arrayDef.map) {
704
+ node.children = items.map((item, index) => {
705
+ const childScope = Object.create(scope);
706
+ childScope.$map = { item, index };
707
+ childScope["$map/item"] = item;
708
+ childScope["$map/index"] = index;
709
+ const expanded = expandMapTemplate(arrayDef.map, childScope);
710
+ resolveDocTemplates(expanded, childScope);
711
+ return expanded;
712
+ });
713
+ return;
714
+ }
715
+ }
620
716
  if (typeof rawChildren === "string" && isTemplateString(rawChildren)) {
621
717
  const resolved = evaluateStaticTemplate(rawChildren, scope);
622
718
  if (Array.isArray(resolved)) {
@@ -203,7 +203,7 @@ ${importmapEntries.join(",\n")}
203
203
  }
204
204
  </script>
205
205
  ${styleBlock}
206
- <script type="module" src="/${modulePath}"></script>
206
+ <script type="module" src="./${modulePath}"></script>
207
207
  </head>
208
208
  <body>
209
209
  ${bodyContent}