@barefootjs/mojolicious 0.5.0 → 0.5.2
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/adapter/index.js +109 -145
- package/dist/adapter/mojo-adapter.d.ts +16 -46
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/build.js +109 -145
- package/dist/index.js +109 -145
- package/dist/test-render.d.ts +5 -0
- package/dist/test-render.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/evaluate-signal-init.test.ts +35 -0
- package/src/__tests__/mojo-adapter.test.ts +292 -0
- package/src/adapter/mojo-adapter.ts +213 -294
- package/src/test-render.ts +62 -37
package/dist/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
parseExpression as parseExpression2,
|
|
6
6
|
isSupported,
|
|
7
7
|
identifierPath,
|
|
8
|
-
stringifyParsedExpr,
|
|
9
8
|
emitParsedExpr,
|
|
10
9
|
emitIRNode,
|
|
11
10
|
emitAttrValue
|
|
@@ -72,7 +71,6 @@ var MOJO_TEMPLATE_PRIMITIVES = {
|
|
|
72
71
|
"Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
|
|
73
72
|
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
|
|
74
73
|
};
|
|
75
|
-
var PRIMITIVE_SUBSTRING_RE = new RegExp(Object.keys(MOJO_TEMPLATE_PRIMITIVES).map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"));
|
|
76
74
|
var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
77
75
|
function resolveJsxChildrenProp(props) {
|
|
78
76
|
const prop = props.find((p) => p.name === "children");
|
|
@@ -93,9 +91,9 @@ class MojoAdapter extends BaseAdapter {
|
|
|
93
91
|
options;
|
|
94
92
|
errors = [];
|
|
95
93
|
inLoop = false;
|
|
96
|
-
higherOrderInFlight = new Set;
|
|
97
94
|
propsObjectName = null;
|
|
98
95
|
propsParams = [];
|
|
96
|
+
stringValueNames = new Set;
|
|
99
97
|
constructor(options = {}) {
|
|
100
98
|
super();
|
|
101
99
|
this.options = {
|
|
@@ -107,8 +105,17 @@ class MojoAdapter extends BaseAdapter {
|
|
|
107
105
|
this.componentName = ir.metadata.componentName;
|
|
108
106
|
this.propsObjectName = ir.metadata.propsObjectName ?? null;
|
|
109
107
|
this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
|
|
108
|
+
this.stringValueNames = new Set;
|
|
109
|
+
for (const s of ir.metadata.signals) {
|
|
110
|
+
if (isStringTypeInfo(s.type) || isBareStringLiteral(s.initialValue)) {
|
|
111
|
+
this.stringValueNames.add(s.getter);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const p of ir.metadata.propsParams) {
|
|
115
|
+
if (isStringTypeInfo(p.type))
|
|
116
|
+
this.stringValueNames.add(p.name);
|
|
117
|
+
}
|
|
110
118
|
this.errors = [];
|
|
111
|
-
this.higherOrderInFlight = new Set;
|
|
112
119
|
this.childrenCaptureCounter = 0;
|
|
113
120
|
if (!options?.siblingTemplatesRegistered) {
|
|
114
121
|
this.checkImportedLoopChildComponents(ir);
|
|
@@ -400,8 +407,10 @@ ${whenTrue}
|
|
|
400
407
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
401
408
|
const prevInLoop = this.inLoop;
|
|
402
409
|
this.inLoop = true;
|
|
403
|
-
const
|
|
410
|
+
const renderedChildren = this.renderChildren(loop.children);
|
|
404
411
|
this.inLoop = prevInLoop;
|
|
412
|
+
const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
|
|
413
|
+
${renderedChildren}` : renderedChildren;
|
|
405
414
|
const lines = [];
|
|
406
415
|
lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
|
|
407
416
|
if (sortedHoist && loop.sortComparator) {
|
|
@@ -572,7 +581,7 @@ ${children}`;
|
|
|
572
581
|
return `${BF_COND}="${condId}"`;
|
|
573
582
|
}
|
|
574
583
|
renderPerlFilterExpr(expr, param, localVarMap = new Map) {
|
|
575
|
-
return emitParsedExpr(expr, new MojoFilterEmitter(param, localVarMap));
|
|
584
|
+
return emitParsedExpr(expr, new MojoFilterEmitter(param, localVarMap, (n) => this._isStringValueName(n)));
|
|
576
585
|
}
|
|
577
586
|
renderBlockBodyCondition(statements, param) {
|
|
578
587
|
const localVarMap = new Map;
|
|
@@ -705,154 +714,53 @@ ${reason}` : "";
|
|
|
705
714
|
return true;
|
|
706
715
|
}
|
|
707
716
|
convertExpressionToPerl(expr) {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
if (/\.\s*join\s*\(/.test(expr)) {
|
|
715
|
-
return this.convertHigherOrderExpr(expr);
|
|
716
|
-
}
|
|
717
|
-
const mojoOnlyMatch = /\.\s*(?<method>find|findIndex|findLast|findLastIndex)\s*\(/.exec(expr);
|
|
718
|
-
if (mojoOnlyMatch) {
|
|
719
|
-
const methodName = mojoOnlyMatch.groups.method;
|
|
717
|
+
const trimmed = expr.trim();
|
|
718
|
+
if (trimmed === "")
|
|
719
|
+
return "''";
|
|
720
|
+
const parsed = parseExpression2(trimmed);
|
|
721
|
+
const support = isSupported(parsed);
|
|
722
|
+
if (!support.supported) {
|
|
720
723
|
this.errors.push({
|
|
721
724
|
code: "BF101",
|
|
722
725
|
severity: "error",
|
|
723
|
-
message: `
|
|
726
|
+
message: `Expression not supported: ${trimmed}`,
|
|
724
727
|
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
725
728
|
suggestion: {
|
|
726
|
-
message:
|
|
729
|
+
message: support.reason ? `${support.reason}
|
|
730
|
+
|
|
731
|
+
Options:
|
|
732
|
+
1. Use /* @client */ for client-side evaluation
|
|
733
|
+
2. Pre-compute the value in Perl` : `Options:
|
|
727
734
|
1. Use /* @client */ for client-side evaluation
|
|
728
735
|
2. Pre-compute the value in Perl`
|
|
729
736
|
}
|
|
730
737
|
});
|
|
731
738
|
return "''";
|
|
732
739
|
}
|
|
733
|
-
|
|
734
|
-
let result = expr.replace(/\b([a-z_]\w*)\(\)/g, (_, name) => `$${name}`);
|
|
735
|
-
result = result.replace(/\bprops\.(\w+)/g, (_, prop) => `$${prop}`);
|
|
736
|
-
result = result.replace(/(?<!\$)\b([a-z_]\w*)\.(\w+)/g, (match, obj, field) => {
|
|
737
|
-
if (match.startsWith("$"))
|
|
738
|
-
return match;
|
|
739
|
-
return `$${obj}->{${field}}`;
|
|
740
|
-
});
|
|
741
|
-
result = result.replace(/\$(\w+)\.(\w+)/g, (_, obj, field) => `$${obj}->{${field}}`);
|
|
742
|
-
result = result.replace(/\}->\{(\w+)\}\.(\w+)/g, (_, f1, f2) => `}->{${f1}}->{${f2}}`);
|
|
743
|
-
result = result.replace(/\$(\w+)->\{length\}/g, (_, arr) => `scalar(@{$${arr}})`);
|
|
744
|
-
result = result.replace(/\?\?/g, "//");
|
|
745
|
-
result = result.replace(/\s*===\s*(['"])/g, " eq $1");
|
|
746
|
-
result = result.replace(/\s*!==\s*(['"])/g, " ne $1");
|
|
747
|
-
result = result.replace(/(['"])\s*===\s*/g, "$1 eq ");
|
|
748
|
-
result = result.replace(/(['"])\s*!==\s*/g, "$1 ne ");
|
|
749
|
-
result = result.replace(/===/g, "==");
|
|
750
|
-
result = result.replace(/!==/g, "!=");
|
|
751
|
-
result = result.replace(/`([^`]*)`/g, (_, content) => {
|
|
752
|
-
const perlStr = content.replace(/\$\{([^}]+)\}/g, (_2, e) => `${this.convertExpressionToPerl(e)}`);
|
|
753
|
-
return `"${perlStr}"`;
|
|
754
|
-
});
|
|
755
|
-
if (/^[a-z_]\w*$/i.test(result) && !result.startsWith("$")) {
|
|
756
|
-
result = `$${result}`;
|
|
757
|
-
}
|
|
758
|
-
return result;
|
|
740
|
+
return this.renderParsedExprToPerl(parsed);
|
|
759
741
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
return expr;
|
|
763
|
-
const parsed = parseExpression2(expr);
|
|
764
|
-
if (parsed.kind === "unsupported")
|
|
765
|
-
return expr;
|
|
766
|
-
let mutated = false;
|
|
767
|
-
const walk = (n) => {
|
|
768
|
-
if (n.kind === "call") {
|
|
769
|
-
const path = identifierPath(n.callee);
|
|
770
|
-
const spec = path ? MOJO_TEMPLATE_PRIMITIVES[path] : undefined;
|
|
771
|
-
if (path && spec) {
|
|
772
|
-
if (n.args.length !== spec.arity) {
|
|
773
|
-
this.errors.push({
|
|
774
|
-
code: "BF101",
|
|
775
|
-
severity: "error",
|
|
776
|
-
message: `templatePrimitive '${path}' expects ${spec.arity} arg(s), got ${n.args.length}`,
|
|
777
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
778
|
-
suggestion: {
|
|
779
|
-
message: `Call '${path}' with exactly ${spec.arity} argument(s), or wrap the JSX expression in /* @client */ to defer evaluation.`
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
return { kind: "call", callee: walk(n.callee), args: n.args.map(walk) };
|
|
783
|
-
}
|
|
784
|
-
const renderedArgs = n.args.map((a) => this.convertExpressionToPerl(stringifyParsedExpr(walk(a))));
|
|
785
|
-
mutated = true;
|
|
786
|
-
return { kind: "identifier", name: spec.emit(renderedArgs) };
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
switch (n.kind) {
|
|
790
|
-
case "call":
|
|
791
|
-
return { kind: "call", callee: walk(n.callee), args: n.args.map(walk) };
|
|
792
|
-
case "member":
|
|
793
|
-
return { kind: "member", object: walk(n.object), property: n.property, computed: n.computed };
|
|
794
|
-
case "binary":
|
|
795
|
-
return { kind: "binary", op: n.op, left: walk(n.left), right: walk(n.right) };
|
|
796
|
-
case "unary":
|
|
797
|
-
return { kind: "unary", op: n.op, argument: walk(n.argument) };
|
|
798
|
-
case "logical":
|
|
799
|
-
return { kind: "logical", op: n.op, left: walk(n.left), right: walk(n.right) };
|
|
800
|
-
case "conditional":
|
|
801
|
-
return { kind: "conditional", test: walk(n.test), consequent: walk(n.consequent), alternate: walk(n.alternate) };
|
|
802
|
-
default:
|
|
803
|
-
return n;
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
const transformed = walk(parsed);
|
|
807
|
-
if (!mutated)
|
|
808
|
-
return expr;
|
|
809
|
-
return stringifyParsedExpr(transformed);
|
|
742
|
+
renderParsedExprToPerl(expr) {
|
|
743
|
+
return emitParsedExpr(expr, new MojoTopLevelEmitter(this));
|
|
810
744
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return "''";
|
|
823
|
-
}
|
|
824
|
-
this.higherOrderInFlight.add(expr);
|
|
825
|
-
try {
|
|
826
|
-
const parsed = parseExpression2(expr);
|
|
827
|
-
const support = isSupported(parsed);
|
|
828
|
-
if (!support.supported) {
|
|
829
|
-
this.errors.push({
|
|
830
|
-
code: "BF101",
|
|
831
|
-
severity: "error",
|
|
832
|
-
message: `Cannot lower higher-order chain to Embedded Perl: ${expr.trim()}`,
|
|
833
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
834
|
-
suggestion: {
|
|
835
|
-
message: support.reason ? `${support.reason}
|
|
745
|
+
_isStringValueName(name) {
|
|
746
|
+
return this.stringValueNames.has(name);
|
|
747
|
+
}
|
|
748
|
+
_recordExprBF101(message, reason) {
|
|
749
|
+
this.errors.push({
|
|
750
|
+
code: "BF101",
|
|
751
|
+
severity: "error",
|
|
752
|
+
message,
|
|
753
|
+
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
754
|
+
suggestion: {
|
|
755
|
+
message: reason ? `${reason}
|
|
836
756
|
|
|
837
757
|
Options:
|
|
838
758
|
1. Use /* @client */ for client-side evaluation
|
|
839
759
|
2. Pre-compute the value in Perl` : `Options:
|
|
840
760
|
1. Use /* @client */ for client-side evaluation
|
|
841
761
|
2. Pre-compute the value in Perl`
|
|
842
|
-
}
|
|
843
|
-
});
|
|
844
|
-
return "''";
|
|
845
762
|
}
|
|
846
|
-
|
|
847
|
-
} finally {
|
|
848
|
-
this.higherOrderInFlight.delete(expr);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
renderParsedExprToPerl(expr) {
|
|
852
|
-
return emitParsedExpr(expr, new MojoTopLevelEmitter(this));
|
|
853
|
-
}
|
|
854
|
-
_convertExpressionToPerlPublic(raw) {
|
|
855
|
-
return this.convertExpressionToPerl(raw);
|
|
763
|
+
});
|
|
856
764
|
}
|
|
857
765
|
_renderPerlFilterExprPublic(expr, param) {
|
|
858
766
|
return this.renderPerlFilterExpr(expr, param);
|
|
@@ -926,13 +834,35 @@ function renderSortMethod(recv, c) {
|
|
|
926
834
|
});
|
|
927
835
|
return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
|
|
928
836
|
}
|
|
837
|
+
function isStringTypeInfo(type) {
|
|
838
|
+
return type?.kind === "primitive" && type.primitive === "string";
|
|
839
|
+
}
|
|
840
|
+
function isBareStringLiteral(initialValue) {
|
|
841
|
+
if (!initialValue)
|
|
842
|
+
return false;
|
|
843
|
+
const v = initialValue.trim();
|
|
844
|
+
return v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"');
|
|
845
|
+
}
|
|
846
|
+
function isStringTypedOperand(expr, isStringName) {
|
|
847
|
+
if (expr.kind === "literal" && expr.literalType === "string")
|
|
848
|
+
return true;
|
|
849
|
+
if (expr.kind === "call" && expr.callee.kind === "identifier" && expr.args.length === 0) {
|
|
850
|
+
return isStringName(expr.callee.name);
|
|
851
|
+
}
|
|
852
|
+
if (expr.kind === "member" && expr.object.kind === "identifier" && expr.object.name === "props") {
|
|
853
|
+
return isStringName(expr.property);
|
|
854
|
+
}
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
929
857
|
|
|
930
858
|
class MojoFilterEmitter {
|
|
931
859
|
param;
|
|
932
860
|
localVarMap;
|
|
933
|
-
|
|
861
|
+
isStringName;
|
|
862
|
+
constructor(param, localVarMap, isStringName = () => false) {
|
|
934
863
|
this.param = param;
|
|
935
864
|
this.localVarMap = localVarMap;
|
|
865
|
+
this.isStringName = isStringName;
|
|
936
866
|
}
|
|
937
867
|
identifier(name) {
|
|
938
868
|
if (name === this.param)
|
|
@@ -976,10 +906,12 @@ class MojoFilterEmitter {
|
|
|
976
906
|
binary(op, left, right, emit) {
|
|
977
907
|
const l = emit(left);
|
|
978
908
|
const r = emit(right);
|
|
979
|
-
|
|
909
|
+
const isStr = (e) => isStringTypedOperand(e, this.isStringName);
|
|
910
|
+
const stringCmp = isStr(left) || isStr(right);
|
|
911
|
+
if ((op === "===" || op === "==") && stringCmp) {
|
|
980
912
|
return `${l} eq ${r}`;
|
|
981
913
|
}
|
|
982
|
-
if ((op === "!==" || op === "!=") &&
|
|
914
|
+
if ((op === "!==" || op === "!=") && stringCmp) {
|
|
983
915
|
return `${l} ne ${r}`;
|
|
984
916
|
}
|
|
985
917
|
const opMap = {
|
|
@@ -1007,7 +939,7 @@ class MojoFilterEmitter {
|
|
|
1007
939
|
}
|
|
1008
940
|
higherOrder(method, object, param, predicate, emit) {
|
|
1009
941
|
const arrayExpr = emit(object);
|
|
1010
|
-
const predBody = emitParsedExpr(predicate, new MojoFilterEmitter(param, this.localVarMap));
|
|
942
|
+
const predBody = emitParsedExpr(predicate, new MojoFilterEmitter(param, this.localVarMap, this.isStringName));
|
|
1011
943
|
const grepBody = predBody.replace(new RegExp(`\\$${param}\\b`, "g"), "$_");
|
|
1012
944
|
if (method === "filter")
|
|
1013
945
|
return `[grep { ${grepBody} } @{${arrayExpr}}]`;
|
|
@@ -1058,6 +990,9 @@ class MojoTopLevelEmitter {
|
|
|
1058
990
|
return String(value);
|
|
1059
991
|
}
|
|
1060
992
|
member(object, property, _computed, emit) {
|
|
993
|
+
if (object.kind === "identifier" && object.name === "props") {
|
|
994
|
+
return `$${property}`;
|
|
995
|
+
}
|
|
1061
996
|
const obj = emit(object);
|
|
1062
997
|
if (property === "length")
|
|
1063
998
|
return `scalar(@{${obj}})`;
|
|
@@ -1067,6 +1002,15 @@ class MojoTopLevelEmitter {
|
|
|
1067
1002
|
if (callee.kind === "identifier" && args.length === 0) {
|
|
1068
1003
|
return `$${callee.name}`;
|
|
1069
1004
|
}
|
|
1005
|
+
const path = identifierPath(callee);
|
|
1006
|
+
const spec = path ? MOJO_TEMPLATE_PRIMITIVES[path] : undefined;
|
|
1007
|
+
if (path && spec) {
|
|
1008
|
+
if (args.length === spec.arity) {
|
|
1009
|
+
return spec.emit(args.map(emit));
|
|
1010
|
+
}
|
|
1011
|
+
this.adapter._recordExprBF101(`templatePrimitive '${path}' expects ${spec.arity} arg(s), got ${args.length}`, `Call '${path}' with exactly ${spec.arity} argument(s).`);
|
|
1012
|
+
return "''";
|
|
1013
|
+
}
|
|
1070
1014
|
return emit(callee);
|
|
1071
1015
|
}
|
|
1072
1016
|
unary(op, argument, emit) {
|
|
@@ -1080,10 +1024,12 @@ class MojoTopLevelEmitter {
|
|
|
1080
1024
|
binary(op, left, right, emit) {
|
|
1081
1025
|
const l = emit(left);
|
|
1082
1026
|
const r = emit(right);
|
|
1083
|
-
|
|
1027
|
+
const isStr = (e) => isStringTypedOperand(e, (n) => this.adapter._isStringValueName(n));
|
|
1028
|
+
const stringCmp = isStr(left) || isStr(right);
|
|
1029
|
+
if ((op === "===" || op === "==") && stringCmp) {
|
|
1084
1030
|
return `${l} eq ${r}`;
|
|
1085
1031
|
}
|
|
1086
|
-
if ((op === "!==" || op === "!=") &&
|
|
1032
|
+
if ((op === "!==" || op === "!=") && stringCmp) {
|
|
1087
1033
|
return `${l} ne ${r}`;
|
|
1088
1034
|
}
|
|
1089
1035
|
const opMap = {
|
|
@@ -1109,6 +1055,10 @@ class MojoTopLevelEmitter {
|
|
|
1109
1055
|
return `(${l} // ${r})`;
|
|
1110
1056
|
}
|
|
1111
1057
|
higherOrder(method, object, param, predicate, emit) {
|
|
1058
|
+
if (method === "find" || method === "findIndex" || method === "findLast" || method === "findLastIndex") {
|
|
1059
|
+
this.adapter._recordExprBF101(`Mojo adapter has not lowered Array.prototype.${method} yet`);
|
|
1060
|
+
return "''";
|
|
1061
|
+
}
|
|
1112
1062
|
const arrayExpr = emit(object);
|
|
1113
1063
|
const predBody = this.adapter._renderPerlFilterExprPublic(predicate, param);
|
|
1114
1064
|
const grepBody = predBody.replace(new RegExp(`\\$${param}\\b`, "g"), "$_");
|
|
@@ -1132,14 +1082,28 @@ class MojoTopLevelEmitter {
|
|
|
1132
1082
|
conditional(test, consequent, alternate, emit) {
|
|
1133
1083
|
return `(${emit(test)} ? ${emit(consequent)} : ${emit(alternate)})`;
|
|
1134
1084
|
}
|
|
1135
|
-
templateLiteral(
|
|
1136
|
-
|
|
1085
|
+
templateLiteral(parts, emit) {
|
|
1086
|
+
const terms = [];
|
|
1087
|
+
for (const part of parts) {
|
|
1088
|
+
if (part.type === "string") {
|
|
1089
|
+
if (part.value !== "") {
|
|
1090
|
+
terms.push(`"${part.value.replace(/[\\"$@]/g, (m) => `\\${m}`)}"`);
|
|
1091
|
+
}
|
|
1092
|
+
} else {
|
|
1093
|
+
const rendered = emit(part.expr);
|
|
1094
|
+
const needsParens = part.expr.kind === "binary" || part.expr.kind === "logical" || part.expr.kind === "conditional";
|
|
1095
|
+
terms.push(needsParens ? `(${rendered})` : rendered);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if (terms.length === 0)
|
|
1099
|
+
return '""';
|
|
1100
|
+
return terms.join(" . ");
|
|
1137
1101
|
}
|
|
1138
1102
|
arrowFn(_param, _body) {
|
|
1139
|
-
return "";
|
|
1103
|
+
return "''";
|
|
1140
1104
|
}
|
|
1141
|
-
unsupported(
|
|
1142
|
-
return
|
|
1105
|
+
unsupported(_raw, _reason) {
|
|
1106
|
+
return "''";
|
|
1143
1107
|
}
|
|
1144
1108
|
}
|
|
1145
1109
|
var mojoAdapter = new MojoAdapter;
|
package/dist/test-render.d.ts
CHANGED
|
@@ -35,4 +35,9 @@ export interface RenderOptions {
|
|
|
35
35
|
components?: Record<string, string>;
|
|
36
36
|
}
|
|
37
37
|
export declare function renderMojoComponent(options: RenderOptions): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Evaluate a signal initializer expression using provided props.
|
|
40
|
+
* Handles patterns like: props.initial ?? 0, props.value, literal values.
|
|
41
|
+
*/
|
|
42
|
+
export declare function evaluateSignalInit(expr: string, props?: Record<string, unknown>): unknown;
|
|
38
43
|
//# sourceMappingURL=test-render.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKxE;AAkCD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,OAAO,iBAAiB,EAAE,eAAe,CAAA;IAClD,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA2MjF"}
|
|
1
|
+
{"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKxE;AAkCD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,OAAO,iBAAiB,EAAE,eAAe,CAAA;IAClD,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA2MjF;AAgND;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAuBT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/mojolicious",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Mojolicious EP template adapter for BarefootJS - generates .html.ep files from IR",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
"directory": "packages/adapter-mojolicious"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@barefootjs/shared": "0.5.
|
|
55
|
+
"@barefootjs/shared": "0.5.2"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@barefootjs/jsx": ">=0.2.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
62
|
-
"@barefootjs/jsx": "0.5.
|
|
62
|
+
"@barefootjs/jsx": "0.5.2",
|
|
63
63
|
"typescript": "^5.0.0"
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import { evaluateSignalInit } from '../test-render'
|
|
3
|
+
|
|
4
|
+
describe('evaluateSignalInit — SSR signal seeding (#1672)', () => {
|
|
5
|
+
test('parses an inline object-array initial value', () => {
|
|
6
|
+
// The whole-item loop-conditional fixture seeds `items` from an inline
|
|
7
|
+
// object array. Without array support this returned null, so `$items` was
|
|
8
|
+
// undefined in the Mojo SSR render and the loop rendered empty.
|
|
9
|
+
expect(evaluateSignalInit(`[{ id: 'a' }, { id: 'b' }, { id: 'c' }]`)).toEqual([
|
|
10
|
+
{ id: 'a' },
|
|
11
|
+
{ id: 'b' },
|
|
12
|
+
{ id: 'c' },
|
|
13
|
+
])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('parses scalar and mixed arrays, including nested objects', () => {
|
|
17
|
+
expect(evaluateSignalInit(`['x', 'y']`)).toEqual(['x', 'y'])
|
|
18
|
+
expect(evaluateSignalInit(`[1, 2, 3]`)).toEqual([1, 2, 3])
|
|
19
|
+
expect(evaluateSignalInit(`[{ id: 'a', n: 1, ok: true }]`)).toEqual([
|
|
20
|
+
{ id: 'a', n: 1, ok: true },
|
|
21
|
+
])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('still parses scalars, empty array, and props passthrough', () => {
|
|
25
|
+
expect(evaluateSignalInit(`'b'`)).toBe('b')
|
|
26
|
+
expect(evaluateSignalInit(`5`)).toBe(5)
|
|
27
|
+
expect(evaluateSignalInit(`[]`)).toEqual([])
|
|
28
|
+
expect(evaluateSignalInit(`props.value`, { value: 42 })).toBe(42)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('bails to null for arrays with non-literal elements', () => {
|
|
32
|
+
// A call / identifier element can't be evaluated at seed time.
|
|
33
|
+
expect(evaluateSignalInit(`[foo(), bar]`)).toBeNull()
|
|
34
|
+
})
|
|
35
|
+
})
|