@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.
- package/dist/compiler.js +4 -4
- package/package.json +6 -4
- package/src/cli.js +1 -1
- package/src/compiler.js +2 -2
- package/src/shared.js +183 -145
- package/src/site/content-loader.js +106 -76
- package/src/site/context-injection.js +30 -24
- package/src/site/head-merger.js +13 -12
- package/src/site/image-optimizer.js +2 -2
- package/src/site/image-transform.js +5 -4
- package/src/site/layout-resolver.js +32 -25
- package/src/site/pages-discovery.js +6 -6
- package/src/site/prototype-resolver.js +1 -1
- package/src/site/site-build.js +71 -51
- package/src/site/site-loader.js +1 -1
- package/src/targets/compile-class.js +27 -21
- package/src/targets/compile-client.js +75 -33
- package/src/targets/compile-element.js +57 -39
- package/src/targets/compile-markdown.js +99 -78
- package/src/targets/compile-server.js +1 -1
- package/src/targets/compile-static.js +24 -13
|
@@ -28,8 +28,8 @@ import {
|
|
|
28
28
|
/**
|
|
29
29
|
* Compile a Jx document to pre-rendered HTML + reactive JS module.
|
|
30
30
|
*
|
|
31
|
-
* @param {any} raw
|
|
32
|
-
* @param {
|
|
31
|
+
* @param {JxMutableNode | Record<string, any>} raw
|
|
32
|
+
* @param {Record<string, unknown>} opts
|
|
33
33
|
* @returns {{ html: string; files: { path: string; content: string }[] }}
|
|
34
34
|
*/
|
|
35
35
|
export function compileClient(raw, opts) {
|
|
@@ -38,10 +38,14 @@ export function compileClient(raw, opts) {
|
|
|
38
38
|
reactivitySrc = DEFAULT_REACTIVITY_SRC,
|
|
39
39
|
litHtmlSrc = DEFAULT_LIT_HTML_SRC,
|
|
40
40
|
modulePath = "app.js",
|
|
41
|
-
} = opts;
|
|
41
|
+
} = /** @type {JxMutableNode} */ (opts);
|
|
42
42
|
|
|
43
43
|
const context = createCompileContext(raw, null, raw.state ?? {}, raw.$media ?? {});
|
|
44
|
-
const styleBlock = compileStyles(
|
|
44
|
+
const styleBlock = compileStyles(
|
|
45
|
+
raw,
|
|
46
|
+
raw.$media ?? {},
|
|
47
|
+
/** @type {JxStyle | null} */ (opts.projectStyle ?? null),
|
|
48
|
+
);
|
|
45
49
|
|
|
46
50
|
// Collectors for bindings and handlers
|
|
47
51
|
const counter = { t: 0, s: 0, h: 0, m: 0, sw: 0, l: 0, needsLit: false };
|
|
@@ -79,7 +83,7 @@ export function compileClient(raw, opts) {
|
|
|
79
83
|
continue;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
const d = /** @type {
|
|
86
|
+
const d = /** @type {JxMutableNode} */ (def);
|
|
83
87
|
|
|
84
88
|
// $prototype: "Function"
|
|
85
89
|
if (d.$prototype === "Function") {
|
|
@@ -197,12 +201,20 @@ ${importmapEntries.join(",\n")}
|
|
|
197
201
|
// ─── HTML tree walker ─────────────────────────────────────────────────────────
|
|
198
202
|
|
|
199
203
|
/**
|
|
200
|
-
* @param {
|
|
201
|
-
* @param {
|
|
204
|
+
* @param {JxMutableNode} def
|
|
205
|
+
* @param {JxMutableNode} raw
|
|
202
206
|
* @param {any} context
|
|
203
207
|
* @param {Map<string, string>} bindings
|
|
204
208
|
* @param {Map<string, any>} handlers
|
|
205
|
-
* @param {
|
|
209
|
+
* @param {{
|
|
210
|
+
* t: number;
|
|
211
|
+
* s: number;
|
|
212
|
+
* h: number;
|
|
213
|
+
* m: number;
|
|
214
|
+
* sw: number;
|
|
215
|
+
* l: number;
|
|
216
|
+
* needsLit: boolean;
|
|
217
|
+
* }} counter
|
|
206
218
|
* @returns {string}
|
|
207
219
|
*/
|
|
208
220
|
function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
@@ -228,11 +240,13 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
228
240
|
|
|
229
241
|
// textContent bindings
|
|
230
242
|
if (def.textContent !== undefined) {
|
|
231
|
-
const tc =
|
|
243
|
+
const tc = /** @type {JxMutableNode} */ (
|
|
244
|
+
/** @type {unknown} */ (raw?.textContent ?? def.textContent)
|
|
245
|
+
);
|
|
232
246
|
if (isRefObject(tc)) {
|
|
233
|
-
const key = refToBindingKey(tc.$ref);
|
|
247
|
+
const key = refToBindingKey(/** @type {string} */ (tc.$ref));
|
|
234
248
|
bindAttrs.push(`:text-content="${key}"`);
|
|
235
|
-
addRefBinding(bindings, key, tc.$ref);
|
|
249
|
+
addRefBinding(bindings, key, /** @type {string} */ (tc.$ref));
|
|
236
250
|
needsBind = true;
|
|
237
251
|
} else if (isTemplateString(tc)) {
|
|
238
252
|
const key = `_t${counter.t++}`;
|
|
@@ -247,15 +261,15 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
247
261
|
if (!prop.startsWith("on") || prop === "observedAttributes") continue;
|
|
248
262
|
const eventName = prop.slice(2).toLowerCase();
|
|
249
263
|
if (isRefObject(val)) {
|
|
250
|
-
const key = refToBindingKey(/** @type {
|
|
264
|
+
const key = refToBindingKey(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref));
|
|
251
265
|
bindAttrs.push(`@${eventName}="${key}"`);
|
|
252
266
|
needsBind = true;
|
|
253
267
|
} else if (
|
|
254
268
|
val &&
|
|
255
269
|
typeof val === "object" &&
|
|
256
|
-
/** @type {
|
|
270
|
+
/** @type {JxMutableNode} */ (val).$prototype === "Function"
|
|
257
271
|
) {
|
|
258
|
-
const v = /** @type {
|
|
272
|
+
const v = /** @type {JxMutableNode} */ (val);
|
|
259
273
|
const key = `_h${counter.h++}`;
|
|
260
274
|
bindAttrs.push(`@${eventName}="${key}"`);
|
|
261
275
|
handlers.set(key, { args: v.parameters ?? v.arguments ?? ["state", "event"], body: v.body });
|
|
@@ -288,9 +302,15 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
288
302
|
if (def.attributes && typeof def.attributes === "object") {
|
|
289
303
|
for (const [attr, val] of Object.entries(def.attributes)) {
|
|
290
304
|
if (isRefObject(val)) {
|
|
291
|
-
const key = refToBindingKey(
|
|
305
|
+
const key = refToBindingKey(
|
|
306
|
+
/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref),
|
|
307
|
+
);
|
|
292
308
|
bindAttrs.push(`:attr.${attr}="${key}"`);
|
|
293
|
-
addRefBinding(
|
|
309
|
+
addRefBinding(
|
|
310
|
+
bindings,
|
|
311
|
+
key,
|
|
312
|
+
/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref),
|
|
313
|
+
);
|
|
294
314
|
needsBind = true;
|
|
295
315
|
} else if (isTemplateString(val)) {
|
|
296
316
|
const key = `_t${counter.t++}`;
|
|
@@ -318,9 +338,9 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
318
338
|
)
|
|
319
339
|
continue;
|
|
320
340
|
if (isRefObject(val)) {
|
|
321
|
-
const key = refToBindingKey(/** @type {
|
|
341
|
+
const key = refToBindingKey(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref));
|
|
322
342
|
bindAttrs.push(`:${camelToKebab(prop)}="${key}"`);
|
|
323
|
-
addRefBinding(bindings, key, /** @type {
|
|
343
|
+
addRefBinding(bindings, key, /** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref));
|
|
324
344
|
needsBind = true;
|
|
325
345
|
} else if (isTemplateString(val)) {
|
|
326
346
|
const key = `_t${counter.t++}`;
|
|
@@ -347,17 +367,18 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
347
367
|
} else if (source.innerHTML) {
|
|
348
368
|
// resolveStaticValue may return null if innerHTML contains `${` from rendered content
|
|
349
369
|
// (e.g., code examples) that isn't an actual template expression. Fall back to raw value.
|
|
350
|
-
inner = resolveStaticValue(source.innerHTML, nextContext.scope) ??
|
|
370
|
+
inner = /** @type {string} */ (resolveStaticValue(source.innerHTML, nextContext.scope)) ??
|
|
371
|
+
source.innerHTML;
|
|
351
372
|
} else if (
|
|
352
373
|
source.children &&
|
|
353
374
|
typeof source.children === "object" &&
|
|
354
375
|
!Array.isArray(source.children) &&
|
|
355
|
-
source.children.$prototype === "Array"
|
|
376
|
+
/** @type {JxMutableNode} */ (source.children).$prototype === "Array"
|
|
356
377
|
) {
|
|
357
378
|
// ─── Mapped array → lit-html render binding ───
|
|
358
379
|
counter.needsLit = true;
|
|
359
380
|
const listKey = `_list${counter.l++}`;
|
|
360
|
-
const arrayDef = source.children;
|
|
381
|
+
const arrayDef = /** @type {JxMutableNode} */ (source.children);
|
|
361
382
|
|
|
362
383
|
// Resolve items source expression
|
|
363
384
|
let itemsExpr;
|
|
@@ -388,9 +409,16 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
388
409
|
} else if (Array.isArray(source.children)) {
|
|
389
410
|
const rawChildren = raw?.children;
|
|
390
411
|
inner = source.children
|
|
391
|
-
.map((
|
|
412
|
+
.map((c, i) => {
|
|
392
413
|
const childRaw = rawChildren?.[i] ?? c;
|
|
393
|
-
return buildClientNode(
|
|
414
|
+
return buildClientNode(
|
|
415
|
+
/** @type {JxMutableNode} */ (/** @type {unknown} */ (c)),
|
|
416
|
+
/** @type {JxMutableNode} */ (/** @type {unknown} */ (childRaw)),
|
|
417
|
+
nextContext,
|
|
418
|
+
bindings,
|
|
419
|
+
handlers,
|
|
420
|
+
counter,
|
|
421
|
+
);
|
|
394
422
|
})
|
|
395
423
|
.join("\n ");
|
|
396
424
|
}
|
|
@@ -410,7 +438,7 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
|
|
|
410
438
|
* Compile a map definition to a lit-html template string. Converts $map.item → item, $map.index →
|
|
411
439
|
* index.
|
|
412
440
|
*
|
|
413
|
-
* @param {
|
|
441
|
+
* @param {JxMutableNode} def
|
|
414
442
|
* @returns {string}
|
|
415
443
|
*/
|
|
416
444
|
function emitLitMapTemplate(def) {
|
|
@@ -463,14 +491,14 @@ function emitLitMapTemplate(def) {
|
|
|
463
491
|
if (!prop.startsWith("on") || prop === "observedAttributes") continue;
|
|
464
492
|
const eventName = prop.slice(2).toLowerCase();
|
|
465
493
|
if (isRefObject(val)) {
|
|
466
|
-
const key = refToBindingKey(/** @type {
|
|
494
|
+
const key = refToBindingKey(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref));
|
|
467
495
|
attrs += " @" + eventName + "=${(e) => { state.$map = { item, index }; on." + key + "(e); }}";
|
|
468
496
|
} else if (
|
|
469
497
|
val &&
|
|
470
498
|
typeof val === "object" &&
|
|
471
|
-
/** @type {
|
|
499
|
+
/** @type {JxMutableNode} */ (val).$prototype === "Function"
|
|
472
500
|
) {
|
|
473
|
-
const body = mapRefsToLit(/** @type {
|
|
501
|
+
const body = mapRefsToLit(/** @type {string} */ (/** @type {JxMutableNode} */ (val).body));
|
|
474
502
|
attrs += " @" + eventName + "=${(e) => { " + body + " }}";
|
|
475
503
|
}
|
|
476
504
|
}
|
|
@@ -487,7 +515,11 @@ function emitLitMapTemplate(def) {
|
|
|
487
515
|
if (isTemplateString(tc)) {
|
|
488
516
|
inner = mapRefsToLit(tc);
|
|
489
517
|
} else if (isRefObject(def.textContent)) {
|
|
490
|
-
const path = refToBindingKey(
|
|
518
|
+
const path = refToBindingKey(
|
|
519
|
+
/** @type {string} */ (
|
|
520
|
+
/** @type {JxMutableNode} */ (/** @type {unknown} */ (def.textContent)).$ref
|
|
521
|
+
),
|
|
522
|
+
);
|
|
491
523
|
inner = "${state." + path + "}";
|
|
492
524
|
} else {
|
|
493
525
|
inner = escapeHtml(tc);
|
|
@@ -497,7 +529,9 @@ function emitLitMapTemplate(def) {
|
|
|
497
529
|
} else if (Array.isArray(def.children)) {
|
|
498
530
|
inner =
|
|
499
531
|
"\n " +
|
|
500
|
-
def.children
|
|
532
|
+
def.children
|
|
533
|
+
.map((c) => emitLitMapTemplate(/** @type {JxMutableNode} */ (c)))
|
|
534
|
+
.join("\n ") +
|
|
501
535
|
"\n ";
|
|
502
536
|
}
|
|
503
537
|
|
|
@@ -525,7 +559,15 @@ function mapRefsToLit(str) {
|
|
|
525
559
|
* @param {[string, any][]} onEntries
|
|
526
560
|
* @param {string[]} initBlocks
|
|
527
561
|
* @param {Map<string, Set<string>>} srcImportMap
|
|
528
|
-
* @param {
|
|
562
|
+
* @param {{
|
|
563
|
+
* t: number;
|
|
564
|
+
* s: number;
|
|
565
|
+
* h: number;
|
|
566
|
+
* m: number;
|
|
567
|
+
* sw: number;
|
|
568
|
+
* l: number;
|
|
569
|
+
* needsLit: boolean;
|
|
570
|
+
* }} counter
|
|
529
571
|
* @param {string} _reactivitySrc
|
|
530
572
|
* @returns {string}
|
|
531
573
|
*/
|
|
@@ -663,7 +705,7 @@ function emitClientModule(
|
|
|
663
705
|
|
|
664
706
|
/**
|
|
665
707
|
* @param {string} key
|
|
666
|
-
* @param {
|
|
708
|
+
* @param {JxMutableNode} def
|
|
667
709
|
* @returns {string}
|
|
668
710
|
*/
|
|
669
711
|
function emitRequestInit(key, def) {
|
|
@@ -713,7 +755,7 @@ function emitRequestInit(key, def) {
|
|
|
713
755
|
* @param {string} key
|
|
714
756
|
* @param {string} storeName
|
|
715
757
|
* @param {string} storageKey
|
|
716
|
-
* @param {
|
|
758
|
+
* @param {unknown} defaultVal
|
|
717
759
|
* @returns {string}
|
|
718
760
|
*/
|
|
719
761
|
function emitStorageInit(key, storeName, storageKey, defaultVal) {
|
|
@@ -743,7 +785,7 @@ function emitStorageInit(key, storeName, storageKey, defaultVal) {
|
|
|
743
785
|
/**
|
|
744
786
|
* @param {string} key
|
|
745
787
|
* @param {string} cookieName
|
|
746
|
-
* @param {
|
|
788
|
+
* @param {unknown} defaultVal
|
|
747
789
|
* @returns {string}
|
|
748
790
|
*/
|
|
749
791
|
function emitCookieInit(key, cookieName, defaultVal) {
|
|
@@ -12,22 +12,22 @@ import { escapeHtml, tagNameToClassName, isSchemaOnly, collectStyles } from "../
|
|
|
12
12
|
* Compile a Jx custom element document to a JS module string.
|
|
13
13
|
*
|
|
14
14
|
* @param {string | any} sourcePath - Path to .json file or raw object
|
|
15
|
-
* @param {
|
|
15
|
+
* @param {Record<string, unknown>} [opts]
|
|
16
16
|
* @returns {Promise<{ files: { path: string; content: string; tagName: string }[] }>}
|
|
17
17
|
*/
|
|
18
18
|
export async function compileElement(sourcePath, opts = {}) {
|
|
19
|
-
const { resolveElementPath } = opts;
|
|
19
|
+
const { resolveElementPath } = /** @type {JxMutableNode} */ (opts);
|
|
20
20
|
/** @type {{ path: string; content: string; tagName: string }[]} */
|
|
21
21
|
const files = [];
|
|
22
22
|
/** @type {Set<string>} */
|
|
23
23
|
const visited = new Set();
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* @param {any} srcPath
|
|
26
|
+
* @param {string | any} srcPath
|
|
27
27
|
* @param {string | null} parentDir
|
|
28
28
|
*/
|
|
29
29
|
async function processElement(srcPath, parentDir) {
|
|
30
|
-
/** @type {
|
|
30
|
+
/** @type {JxMutableNode} */
|
|
31
31
|
let doc;
|
|
32
32
|
/** @type {string | null} */
|
|
33
33
|
let filePath;
|
|
@@ -46,8 +46,8 @@ export async function compileElement(sourcePath, opts = {}) {
|
|
|
46
46
|
} else {
|
|
47
47
|
doc = srcPath;
|
|
48
48
|
filePath = null;
|
|
49
|
-
if (visited.has(doc.tagName)) return;
|
|
50
|
-
visited.add(doc.tagName);
|
|
49
|
+
if (visited.has(/** @type {string} */ (doc.tagName))) return;
|
|
50
|
+
visited.add(/** @type {string} */ (doc.tagName));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const tagName = doc.tagName;
|
|
@@ -63,7 +63,7 @@ export async function compileElement(sourcePath, opts = {}) {
|
|
|
63
63
|
const elementImports = [];
|
|
64
64
|
if (Array.isArray(doc.$elements)) {
|
|
65
65
|
for (const elRef of doc.$elements) {
|
|
66
|
-
const refPath = elRef.$ref ?? elRef;
|
|
66
|
+
const refPath = /** @type {JxMutableNode} */ (elRef).$ref ?? elRef;
|
|
67
67
|
if (typeof refPath !== "string") continue;
|
|
68
68
|
|
|
69
69
|
if (currentDir) {
|
|
@@ -87,7 +87,7 @@ export async function compileElement(sourcePath, opts = {}) {
|
|
|
87
87
|
files.push({ path: outputPath, content: jsContent, tagName });
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
await processElement(sourcePath, opts.basePath ?? null);
|
|
90
|
+
await processElement(sourcePath, /** @type {string | null} */ (opts.basePath ?? null));
|
|
91
91
|
return { files };
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -96,7 +96,7 @@ export async function compileElement(sourcePath, opts = {}) {
|
|
|
96
96
|
* dependencies.
|
|
97
97
|
*
|
|
98
98
|
* @param {string | any} sourcePath
|
|
99
|
-
* @param {
|
|
99
|
+
* @param {Record<string, unknown>} [opts]
|
|
100
100
|
* @returns {Promise<{
|
|
101
101
|
* html: string;
|
|
102
102
|
* files: { path: string; content: string; tagName: string }[];
|
|
@@ -107,7 +107,7 @@ export async function compileElementPage(sourcePath, opts = {}) {
|
|
|
107
107
|
title = "Jx App",
|
|
108
108
|
reactivitySrc = "https://esm.sh/@vue/reactivity@3.5.13",
|
|
109
109
|
litHtmlSrc = "https://esm.sh/lit-html@3.3.0",
|
|
110
|
-
} = opts;
|
|
110
|
+
} = /** @type {JxMutableNode} */ (opts);
|
|
111
111
|
|
|
112
112
|
const result = await compileElement(sourcePath, opts);
|
|
113
113
|
const root = result.files[result.files.length - 1];
|
|
@@ -146,7 +146,7 @@ export async function compileElementPage(sourcePath, opts = {}) {
|
|
|
146
146
|
* like { type, default, description } now correctly extract the `default` value instead of dumping
|
|
147
147
|
* the whole object.
|
|
148
148
|
*
|
|
149
|
-
* @param {
|
|
149
|
+
* @param {JxMutableNode} def
|
|
150
150
|
* @returns {string | undefined}
|
|
151
151
|
*/
|
|
152
152
|
function extractInitialValue(def) {
|
|
@@ -175,7 +175,7 @@ function extractInitialValue(def) {
|
|
|
175
175
|
/**
|
|
176
176
|
* Generate a complete ES module string for a custom element.
|
|
177
177
|
*
|
|
178
|
-
* @param {any} doc
|
|
178
|
+
* @param {JxMutableNode | Record<string, any>} doc
|
|
179
179
|
* @param {string} className
|
|
180
180
|
* @param {string[]} elementImports
|
|
181
181
|
* @returns {string}
|
|
@@ -196,7 +196,7 @@ export function emitElementModule(doc, className, elementImports) {
|
|
|
196
196
|
const srcImportMap = new Map();
|
|
197
197
|
const defs = doc.state ?? {};
|
|
198
198
|
for (const [key, def] of Object.entries(defs)) {
|
|
199
|
-
const d = /** @type {
|
|
199
|
+
const d = /** @type {JxMutableNode} */ (def);
|
|
200
200
|
if (d && typeof d === "object" && !Array.isArray(d) && d.$prototype === "Function" && d.$src) {
|
|
201
201
|
const srcPath = d.$src;
|
|
202
202
|
if (!srcImportMap.has(srcPath)) srcImportMap.set(srcPath, []);
|
|
@@ -226,7 +226,7 @@ export function emitElementModule(doc, className, elementImports) {
|
|
|
226
226
|
const functionEntries = [];
|
|
227
227
|
|
|
228
228
|
for (const [key, def] of Object.entries(defs)) {
|
|
229
|
-
const d = /** @type {
|
|
229
|
+
const d = /** @type {JxMutableNode} */ (def);
|
|
230
230
|
if (d && typeof d === "object" && !Array.isArray(d) && d.$prototype === "Function") {
|
|
231
231
|
if (typeof d.body === "string" && d.body.includes("return")) {
|
|
232
232
|
computedEntries.push([key, d]);
|
|
@@ -391,25 +391,27 @@ export function emitElementModule(doc, className, elementImports) {
|
|
|
391
391
|
/**
|
|
392
392
|
* Convert Jx children to lit-html template content.
|
|
393
393
|
*
|
|
394
|
-
* @param {
|
|
395
|
-
* @param {any} parentStyle
|
|
394
|
+
* @param {(JxMutableNode | string)[] | JxMutableNode | undefined} children
|
|
395
|
+
* @param {JxStyle | Record<string, any> | null | undefined} parentStyle
|
|
396
396
|
* @param {string} indent
|
|
397
397
|
* @returns {string}
|
|
398
398
|
*/
|
|
399
399
|
function emitLitChildren(children, parentStyle, indent) {
|
|
400
400
|
if (!children) return "";
|
|
401
401
|
|
|
402
|
-
if (children.$prototype === "Array") {
|
|
403
|
-
return emitMappedArray(children, indent);
|
|
402
|
+
if (/** @type {JxMutableNode} */ (children).$prototype === "Array") {
|
|
403
|
+
return emitMappedArray(/** @type {JxMutableNode} */ (children), indent);
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
if (!Array.isArray(children)) return "";
|
|
407
407
|
|
|
408
|
-
return children
|
|
408
|
+
return children
|
|
409
|
+
.map((/** @type {JxMutableNode | string} */ child) => emitLitNode(child, indent))
|
|
410
|
+
.join("\n");
|
|
409
411
|
}
|
|
410
412
|
|
|
411
413
|
/**
|
|
412
|
-
* @param {
|
|
414
|
+
* @param {JxMutableNode | string} def
|
|
413
415
|
* @param {string} indent
|
|
414
416
|
* @returns {string}
|
|
415
417
|
*/
|
|
@@ -458,8 +460,10 @@ function emitLitNode(def, indent) {
|
|
|
458
460
|
)
|
|
459
461
|
continue;
|
|
460
462
|
|
|
461
|
-
if (val && typeof val === "object" && /** @type {
|
|
462
|
-
parts.push(
|
|
463
|
+
if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
|
|
464
|
+
parts.push(
|
|
465
|
+
`.${key}="\${${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}}"`,
|
|
466
|
+
);
|
|
463
467
|
} else if (typeof val === "string" && val.includes("${")) {
|
|
464
468
|
parts.push(`.${key}="${toLitExpr(val)}"`);
|
|
465
469
|
}
|
|
@@ -467,8 +471,10 @@ function emitLitNode(def, indent) {
|
|
|
467
471
|
|
|
468
472
|
if (def.$props) {
|
|
469
473
|
for (const [key, val] of Object.entries(def.$props)) {
|
|
470
|
-
if (val && typeof val === "object" && /** @type {
|
|
471
|
-
parts.push(
|
|
474
|
+
if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
|
|
475
|
+
parts.push(
|
|
476
|
+
`.${key}="\${${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}}"`,
|
|
477
|
+
);
|
|
472
478
|
} else {
|
|
473
479
|
parts.push(`.${key}="\${${JSON.stringify(val)}}"`);
|
|
474
480
|
}
|
|
@@ -478,14 +484,18 @@ function emitLitNode(def, indent) {
|
|
|
478
484
|
for (const [key, val] of Object.entries(def)) {
|
|
479
485
|
if (!key.startsWith("on") || key === "observedAttributes") continue;
|
|
480
486
|
const eventName = key.slice(2).toLowerCase();
|
|
481
|
-
if (val && typeof val === "object" && /** @type {
|
|
482
|
-
parts.push(
|
|
487
|
+
if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
|
|
488
|
+
parts.push(
|
|
489
|
+
`@${eventName}="\${(e) => ${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}(s, e)}"`,
|
|
490
|
+
);
|
|
483
491
|
} else if (
|
|
484
492
|
val &&
|
|
485
493
|
typeof val === "object" &&
|
|
486
|
-
/** @type {
|
|
494
|
+
/** @type {JxMutableNode} */ (val).$prototype === "Function"
|
|
487
495
|
) {
|
|
488
|
-
parts.push(
|
|
496
|
+
parts.push(
|
|
497
|
+
`@${eventName}="\${(e) => { ${inlineHandlerBody(/** @type {JxMutableNode} */ (val))} }}"`,
|
|
498
|
+
);
|
|
489
499
|
}
|
|
490
500
|
}
|
|
491
501
|
|
|
@@ -512,7 +522,7 @@ function emitLitNode(def, indent) {
|
|
|
512
522
|
}
|
|
513
523
|
|
|
514
524
|
/**
|
|
515
|
-
* @param {
|
|
525
|
+
* @param {JxMutableNode} arrayDef
|
|
516
526
|
* @param {string} indent
|
|
517
527
|
* @returns {string}
|
|
518
528
|
*/
|
|
@@ -528,8 +538,10 @@ function emitMappedArray(arrayDef, indent) {
|
|
|
528
538
|
|
|
529
539
|
if (mapDef.$props) {
|
|
530
540
|
for (const [key, val] of Object.entries(mapDef.$props)) {
|
|
531
|
-
if (val && typeof val === "object" && /** @type {
|
|
532
|
-
parts.push(
|
|
541
|
+
if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
|
|
542
|
+
parts.push(
|
|
543
|
+
`.${key}="\${${mapRefToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}}"`,
|
|
544
|
+
);
|
|
533
545
|
} else {
|
|
534
546
|
parts.push(`.${key}="\${${JSON.stringify(val)}}"`);
|
|
535
547
|
}
|
|
@@ -542,8 +554,10 @@ function emitMappedArray(arrayDef, indent) {
|
|
|
542
554
|
for (const [key, val] of Object.entries(mapDef)) {
|
|
543
555
|
if (!key.startsWith("on")) continue;
|
|
544
556
|
const eventName = key.slice(2).toLowerCase();
|
|
545
|
-
if (val && typeof val === "object" && /** @type {
|
|
546
|
-
parts.push(
|
|
557
|
+
if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
|
|
558
|
+
parts.push(
|
|
559
|
+
`@${eventName}="\${(e) => ${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}(s, e)}"`,
|
|
560
|
+
);
|
|
547
561
|
}
|
|
548
562
|
}
|
|
549
563
|
|
|
@@ -602,13 +616,17 @@ function toLitExpr(str) {
|
|
|
602
616
|
* Convert textContent value to lit-html text content. Bug fix: handles $ref objects, which
|
|
603
617
|
* previously produced [object Object].
|
|
604
618
|
*
|
|
605
|
-
* @param {
|
|
619
|
+
* @param {unknown} value
|
|
606
620
|
* @returns {string}
|
|
607
621
|
*/
|
|
608
622
|
function toLitTextContent(value) {
|
|
609
623
|
// Handle $ref objects → emit as lit expression
|
|
610
|
-
if (
|
|
611
|
-
|
|
624
|
+
if (
|
|
625
|
+
value !== null &&
|
|
626
|
+
typeof value === "object" &&
|
|
627
|
+
typeof (/** @type {JxMutableNode} */ (value).$ref) === "string"
|
|
628
|
+
) {
|
|
629
|
+
return `\${${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (value).$ref))}}`;
|
|
612
630
|
}
|
|
613
631
|
if (typeof value === "string" && value.includes("${")) {
|
|
614
632
|
return toLitExpr(value);
|
|
@@ -617,7 +635,7 @@ function toLitTextContent(value) {
|
|
|
617
635
|
}
|
|
618
636
|
|
|
619
637
|
/**
|
|
620
|
-
* @param {
|
|
638
|
+
* @param {JxMutableNode} def
|
|
621
639
|
* @returns {string}
|
|
622
640
|
*/
|
|
623
641
|
function inlineHandlerBody(def) {
|
|
@@ -626,7 +644,7 @@ function inlineHandlerBody(def) {
|
|
|
626
644
|
}
|
|
627
645
|
|
|
628
646
|
/**
|
|
629
|
-
* @param {any} styleDef
|
|
647
|
+
* @param {JxStyle | Record<string, any> | null | undefined} styleDef
|
|
630
648
|
* @returns {string}
|
|
631
649
|
*/
|
|
632
650
|
function emitStyleString(styleDef) {
|
|
@@ -658,7 +676,7 @@ function emitStyleString(styleDef) {
|
|
|
658
676
|
/**
|
|
659
677
|
* Check if a children tree contains a `<slot>` element.
|
|
660
678
|
*
|
|
661
|
-
* @param {
|
|
679
|
+
* @param {(JxMutableNode | string)[] | undefined} children
|
|
662
680
|
* @returns {boolean}
|
|
663
681
|
*/
|
|
664
682
|
function treeHasSlot(children) {
|