@barefootjs/mojolicious 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/index.js +32 -3
- package/dist/adapter/mojo-adapter.d.ts +10 -0
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/build.js +32 -3
- package/dist/index.js +32 -3
- 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 +10 -20
- package/src/adapter/mojo-adapter.ts +76 -11
package/dist/adapter/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BaseAdapter,
|
|
5
5
|
isBooleanAttr,
|
|
6
6
|
parseExpression as parseExpression2,
|
|
7
|
+
parseStyleObjectEntries,
|
|
7
8
|
isSupported,
|
|
8
9
|
identifierPath,
|
|
9
10
|
emitParsedExpr,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
evalStringArrayJoin,
|
|
15
16
|
extractArrowBodyExpression,
|
|
16
17
|
collectContextConsumers,
|
|
18
|
+
isLowerableObjectRestDestructure,
|
|
17
19
|
extractSsrDefaults
|
|
18
20
|
} from "@barefootjs/jsx";
|
|
19
21
|
|
|
@@ -551,7 +553,9 @@ ${whenTrue}
|
|
|
551
553
|
renderLoop(loop) {
|
|
552
554
|
if (loop.clientOnly)
|
|
553
555
|
return "";
|
|
554
|
-
|
|
556
|
+
const destructure = !!(loop.paramBindings && loop.paramBindings.length > 0);
|
|
557
|
+
const supportableDestructure = destructure && isLowerableObjectRestDestructure(loop);
|
|
558
|
+
if (destructure && !supportableDestructure) {
|
|
555
559
|
this.errors.push({
|
|
556
560
|
code: "BF104",
|
|
557
561
|
severity: "error",
|
|
@@ -574,7 +578,7 @@ ${whenTrue}
|
|
|
574
578
|
}
|
|
575
579
|
const param = loop.param;
|
|
576
580
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
577
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : [param, loop.index ?? "_i"];
|
|
581
|
+
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
578
582
|
for (const n of loopBound) {
|
|
579
583
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
580
584
|
}
|
|
@@ -591,7 +595,14 @@ ${renderedChildren}` : renderedChildren;
|
|
|
591
595
|
}
|
|
592
596
|
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
593
597
|
if (loop.iterationShape !== "keys") {
|
|
594
|
-
|
|
598
|
+
if (supportableDestructure) {
|
|
599
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
600
|
+
for (const b of loop.paramBindings ?? []) {
|
|
601
|
+
lines.push(b.rest ? `% my $${b.name} = $__bf_item;` : `% my $${b.name} = $__bf_item->{${b.path.slice(1)}};`);
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
605
|
+
}
|
|
595
606
|
}
|
|
596
607
|
if (loop.filterPredicate) {
|
|
597
608
|
let filterCond;
|
|
@@ -706,6 +717,11 @@ ${children}`;
|
|
|
706
717
|
elementAttrEmitter = {
|
|
707
718
|
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
708
719
|
emitExpression: (value, name) => {
|
|
720
|
+
if (name === "style") {
|
|
721
|
+
const css = this.tryLowerStyleObject(value.expr);
|
|
722
|
+
if (css !== null)
|
|
723
|
+
return `style="${css}"`;
|
|
724
|
+
}
|
|
709
725
|
if (this.refuseUnsupportedAttrExpression(value.expr, name)) {
|
|
710
726
|
return "";
|
|
711
727
|
}
|
|
@@ -758,6 +774,19 @@ ${children}`;
|
|
|
758
774
|
emitBooleanShorthand: () => "",
|
|
759
775
|
emitJsxChildren: () => ""
|
|
760
776
|
};
|
|
777
|
+
tryLowerStyleObject(expr) {
|
|
778
|
+
const entries = parseStyleObjectEntries(expr);
|
|
779
|
+
if (!entries)
|
|
780
|
+
return null;
|
|
781
|
+
for (const e of entries) {
|
|
782
|
+
if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:<%= ${this.convertExpressionToPerl(e.expr)} %>`).join(";");
|
|
786
|
+
}
|
|
787
|
+
escapeAttrText(s) {
|
|
788
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
789
|
+
}
|
|
761
790
|
renderAttributes(element) {
|
|
762
791
|
const parts = [];
|
|
763
792
|
for (const attr of element.attrs) {
|
|
@@ -218,6 +218,16 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
218
218
|
* template). Routed through the shared dispatcher (#1290 step 2).
|
|
219
219
|
*/
|
|
220
220
|
private readonly elementAttrEmitter;
|
|
221
|
+
/**
|
|
222
|
+
* Lower a `style={{ … }}` object literal to a CSS string with dynamic values
|
|
223
|
+
* interpolated as EP actions, e.g. `{ backgroundColor: color, padding: '8px' }`
|
|
224
|
+
* → `background-color:<%= $color %>;padding:8px`. Returns null when the shape
|
|
225
|
+
* is unsupported or any value can't be lowered (caller then falls through to
|
|
226
|
+
* the BF101 refusal). (#1322)
|
|
227
|
+
*/
|
|
228
|
+
private tryLowerStyleObject;
|
|
229
|
+
/** HTML-attribute escape for static text inlined into a `"..."` attribute. */
|
|
230
|
+
private escapeAttrText;
|
|
221
231
|
private renderAttributes;
|
|
222
232
|
renderScopeMarker(_instanceIdExpr: string): string;
|
|
223
233
|
renderSlotMarker(slotId: string): string;
|
|
@@ -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,EAC1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAM3B,KAAK,aAAa,EAClB,KAAK,UAAU,
|
|
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
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BaseAdapter,
|
|
5
5
|
isBooleanAttr,
|
|
6
6
|
parseExpression as parseExpression2,
|
|
7
|
+
parseStyleObjectEntries,
|
|
7
8
|
isSupported,
|
|
8
9
|
identifierPath,
|
|
9
10
|
emitParsedExpr,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
evalStringArrayJoin,
|
|
15
16
|
extractArrowBodyExpression,
|
|
16
17
|
collectContextConsumers,
|
|
18
|
+
isLowerableObjectRestDestructure,
|
|
17
19
|
extractSsrDefaults
|
|
18
20
|
} from "@barefootjs/jsx";
|
|
19
21
|
|
|
@@ -551,7 +553,9 @@ ${whenTrue}
|
|
|
551
553
|
renderLoop(loop) {
|
|
552
554
|
if (loop.clientOnly)
|
|
553
555
|
return "";
|
|
554
|
-
|
|
556
|
+
const destructure = !!(loop.paramBindings && loop.paramBindings.length > 0);
|
|
557
|
+
const supportableDestructure = destructure && isLowerableObjectRestDestructure(loop);
|
|
558
|
+
if (destructure && !supportableDestructure) {
|
|
555
559
|
this.errors.push({
|
|
556
560
|
code: "BF104",
|
|
557
561
|
severity: "error",
|
|
@@ -574,7 +578,7 @@ ${whenTrue}
|
|
|
574
578
|
}
|
|
575
579
|
const param = loop.param;
|
|
576
580
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
577
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : [param, loop.index ?? "_i"];
|
|
581
|
+
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
578
582
|
for (const n of loopBound) {
|
|
579
583
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
580
584
|
}
|
|
@@ -591,7 +595,14 @@ ${renderedChildren}` : renderedChildren;
|
|
|
591
595
|
}
|
|
592
596
|
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
593
597
|
if (loop.iterationShape !== "keys") {
|
|
594
|
-
|
|
598
|
+
if (supportableDestructure) {
|
|
599
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
600
|
+
for (const b of loop.paramBindings ?? []) {
|
|
601
|
+
lines.push(b.rest ? `% my $${b.name} = $__bf_item;` : `% my $${b.name} = $__bf_item->{${b.path.slice(1)}};`);
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
605
|
+
}
|
|
595
606
|
}
|
|
596
607
|
if (loop.filterPredicate) {
|
|
597
608
|
let filterCond;
|
|
@@ -706,6 +717,11 @@ ${children}`;
|
|
|
706
717
|
elementAttrEmitter = {
|
|
707
718
|
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
708
719
|
emitExpression: (value, name) => {
|
|
720
|
+
if (name === "style") {
|
|
721
|
+
const css = this.tryLowerStyleObject(value.expr);
|
|
722
|
+
if (css !== null)
|
|
723
|
+
return `style="${css}"`;
|
|
724
|
+
}
|
|
709
725
|
if (this.refuseUnsupportedAttrExpression(value.expr, name)) {
|
|
710
726
|
return "";
|
|
711
727
|
}
|
|
@@ -758,6 +774,19 @@ ${children}`;
|
|
|
758
774
|
emitBooleanShorthand: () => "",
|
|
759
775
|
emitJsxChildren: () => ""
|
|
760
776
|
};
|
|
777
|
+
tryLowerStyleObject(expr) {
|
|
778
|
+
const entries = parseStyleObjectEntries(expr);
|
|
779
|
+
if (!entries)
|
|
780
|
+
return null;
|
|
781
|
+
for (const e of entries) {
|
|
782
|
+
if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:<%= ${this.convertExpressionToPerl(e.expr)} %>`).join(";");
|
|
786
|
+
}
|
|
787
|
+
escapeAttrText(s) {
|
|
788
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
789
|
+
}
|
|
761
790
|
renderAttributes(element) {
|
|
762
791
|
const parts = [];
|
|
763
792
|
for (const attr of element.attrs) {
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BaseAdapter,
|
|
5
5
|
isBooleanAttr,
|
|
6
6
|
parseExpression as parseExpression2,
|
|
7
|
+
parseStyleObjectEntries,
|
|
7
8
|
isSupported,
|
|
8
9
|
identifierPath,
|
|
9
10
|
emitParsedExpr,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
evalStringArrayJoin,
|
|
15
16
|
extractArrowBodyExpression,
|
|
16
17
|
collectContextConsumers,
|
|
18
|
+
isLowerableObjectRestDestructure,
|
|
17
19
|
extractSsrDefaults
|
|
18
20
|
} from "@barefootjs/jsx";
|
|
19
21
|
|
|
@@ -551,7 +553,9 @@ ${whenTrue}
|
|
|
551
553
|
renderLoop(loop) {
|
|
552
554
|
if (loop.clientOnly)
|
|
553
555
|
return "";
|
|
554
|
-
|
|
556
|
+
const destructure = !!(loop.paramBindings && loop.paramBindings.length > 0);
|
|
557
|
+
const supportableDestructure = destructure && isLowerableObjectRestDestructure(loop);
|
|
558
|
+
if (destructure && !supportableDestructure) {
|
|
555
559
|
this.errors.push({
|
|
556
560
|
code: "BF104",
|
|
557
561
|
severity: "error",
|
|
@@ -574,7 +578,7 @@ ${whenTrue}
|
|
|
574
578
|
}
|
|
575
579
|
const param = loop.param;
|
|
576
580
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
577
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : [param, loop.index ?? "_i"];
|
|
581
|
+
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
578
582
|
for (const n of loopBound) {
|
|
579
583
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
580
584
|
}
|
|
@@ -591,7 +595,14 @@ ${renderedChildren}` : renderedChildren;
|
|
|
591
595
|
}
|
|
592
596
|
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
593
597
|
if (loop.iterationShape !== "keys") {
|
|
594
|
-
|
|
598
|
+
if (supportableDestructure) {
|
|
599
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
600
|
+
for (const b of loop.paramBindings ?? []) {
|
|
601
|
+
lines.push(b.rest ? `% my $${b.name} = $__bf_item;` : `% my $${b.name} = $__bf_item->{${b.path.slice(1)}};`);
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
605
|
+
}
|
|
595
606
|
}
|
|
596
607
|
if (loop.filterPredicate) {
|
|
597
608
|
let filterCond;
|
|
@@ -706,6 +717,11 @@ ${children}`;
|
|
|
706
717
|
elementAttrEmitter = {
|
|
707
718
|
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
708
719
|
emitExpression: (value, name) => {
|
|
720
|
+
if (name === "style") {
|
|
721
|
+
const css = this.tryLowerStyleObject(value.expr);
|
|
722
|
+
if (css !== null)
|
|
723
|
+
return `style="${css}"`;
|
|
724
|
+
}
|
|
709
725
|
if (this.refuseUnsupportedAttrExpression(value.expr, name)) {
|
|
710
726
|
return "";
|
|
711
727
|
}
|
|
@@ -758,6 +774,19 @@ ${children}`;
|
|
|
758
774
|
emitBooleanShorthand: () => "",
|
|
759
775
|
emitJsxChildren: () => ""
|
|
760
776
|
};
|
|
777
|
+
tryLowerStyleObject(expr) {
|
|
778
|
+
const entries = parseStyleObjectEntries(expr);
|
|
779
|
+
if (!entries)
|
|
780
|
+
return null;
|
|
781
|
+
for (const e of entries) {
|
|
782
|
+
if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:<%= ${this.convertExpressionToPerl(e.expr)} %>`).join(";");
|
|
786
|
+
}
|
|
787
|
+
escapeAttrText(s) {
|
|
788
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
789
|
+
}
|
|
761
790
|
renderAttributes(element) {
|
|
762
791
|
const parts = [];
|
|
763
792
|
for (const attr of element.attrs) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/mojolicious",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
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.
|
|
55
|
+
"@barefootjs/shared": "0.11.0"
|
|
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.
|
|
63
|
+
"@barefootjs/jsx": "0.11.0"
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -45,29 +45,19 @@ runAdapterConformanceTests({
|
|
|
45
45
|
{ code: 'BF103', severity: 'error' },
|
|
46
46
|
{ code: 'BF104', severity: 'error' },
|
|
47
47
|
],
|
|
48
|
-
// #1310: rest destructure in .map() callback.
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
// #1244 catalog: rest spread back onto the root element. Same
|
|
55
|
-
// refusal shape as the read-only variant above — `paramBindings`
|
|
56
|
-
// is non-empty so BF104 fires regardless of how `rest` is used.
|
|
48
|
+
// #1310: rest destructure in .map() callback. The object-rest shape read
|
|
49
|
+
// via member access (`rest-destructure-object-in-map`) now lowers — each
|
|
50
|
+
// binding becomes a Perl `my` local off the per-item var (`$rest` aliases
|
|
51
|
+
// the item so `$rest->{flag}` resolves). The other three stay refused:
|
|
52
|
+
// rest SPREAD (`{...rest}`) needs a residual hash, and array-index /
|
|
53
|
+
// nested paths can't unpack into scalar `my`s.
|
|
57
54
|
'rest-destructure-object-spread-in-map': [{ code: 'BF104', severity: 'error' }],
|
|
58
55
|
'rest-destructure-array-in-map': [{ code: 'BF104', severity: 'error' }],
|
|
59
56
|
'rest-destructure-nested-in-map': [{ code: 'BF104', severity: 'error' }],
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
// the Go adapter's behaviour.
|
|
65
|
-
'style-3-signals': [{ code: 'BF101', severity: 'error' }],
|
|
66
|
-
// Dynamic JS object literal in `style={{ … }}` — same no-EP-form
|
|
67
|
-
// refusal as `style-3-signals`; `refuseUnsupportedAttrExpression`
|
|
68
|
-
// surfaces BF101 (mirrors the xslate + Go adapters). Was a stale
|
|
69
|
-
// `skipJsx` entry claiming the gate didn't lift to a CompilerError.
|
|
70
|
-
'style-object-dynamic': [{ code: 'BF101', severity: 'error' }],
|
|
57
|
+
// `style-3-signals` / `style-object-dynamic` no longer pinned — a
|
|
58
|
+
// `style={{ … }}` object literal now lowers to a CSS string with dynamic
|
|
59
|
+
// values interpolated (`background-color:<%= $color %>;padding:8px`) via
|
|
60
|
+
// `tryLowerStyleObject` (#1322).
|
|
71
61
|
// #1244 stress catalog #12 (#1323): tagged-template-literal call
|
|
72
62
|
// (`cn\`base \${tone()}\``) — same family as #1322 above and refused
|
|
73
63
|
// via the same gate.
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
type AttrValueEmitter,
|
|
42
42
|
isBooleanAttr,
|
|
43
43
|
parseExpression,
|
|
44
|
+
parseStyleObjectEntries,
|
|
44
45
|
isSupported,
|
|
45
46
|
exprToString,
|
|
46
47
|
identifierPath,
|
|
@@ -52,6 +53,7 @@ import {
|
|
|
52
53
|
evalStringArrayJoin,
|
|
53
54
|
extractArrowBodyExpression,
|
|
54
55
|
collectContextConsumers,
|
|
56
|
+
isLowerableObjectRestDestructure,
|
|
55
57
|
type ContextConsumer,
|
|
56
58
|
extractSsrDefaults,
|
|
57
59
|
} from '@barefootjs/jsx'
|
|
@@ -904,7 +906,14 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
904
906
|
// iff the param is a destructure pattern (array or object); a
|
|
905
907
|
// simple identifier leaves it `undefined`. The structured check is
|
|
906
908
|
// robust to whitespace / formatting variants in the source.
|
|
907
|
-
|
|
909
|
+
// A destructure loop param is lowerable for the object-rest / simple-field
|
|
910
|
+
// shape (`.map(({ id, title, ...rest }) => …)`, `rest` read via member
|
|
911
|
+
// access): each binding becomes a Perl `my` local off the per-item var, so
|
|
912
|
+
// the body's `$id` / `$rest->{flag}` resolve natively. Array-index / nested
|
|
913
|
+
// / rest-spread shapes still can't unpack into scalar `my`s → BF104. (#1310)
|
|
914
|
+
const destructure = !!(loop.paramBindings && loop.paramBindings.length > 0)
|
|
915
|
+
const supportableDestructure = destructure && isLowerableObjectRestDestructure(loop)
|
|
916
|
+
if (destructure && !supportableDestructure) {
|
|
908
917
|
this.errors.push({
|
|
909
918
|
code: 'BF104',
|
|
910
919
|
severity: 'error',
|
|
@@ -951,7 +960,9 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
951
960
|
// nested loops; released after the body lines are assembled below.
|
|
952
961
|
const loopBound = loop.iterationShape === 'keys'
|
|
953
962
|
? [param]
|
|
954
|
-
:
|
|
963
|
+
: supportableDestructure
|
|
964
|
+
? ['__bf_item', ...(loop.paramBindings ?? []).map(b => b.name), loop.index ?? '_i']
|
|
965
|
+
: [param, loop.index ?? '_i']
|
|
955
966
|
for (const n of loopBound) {
|
|
956
967
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1)
|
|
957
968
|
}
|
|
@@ -979,7 +990,20 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
979
990
|
}
|
|
980
991
|
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`)
|
|
981
992
|
if (loop.iterationShape !== 'keys') {
|
|
982
|
-
|
|
993
|
+
if (supportableDestructure) {
|
|
994
|
+
// Per-item var + one `my` local per binding; `rest` aliases the item
|
|
995
|
+
// so `$rest->{flag}` resolves (object-rest read via member access).
|
|
996
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`)
|
|
997
|
+
for (const b of loop.paramBindings ?? []) {
|
|
998
|
+
lines.push(
|
|
999
|
+
b.rest
|
|
1000
|
+
? `% my $${b.name} = $__bf_item;`
|
|
1001
|
+
: `% my $${b.name} = $__bf_item->{${b.path.slice(1)}};`,
|
|
1002
|
+
)
|
|
1003
|
+
}
|
|
1004
|
+
} else {
|
|
1005
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`)
|
|
1006
|
+
}
|
|
983
1007
|
}
|
|
984
1008
|
|
|
985
1009
|
// Handle filter().map() pattern by wrapping children in if-condition
|
|
@@ -1196,15 +1220,20 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
1196
1220
|
private readonly elementAttrEmitter: AttrValueEmitter = {
|
|
1197
1221
|
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1198
1222
|
emitExpression: (value, name) => {
|
|
1223
|
+
// `style={{ … }}` object literal → a CSS string with dynamic values
|
|
1224
|
+
// interpolated, instead of refusing the bare object with BF101 (#1322).
|
|
1225
|
+
if (name === 'style') {
|
|
1226
|
+
const css = this.tryLowerStyleObject(value.expr)
|
|
1227
|
+
if (css !== null) return `style="${css}"`
|
|
1228
|
+
}
|
|
1199
1229
|
// Refuse shapes that the regex pipeline silently mangles into
|
|
1200
|
-
// invalid Perl (#1322).
|
|
1201
|
-
//
|
|
1202
|
-
//
|
|
1203
|
-
//
|
|
1204
|
-
//
|
|
1205
|
-
//
|
|
1206
|
-
//
|
|
1207
|
-
// shape we've already rejected.
|
|
1230
|
+
// invalid Perl (#1322). Tagged-template-literal call expressions
|
|
1231
|
+
// (`cn\`base \${tone()}\``) have no idiomatic Mojo template form; the Go
|
|
1232
|
+
// adapter raises BF101 here via `convertExpressionToGo` + `isSupported`.
|
|
1233
|
+
// Lift the same gate so the user gets a clear diagnostic instead of
|
|
1234
|
+
// broken output. The check runs before `convertExpressionToPerl` so the
|
|
1235
|
+
// regex pipeline never produces template-text fragments for a shape
|
|
1236
|
+
// we've already rejected.
|
|
1208
1237
|
if (this.refuseUnsupportedAttrExpression(value.expr, name)) {
|
|
1209
1238
|
return ''
|
|
1210
1239
|
}
|
|
@@ -1355,6 +1384,42 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
1355
1384
|
emitJsxChildren: () => '',
|
|
1356
1385
|
}
|
|
1357
1386
|
|
|
1387
|
+
/**
|
|
1388
|
+
* Lower a `style={{ … }}` object literal to a CSS string with dynamic values
|
|
1389
|
+
* interpolated as EP actions, e.g. `{ backgroundColor: color, padding: '8px' }`
|
|
1390
|
+
* → `background-color:<%= $color %>;padding:8px`. Returns null when the shape
|
|
1391
|
+
* is unsupported or any value can't be lowered (caller then falls through to
|
|
1392
|
+
* the BF101 refusal). (#1322)
|
|
1393
|
+
*/
|
|
1394
|
+
private tryLowerStyleObject(expr: string): string | null {
|
|
1395
|
+
const entries = parseStyleObjectEntries(expr)
|
|
1396
|
+
if (!entries) return null
|
|
1397
|
+
for (const e of entries) {
|
|
1398
|
+
if (e.kind === 'expr' && !isSupported(parseExpression(e.expr)).supported) return null
|
|
1399
|
+
}
|
|
1400
|
+
// The static CSS key + literal value are inlined into a double-quoted
|
|
1401
|
+
// `style="..."` attribute as raw template text, so HTML-attr escape them
|
|
1402
|
+
// (a value like `'"'` would otherwise break the attribute / inject
|
|
1403
|
+
// markup). The dynamic arm's `<%= … %>` is HTML-escaped by Mojo's EP.
|
|
1404
|
+
return entries
|
|
1405
|
+
.map(e =>
|
|
1406
|
+
e.kind === 'literal'
|
|
1407
|
+
? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}`
|
|
1408
|
+
: `${this.escapeAttrText(e.cssKey)}:<%= ${this.convertExpressionToPerl(e.expr)} %>`,
|
|
1409
|
+
)
|
|
1410
|
+
.join(';')
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/** HTML-attribute escape for static text inlined into a `"..."` attribute. */
|
|
1414
|
+
private escapeAttrText(s: string): string {
|
|
1415
|
+
return s
|
|
1416
|
+
.replace(/&/g, '&')
|
|
1417
|
+
.replace(/"/g, '"')
|
|
1418
|
+
.replace(/'/g, ''')
|
|
1419
|
+
.replace(/</g, '<')
|
|
1420
|
+
.replace(/>/g, '>')
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1358
1423
|
private renderAttributes(element: IRElement): string {
|
|
1359
1424
|
const parts: string[] = []
|
|
1360
1425
|
|