@jxsuite/compiler 1.0.0 → 1.1.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/cli.js +163 -157
- package/dist/compiler.js +102 -99
- package/package.json +6 -6
- package/src/cli.ts +30 -0
- package/src/shared.ts +53 -4
- package/src/site/dev-command.ts +84 -0
- package/src/site/head-merger.ts +13 -0
- package/src/site/image-cache.ts +57 -6
- package/src/site/image-transform.ts +21 -0
- package/src/site/preview-server.ts +93 -0
- package/src/site/site-build.ts +16 -2
- package/src/targets/compile-client.ts +36 -5
- package/src/targets/compile-element.ts +41 -4
|
@@ -17,8 +17,10 @@ import {
|
|
|
17
17
|
DEFAULT_REACTIVITY_SRC,
|
|
18
18
|
buildAttrs,
|
|
19
19
|
compileExpression,
|
|
20
|
+
compileStatements,
|
|
20
21
|
compileStyles,
|
|
21
22
|
createCompileContext,
|
|
23
|
+
emitFormulaFn,
|
|
22
24
|
escapeHtml,
|
|
23
25
|
isMutating,
|
|
24
26
|
isRefObject,
|
|
@@ -27,10 +29,12 @@ import {
|
|
|
27
29
|
resolveStaticValue,
|
|
28
30
|
} from "../shared.ts";
|
|
29
31
|
import {
|
|
32
|
+
hasStructuredBody,
|
|
30
33
|
isExpandedSignal,
|
|
31
34
|
isExpressionDef,
|
|
32
35
|
isFunctionDef,
|
|
33
36
|
isMappedArray,
|
|
37
|
+
isNamedFormulaDef,
|
|
34
38
|
isPrototypeDef,
|
|
35
39
|
paramNames,
|
|
36
40
|
} from "@jxsuite/schema/guards";
|
|
@@ -124,7 +128,11 @@ export function compileClient(
|
|
|
124
128
|
eventParam: "e",
|
|
125
129
|
statePrefix: "state",
|
|
126
130
|
});
|
|
127
|
-
if (
|
|
131
|
+
if (isNamedFormulaDef(def)) {
|
|
132
|
+
// Named formula → a scope callable mapping positional args onto declared parameters.
|
|
133
|
+
// Call sites stay positional, matching the interpreter.
|
|
134
|
+
initBlocks.push(`state[${JSON.stringify(key)}] = ${emitFormulaFn(def, compiled)};`);
|
|
135
|
+
} else if (isMutating(node.operator)) {
|
|
128
136
|
onEntries.push([key, { args: ["state", "e"], body: compiled }]);
|
|
129
137
|
} else {
|
|
130
138
|
computedEntries.push([key, `() => ${compiled}`]);
|
|
@@ -135,7 +143,18 @@ export function compileClient(
|
|
|
135
143
|
// $prototype: "Function"
|
|
136
144
|
if (isFunctionDef(def)) {
|
|
137
145
|
const args = def.parameters ? paramNames(def.parameters) : def.arguments;
|
|
138
|
-
if (def
|
|
146
|
+
if (hasStructuredBody(def)) {
|
|
147
|
+
// Structured body (spec §20) → an event handler compiled from statements.
|
|
148
|
+
// With parameters, a positional callable like a named formula.
|
|
149
|
+
const compiled = compileStatements(def.body, { eventParam: "e", statePrefix: "state" });
|
|
150
|
+
if (def.parameters && def.parameters.length > 0) {
|
|
151
|
+
initBlocks.push(
|
|
152
|
+
`state[${JSON.stringify(key)}] = ${emitFormulaFn(def, `(() => {\n${compiled}\n})()`)};`,
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
onEntries.push([key, { args: ["state", "e"], body: compiled }]);
|
|
156
|
+
}
|
|
157
|
+
} else if (def.$src) {
|
|
139
158
|
if (!srcImportMap.has(def.$src)) {
|
|
140
159
|
srcImportMap.set(def.$src, new Set());
|
|
141
160
|
}
|
|
@@ -143,12 +162,12 @@ export function compileClient(
|
|
|
143
162
|
|
|
144
163
|
// $src functions always produce computed entries (they return values)
|
|
145
164
|
computedEntries.push([key, `() => { return ${key}(state); }`]);
|
|
146
|
-
} else if (def.body && def.body.includes("return")) {
|
|
165
|
+
} else if (typeof def.body === "string" && def.body.includes("return")) {
|
|
147
166
|
// Body contains return → computed
|
|
148
167
|
computedEntries.push([key, `() => { ${def.body} }`]);
|
|
149
168
|
} else {
|
|
150
169
|
// No return → event handler
|
|
151
|
-
onEntries.push([key, { args: args ?? ["state"], body: def.body }]);
|
|
170
|
+
onEntries.push([key, { args: args ?? ["state"], body: def.body as string | undefined }]);
|
|
152
171
|
}
|
|
153
172
|
continue;
|
|
154
173
|
}
|
|
@@ -335,13 +354,22 @@ function buildClientNode(
|
|
|
335
354
|
const key = refToBindingKey(val.$ref);
|
|
336
355
|
bindAttrs.push(`@${eventName}="${key}"`);
|
|
337
356
|
needsBind = true;
|
|
357
|
+
} else if (hasStructuredBody(val)) {
|
|
358
|
+
const key = `_h${counter.h}`;
|
|
359
|
+
counter.h += 1;
|
|
360
|
+
bindAttrs.push(`@${eventName}="${key}"`);
|
|
361
|
+
handlers.set(key, {
|
|
362
|
+
args: ["state", "e"],
|
|
363
|
+
body: compileStatements(val.body, { eventParam: "e", statePrefix: "state" }),
|
|
364
|
+
});
|
|
365
|
+
needsBind = true;
|
|
338
366
|
} else if (isFunctionDef(val)) {
|
|
339
367
|
const key = `_h${counter.h}`;
|
|
340
368
|
counter.h += 1;
|
|
341
369
|
bindAttrs.push(`@${eventName}="${key}"`);
|
|
342
370
|
handlers.set(key, {
|
|
343
371
|
args: val.parameters ? paramNames(val.parameters) : (val.arguments ?? ["state", "event"]),
|
|
344
|
-
body: val.body,
|
|
372
|
+
body: val.body as string | undefined,
|
|
345
373
|
});
|
|
346
374
|
needsBind = true;
|
|
347
375
|
} else if (isExpressionDef(val)) {
|
|
@@ -594,6 +622,9 @@ function emitLitMapTemplate(def: JxMutableNode | undefined) {
|
|
|
594
622
|
if (isRefObject(val)) {
|
|
595
623
|
const key = refToBindingKey((val as JxMutableNode).$ref as string);
|
|
596
624
|
attrs += ` @${eventName}=\${(e) => { state.$map = { item, index }; on.${key}(e); }}`;
|
|
625
|
+
} else if (hasStructuredBody(val)) {
|
|
626
|
+
const compiled = compileStatements(val.body, { eventParam: "e", statePrefix: "state" });
|
|
627
|
+
attrs += ` @${eventName}=\${(e) => { state.$map = { item, index }; ${compiled} }}`;
|
|
597
628
|
} else if (val && typeof val === "object" && (val as JxMutableNode).$prototype === "Function") {
|
|
598
629
|
const body = mapRefsToLit((val as JxMutableNode).body as string);
|
|
599
630
|
attrs += ` @${eventName}=\${(e) => { ${body} }}`;
|
|
@@ -9,15 +9,19 @@ import { RESERVED_KEYS, camelToKebab } from "@jxsuite/runtime";
|
|
|
9
9
|
import {
|
|
10
10
|
collectStyles,
|
|
11
11
|
compileExpression,
|
|
12
|
+
compileStatements,
|
|
13
|
+
emitFormulaFn,
|
|
12
14
|
escapeHtml,
|
|
13
15
|
isMutating,
|
|
14
16
|
isSchemaOnly,
|
|
15
17
|
tagNameToClassName,
|
|
16
18
|
} from "../shared.ts";
|
|
17
19
|
import {
|
|
20
|
+
hasStructuredBody,
|
|
18
21
|
isExpressionDef,
|
|
19
22
|
isFunctionDef,
|
|
20
23
|
isMappedArray,
|
|
24
|
+
isNamedFormulaDef,
|
|
21
25
|
isRef,
|
|
22
26
|
paramNames,
|
|
23
27
|
} from "@jxsuite/schema/guards";
|
|
@@ -284,11 +288,14 @@ export function emitElementModule(doc: JxDocument, className: string, elementImp
|
|
|
284
288
|
const computedEntries: [string, JxExpressionDef | JxFunctionDef][] = [];
|
|
285
289
|
const functionEntries: [string, JxExpressionDef | JxFunctionDef][] = [];
|
|
286
290
|
|
|
291
|
+
const formulaEntries: [string, JxExpressionDef][] = [];
|
|
287
292
|
for (const [key, def] of Object.entries(defs)) {
|
|
288
293
|
const d = def as JxMutableNode;
|
|
289
294
|
if (isExpressionDef(d)) {
|
|
290
295
|
const node = d.$expression as ExpressionNode;
|
|
291
|
-
if (
|
|
296
|
+
if (isNamedFormulaDef(d)) {
|
|
297
|
+
formulaEntries.push([key, d]);
|
|
298
|
+
} else if (isMutating(node.operator)) {
|
|
292
299
|
functionEntries.push([key, d]);
|
|
293
300
|
} else {
|
|
294
301
|
computedEntries.push([key, d]);
|
|
@@ -324,6 +331,16 @@ export function emitElementModule(doc: JxDocument, className: string, elementImp
|
|
|
324
331
|
statePrefix: "s",
|
|
325
332
|
});
|
|
326
333
|
lines.push(` this.state.${key} = (s, e) => { ${compiled}; };`);
|
|
334
|
+
} else if (hasStructuredBody(def)) {
|
|
335
|
+
// Structured body (spec §20) — statements compiled against this.state; dispatch target is
|
|
336
|
+
// The component instance itself (WHATWG dispatchEvent on the custom element).
|
|
337
|
+
const compiled = compileStatements(def.body, {
|
|
338
|
+
dispatchTarget: "this",
|
|
339
|
+
eventParam: "e",
|
|
340
|
+
indent: " ",
|
|
341
|
+
statePrefix: "this.state",
|
|
342
|
+
});
|
|
343
|
+
lines.push(` this.state.${key} = (s, e) => {`, compiled, " };");
|
|
327
344
|
} else {
|
|
328
345
|
const args = def.parameters ? paramNames(def.parameters) : (def.arguments ?? ["state"]);
|
|
329
346
|
const paramList = args.join(", ");
|
|
@@ -333,13 +350,23 @@ export function emitElementModule(doc: JxDocument, className: string, elementImp
|
|
|
333
350
|
} else {
|
|
334
351
|
lines.push(
|
|
335
352
|
` this.state.${key} = (${paramList}) => {`,
|
|
336
|
-
` ${def.body
|
|
353
|
+
` ${typeof def.body === "string" ? def.body : ""}`,
|
|
337
354
|
" };",
|
|
338
355
|
);
|
|
339
356
|
}
|
|
340
357
|
}
|
|
341
358
|
}
|
|
342
359
|
|
|
360
|
+
// Emit named formulas — scope callables mapping positional args onto declared parameters
|
|
361
|
+
for (const [key, def] of formulaEntries) {
|
|
362
|
+
lines.push("");
|
|
363
|
+
const compiled = compileExpression(def.$expression as ExpressionNode, {
|
|
364
|
+
eventParam: "e",
|
|
365
|
+
statePrefix: "this.state",
|
|
366
|
+
});
|
|
367
|
+
lines.push(` this.state.${key} = ${emitFormulaFn(def, compiled)};`);
|
|
368
|
+
}
|
|
369
|
+
|
|
343
370
|
// Emit computed signals — $src or inline body
|
|
344
371
|
for (const [key, def] of computedEntries) {
|
|
345
372
|
lines.push("");
|
|
@@ -353,7 +380,10 @@ export function emitElementModule(doc: JxDocument, className: string, elementImp
|
|
|
353
380
|
lines.push(` this.state.${key} = computed(() => ${key}(this.state));`);
|
|
354
381
|
} else {
|
|
355
382
|
lines.push(` this.state.${key} = computed(() => {`);
|
|
356
|
-
const body = (def.body
|
|
383
|
+
const body = (typeof def.body === "string" ? def.body : "").replaceAll(
|
|
384
|
+
"state.",
|
|
385
|
+
"this.state.",
|
|
386
|
+
);
|
|
357
387
|
lines.push(` ${body}`, " });");
|
|
358
388
|
}
|
|
359
389
|
}
|
|
@@ -765,7 +795,14 @@ function toLitTextContent(value: unknown) {
|
|
|
765
795
|
* @returns {string}
|
|
766
796
|
*/
|
|
767
797
|
function inlineHandlerBody(def: JxFunctionDef) {
|
|
768
|
-
|
|
798
|
+
if (hasStructuredBody(def)) {
|
|
799
|
+
return compileStatements(def.body, {
|
|
800
|
+
dispatchTarget: "e.currentTarget",
|
|
801
|
+
eventParam: "e",
|
|
802
|
+
statePrefix: "s",
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
const body = typeof def.body === "string" ? def.body : "";
|
|
769
806
|
return body.replaceAll(/(?<!this\.)state\./g, "s.").replaceAll(/(?<!this\.)state(?!\.)/g, "s");
|
|
770
807
|
}
|
|
771
808
|
|