@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.
@@ -13,7 +13,9 @@ import {
13
13
  collectStyles,
14
14
  compileExpression,
15
15
  isMutating,
16
- } from "../shared.js";
16
+ } from "../shared.ts";
17
+ import type { ExpressionNode } from "../shared.ts";
18
+ import type { JxStyle, JxMutableNode } from "@jxsuite/schema/types";
17
19
 
18
20
  /**
19
21
  * Compile a Jx custom element document to a JS module string.
@@ -22,18 +24,17 @@ import {
22
24
  * @param {Record<string, unknown>} [opts]
23
25
  * @returns {Promise<{ files: { path: string; content: string; tagName: string }[] }>}
24
26
  */
25
- export async function compileElement(sourcePath, opts = {}) {
26
- const { resolveElementPath, $media: optsMedia } = /** @type {JxMutableNode} */ (opts);
27
+ export async function compileElement(sourcePath: string | any, opts: Record<string, unknown> = {}) {
28
+ const { resolveElementPath, $media: optsMedia } = opts as JxMutableNode;
27
29
  /** @type {{ path: string; content: string; tagName: string }[]} */
28
- const files = [];
29
- /** @type {Set<string>} */
30
- const visited = new Set();
30
+ const files: { path: string; content: string; tagName: string }[] = [];
31
+ const visited: Set<string> = new Set();
31
32
 
32
33
  /**
33
34
  * @param {string | any} srcPath
34
35
  * @param {string | null} parentDir
35
36
  */
36
- async function processElement(srcPath, parentDir) {
37
+ async function processElement(srcPath: string | any, parentDir: string | null) {
37
38
  /** @type {JxMutableNode} */
38
39
  let doc;
39
40
  /** @type {string | null} */
@@ -53,8 +54,8 @@ export async function compileElement(sourcePath, opts = {}) {
53
54
  } else {
54
55
  doc = srcPath;
55
56
  filePath = null;
56
- if (visited.has(/** @type {string} */ (doc.tagName))) return;
57
- visited.add(/** @type {string} */ (doc.tagName));
57
+ if (visited.has(doc.tagName as string)) return;
58
+ visited.add(doc.tagName as string);
58
59
  }
59
60
 
60
61
  const tagName = doc.tagName;
@@ -66,11 +67,10 @@ export async function compileElement(sourcePath, opts = {}) {
66
67
  const currentDir = filePath ? dn(filePath) : null;
67
68
 
68
69
  // Process $elements dependencies depth-first
69
- /** @type {string[]} */
70
- const elementImports = [];
70
+ const elementImports: string[] = [];
71
71
  if (Array.isArray(doc.$elements)) {
72
72
  for (const elRef of doc.$elements) {
73
- const refPath = /** @type {JxMutableNode} */ (elRef).$ref ?? elRef;
73
+ const refPath = (elRef as JxMutableNode).$ref ?? elRef;
74
74
  if (typeof refPath !== "string") continue;
75
75
 
76
76
  if (currentDir) {
@@ -97,7 +97,7 @@ export async function compileElement(sourcePath, opts = {}) {
97
97
  files.push({ path: outputPath, content: jsContent, tagName });
98
98
  }
99
99
 
100
- await processElement(sourcePath, /** @type {string | null} */ (opts.basePath ?? null));
100
+ await processElement(sourcePath, (opts.basePath ?? null) as string | null);
101
101
  return { files };
102
102
  }
103
103
 
@@ -112,12 +112,15 @@ export async function compileElement(sourcePath, opts = {}) {
112
112
  * files: { path: string; content: string; tagName: string }[];
113
113
  * }>}
114
114
  */
115
- export async function compileElementPage(sourcePath, opts = {}) {
115
+ export async function compileElementPage(
116
+ sourcePath: string | any,
117
+ opts: Record<string, unknown> = {},
118
+ ) {
116
119
  const {
117
120
  title = "Jx App",
118
121
  reactivitySrc = "https://esm.sh/@vue/reactivity@3.5.13",
119
122
  litHtmlSrc = "https://esm.sh/lit-html@3.3.0",
120
- } = /** @type {JxMutableNode} */ (opts);
123
+ } = opts as JxMutableNode;
121
124
 
122
125
  const result = await compileElement(sourcePath, opts);
123
126
  const root = result.files[result.files.length - 1];
@@ -159,7 +162,7 @@ export async function compileElementPage(sourcePath, opts = {}) {
159
162
  * @param {JxMutableNode} def
160
163
  * @returns {string | undefined}
161
164
  */
162
- function extractInitialValue(def) {
165
+ function extractInitialValue(def: JxMutableNode) {
163
166
  if (def === null || typeof def !== "object" || Array.isArray(def)) {
164
167
  return JSON.stringify(def);
165
168
  }
@@ -190,9 +193,12 @@ function extractInitialValue(def) {
190
193
  * @param {string[]} elementImports
191
194
  * @returns {string}
192
195
  */
193
- export function emitElementModule(doc, className, elementImports) {
194
- /** @type {string[]} */
195
- const lines = [];
196
+ export function emitElementModule(
197
+ doc: JxMutableNode | Record<string, any>,
198
+ className: string,
199
+ elementImports: string[],
200
+ ) {
201
+ const lines: string[] = [];
196
202
 
197
203
  lines.push("// Generated by @jxsuite/compiler — do not edit manually");
198
204
  if (doc.$id) lines.push(`// Source: ${doc.$id}`);
@@ -202,15 +208,14 @@ export function emitElementModule(doc, className, elementImports) {
202
208
  }
203
209
 
204
210
  // Collect $src imports from state entries before emitting other imports
205
- /** @type {Map<string, string[]>} */
206
- const srcImportMap = new Map();
211
+ const srcImportMap: Map<string, string[]> = new Map();
207
212
  const defs = doc.state ?? {};
208
213
  for (const [key, def] of Object.entries(defs)) {
209
- const d = /** @type {JxMutableNode} */ (def);
214
+ const d = def as JxMutableNode;
210
215
  if (d && typeof d === "object" && !Array.isArray(d) && d.$prototype === "Function" && d.$src) {
211
216
  const srcPath = d.$src;
212
217
  if (!srcImportMap.has(srcPath)) srcImportMap.set(srcPath, []);
213
- /** @type {string[]} */ (srcImportMap.get(srcPath)).push(key);
218
+ (srcImportMap.get(srcPath) as string[]).push(key);
214
219
  }
215
220
  }
216
221
  for (const [srcPath, names] of srcImportMap) {
@@ -228,17 +233,14 @@ export function emitElementModule(doc, className, elementImports) {
228
233
  lines.push(" constructor() {");
229
234
  lines.push(" super();");
230
235
 
231
- /** @type {[string, string][]} */
232
- const stateEntries = [];
233
- /** @type {[string, any][]} */
234
- const computedEntries = [];
235
- /** @type {[string, any][]} */
236
- const functionEntries = [];
236
+ const stateEntries: [string, string][] = [];
237
+ const computedEntries: [string, any][] = [];
238
+ const functionEntries: [string, any][] = [];
237
239
 
238
240
  for (const [key, def] of Object.entries(defs)) {
239
- const d = /** @type {JxMutableNode} */ (def);
241
+ const d = def as JxMutableNode;
240
242
  if (d && typeof d === "object" && !Array.isArray(d) && "$expression" in d) {
241
- const node = /** @type {any} */ (d).$expression;
243
+ const node = (d as Record<string, unknown>).$expression as ExpressionNode;
242
244
  if (isMutating(node.operator)) {
243
245
  functionEntries.push([key, d]);
244
246
  } else {
@@ -309,8 +311,7 @@ export function emitElementModule(doc, className, elementImports) {
309
311
  lines.push("");
310
312
 
311
313
  // Collect CSS rules from children tree (assigns .jx-N classes to defs)
312
- /** @type {string[]} */
313
- const cssRules = [];
314
+ const cssRules: string[] = [];
314
315
  if (Array.isArray(doc.children)) {
315
316
  const counter = { n: 0 };
316
317
  for (const child of doc.children) {
@@ -348,8 +349,7 @@ export function emitElementModule(doc, className, elementImports) {
348
349
  lines.push(" }");
349
350
  lines.push(" }");
350
351
  if (doc.style && typeof doc.style === "object") {
351
- /** @type {[string, string][]} */
352
- const dynamicStyles = [];
352
+ const dynamicStyles: [string, string][] = [];
353
353
  for (const [prop, value] of Object.entries(doc.style)) {
354
354
  if (
355
355
  prop.startsWith(":") ||
@@ -370,8 +370,7 @@ export function emitElementModule(doc, className, elementImports) {
370
370
  for (const [cssProp, value] of dynamicStyles) {
371
371
  const expr = value.replace(
372
372
  /\$\{([^}]+)\}/g,
373
- (/** @type {string} */ _, /** @type {string} */ e) =>
374
- "${" + e.replace(/state\./g, "this.state.") + "}",
373
+ (_: string, e: string) => "${" + e.replace(/state\./g, "this.state.") + "}",
375
374
  );
376
375
  lines.push(` this.style['${cssProp}'] = \`${expr}\`;`);
377
376
  }
@@ -422,18 +421,20 @@ export function emitElementModule(doc, className, elementImports) {
422
421
  * @param {string} indent
423
422
  * @returns {string}
424
423
  */
425
- function emitLitChildren(children, parentStyle, indent) {
424
+ function emitLitChildren(
425
+ children: (JxMutableNode | string)[] | JxMutableNode | undefined,
426
+ parentStyle: JxStyle | Record<string, any> | null | undefined,
427
+ indent: string,
428
+ ) {
426
429
  if (!children) return "";
427
430
 
428
- if (/** @type {JxMutableNode} */ (children).$prototype === "Array") {
431
+ if ((children as JxMutableNode).$prototype === "Array") {
429
432
  return emitMappedArray(/** @type {JxMutableNode} */ (children), indent);
430
433
  }
431
434
 
432
435
  if (!Array.isArray(children)) return "";
433
436
 
434
- return children
435
- .map((/** @type {JxMutableNode | string} */ child) => emitLitNode(child, indent))
436
- .join("\n");
437
+ return children.map((child: JxMutableNode | string) => emitLitNode(child, indent)).join("\n");
437
438
  }
438
439
 
439
440
  /**
@@ -441,7 +442,7 @@ function emitLitChildren(children, parentStyle, indent) {
441
442
  * @param {string} indent
442
443
  * @returns {string}
443
444
  */
444
- function emitLitNode(def, indent) {
445
+ function emitLitNode(def: JxMutableNode | string, indent: string) {
445
446
  // String children are text nodes
446
447
  if (typeof def === "string") {
447
448
  if (def.includes("${")) return `${indent}${toLitTextContent(def)}`;
@@ -454,8 +455,7 @@ function emitLitNode(def, indent) {
454
455
 
455
456
  const tag = def.tagName ?? "div";
456
457
 
457
- /** @type {string[]} */
458
- const parts = [];
458
+ const parts: string[] = [];
459
459
 
460
460
  if (def.attributes) {
461
461
  for (const [key, val] of Object.entries(def.attributes)) {
@@ -486,10 +486,8 @@ function emitLitNode(def, indent) {
486
486
  )
487
487
  continue;
488
488
 
489
- if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
490
- parts.push(
491
- `.${key}="\${${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}}"`,
492
- );
489
+ if (val && typeof val === "object" && (val as JxMutableNode).$ref) {
490
+ parts.push(`.${key}="\${${refToExpr((val as JxMutableNode).$ref as string)}}"`);
493
491
  } else if (typeof val === "string" && val.includes("${")) {
494
492
  parts.push(`.${key}="${toLitExpr(val)}"`);
495
493
  }
@@ -497,10 +495,8 @@ function emitLitNode(def, indent) {
497
495
 
498
496
  if (def.$props) {
499
497
  for (const [key, val] of Object.entries(def.$props)) {
500
- if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
501
- parts.push(
502
- `.${key}="\${${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}}"`,
503
- );
498
+ if (val && typeof val === "object" && (val as JxMutableNode).$ref) {
499
+ parts.push(`.${key}="\${${refToExpr((val as JxMutableNode).$ref as string)}}"`);
504
500
  } else {
505
501
  parts.push(`.${key}="\${${JSON.stringify(val)}}"`);
506
502
  }
@@ -510,21 +506,20 @@ function emitLitNode(def, indent) {
510
506
  for (const [key, val] of Object.entries(def)) {
511
507
  if (!key.startsWith("on") || key === "observedAttributes") continue;
512
508
  const eventName = key.slice(2).toLowerCase();
513
- if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
509
+ if (val && typeof val === "object" && (val as JxMutableNode).$ref) {
514
510
  parts.push(
515
- `@${eventName}="\${(e) => ${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}(s, e)}"`,
511
+ `@${eventName}="\${(e) => ${refToExpr((val as JxMutableNode).$ref as string)}(s, e)}"`,
516
512
  );
517
513
  } else if (val && typeof val === "object" && "$expression" in /** @type {any} */ (val)) {
518
- const compiled = compileExpression(/** @type {any} */ (val).$expression, {
519
- statePrefix: "s",
520
- eventParam: "e",
521
- });
514
+ const compiled = compileExpression(
515
+ (val as Record<string, unknown>).$expression as ExpressionNode,
516
+ {
517
+ statePrefix: "s",
518
+ eventParam: "e",
519
+ },
520
+ );
522
521
  parts.push(`@${eventName}="\${(e) => { ${compiled}; }}"`);
523
- } else if (
524
- val &&
525
- typeof val === "object" &&
526
- /** @type {JxMutableNode} */ (val).$prototype === "Function"
527
- ) {
522
+ } else if (val && typeof val === "object" && (val as JxMutableNode).$prototype === "Function") {
528
523
  parts.push(
529
524
  `@${eventName}="\${(e) => { ${inlineHandlerBody(/** @type {JxMutableNode} */ (val))} }}"`,
530
525
  );
@@ -558,22 +553,19 @@ function emitLitNode(def, indent) {
558
553
  * @param {string} indent
559
554
  * @returns {string}
560
555
  */
561
- function emitMappedArray(arrayDef, indent) {
556
+ function emitMappedArray(arrayDef: JxMutableNode, indent: string) {
562
557
  const itemsExpr = arrayDef.items?.$ref ? refToExpr(arrayDef.items.$ref) : "ITEMS";
563
558
  const mapDef = arrayDef.map;
564
559
 
565
560
  if (!mapDef) return "";
566
561
 
567
562
  const tag = mapDef.tagName ?? "div";
568
- /** @type {string[]} */
569
- const parts = [];
563
+ const parts: string[] = [];
570
564
 
571
565
  if (mapDef.$props) {
572
566
  for (const [key, val] of Object.entries(mapDef.$props)) {
573
- if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
574
- parts.push(
575
- `.${key}="\${${mapRefToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}}"`,
576
- );
567
+ if (val && typeof val === "object" && (val as JxMutableNode).$ref) {
568
+ parts.push(`.${key}="\${${mapRefToExpr((val as JxMutableNode).$ref as string)}}"`);
577
569
  } else {
578
570
  parts.push(`.${key}="\${${JSON.stringify(val)}}"`);
579
571
  }
@@ -586,15 +578,18 @@ function emitMappedArray(arrayDef, indent) {
586
578
  for (const [key, val] of Object.entries(mapDef)) {
587
579
  if (!key.startsWith("on")) continue;
588
580
  const eventName = key.slice(2).toLowerCase();
589
- if (val && typeof val === "object" && /** @type {JxMutableNode} */ (val).$ref) {
581
+ if (val && typeof val === "object" && (val as JxMutableNode).$ref) {
590
582
  parts.push(
591
- `@${eventName}="\${(e) => ${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}(s, e)}"`,
583
+ `@${eventName}="\${(e) => ${refToExpr((val as JxMutableNode).$ref as string)}(s, e)}"`,
592
584
  );
593
585
  } else if (val && typeof val === "object" && "$expression" in /** @type {any} */ (val)) {
594
- const compiled = compileExpression(/** @type {any} */ (val).$expression, {
595
- statePrefix: "s",
596
- eventParam: "e",
597
- });
586
+ const compiled = compileExpression(
587
+ (val as Record<string, unknown>).$expression as ExpressionNode,
588
+ {
589
+ statePrefix: "s",
590
+ eventParam: "e",
591
+ },
592
+ );
598
593
  parts.push(`@${eventName}="\${(e) => { ${compiled}; }}"`);
599
594
  }
600
595
  }
@@ -619,7 +614,7 @@ function emitMappedArray(arrayDef, indent) {
619
614
  * @param {string} ref
620
615
  * @returns {string}
621
616
  */
622
- function refToExpr(ref) {
617
+ function refToExpr(ref: string) {
623
618
  if (ref.startsWith("#/state/")) {
624
619
  const path = ref.slice("#/state/".length);
625
620
  return "s." + path.replace(/\//g, ".");
@@ -635,7 +630,7 @@ function refToExpr(ref) {
635
630
  * @param {string} ref
636
631
  * @returns {string}
637
632
  */
638
- function mapRefToExpr(ref) {
633
+ function mapRefToExpr(ref: string) {
639
634
  if (ref.startsWith("$map/")) {
640
635
  return ref.slice("$map/".length).replace(/\//g, ".");
641
636
  }
@@ -646,7 +641,7 @@ function mapRefToExpr(ref) {
646
641
  * @param {string} str
647
642
  * @returns {string}
648
643
  */
649
- function toLitExpr(str) {
644
+ function toLitExpr(str: string) {
650
645
  return str.replace(/state\./g, "s.");
651
646
  }
652
647
 
@@ -657,14 +652,14 @@ function toLitExpr(str) {
657
652
  * @param {unknown} value
658
653
  * @returns {string}
659
654
  */
660
- function toLitTextContent(value) {
655
+ function toLitTextContent(value: unknown) {
661
656
  // Handle $ref objects → emit as lit expression
662
657
  if (
663
658
  value !== null &&
664
659
  typeof value === "object" &&
665
- typeof (/** @type {JxMutableNode} */ (value).$ref) === "string"
660
+ typeof (value as JxMutableNode).$ref === "string"
666
661
  ) {
667
- return `\${${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (value).$ref))}}`;
662
+ return `\${${refToExpr((value as JxMutableNode).$ref as string)}}`;
668
663
  }
669
664
  if (typeof value === "string" && value.includes("${")) {
670
665
  return toLitExpr(value);
@@ -676,7 +671,7 @@ function toLitTextContent(value) {
676
671
  * @param {JxMutableNode} def
677
672
  * @returns {string}
678
673
  */
679
- function inlineHandlerBody(def) {
674
+ function inlineHandlerBody(def: JxMutableNode) {
680
675
  const body = def.body ?? "";
681
676
  return body.replace(/(?<!this\.)state\./g, "s.").replace(/(?<!this\.)state(?!\.)/g, "s");
682
677
  }
@@ -685,11 +680,10 @@ function inlineHandlerBody(def) {
685
680
  * @param {JxStyle | Record<string, any> | null | undefined} styleDef
686
681
  * @returns {string}
687
682
  */
688
- function emitStyleString(styleDef) {
683
+ function emitStyleString(styleDef: JxStyle | Record<string, any> | null | undefined) {
689
684
  if (!styleDef || typeof styleDef !== "object") return "";
690
685
 
691
- /** @type {string[]} */
692
- const parts = [];
686
+ const parts: string[] = [];
693
687
  for (const [prop, value] of Object.entries(styleDef)) {
694
688
  if (
695
689
  prop.startsWith(":") ||
@@ -717,7 +711,7 @@ function emitStyleString(styleDef) {
717
711
  * @param {(JxMutableNode | string)[] | undefined} children
718
712
  * @returns {boolean}
719
713
  */
720
- function treeHasSlot(children) {
714
+ function treeHasSlot(children: (JxMutableNode | string)[] | undefined) {
721
715
  if (!Array.isArray(children)) return false;
722
716
  for (const child of children) {
723
717
  if (!child || typeof child !== "object") continue;