@barefootjs/mojolicious 0.5.1 → 0.5.3
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 +106 -144
- package/dist/adapter/mojo-adapter.d.ts +16 -46
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/build.js +106 -144
- package/dist/index.js +106 -144
- 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 +189 -72
- package/src/adapter/mojo-adapter.ts +202 -293
- 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);
|
|
@@ -574,7 +581,7 @@ ${children}`;
|
|
|
574
581
|
return `${BF_COND}="${condId}"`;
|
|
575
582
|
}
|
|
576
583
|
renderPerlFilterExpr(expr, param, localVarMap = new Map) {
|
|
577
|
-
return emitParsedExpr(expr, new MojoFilterEmitter(param, localVarMap));
|
|
584
|
+
return emitParsedExpr(expr, new MojoFilterEmitter(param, localVarMap, (n) => this._isStringValueName(n)));
|
|
578
585
|
}
|
|
579
586
|
renderBlockBodyCondition(statements, param) {
|
|
580
587
|
const localVarMap = new Map;
|
|
@@ -707,154 +714,53 @@ ${reason}` : "";
|
|
|
707
714
|
return true;
|
|
708
715
|
}
|
|
709
716
|
convertExpressionToPerl(expr) {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
if (/\.\s*join\s*\(/.test(expr)) {
|
|
717
|
-
return this.convertHigherOrderExpr(expr);
|
|
718
|
-
}
|
|
719
|
-
const mojoOnlyMatch = /\.\s*(?<method>find|findIndex|findLast|findLastIndex)\s*\(/.exec(expr);
|
|
720
|
-
if (mojoOnlyMatch) {
|
|
721
|
-
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) {
|
|
722
723
|
this.errors.push({
|
|
723
724
|
code: "BF101",
|
|
724
725
|
severity: "error",
|
|
725
|
-
message: `
|
|
726
|
+
message: `Expression not supported: ${trimmed}`,
|
|
726
727
|
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
727
728
|
suggestion: {
|
|
728
|
-
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:
|
|
729
734
|
1. Use /* @client */ for client-side evaluation
|
|
730
735
|
2. Pre-compute the value in Perl`
|
|
731
736
|
}
|
|
732
737
|
});
|
|
733
738
|
return "''";
|
|
734
739
|
}
|
|
735
|
-
|
|
736
|
-
let result = expr.replace(/\b([a-z_]\w*)\(\)/g, (_, name) => `$${name}`);
|
|
737
|
-
result = result.replace(/\bprops\.(\w+)/g, (_, prop) => `$${prop}`);
|
|
738
|
-
result = result.replace(/(?<!\$)\b([a-z_]\w*)\.(\w+)/g, (match, obj, field) => {
|
|
739
|
-
if (match.startsWith("$"))
|
|
740
|
-
return match;
|
|
741
|
-
return `$${obj}->{${field}}`;
|
|
742
|
-
});
|
|
743
|
-
result = result.replace(/\$(\w+)\.(\w+)/g, (_, obj, field) => `$${obj}->{${field}}`);
|
|
744
|
-
result = result.replace(/\}->\{(\w+)\}\.(\w+)/g, (_, f1, f2) => `}->{${f1}}->{${f2}}`);
|
|
745
|
-
result = result.replace(/\$(\w+)->\{length\}/g, (_, arr) => `scalar(@{$${arr}})`);
|
|
746
|
-
result = result.replace(/\?\?/g, "//");
|
|
747
|
-
result = result.replace(/\s*===\s*(['"])/g, " eq $1");
|
|
748
|
-
result = result.replace(/\s*!==\s*(['"])/g, " ne $1");
|
|
749
|
-
result = result.replace(/(['"])\s*===\s*/g, "$1 eq ");
|
|
750
|
-
result = result.replace(/(['"])\s*!==\s*/g, "$1 ne ");
|
|
751
|
-
result = result.replace(/===/g, "==");
|
|
752
|
-
result = result.replace(/!==/g, "!=");
|
|
753
|
-
result = result.replace(/`([^`]*)`/g, (_, content) => {
|
|
754
|
-
const perlStr = content.replace(/\$\{([^}]+)\}/g, (_2, e) => `${this.convertExpressionToPerl(e)}`);
|
|
755
|
-
return `"${perlStr}"`;
|
|
756
|
-
});
|
|
757
|
-
if (/^[a-z_]\w*$/i.test(result) && !result.startsWith("$")) {
|
|
758
|
-
result = `$${result}`;
|
|
759
|
-
}
|
|
760
|
-
return result;
|
|
740
|
+
return this.renderParsedExprToPerl(parsed);
|
|
761
741
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
return expr;
|
|
765
|
-
const parsed = parseExpression2(expr);
|
|
766
|
-
if (parsed.kind === "unsupported")
|
|
767
|
-
return expr;
|
|
768
|
-
let mutated = false;
|
|
769
|
-
const walk = (n) => {
|
|
770
|
-
if (n.kind === "call") {
|
|
771
|
-
const path = identifierPath(n.callee);
|
|
772
|
-
const spec = path ? MOJO_TEMPLATE_PRIMITIVES[path] : undefined;
|
|
773
|
-
if (path && spec) {
|
|
774
|
-
if (n.args.length !== spec.arity) {
|
|
775
|
-
this.errors.push({
|
|
776
|
-
code: "BF101",
|
|
777
|
-
severity: "error",
|
|
778
|
-
message: `templatePrimitive '${path}' expects ${spec.arity} arg(s), got ${n.args.length}`,
|
|
779
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
780
|
-
suggestion: {
|
|
781
|
-
message: `Call '${path}' with exactly ${spec.arity} argument(s), or wrap the JSX expression in /* @client */ to defer evaluation.`
|
|
782
|
-
}
|
|
783
|
-
});
|
|
784
|
-
return { kind: "call", callee: walk(n.callee), args: n.args.map(walk) };
|
|
785
|
-
}
|
|
786
|
-
const renderedArgs = n.args.map((a) => this.convertExpressionToPerl(stringifyParsedExpr(walk(a))));
|
|
787
|
-
mutated = true;
|
|
788
|
-
return { kind: "identifier", name: spec.emit(renderedArgs) };
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
switch (n.kind) {
|
|
792
|
-
case "call":
|
|
793
|
-
return { kind: "call", callee: walk(n.callee), args: n.args.map(walk) };
|
|
794
|
-
case "member":
|
|
795
|
-
return { kind: "member", object: walk(n.object), property: n.property, computed: n.computed };
|
|
796
|
-
case "binary":
|
|
797
|
-
return { kind: "binary", op: n.op, left: walk(n.left), right: walk(n.right) };
|
|
798
|
-
case "unary":
|
|
799
|
-
return { kind: "unary", op: n.op, argument: walk(n.argument) };
|
|
800
|
-
case "logical":
|
|
801
|
-
return { kind: "logical", op: n.op, left: walk(n.left), right: walk(n.right) };
|
|
802
|
-
case "conditional":
|
|
803
|
-
return { kind: "conditional", test: walk(n.test), consequent: walk(n.consequent), alternate: walk(n.alternate) };
|
|
804
|
-
default:
|
|
805
|
-
return n;
|
|
806
|
-
}
|
|
807
|
-
};
|
|
808
|
-
const transformed = walk(parsed);
|
|
809
|
-
if (!mutated)
|
|
810
|
-
return expr;
|
|
811
|
-
return stringifyParsedExpr(transformed);
|
|
742
|
+
renderParsedExprToPerl(expr) {
|
|
743
|
+
return emitParsedExpr(expr, new MojoTopLevelEmitter(this));
|
|
812
744
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
return "''";
|
|
825
|
-
}
|
|
826
|
-
this.higherOrderInFlight.add(expr);
|
|
827
|
-
try {
|
|
828
|
-
const parsed = parseExpression2(expr);
|
|
829
|
-
const support = isSupported(parsed);
|
|
830
|
-
if (!support.supported) {
|
|
831
|
-
this.errors.push({
|
|
832
|
-
code: "BF101",
|
|
833
|
-
severity: "error",
|
|
834
|
-
message: `Cannot lower higher-order chain to Embedded Perl: ${expr.trim()}`,
|
|
835
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
836
|
-
suggestion: {
|
|
837
|
-
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}
|
|
838
756
|
|
|
839
757
|
Options:
|
|
840
758
|
1. Use /* @client */ for client-side evaluation
|
|
841
759
|
2. Pre-compute the value in Perl` : `Options:
|
|
842
760
|
1. Use /* @client */ for client-side evaluation
|
|
843
761
|
2. Pre-compute the value in Perl`
|
|
844
|
-
}
|
|
845
|
-
});
|
|
846
|
-
return "''";
|
|
847
762
|
}
|
|
848
|
-
|
|
849
|
-
} finally {
|
|
850
|
-
this.higherOrderInFlight.delete(expr);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
renderParsedExprToPerl(expr) {
|
|
854
|
-
return emitParsedExpr(expr, new MojoTopLevelEmitter(this));
|
|
855
|
-
}
|
|
856
|
-
_convertExpressionToPerlPublic(raw) {
|
|
857
|
-
return this.convertExpressionToPerl(raw);
|
|
763
|
+
});
|
|
858
764
|
}
|
|
859
765
|
_renderPerlFilterExprPublic(expr, param) {
|
|
860
766
|
return this.renderPerlFilterExpr(expr, param);
|
|
@@ -928,13 +834,35 @@ function renderSortMethod(recv, c) {
|
|
|
928
834
|
});
|
|
929
835
|
return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
|
|
930
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
|
+
}
|
|
931
857
|
|
|
932
858
|
class MojoFilterEmitter {
|
|
933
859
|
param;
|
|
934
860
|
localVarMap;
|
|
935
|
-
|
|
861
|
+
isStringName;
|
|
862
|
+
constructor(param, localVarMap, isStringName = () => false) {
|
|
936
863
|
this.param = param;
|
|
937
864
|
this.localVarMap = localVarMap;
|
|
865
|
+
this.isStringName = isStringName;
|
|
938
866
|
}
|
|
939
867
|
identifier(name) {
|
|
940
868
|
if (name === this.param)
|
|
@@ -978,10 +906,12 @@ class MojoFilterEmitter {
|
|
|
978
906
|
binary(op, left, right, emit) {
|
|
979
907
|
const l = emit(left);
|
|
980
908
|
const r = emit(right);
|
|
981
|
-
|
|
909
|
+
const isStr = (e) => isStringTypedOperand(e, this.isStringName);
|
|
910
|
+
const stringCmp = isStr(left) || isStr(right);
|
|
911
|
+
if ((op === "===" || op === "==") && stringCmp) {
|
|
982
912
|
return `${l} eq ${r}`;
|
|
983
913
|
}
|
|
984
|
-
if ((op === "!==" || op === "!=") &&
|
|
914
|
+
if ((op === "!==" || op === "!=") && stringCmp) {
|
|
985
915
|
return `${l} ne ${r}`;
|
|
986
916
|
}
|
|
987
917
|
const opMap = {
|
|
@@ -1009,7 +939,7 @@ class MojoFilterEmitter {
|
|
|
1009
939
|
}
|
|
1010
940
|
higherOrder(method, object, param, predicate, emit) {
|
|
1011
941
|
const arrayExpr = emit(object);
|
|
1012
|
-
const predBody = emitParsedExpr(predicate, new MojoFilterEmitter(param, this.localVarMap));
|
|
942
|
+
const predBody = emitParsedExpr(predicate, new MojoFilterEmitter(param, this.localVarMap, this.isStringName));
|
|
1013
943
|
const grepBody = predBody.replace(new RegExp(`\\$${param}\\b`, "g"), "$_");
|
|
1014
944
|
if (method === "filter")
|
|
1015
945
|
return `[grep { ${grepBody} } @{${arrayExpr}}]`;
|
|
@@ -1060,6 +990,9 @@ class MojoTopLevelEmitter {
|
|
|
1060
990
|
return String(value);
|
|
1061
991
|
}
|
|
1062
992
|
member(object, property, _computed, emit) {
|
|
993
|
+
if (object.kind === "identifier" && object.name === "props") {
|
|
994
|
+
return `$${property}`;
|
|
995
|
+
}
|
|
1063
996
|
const obj = emit(object);
|
|
1064
997
|
if (property === "length")
|
|
1065
998
|
return `scalar(@{${obj}})`;
|
|
@@ -1069,6 +1002,15 @@ class MojoTopLevelEmitter {
|
|
|
1069
1002
|
if (callee.kind === "identifier" && args.length === 0) {
|
|
1070
1003
|
return `$${callee.name}`;
|
|
1071
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
|
+
}
|
|
1072
1014
|
return emit(callee);
|
|
1073
1015
|
}
|
|
1074
1016
|
unary(op, argument, emit) {
|
|
@@ -1082,10 +1024,12 @@ class MojoTopLevelEmitter {
|
|
|
1082
1024
|
binary(op, left, right, emit) {
|
|
1083
1025
|
const l = emit(left);
|
|
1084
1026
|
const r = emit(right);
|
|
1085
|
-
|
|
1027
|
+
const isStr = (e) => isStringTypedOperand(e, (n) => this.adapter._isStringValueName(n));
|
|
1028
|
+
const stringCmp = isStr(left) || isStr(right);
|
|
1029
|
+
if ((op === "===" || op === "==") && stringCmp) {
|
|
1086
1030
|
return `${l} eq ${r}`;
|
|
1087
1031
|
}
|
|
1088
|
-
if ((op === "!==" || op === "!=") &&
|
|
1032
|
+
if ((op === "!==" || op === "!=") && stringCmp) {
|
|
1089
1033
|
return `${l} ne ${r}`;
|
|
1090
1034
|
}
|
|
1091
1035
|
const opMap = {
|
|
@@ -1111,6 +1055,10 @@ class MojoTopLevelEmitter {
|
|
|
1111
1055
|
return `(${l} // ${r})`;
|
|
1112
1056
|
}
|
|
1113
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
|
+
}
|
|
1114
1062
|
const arrayExpr = emit(object);
|
|
1115
1063
|
const predBody = this.adapter._renderPerlFilterExprPublic(predicate, param);
|
|
1116
1064
|
const grepBody = predBody.replace(new RegExp(`\\$${param}\\b`, "g"), "$_");
|
|
@@ -1134,14 +1082,28 @@ class MojoTopLevelEmitter {
|
|
|
1134
1082
|
conditional(test, consequent, alternate, emit) {
|
|
1135
1083
|
return `(${emit(test)} ? ${emit(consequent)} : ${emit(alternate)})`;
|
|
1136
1084
|
}
|
|
1137
|
-
templateLiteral(
|
|
1138
|
-
|
|
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(" . ");
|
|
1139
1101
|
}
|
|
1140
1102
|
arrowFn(_param, _body) {
|
|
1141
|
-
return "";
|
|
1103
|
+
return "''";
|
|
1142
1104
|
}
|
|
1143
|
-
unsupported(
|
|
1144
|
-
return
|
|
1105
|
+
unsupported(_raw, _reason) {
|
|
1106
|
+
return "''";
|
|
1145
1107
|
}
|
|
1146
1108
|
}
|
|
1147
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.3",
|
|
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.3"
|
|
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.3",
|
|
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
|
+
})
|