@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.
- package/dist/compiler.js +155 -155
- package/package.json +18 -16
- package/src/{cli.js → cli.ts} +2 -2
- package/src/{compile-cli.js → compile-cli.ts} +1 -1
- package/src/{compiler.js → compiler.ts} +10 -8
- package/src/{shared.js → shared.ts} +161 -123
- package/src/site/{content-loader.js → content-loader.ts} +59 -52
- package/src/site/{context-injection.js → context-injection.ts} +18 -12
- package/src/site/{head-merger.js → head-merger.ts} +17 -11
- package/src/site/{image-cache.js → image-cache.ts} +22 -22
- package/src/site/{image-optimizer.js → image-optimizer.ts} +35 -44
- package/src/site/{image-transform.js → image-transform.ts} +52 -22
- package/src/site/{layout-resolver.js → layout-resolver.ts} +31 -26
- package/src/site/{pages-discovery.js → pages-discovery.ts} +31 -30
- package/src/site/{prototype-resolver.js → prototype-resolver.ts} +16 -5
- package/src/site/{site-build.js → site-build.ts} +134 -130
- package/src/site/{site-loader.js → site-loader.ts} +2 -2
- package/src/targets/{compile-class.js → compile-class.ts} +20 -19
- package/src/targets/{compile-client.js → compile-client.ts} +84 -105
- package/src/targets/{compile-element.js → compile-element.ts} +82 -88
- package/src/targets/{compile-markdown.js → compile-markdown.ts} +95 -81
- package/src/targets/{compile-server.js → compile-server.ts} +23 -6
- package/src/targets/{compile-static.js → compile-static.ts} +35 -18
- package/src/types.ts +16 -0
|
@@ -9,12 +9,19 @@
|
|
|
9
9
|
import { unified } from "unified";
|
|
10
10
|
import remarkStringify from "remark-stringify";
|
|
11
11
|
import remarkGfm from "remark-gfm";
|
|
12
|
-
import { buildInitialScope, evaluateStaticTemplate, isTemplateString } from "../shared.
|
|
12
|
+
import { buildInitialScope, evaluateStaticTemplate, isTemplateString } from "../shared.ts";
|
|
13
|
+
import type {
|
|
14
|
+
JxElement,
|
|
15
|
+
JxDocument,
|
|
16
|
+
JxStateDefinition,
|
|
17
|
+
JxMutableNode,
|
|
18
|
+
} from "@jxsuite/schema/types";
|
|
19
|
+
import type { MdastNode } from "@jxsuite/parser/types";
|
|
13
20
|
|
|
14
21
|
// ─── Tag classification ────────────────────────────────────────────────────
|
|
15
22
|
|
|
16
23
|
/** Tags that map directly to mdast node types. */
|
|
17
|
-
const TAG_MDAST_MAP =
|
|
24
|
+
const TAG_MDAST_MAP = {
|
|
18
25
|
h1: "heading",
|
|
19
26
|
h2: "heading",
|
|
20
27
|
h3: "heading",
|
|
@@ -41,7 +48,7 @@ const TAG_MDAST_MAP = /** @type {Record<string, string>} */ ({
|
|
|
41
48
|
tr: "tableRow",
|
|
42
49
|
th: "tableCell",
|
|
43
50
|
td: "tableCell",
|
|
44
|
-
}
|
|
51
|
+
} as Record<string, string>;
|
|
45
52
|
|
|
46
53
|
/** Wrapper tags that should be unwrapped — their children are promoted. */
|
|
47
54
|
const WRAPPER_TAGS = new Set([
|
|
@@ -70,7 +77,11 @@ const WRAPPER_TAGS = new Set([
|
|
|
70
77
|
* @param {Record<string, unknown> | null} [scope] - Current resolution scope
|
|
71
78
|
* @returns {MdastNode[]}
|
|
72
79
|
*/
|
|
73
|
-
function nodeToMdast(
|
|
80
|
+
function nodeToMdast(
|
|
81
|
+
node: JxElement | string,
|
|
82
|
+
componentDefs: Map<string, JxElement>,
|
|
83
|
+
scope?: Record<string, unknown> | null,
|
|
84
|
+
): MdastNode[] {
|
|
74
85
|
// Bare text
|
|
75
86
|
if (typeof node === "string") {
|
|
76
87
|
return node.trim() ? [{ type: "text", value: node }] : [];
|
|
@@ -154,7 +165,7 @@ function nodeToMdast(node, componentDefs, scope) {
|
|
|
154
165
|
|
|
155
166
|
case "link": {
|
|
156
167
|
const href = String(node.attributes?.href ?? "");
|
|
157
|
-
const title =
|
|
168
|
+
const title = (node.attributes?.title as string | null) ?? null;
|
|
158
169
|
const children =
|
|
159
170
|
text != null
|
|
160
171
|
? [{ type: "text", value: text }]
|
|
@@ -165,7 +176,7 @@ function nodeToMdast(node, componentDefs, scope) {
|
|
|
165
176
|
case "image": {
|
|
166
177
|
const src = String(node.attributes?.src ?? "");
|
|
167
178
|
const alt = String(node.attributes?.alt ?? "");
|
|
168
|
-
const title =
|
|
179
|
+
const title = (node.attributes?.title as string | null) ?? null;
|
|
169
180
|
return [{ type: "image", url: src, alt, title }];
|
|
170
181
|
}
|
|
171
182
|
|
|
@@ -199,16 +210,11 @@ function nodeToMdast(node, componentDefs, scope) {
|
|
|
199
210
|
case "code": {
|
|
200
211
|
// pre > code → fenced code block
|
|
201
212
|
const codeChild = Array.isArray(node.children)
|
|
202
|
-
? node.children.find(
|
|
203
|
-
(/** @type {JxElement | string} */ c) =>
|
|
204
|
-
/** @type {JxMutableNode} */ (c)?.tagName === "code",
|
|
205
|
-
)
|
|
213
|
+
? node.children.find((c: JxElement | string) => (c as JxMutableNode)?.tagName === "code")
|
|
206
214
|
: null;
|
|
207
|
-
const value =
|
|
208
|
-
const lang =
|
|
209
|
-
"language-",
|
|
210
|
-
"",
|
|
211
|
-
) ?? null;
|
|
215
|
+
const value = (codeChild as JxElement | undefined)?.textContent ?? text ?? "";
|
|
216
|
+
const lang =
|
|
217
|
+
(codeChild as JxElement | undefined)?.className?.replace("language-", "") ?? null;
|
|
212
218
|
return [{ type: "code", lang, value }];
|
|
213
219
|
}
|
|
214
220
|
|
|
@@ -247,7 +253,7 @@ function nodeToMdast(node, componentDefs, scope) {
|
|
|
247
253
|
* @param {string} type
|
|
248
254
|
* @returns {boolean}
|
|
249
255
|
*/
|
|
250
|
-
function isInlineType(type) {
|
|
256
|
+
function isInlineType(type: string) {
|
|
251
257
|
return ["text", "emphasis", "strong", "delete", "inlineCode", "link", "image", "break"].includes(
|
|
252
258
|
type,
|
|
253
259
|
);
|
|
@@ -261,16 +267,18 @@ function isInlineType(type) {
|
|
|
261
267
|
* @param {Record<string, unknown> | null} [scope]
|
|
262
268
|
* @returns {MdastNode[]}
|
|
263
269
|
*/
|
|
264
|
-
function convertChildren(
|
|
270
|
+
function convertChildren(
|
|
271
|
+
node: JxElement,
|
|
272
|
+
componentDefs: Map<string, JxElement>,
|
|
273
|
+
scope?: Record<string, unknown> | null,
|
|
274
|
+
): MdastNode[] {
|
|
265
275
|
if (node.textContent != null) {
|
|
266
276
|
const text = resolveText(node.textContent, scope);
|
|
267
277
|
if (text) return [{ type: "text", value: text }];
|
|
268
278
|
return [];
|
|
269
279
|
}
|
|
270
280
|
if (!Array.isArray(node.children)) return [];
|
|
271
|
-
return node.children.flatMap((
|
|
272
|
-
nodeToMdast(c, componentDefs, scope),
|
|
273
|
-
);
|
|
281
|
+
return node.children.flatMap((c: JxElement | string) => nodeToMdast(c, componentDefs, scope));
|
|
274
282
|
}
|
|
275
283
|
|
|
276
284
|
/**
|
|
@@ -281,16 +289,18 @@ function convertChildren(node, componentDefs, scope) {
|
|
|
281
289
|
* @param {Record<string, unknown> | null} [scope]
|
|
282
290
|
* @returns {MdastNode[]}
|
|
283
291
|
*/
|
|
284
|
-
function convertChildrenInline(
|
|
292
|
+
function convertChildrenInline(
|
|
293
|
+
node: JxElement,
|
|
294
|
+
componentDefs: Map<string, JxElement>,
|
|
295
|
+
scope?: Record<string, unknown> | null,
|
|
296
|
+
): MdastNode[] {
|
|
285
297
|
if (node.textContent != null) {
|
|
286
298
|
const text = resolveText(node.textContent, scope);
|
|
287
299
|
if (text) return [{ type: "text", value: text }];
|
|
288
300
|
return [];
|
|
289
301
|
}
|
|
290
302
|
if (!Array.isArray(node.children)) return [];
|
|
291
|
-
return node.children.flatMap((
|
|
292
|
-
nodeToMdast(c, componentDefs, scope),
|
|
293
|
-
);
|
|
303
|
+
return node.children.flatMap((c: JxElement | string) => nodeToMdast(c, componentDefs, scope));
|
|
294
304
|
}
|
|
295
305
|
|
|
296
306
|
// ─── Component inlining ─────────────────────────────────────────────────────
|
|
@@ -303,7 +313,11 @@ function convertChildrenInline(node, componentDefs, scope) {
|
|
|
303
313
|
* @param {Map<string, JxElement>} componentDefs
|
|
304
314
|
* @returns {MdastNode[]}
|
|
305
315
|
*/
|
|
306
|
-
function inlineComponent(
|
|
316
|
+
function inlineComponent(
|
|
317
|
+
node: JxElement,
|
|
318
|
+
tag: string,
|
|
319
|
+
componentDefs: Map<string, JxElement>,
|
|
320
|
+
): MdastNode[] {
|
|
307
321
|
const def = componentDefs.get(tag);
|
|
308
322
|
if (!def) {
|
|
309
323
|
// No definition — unwrap any children the instance has
|
|
@@ -324,10 +338,10 @@ function inlineComponent(node, tag, componentDefs) {
|
|
|
324
338
|
) {
|
|
325
339
|
stateDefs[key] = { ...existing, default: value };
|
|
326
340
|
} else {
|
|
327
|
-
stateDefs[key] =
|
|
341
|
+
stateDefs[key] = value as JxStateDefinition;
|
|
328
342
|
}
|
|
329
343
|
} else {
|
|
330
|
-
stateDefs[key] =
|
|
344
|
+
stateDefs[key] = value as JxStateDefinition;
|
|
331
345
|
}
|
|
332
346
|
}
|
|
333
347
|
|
|
@@ -341,13 +355,10 @@ function inlineComponent(node, tag, componentDefs) {
|
|
|
341
355
|
|
|
342
356
|
// Convert to mdast, passing instance's own children as potential slot content
|
|
343
357
|
const instanceChildren = node.children;
|
|
344
|
-
return resolved.flatMap((
|
|
358
|
+
return resolved.flatMap((child: JxElement | string): MdastNode[] => {
|
|
345
359
|
// Replace slot elements with instance children
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
Array.isArray(instanceChildren)
|
|
349
|
-
) {
|
|
350
|
-
return instanceChildren.flatMap((/** @type {JxElement | string} */ c) =>
|
|
360
|
+
if (typeof child !== "string" && child?.tagName === "slot" && Array.isArray(instanceChildren)) {
|
|
361
|
+
return instanceChildren.flatMap((c: JxElement | string): MdastNode[] =>
|
|
351
362
|
nodeToMdast(c, componentDefs),
|
|
352
363
|
);
|
|
353
364
|
}
|
|
@@ -362,9 +373,9 @@ function inlineComponent(node, tag, componentDefs) {
|
|
|
362
373
|
* @param {Record<string, unknown>} scope
|
|
363
374
|
* @returns {(JxElement | string)[]}
|
|
364
375
|
*/
|
|
365
|
-
function deepResolve(nodes, scope) {
|
|
376
|
+
function deepResolve(nodes: (JxElement | string)[], scope: Record<string, unknown>) {
|
|
366
377
|
if (!Array.isArray(nodes)) return [];
|
|
367
|
-
return nodes.map((
|
|
378
|
+
return nodes.map((node: JxElement | string) => resolveNode(node, scope));
|
|
368
379
|
}
|
|
369
380
|
|
|
370
381
|
/**
|
|
@@ -374,7 +385,7 @@ function deepResolve(nodes, scope) {
|
|
|
374
385
|
* @param {Record<string, unknown>} scope
|
|
375
386
|
* @returns {JxElement | string}
|
|
376
387
|
*/
|
|
377
|
-
function resolveNode(node, scope) {
|
|
388
|
+
function resolveNode(node: JxElement | string, scope: Record<string, unknown>) {
|
|
378
389
|
if (typeof node === "string") {
|
|
379
390
|
return isTemplateString(node) ? String(evaluateStaticTemplate(node, scope) ?? node) : node;
|
|
380
391
|
}
|
|
@@ -415,18 +426,22 @@ function resolveNode(node, scope) {
|
|
|
415
426
|
* @param {Record<string, unknown> | null} [scope]
|
|
416
427
|
* @returns {MdastNode[]}
|
|
417
428
|
*/
|
|
418
|
-
function expandArray(
|
|
419
|
-
|
|
429
|
+
function expandArray(
|
|
430
|
+
arrayDef: JxElement,
|
|
431
|
+
componentDefs: Map<string, JxElement>,
|
|
432
|
+
scope?: Record<string, unknown> | null,
|
|
433
|
+
): MdastNode[] {
|
|
434
|
+
const itemsRef = (arrayDef as JxMutableNode).items?.$ref;
|
|
420
435
|
if (!itemsRef || !scope) return [];
|
|
421
436
|
|
|
422
437
|
// Resolve the items array from scope
|
|
423
438
|
const items = resolveRef(itemsRef, scope);
|
|
424
439
|
if (!Array.isArray(items)) return [];
|
|
425
440
|
|
|
426
|
-
const mapTemplate =
|
|
441
|
+
const mapTemplate = (arrayDef as JxMutableNode).map;
|
|
427
442
|
if (!mapTemplate) return [];
|
|
428
443
|
|
|
429
|
-
return items.flatMap((
|
|
444
|
+
return items.flatMap((item: JxMutableNode, index: number): MdastNode[] => {
|
|
430
445
|
// Create a scope with $map values
|
|
431
446
|
const mapScope = Object.create(scope);
|
|
432
447
|
mapScope.item = item;
|
|
@@ -434,7 +449,7 @@ function expandArray(arrayDef, componentDefs, scope) {
|
|
|
434
449
|
|
|
435
450
|
// Resolve the map template with $map refs
|
|
436
451
|
const resolved = resolveMapNode(mapTemplate, item);
|
|
437
|
-
return nodeToMdast(resolved, componentDefs, scope);
|
|
452
|
+
return nodeToMdast(resolved as JxElement | string, componentDefs, scope);
|
|
438
453
|
});
|
|
439
454
|
}
|
|
440
455
|
|
|
@@ -445,7 +460,7 @@ function expandArray(arrayDef, componentDefs, scope) {
|
|
|
445
460
|
* @param {Record<string, unknown>} item
|
|
446
461
|
* @returns {any}
|
|
447
462
|
*/
|
|
448
|
-
function resolveMapNode(node, item) {
|
|
463
|
+
function resolveMapNode(node: JxMutableNode, item: Record<string, unknown>) {
|
|
449
464
|
if (typeof node === "string") return node;
|
|
450
465
|
if (!node || typeof node !== "object") return node;
|
|
451
466
|
|
|
@@ -453,8 +468,8 @@ function resolveMapNode(node, item) {
|
|
|
453
468
|
|
|
454
469
|
// Resolve $ref values
|
|
455
470
|
for (const [key, value] of Object.entries(result)) {
|
|
456
|
-
if (value && typeof value === "object" &&
|
|
457
|
-
const ref =
|
|
471
|
+
if (value && typeof value === "object" && (value as JxMutableNode).$ref) {
|
|
472
|
+
const ref = (value as JxMutableNode).$ref as string;
|
|
458
473
|
if (ref.startsWith("$map/")) {
|
|
459
474
|
const path = ref.slice("$map/".length);
|
|
460
475
|
result[key] = resolvePath(
|
|
@@ -470,12 +485,14 @@ function resolveMapNode(node, item) {
|
|
|
470
485
|
}
|
|
471
486
|
|
|
472
487
|
if (typeof result.textContent === "string" && result.textContent.startsWith("$map/")) {
|
|
473
|
-
result.textContent = resolvePath(item, result.textContent.slice("$map/".length))
|
|
488
|
+
result.textContent = resolvePath(item, result.textContent.slice("$map/".length)) as unknown as
|
|
489
|
+
| string
|
|
490
|
+
| null;
|
|
474
491
|
}
|
|
475
492
|
|
|
476
493
|
if (Array.isArray(result.children)) {
|
|
477
494
|
result.children = result.children.map((c) =>
|
|
478
|
-
resolveMapNode(
|
|
495
|
+
resolveMapNode(c as unknown as JxMutableNode, item),
|
|
479
496
|
);
|
|
480
497
|
}
|
|
481
498
|
|
|
@@ -489,9 +506,9 @@ function resolveMapNode(node, item) {
|
|
|
489
506
|
* @param {string} path
|
|
490
507
|
* @returns {any}
|
|
491
508
|
*/
|
|
492
|
-
function resolvePath(obj, path) {
|
|
509
|
+
function resolvePath(obj: unknown, path: string) {
|
|
493
510
|
const parts = path.split(/[/.]/);
|
|
494
|
-
let current =
|
|
511
|
+
let current = obj as JxMutableNode;
|
|
495
512
|
for (const part of parts) {
|
|
496
513
|
if (current == null) return undefined;
|
|
497
514
|
current = current[part];
|
|
@@ -506,7 +523,7 @@ function resolvePath(obj, path) {
|
|
|
506
523
|
* @param {Record<string, unknown>} scope
|
|
507
524
|
* @returns {unknown}
|
|
508
525
|
*/
|
|
509
|
-
function resolveRef(ref, scope) {
|
|
526
|
+
function resolveRef(ref: string, scope: Record<string, unknown>) {
|
|
510
527
|
if (ref.startsWith("#/state/")) {
|
|
511
528
|
return resolvePath(scope, ref.slice("#/state/".length));
|
|
512
529
|
}
|
|
@@ -523,7 +540,11 @@ function resolveRef(ref, scope) {
|
|
|
523
540
|
* @param {Record<string, unknown> | null} [scope]
|
|
524
541
|
* @returns {MdastNode[]}
|
|
525
542
|
*/
|
|
526
|
-
function convertTable(
|
|
543
|
+
function convertTable(
|
|
544
|
+
node: JxElement,
|
|
545
|
+
componentDefs: Map<string, JxElement>,
|
|
546
|
+
scope?: Record<string, unknown> | null,
|
|
547
|
+
) {
|
|
527
548
|
// Flatten thead/tbody wrappers to get rows
|
|
528
549
|
const rows = convertChildren(node, componentDefs, scope).filter((c) => c.type === "tableRow");
|
|
529
550
|
if (rows.length === 0) return [];
|
|
@@ -539,7 +560,7 @@ function convertTable(node, componentDefs, scope) {
|
|
|
539
560
|
* @param {Record<string, unknown> | null} [scope]
|
|
540
561
|
* @returns {string | null}
|
|
541
562
|
*/
|
|
542
|
-
function resolveText(value, scope) {
|
|
563
|
+
function resolveText(value: unknown, scope?: Record<string, unknown> | null) {
|
|
543
564
|
if (value == null) return null;
|
|
544
565
|
if (typeof value === "string") {
|
|
545
566
|
if (scope && isTemplateString(value)) {
|
|
@@ -560,9 +581,8 @@ function resolveText(value, scope) {
|
|
|
560
581
|
* @param {string} html
|
|
561
582
|
* @returns {MdastNode[]}
|
|
562
583
|
*/
|
|
563
|
-
function htmlToMdast(html) {
|
|
564
|
-
|
|
565
|
-
const nodes = [];
|
|
584
|
+
function htmlToMdast(html: string) {
|
|
585
|
+
const nodes: MdastNode[] = [];
|
|
566
586
|
|
|
567
587
|
// Simple top-level block parser
|
|
568
588
|
const parts = splitHtmlBlocks(html);
|
|
@@ -582,9 +602,8 @@ function htmlToMdast(html) {
|
|
|
582
602
|
* @param {string} html
|
|
583
603
|
* @returns {string[]}
|
|
584
604
|
*/
|
|
585
|
-
function splitHtmlBlocks(html) {
|
|
586
|
-
|
|
587
|
-
const blocks = [];
|
|
605
|
+
function splitHtmlBlocks(html: string) {
|
|
606
|
+
const blocks: string[] = [];
|
|
588
607
|
const trimmed = html.trim();
|
|
589
608
|
|
|
590
609
|
// Simple regex: split on top-level block boundaries
|
|
@@ -615,7 +634,7 @@ function splitHtmlBlocks(html) {
|
|
|
615
634
|
* @param {string} html
|
|
616
635
|
* @returns {MdastNode[] | null}
|
|
617
636
|
*/
|
|
618
|
-
function parseHtmlElement(html) {
|
|
637
|
+
function parseHtmlElement(html: string) {
|
|
619
638
|
// Heading
|
|
620
639
|
const hMatch = html.match(/^<(h[1-6])(?:\s[^>]*)?>(.+?)<\/\1>$/is);
|
|
621
640
|
if (hMatch) {
|
|
@@ -700,9 +719,8 @@ function parseHtmlElement(html) {
|
|
|
700
719
|
* @param {string} html
|
|
701
720
|
* @returns {MdastNode[]}
|
|
702
721
|
*/
|
|
703
|
-
function parseInlineHtml(html) {
|
|
704
|
-
|
|
705
|
-
const nodes = [];
|
|
722
|
+
function parseInlineHtml(html: string) {
|
|
723
|
+
const nodes: MdastNode[] = [];
|
|
706
724
|
let pos = 0;
|
|
707
725
|
|
|
708
726
|
while (pos < html.length) {
|
|
@@ -803,7 +821,7 @@ function parseInlineHtml(html) {
|
|
|
803
821
|
* @param {string} tag - Tag name to match
|
|
804
822
|
* @returns {number} Position of the matching closing tag, or -1
|
|
805
823
|
*/
|
|
806
|
-
function findMatchingClose(html, start, tag) {
|
|
824
|
+
function findMatchingClose(html: string, start: number, tag: string) {
|
|
807
825
|
let depth = 1;
|
|
808
826
|
const openRe = new RegExp(`<${tag}[\\s>]`, "gi");
|
|
809
827
|
const closeRe = new RegExp(`</${tag}>`, "gi");
|
|
@@ -834,9 +852,8 @@ function findMatchingClose(html, start, tag) {
|
|
|
834
852
|
* @param {string} html
|
|
835
853
|
* @returns {MdastNode[]}
|
|
836
854
|
*/
|
|
837
|
-
function parseListItems(html) {
|
|
838
|
-
|
|
839
|
-
const items = [];
|
|
855
|
+
function parseListItems(html: string) {
|
|
856
|
+
const items: MdastNode[] = [];
|
|
840
857
|
const liPattern = /<li(?:\s[^>]*)?>([\s\S]*?)<\/li>/gi;
|
|
841
858
|
let m;
|
|
842
859
|
while ((m = liPattern.exec(html)) !== null) {
|
|
@@ -856,15 +873,13 @@ function parseListItems(html) {
|
|
|
856
873
|
* @param {string} html
|
|
857
874
|
* @returns {MdastNode[]}
|
|
858
875
|
*/
|
|
859
|
-
function parseHtmlTable(html) {
|
|
860
|
-
|
|
861
|
-
const rows = [];
|
|
876
|
+
function parseHtmlTable(html: string) {
|
|
877
|
+
const rows: MdastNode[] = [];
|
|
862
878
|
const trPattern = /<tr(?:\s[^>]*)?>([\s\S]*?)<\/tr>/gi;
|
|
863
879
|
let m;
|
|
864
880
|
while ((m = trPattern.exec(html)) !== null) {
|
|
865
881
|
const cellPattern = /<(?:th|td)(?:\s[^>]*)?>([\s\S]*?)<\/(?:th|td)>/gi;
|
|
866
|
-
|
|
867
|
-
const cells = [];
|
|
882
|
+
const cells: MdastNode[] = [];
|
|
868
883
|
let c;
|
|
869
884
|
while ((c = cellPattern.exec(m[1])) !== null) {
|
|
870
885
|
cells.push({ type: "tableCell", children: parseInlineHtml(c[1]) });
|
|
@@ -881,7 +896,7 @@ function parseHtmlTable(html) {
|
|
|
881
896
|
* @param {string} html
|
|
882
897
|
* @returns {string}
|
|
883
898
|
*/
|
|
884
|
-
function stripHtmlTags(html) {
|
|
899
|
+
function stripHtmlTags(html: string) {
|
|
885
900
|
return html.replace(/<[^>]+>/g, "");
|
|
886
901
|
}
|
|
887
902
|
|
|
@@ -891,7 +906,7 @@ function stripHtmlTags(html) {
|
|
|
891
906
|
* @param {string} str
|
|
892
907
|
* @returns {string}
|
|
893
908
|
*/
|
|
894
|
-
function decodeHtmlEntities(str) {
|
|
909
|
+
function decodeHtmlEntities(str: string) {
|
|
895
910
|
return str
|
|
896
911
|
.replace(/&/g, "&")
|
|
897
912
|
.replace(/</g, "<")
|
|
@@ -911,7 +926,10 @@ function decodeHtmlEntities(str) {
|
|
|
911
926
|
* @param {Map<string, JxElement>} [componentDefs] - Component definitions for inlining
|
|
912
927
|
* @returns {{ content: string }}
|
|
913
928
|
*/
|
|
914
|
-
export function compileMarkdown(
|
|
929
|
+
export function compileMarkdown(
|
|
930
|
+
doc: JxDocument,
|
|
931
|
+
componentDefs: Map<string, JxElement> = new Map(),
|
|
932
|
+
) {
|
|
915
933
|
if (!Array.isArray(doc.children) || doc.children.length === 0) {
|
|
916
934
|
return { content: "" };
|
|
917
935
|
}
|
|
@@ -920,15 +938,13 @@ export function compileMarkdown(doc, componentDefs = new Map()) {
|
|
|
920
938
|
const scope = doc.state ? buildInitialScope(doc.state, null) : null;
|
|
921
939
|
|
|
922
940
|
// Convert to mdast
|
|
923
|
-
const mdastChildren = doc.children.flatMap((
|
|
941
|
+
const mdastChildren = doc.children.flatMap((child: JxElement | string) =>
|
|
924
942
|
nodeToMdast(child, componentDefs, scope),
|
|
925
943
|
);
|
|
926
944
|
|
|
927
945
|
// Clean up: ensure block-level structure (no bare inline nodes at root)
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
/** @type {MdastNode[]} */
|
|
931
|
-
let inlineBuf = [];
|
|
946
|
+
const cleaned: MdastNode[] = [];
|
|
947
|
+
let inlineBuf: MdastNode[] = [];
|
|
932
948
|
|
|
933
949
|
const flushInline = () => {
|
|
934
950
|
if (inlineBuf.length > 0) {
|
|
@@ -947,9 +963,7 @@ export function compileMarkdown(doc, componentDefs = new Map()) {
|
|
|
947
963
|
}
|
|
948
964
|
flushInline();
|
|
949
965
|
|
|
950
|
-
const mdast =
|
|
951
|
-
/** @type {unknown} */ ({ type: "root", children: cleaned })
|
|
952
|
-
);
|
|
966
|
+
const mdast = { type: "root", children: cleaned } as unknown as import("mdast").Root;
|
|
953
967
|
|
|
954
968
|
const md = unified()
|
|
955
969
|
.use(remarkGfm)
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
9
|
-
import { collectServerEntries } from "../shared.
|
|
9
|
+
import { collectServerEntries } from "../shared.ts";
|
|
10
|
+
import type { JxElement } from "@jxsuite/schema/types";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Compile a Jx document to a Hono server handler file. The handler exposes each `timing: "server"`
|
|
@@ -22,9 +23,14 @@ import { collectServerEntries } from "../shared.js";
|
|
|
22
23
|
* @param {string} [opts.baseUrl] - Base path for server endpoints. Default is `'/_jx/server'`
|
|
23
24
|
* @returns {Promise<string | null>} Hono server handler source string, or null
|
|
24
25
|
*/
|
|
25
|
-
export async function compileServer(
|
|
26
|
+
export async function compileServer(
|
|
27
|
+
sourcePath: string | object,
|
|
28
|
+
opts: {
|
|
29
|
+
baseUrl?: string;
|
|
30
|
+
} = {},
|
|
31
|
+
) {
|
|
26
32
|
const { baseUrl = "/_jx/server" } = opts;
|
|
27
|
-
const doc =
|
|
33
|
+
const doc = (await $RefParser.dereference(sourcePath)) as JxElement;
|
|
28
34
|
const entries = collectServerEntries(doc);
|
|
29
35
|
if (entries.length === 0) return null;
|
|
30
36
|
|
|
@@ -58,7 +64,13 @@ export default app
|
|
|
58
64
|
* @param {string | null} [opts.adapter] - Deployment adapter: `"cloudflare"`, `"node"`, `"bun"`
|
|
59
65
|
* @returns {string | null} Complete worker/server source string, or null
|
|
60
66
|
*/
|
|
61
|
-
export function compileSiteServer(
|
|
67
|
+
export function compileSiteServer(
|
|
68
|
+
entries: { exportName: string; src: string }[],
|
|
69
|
+
opts: {
|
|
70
|
+
baseUrl?: string;
|
|
71
|
+
adapter?: string | null;
|
|
72
|
+
} = {},
|
|
73
|
+
) {
|
|
62
74
|
const { baseUrl = "/_jx/server", adapter = null } = opts;
|
|
63
75
|
if (entries.length === 0 && !adapter) return null;
|
|
64
76
|
|
|
@@ -85,7 +97,7 @@ export default app
|
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
/** @param {string} exportName @param {string} baseUrl */
|
|
88
|
-
function buildRoute(exportName, baseUrl) {
|
|
100
|
+
function buildRoute(exportName: string, baseUrl: string) {
|
|
89
101
|
return `
|
|
90
102
|
app.post('${baseUrl}/${exportName}', async (c) => {
|
|
91
103
|
const args = await c.req.json().catch(() => ({}))
|
|
@@ -107,7 +119,12 @@ app.post('${baseUrl}/${exportName}', async (c) => {
|
|
|
107
119
|
* @returns {Map<string, string>} Map of output path → source (e.g.
|
|
108
120
|
* `"functions/_jx/server/sendEmail.js"` → source)
|
|
109
121
|
*/
|
|
110
|
-
export function compilePagesFunctions(
|
|
122
|
+
export function compilePagesFunctions(
|
|
123
|
+
entries: { exportName: string; src: string }[],
|
|
124
|
+
opts: {
|
|
125
|
+
baseUrl?: string;
|
|
126
|
+
} = {},
|
|
127
|
+
) {
|
|
111
128
|
const { baseUrl = "/_jx/server" } = opts;
|
|
112
129
|
const files = new Map();
|
|
113
130
|
|
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
buildAttrs,
|
|
13
13
|
compileStyles,
|
|
14
14
|
escapeHtml,
|
|
15
|
-
} from "../shared.
|
|
16
|
-
import { emitElementModule } from "./compile-element.
|
|
15
|
+
} from "../shared.ts";
|
|
16
|
+
import { emitElementModule } from "./compile-element.ts";
|
|
17
|
+
import type { JxStyle, JxMutableNode } from "@jxsuite/schema/types";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Compile a static document to HTML, with dynamic subtrees as islands.
|
|
@@ -23,17 +24,19 @@ import { emitElementModule } from "./compile-element.js";
|
|
|
23
24
|
* @param {Record<string, unknown>} opts
|
|
24
25
|
* @returns {{ html: string; files: { path: string; content: string; tagName: string }[] }}
|
|
25
26
|
*/
|
|
26
|
-
export function compileStaticPage(
|
|
27
|
-
|
|
27
|
+
export function compileStaticPage(
|
|
28
|
+
raw: JxMutableNode | Record<string, any>,
|
|
29
|
+
opts: Record<string, unknown>,
|
|
30
|
+
) {
|
|
31
|
+
const { title, reactivitySrc, litHtmlSrc } = opts as JxMutableNode;
|
|
28
32
|
|
|
29
33
|
const rootContext = createCompileContext(raw, null, raw.state ?? {}, raw.$media ?? {});
|
|
30
34
|
const styleBlock = compileStyles(
|
|
31
35
|
raw,
|
|
32
36
|
raw.$media ?? {},
|
|
33
|
-
|
|
37
|
+
(opts.projectStyle ?? null) as JxStyle | null,
|
|
34
38
|
);
|
|
35
|
-
|
|
36
|
-
const islands = [];
|
|
39
|
+
const islands: { def: JxMutableNode; tagName: string; className: string }[] = [];
|
|
37
40
|
const bodyContent = compileNode(raw, false, raw, rootContext, islands);
|
|
38
41
|
|
|
39
42
|
/** @type {{ path: string; content: string; tagName: string }[]} */
|
|
@@ -95,7 +98,17 @@ export function compileStaticPage(raw, opts) {
|
|
|
95
98
|
* @param {{ def: any; tagName: string; className: string }[]} islands
|
|
96
99
|
* @returns {string}
|
|
97
100
|
*/
|
|
98
|
-
function compileNode(
|
|
101
|
+
function compileNode(
|
|
102
|
+
def: JxMutableNode,
|
|
103
|
+
dynamic: boolean,
|
|
104
|
+
raw: JxMutableNode,
|
|
105
|
+
context: {
|
|
106
|
+
scope: Record<string, unknown> | null;
|
|
107
|
+
scopeDefs: Record<string, unknown>;
|
|
108
|
+
media: Record<string, string>;
|
|
109
|
+
},
|
|
110
|
+
islands: { def: JxMutableNode; tagName: string; className: string }[],
|
|
111
|
+
): string {
|
|
99
112
|
// String children are text nodes
|
|
100
113
|
if (typeof def === "string") {
|
|
101
114
|
return escapeHtml(def);
|
|
@@ -138,7 +151,16 @@ function compileNode(def, dynamic, raw, context, islands) {
|
|
|
138
151
|
* @param {{ def: any; tagName: string; className: string }[]} islands
|
|
139
152
|
* @returns {string}
|
|
140
153
|
*/
|
|
141
|
-
function buildInnerWithIslands(
|
|
154
|
+
function buildInnerWithIslands(
|
|
155
|
+
def: JxMutableNode,
|
|
156
|
+
raw: JxMutableNode,
|
|
157
|
+
context: {
|
|
158
|
+
scope: Record<string, unknown> | null;
|
|
159
|
+
scopeDefs: Record<string, unknown>;
|
|
160
|
+
media: Record<string, string>;
|
|
161
|
+
},
|
|
162
|
+
islands: { def: JxMutableNode; tagName: string; className: string }[],
|
|
163
|
+
): string {
|
|
142
164
|
const source = raw ?? def;
|
|
143
165
|
|
|
144
166
|
if (source.textContent !== undefined) {
|
|
@@ -146,19 +168,14 @@ function buildInnerWithIslands(def, raw, context, islands) {
|
|
|
146
168
|
return value == null ? "" : escapeHtml(String(value));
|
|
147
169
|
}
|
|
148
170
|
if (source.innerHTML)
|
|
149
|
-
return (
|
|
150
|
-
/** @type {string} */ (resolveStaticValue(source.innerHTML, context.scope)) ??
|
|
151
|
-
source.innerHTML
|
|
152
|
-
);
|
|
171
|
+
return (resolveStaticValue(source.innerHTML, context.scope) as string) ?? source.innerHTML;
|
|
153
172
|
if (Array.isArray(source.children)) {
|
|
154
173
|
const rawChildren = raw?.children;
|
|
155
174
|
return source.children
|
|
156
|
-
.map((c,
|
|
157
|
-
const child =
|
|
175
|
+
.map((c, i: number) => {
|
|
176
|
+
const child = c as unknown as JxMutableNode;
|
|
158
177
|
const childDynamic = isNodeDynamic(child);
|
|
159
|
-
const childRaw =
|
|
160
|
-
/** @type {unknown} */ (rawChildren?.[i] ?? c)
|
|
161
|
-
);
|
|
178
|
+
const childRaw = (rawChildren?.[i] ?? c) as JxMutableNode;
|
|
162
179
|
return compileNode(child, childDynamic, childRaw, context, islands);
|
|
163
180
|
})
|
|
164
181
|
.join("\n ");
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface HeadMergeContext {
|
|
2
|
+
title?: string;
|
|
3
|
+
siteName?: string;
|
|
4
|
+
lang?: string;
|
|
5
|
+
charset?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
siteUrl?: string;
|
|
8
|
+
pageUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SiteRoute {
|
|
12
|
+
urlPattern: string;
|
|
13
|
+
sourcePath?: string;
|
|
14
|
+
_pathParams?: Record<string, string>;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|