@barefootjs/erb 0.18.0 → 0.18.2
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/README.md +32 -0
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -1
- package/dist/render-divergences.d.ts +16 -0
- package/dist/render-divergences.d.ts.map +1 -0
- package/lib/barefoot_js.rb +9 -1
- package/package.json +3 -3
- package/src/__tests__/erb-adapter.test.ts +8 -0
- package/src/conformance-pins.ts +11 -0
- package/src/index.ts +1 -0
- package/src/render-divergences.ts +44 -0
- package/src/test-render.ts +49 -29
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# barefoot_js
|
|
2
|
+
|
|
3
|
+
Ruby runtime for [BarefootJS](https://barefootjs.dev/) marked templates, targeting ERB.
|
|
4
|
+
|
|
5
|
+
[BarefootJS](https://github.com/piconic-ai/barefootjs) is a fine-grained reactive TSX compiler: you write components in TSX, and the compiler emits templates for your backend's template engine plus the client-side JS that hydrates them. This gem is the server half for Ruby — it renders the `.erb` templates produced by the `@barefootjs/erb` adapter.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
gem install barefoot_js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Every compiled `.erb` template receives a `BarefootJS::Context` as the `bf` local. The context is engine-agnostic; everything that depends on how a template is rendered is delegated to a pluggable backend (`BarefootJS::Backend::Erb` is the ERB reference implementation):
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require 'barefoot_js'
|
|
19
|
+
|
|
20
|
+
bf = BarefootJS::Context.new(backend)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Values are JSON-shaped Ruby data with symbol hash keys throughout (props, env hashes, array-of-hash records).
|
|
24
|
+
|
|
25
|
+
## Documentation
|
|
26
|
+
|
|
27
|
+
- [barefootjs.dev](https://barefootjs.dev/) — core documentation
|
|
28
|
+
- [GitHub: piconic-ai/barefootjs](https://github.com/piconic-ai/barefootjs) — monorepo (this gem lives at `packages/adapter-erb`)
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;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;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAiG7B,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@
|
|
|
6
6
|
export { ErbAdapter, erbAdapter } from './adapter/index.ts';
|
|
7
7
|
export type { ErbAdapterOptions } from './adapter/index.ts';
|
|
8
8
|
export { conformancePins } from './conformance-pins.ts';
|
|
9
|
+
export { renderDivergences } from './render-divergences.ts';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -189163,9 +189163,28 @@ var conformancePins = {
|
|
|
189163
189163
|
"filter-nested-find-predicate": [
|
|
189164
189164
|
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
|
|
189165
189165
|
],
|
|
189166
|
-
"array-map-function-reference": [{ code: "BF101", severity: "error" }]
|
|
189166
|
+
"array-map-function-reference": [{ code: "BF101", severity: "error" }],
|
|
189167
|
+
"dangerous-inner-html": [{ code: "BF101", severity: "error" }],
|
|
189168
|
+
"string-replaceall": [{ code: "BF101", severity: "error" }]
|
|
189169
|
+
};
|
|
189170
|
+
// src/render-divergences.ts
|
|
189171
|
+
var renderDivergences = {
|
|
189172
|
+
"falsy-text-values": '`{false}` renders "false" (Hono drops it); `{null}`/`{undefined}` render empty (Hono renders "null") — neither side matches JSX semantics',
|
|
189173
|
+
"html-entity-text": "`©` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes",
|
|
189174
|
+
"optional-chaining-prop": "`user?.name ?? …` on an object prop: the Ruby render exits 1 (optional chaining into a Hash prop has no lowering)",
|
|
189175
|
+
"math-methods": "Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)",
|
|
189176
|
+
"boolean-attr-literals": 'camelCase boolean alias `readOnly`: Hono SSRs `readOnly="true"`, this adapter emits bare presence',
|
|
189177
|
+
"camelcase-attributes": "`htmlFor` is not lowered to `for` (Hono maps it)",
|
|
189178
|
+
"static-attr-escape": 'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
|
|
189179
|
+
"svg-icon": "SVG camelCase presentation attrs (`strokeWidth`, `strokeLinecap`) pass through unmapped; Hono lowers to kebab-case",
|
|
189180
|
+
"object-entries-map": "`Object.entries(prop).map(([k, v]) => …)` renders but its loop item keys diverge from the reference serialisation",
|
|
189181
|
+
"nested-loop-outer-binding": "nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`",
|
|
189182
|
+
"jsx-element-prop": "a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped",
|
|
189183
|
+
"string-slice": '`.slice()` on a STRING lowers through the array slice helper and renders "[]" instead of the substring',
|
|
189184
|
+
"string-trim-sided": "`.trimStart()` / `.trimEnd()` render empty (no lowering; only both-sides `.trim` is wired)"
|
|
189167
189185
|
};
|
|
189168
189186
|
export {
|
|
189187
|
+
renderDivergences,
|
|
189169
189188
|
erbAdapter,
|
|
189170
189189
|
conformancePins,
|
|
189171
189190
|
ErbAdapter
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-level divergences against the shared conformance corpus
|
|
3
|
+
* (Priority-12 edge-case sweep, #2168): fixtures that COMPILE clean on
|
|
4
|
+
* this adapter but whose rendered output diverges from the Hono
|
|
5
|
+
* reference on real Ruby erb (or fails at render time).
|
|
6
|
+
*
|
|
7
|
+
* Consumed by this package's conformance test (its `skipJsx` set is
|
|
8
|
+
* derived from these keys, so the skip list and this declaration can't
|
|
9
|
+
* drift) and by `packages/compat`, which publishes the entries in the
|
|
10
|
+
* fixture-divergences section of `ui/compat.lock.json` — surfaced on
|
|
11
|
+
* the docs compatibility-matrix page. Graduating an entry means fixing
|
|
12
|
+
* the adapter (or the shared compiler layer) and deleting the line.
|
|
13
|
+
*/
|
|
14
|
+
import type { RenderDivergences } from '@barefootjs/jsx';
|
|
15
|
+
export declare const renderDivergences: RenderDivergences;
|
|
16
|
+
//# sourceMappingURL=render-divergences.d.ts.map
|
|
@@ -0,0 +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,iBA2B/B,CAAA"}
|
package/lib/barefoot_js.rb
CHANGED
|
@@ -324,7 +324,15 @@ module BarefootJS
|
|
|
324
324
|
extra[name] = props.key?(name) ? props[name] : d[:value]
|
|
325
325
|
next
|
|
326
326
|
end
|
|
327
|
-
|
|
327
|
+
# `propName` rides in from the JSON manifest as a String -- JSON has
|
|
328
|
+
# no symbol type, and the manifest's `symbolize_names: true` parse
|
|
329
|
+
# only symbolizes hash KEYS, never string values. Runtime prop
|
|
330
|
+
# hashes, meanwhile, are symbol-keyed because compiled ERB templates
|
|
331
|
+
# pass `{ children: ... }` literals, so `props.key?(prop_name)` with
|
|
332
|
+
# the String would always miss, silently falling back to the static
|
|
333
|
+
# default for every manifest-registered child (e.g. `children`
|
|
334
|
+
# rendering empty) (#2157).
|
|
335
|
+
prop_name = d[:propName]&.to_sym
|
|
328
336
|
extra[name] =
|
|
329
337
|
if !prop_name.nil? && props.key?(prop_name) && !props[prop_name].nil?
|
|
330
338
|
props[prop_name]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/erb",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "ERB (Embedded Ruby) adapter for BarefootJS — compiles IR to .erb templates and ships the Ruby rendering backend; runs under any Rack app (Sinatra, Rails)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -54,14 +54,14 @@
|
|
|
54
54
|
"directory": "packages/adapter-erb"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@barefootjs/shared": "0.18.
|
|
57
|
+
"@barefootjs/shared": "0.18.2"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@barefootjs/jsx": ">=0.2.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
64
|
-
"@barefootjs/jsx": "0.18.
|
|
64
|
+
"@barefootjs/jsx": "0.18.2",
|
|
65
65
|
"typescript": "^5.0.0"
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -26,11 +26,19 @@ import { ErbAdapter } from '../adapter'
|
|
|
26
26
|
import { renderErbComponent, ErbNotAvailableError } from '../test-render'
|
|
27
27
|
import { compileJSX, type ComponentIR } from '@barefootjs/jsx'
|
|
28
28
|
import { conformancePins } from '../conformance-pins'
|
|
29
|
+
import { renderDivergences } from '../render-divergences'
|
|
29
30
|
|
|
30
31
|
runAdapterConformanceTests({
|
|
31
32
|
name: 'erb',
|
|
32
33
|
factory: () => new ErbAdapter(),
|
|
33
34
|
render: renderErbComponent,
|
|
35
|
+
// Priority-12 edge-case sweep (炙り出し, #2168): render-level
|
|
36
|
+
// divergences are declared in `../render-divergences` (exported from the
|
|
37
|
+
// package index and published to `ui/compat.lock.json` / the docs
|
|
38
|
+
// compatibility-matrix page by `packages/compat`). Deriving the skip
|
|
39
|
+
// list from that object keeps the public declaration and these test
|
|
40
|
+
// skips from drifting; each entry's rationale lives there.
|
|
41
|
+
skipJsx: Object.keys(renderDivergences),
|
|
34
42
|
// No JSX-render skips: every shared conformance fixture — including
|
|
35
43
|
// the composed `site/ui` demo corpus (#1467 / #1897) — renders to
|
|
36
44
|
// Hono parity on real Ruby `erb`. `data-table` came off via the
|
package/src/conformance-pins.ts
CHANGED
|
@@ -97,4 +97,15 @@ export const conformancePins: ConformancePins = {
|
|
|
97
97
|
// with BF101 rather than emitting a broken template. Same pin as
|
|
98
98
|
// mojo/xslate.
|
|
99
99
|
'array-map-function-reference': [{ code: 'BF101', severity: 'error' }],
|
|
100
|
+
// Edge-case sweep (Priority 12): `dangerouslySetInnerHTML` requires a
|
|
101
|
+
// deliberate raw-HTML (unescaped) output affordance in the target
|
|
102
|
+
// template language. No lowering exists yet, so the compiler refuses
|
|
103
|
+
// the shape loudly instead of emitting entity-escaped markup that
|
|
104
|
+
// silently renders tags as text.
|
|
105
|
+
'dangerous-inner-html': [{ code: 'BF101', severity: 'error' }],
|
|
106
|
+
// Edge-case sweep (Priority 12): `.replaceAll` has no lowering yet —
|
|
107
|
+
// only first-occurrence `.replace` is wired to the runtime helpers.
|
|
108
|
+
// Refused with BF101 rather than reusing the first-only lowering,
|
|
109
|
+
// which would silently change semantics.
|
|
110
|
+
'string-replaceall': [{ code: 'BF101', severity: 'error' }],
|
|
100
111
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-level divergences against the shared conformance corpus
|
|
3
|
+
* (Priority-12 edge-case sweep, #2168): fixtures that COMPILE clean on
|
|
4
|
+
* this adapter but whose rendered output diverges from the Hono
|
|
5
|
+
* reference on real Ruby erb (or fails at render time).
|
|
6
|
+
*
|
|
7
|
+
* Consumed by this package's conformance test (its `skipJsx` set is
|
|
8
|
+
* derived from these keys, so the skip list and this declaration can't
|
|
9
|
+
* drift) and by `packages/compat`, which publishes the entries in the
|
|
10
|
+
* fixture-divergences section of `ui/compat.lock.json` — surfaced on
|
|
11
|
+
* the docs compatibility-matrix page. Graduating an entry means fixing
|
|
12
|
+
* the adapter (or the shared compiler layer) and deleting the line.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { RenderDivergences } from '@barefootjs/jsx'
|
|
16
|
+
|
|
17
|
+
export const renderDivergences: RenderDivergences = {
|
|
18
|
+
'falsy-text-values':
|
|
19
|
+
'`{false}` renders "false" (Hono drops it); `{null}`/`{undefined}` render empty (Hono renders "null") — neither side matches JSX semantics',
|
|
20
|
+
'html-entity-text':
|
|
21
|
+
'`©` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes',
|
|
22
|
+
'optional-chaining-prop':
|
|
23
|
+
'`user?.name ?? …` on an object prop: the Ruby render exits 1 (optional chaining into a Hash prop has no lowering)',
|
|
24
|
+
'math-methods':
|
|
25
|
+
'Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)',
|
|
26
|
+
'boolean-attr-literals':
|
|
27
|
+
'camelCase boolean alias `readOnly`: Hono SSRs `readOnly="true"`, this adapter emits bare presence',
|
|
28
|
+
'camelcase-attributes':
|
|
29
|
+
'`htmlFor` is not lowered to `for` (Hono maps it)',
|
|
30
|
+
'static-attr-escape':
|
|
31
|
+
'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
|
|
32
|
+
'svg-icon':
|
|
33
|
+
'SVG camelCase presentation attrs (`strokeWidth`, `strokeLinecap`) pass through unmapped; Hono lowers to kebab-case',
|
|
34
|
+
'object-entries-map':
|
|
35
|
+
'`Object.entries(prop).map(([k, v]) => …)` renders but its loop item keys diverge from the reference serialisation',
|
|
36
|
+
'nested-loop-outer-binding':
|
|
37
|
+
'nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`',
|
|
38
|
+
'jsx-element-prop':
|
|
39
|
+
'a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped',
|
|
40
|
+
'string-slice':
|
|
41
|
+
'`.slice()` on a STRING lowers through the array slice helper and renders "[]" instead of the substring',
|
|
42
|
+
'string-trim-sided':
|
|
43
|
+
'`.trimStart()` / `.trimEnd()` render empty (no lowering; only both-sides `.trim` is wired)',
|
|
44
|
+
}
|
package/src/test-render.ts
CHANGED
|
@@ -22,7 +22,18 @@
|
|
|
22
22
|
* Child components are wired through the production
|
|
23
23
|
* `BarefootJS::Context#register_child_renderer` so the child's `bf-s` scope
|
|
24
24
|
* id derives from `<parentScope>_<slotId>` exactly as a real `bf build`
|
|
25
|
-
* page would.
|
|
25
|
+
* page would. As of #2158, the child-vars seeding step also runs the exact
|
|
26
|
+
* production sequence — `BarefootJS::Context.derive_vars_from_defaults`,
|
|
27
|
+
* the same call `register_components_from_manifest` makes
|
|
28
|
+
* (`lib/barefoot_js.rb` ~lines 283-292) — instead of a harness-local
|
|
29
|
+
* `defaults.merge(child_props)` shortcut. That shortcut is why the #2157
|
|
30
|
+
* bug (a manifest `propName` riding in as a JSON String, looked up against
|
|
31
|
+
* symbol-keyed runtime prop Hashes, so `children` silently fell back to
|
|
32
|
+
* the static default and rendered empty) was invisible to this whole
|
|
33
|
+
* conformance corpus: the harness never exercised the code path the bug
|
|
34
|
+
* lived in. Now it does, so the same class of regression is caught by
|
|
35
|
+
* every fixture that renders a child component, not just a dedicated
|
|
36
|
+
* regression test.
|
|
26
37
|
*/
|
|
27
38
|
|
|
28
39
|
import { compileJSX, extractSsrDefaults, importsSearchParams } from '@barefootjs/jsx'
|
|
@@ -255,12 +266,21 @@ export async function renderErbComponent(options: RenderOptions): Promise<string
|
|
|
255
266
|
const rootScopeIdRaw = typeof props?.__instanceId === 'string' ? props.__instanceId : 'test'
|
|
256
267
|
const rootScopeId = rubyStringLiteral(rootScopeIdRaw)
|
|
257
268
|
|
|
258
|
-
// Static ssrDefaults per child,
|
|
259
|
-
//
|
|
260
|
-
// written as one JSON file keyed by
|
|
261
|
-
|
|
269
|
+
// Static ssrDefaults per child, written VERBATIM — `propName` /
|
|
270
|
+
// `isRestProps` / `value` intact, exactly the shape the production
|
|
271
|
+
// build manifest embeds — and written as one JSON file keyed by
|
|
272
|
+
// snake_case template name. `buildChildRenderersRuby` below feeds this
|
|
273
|
+
// straight into `BarefootJS::Context.derive_vars_from_defaults`, the
|
|
274
|
+
// same call `register_components_from_manifest` makes in the published
|
|
275
|
+
// gem (#2158). A prior version of this harness simplified each entry
|
|
276
|
+
// to its bare `value` and did `defaults.merge(child_props)` directly —
|
|
277
|
+
// that sidestepped `derive_vars_from_defaults`'s `propName` lookup
|
|
278
|
+
// entirely, which is exactly why the #2157 String-vs-symbol `propName`
|
|
279
|
+
// bug (children rendering empty) was invisible to this whole
|
|
280
|
+
// conformance corpus.
|
|
281
|
+
const childDefaults: Record<string, Record<string, SsrDefault>> = {}
|
|
262
282
|
for (const [childName, { ir: childIR }] of childTemplates) {
|
|
263
|
-
childDefaults[toSnakeCase(childName)] =
|
|
283
|
+
childDefaults[toSnakeCase(childName)] = extractSsrDefaults(childIR.metadata) ?? {}
|
|
264
284
|
}
|
|
265
285
|
if (childTemplates.size > 0) {
|
|
266
286
|
await Bun.write(resolve(tempDir, 'child_defaults.json'), JSON.stringify(childDefaults))
|
|
@@ -343,10 +363,19 @@ function collectImportedComponentNames(ir: ComponentIR): string[] {
|
|
|
343
363
|
* derives the child scope id from `<parentScope>_<slotId>` (the parent's
|
|
344
364
|
* `bf.render_child('<name>', { …, _bf_slot: '<slotId>' })` passes
|
|
345
365
|
* `_bf_slot`), seeds signal / memo / prop defaults from the child IR's
|
|
346
|
-
* `ssrDefaults` (loaded once from `child_defaults.json
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
366
|
+
* `ssrDefaults` (loaded once from `child_defaults.json`) THROUGH THE SAME
|
|
367
|
+
* `BarefootJS::Context.derive_vars_from_defaults` call
|
|
368
|
+
* `register_components_from_manifest` makes (lib/barefoot_js.rb ~lines
|
|
369
|
+
* 283-292) — not a harness-local re-derivation — so the propName /
|
|
370
|
+
* isRestProps resolution the published gem actually runs is exactly what
|
|
371
|
+
* this harness exercises (#2158; this is the fix for the #2157 class of
|
|
372
|
+
* bug, where a harness-local `defaults.merge(child_props)` shortcut never
|
|
373
|
+
* touched that code path and so never caught the String-vs-symbol
|
|
374
|
+
* `propName` mismatch). Caller props still win over static defaults —
|
|
375
|
+
* that's `derive_vars_from_defaults`'s own contract — shares the parent's
|
|
376
|
+
* script list, and renders the child `.erb` through the same backend.
|
|
377
|
+
* Loop children (no `_bf_slot`) fall back to `<ComponentName>_<rand>`
|
|
378
|
+
* like the Perl harnesses.
|
|
350
379
|
*/
|
|
351
380
|
function buildChildRenderersRuby(
|
|
352
381
|
childTemplates: Map<string, { template: string; ir: ComponentIR }>,
|
|
@@ -411,8 +440,16 @@ function buildChildRenderersRuby(
|
|
|
411
440
|
lines.push(` child_bf._child_renderers(bf._child_renderers)`)
|
|
412
441
|
lines.push(` child_bf._scripts(bf._scripts)`)
|
|
413
442
|
lines.push(` child_bf._script_seen(bf._script_seen)`)
|
|
414
|
-
// Seed template vars
|
|
415
|
-
|
|
443
|
+
// Seed template vars through the production sequence — the exact
|
|
444
|
+
// call `register_components_from_manifest` makes in the published gem
|
|
445
|
+
// (lib/barefoot_js.rb ~lines 283-292) — instead of a harness-local
|
|
446
|
+
// `defaults.merge(child_props)` shortcut. `derive_vars_from_defaults`
|
|
447
|
+
// resolves each entry's `propName` (symbolized) against the live
|
|
448
|
+
// `child_props`, falling back to the static `value`; `isRestProps`
|
|
449
|
+
// entries pass the rest bag through. Caller props still win overall
|
|
450
|
+
// via the final `merge`.
|
|
451
|
+
lines.push(` extra = BarefootJS::Context.derive_vars_from_defaults(defaults_${snakeName}, child_props)`)
|
|
452
|
+
lines.push(` vars = child_props.merge(extra)`)
|
|
416
453
|
lines.push(` rendered = backend.render_named(${rubyStringLiteral(snakeName)}, child_bf, vars)`)
|
|
417
454
|
lines.push(` rendered.chomp`)
|
|
418
455
|
lines.push(`end)`)
|
|
@@ -430,23 +467,6 @@ function rubySymbol(name: string): string {
|
|
|
430
467
|
return /^[A-Za-z_][A-Za-z0-9_]*[?!]?$/.test(name) ? `:${name}` : `:"${name.replace(/"/g, '\\"')}"`
|
|
431
468
|
}
|
|
432
469
|
|
|
433
|
-
/**
|
|
434
|
-
* Simplify an `extractSsrDefaults` map to bare values for the child
|
|
435
|
-
* defaults JSON file. Each entry is either a bare value or
|
|
436
|
-
* `{ value, propName?, isRestProps? }`; the child renderer always merges
|
|
437
|
-
* the caller's live `child_props` OVER these defaults (`Hash#merge`), so
|
|
438
|
-
* only the static fallback `value` is needed here — the `propName`-aware
|
|
439
|
-
* resolution the production manifest path does is redundant with that
|
|
440
|
-
* merge.
|
|
441
|
-
*/
|
|
442
|
-
function simplifySsrDefaults(defaults: Record<string, SsrDefault>): Record<string, unknown> {
|
|
443
|
-
const out: Record<string, unknown> = {}
|
|
444
|
-
for (const [name, d] of Object.entries(defaults)) {
|
|
445
|
-
out[name] = d.value
|
|
446
|
-
}
|
|
447
|
-
return out
|
|
448
|
-
}
|
|
449
|
-
|
|
450
470
|
/**
|
|
451
471
|
* Convert PascalCase to snake_case for template naming (matches the
|
|
452
472
|
* adapter's `toTemplateName`).
|