@barefootjs/mojolicious 0.31.9 → 0.32.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/index.js CHANGED
@@ -1930,7 +1930,11 @@ var conformancePins = {
1930
1930
  "rich-prop-client-read": [{ code: "BF049", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2648" }]
1931
1931
  };
1932
1932
  // src/render-divergences.ts
1933
- var renderDivergences = {};
1933
+ var renderDivergences = {
1934
+ "todo-app": "the `todos` signal seeds from `(props.initialTodos ?? []).map(t => ({ ...t, editing: false }))` — a different-prop-derived `.map()` chain `extractSsrDefaults` cannot statically resolve, and `computeSsrSeedPlan` classifies it opaque (no in-template recompute), so it seeds `undef`; the non-`/* @client */`-marked `{todos().length > 0 && ...}` toggle-all block SSRs as if there are zero todos regardless of `initialTodos` (https://github.com/piconic-ai/barefootjs/issues/2696)",
1935
+ "todo-app-ssr": "same root cause as `todo-app` (https://github.com/piconic-ai/barefootjs/issues/2696), but this fixture's todo-list loop carries no `/* @client */` marker — the compiled Mojo::Template loop iterates the undef-seeded `todos` directly and Perl's strict vars abort the render instead of producing HTML",
1936
+ "callback-param-shadows-prop": "the `first` signal seeds from `[{ a: 'p' }].map((title) => title.a).join(',')` — a constant expression `extractSsrDefaults` still cannot statically resolve (`.map()` is unsupported for any receiver) and, unlike the sibling `joined` memo's structurally similar chain, gets no in-template recompute (`computeSsrSeedPlan` classifies it opaque too); `<span>{first()}</span>` SSRs empty instead of `p` (https://github.com/piconic-ai/barefootjs/issues/2696)"
1937
+ };
1934
1938
  export {
1935
1939
  renderDivergences,
1936
1940
  mojoAdapter,
@@ -1 +1 @@
1
- {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAC/B,CAAA"}
1
+ {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAO/B,CAAA"}
package/dist/vite.js CHANGED
@@ -3916,6 +3916,38 @@ function classify(name, origin, expr, parsed, available) {
3916
3916
  }
3917
3917
  return { kind: "derived", name, origin, expr, parsed, frees: [...frees] };
3918
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
+ }
3919
3951
  function computeSsrSeedPlan(metadata) {
3920
3952
  const baseScope = metadata.propsParams.map((p) => p.name);
3921
3953
  if (metadata.propsObjectName)
@@ -3924,6 +3956,7 @@ function computeSsrSeedPlan(metadata) {
3924
3956
  baseScope.push(name);
3925
3957
  }
3926
3958
  const available = new Set(baseScope);
3959
+ const localConsts = localConstExprsByName(metadata);
3927
3960
  const steps = [];
3928
3961
  for (const signal of metadata.signals) {
3929
3962
  if (signal.envReader) {
@@ -3935,13 +3968,13 @@ function computeSsrSeedPlan(metadata) {
3935
3968
  }
3936
3969
  }
3937
3970
  const expr = signal.initialValue.trim();
3938
- 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));
3939
3972
  available.add(signal.getter);
3940
3973
  }
3941
3974
  for (const memo of metadata.memos) {
3942
3975
  const body = extractArrowBodyExpression(memo.computation);
3943
3976
  const expr = body?.trim() ?? "";
3944
- 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));
3945
3978
  available.add(memo.name);
3946
3979
  }
3947
3980
  return { baseScope, steps };
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Mojo;
2
- our $VERSION = "0.31.8";
2
+ our $VERSION = "0.31.10";
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.8";
2
+ our $VERSION = "0.31.10";
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.8";
2
+ our $VERSION = "0.31.10";
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.9",
3
+ "version": "0.32.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.31.9"
55
+ "@barefootjs/shared": "0.32.0"
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.9",
74
- "@barefootjs/vite": "0.31.9",
75
- "@barefootjs/client": "0.31.9",
73
+ "@barefootjs/jsx": "0.32.0",
74
+ "@barefootjs/vite": "0.32.0",
75
+ "@barefootjs/client": "0.32.0",
76
76
  "vite": "^6.0.0"
77
77
  }
78
78
  }
@@ -10,4 +10,10 @@
10
10
  import type { RenderDivergences } from '@barefootjs/jsx'
11
11
 
12
12
  export const renderDivergences: RenderDivergences = {
13
+ 'todo-app':
14
+ 'the `todos` signal seeds from `(props.initialTodos ?? []).map(t => ({ ...t, editing: false }))` — a different-prop-derived `.map()` chain `extractSsrDefaults` cannot statically resolve, and `computeSsrSeedPlan` classifies it opaque (no in-template recompute), so it seeds `undef`; the non-`/* @client */`-marked `{todos().length > 0 && ...}` toggle-all block SSRs as if there are zero todos regardless of `initialTodos` (https://github.com/piconic-ai/barefootjs/issues/2696)',
15
+ 'todo-app-ssr':
16
+ "same root cause as `todo-app` (https://github.com/piconic-ai/barefootjs/issues/2696), but this fixture's todo-list loop carries no `/* @client */` marker — the compiled Mojo::Template loop iterates the undef-seeded `todos` directly and Perl's strict vars abort the render instead of producing HTML",
17
+ 'callback-param-shadows-prop':
18
+ "the `first` signal seeds from `[{ a: 'p' }].map((title) => title.a).join(',')` — a constant expression `extractSsrDefaults` still cannot statically resolve (`.map()` is unsupported for any receiver) and, unlike the sibling `joined` memo's structurally similar chain, gets no in-template recompute (`computeSsrSeedPlan` classifies it opaque too); `<span>{first()}</span>` SSRs empty instead of `p` (https://github.com/piconic-ai/barefootjs/issues/2696)",
13
19
  }
@@ -5,7 +5,7 @@
5
5
  * Used by adapter-tests conformance runner.
6
6
  */
7
7
 
8
- import { compileJSX, extractSsrDefaults, deriveStashFromDefaults, augmentInheritedPropAccesses, importsSearchParams, evaluateSignalInit } from '@barefootjs/jsx'
8
+ import { compileJSX, extractSsrDefaults, deriveStashFromDefaults, augmentInheritedPropAccesses, importsSearchParams } from '@barefootjs/jsx'
9
9
  import type { ComponentIR, SsrDefault } from '@barefootjs/jsx'
10
10
  import { mkdir, rm } from 'node:fs/promises'
11
11
  import { resolve } from 'node:path'
@@ -514,11 +514,35 @@ function buildChildDefaultsPerl(ir: ComponentIR): string {
514
514
  // semantic divergence from those adapters.
515
515
  for (const signal of ir.metadata.signals) {
516
516
  if (signal.envReader) continue // env signal is the request reader, not a stashed value (#2057)
517
- const value = evaluateSignalInit(signal.initialValue.trim(), undefined)
518
- entries.push(`${signal.getter} => ${value !== null ? toPerlLiteral(value) : 'undef'}`)
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.
524
+ const d = ssrDefaults[signal.getter]
525
+ if (d?.propName !== undefined) {
526
+ if (!declared.has(signal.getter)) {
527
+ entries.push(`${signal.getter} => ${ssrDefaultEntryToPerl(d)}`)
528
+ declared.add(signal.getter)
529
+ }
530
+ continue
531
+ }
532
+ // Static manifest value only — no harness-side evaluation (#2696).
533
+ const value = d ? d.value : undefined
534
+ entries.push(`${signal.getter} => ${value !== undefined && value !== null ? toPerlLiteral(value) : 'undef'}`)
519
535
  }
520
536
  for (const memo of ir.metadata.memos) {
521
537
  const entry = ssrDefaults[memo.name]
538
+ // #2669: same self-derivation handling as the signal loop above.
539
+ if (entry?.propName !== undefined) {
540
+ if (!declared.has(memo.name)) {
541
+ entries.push(`${memo.name} => ${ssrDefaultEntryToPerl(entry)}`)
542
+ declared.add(memo.name)
543
+ }
544
+ continue
545
+ }
522
546
  const value = entry ? entry.value : undefined
523
547
  entries.push(
524
548
  `${memo.name} => ${value !== undefined && value !== null ? toPerlLiteral(value) : 'undef'}`,
@@ -718,22 +742,20 @@ function buildPerlProps(
718
742
  // ternary) rather than skipping it — an unseeded getter faults strict
719
743
  // vars with `Global symbol "$x" requires explicit package name`. Same
720
744
  // rule `buildChildDefaultsPerl` applies to child signals (#1897).
745
+ // No initializer re-evaluation against raw props — production's
746
+ // before_render seeds only from `deriveStashFromDefaults` (#2696).
721
747
  for (const signal of ir.metadata.signals) {
722
748
  if (signal.envReader) continue // env signal bound below via search_params('') (#2057)
723
- const value = evaluateSignalInit(signal.initialValue.trim(), props)
724
- entries.push(`${signal.getter} => ${value !== null ? toPerlLiteral(value) : 'undef'}`)
749
+ if (Object.prototype.hasOwnProperty.call(derivedProps, signal.getter)) {
750
+ entries.push(`${signal.getter} => ${toPerlLiteral(derivedProps[signal.getter])}`)
751
+ }
725
752
  }
726
753
 
727
- // Add memo values. The production Mojo plugin seeds these from the
728
- // manifest's `ssrDefaults` (see Plugin/BarefootJS.pm `before_render`
729
- // hook), which carries the statically-evaluated result of each memo
730
- // computation. Mirror that here so the test harness doesn't diverge
731
- // from the plugin: hard-coding `0` masked memos with non-zero
732
- // initial values until #1423 added a fixture that exposed the gap.
754
+ // No `?? 0` for entries the manifest lacks — production seeds nothing there (#2696).
733
755
  for (const memo of ir.metadata.memos) {
734
- const entry = rootSsrDefaults[memo.name]
735
- const value = entry ? entry.value : 0
736
- entries.push(`${memo.name} => ${toPerlLiteral(value ?? 0)}`)
756
+ if (Object.prototype.hasOwnProperty.call(derivedProps, memo.name)) {
757
+ entries.push(`${memo.name} => ${toPerlLiteral(derivedProps[memo.name])}`)
758
+ }
737
759
  }
738
760
 
739
761
  // (#1922) Request-scoped `searchParams()`: bind `$searchParams` to an