@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/adapter/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;
|
|
@@ -42,21 +42,6 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
42
42
|
private options;
|
|
43
43
|
private errors;
|
|
44
44
|
private inLoop;
|
|
45
|
-
/**
|
|
46
|
-
* Re-entry guard for `convertHigherOrderExpr` (#1421).
|
|
47
|
-
*
|
|
48
|
-
* `MojoTopLevelEmitter.unsupported` falls back to the regex pipeline
|
|
49
|
-
* via `_convertExpressionToPerlPublic`, which re-detects the
|
|
50
|
-
* `.filter|every|some` short-circuit and re-enters
|
|
51
|
-
* `convertHigherOrderExpr` with the same raw text. When the parser
|
|
52
|
-
* carries the full original expression down to every nested
|
|
53
|
-
* `unsupported` node (e.g. an array-literal callee that the AST
|
|
54
|
-
* can't classify), the cycle has no terminator and the JS stack
|
|
55
|
-
* blows. The guard records the expression on entry, emits BF101 on
|
|
56
|
-
* second visit, and bails out — so the user sees an actionable
|
|
57
|
-
* diagnostic instead of `RangeError: Maximum call stack size`.
|
|
58
|
-
*/
|
|
59
|
-
private higherOrderInFlight;
|
|
60
45
|
/**
|
|
61
46
|
* SolidJS-style props identifier (`function(props: P)`) and the
|
|
62
47
|
* analyzer-extracted prop names. Stashed at `generate()` entry so
|
|
@@ -66,6 +51,13 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
66
51
|
*/
|
|
67
52
|
private propsObjectName;
|
|
68
53
|
private propsParams;
|
|
54
|
+
/**
|
|
55
|
+
* Names (signal getters + props) whose value is a string, so `===`/`!==`
|
|
56
|
+
* against them lowers to Perl `eq`/`ne` rather than numeric `==`/`!=`.
|
|
57
|
+
* Perl's numeric `==` coerces non-numeric strings to 0, making `"b" == "a"`
|
|
58
|
+
* true — selecting the string operator from the operand's type avoids that.
|
|
59
|
+
*/
|
|
60
|
+
private stringValueNames;
|
|
69
61
|
constructor(options?: MojoAdapterOptions);
|
|
70
62
|
generate(ir: ComponentIR, options?: AdapterGenerateOptions): AdapterOutput;
|
|
71
63
|
private generateScriptRegistrations;
|
|
@@ -174,35 +166,6 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
174
166
|
*/
|
|
175
167
|
private refuseUnsupportedAttrExpression;
|
|
176
168
|
private convertExpressionToPerl;
|
|
177
|
-
/**
|
|
178
|
-
* Walk the parsed AST of `expr` and substitute each registered
|
|
179
|
-
* primitive call (e.g. `JSON.stringify(props.config)`) with its
|
|
180
|
-
* Mojo helper-call equivalent (e.g. `bf->json($config)`). All
|
|
181
|
-
* other shapes round-trip back to source text via
|
|
182
|
-
* `stringifyParsedExpr`, so the result is still a JS-shaped
|
|
183
|
-
* string that the existing regex pipeline in
|
|
184
|
-
* `convertExpressionToPerl` can finish translating.
|
|
185
|
-
*
|
|
186
|
-
* Bails out (returns the input unchanged) when:
|
|
187
|
-
* - the expression doesn't parse cleanly,
|
|
188
|
-
* - no primitive call is found in the AST, or
|
|
189
|
-
* - a primitive's arity doesn't match the registered shape
|
|
190
|
-
* (BF101 is recorded so the user sees the diagnostic).
|
|
191
|
-
*
|
|
192
|
-
* Identifier-path-only matching (#1187 R1) — same constraint the
|
|
193
|
-
* Go adapter applies in #1188.
|
|
194
|
-
*/
|
|
195
|
-
private rewriteTemplatePrimitives;
|
|
196
|
-
/**
|
|
197
|
-
* Convert expressions containing higher-order array methods to Perl.
|
|
198
|
-
* Parses the full expression as AST and renders recursively.
|
|
199
|
-
*
|
|
200
|
-
* Handles patterns like:
|
|
201
|
-
* - todos().filter(t => !t.done).length → scalar(grep { !$_->{done} } @{$todos})
|
|
202
|
-
* - todos().every(t => t.done) → !(grep { !$_->{done} } @{$todos})
|
|
203
|
-
* - todos().filter(t => t.done).length > 0 → scalar(grep { $_->{done} } @{$todos}) > 0
|
|
204
|
-
*/
|
|
205
|
-
private convertHigherOrderExpr;
|
|
206
169
|
/**
|
|
207
170
|
* Render a full ParsedExpr tree to Perl for top-level (non-filter)
|
|
208
171
|
* expressions where identifiers are signals / stash vars. Delegates
|
|
@@ -210,8 +173,15 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
210
173
|
* (#1250 phase 1B).
|
|
211
174
|
*/
|
|
212
175
|
private renderParsedExprToPerl;
|
|
213
|
-
/**
|
|
214
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Hook for the ParsedExpr emitters to record a BF101 while walking
|
|
178
|
+
* the AST — used for Mojo-specific gaps (`.find` / `.findIndex` have
|
|
179
|
+
* no Embedded-Perl lowering) and templatePrimitive arity errors.
|
|
180
|
+
*/
|
|
181
|
+
/** Whether `name` (a signal getter or prop) holds a string value, so an
|
|
182
|
+
* equality comparison against it should use Perl `eq`/`ne` (#1672). */
|
|
183
|
+
_isStringValueName(name: string): boolean;
|
|
184
|
+
_recordExprBF101(message: string, reason?: string): void;
|
|
215
185
|
/** Internal hook for higher-order: predicate body re-uses the filter emitter. */
|
|
216
186
|
_renderPerlFilterExprPublic(expr: ParsedExpr, param: string): string;
|
|
217
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,
|
|
1
|
+
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
|