@jxsuite/compiler 0.22.1 → 0.23.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/compiler",
3
- "version": "0.22.1",
3
+ "version": "0.23.0",
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.22.1",
42
- "@jxsuite/runtime": "^0.22.1",
41
+ "@jxsuite/parser": "^0.23.0",
42
+ "@jxsuite/runtime": "^0.23.0",
43
43
  "remark-gfm": "^4.0.1",
44
44
  "remark-stringify": "^11.0.0",
45
45
  "sharp": "^0.34.5",
package/src/shared.js CHANGED
@@ -6,9 +6,17 @@
6
6
  */
7
7
 
8
8
  import { camelToKebab, toCSSText, RESERVED_KEYS } from "@jxsuite/runtime";
9
+ import { compileExpression, isMutating, evaluateExpression } from "@jxsuite/runtime/expression";
9
10
 
10
11
  // Re-export runtime utilities used by submodules
11
- export { camelToKebab, toCSSText, RESERVED_KEYS };
12
+ export {
13
+ camelToKebab,
14
+ toCSSText,
15
+ RESERVED_KEYS,
16
+ compileExpression,
17
+ isMutating,
18
+ evaluateExpression,
19
+ };
12
20
 
13
21
  // CDN defaults
14
22
  export const DEFAULT_REACTIVITY_SRC = "https://esm.sh/@vue/reactivity@3.5.32";
@@ -231,7 +239,7 @@ export function buildInitialScope(defs = {}, parentScope = null) {
231
239
  setOwnScopeValue(scope, key, cloneValue(d.default));
232
240
  continue;
233
241
  }
234
- if (!d.$prototype && !isSchemaOnly(d)) {
242
+ if (!d.$prototype && !("$expression" in d) && !isSchemaOnly(d)) {
235
243
  setOwnScopeValue(scope, key, cloneValue(d));
236
244
  }
237
245
  }
@@ -243,6 +251,17 @@ export function buildInitialScope(defs = {}, parentScope = null) {
243
251
  }
244
252
  if (!def || typeof def !== "object") continue;
245
253
  const d = /** @type {JxStateObject & JxPrototypeDef} */ (def);
254
+ if ("$expression" in d) {
255
+ const node = /** @type {any} */ (d).$expression;
256
+ if (isMutating(node.operator)) {
257
+ setOwnScopeValue(scope, key, (/** @type {any} */ s, /** @type {any} */ event) =>
258
+ evaluateExpression(node, s, event),
259
+ );
260
+ } else {
261
+ defineLazyScopeValue(scope, key, () => evaluateExpression(node, scope, null));
262
+ }
263
+ continue;
264
+ }
246
265
  if (d.$prototype === "Function") {
247
266
  if (d.body) {
248
267
  const fn = new Function("state", ...(d.parameters ?? d.arguments ?? []), d.body);
@@ -23,6 +23,8 @@ import {
23
23
  escapeHtml,
24
24
  DEFAULT_REACTIVITY_SRC,
25
25
  DEFAULT_LIT_HTML_SRC,
26
+ compileExpression,
27
+ isMutating,
26
28
  } from "../shared.js";
27
29
 
28
30
  /**
@@ -85,6 +87,19 @@ export function compileClient(raw, opts) {
85
87
 
86
88
  const d = /** @type {JxMutableNode} */ (def);
87
89
 
90
+ // $expression: Shape 5
91
+ if ("$expression" in d) {
92
+ const node = /** @type {any} */ (d).$expression;
93
+ if (isMutating(node.operator)) {
94
+ const compiled = compileExpression(node, { statePrefix: "state", eventParam: "e" });
95
+ onEntries.push([key, { args: ["state", "e"], body: compiled }]);
96
+ } else {
97
+ const compiled = compileExpression(node, { statePrefix: "state", eventParam: "e" });
98
+ computedEntries.push([key, `() => ${compiled}`]);
99
+ }
100
+ continue;
101
+ }
102
+
88
103
  // $prototype: "Function"
89
104
  if (d.$prototype === "Function") {
90
105
  const args = d.parameters ?? d.arguments;
@@ -274,6 +289,15 @@ function buildClientNode(def, raw, context, bindings, handlers, counter) {
274
289
  bindAttrs.push(`@${eventName}="${key}"`);
275
290
  handlers.set(key, { args: v.parameters ?? v.arguments ?? ["state", "event"], body: v.body });
276
291
  needsBind = true;
292
+ } else if (val && typeof val === "object" && "$expression" in /** @type {any} */ (val)) {
293
+ const key = `_h${counter.h++}`;
294
+ bindAttrs.push(`@${eventName}="${key}"`);
295
+ const compiled = compileExpression(/** @type {any} */ (val).$expression, {
296
+ statePrefix: "state",
297
+ eventParam: "e",
298
+ });
299
+ handlers.set(key, { args: ["state", "e"], body: compiled });
300
+ needsBind = true;
277
301
  }
278
302
  }
279
303
 
@@ -500,6 +524,12 @@ function emitLitMapTemplate(def) {
500
524
  ) {
501
525
  const body = mapRefsToLit(/** @type {string} */ (/** @type {JxMutableNode} */ (val).body));
502
526
  attrs += " @" + eventName + "=${(e) => { " + body + " }}";
527
+ } else if (val && typeof val === "object" && "$expression" in /** @type {any} */ (val)) {
528
+ const compiled = compileExpression(/** @type {any} */ (val).$expression, {
529
+ statePrefix: "state",
530
+ eventParam: "e",
531
+ });
532
+ attrs += " @" + eventName + "=${(e) => { state.$map = { item, index }; " + compiled + "; }}";
503
533
  }
504
534
  }
505
535
 
@@ -6,7 +6,14 @@
6
6
  */
7
7
 
8
8
  import { camelToKebab, RESERVED_KEYS } from "@jxsuite/runtime";
9
- import { escapeHtml, tagNameToClassName, isSchemaOnly, collectStyles } from "../shared.js";
9
+ import {
10
+ escapeHtml,
11
+ tagNameToClassName,
12
+ isSchemaOnly,
13
+ collectStyles,
14
+ compileExpression,
15
+ isMutating,
16
+ } from "../shared.js";
10
17
 
11
18
  /**
12
19
  * Compile a Jx custom element document to a JS module string.
@@ -230,7 +237,14 @@ export function emitElementModule(doc, className, elementImports) {
230
237
 
231
238
  for (const [key, def] of Object.entries(defs)) {
232
239
  const d = /** @type {JxMutableNode} */ (def);
233
- if (d && typeof d === "object" && !Array.isArray(d) && d.$prototype === "Function") {
240
+ if (d && typeof d === "object" && !Array.isArray(d) && "$expression" in d) {
241
+ const node = /** @type {any} */ (d).$expression;
242
+ if (isMutating(node.operator)) {
243
+ functionEntries.push([key, d]);
244
+ } else {
245
+ computedEntries.push([key, d]);
246
+ }
247
+ } else if (d && typeof d === "object" && !Array.isArray(d) && d.$prototype === "Function") {
234
248
  if (typeof d.body === "string" && d.body.includes("return")) {
235
249
  computedEntries.push([key, d]);
236
250
  } else {
@@ -255,7 +269,10 @@ export function emitElementModule(doc, className, elementImports) {
255
269
  // Emit functions: this.state.fnName = (state) => { body } or imported $src function
256
270
  for (const [key, def] of functionEntries) {
257
271
  lines.push("");
258
- if (def.$src) {
272
+ if ("$expression" in def) {
273
+ const compiled = compileExpression(def.$expression, { statePrefix: "s", eventParam: "e" });
274
+ lines.push(` this.state.${key} = (s, e) => { ${compiled}; };`);
275
+ } else if (def.$src) {
259
276
  // $src function — wrap imported function so it receives state
260
277
  const args = def.parameters ?? def.arguments ?? ["state"];
261
278
  const paramList = args.join(", ");
@@ -272,7 +289,13 @@ export function emitElementModule(doc, className, elementImports) {
272
289
  // Emit computed signals — $src or inline body
273
290
  for (const [key, def] of computedEntries) {
274
291
  lines.push("");
275
- if (def.$src) {
292
+ if ("$expression" in def) {
293
+ const compiled = compileExpression(def.$expression, {
294
+ statePrefix: "this.state",
295
+ eventParam: "e",
296
+ });
297
+ lines.push(` this.state.${key} = computed(() => ${compiled});`);
298
+ } else if (def.$src) {
276
299
  lines.push(` this.state.${key} = computed(() => ${key}(this.state));`);
277
300
  } else {
278
301
  lines.push(` this.state.${key} = computed(() => {`);
@@ -491,6 +514,12 @@ function emitLitNode(def, indent) {
491
514
  parts.push(
492
515
  `@${eventName}="\${(e) => ${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}(s, e)}"`,
493
516
  );
517
+ } 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
+ });
522
+ parts.push(`@${eventName}="\${(e) => { ${compiled}; }}"`);
494
523
  } else if (
495
524
  val &&
496
525
  typeof val === "object" &&
@@ -561,6 +590,12 @@ function emitMappedArray(arrayDef, indent) {
561
590
  parts.push(
562
591
  `@${eventName}="\${(e) => ${refToExpr(/** @type {string} */ (/** @type {JxMutableNode} */ (val).$ref))}(s, e)}"`,
563
592
  );
593
+ } 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
+ });
598
+ parts.push(`@${eventName}="\${(e) => { ${compiled}; }}"`);
564
599
  }
565
600
  }
566
601