@barefootjs/erb 0.31.8 → 0.31.9
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/vite.js +28 -0
- package/lib/barefoot_js.rb +13 -1
- package/package.json +5 -5
- package/src/__tests__/erb-adapter.test.ts +9 -5
- package/src/test-render.ts +46 -4
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", "''", '""', "``"]);
|
package/lib/barefoot_js.rb
CHANGED
|
@@ -125,11 +125,23 @@ module BarefootJS
|
|
|
125
125
|
props = _props
|
|
126
126
|
return '' unless props && !props.empty?
|
|
127
127
|
|
|
128
|
+
# Exclude the internal `scope_id` key from the client hydration
|
|
129
|
+
# payload: it is a server-side render detail (already carried by
|
|
130
|
+
# `_scope_id` for bf-s/bf-h/bf-m emission) with zero client runtime
|
|
131
|
+
# consumers -- the only bf-p parser (packages/client/src/runtime/
|
|
132
|
+
# hydrate.ts's parseProps -> runInit) never reads it back out.
|
|
133
|
+
# Filtered here, the single marshal boundary for bf-p, so it's
|
|
134
|
+
# excluded no matter how a `scope_id` key ends up in the props Hash
|
|
135
|
+
# (found via the bf-p semantic-comparison audit against the Hono
|
|
136
|
+
# reference adapter, which never emits it).
|
|
137
|
+
client_props = props.reject { |k, _| k.to_s == 'scope_id' }
|
|
138
|
+
return '' if client_props.empty?
|
|
139
|
+
|
|
128
140
|
# The JSON must be attribute-escaped: a raw `'` inside a string value
|
|
129
141
|
# (e.g. a blog paragraph) terminates the single-quoted attribute and
|
|
130
142
|
# truncates the hydration payload. The browser entity-decodes the
|
|
131
143
|
# attribute value, so the client's JSON.parse sees the original text.
|
|
132
|
-
json = html_escape(backend.encode_json(
|
|
144
|
+
json = html_escape(backend.encode_json(client_props))
|
|
133
145
|
%( bf-p='#{json}')
|
|
134
146
|
end
|
|
135
147
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/erb",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.9",
|
|
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,7 +54,7 @@
|
|
|
54
54
|
"directory": "packages/adapter-erb"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@barefootjs/shared": "0.31.
|
|
57
|
+
"@barefootjs/shared": "0.31.9"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@barefootjs/jsx": ">=0.2.0",
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
74
|
-
"@barefootjs/jsx": "0.31.
|
|
75
|
-
"@barefootjs/vite": "0.31.
|
|
76
|
-
"@barefootjs/client": "0.31.
|
|
74
|
+
"@barefootjs/jsx": "0.31.9",
|
|
75
|
+
"@barefootjs/vite": "0.31.9",
|
|
76
|
+
"@barefootjs/client": "0.31.9",
|
|
77
77
|
"typescript": "^5.0.0",
|
|
78
78
|
"vite": "^6.0.0"
|
|
79
79
|
}
|
|
@@ -479,11 +479,15 @@ export function TodoList(props: { initialTodos?: Todo[] }) {
|
|
|
479
479
|
// as a thrown "ruby render failed" error, so a `NoMethodError` string
|
|
480
480
|
// anywhere in a caught error would fail this test outright rather than
|
|
481
481
|
// reaching these assertions. Post-fix: only the not-done todo (id 2)
|
|
482
|
-
// survives the 'active' filter.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
482
|
+
// survives the 'active' filter. The bf-p hydration payload legitimately
|
|
483
|
+
// carries the full unfiltered `initialTodos` (both quote styles,
|
|
484
|
+
// mirroring `normalizeHTML`), so the assertions target the rendered
|
|
485
|
+
// list only.
|
|
486
|
+
const rendered = html.replace(/\s*bf-p=(?:"[^"]*"|'[^']*')/g, '')
|
|
487
|
+
expect(rendered).not.toContain('Eat breakfast')
|
|
488
|
+
expect(rendered).toContain('Write tests')
|
|
489
|
+
expect(rendered).toContain('data-key="2"')
|
|
490
|
+
expect(rendered).not.toContain('data-key="1"')
|
|
487
491
|
})
|
|
488
492
|
})
|
|
489
493
|
|
package/src/test-render.ts
CHANGED
|
@@ -264,8 +264,13 @@ export async function renderErbComponent(options: RenderOptions): Promise<string
|
|
|
264
264
|
// value domain is JSON-shaped symbol-keyed Hashes throughout, so
|
|
265
265
|
// `JSON.parse(..., symbolize_names: true)` on the Ruby side is the
|
|
266
266
|
// whole marshalling story; no hand-built Ruby literals.
|
|
267
|
-
const { obj: rootProps, needsSearchParams } = buildRubyProps(props, ir)
|
|
267
|
+
const { obj: rootProps, needsSearchParams, userProps } = buildRubyProps(props, ir)
|
|
268
268
|
await Bun.write(resolve(tempDir, 'props.json'), JSON.stringify(rootProps))
|
|
269
|
+
// Separate from `props.json`: the caller-facing subset only (no
|
|
270
|
+
// scope_id / signal / memo bookkeeping) — what `bf._props(...)` below
|
|
271
|
+
// feeds `props_attr`'s bf-p payload, matching what a production
|
|
272
|
+
// Sinatra route handler passes, not this harness's internal vars hash.
|
|
273
|
+
await Bun.write(resolve(tempDir, 'user_props.json'), JSON.stringify(userProps))
|
|
269
274
|
|
|
270
275
|
// Honour `__instanceId` from props for the root scope id so
|
|
271
276
|
// shared-component fixtures (which pin `<ComponentName>_test`) match
|
|
@@ -310,6 +315,16 @@ bf = BarefootJS::Context.new(backend)
|
|
|
310
315
|
bf._scope_id(${rootScopeId})
|
|
311
316
|
|
|
312
317
|
props = JSON.parse(File.read(File.join(__dir__, 'props.json')), symbolize_names: true)
|
|
318
|
+
# Mirrors production's \`props_attr\` contract (see barefoot_js.rb and
|
|
319
|
+
# props_attr_test.rb): the caller is expected to seed \`_props\` before
|
|
320
|
+
# rendering the root component, so this harness must call it explicitly.
|
|
321
|
+
#
|
|
322
|
+
# \`user_props.json\` — NOT \`props\` above — is what \`_props\` gets: \`props\`
|
|
323
|
+
# is this harness's internal vars hash (scope_id, signal/memo seed
|
|
324
|
+
# values), which a real Sinatra route handler never has and never passes.
|
|
325
|
+
# Production's bf-p carries neither scope_id nor signal state, only the
|
|
326
|
+
# caller-facing props.
|
|
327
|
+
bf._props(JSON.parse(File.read(File.join(__dir__, 'user_props.json')), symbolize_names: true))
|
|
313
328
|
${needsSearchParams ? "# (#1922) Request-scoped searchParams() env signal: bind the reserved\n# `search_params` vars key to an empty-query reader. Only when the\n# component imports `searchParams`.\nprops[:search_params] = bf.search_params('')\n" : ''}
|
|
314
329
|
${childRenderers}
|
|
315
330
|
html = backend.render_named(${rubyStringLiteral(toSnakeCase(componentName))}, bf, props)
|
|
@@ -491,12 +506,20 @@ function toSnakeCase(name: string): string {
|
|
|
491
506
|
|
|
492
507
|
/**
|
|
493
508
|
* Build the root props object (later JSON-serialised) + whether the
|
|
494
|
-
* component imports `searchParams
|
|
509
|
+
* component imports `searchParams`, PLUS `userProps` — the exact raw
|
|
510
|
+
* fixture props (no defaults, no null-fill, no signal/memo seeding),
|
|
511
|
+
* mirroring production's route-handler call `bf._props(props)` verbatim.
|
|
512
|
+
* `obj` stays the full internal vars hash the compiled template renders
|
|
513
|
+
* against (local-var-keyed, defaulted, signal/memo-seeded); `userProps`
|
|
514
|
+
* is for `bf._props(...)` (bf-p hydration payload) only — these must NOT
|
|
515
|
+
* be the same object, and `userProps` must NOT be derived from `obj` or
|
|
516
|
+
* from the defaulted stash: production's own bf-p carries only what the
|
|
517
|
+
* caller actually passed in, unmodified.
|
|
495
518
|
*/
|
|
496
519
|
function buildRubyProps(
|
|
497
520
|
props: Record<string, unknown> | undefined,
|
|
498
521
|
ir: ComponentIR,
|
|
499
|
-
): { obj: Record<string, unknown>; needsSearchParams: boolean } {
|
|
522
|
+
): { obj: Record<string, unknown>; needsSearchParams: boolean; userProps: Record<string, unknown> } {
|
|
500
523
|
const obj: Record<string, unknown> = {}
|
|
501
524
|
|
|
502
525
|
const explicitScope = typeof props?.__instanceId === 'string' ? props.__instanceId : 'test'
|
|
@@ -509,6 +532,10 @@ function buildRubyProps(
|
|
|
509
532
|
// (`propName`, e.g. `n` for `{ n: count }`) is honoured, not just the
|
|
510
533
|
// local template var name (#2524 SSR half). `props` is keyed by the
|
|
511
534
|
// caller-facing name, exactly what `propName` resolves against.
|
|
535
|
+
//
|
|
536
|
+
// This defaulted/derived stash feeds ONLY `obj` (the template-rendering
|
|
537
|
+
// vars hash) below — never `userProps` (the bf-p payload), which is
|
|
538
|
+
// built separately, straight off the raw `props` argument.
|
|
512
539
|
const rootSsrDefaults = extractSsrDefaults(ir.metadata) ?? {}
|
|
513
540
|
const derivedProps = deriveStashFromDefaults(rootSsrDefaults, props ?? {})
|
|
514
541
|
for (const param of ir.metadata.propsParams) {
|
|
@@ -558,6 +585,21 @@ function buildRubyProps(
|
|
|
558
585
|
}
|
|
559
586
|
}
|
|
560
587
|
|
|
588
|
+
// `bf._props(...)` payload (bf-p hydration): mirrors production's
|
|
589
|
+
// route-handler call `bf._props(props)` verbatim — the caller's raw
|
|
590
|
+
// props dict, unmodified. No default-filling, no signal/memo seeding,
|
|
591
|
+
// no rest-bag nesting: a real Sinatra/Rack handler never rearranges
|
|
592
|
+
// the dict it received before handing it to `_props`. Excludes
|
|
593
|
+
// internal harness-only keys (`__instanceId` etc.), which are never a
|
|
594
|
+
// real caller-facing prop.
|
|
595
|
+
const userProps: Record<string, unknown> = {}
|
|
596
|
+
if (props) {
|
|
597
|
+
for (const [key, value] of Object.entries(props)) {
|
|
598
|
+
if (key.startsWith('__')) continue
|
|
599
|
+
userProps[key] = value
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
561
603
|
// Signal values evaluated from props (after user props).
|
|
562
604
|
for (const signal of ir.metadata.signals) {
|
|
563
605
|
// Env signals (#2057 / #1922) are bound below via `search_params('')`,
|
|
@@ -577,6 +619,6 @@ function buildRubyProps(
|
|
|
577
619
|
|
|
578
620
|
const needsSearchParams = importsSearchParams(ir.metadata)
|
|
579
621
|
|
|
580
|
-
return { obj, needsSearchParams }
|
|
622
|
+
return { obj, needsSearchParams, userProps }
|
|
581
623
|
}
|
|
582
624
|
|