@barefootjs/erb 0.17.0 → 0.18.1
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/adapter/index.js +1 -1
- package/dist/build.js +1 -1
- package/dist/index.js +1 -1
- package/lib/barefoot_js.rb +9 -1
- package/package.json +3 -3
- 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
|
package/dist/adapter/index.js
CHANGED
|
@@ -18568,7 +18568,7 @@ https://github.com/browserify/crypto-browserify`);
|
|
|
18568
18568
|
|
|
18569
18569
|
// ../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/typescript.js
|
|
18570
18570
|
var require_typescript = __commonJS((exports, module) => {
|
|
18571
|
-
var __dirname = "/
|
|
18571
|
+
var __dirname = "/home/runner/work/barefootjs/barefootjs/node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib", __filename = "/home/runner/work/barefootjs/barefootjs/node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/typescript.js";
|
|
18572
18572
|
/*! *****************************************************************************
|
|
18573
18573
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
18574
18574
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
package/dist/build.js
CHANGED
|
@@ -18568,7 +18568,7 @@ https://github.com/browserify/crypto-browserify`);
|
|
|
18568
18568
|
|
|
18569
18569
|
// ../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/typescript.js
|
|
18570
18570
|
var require_typescript = __commonJS((exports, module) => {
|
|
18571
|
-
var __dirname = "/
|
|
18571
|
+
var __dirname = "/home/runner/work/barefootjs/barefootjs/node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib", __filename = "/home/runner/work/barefootjs/barefootjs/node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/typescript.js";
|
|
18572
18572
|
/*! *****************************************************************************
|
|
18573
18573
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
18574
18574
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
package/dist/index.js
CHANGED
|
@@ -18568,7 +18568,7 @@ https://github.com/browserify/crypto-browserify`);
|
|
|
18568
18568
|
|
|
18569
18569
|
// ../../node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/typescript.js
|
|
18570
18570
|
var require_typescript = __commonJS((exports, module) => {
|
|
18571
|
-
var __dirname = "/
|
|
18571
|
+
var __dirname = "/home/runner/work/barefootjs/barefootjs/node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib", __filename = "/home/runner/work/barefootjs/barefootjs/node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/typescript.js";
|
|
18572
18572
|
/*! *****************************************************************************
|
|
18573
18573
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
18574
18574
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
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.
|
|
3
|
+
"version": "0.18.1",
|
|
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.
|
|
57
|
+
"@barefootjs/shared": "0.18.1"
|
|
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.
|
|
64
|
+
"@barefootjs/jsx": "0.18.1",
|
|
65
65
|
"typescript": "^5.0.0"
|
|
66
66
|
}
|
|
67
67
|
}
|
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`).
|