@jxsuite/compiler 0.23.0 → 0.23.2
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/compiler",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.2",
|
|
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.
|
|
42
|
-
"@jxsuite/runtime": "^0.23.
|
|
41
|
+
"@jxsuite/parser": "^0.23.2",
|
|
42
|
+
"@jxsuite/runtime": "^0.23.2",
|
|
43
43
|
"remark-gfm": "^4.0.1",
|
|
44
44
|
"remark-stringify": "^11.0.0",
|
|
45
45
|
"sharp": "^0.34.5",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @module content-loader
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { readFileSync, readdirSync, existsSync
|
|
12
|
+
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
13
13
|
import { resolve, basename, extname } from "node:path";
|
|
14
14
|
|
|
15
15
|
// ─── CSV Parser (minimal, spec-compliant) ─────────────────────────────────────
|
|
@@ -291,9 +291,7 @@ async function loadContentType(name, contentTypeDef, projectRoot) {
|
|
|
291
291
|
/** @type {string[]} */
|
|
292
292
|
let files;
|
|
293
293
|
|
|
294
|
-
if (
|
|
295
|
-
files = globSync(source, { cwd: projectRoot }).map((f) => resolve(projectRoot, f));
|
|
296
|
-
} else if (extname(source)) {
|
|
294
|
+
if (extname(source)) {
|
|
297
295
|
files = existsSync(resolvedSource) ? [resolvedSource] : [];
|
|
298
296
|
} else {
|
|
299
297
|
const format = contentTypeDef.format || "md";
|
package/src/site/site-build.js
CHANGED
|
@@ -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,71 @@ 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
|
+
const style = /** @type {Record<string, unknown>} */ ({ ...v });
|
|
602
|
+
for (const [sk, sv] of Object.entries(style)) {
|
|
603
|
+
if (typeof sv === "string" && isTemplateString(sv)) {
|
|
604
|
+
style[sk] = evaluateMapTemplate(sv, scope) ?? sv;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
node.style = style;
|
|
608
|
+
} else if (k === "attributes" && v && typeof v === "object") {
|
|
609
|
+
const attrs = /** @type {Record<string, unknown>} */ ({ ...v });
|
|
610
|
+
for (const [ak, av] of Object.entries(attrs)) {
|
|
611
|
+
if (typeof av === "string" && isTemplateString(av)) {
|
|
612
|
+
attrs[ak] = evaluateMapTemplate(av, scope) ?? av;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
node.attributes = attrs;
|
|
616
|
+
} else if (typeof v === "string" && isTemplateString(v)) {
|
|
617
|
+
node[k] = evaluateMapTemplate(v, scope) ?? v;
|
|
618
|
+
} else {
|
|
619
|
+
node[k] = v;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return node;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Evaluate a template string in the context of a mapped array item. Exposes `item`, `index`,
|
|
627
|
+
* `state`, and `$map` as local variables.
|
|
628
|
+
*
|
|
629
|
+
* @param {string} str
|
|
630
|
+
* @param {Record<string, unknown>} scope
|
|
631
|
+
* @returns {unknown}
|
|
632
|
+
*/
|
|
633
|
+
function evaluateMapTemplate(str, scope) {
|
|
634
|
+
try {
|
|
635
|
+
const item = /** @type {Record<string, unknown>} */ (scope.$map)?.item;
|
|
636
|
+
const index = /** @type {Record<string, unknown>} */ (scope.$map)?.index;
|
|
637
|
+
const singleExprMatch = str.match(/^\$\{(.+)\}$/s);
|
|
638
|
+
if (singleExprMatch) {
|
|
639
|
+
const fn = new Function("state", "$map", "item", "index", `return (${singleExprMatch[1]})`);
|
|
640
|
+
return fn(scope, scope.$map, item, index);
|
|
641
|
+
}
|
|
642
|
+
const fn = new Function("state", "$map", "item", "index", `return \`${str}\``);
|
|
643
|
+
return fn(scope, scope.$map, item, index);
|
|
644
|
+
} catch {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
579
649
|
/**
|
|
580
650
|
* Recursively resolve template strings in a document tree against a scope. Mutates the document in
|
|
581
651
|
* place — evaluates ${...} in innerHTML, textContent, style values, and attribute values.
|
|
@@ -617,6 +687,36 @@ function resolveDocTemplates(node, scope) {
|
|
|
617
687
|
}
|
|
618
688
|
}
|
|
619
689
|
const rawChildren = /** @type {unknown} */ (node.children);
|
|
690
|
+
if (
|
|
691
|
+
rawChildren &&
|
|
692
|
+
typeof rawChildren === "object" &&
|
|
693
|
+
!Array.isArray(rawChildren) &&
|
|
694
|
+
/** @type {JxMutableNode} */ (rawChildren).$prototype === "Array"
|
|
695
|
+
) {
|
|
696
|
+
const arrayDef = /** @type {JxMutableNode} */ (rawChildren);
|
|
697
|
+
const itemsSrc = arrayDef.items;
|
|
698
|
+
let items = null;
|
|
699
|
+
if (itemsSrc && typeof itemsSrc === "object" && /** @type {JxMutableNode} */ (itemsSrc).$ref) {
|
|
700
|
+
const ref = /** @type {string} */ (/** @type {JxMutableNode} */ (itemsSrc).$ref);
|
|
701
|
+
items = resolveRefValue(ref, scope);
|
|
702
|
+
} else if (Array.isArray(itemsSrc)) {
|
|
703
|
+
items = itemsSrc;
|
|
704
|
+
}
|
|
705
|
+
if (Array.isArray(items) && arrayDef.map) {
|
|
706
|
+
node.children = /** @type {(string | JxElement)[]} */ (
|
|
707
|
+
items.map((item, index) => {
|
|
708
|
+
const childScope = Object.create(scope);
|
|
709
|
+
childScope.$map = { item, index };
|
|
710
|
+
childScope["$map/item"] = item;
|
|
711
|
+
childScope["$map/index"] = index;
|
|
712
|
+
const expanded = expandMapTemplate(arrayDef.map, childScope);
|
|
713
|
+
resolveDocTemplates(/** @type {JxElement} */ (expanded), childScope);
|
|
714
|
+
return expanded;
|
|
715
|
+
})
|
|
716
|
+
);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
620
720
|
if (typeof rawChildren === "string" && isTemplateString(rawChildren)) {
|
|
621
721
|
const resolved = evaluateStaticTemplate(rawChildren, scope);
|
|
622
722
|
if (Array.isArray(resolved)) {
|