@barefootjs/mojolicious 0.31.8 → 0.31.10

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.
@@ -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,CAoOjF"}
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,CAgPjF"}
package/dist/vite.js CHANGED
@@ -3025,6 +3025,34 @@ var SVG_ROOT_TAGS = new Set([
3025
3025
  "animateTransform",
3026
3026
  "animateMotion"
3027
3027
  ]);
3028
+ var MATHML_ROOT_TAGS = new Set([
3029
+ "math",
3030
+ "mrow",
3031
+ "mfrac",
3032
+ "msup",
3033
+ "msub",
3034
+ "msubsup",
3035
+ "mn",
3036
+ "mi",
3037
+ "mo",
3038
+ "mtext",
3039
+ "munder",
3040
+ "mover",
3041
+ "munderover",
3042
+ "mtable",
3043
+ "mtr",
3044
+ "mtd",
3045
+ "msqrt",
3046
+ "mroot",
3047
+ "mstyle",
3048
+ "merror",
3049
+ "mpadded",
3050
+ "mphantom",
3051
+ "menclose",
3052
+ "semantics",
3053
+ "annotation",
3054
+ "annotation-xml"
3055
+ ]);
3028
3056
 
3029
3057
  // ../jsx/src/ir-to-client-js/collect-elements.ts
3030
3058
  var EMPTY_RENDER_EXPRS = new Set(["null", "undefined", "false", "''", '""', "``"]);
@@ -3888,6 +3916,38 @@ function classify(name, origin, expr, parsed, available) {
3888
3916
  }
3889
3917
  return { kind: "derived", name, origin, expr, parsed, frees: [...frees] };
3890
3918
  }
3919
+ function localConstExprsByName(metadata) {
3920
+ const out = new Map;
3921
+ for (const c of metadata.localConstants ?? []) {
3922
+ if (c.isModule || c.declarationKind !== "const" || c.value === undefined)
3923
+ continue;
3924
+ out.set(c.name, c.parsed ?? parseExpression(c.value.trim()));
3925
+ }
3926
+ return out;
3927
+ }
3928
+ function resolveThroughLocalConsts(parsed, localConsts) {
3929
+ let current = parsed;
3930
+ const maxIter = localConsts.size + 1;
3931
+ for (let i = 0;i < maxIter; i++) {
3932
+ const frees = freeIdentifiers(current);
3933
+ if (frees === null)
3934
+ break;
3935
+ let changed = false;
3936
+ for (const name of frees) {
3937
+ const value = localConsts.get(name);
3938
+ if (!value)
3939
+ continue;
3940
+ const inlined = inlineBinding(current, name, value);
3941
+ if (inlined === null)
3942
+ continue;
3943
+ current = inlined;
3944
+ changed = true;
3945
+ }
3946
+ if (!changed)
3947
+ break;
3948
+ }
3949
+ return current;
3950
+ }
3891
3951
  function computeSsrSeedPlan(metadata) {
3892
3952
  const baseScope = metadata.propsParams.map((p) => p.name);
3893
3953
  if (metadata.propsObjectName)
@@ -3896,6 +3956,7 @@ function computeSsrSeedPlan(metadata) {
3896
3956
  baseScope.push(name);
3897
3957
  }
3898
3958
  const available = new Set(baseScope);
3959
+ const localConsts = localConstExprsByName(metadata);
3899
3960
  const steps = [];
3900
3961
  for (const signal of metadata.signals) {
3901
3962
  if (signal.envReader) {
@@ -3907,13 +3968,13 @@ function computeSsrSeedPlan(metadata) {
3907
3968
  }
3908
3969
  }
3909
3970
  const expr = signal.initialValue.trim();
3910
- steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, parseExpression(expr), available));
3971
+ steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, resolveThroughLocalConsts(parseExpression(expr), localConsts), available));
3911
3972
  available.add(signal.getter);
3912
3973
  }
3913
3974
  for (const memo of metadata.memos) {
3914
3975
  const body = extractArrowBodyExpression(memo.computation);
3915
3976
  const expr = body?.trim() ?? "";
3916
- steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify(memo.name, "memo", expr, memo.parsed ?? parseExpression(expr), available));
3977
+ steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify(memo.name, "memo", expr, resolveThroughLocalConsts(memo.parsed ?? parseExpression(expr), localConsts), available));
3917
3978
  available.add(memo.name);
3918
3979
  }
3919
3980
  return { baseScope, steps };
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Mojo;
2
- our $VERSION = "0.31.7";
2
+ our $VERSION = "0.31.9";
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.31.7";
2
+ our $VERSION = "0.31.9";
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.31.7";
2
+ our $VERSION = "0.31.9";
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.31.8",
3
+ "version": "0.31.10",
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.31.8"
55
+ "@barefootjs/shared": "0.31.10"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@barefootjs/jsx": ">=0.2.0",
@@ -70,9 +70,9 @@
70
70
  },
71
71
  "devDependencies": {
72
72
  "@barefootjs/adapter-tests": "0.1.0",
73
- "@barefootjs/jsx": "0.31.8",
74
- "@barefootjs/vite": "0.31.8",
75
- "@barefootjs/client": "0.31.8",
73
+ "@barefootjs/jsx": "0.31.10",
74
+ "@barefootjs/vite": "0.31.10",
75
+ "@barefootjs/client": "0.31.10",
76
76
  "vite": "^6.0.0"
77
77
  }
78
78
  }
@@ -229,7 +229,7 @@ export async function renderMojoComponent(options: RenderOptions): Promise<strin
229
229
  // bare `$var` aborts under Mojo::Template's strict vars.
230
230
  augmentInheritedPropAccesses(ir)
231
231
  // Build props hash for Perl
232
- const propsPerl = buildPerlProps(componentName, props, ir)
232
+ const { propsPerl, userPropsPerl } = buildPerlProps(componentName, props, ir)
233
233
 
234
234
  // Honour `__instanceId` from props for the root scope id so
235
235
  // shared-component fixtures (which pin `<ComponentName>_test`) match
@@ -281,6 +281,18 @@ my $bf = BarefootJS->new($c, {});
281
281
  # (which pin <ComponentName>_test scope ids for cross-adapter normalisation)
282
282
  # match what Hono renderHonoComponent emits. Default to 'test' otherwise.
283
283
  $bf->_scope_id('${rootScopeId}');
284
+ # Mirrors production's \`props_attr\` contract (BarefootJS.pm's \`_props\`
285
+ # accessor): the caller is expected to seed it before rendering the root
286
+ # component, so this harness must call it explicitly.
287
+ #
288
+ # \$user_props — NOT \$props above — is what \`_props\` gets: \$props is
289
+ # this harness's internal vars hash (scope_id, inherited-attribute \`undef\`
290
+ # placeholders, signal/memo seed values, and — when the component imports
291
+ # searchParams — a SearchParams reader object), none of which a real
292
+ # Mojolicious route handler has or passes. Production's bf-p carries none
293
+ # of those, only the caller-facing props.
294
+ my $user_props = ${userPropsPerl};
295
+ $bf->_props($user_props);
284
296
 
285
297
  ${childRenderers}
286
298
 
@@ -502,11 +514,37 @@ function buildChildDefaultsPerl(ir: ComponentIR): string {
502
514
  // semantic divergence from those adapters.
503
515
  for (const signal of ir.metadata.signals) {
504
516
  if (signal.envReader) continue // env signal is the request reader, not a stashed value (#2057)
517
+ // #2669: a self-derivation collision (the signal's initializer derives
518
+ // from a same-named prop) gets a `propName`-carrying entry from
519
+ // `extractSsrDefaults` (see its docstring's invariant) — that entry
520
+ // MUST stay a hashref so `_derive_stash_from_defaults` resolves it
521
+ // against the REAL child props at runtime; the declared-prop loop above
522
+ // already emitted it for a DECLARED prop, so only an undeclared one
523
+ // needs adding here. Either way, do NOT fall through to this harness's
524
+ // own `evaluateSignalInit(..., undefined)` recompute below — that call
525
+ // sees no props at all and would silently re-flatten the collision back
526
+ // to a bare, non-caller-overridable value.
527
+ const d = ssrDefaults[signal.getter]
528
+ if (d?.propName !== undefined) {
529
+ if (!declared.has(signal.getter)) {
530
+ entries.push(`${signal.getter} => ${ssrDefaultEntryToPerl(d)}`)
531
+ declared.add(signal.getter)
532
+ }
533
+ continue
534
+ }
505
535
  const value = evaluateSignalInit(signal.initialValue.trim(), undefined)
506
536
  entries.push(`${signal.getter} => ${value !== null ? toPerlLiteral(value) : 'undef'}`)
507
537
  }
508
538
  for (const memo of ir.metadata.memos) {
509
539
  const entry = ssrDefaults[memo.name]
540
+ // #2669: same self-derivation handling as the signal loop above.
541
+ if (entry?.propName !== undefined) {
542
+ if (!declared.has(memo.name)) {
543
+ entries.push(`${memo.name} => ${ssrDefaultEntryToPerl(entry)}`)
544
+ declared.add(memo.name)
545
+ }
546
+ continue
547
+ }
510
548
  const value = entry ? entry.value : undefined
511
549
  entries.push(
512
550
  `${memo.name} => ${value !== undefined && value !== null ? toPerlLiteral(value) : 'undef'}`,
@@ -515,11 +553,22 @@ function buildChildDefaultsPerl(ir: ComponentIR): string {
515
553
  return `{${entries.join(', ')}}`
516
554
  }
517
555
 
556
+ /**
557
+ * Returns the full internal vars-hash Perl literal (`props`, what the
558
+ * inline `Mojo::Template` renders against) AND `userPropsPerl` — the
559
+ * exact raw fixture props (no defaults, no null-fill, no signal/memo
560
+ * seeding, no rest-bag nesting), mirroring production's route-handler
561
+ * call `$bf->_props($props)` verbatim. `userPropsPerl` is for
562
+ * `$bf->_props(...)` (bf-p hydration payload) only — these must NOT be
563
+ * the same value, and `userPropsPerl` must NOT be derived from
564
+ * `entries` or the defaulted stash: production's own bf-p carries only
565
+ * what the caller actually passed in, unmodified.
566
+ */
518
567
  function buildPerlProps(
519
568
  _componentName: string,
520
569
  props: Record<string, unknown> | undefined,
521
570
  ir: ComponentIR,
522
- ): string {
571
+ ): { propsPerl: string; userPropsPerl: string } {
523
572
  const entries: string[] = []
524
573
 
525
574
  // Add scope_id — honour an explicit `__instanceId` from props so
@@ -552,7 +601,8 @@ function buildPerlProps(
552
601
  // emits a literal `$label` reference where the BF101 path used
553
602
  // to emit `''`, exposing this latent test-harness gap.
554
603
  const value = derivedProps[param.name]
555
- entries.push(`${param.name} => ${value !== undefined && value !== null ? toPerlLiteral(value) : 'undef'}`)
604
+ const literal = value !== undefined && value !== null ? toPerlLiteral(value) : 'undef'
605
+ entries.push(`${param.name} => ${literal}`)
556
606
  }
557
607
 
558
608
  // (#checkbox) SolidJS props-object pattern: `function Checkbox(props:
@@ -623,8 +673,12 @@ function buildPerlProps(
623
673
  const localParamNames = new Set(ir.metadata.propsParams.map(p => p.name))
624
674
  if (props) {
625
675
  for (const [key, value] of Object.entries(props)) {
676
+ // Internal hydration markers (`__instanceId` etc.) are routed by
677
+ // the framework, never a real caller-facing prop.
678
+ if (key.startsWith('__')) continue
626
679
  if (routedKeys.has(key)) continue
627
680
  if (localParamNames.has(key)) continue
681
+ let literal: string | null = null
628
682
  if (value === null) {
629
683
  // An explicit-null prop must still DECLARE the template var —
630
684
  // `typeof null === 'object'` fails every branch below, so the key
@@ -632,15 +686,15 @@ function buildPerlProps(
632
686
  // `my $user`, tripping Perl's strict-mode "Global symbol requires
633
687
  // explicit package name" before the `//` fallback could apply
634
688
  // (data-point conformance, optional-chaining-prop:null-user).
635
- entries.push(`${key} => undef`)
689
+ literal = 'undef'
636
690
  } else if (typeof value === 'string') {
637
- entries.push(`${key} => '${value.replace(/'/g, "\\'")}'`)
691
+ literal = `'${value.replace(/'/g, "\\'")}'`
638
692
  } else if (typeof value === 'number') {
639
- entries.push(`${key} => ${value}`)
693
+ literal = `${value}`
640
694
  } else if (typeof value === 'boolean') {
641
695
  // Mojo::JSON sentinels so BarefootJS helpers can detect
642
696
  // booleans via ref() (see toPerlLiteral / spread_attrs).
643
- entries.push(`${key} => ${value ? 'Mojo::JSON::true' : 'Mojo::JSON::false'}`)
697
+ literal = value ? 'Mojo::JSON::true' : 'Mojo::JSON::false'
644
698
  } else if (Array.isArray(value)) {
645
699
  // Array → Perl arrayref literal. Fixtures that exercise
646
700
  // array-receiver methods (`items.every(...)`, `items.some(...)`,
@@ -651,17 +705,38 @@ function buildPerlProps(
651
705
  // declares `my $items` (the key is absent from the vars
652
706
  // hash) and the template trips Perl's strict-mode "Global
653
707
  // symbol $items requires explicit package name" check.
654
- entries.push(`${key} => ${toPerlLiteral(value)}`)
708
+ literal = toPerlLiteral(value)
655
709
  } else if (value && typeof value === 'object') {
656
710
  // Plain object → Perl hashref literal (#1407 follow-up).
657
711
  // Used by the destructured-rest / propsObject fixtures
658
712
  // (`jsx-spread-rest-prop`, `jsx-spread-props-object`) so
659
713
  // the test harness can pass through bag-shaped props that
660
714
  // weren't enumerated by the analyzer.
661
- entries.push(`${key} => ${toPerlLiteral(value)}`)
715
+ literal = toPerlLiteral(value)
662
716
  }
717
+ if (literal !== null) {
718
+ entries.push(`${key} => ${literal}`)
719
+ }
720
+ }
721
+ }
722
+
723
+ // `$bf->_props(...)` payload (bf-p hydration): mirrors production's
724
+ // route-handler call `$bf->_props($props)` verbatim — the caller's raw
725
+ // props hash, unmodified. No default-filling, no signal/memo seeding,
726
+ // no rest-bag nesting: a real Mojolicious route handler never
727
+ // rearranges the hash it received before handing it to `_props`.
728
+ // Excludes internal harness-only keys (`__instanceId` etc.), which are
729
+ // never a real caller-facing prop. Bareword key relies on Perl's
730
+ // fat-comma auto-quoting, matching this function's other hash-literal
731
+ // construction above.
732
+ const userEntries: string[] = []
733
+ if (props) {
734
+ for (const [key, value] of Object.entries(props)) {
735
+ if (key.startsWith('__')) continue
736
+ userEntries.push(`${key} => ${toPerlLiteral(value)}`)
663
737
  }
664
738
  }
739
+ const userPropsPerl = `{${userEntries.join(', ')}}`
665
740
 
666
741
  // Add signal values evaluated from props (must come after user props).
667
742
  // Seed `undef` for a null / unevaluable initial (e.g. a
@@ -671,6 +746,12 @@ function buildPerlProps(
671
746
  // rule `buildChildDefaultsPerl` applies to child signals (#1897).
672
747
  for (const signal of ir.metadata.signals) {
673
748
  if (signal.envReader) continue // env signal bound below via search_params('') (#2057)
749
+ // #2669: a self-derivation collision already got the correct RAW-prop
750
+ // seed above via `derivedProps` — `extractSsrDefaults` marks that with
751
+ // a `propName`-carrying entry (see its docstring's invariant). Skip
752
+ // re-seeding here or this harness's own `evaluateSignalInit` recompute
753
+ // clobbers the correctly-derived caller value.
754
+ if (rootSsrDefaults[signal.getter]?.propName !== undefined) continue
674
755
  const value = evaluateSignalInit(signal.initialValue.trim(), props)
675
756
  entries.push(`${signal.getter} => ${value !== null ? toPerlLiteral(value) : 'undef'}`)
676
757
  }
@@ -682,6 +763,8 @@ function buildPerlProps(
682
763
  // from the plugin: hard-coding `0` masked memos with non-zero
683
764
  // initial values until #1423 added a fixture that exposed the gap.
684
765
  for (const memo of ir.metadata.memos) {
766
+ // #2669: same self-derivation skip as the signal loop above.
767
+ if (rootSsrDefaults[memo.name]?.propName !== undefined) continue
685
768
  const entry = rootSsrDefaults[memo.name]
686
769
  const value = entry ? entry.value : 0
687
770
  entries.push(`${memo.name} => ${toPerlLiteral(value ?? 0)}`)
@@ -697,7 +780,7 @@ function buildPerlProps(
697
780
  entries.push(`searchParams => BarefootJS->search_params('')`)
698
781
  }
699
782
 
700
- return `{${entries.join(', ')}}`
783
+ return { propsPerl: `{${entries.join(', ')}}`, userPropsPerl }
701
784
  }
702
785
 
703
786
  /**