@barefootjs/mojolicious 0.18.4 → 0.18.5
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/expr/array-method.d.ts.map +1 -1
- package/dist/adapter/expr/emitters.d.ts +2 -2
- package/dist/adapter/expr/emitters.d.ts.map +1 -1
- package/dist/adapter/index.js +72 -28
- package/dist/adapter/lib/constants.d.ts.map +1 -1
- package/dist/adapter/mojo-adapter.d.ts +8 -0
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/build.js +72 -28
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +74 -42
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/test-render.d.ts.map +1 -1
- package/lib/BarefootJS/Backend/Mojo.pm +1 -1
- package/lib/Mojolicious/Plugin/BarefootJS/DevReload.pm +1 -1
- package/lib/Mojolicious/Plugin/BarefootJS.pm +1 -1
- package/package.json +3 -3
- package/src/__tests__/mojo-adapter.test.ts +21 -1
- package/src/adapter/expr/array-method.ts +19 -0
- package/src/adapter/expr/emitters.ts +18 -3
- package/src/adapter/lib/constants.ts +3 -0
- package/src/adapter/mojo-adapter.ts +71 -11
- package/src/conformance-pins.ts +0 -5
- package/src/render-divergences.ts +1 -22
- package/src/test-render.ts +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-method.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/array-method.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAA;AAExB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,
|
|
1
|
+
{"version":3,"file":"array-method.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/array-method.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAA;AAExB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAgOR;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAOf;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,GAAG,OAAO,EAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAmBf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,EAC/B,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAMf;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAaxE;AAKD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,EACvC,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAoBR"}
|
|
@@ -20,7 +20,7 @@ export declare class MojoFilterEmitter implements ParsedExprEmitter {
|
|
|
20
20
|
constructor(param: string, localVarMap: Map<string, string>, isStringName?: (n: string) => boolean, onUnsupported?: ((message: string, reason?: string) => void) | undefined);
|
|
21
21
|
identifier(name: string): string;
|
|
22
22
|
literal(value: string | number | boolean | null, literalType: LiteralType): string;
|
|
23
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
|
|
23
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string;
|
|
24
24
|
indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
25
25
|
call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
|
|
26
26
|
unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
@@ -57,7 +57,7 @@ export declare class MojoTopLevelEmitter implements ParsedExprEmitter {
|
|
|
57
57
|
constructor(ctx: MojoEmitContext);
|
|
58
58
|
identifier(name: string): string;
|
|
59
59
|
literal(value: string | number | boolean | null, literalType: LiteralType): string;
|
|
60
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
|
|
60
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string;
|
|
61
61
|
indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
62
62
|
call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
|
|
63
63
|
unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,YAAY,EAMlB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AA+CzD,qBAAa,iBAAkB,YAAW,iBAAiB;IAEvD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAI5B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAK7B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAXjC,YACmB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAIhC,YAAY,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAqB,EAKlD,aAAa,CAAC,GAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,aAAA,EACzE;IAEJ,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/B;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,CAKjF;IAED,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -76,7 +76,7 @@ function isAriaBooleanAttr(name) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/adapter/mojo-adapter.ts
|
|
79
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
79
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
80
80
|
|
|
81
81
|
// src/adapter/lib/constants.ts
|
|
82
82
|
var MOJO_TEMPLATE_PRIMITIVES = {
|
|
@@ -85,7 +85,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
|
|
|
85
85
|
Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
|
|
86
86
|
"Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
|
|
87
87
|
"Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
|
|
88
|
-
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
|
|
88
|
+
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
|
|
89
|
+
"Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
|
|
90
|
+
"Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
|
|
91
|
+
"Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
|
|
89
92
|
};
|
|
90
93
|
var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
91
94
|
|
|
@@ -190,6 +193,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
190
193
|
const recv = emit(object);
|
|
191
194
|
return `bf->trim(${recv})`;
|
|
192
195
|
}
|
|
196
|
+
case "trimStart":
|
|
197
|
+
case "trimEnd": {
|
|
198
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
199
|
+
const recv = emit(object);
|
|
200
|
+
return `bf->${fn}(${recv})`;
|
|
201
|
+
}
|
|
193
202
|
case "toFixed": {
|
|
194
203
|
const recv = emit(object);
|
|
195
204
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -223,6 +232,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
223
232
|
const newS = emit(args[1]);
|
|
224
233
|
return `bf->replace(${recv}, ${oldS}, ${newS})`;
|
|
225
234
|
}
|
|
235
|
+
case "replaceAll": {
|
|
236
|
+
const recv = emit(object);
|
|
237
|
+
const oldS = emit(args[0]);
|
|
238
|
+
const newS = emit(args[1]);
|
|
239
|
+
return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
|
|
240
|
+
}
|
|
226
241
|
case "repeat": {
|
|
227
242
|
const recv = emit(object);
|
|
228
243
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -381,7 +396,7 @@ class MojoFilterEmitter {
|
|
|
381
396
|
return "undef";
|
|
382
397
|
return String(value);
|
|
383
398
|
}
|
|
384
|
-
member(object, property, _computed, emit) {
|
|
399
|
+
member(object, property, _computed, _optional, emit) {
|
|
385
400
|
if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
|
|
386
401
|
return `scalar(@{${emit(object)}})`;
|
|
387
402
|
}
|
|
@@ -516,7 +531,7 @@ class MojoTopLevelEmitter {
|
|
|
516
531
|
return "undef";
|
|
517
532
|
return String(value);
|
|
518
533
|
}
|
|
519
|
-
member(object, property, _computed, emit) {
|
|
534
|
+
member(object, property, _computed, _optional, emit) {
|
|
520
535
|
if (object.kind === "identifier" && object.name === "props") {
|
|
521
536
|
return `$${property}`;
|
|
522
537
|
}
|
|
@@ -526,8 +541,13 @@ class MojoTopLevelEmitter {
|
|
|
526
541
|
return staticValue;
|
|
527
542
|
}
|
|
528
543
|
const obj = emit(object);
|
|
529
|
-
if (property === "length")
|
|
544
|
+
if (property === "length") {
|
|
545
|
+
const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
|
|
546
|
+
const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
|
|
547
|
+
if (isStringReceiver)
|
|
548
|
+
return `length(${obj})`;
|
|
530
549
|
return `scalar(@{${obj}})`;
|
|
550
|
+
}
|
|
531
551
|
return `${obj}->{${property}}`;
|
|
532
552
|
}
|
|
533
553
|
indexAccess(object, index, emit) {
|
|
@@ -976,6 +996,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
976
996
|
options;
|
|
977
997
|
errors = [];
|
|
978
998
|
inLoop = false;
|
|
999
|
+
currentLoopKeyDepth = 0;
|
|
979
1000
|
propsObjectName = null;
|
|
980
1001
|
propsParams = [];
|
|
981
1002
|
booleanTypedProps = new Set;
|
|
@@ -1108,7 +1129,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1108
1129
|
return this.renderElement(node);
|
|
1109
1130
|
}
|
|
1110
1131
|
emitText(node) {
|
|
1111
|
-
return node.value;
|
|
1132
|
+
return escapeHtml(node.value);
|
|
1112
1133
|
}
|
|
1113
1134
|
emitExpression(node) {
|
|
1114
1135
|
return this.renderExpression(node);
|
|
@@ -1337,13 +1358,16 @@ ${whenTrue}
|
|
|
1337
1358
|
}
|
|
1338
1359
|
const param = loop.param;
|
|
1339
1360
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
1340
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1361
|
+
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1341
1362
|
for (const n of loopBound) {
|
|
1342
1363
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
1343
1364
|
}
|
|
1344
1365
|
const prevInLoop = this.inLoop;
|
|
1345
1366
|
this.inLoop = true;
|
|
1367
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
1368
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
1346
1369
|
const renderedChildren = this.renderChildren(loop.children);
|
|
1370
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
1347
1371
|
this.inLoop = prevInLoop;
|
|
1348
1372
|
const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
|
|
1349
1373
|
${renderedChildren}` : renderedChildren;
|
|
@@ -1377,23 +1401,31 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1377
1401
|
}
|
|
1378
1402
|
lines.push(`% my $${sortedHoist} = ${sorted};`);
|
|
1379
1403
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1404
|
+
if (loop.objectIteration) {
|
|
1405
|
+
const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
|
|
1406
|
+
lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
|
|
1407
|
+
if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
|
|
1408
|
+
lines.push(`% my $${param} = ${array}->{${keyVar}};`);
|
|
1409
|
+
}
|
|
1410
|
+
} else {
|
|
1411
|
+
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
1412
|
+
if (loop.iterationShape !== "keys") {
|
|
1413
|
+
if (supportableDestructure) {
|
|
1414
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
1415
|
+
for (const b of loop.paramBindings ?? []) {
|
|
1416
|
+
const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
|
|
1417
|
+
if (b.rest?.kind === "object") {
|
|
1418
|
+
const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
|
|
1419
|
+
lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
|
|
1420
|
+
} else if (b.rest?.kind === "array") {
|
|
1421
|
+
lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
|
|
1422
|
+
} else {
|
|
1423
|
+
lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
|
|
1424
|
+
}
|
|
1393
1425
|
}
|
|
1426
|
+
} else {
|
|
1427
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1394
1428
|
}
|
|
1395
|
-
} else {
|
|
1396
|
-
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1397
1429
|
}
|
|
1398
1430
|
}
|
|
1399
1431
|
if (loop.filterPredicate) {
|
|
@@ -1448,9 +1480,20 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1448
1480
|
};
|
|
1449
1481
|
renderComponent(comp) {
|
|
1450
1482
|
const propParts = [];
|
|
1483
|
+
const namedSlotCaptures = [];
|
|
1451
1484
|
for (const p of comp.props) {
|
|
1452
1485
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
1453
1486
|
continue;
|
|
1487
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
1488
|
+
const prevInLoop = this.inLoop;
|
|
1489
|
+
this.inLoop = false;
|
|
1490
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
1491
|
+
this.inLoop = prevInLoop;
|
|
1492
|
+
const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
|
|
1493
|
+
namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
|
|
1494
|
+
propParts.push(`${perlHashKey(p.name)} => ${varName}`);
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1454
1497
|
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
|
|
1455
1498
|
if (lowered)
|
|
1456
1499
|
propParts.push(lowered);
|
|
@@ -1467,9 +1510,9 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1467
1510
|
const childrenBody = this.renderChildren(effectiveChildren);
|
|
1468
1511
|
this.inLoop = prevInLoop;
|
|
1469
1512
|
const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
1470
|
-
return
|
|
1513
|
+
return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
|
|
1471
1514
|
}
|
|
1472
|
-
return
|
|
1515
|
+
return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
|
|
1473
1516
|
}
|
|
1474
1517
|
childrenCaptureCounter = 0;
|
|
1475
1518
|
presenceVarCounter = 0;
|
|
@@ -1514,7 +1557,7 @@ ${alternate}
|
|
|
1514
1557
|
${children}`;
|
|
1515
1558
|
}
|
|
1516
1559
|
elementAttrEmitter = {
|
|
1517
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1560
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
1518
1561
|
emitExpression: (value, name) => {
|
|
1519
1562
|
if (name === "style") {
|
|
1520
1563
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -1608,9 +1651,10 @@ ${children}`;
|
|
|
1608
1651
|
let attrName;
|
|
1609
1652
|
if (attr.name === "className")
|
|
1610
1653
|
attrName = "class";
|
|
1611
|
-
else if (attr.name === "key")
|
|
1612
|
-
|
|
1613
|
-
|
|
1654
|
+
else if (attr.name === "key") {
|
|
1655
|
+
const depth = this.currentLoopKeyDepth;
|
|
1656
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
1657
|
+
} else
|
|
1614
1658
|
attrName = attr.name;
|
|
1615
1659
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
1616
1660
|
if (lowered)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAUlE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAG5E,CAAA"}
|
|
@@ -36,6 +36,14 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
36
36
|
private options;
|
|
37
37
|
private errors;
|
|
38
38
|
private inLoop;
|
|
39
|
+
/**
|
|
40
|
+
* `IRLoop.depth` of the loop currently being rendered (save/restore
|
|
41
|
+
* around `renderChildren(loop.children)`, mirroring `inLoop` above).
|
|
42
|
+
* `renderAttributes` reads this to derive the `key` → `data-key`/
|
|
43
|
+
* `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
|
|
44
|
+
* re-derived here (#2168 nested-loop-outer-binding).
|
|
45
|
+
*/
|
|
46
|
+
private currentLoopKeyDepth;
|
|
39
47
|
/**
|
|
40
48
|
* SolidJS-style props identifier (`function(props: P)`) and the
|
|
41
49
|
* analyzer-extracted prop names. Stashed at `generate()` entry so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,WAAW,EAEX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,EAMP,yBAAyB,EAE1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAE3B,KAAK,aAAa,EAClB,KAAK,UAAU,EAyBhB,MAAM,iBAAiB,CAAA;AAKxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AA6BnD,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAuCxD,qBAAa,WAAY,SAAQ,WAAY,YAAW,aAAa,CAAC,aAAa,CAAC;IAClF,IAAI,SAAgB;IACpB,SAAS,SAAa;IACtB,qBAAqB,UAAO;IAG5B,kBAAkB,EAAG,cAAc,CAAS;IAE5C;;;;;;;;;;;OAWG;IACH,kBAAkB,EAAE,yBAAyB,CAA0B;IAEvE,OAAO,CAAC,aAAa,CAAa;IAClC;;;;wCAIoC;IACpC,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAAiB;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,iBAAiB,CAAyB;IAClD;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB,CAAyB;IAClD;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAAyB;IACjD;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB,CAAyB;IAEpD;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAAwB;IACjD;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB,CAAiC;IAC3D;;;;;;OAMG;IACH,OAAO,CAAC,cAAc,CAAmC;IACzD;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc,CAAiC;IACvD;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,qBAAqB,CAAyB;IAEtD,YAAY,OAAO,GAAE,kBAAuB,EAM3C;IAED,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,aAAa,CAsFzE;IAGD;;;;;OAKG;IACH;;;;;OAKG;IACH;;;;;OAKG;IACH,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAO3C;IAED;;;;OAIG;IACH,8BAA8B,CAC5B,IAAI,EAAE,MAAM,GACX;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAgBlD;IAED;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,2BAA2B;IAmBnC;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/B;IAMD,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAE1F;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/build.js
CHANGED
|
@@ -76,7 +76,7 @@ function isAriaBooleanAttr(name) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/adapter/mojo-adapter.ts
|
|
79
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
79
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
80
80
|
|
|
81
81
|
// src/adapter/lib/constants.ts
|
|
82
82
|
var MOJO_TEMPLATE_PRIMITIVES = {
|
|
@@ -85,7 +85,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
|
|
|
85
85
|
Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
|
|
86
86
|
"Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
|
|
87
87
|
"Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
|
|
88
|
-
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
|
|
88
|
+
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
|
|
89
|
+
"Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
|
|
90
|
+
"Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
|
|
91
|
+
"Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
|
|
89
92
|
};
|
|
90
93
|
var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
91
94
|
|
|
@@ -190,6 +193,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
190
193
|
const recv = emit(object);
|
|
191
194
|
return `bf->trim(${recv})`;
|
|
192
195
|
}
|
|
196
|
+
case "trimStart":
|
|
197
|
+
case "trimEnd": {
|
|
198
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
199
|
+
const recv = emit(object);
|
|
200
|
+
return `bf->${fn}(${recv})`;
|
|
201
|
+
}
|
|
193
202
|
case "toFixed": {
|
|
194
203
|
const recv = emit(object);
|
|
195
204
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -223,6 +232,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
223
232
|
const newS = emit(args[1]);
|
|
224
233
|
return `bf->replace(${recv}, ${oldS}, ${newS})`;
|
|
225
234
|
}
|
|
235
|
+
case "replaceAll": {
|
|
236
|
+
const recv = emit(object);
|
|
237
|
+
const oldS = emit(args[0]);
|
|
238
|
+
const newS = emit(args[1]);
|
|
239
|
+
return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
|
|
240
|
+
}
|
|
226
241
|
case "repeat": {
|
|
227
242
|
const recv = emit(object);
|
|
228
243
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -381,7 +396,7 @@ class MojoFilterEmitter {
|
|
|
381
396
|
return "undef";
|
|
382
397
|
return String(value);
|
|
383
398
|
}
|
|
384
|
-
member(object, property, _computed, emit) {
|
|
399
|
+
member(object, property, _computed, _optional, emit) {
|
|
385
400
|
if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
|
|
386
401
|
return `scalar(@{${emit(object)}})`;
|
|
387
402
|
}
|
|
@@ -516,7 +531,7 @@ class MojoTopLevelEmitter {
|
|
|
516
531
|
return "undef";
|
|
517
532
|
return String(value);
|
|
518
533
|
}
|
|
519
|
-
member(object, property, _computed, emit) {
|
|
534
|
+
member(object, property, _computed, _optional, emit) {
|
|
520
535
|
if (object.kind === "identifier" && object.name === "props") {
|
|
521
536
|
return `$${property}`;
|
|
522
537
|
}
|
|
@@ -526,8 +541,13 @@ class MojoTopLevelEmitter {
|
|
|
526
541
|
return staticValue;
|
|
527
542
|
}
|
|
528
543
|
const obj = emit(object);
|
|
529
|
-
if (property === "length")
|
|
544
|
+
if (property === "length") {
|
|
545
|
+
const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
|
|
546
|
+
const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
|
|
547
|
+
if (isStringReceiver)
|
|
548
|
+
return `length(${obj})`;
|
|
530
549
|
return `scalar(@{${obj}})`;
|
|
550
|
+
}
|
|
531
551
|
return `${obj}->{${property}}`;
|
|
532
552
|
}
|
|
533
553
|
indexAccess(object, index, emit) {
|
|
@@ -976,6 +996,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
976
996
|
options;
|
|
977
997
|
errors = [];
|
|
978
998
|
inLoop = false;
|
|
999
|
+
currentLoopKeyDepth = 0;
|
|
979
1000
|
propsObjectName = null;
|
|
980
1001
|
propsParams = [];
|
|
981
1002
|
booleanTypedProps = new Set;
|
|
@@ -1108,7 +1129,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1108
1129
|
return this.renderElement(node);
|
|
1109
1130
|
}
|
|
1110
1131
|
emitText(node) {
|
|
1111
|
-
return node.value;
|
|
1132
|
+
return escapeHtml(node.value);
|
|
1112
1133
|
}
|
|
1113
1134
|
emitExpression(node) {
|
|
1114
1135
|
return this.renderExpression(node);
|
|
@@ -1337,13 +1358,16 @@ ${whenTrue}
|
|
|
1337
1358
|
}
|
|
1338
1359
|
const param = loop.param;
|
|
1339
1360
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
1340
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1361
|
+
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1341
1362
|
for (const n of loopBound) {
|
|
1342
1363
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
1343
1364
|
}
|
|
1344
1365
|
const prevInLoop = this.inLoop;
|
|
1345
1366
|
this.inLoop = true;
|
|
1367
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
1368
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
1346
1369
|
const renderedChildren = this.renderChildren(loop.children);
|
|
1370
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
1347
1371
|
this.inLoop = prevInLoop;
|
|
1348
1372
|
const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
|
|
1349
1373
|
${renderedChildren}` : renderedChildren;
|
|
@@ -1377,23 +1401,31 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1377
1401
|
}
|
|
1378
1402
|
lines.push(`% my $${sortedHoist} = ${sorted};`);
|
|
1379
1403
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1404
|
+
if (loop.objectIteration) {
|
|
1405
|
+
const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
|
|
1406
|
+
lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
|
|
1407
|
+
if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
|
|
1408
|
+
lines.push(`% my $${param} = ${array}->{${keyVar}};`);
|
|
1409
|
+
}
|
|
1410
|
+
} else {
|
|
1411
|
+
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
1412
|
+
if (loop.iterationShape !== "keys") {
|
|
1413
|
+
if (supportableDestructure) {
|
|
1414
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
1415
|
+
for (const b of loop.paramBindings ?? []) {
|
|
1416
|
+
const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
|
|
1417
|
+
if (b.rest?.kind === "object") {
|
|
1418
|
+
const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
|
|
1419
|
+
lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
|
|
1420
|
+
} else if (b.rest?.kind === "array") {
|
|
1421
|
+
lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
|
|
1422
|
+
} else {
|
|
1423
|
+
lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
|
|
1424
|
+
}
|
|
1393
1425
|
}
|
|
1426
|
+
} else {
|
|
1427
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1394
1428
|
}
|
|
1395
|
-
} else {
|
|
1396
|
-
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1397
1429
|
}
|
|
1398
1430
|
}
|
|
1399
1431
|
if (loop.filterPredicate) {
|
|
@@ -1448,9 +1480,20 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1448
1480
|
};
|
|
1449
1481
|
renderComponent(comp) {
|
|
1450
1482
|
const propParts = [];
|
|
1483
|
+
const namedSlotCaptures = [];
|
|
1451
1484
|
for (const p of comp.props) {
|
|
1452
1485
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
1453
1486
|
continue;
|
|
1487
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
1488
|
+
const prevInLoop = this.inLoop;
|
|
1489
|
+
this.inLoop = false;
|
|
1490
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
1491
|
+
this.inLoop = prevInLoop;
|
|
1492
|
+
const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
|
|
1493
|
+
namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
|
|
1494
|
+
propParts.push(`${perlHashKey(p.name)} => ${varName}`);
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1454
1497
|
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
|
|
1455
1498
|
if (lowered)
|
|
1456
1499
|
propParts.push(lowered);
|
|
@@ -1467,9 +1510,9 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1467
1510
|
const childrenBody = this.renderChildren(effectiveChildren);
|
|
1468
1511
|
this.inLoop = prevInLoop;
|
|
1469
1512
|
const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
1470
|
-
return
|
|
1513
|
+
return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
|
|
1471
1514
|
}
|
|
1472
|
-
return
|
|
1515
|
+
return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
|
|
1473
1516
|
}
|
|
1474
1517
|
childrenCaptureCounter = 0;
|
|
1475
1518
|
presenceVarCounter = 0;
|
|
@@ -1514,7 +1557,7 @@ ${alternate}
|
|
|
1514
1557
|
${children}`;
|
|
1515
1558
|
}
|
|
1516
1559
|
elementAttrEmitter = {
|
|
1517
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1560
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
1518
1561
|
emitExpression: (value, name) => {
|
|
1519
1562
|
if (name === "style") {
|
|
1520
1563
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -1608,9 +1651,10 @@ ${children}`;
|
|
|
1608
1651
|
let attrName;
|
|
1609
1652
|
if (attr.name === "className")
|
|
1610
1653
|
attrName = "class";
|
|
1611
|
-
else if (attr.name === "key")
|
|
1612
|
-
|
|
1613
|
-
|
|
1654
|
+
else if (attr.name === "key") {
|
|
1655
|
+
const depth = this.currentLoopKeyDepth;
|
|
1656
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
1657
|
+
} else
|
|
1614
1658
|
attrName = attr.name;
|
|
1615
1659
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
1616
1660
|
if (lowered)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAqI7B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ function isAriaBooleanAttr(name) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/adapter/mojo-adapter.ts
|
|
79
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
79
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
80
80
|
|
|
81
81
|
// src/adapter/lib/constants.ts
|
|
82
82
|
var MOJO_TEMPLATE_PRIMITIVES = {
|
|
@@ -85,7 +85,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
|
|
|
85
85
|
Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
|
|
86
86
|
"Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
|
|
87
87
|
"Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
|
|
88
|
-
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
|
|
88
|
+
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
|
|
89
|
+
"Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
|
|
90
|
+
"Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
|
|
91
|
+
"Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
|
|
89
92
|
};
|
|
90
93
|
var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
91
94
|
|
|
@@ -190,6 +193,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
190
193
|
const recv = emit(object);
|
|
191
194
|
return `bf->trim(${recv})`;
|
|
192
195
|
}
|
|
196
|
+
case "trimStart":
|
|
197
|
+
case "trimEnd": {
|
|
198
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
199
|
+
const recv = emit(object);
|
|
200
|
+
return `bf->${fn}(${recv})`;
|
|
201
|
+
}
|
|
193
202
|
case "toFixed": {
|
|
194
203
|
const recv = emit(object);
|
|
195
204
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -223,6 +232,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
223
232
|
const newS = emit(args[1]);
|
|
224
233
|
return `bf->replace(${recv}, ${oldS}, ${newS})`;
|
|
225
234
|
}
|
|
235
|
+
case "replaceAll": {
|
|
236
|
+
const recv = emit(object);
|
|
237
|
+
const oldS = emit(args[0]);
|
|
238
|
+
const newS = emit(args[1]);
|
|
239
|
+
return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
|
|
240
|
+
}
|
|
226
241
|
case "repeat": {
|
|
227
242
|
const recv = emit(object);
|
|
228
243
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -381,7 +396,7 @@ class MojoFilterEmitter {
|
|
|
381
396
|
return "undef";
|
|
382
397
|
return String(value);
|
|
383
398
|
}
|
|
384
|
-
member(object, property, _computed, emit) {
|
|
399
|
+
member(object, property, _computed, _optional, emit) {
|
|
385
400
|
if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
|
|
386
401
|
return `scalar(@{${emit(object)}})`;
|
|
387
402
|
}
|
|
@@ -516,7 +531,7 @@ class MojoTopLevelEmitter {
|
|
|
516
531
|
return "undef";
|
|
517
532
|
return String(value);
|
|
518
533
|
}
|
|
519
|
-
member(object, property, _computed, emit) {
|
|
534
|
+
member(object, property, _computed, _optional, emit) {
|
|
520
535
|
if (object.kind === "identifier" && object.name === "props") {
|
|
521
536
|
return `$${property}`;
|
|
522
537
|
}
|
|
@@ -526,8 +541,13 @@ class MojoTopLevelEmitter {
|
|
|
526
541
|
return staticValue;
|
|
527
542
|
}
|
|
528
543
|
const obj = emit(object);
|
|
529
|
-
if (property === "length")
|
|
544
|
+
if (property === "length") {
|
|
545
|
+
const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
|
|
546
|
+
const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
|
|
547
|
+
if (isStringReceiver)
|
|
548
|
+
return `length(${obj})`;
|
|
530
549
|
return `scalar(@{${obj}})`;
|
|
550
|
+
}
|
|
531
551
|
return `${obj}->{${property}}`;
|
|
532
552
|
}
|
|
533
553
|
indexAccess(object, index, emit) {
|
|
@@ -976,6 +996,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
976
996
|
options;
|
|
977
997
|
errors = [];
|
|
978
998
|
inLoop = false;
|
|
999
|
+
currentLoopKeyDepth = 0;
|
|
979
1000
|
propsObjectName = null;
|
|
980
1001
|
propsParams = [];
|
|
981
1002
|
booleanTypedProps = new Set;
|
|
@@ -1108,7 +1129,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1108
1129
|
return this.renderElement(node);
|
|
1109
1130
|
}
|
|
1110
1131
|
emitText(node) {
|
|
1111
|
-
return node.value;
|
|
1132
|
+
return escapeHtml(node.value);
|
|
1112
1133
|
}
|
|
1113
1134
|
emitExpression(node) {
|
|
1114
1135
|
return this.renderExpression(node);
|
|
@@ -1337,13 +1358,16 @@ ${whenTrue}
|
|
|
1337
1358
|
}
|
|
1338
1359
|
const param = loop.param;
|
|
1339
1360
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
1340
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1361
|
+
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1341
1362
|
for (const n of loopBound) {
|
|
1342
1363
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
1343
1364
|
}
|
|
1344
1365
|
const prevInLoop = this.inLoop;
|
|
1345
1366
|
this.inLoop = true;
|
|
1367
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
1368
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
1346
1369
|
const renderedChildren = this.renderChildren(loop.children);
|
|
1370
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
1347
1371
|
this.inLoop = prevInLoop;
|
|
1348
1372
|
const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
|
|
1349
1373
|
${renderedChildren}` : renderedChildren;
|
|
@@ -1377,23 +1401,31 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1377
1401
|
}
|
|
1378
1402
|
lines.push(`% my $${sortedHoist} = ${sorted};`);
|
|
1379
1403
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1404
|
+
if (loop.objectIteration) {
|
|
1405
|
+
const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
|
|
1406
|
+
lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
|
|
1407
|
+
if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
|
|
1408
|
+
lines.push(`% my $${param} = ${array}->{${keyVar}};`);
|
|
1409
|
+
}
|
|
1410
|
+
} else {
|
|
1411
|
+
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
1412
|
+
if (loop.iterationShape !== "keys") {
|
|
1413
|
+
if (supportableDestructure) {
|
|
1414
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
1415
|
+
for (const b of loop.paramBindings ?? []) {
|
|
1416
|
+
const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
|
|
1417
|
+
if (b.rest?.kind === "object") {
|
|
1418
|
+
const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
|
|
1419
|
+
lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
|
|
1420
|
+
} else if (b.rest?.kind === "array") {
|
|
1421
|
+
lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
|
|
1422
|
+
} else {
|
|
1423
|
+
lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
|
|
1424
|
+
}
|
|
1393
1425
|
}
|
|
1426
|
+
} else {
|
|
1427
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1394
1428
|
}
|
|
1395
|
-
} else {
|
|
1396
|
-
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1397
1429
|
}
|
|
1398
1430
|
}
|
|
1399
1431
|
if (loop.filterPredicate) {
|
|
@@ -1448,9 +1480,20 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1448
1480
|
};
|
|
1449
1481
|
renderComponent(comp) {
|
|
1450
1482
|
const propParts = [];
|
|
1483
|
+
const namedSlotCaptures = [];
|
|
1451
1484
|
for (const p of comp.props) {
|
|
1452
1485
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
1453
1486
|
continue;
|
|
1487
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
1488
|
+
const prevInLoop = this.inLoop;
|
|
1489
|
+
this.inLoop = false;
|
|
1490
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
1491
|
+
this.inLoop = prevInLoop;
|
|
1492
|
+
const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
|
|
1493
|
+
namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
|
|
1494
|
+
propParts.push(`${perlHashKey(p.name)} => ${varName}`);
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
1454
1497
|
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
|
|
1455
1498
|
if (lowered)
|
|
1456
1499
|
propParts.push(lowered);
|
|
@@ -1467,9 +1510,9 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1467
1510
|
const childrenBody = this.renderChildren(effectiveChildren);
|
|
1468
1511
|
this.inLoop = prevInLoop;
|
|
1469
1512
|
const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
1470
|
-
return
|
|
1513
|
+
return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
|
|
1471
1514
|
}
|
|
1472
|
-
return
|
|
1515
|
+
return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
|
|
1473
1516
|
}
|
|
1474
1517
|
childrenCaptureCounter = 0;
|
|
1475
1518
|
presenceVarCounter = 0;
|
|
@@ -1514,7 +1557,7 @@ ${alternate}
|
|
|
1514
1557
|
${children}`;
|
|
1515
1558
|
}
|
|
1516
1559
|
elementAttrEmitter = {
|
|
1517
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1560
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
1518
1561
|
emitExpression: (value, name) => {
|
|
1519
1562
|
if (name === "style") {
|
|
1520
1563
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -1608,9 +1651,10 @@ ${children}`;
|
|
|
1608
1651
|
let attrName;
|
|
1609
1652
|
if (attr.name === "className")
|
|
1610
1653
|
attrName = "class";
|
|
1611
|
-
else if (attr.name === "key")
|
|
1612
|
-
|
|
1613
|
-
|
|
1654
|
+
else if (attr.name === "key") {
|
|
1655
|
+
const depth = this.currentLoopKeyDepth;
|
|
1656
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
1657
|
+
} else
|
|
1614
1658
|
attrName = attr.name;
|
|
1615
1659
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
1616
1660
|
if (lowered)
|
|
@@ -1800,22 +1844,10 @@ var conformancePins = {
|
|
|
1800
1844
|
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
|
|
1801
1845
|
],
|
|
1802
1846
|
"array-map-function-reference": [{ code: "BF101", severity: "error" }],
|
|
1803
|
-
"dangerous-inner-html": [{ code: "BF101", severity: "error" }]
|
|
1804
|
-
"string-replaceall": [{ code: "BF101", severity: "error" }]
|
|
1847
|
+
"dangerous-inner-html": [{ code: "BF101", severity: "error" }]
|
|
1805
1848
|
};
|
|
1806
1849
|
// src/render-divergences.ts
|
|
1807
|
-
var renderDivergences = {
|
|
1808
|
-
"string-length-text": "`.length` on a STRING prop diverges (array-length lowering misapplied to a scalar)",
|
|
1809
|
-
"number-tofixed": "the literal `¥` in template text reaches the output as U+FFFD — a UTF-8 encoding gap for non-ASCII literal text adjacent to a dynamic slot",
|
|
1810
|
-
"html-entity-text": "`©` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes",
|
|
1811
|
-
"math-methods": "Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)",
|
|
1812
|
-
"static-attr-escape": 'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
|
|
1813
|
-
"object-entries-map": "`Object.entries(prop).map(([k, v]) => …)` renders an EMPTY list — the object-shaped prop silently produces zero iterations",
|
|
1814
|
-
"nested-loop-outer-binding": "nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`",
|
|
1815
|
-
"jsx-element-prop": "a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped",
|
|
1816
|
-
"string-slice": "`.slice()` on a STRING misfires through the array slice helper",
|
|
1817
|
-
"string-trim-sided": "`.trimStart()` / `.trimEnd()` render empty (no lowering)"
|
|
1818
|
-
};
|
|
1850
|
+
var renderDivergences = {};
|
|
1819
1851
|
export {
|
|
1820
1852
|
renderDivergences,
|
|
1821
1853
|
mojoAdapter,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,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;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,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;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA6NjF;AAqWD;;;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.18.
|
|
3
|
+
"version": "0.18.5",
|
|
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,7 +52,7 @@
|
|
|
52
52
|
"directory": "packages/adapter-mojolicious"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@barefootjs/shared": "0.18.
|
|
55
|
+
"@barefootjs/shared": "0.18.5"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@barefootjs/jsx": ">=0.2.0",
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
63
|
-
"@barefootjs/jsx": "0.18.
|
|
63
|
+
"@barefootjs/jsx": "0.18.5"
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -1163,7 +1163,7 @@ export function Foo(props: { v: string }) {
|
|
|
1163
1163
|
// list rather than replace.
|
|
1164
1164
|
const a = new MojoAdapter()
|
|
1165
1165
|
const keys = Object.keys(a.templatePrimitives ?? {}).sort()
|
|
1166
|
-
expect(keys).toEqual(['JSON.stringify', 'Math.ceil', 'Math.floor', 'Math.round', 'Number', 'String'])
|
|
1166
|
+
expect(keys).toEqual(['JSON.stringify', 'Math.abs', 'Math.ceil', 'Math.floor', 'Math.max', 'Math.min', 'Math.round', 'Number', 'String'])
|
|
1167
1167
|
})
|
|
1168
1168
|
|
|
1169
1169
|
test('unregistered identifier-path callee is NOT accepted', () => {
|
|
@@ -1902,3 +1902,23 @@ export function C() {
|
|
|
1902
1902
|
expect(deferred.template).not.toContain('data-x')
|
|
1903
1903
|
})
|
|
1904
1904
|
})
|
|
1905
|
+
|
|
1906
|
+
describe('MojoAdapter - named-slot capture identifier safety (#2168 jsx-element-prop)', () => {
|
|
1907
|
+
// A JSX-valued prop under a hyphenated name (`data-slot`, a valid JSX
|
|
1908
|
+
// attribute name) must not leak into the `begin %>...<% end` capture
|
|
1909
|
+
// variable — Perl variable tokens can't contain `-`. The capture
|
|
1910
|
+
// variable is purely counter-based (never derived from the prop name);
|
|
1911
|
+
// the hash KEY passed to `render_child` still carries the real name,
|
|
1912
|
+
// quoted via `perlHashKey`.
|
|
1913
|
+
test('a hyphenated prop name does not appear in the capture variable', () => {
|
|
1914
|
+
const { template } = compileAndGenerate(`
|
|
1915
|
+
function Card(props) { return null }
|
|
1916
|
+
export function Parent() {
|
|
1917
|
+
return <Card data-slot={<strong>Title</strong>}>text</Card>
|
|
1918
|
+
}
|
|
1919
|
+
`)
|
|
1920
|
+
expect(template).toContain('<% my $bf_prop_0 = begin %>')
|
|
1921
|
+
expect(template).toContain("'data-slot' => $bf_prop_0")
|
|
1922
|
+
expect(template).not.toContain('$bf_prop_data')
|
|
1923
|
+
})
|
|
1924
|
+
})
|
|
@@ -139,6 +139,15 @@ export function renderArrayMethod(
|
|
|
139
139
|
const recv = emit(object)
|
|
140
140
|
return `bf->trim(${recv})`
|
|
141
141
|
}
|
|
142
|
+
case 'trimStart':
|
|
143
|
+
case 'trimEnd': {
|
|
144
|
+
// `.trimStart()` / `.trimEnd()` — the one-sided siblings of
|
|
145
|
+
// `.trim()` (#2183 follow-up). Dedicated `bf->trim_start` /
|
|
146
|
+
// `bf->trim_end` helpers, not `bf->trim` with a flag.
|
|
147
|
+
const fn = method === 'trimStart' ? 'trim_start' : 'trim_end'
|
|
148
|
+
const recv = emit(object)
|
|
149
|
+
return `bf->${fn}(${recv})`
|
|
150
|
+
}
|
|
142
151
|
case 'toFixed': {
|
|
143
152
|
// `.toFixed(digits?)` — Number → fixed-decimal string. `bf->to_fixed`
|
|
144
153
|
// mirrors JS rounding + zero-padding (default 0 digits). #1897.
|
|
@@ -195,6 +204,16 @@ export function renderArrayMethod(
|
|
|
195
204
|
const newS = emit(args[1])
|
|
196
205
|
return `bf->replace(${recv}, ${oldS}, ${newS})`
|
|
197
206
|
}
|
|
207
|
+
case 'replaceAll': {
|
|
208
|
+
// `.replaceAll(old, new)` — string-pattern form, EVERY occurrence,
|
|
209
|
+
// via the dedicated `bf->replace_all` helper (not `bf->replace`
|
|
210
|
+
// with a flag) — the regex-pattern form is refused upstream at
|
|
211
|
+
// the parser, same as `.replace`. See #2182.
|
|
212
|
+
const recv = emit(object)
|
|
213
|
+
const oldS = emit(args[0])
|
|
214
|
+
const newS = emit(args[1])
|
|
215
|
+
return `bf->replace_all(${recv}, ${oldS}, ${newS})`
|
|
216
|
+
}
|
|
198
217
|
case 'repeat': {
|
|
199
218
|
// `.repeat(n)` — string repeated `n` times. The `bf->repeat`
|
|
200
219
|
// helper wraps Perl's `x` operator with the same negative-count
|
|
@@ -105,7 +105,7 @@ export class MojoFilterEmitter implements ParsedExprEmitter {
|
|
|
105
105
|
return String(value)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string {
|
|
108
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string {
|
|
109
109
|
// `.length` on a higher-order result (e.g.
|
|
110
110
|
// `x.tags.filter(t => t.active).length > 0` inside the outer
|
|
111
111
|
// filter predicate, #1443). The higher-order emit produces an
|
|
@@ -322,7 +322,7 @@ export class MojoTopLevelEmitter implements ParsedExprEmitter {
|
|
|
322
322
|
return String(value)
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string {
|
|
325
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string {
|
|
326
326
|
// `props.x` flattens to the bare `$x` the Mojo SSR caller binds each
|
|
327
327
|
// prop to (props arrive as individual `my $x = ...` vars, not a
|
|
328
328
|
// `$props` hashref).
|
|
@@ -338,7 +338,22 @@ export class MojoTopLevelEmitter implements ParsedExprEmitter {
|
|
|
338
338
|
if (staticValue !== null) return staticValue
|
|
339
339
|
}
|
|
340
340
|
const obj = emit(object)
|
|
341
|
-
if (property === 'length')
|
|
341
|
+
if (property === 'length') {
|
|
342
|
+
// `.length` dispatches on receiver type: a STRING receiver needs
|
|
343
|
+
// Perl's scalar `length($x)`, while the array lowering
|
|
344
|
+
// `scalar(@{$x})` dereferences the value as an array ref and
|
|
345
|
+
// returns 0 for a scalar string (the `string-length-text`
|
|
346
|
+
// divergence). The receiver is string-typed when it's a known
|
|
347
|
+
// string prop/getter (`isStringTypedOperand`) or a bare
|
|
348
|
+
// identifier bound to one — the same `_isStringValueName` witness
|
|
349
|
+
// the `eq`/concat lowering already consults.
|
|
350
|
+
const isStr = (e: ParsedExpr) => isStringTypedOperand(e, n => this.ctx._isStringValueName(n))
|
|
351
|
+
const isStringReceiver =
|
|
352
|
+
isStr(object) ||
|
|
353
|
+
(object.kind === 'identifier' && this.ctx._isStringValueName(object.name))
|
|
354
|
+
if (isStringReceiver) return `length(${obj})`
|
|
355
|
+
return `scalar(@{${obj}})`
|
|
356
|
+
}
|
|
342
357
|
return `${obj}->{${property}}`
|
|
343
358
|
}
|
|
344
359
|
|
|
@@ -26,6 +26,9 @@ export const MOJO_TEMPLATE_PRIMITIVES: Record<string, PrimitiveSpec> = {
|
|
|
26
26
|
'Math.floor': { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
|
|
27
27
|
'Math.ceil': { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
|
|
28
28
|
'Math.round': { arity: 1, emit: (args) => `bf->round(${args[0]})` },
|
|
29
|
+
'Math.min': { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
|
|
30
|
+
'Math.max': { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
|
|
31
|
+
'Math.abs': { arity: 1, emit: (args) => `bf->abs(${args[0]})` },
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
} from '@barefootjs/jsx'
|
|
63
63
|
import { isAriaBooleanAttr, isBooleanResultExpr } from './boolean-result.ts'
|
|
64
64
|
import type { ParsedExpr, LoweringMatcher } from '@barefootjs/jsx'
|
|
65
|
-
import { BF_SLOT, BF_COND, BF_REGION } from '@barefootjs/shared'
|
|
65
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from '@barefootjs/shared'
|
|
66
66
|
|
|
67
67
|
import type { MojoRenderCtx } from './lib/types.ts'
|
|
68
68
|
import { MOJO_PRIMITIVE_EMIT_MAP } from './lib/constants.ts'
|
|
@@ -165,6 +165,14 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
165
165
|
private options: Required<MojoAdapterOptions>
|
|
166
166
|
private errors: CompilerError[] = []
|
|
167
167
|
private inLoop: boolean = false
|
|
168
|
+
/**
|
|
169
|
+
* `IRLoop.depth` of the loop currently being rendered (save/restore
|
|
170
|
+
* around `renderChildren(loop.children)`, mirroring `inLoop` above).
|
|
171
|
+
* `renderAttributes` reads this to derive the `key` → `data-key`/
|
|
172
|
+
* `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
|
|
173
|
+
* re-derived here (#2168 nested-loop-outer-binding).
|
|
174
|
+
*/
|
|
175
|
+
private currentLoopKeyDepth = 0
|
|
168
176
|
/**
|
|
169
177
|
* SolidJS-style props identifier (`function(props: P)`) and the
|
|
170
178
|
* analyzer-extracted prop names. Stashed at `generate()` entry so
|
|
@@ -478,7 +486,9 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
478
486
|
}
|
|
479
487
|
|
|
480
488
|
emitText(node: IRText): string {
|
|
481
|
-
|
|
489
|
+
// IRText carries the entity-DECODED value (Phase 1 decodes JSX
|
|
490
|
+
// character references); re-escape for direct HTML emission.
|
|
491
|
+
return escapeHtml(node.value)
|
|
482
492
|
}
|
|
483
493
|
|
|
484
494
|
emitExpression(node: IRExpression): string {
|
|
@@ -846,17 +856,22 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
846
856
|
// the whole body (children + key + filter) so a same-named loop variable
|
|
847
857
|
// isn't replaced by the const literal (#1749 review). Ref-counted for
|
|
848
858
|
// nested loops; released after the body lines are assembled below.
|
|
849
|
-
const loopBound = loop.
|
|
850
|
-
? [param]
|
|
851
|
-
:
|
|
852
|
-
? [
|
|
853
|
-
:
|
|
859
|
+
const loopBound = loop.objectIteration === 'entries'
|
|
860
|
+
? [param, loop.index ?? '_k']
|
|
861
|
+
: loop.objectIteration === 'keys' || loop.objectIteration === 'values' || loop.iterationShape === 'keys'
|
|
862
|
+
? [param]
|
|
863
|
+
: supportableDestructure
|
|
864
|
+
? ['__bf_item', ...(loop.paramBindings ?? []).map(b => b.name), loop.index ?? '_i']
|
|
865
|
+
: [param, loop.index ?? '_i']
|
|
854
866
|
for (const n of loopBound) {
|
|
855
867
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1)
|
|
856
868
|
}
|
|
857
869
|
const prevInLoop = this.inLoop
|
|
858
870
|
this.inLoop = true
|
|
871
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth
|
|
872
|
+
this.currentLoopKeyDepth = loop.depth
|
|
859
873
|
const renderedChildren = this.renderChildren(loop.children)
|
|
874
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth
|
|
860
875
|
this.inLoop = prevInLoop
|
|
861
876
|
|
|
862
877
|
// Whole-item conditional (#1665): prepend an always-present
|
|
@@ -918,6 +933,21 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
918
933
|
}
|
|
919
934
|
lines.push(`% my $${sortedHoist} = ${sorted};`)
|
|
920
935
|
}
|
|
936
|
+
if (loop.objectIteration) {
|
|
937
|
+
// `objectIteration` (#2168 object-entries-map): a Perl hash has no
|
|
938
|
+
// native insertion-order guarantee (unlike Ruby's `Hash`/Python's
|
|
939
|
+
// `dict`) — this codebase's own runtime already works around this
|
|
940
|
+
// elsewhere with `sort keys %$hash` (`BarefootJS.pm`'s
|
|
941
|
+
// `spread_attrs`/`_style_to_css`), so this reuses that exact
|
|
942
|
+
// convention for a deterministic, alphabetically-sorted iteration
|
|
943
|
+
// (not JS insertion order — a documented known limitation for
|
|
944
|
+
// out-of-alphabetical-order data, same as Go/Rust/Xslate).
|
|
945
|
+
const keyVar = loop.objectIteration === 'values' ? '$__bf_k' : `$${loop.index ?? param}`
|
|
946
|
+
lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`)
|
|
947
|
+
if (loop.objectIteration === 'entries' || loop.objectIteration === 'values') {
|
|
948
|
+
lines.push(`% my $${param} = ${array}->{${keyVar}};`)
|
|
949
|
+
}
|
|
950
|
+
} else {
|
|
921
951
|
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`)
|
|
922
952
|
if (loop.iterationShape !== 'keys') {
|
|
923
953
|
if (supportableDestructure) {
|
|
@@ -951,6 +981,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
951
981
|
lines.push(`% my $${param} = ${array}->[${indexVar}];`)
|
|
952
982
|
}
|
|
953
983
|
}
|
|
984
|
+
}
|
|
954
985
|
|
|
955
986
|
// Handle filter().map() pattern by wrapping children in if-condition
|
|
956
987
|
if (loop.filterPredicate) {
|
|
@@ -1049,10 +1080,36 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
1049
1080
|
|
|
1050
1081
|
renderComponent(comp: IRComponent): string {
|
|
1051
1082
|
const propParts: string[] = []
|
|
1083
|
+
// Named JSX-valued props OTHER than the reserved `children`
|
|
1084
|
+
// (`header={<strong>Title</strong>}`, #2168 jsx-element-prop) get the
|
|
1085
|
+
// same `begin %>…<% end` capture as the reserved children slot below,
|
|
1086
|
+
// just keyed by the prop's own name. `render_child` (BarefootJS.pm)
|
|
1087
|
+
// materializes every prop value that's a CODE ref — not only
|
|
1088
|
+
// `children` — into the Mojo::ByteStream the capture block produces,
|
|
1089
|
+
// so the child's read of the slot back out (`<%= $header %>`) sees an
|
|
1090
|
+
// already-safe ByteStream and Mojo::Template's auto-escape passes it
|
|
1091
|
+
// through unescaped, the same way it already does for `children`.
|
|
1092
|
+
const namedSlotCaptures: string[] = []
|
|
1052
1093
|
for (const p of comp.props) {
|
|
1053
1094
|
// Skip callback props (onXxx) and `ref` — both are client-only for
|
|
1054
1095
|
// SSR (Hono renders neither; the client JS wires them at hydration).
|
|
1055
1096
|
if ((p.name.match(/^on[A-Z]/) || p.name === 'ref') && p.value.kind === 'expression') continue
|
|
1097
|
+
if (p.value.kind === 'jsx-children' && p.name !== 'children') {
|
|
1098
|
+
const prevInLoop = this.inLoop
|
|
1099
|
+
this.inLoop = false
|
|
1100
|
+
const slotBody = this.renderChildren(p.value.children)
|
|
1101
|
+
this.inLoop = prevInLoop
|
|
1102
|
+
// Purely counter-based — NOT derived from `p.name` or `comp.slotId`.
|
|
1103
|
+
// A JSX prop name can contain characters (`data-slot`) that aren't a
|
|
1104
|
+
// valid Perl variable token, and `comp.slotId` alone would collide
|
|
1105
|
+
// across two named-slot props on the same component invocation
|
|
1106
|
+
// (unlike the reserved children slot, there's only ever one of
|
|
1107
|
+
// those per invocation).
|
|
1108
|
+
const varName = `$bf_prop_${this.childrenCaptureCounter++}`
|
|
1109
|
+
namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`)
|
|
1110
|
+
propParts.push(`${perlHashKey(p.name)} => ${varName}`)
|
|
1111
|
+
continue
|
|
1112
|
+
}
|
|
1056
1113
|
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name)
|
|
1057
1114
|
if (lowered) propParts.push(lowered)
|
|
1058
1115
|
}
|
|
@@ -1090,9 +1147,9 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
1090
1147
|
const childrenBody = this.renderChildren(effectiveChildren)
|
|
1091
1148
|
this.inLoop = prevInLoop
|
|
1092
1149
|
const varName = `$bf_children_${comp.slotId ?? 'c' + this.childrenCaptureCounter++}`
|
|
1093
|
-
return
|
|
1150
|
+
return `${namedSlotCaptures.join('')}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`
|
|
1094
1151
|
}
|
|
1095
|
-
return
|
|
1152
|
+
return `${namedSlotCaptures.join('')}<%== bf->render_child('${tplName}'${propsStr}) %>`
|
|
1096
1153
|
}
|
|
1097
1154
|
|
|
1098
1155
|
private childrenCaptureCounter = 0
|
|
@@ -1178,7 +1235,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
1178
1235
|
* template). Routed through the shared dispatcher (#1290 step 2).
|
|
1179
1236
|
*/
|
|
1180
1237
|
private readonly elementAttrEmitter: AttrValueEmitter = {
|
|
1181
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1238
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
1182
1239
|
emitExpression: (value, name) => {
|
|
1183
1240
|
// `style={{ … }}` object literal → a CSS string with dynamic values
|
|
1184
1241
|
// interpolated, instead of refusing the bare object with BF101 (#1322).
|
|
@@ -1440,7 +1497,10 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
1440
1497
|
// rewrite happens at attribute-emit time.
|
|
1441
1498
|
let attrName: string
|
|
1442
1499
|
if (attr.name === 'className') attrName = 'class'
|
|
1443
|
-
else if (attr.name === 'key')
|
|
1500
|
+
else if (attr.name === 'key') {
|
|
1501
|
+
const depth = this.currentLoopKeyDepth
|
|
1502
|
+
attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
|
|
1503
|
+
}
|
|
1444
1504
|
else attrName = attr.name
|
|
1445
1505
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
|
|
1446
1506
|
if (lowered) parts.push(lowered)
|
package/src/conformance-pins.ts
CHANGED
|
@@ -141,9 +141,4 @@ export const conformancePins: ConformancePins = {
|
|
|
141
141
|
// the shape loudly instead of emitting entity-escaped markup that
|
|
142
142
|
// silently renders tags as text.
|
|
143
143
|
'dangerous-inner-html': [{ code: 'BF101', severity: 'error' }],
|
|
144
|
-
// Edge-case sweep (Priority 12): `.replaceAll` has no lowering yet —
|
|
145
|
-
// only first-occurrence `.replace` is wired to the runtime helpers.
|
|
146
|
-
// Refused with BF101 rather than reusing the first-only lowering,
|
|
147
|
-
// which would silently change semantics.
|
|
148
|
-
'string-replaceall': [{ code: 'BF101', severity: 'error' }],
|
|
149
144
|
}
|
|
@@ -14,25 +14,4 @@
|
|
|
14
14
|
|
|
15
15
|
import type { RenderDivergences } from '@barefootjs/jsx'
|
|
16
16
|
|
|
17
|
-
export const renderDivergences: RenderDivergences = {
|
|
18
|
-
'string-length-text':
|
|
19
|
-
'`.length` on a STRING prop diverges (array-length lowering misapplied to a scalar)',
|
|
20
|
-
'number-tofixed':
|
|
21
|
-
'the literal `¥` in template text reaches the output as U+FFFD — a UTF-8 encoding gap for non-ASCII literal text adjacent to a dynamic slot',
|
|
22
|
-
'html-entity-text':
|
|
23
|
-
'`©` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes',
|
|
24
|
-
'math-methods':
|
|
25
|
-
'Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)',
|
|
26
|
-
'static-attr-escape':
|
|
27
|
-
'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
|
|
28
|
-
'object-entries-map':
|
|
29
|
-
'`Object.entries(prop).map(([k, v]) => …)` renders an EMPTY list — the object-shaped prop silently produces zero iterations',
|
|
30
|
-
'nested-loop-outer-binding':
|
|
31
|
-
'nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`',
|
|
32
|
-
'jsx-element-prop':
|
|
33
|
-
'a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped',
|
|
34
|
-
'string-slice':
|
|
35
|
-
'`.slice()` on a STRING misfires through the array slice helper',
|
|
36
|
-
'string-trim-sided':
|
|
37
|
-
'`.trimStart()` / `.trimEnd()` render empty (no lowering)',
|
|
38
|
-
}
|
|
17
|
+
export const renderDivergences: RenderDivergences = {}
|
package/src/test-render.ts
CHANGED
|
@@ -240,6 +240,11 @@ export async function renderMojoComponent(options: RenderOptions): Promise<strin
|
|
|
240
240
|
use strict;
|
|
241
241
|
use warnings;
|
|
242
242
|
use utf8;
|
|
243
|
+
# The template is read through :utf8, so $output holds decoded wide
|
|
244
|
+
# characters; without a matching layer on STDOUT Perl emits them as
|
|
245
|
+
# latin-1 bytes and the harness reads U+FFFD (©/¥ mangling). A real
|
|
246
|
+
# Mojolicious response encodes on the way out — this is harness-only.
|
|
247
|
+
binmode STDOUT, ':encoding(UTF-8)';
|
|
243
248
|
|
|
244
249
|
use lib '${LIB_DIR}', '${PERL_CORE_LIB_DIR}';
|
|
245
250
|
use Mojolicious;
|