@barefootjs/mojolicious 0.10.1 → 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.
@@ -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
- if (loop.paramBindings && loop.paramBindings.length > 0) {
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
- lines.push(`% my $${param} = ${array}->[${indexVar}];`);
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, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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":"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"}
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
- if (loop.paramBindings && loop.paramBindings.length > 0) {
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
- lines.push(`% my $${param} = ${array}->[${indexVar}];`);
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, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
- if (loop.paramBindings && loop.paramBindings.length > 0) {
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
- lines.push(`% my $${param} = ${array}->[${indexVar}];`);
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, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
789
+ }
761
790
  renderAttributes(element) {
762
791
  const parts = [];
763
792
  for (const attr of element.attrs) {
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Mojo;
2
- our $VERSION = "0.10.0";
2
+ our $VERSION = "0.10.1";
3
3
  use Mojo::Base -base, -signatures;
4
4
 
5
5
  use Mojo::ByteStream qw(b);
@@ -1,5 +1,5 @@
1
1
  package Mojolicious::Plugin::BarefootJS::DevReload;
2
- our $VERSION = "0.10.0";
2
+ our $VERSION = "0.10.1";
3
3
  use Mojo::Base 'Mojolicious::Plugin', -signatures;
4
4
 
5
5
  =head1 NAME
@@ -1,5 +1,5 @@
1
1
  package Mojolicious::Plugin::BarefootJS;
2
- our $VERSION = "0.10.0";
2
+ our $VERSION = "0.10.1";
3
3
  use Mojo::Base 'Mojolicious::Plugin', -signatures;
4
4
 
5
5
  use Mojo::File qw(path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/mojolicious",
3
- "version": "0.10.1",
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.10.1"
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.10.1"
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. Hono / CSR lower
49
- // these via the inline residual-object accessor (#1309); the Mojo
50
- // adapter's loop emitter raises the generic BF104 destructure
51
- // refusal regardless of whether the binding is rest or plain.
52
- // Pinning the contract here makes the limitation declarative.
53
- 'rest-destructure-object-in-map': [{ code: 'BF104', severity: 'error' }],
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
- // #1244 stress catalog #11 (#1322): JS object literal in an
61
- // attribute value (`style={{ background: bg(), color: fg() }}`) has
62
- // no idiomatic Mojo template form. `refuseUnsupportedAttrExpression`
63
- // surfaces BF101 with a wrap-in-`/* @client */` suggestion, matching
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
- if (loop.paramBindings && loop.paramBindings.length > 0) {
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
- : [param, loop.index ?? '_i']
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
- lines.push(`% my $${param} = ${array}->[${indexVar}];`)
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). Object literals (`style={{...}}`) and
1201
- // tagged-template-literal call expressions (`cn\`base \${tone()}\``)
1202
- // have no idiomatic Mojo template form; the Go adapter raises
1203
- // BF101 here via `convertExpressionToGo` + `isSupported`. Lift the
1204
- // same gate so the user gets a clear diagnostic instead of broken
1205
- // output. The check runs before `convertExpressionToPerl` so the
1206
- // regex pipeline never produces template-text fragments for a
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, '&amp;')
1417
+ .replace(/"/g, '&quot;')
1418
+ .replace(/'/g, '&#39;')
1419
+ .replace(/</g, '&lt;')
1420
+ .replace(/>/g, '&gt;')
1421
+ }
1422
+
1358
1423
  private renderAttributes(element: IRElement): string {
1359
1424
  const parts: string[] = []
1360
1425