@barefootjs/blade 0.1.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/README.md +73 -0
- package/dist/adapter/analysis/component-tree.d.ts +26 -0
- package/dist/adapter/analysis/component-tree.d.ts.map +1 -0
- package/dist/adapter/blade-adapter.d.ts +537 -0
- package/dist/adapter/blade-adapter.d.ts.map +1 -0
- package/dist/adapter/boolean-result.d.ts +76 -0
- package/dist/adapter/boolean-result.d.ts.map +1 -0
- package/dist/adapter/emit-context.d.ts +105 -0
- package/dist/adapter/emit-context.d.ts.map +1 -0
- package/dist/adapter/expr/array-method.d.ts +74 -0
- package/dist/adapter/expr/array-method.d.ts.map +1 -0
- package/dist/adapter/expr/emitters.d.ts +176 -0
- package/dist/adapter/expr/emitters.d.ts.map +1 -0
- package/dist/adapter/expr/operand.d.ts +25 -0
- package/dist/adapter/expr/operand.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +6 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +189060 -0
- package/dist/adapter/lib/blade-naming.d.ts +128 -0
- package/dist/adapter/lib/blade-naming.d.ts.map +1 -0
- package/dist/adapter/lib/constants.d.ts +21 -0
- package/dist/adapter/lib/constants.d.ts.map +1 -0
- package/dist/adapter/lib/ir-scope.d.ts +48 -0
- package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
- package/dist/adapter/lib/types.d.ts +27 -0
- package/dist/adapter/lib/types.d.ts.map +1 -0
- package/dist/adapter/memo/seed.d.ts +84 -0
- package/dist/adapter/memo/seed.d.ts.map +1 -0
- package/dist/adapter/props/prop-classes.d.ts +34 -0
- package/dist/adapter/props/prop-classes.d.ts.map +1 -0
- package/dist/adapter/spread/spread-codegen.d.ts +72 -0
- package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
- package/dist/adapter/value/parsed-literal.d.ts +27 -0
- package/dist/adapter/value/parsed-literal.d.ts.map +1 -0
- package/dist/build.d.ts +28 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +189080 -0
- package/dist/conformance-pins.d.ts +12 -0
- package/dist/conformance-pins.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +189079 -0
- package/package.json +67 -0
- package/php/composer.json +32 -0
- package/php/src/BladeBackend.php +197 -0
- package/php/src/naming.php +84 -0
- package/php/tests/test_render.php +159 -0
- package/src/__tests__/blade-adapter-unit.test.ts +392 -0
- package/src/__tests__/blade-adapter.test.ts +52 -0
- package/src/__tests__/blade-counter.test.ts +63 -0
- package/src/__tests__/blade-query-href.test.ts +113 -0
- package/src/__tests__/blade-spread-attrs.test.ts +235 -0
- package/src/adapter/analysis/component-tree.ts +119 -0
- package/src/adapter/blade-adapter.ts +1912 -0
- package/src/adapter/boolean-result.ts +168 -0
- package/src/adapter/emit-context.ts +117 -0
- package/src/adapter/expr/array-method.ts +345 -0
- package/src/adapter/expr/emitters.ts +636 -0
- package/src/adapter/expr/operand.ts +35 -0
- package/src/adapter/index.ts +6 -0
- package/src/adapter/lib/blade-naming.ts +180 -0
- package/src/adapter/lib/constants.ts +33 -0
- package/src/adapter/lib/ir-scope.ts +83 -0
- package/src/adapter/lib/types.ts +30 -0
- package/src/adapter/memo/seed.ts +135 -0
- package/src/adapter/props/prop-classes.ts +66 -0
- package/src/adapter/spread/spread-codegen.ts +179 -0
- package/src/adapter/value/parsed-literal.ts +75 -0
- package/src/build.ts +37 -0
- package/src/conformance-pins.ts +86 -0
- package/src/index.ts +9 -0
- package/src/test-render.ts +782 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @barefootjs/blade
|
|
2
|
+
|
|
3
|
+
Laravel Blade (PHP) adapter for BarefootJS: compiles the BarefootJS IR
|
|
4
|
+
(JSX → IR, see `spec/compiler.md`) into `.blade.php` template files plus the
|
|
5
|
+
client JS bundle every other adapter produces, and ships a PHP rendering
|
|
6
|
+
runtime (`php/src/`) that renders those templates through `illuminate/view`
|
|
7
|
+
used standalone — no Laravel application/container is required.
|
|
8
|
+
|
|
9
|
+
Near-mechanical port of `@barefootjs/twig` (the Twig adapter) to Blade
|
|
10
|
+
syntax. See `src/adapter/blade-adapter.ts`'s header comment for the full
|
|
11
|
+
Twig↔Blade syntax-mapping table and the JS/PHP semantics divergences this
|
|
12
|
+
port handles uniformly (truthiness, stringification, reserved-word
|
|
13
|
+
identifier mangling, `$bf->eq`/`$bf->neq` for JS strict equality, `data_get`
|
|
14
|
+
for polymorphic member/index access, and the evaluator-only higher-order-
|
|
15
|
+
callback lowering since Blade has no lambda expression).
|
|
16
|
+
|
|
17
|
+
## Template output shape
|
|
18
|
+
|
|
19
|
+
- `name: 'blade'`, `extension: '.blade.php'`, `templatesPerComponent: true` —
|
|
20
|
+
one `.blade.php` file per component, named by snake-casing the PascalCase
|
|
21
|
+
component name (`UserCard` → `user_card.blade.php`).
|
|
22
|
+
- Hydration markers (`bf-s`, `bf-h`/`bf-m`/`bf-r`, `bf-p`, slot/conditional
|
|
23
|
+
comment markers, loop boundary comments) use the SAME runtime method
|
|
24
|
+
names as every other adapter's `bf.*` calls, spelled as PHP method calls
|
|
25
|
+
on the `$bf` variable (`$bf->scope_attr()`, `$bf->hydration_attrs()`,
|
|
26
|
+
`$bf->text_start`/`text_end`, `$bf->comment(...)`, …) — see
|
|
27
|
+
`spec/template-helpers.md` for the shared helper contract.
|
|
28
|
+
- Every text/attribute interpolation of a possibly-non-string value is
|
|
29
|
+
routed through `$bf->string(...)` (or `$bf->bool_str(...)` for
|
|
30
|
+
boolean-shaped values); every non-comparison condition position is
|
|
31
|
+
routed through `$bf->truthy(...)`; every JS `===`/`!==` comparison routes
|
|
32
|
+
through `$bf->eq(...)`/`$bf->neq(...)` (PHP's own `==`/`===` are either
|
|
33
|
+
loose or number-representation-sensitive in ways that diverge from JS
|
|
34
|
+
strict equality). All are pure PHP-runtime helpers — see the PHP runtime
|
|
35
|
+
pointer below.
|
|
36
|
+
|
|
37
|
+
## PHP runtime
|
|
38
|
+
|
|
39
|
+
`php/src/` is a self-contained PHP package (dependencies: `illuminate/view`
|
|
40
|
+
and `barefootjs/runtime`) implementing the engine-agnostic `bf` object
|
|
41
|
+
every emitted template calls into: hydration markers, context propagation
|
|
42
|
+
(`provide_context`/`use_context`), child-component rendering
|
|
43
|
+
(`render_child`), script registration, and the JS-compatible helper
|
|
44
|
+
library (`string`, `bool_str`, `truthy`, `number`, `floor`/`ceil`/`round`,
|
|
45
|
+
array/string helpers, the `*_eval` evaluator helpers, `spread_attrs`,
|
|
46
|
+
`query`, `eq`/`neq`, …). It mirrors the layering of
|
|
47
|
+
`packages/adapter-perl/lib/` (an engine-agnostic core plus a thin
|
|
48
|
+
per-engine backend) — see `spec/template-helpers.md` for the semantic
|
|
49
|
+
contract each helper must satisfy, and the package's own tests under
|
|
50
|
+
`php/tests/`.
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// barefoot.config.ts
|
|
56
|
+
import { createConfig } from '@barefootjs/blade/build'
|
|
57
|
+
|
|
58
|
+
export default createConfig({
|
|
59
|
+
components: ['./src/components'],
|
|
60
|
+
outDir: './dist',
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`bf build` then emits `.blade.php` templates + client JS under `outDir`. A
|
|
65
|
+
PHP host renders a component by constructing an `illuminate/view` `Factory`
|
|
66
|
+
standalone (`Filesystem` + event `Dispatcher` + `EngineResolver` registering
|
|
67
|
+
a `blade` engine over a `BladeCompiler` + `FileViewFinder`, all wired
|
|
68
|
+
together by the `Factory` — see `php/src/BladeBackend.php`'s constructor)
|
|
69
|
+
pointed at the emitted templates, wiring in the PHP `BladeBackend` as the
|
|
70
|
+
render backend. Blade's `{{ }}` echo (`Illuminate\Support\e()`) emits named
|
|
71
|
+
HTML entity forms (`"`/`'` via `ENT_QUOTES`) where Perl/Go/
|
|
72
|
+
markupsafe emit the numeric `"`/`'` forms — see the adapter header
|
|
73
|
+
comment for how that byte-form difference is handled.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component-tree analysis for the Blade template adapter.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `packages/adapter-jinja/src/adapter/analysis/component-tree.ts`.
|
|
5
|
+
* Pure functions over the IR — they read no adapter instance state.
|
|
6
|
+
* `collectImportedLoopChildComponentErrors` returns its diagnostics instead
|
|
7
|
+
* of pushing onto the adapter's error list, so the adapter stays the sole
|
|
8
|
+
* owner of `errors`.
|
|
9
|
+
*/
|
|
10
|
+
import type { ComponentIR, CompilerError } from '@barefootjs/jsx';
|
|
11
|
+
/**
|
|
12
|
+
* Whether the component needs the client runtime — it owns reactive state
|
|
13
|
+
* (signals / effects / onMount) or the analyzer flagged it as needing init.
|
|
14
|
+
*/
|
|
15
|
+
export declare function hasClientInteractivity(ir: ComponentIR): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Build a `BF103` diagnostic for every component reference inside a loop body
|
|
18
|
+
* whose name is imported from a relative-path module. Mirror of the Go /
|
|
19
|
+
* Jinja adapter's check — the Blade adapter has the same
|
|
20
|
+
* cross-template-registration constraint at request time (each `.blade.php`
|
|
21
|
+
* component file must be registered with the shared Blade `Environment`
|
|
22
|
+
* loader alongside the parent). Returns the diagnostics so the caller pushes
|
|
23
|
+
* them onto its own error list.
|
|
24
|
+
*/
|
|
25
|
+
export declare function collectImportedLoopChildComponentErrors(ir: ComponentIR, componentName: string): CompilerError[];
|
|
26
|
+
//# sourceMappingURL=component-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-tree.d.ts","sourceRoot":"","sources":["../../../src/adapter/analysis/component-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,WAAW,EAUX,aAAa,EACd,MAAM,iBAAiB,CAAA;AAExB;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAO/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uCAAuC,CACrD,EAAE,EAAE,WAAW,EACf,aAAa,EAAE,MAAM,GACpB,aAAa,EAAE,CAqEjB"}
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BarefootJS Blade (PHP) Template Adapter
|
|
3
|
+
*
|
|
4
|
+
* Generates Laravel Blade template files (.blade.php) from BarefootJS IR.
|
|
5
|
+
*
|
|
6
|
+
* Near-mechanical port of the Twig (PHP) adapter
|
|
7
|
+
* (packages/adapter-twig/src/adapter/twig-adapter.ts) — itself a
|
|
8
|
+
* near-mechanical port of the Jinja2 adapter — from Twig syntax to Blade
|
|
9
|
+
* syntax. Both Twig and Blade render through PHP, so most of the JS/PHP
|
|
10
|
+
* semantic divergences documented in the Twig port CARRY OVER unchanged
|
|
11
|
+
* (truthiness, stringification, `bf`→`$bf->`-method-call routing, evaluator-
|
|
12
|
+
* JSON callbacks, `===`/`!==` via `eq`/`neq`, reserved-word mangling,
|
|
13
|
+
* in-template self-reference seeding). What's DIFFERENT is the surrounding
|
|
14
|
+
* template syntax — Blade compiles straight to raw PHP rather than its own
|
|
15
|
+
* expression language, so there is no `strict_variables: false` engine-wide
|
|
16
|
+
* forgiveness, no polymorphic `.`/`[]`, and every variable carries PHP's `$`
|
|
17
|
+
* sigil:
|
|
18
|
+
*
|
|
19
|
+
* Twig `{{ EXPR }}` (escaped) → Blade `{!! e(EXPR) !!}` — NOT bare `{{ EXPR }}` (see divergence 0 below: Blade's OWN `{{ }}` would be the natural mapping and DOES compile to `echo e(EXPR)`, but its compiler's regex-based tag matching breaks on evaluator-JSON payload text, so every escaped interpolation in THIS adapter's output explicitly spells out the same `e(...)` call under the `{!! !!}` delimiter instead)
|
|
20
|
+
* Twig `{{ EXPR | raw }}` → Blade `{!! EXPR !!}`
|
|
21
|
+
* Twig `bf.method(args)` → Blade `$bf->method(args)`
|
|
22
|
+
* Twig bare var `name` → Blade `$name` (mangled via `bladeIdent`/`bladeVar` — see divergence 1)
|
|
23
|
+
* Twig `{% if C %}…{% elseif %}…{% else %}…{% endif %}` → Blade `@if(C)…@elseif(…)…@else…@endif`
|
|
24
|
+
* Twig `{% for x in arr %}…{% endfor %}` → Blade `@foreach((arr ?? []) as $x)…@endforeach` (see divergence 2: raw PHP `foreach` over `null` warns, Twig's `strict_variables: false` didn't)
|
|
25
|
+
* Twig `{% set x = e %}` → Blade `@php($x = e)`
|
|
26
|
+
* Twig `{'k': v}` hash → Blade `['k' => v]` PHP array literal (still ALWAYS-quoted keys — see `lib/blade-naming.ts`)
|
|
27
|
+
* Twig `~` concat → Blade `.` concat (same `$bf->string(...)` routing policy)
|
|
28
|
+
* Twig `(t ? a : b)` ternary → Blade same (PHP ternary, unchanged syntax)
|
|
29
|
+
* Twig `l ?? r` → Blade same (PHP `??` also silences undefined-variable/index — see divergence 3)
|
|
30
|
+
* Twig `bf.eq`/`bf.neq` for `===`/`!==` → Blade `$bf->eq`/`$bf->neq` — NEVER PHP `==` (loose) nor `===` (`1 === 1.0` is `false` in PHP)
|
|
31
|
+
* Twig `bf.truthy` in test positions → Blade `$bf->truthy(...)` — same policy (PHP falsiness diverges from JS)
|
|
32
|
+
* Twig `{% set NAME %}…{% endset %}` capture → Blade output buffering: `@php(ob_start())` … content … `@php($NAME = $bf->backend->mark_raw(preg_replace('/\n\z/', '', ob_get_clean(), 1)))` (see divergence 4)
|
|
33
|
+
* Twig `.twig` files, `<snake_case>.twig` → Blade `.blade.php` files, `<snake_case>.blade.php` (`extension = '.blade.php'`)
|
|
34
|
+
* Twig `a.b` / `a[i]` member/index access → Blade `data_get($a, 'b')` / `data_get($a, $i)` (see divergence 5 — the one place Twig's polymorphic `.` has no raw-PHP equivalent)
|
|
35
|
+
* Twig evaluator-JSON `*_eval` payloads for callbacks → UNCHANGED — same mechanism, same shared `Barefoot\Evaluator`
|
|
36
|
+
*
|
|
37
|
+
* The render harness this adapter's output assumes: a Blade `Factory` wired
|
|
38
|
+
* from illuminate/view standalone (`Filesystem`, event `Dispatcher`,
|
|
39
|
+
* `EngineResolver` registering a `blade` engine over a `BladeCompiler`, a
|
|
40
|
+
* `FileViewFinder`, tied together by the `Factory`) — see
|
|
41
|
+
* `php/src/BladeBackend.php`'s constructor and file header for the exact
|
|
42
|
+
* wiring and the `Illuminate\Support\e()`/`Htmlable` escaping contract.
|
|
43
|
+
* Templates are named `<snake_case_component>.blade.php` (same convention as
|
|
44
|
+
* Twig's `.twig` files).
|
|
45
|
+
*
|
|
46
|
+
* **Whitespace policy.** Blade directives compile to raw `<?php … ?>` tags
|
|
47
|
+
* spliced into the surrounding template text — e.g. `@if(C)` compiles to
|
|
48
|
+
* `<?php if(C): ?>`. PHP itself has a well-known parser quirk: a `?>`
|
|
49
|
+
* closing tag immediately followed by exactly one newline has that ONE
|
|
50
|
+
* newline SWALLOWED (never emitted), independent of Blade. Verified
|
|
51
|
+
* empirically (`packages/adapter-blade/php`, standalone illuminate/view):
|
|
52
|
+
* rendering a template with `@if`/`@elseif`/`@else`/`@endif`,
|
|
53
|
+
* `@foreach`/`@endforeach`, and `@php(...)` lines EACH on their own source
|
|
54
|
+
* line at column 0 produces NO leaked blank lines around the directives —
|
|
55
|
+
* byte-identical in shape to Twig's `trim_blocks` behavior, but achieved for
|
|
56
|
+
* free via PHP's own tag-adjacent-newline rule rather than a Blade-specific
|
|
57
|
+
* feature. This gives the exact same uniform policy the Twig port already
|
|
58
|
+
* follows: **emit every directive at column 0 of its own source line, never
|
|
59
|
+
* introduce leading indentation before `@`.** No Blade-specific whitespace-
|
|
60
|
+
* control syntax is needed anywhere in this adapter.
|
|
61
|
+
*
|
|
62
|
+
* Divergences beyond the syntax table above (all uniform, not per-fixture —
|
|
63
|
+
* see the individual definition sites for the full rationale):
|
|
64
|
+
*
|
|
65
|
+
* 0. **CRITICAL — every escaped interpolation uses `{!! e(EXPR) !!}`, NEVER
|
|
66
|
+
* Blade's own `{{ EXPR }}`.** Discovered empirically while porting the
|
|
67
|
+
* evaluator-JSON callback fixtures (`.reduce`/`.sort`/`.filter` etc. —
|
|
68
|
+
* divergence 8 below): `Illuminate\View\Compilers\Concerns\
|
|
69
|
+
* CompilesEchos::compileRegularEchos` finds a `{{ … }}` pair with the
|
|
70
|
+
* regex `/\{\{\s*(.+?)\s*\}\}(\r?\n)?/s` — a NON-GREEDY match against
|
|
71
|
+
* the RAW TEMPLATE TEXT, with NO awareness of PHP string-literal
|
|
72
|
+
* quoting. A serialized-`ParsedExpr` JSON payload for a NESTED
|
|
73
|
+
* expression (e.g. `x => acc * x.qty`'s AST) routinely contains a
|
|
74
|
+
* literal `}}` substring where one JSON object closes immediately
|
|
75
|
+
* inside another (`...,"property":"qty"}}`) — even though that `}}` is
|
|
76
|
+
* safely inside a single-quoted PHP string literal, Blade's regex does
|
|
77
|
+
* not know that and terminates the `{{ … }}` tag AT THE FIRST `}}` IT
|
|
78
|
+
* SEES, truncating the compiled PHP expression mid-string and producing
|
|
79
|
+
* a syntax error (verified empirically — `BladeCompiler::compileString`
|
|
80
|
+
* on `{{ $bf->string($bf->reduce_eval($items, '{"kind":"binary",
|
|
81
|
+
* ...,"property":"qty"}}', ...)) }}` compiles to an unclosed-paren
|
|
82
|
+
* fragment ending right after the first `}}`, confirmed via
|
|
83
|
+
* `php -l` on the compiled output). Blade's raw-echo delimiter
|
|
84
|
+
* (`{!! … !!}`) uses `!!}` as its closer, which this adapter's payload
|
|
85
|
+
* text never produces (JSON output only ever contains `{`/`}`/quotes/
|
|
86
|
+
* alphanumerics/punctuation, never `!`), so it is IMMUNE to this
|
|
87
|
+
* failure mode. This adapter therefore NEVER emits Blade's own
|
|
88
|
+
* escaped-echo delimiter — every position that needs HTML-escaped
|
|
89
|
+
* output calls PHP's global `e()` helper (the SAME function `{{ }}`
|
|
90
|
+
* would have called internally, imported for free via
|
|
91
|
+
* `illuminate/support`'s composer "files" autoload — see
|
|
92
|
+
* `BladeBackend.php`'s header for the `Htmlable` pass-through
|
|
93
|
+
* contract, identical either way) explicitly under `{!! !!}` instead.
|
|
94
|
+
* This is NOT a narrow, evaluator-only special case — it is applied
|
|
95
|
+
* UNIFORMLY at every interpolation site in this file (text
|
|
96
|
+
* expressions, attribute values, boolean-attribute ternaries, style
|
|
97
|
+
* object values, the scope-id marker, template-literal attrs, provider
|
|
98
|
+
* value propagation), because any one of them could in principle carry
|
|
99
|
+
* an evaluator-JSON payload transitively, and a single missed
|
|
100
|
+
* `{{ }}` site would silently reintroduce this failure mode for
|
|
101
|
+
* whatever future fixture happens to nest two callback bodies deep
|
|
102
|
+
* enough to trigger it.
|
|
103
|
+
* 1. **`$` sigil on every variable reference.** Twig identifiers have no
|
|
104
|
+
* sigil; Blade compiles to raw PHP, where every variable — read OR
|
|
105
|
+
* `@php(...)` assignment target — is `$name`. `bladeIdent(name)`
|
|
106
|
+
* (`lib/blade-naming.ts`) mirrors `twigIdent`'s mangling-only role
|
|
107
|
+
* (bare name, no `$`); `bladeVar(name)` is the NEW wrapper used at
|
|
108
|
+
* every actual variable-reference emission site. See
|
|
109
|
+
* `expr/emitters.ts`'s file header for the full rundown of every
|
|
110
|
+
* emitter call site this affects.
|
|
111
|
+
* 2. **`@foreach` needs an explicit null/undefined guard Twig didn't.**
|
|
112
|
+
* Twig's `{% for %}` over `strict_variables: false` tolerated an
|
|
113
|
+
* unbound/`null` array (zero iterations, no error). Raw PHP's
|
|
114
|
+
* `foreach (null as $x)` raises a `TypeError` (PHP 8) /
|
|
115
|
+
* `Warning: Invalid argument` (older PHP) — verified empirically. This
|
|
116
|
+
* adapter therefore ALWAYS wraps the loop array in `(EXPR ?? [])`,
|
|
117
|
+
* matching Twig's tolerance uniformly rather than per-fixture.
|
|
118
|
+
* 3. **`??` is PHP-native and REPLACES Twig's `strict_variables: false`
|
|
119
|
+
* forgiveness with a per-use-site policy.** PHP's `??` silences BOTH
|
|
120
|
+
* an undefined-variable/-array-index notice AND a real `null` in ONE
|
|
121
|
+
* operator (verified empirically: `$missing ?? 'fb'` and `$x ?? 'fb'`
|
|
122
|
+
* with `$x = null` both yield `'fb'`, no warning) — same coverage
|
|
123
|
+
* Twig's native `??` gave (this adapter's divergence 3 in the Twig
|
|
124
|
+
* port), but Blade has no engine-wide switch equivalent to
|
|
125
|
+
* `strict_variables: false`; every place a possibly-unbound var is
|
|
126
|
+
* read (nullable-optional-prop guards, the `Record[key]` lookup
|
|
127
|
+
* fallback) uses `??`/`isset()` explicitly at that site instead — see
|
|
128
|
+
* divergence 6 below for the SEPARATE "omit the attribute entirely"
|
|
129
|
+
* case `??` alone doesn't cover.
|
|
130
|
+
* 4. **Children/fallback capture via output buffering, not a set-block —
|
|
131
|
+
* and NEVER Blade's bare `@php … @endphp` block form.** Blade has no
|
|
132
|
+
* Twig-set-block equivalent that captures arbitrary rendered TEMPLATE
|
|
133
|
+
* OUTPUT (as opposed to a single PHP expression's value) into a
|
|
134
|
+
* variable. This adapter uses ONE uniform mechanism instead — PHP's
|
|
135
|
+
* own output buffering: `@php(ob_start())` … the captured body,
|
|
136
|
+
* rendered normally (so any interpolation inside is escaped exactly as
|
|
137
|
+
* if it rendered inline) …
|
|
138
|
+
* `@php($NAME = $bf->backend->mark_raw(preg_replace('/\n\z/', '', ob_get_clean(), 1)))`.
|
|
139
|
+
* Two things beyond the ob_start/ob_get_clean shape itself:
|
|
140
|
+
* (a) the trailing `preg_replace('/\n\z/', '', …, 1)` strips exactly
|
|
141
|
+
* the ONE newline this adapter's own template-string builder
|
|
142
|
+
* inserts between the captured body and the closing `@php(...)`
|
|
143
|
+
* line (needed so the directive is preceded by a non-word
|
|
144
|
+
* character — Blade's directive regex is `\B@word`, which
|
|
145
|
+
* requires the '@' NOT be glued directly onto a preceding word
|
|
146
|
+
* character) — without this trim, that synthetic newline is
|
|
147
|
+
* itself part of the captured buffer and leaks into the
|
|
148
|
+
* rendered HTML as extra whitespace right before the next
|
|
149
|
+
* sibling/closing tag (caught by the adapter-conformance suite:
|
|
150
|
+
* `<h2>Create New Task</h2>` rendering as `...Task </h2>` — a
|
|
151
|
+
* newline that `normalizeHTML` collapses to a visible trailing
|
|
152
|
+
* space). The trim is bounded to exactly one synthetic
|
|
153
|
+
* newline, not `rtrim`-style — content the caller genuinely
|
|
154
|
+
* renders with real trailing newlines keeps them.
|
|
155
|
+
* (b) this adapter uses ONLY the single-expression `@php(EXPR)`
|
|
156
|
+
* directive form, NEVER the bare `@php` / `@endphp` BLOCK form,
|
|
157
|
+
* anywhere in its emitted templates — verified empirically that
|
|
158
|
+
* `BladeCompiler::compileString` mishandles a `@php(EXPR)`
|
|
159
|
+
* occurrence EARLIER in the same template when a bare
|
|
160
|
+
* `@php ... @endphp` block appears LATER in it (the directive-
|
|
161
|
+
* matching regex's paren-balancing recovery walk gets confused
|
|
162
|
+
* by the later bare `@php` token, leaving `@php(EXPR)` OR its
|
|
163
|
+
* inner expression malformed/uncompiled). Since this adapter
|
|
164
|
+
* cannot control what OTHER templates its output might be
|
|
165
|
+
* concatenated/co-rendered alongside, every capture site stays
|
|
166
|
+
* on the single-expression form exclusively, sidestepping the
|
|
167
|
+
* whole class of mixed-form parsing quirks.
|
|
168
|
+
* `mark_raw` wraps the captured string in an `HtmlString`, so a later
|
|
169
|
+
* plain `{{ $NAME }}` echo does NOT re-escape it (verified empirically
|
|
170
|
+
* — same "capture once, emit raw once" contract Twig's set-block gave,
|
|
171
|
+
* see `BladeBackend.php`'s header for why `Illuminate\Support\e()`
|
|
172
|
+
* makes this work). Every set-block capture site in the Twig port
|
|
173
|
+
* (`renderComponent`'s children forward, `renderAsync`'s fallback)
|
|
174
|
+
* maps onto this ONE mechanism here too — always invoked immediately,
|
|
175
|
+
* in place, with zero arguments, never reused/invoked lazily.
|
|
176
|
+
* 5. **Member/index access lowers to `data_get(...)`, not `.`/`[]`.**
|
|
177
|
+
* Twig's `.`/`[]` are polymorphic over {stdClass, array, null} under
|
|
178
|
+
* `strict_variables: false`; raw PHP has NO single operator with that
|
|
179
|
+
* property (`$x->prop` fatals on a non-object, `$x['key']` warns on
|
|
180
|
+
* `null`/scalar). This adapter uses Laravel's `data_get($target, $key)`
|
|
181
|
+
* (from `illuminate/support`, already a transitive dependency of
|
|
182
|
+
* `illuminate/view` — no NEW runtime dependency, no change to
|
|
183
|
+
* `packages/adapter-php`) uniformly for both member access (`a.b` →
|
|
184
|
+
* `data_get($a, 'b')`) and index access (`a[i]` → `data_get($a, $i)`)
|
|
185
|
+
* — verified null-safe over the SAME {stdClass, array, null} value
|
|
186
|
+
* shapes Twig's dot/bracket cover (see `lib/blade-naming.ts`'s
|
|
187
|
+
* `bladeMemberAccess`/`bladeIndexAccess` docstrings for the verification
|
|
188
|
+
* transcript). This is the ONE place this port required a genuinely new
|
|
189
|
+
* lowering strategy rather than a mechanical syntax substitution.
|
|
190
|
+
* 6. **Nullable-optional-prop attribute omission uses `isset($x)`, a
|
|
191
|
+
* SINGLE check — simpler than Twig's TWO-part `is defined and is not
|
|
192
|
+
* null`.** Twig needed both tests because `is defined` and `is not
|
|
193
|
+
* none` are separate concerns under `strict_variables: false`. PHP's
|
|
194
|
+
* `isset($x)` already returns `false` for BOTH "never extracted into
|
|
195
|
+
* scope" AND "extracted as `null`" in one call (verified empirically —
|
|
196
|
+
* `isset($rows)` is `false` whether `$rows` was omitted from the
|
|
197
|
+
* render-vars array entirely or passed as `null`; `true` only when
|
|
198
|
+
* bound to a non-null value) — so this adapter's `elementAttrEmitter`
|
|
199
|
+
* guards with `@if(isset($x)) … @endif` instead of a two-part test.
|
|
200
|
+
* 7. **`===`/`!==` route through `$bf->eq`/`$bf->neq`, NEVER PHP's own
|
|
201
|
+
* `==`/`!=`/`===`/`!==`.** Unchanged from the Twig port's divergence 4
|
|
202
|
+
* — PHP's `==` is loose (`'1' == 1` is `true`), and PHP's OWN `===` is
|
|
203
|
+
* wrong the OTHER direction (`1 === 1.0` is `false` in PHP; JS has one
|
|
204
|
+
* number type, so `1 === 1.0` is `true`). See `expr/emitters.ts`'s file
|
|
205
|
+
* header for the full rationale.
|
|
206
|
+
* 8. **No Blade lambda** (`expr/emitters.ts` header, divergence 9).
|
|
207
|
+
* Unchanged from Twig — every higher-order callback routes through the
|
|
208
|
+
* evaluator-JSON `*_eval` payload; an unserializable predicate surfaces
|
|
209
|
+
* `BF101`. `.sort`'s non-lambda STRUCTURED fallback (`$bf->sort` with a
|
|
210
|
+
* `['keys' => […]]` descriptor) is unaffected and ports unchanged.
|
|
211
|
+
* 9. **Reserved-word mangling set is COMPLETELY DIFFERENT from Twig's, for
|
|
212
|
+
* a different reason** (`lib/blade-naming.ts`). Twig keywords (`for`,
|
|
213
|
+
* `filter`, `if`, …) collide with TWIG'S OWN grammar — irrelevant here,
|
|
214
|
+
* since Blade template vars are real PHP variables bound via
|
|
215
|
+
* `extract()` (`$for`, `$filter` are perfectly legal). What collides
|
|
216
|
+
* instead is the Blade/illuminate RENDER-TIME PHP SCOPE: `bf` (the
|
|
217
|
+
* runtime binding), `this` (PHP forbids `$this` reassignment),
|
|
218
|
+
* `__env`/`__data`/`__path` (illuminate/view internals), `app`
|
|
219
|
+
* (conventional container binding), `loop` (Blade's own `@foreach`
|
|
220
|
+
* `$loop`). `blade_ident()` in PHP (`php/src/naming.php`);
|
|
221
|
+
* `BladeBackend::ident()` delegates to it. Mirrored exactly by
|
|
222
|
+
* `bladeIdent`/`RESERVED_WORDS` on the TS side, with a parity test on
|
|
223
|
+
* each side (port of the Twig adapter's parity test).
|
|
224
|
+
* 10. **Blade's own `$loop->index`, not `loop.index0`.** Both engines
|
|
225
|
+
* expose a 0-based per-iteration index via a builtin loop object —
|
|
226
|
+
* verified empirically Blade's is `$loop->index` (property, not a
|
|
227
|
+
* method), coincidentally same 0-based numbering as Twig's
|
|
228
|
+
* `loop.index0`, just a different property name and `->` access.
|
|
229
|
+
* 11. **`array_merge(...)`, not a chained `|merge` filter, for ordered
|
|
230
|
+
* component-prop segments.** Twig has no hash-splat syntax, so the
|
|
231
|
+
* Twig port folds `{...before, ...spread, after: 1}` JSX spread
|
|
232
|
+
* semantics through repeated `{a}|merge({b})|merge({c})` filter calls.
|
|
233
|
+
* PHP's `array_merge` is natively variadic AND already resolves later-
|
|
234
|
+
* argument-wins on string-key conflict (verified empirically — same
|
|
235
|
+
* semantics `Object.assign`/JSX spread order needs), so this adapter
|
|
236
|
+
* folds the segment list into ONE `array_merge($seg1, $seg2, …)` call
|
|
237
|
+
* instead of a chain — simpler, not just a syntax reshuffle.
|
|
238
|
+
*/
|
|
239
|
+
import type { ComponentIR, IRNode, IRElement, IRText, IRExpression, IRConditional, IRLoop, IRComponent, IRFragment, IRSlot, IRIfStatement, IRProvider, IRAsync, TemplatePrimitiveRegistry } from '@barefootjs/jsx';
|
|
240
|
+
import { BaseAdapter, type AdapterOutput, type AdapterGenerateOptions, type IRNodeEmitter, type EmitIRNode } from '@barefootjs/jsx';
|
|
241
|
+
import type { BladeRenderCtx } from './lib/types.ts';
|
|
242
|
+
export type { BladeAdapterOptions } from './lib/types.ts';
|
|
243
|
+
import type { BladeAdapterOptions } from './lib/types.ts';
|
|
244
|
+
export declare class BladeAdapter extends BaseAdapter implements IRNodeEmitter<BladeRenderCtx> {
|
|
245
|
+
name: string;
|
|
246
|
+
extension: string;
|
|
247
|
+
templatesPerComponent: boolean;
|
|
248
|
+
importMapInjection: 'html-snippet';
|
|
249
|
+
/**
|
|
250
|
+
* Identifier-path callees the Blade runtime can render in template scope.
|
|
251
|
+
* The relocate pass consults this map to mark matching calls as
|
|
252
|
+
* template-safe; the SSR template emitter substitutes the JS call with the
|
|
253
|
+
* registered `$bf->NAME(...)` helper invocation.
|
|
254
|
+
*/
|
|
255
|
+
templatePrimitives: TemplatePrimitiveRegistry;
|
|
256
|
+
private componentName;
|
|
257
|
+
/** Component root scope element(s) — each carries `data-key` for a keyed loop
|
|
258
|
+
* item (set by the child renderer from the JSX `key` prop). A plain element
|
|
259
|
+
* root is one node; an `if-statement` (early-return) root contributes the
|
|
260
|
+
* top element of every branch. */
|
|
261
|
+
private rootScopeNodes;
|
|
262
|
+
private options;
|
|
263
|
+
private errors;
|
|
264
|
+
private inLoop;
|
|
265
|
+
/**
|
|
266
|
+
* SolidJS-style props identifier (`function(props: P)`) and the
|
|
267
|
+
* analyzer-extracted prop names. Stashed at `generate()` entry so the
|
|
268
|
+
* per-attribute `emitSpread` callback can build a propsObject spread bag as
|
|
269
|
+
* an inline PHP array literal without re-walking the IR.
|
|
270
|
+
*/
|
|
271
|
+
private propsObjectName;
|
|
272
|
+
private propsParams;
|
|
273
|
+
private booleanTypedProps;
|
|
274
|
+
/**
|
|
275
|
+
* Names (signal getters + props) whose value is a string. Carried for
|
|
276
|
+
* parity with the Perl-family adapters (Mojo needs it for `eq`/`ne`
|
|
277
|
+
* selection); the Blade emitters don't consume it — `===`/`!==` always
|
|
278
|
+
* routes through `$bf->eq`/`$bf->neq` regardless of operand type (see
|
|
279
|
+
* `blade-adapter.ts`'s file header, divergence 7).
|
|
280
|
+
*/
|
|
281
|
+
private stringValueNames;
|
|
282
|
+
/**
|
|
283
|
+
* Module-scope pure-string consts (`const x = 'literal'`), keyed by name →
|
|
284
|
+
* unescaped value. A className template literal that references such a const
|
|
285
|
+
* (`className={`${x} ${className}`}`) must inline the literal: the const is
|
|
286
|
+
* module-scope, so it never reaches the per-render context, and a bare
|
|
287
|
+
* reference to `x` would resolve to an undefined variable.
|
|
288
|
+
*/
|
|
289
|
+
private moduleStringConsts;
|
|
290
|
+
/**
|
|
291
|
+
* (#1922) Local binding names the request-scoped `searchParams()` env signal
|
|
292
|
+
* is imported under (handles `import { searchParams as sp }`). When non-empty
|
|
293
|
+
* the emitter lowers a `<binding>().get(k)` call to a real method call on the
|
|
294
|
+
* per-request `searchParams` reader (`$searchParams->get('sort')`) instead of
|
|
295
|
+
* the generic `data_get` deref. Set at `generate()` entry from
|
|
296
|
+
* `ir.metadata.imports`; read by the top-level ParsedExpr emitter.
|
|
297
|
+
*/
|
|
298
|
+
private _searchParamsLocals;
|
|
299
|
+
/**
|
|
300
|
+
* Call-lowering matchers active for this component (#2057). Bound at
|
|
301
|
+
* `generate()` entry via `prepareLoweringMatchers` and read by the top-level
|
|
302
|
+
* emitter. Covers both userland plugins and the compiler's built-in plugins
|
|
303
|
+
* (e.g. `queryHref` → `$bf->query`, #2042) — one uniform path, no per-API branch.
|
|
304
|
+
*/
|
|
305
|
+
private _loweringMatchers;
|
|
306
|
+
/**
|
|
307
|
+
* Local + module constants from the IR, used by the conditional-spread and
|
|
308
|
+
* `Record<staticKeys, scalar>[propKey]` lowering paths (#textarea / #checkbox).
|
|
309
|
+
* Stashed at `generate()` entry so `emitSpread` can resolve a bare local
|
|
310
|
+
* const (`const sizeAttrs = size ? {…} : {}`) to its initializer text.
|
|
311
|
+
*/
|
|
312
|
+
private localConstants;
|
|
313
|
+
/**
|
|
314
|
+
* Optional, no-default props that are `None` when the caller omits them.
|
|
315
|
+
* Their bare-reference attribute emission is guarded with a Blade
|
|
316
|
+
* `isset($x)` test so the attribute DROPS rather than rendering `attr=""`
|
|
317
|
+
* (Hono-style nullish omission, e.g. textarea's `rows`; see this file's
|
|
318
|
+
* header, divergence 6). The filter excludes destructure-defaulted, rest,
|
|
319
|
+
* and concrete-primitive props.
|
|
320
|
+
*/
|
|
321
|
+
private nullableOptionalProps;
|
|
322
|
+
constructor(options?: BladeAdapterOptions);
|
|
323
|
+
generate(ir: ComponentIR, options?: AdapterGenerateOptions): AdapterOutput;
|
|
324
|
+
private generateScriptRegistrations;
|
|
325
|
+
/**
|
|
326
|
+
* Public entry point for node rendering. Delegates to the shared
|
|
327
|
+
* `IRNodeEmitter` dispatcher; per-kind logic lives in the `IRNodeEmitter`
|
|
328
|
+
* methods below.
|
|
329
|
+
*/
|
|
330
|
+
renderNode(node: IRNode): string;
|
|
331
|
+
emitElement(node: IRElement, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
332
|
+
emitText(node: IRText): string;
|
|
333
|
+
emitExpression(node: IRExpression): string;
|
|
334
|
+
emitConditional(node: IRConditional, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
335
|
+
emitLoop(node: IRLoop, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
336
|
+
emitComponent(node: IRComponent, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
337
|
+
emitFragment(node: IRFragment, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
338
|
+
emitSlot(node: IRSlot): string;
|
|
339
|
+
emitIfStatement(node: IRIfStatement, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
340
|
+
emitProvider(node: IRProvider, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
341
|
+
/** Lower a `<Ctx.Provider value>` value prop to a Blade expression. */
|
|
342
|
+
private providerValueBlade;
|
|
343
|
+
/**
|
|
344
|
+
* Lower an object-literal provider value (`value={{ open: () => props.open
|
|
345
|
+
* ?? false, onOpenChange: … }}`) to a PHP array literal (#1897). The
|
|
346
|
+
* SSR lowering is a per-member snapshot of what a consumer would READ
|
|
347
|
+
* during the same render:
|
|
348
|
+
*
|
|
349
|
+
* - zero-param expression-body arrows are getters — lower the body (the
|
|
350
|
+
* value is fixed for the render, so the call-time indirection drops out)
|
|
351
|
+
* - `on[A-Z]`-named members and function-shaped values are client-only
|
|
352
|
+
* behavior SSR never invokes — lower to `null`
|
|
353
|
+
* - anything else lowers through the normal expression pipeline (so an
|
|
354
|
+
* unsupported getter body still refuses loudly with BF101)
|
|
355
|
+
*
|
|
356
|
+
* Keys keep their JS names verbatim so a consumer-side `ctx.open` access
|
|
357
|
+
* maps onto the same array key. Returns `null` when the expression is not a
|
|
358
|
+
* plain object literal (spread / computed key) — the caller falls back to
|
|
359
|
+
* the whole-expression path, which refuses those shapes with BF101.
|
|
360
|
+
*/
|
|
361
|
+
private providerObjectLiteralBlade;
|
|
362
|
+
emitAsync(node: IRAsync, _ctx: BladeRenderCtx, _emit: EmitIRNode<BladeRenderCtx>): string;
|
|
363
|
+
renderElement(element: IRElement): string;
|
|
364
|
+
renderExpression(expr: IRExpression): string;
|
|
365
|
+
renderConditional(cond: IRConditional): string;
|
|
366
|
+
private renderNodeOrNull;
|
|
367
|
+
/**
|
|
368
|
+
* Add bf-c attribute to the first HTML element in a branch.
|
|
369
|
+
* If no element found, wrap with comment markers.
|
|
370
|
+
*/
|
|
371
|
+
private addCondMarkerToFirstElement;
|
|
372
|
+
renderLoop(loop: IRLoop): string;
|
|
373
|
+
/**
|
|
374
|
+
* AttrValue lowering for component invocation props (Blade array-entry
|
|
375
|
+
* form). Blade/PHP CANNOT splat an array into positional args, so every
|
|
376
|
+
* prop is emitted as a `'key' => value` entry that the caller collects
|
|
377
|
+
* into ONE array literal passed to `$bf->render_child(name, [ ... ])`.
|
|
378
|
+
*
|
|
379
|
+
* `jsx-children` returns empty — children are captured via the output-
|
|
380
|
+
* buffering mechanism below, not threaded through the entry list.
|
|
381
|
+
*/
|
|
382
|
+
private readonly componentPropEmitter;
|
|
383
|
+
/**
|
|
384
|
+
* A `renderComponent` props array, built as an ORDERED sequence of
|
|
385
|
+
* segments so `{...before, ...spread, after: 1}` JSX spread semantics
|
|
386
|
+
* (later entries win) survive the trip through Blade/PHP, which has no
|
|
387
|
+
* array-splat call syntax. Each `'entries'` segment is a literal PHP
|
|
388
|
+
* array `[k => v, ...]`; each `'spread'` segment is an arbitrary
|
|
389
|
+
* expression lowered from a `{...expr}` prop. `combineComponentPropSegments`
|
|
390
|
+
* folds the sequence into ONE expression via `array_merge(...)` (later
|
|
391
|
+
* segment wins on key conflict, matching `Object.assign`/JSX order — see
|
|
392
|
+
* this file's header, divergence 11).
|
|
393
|
+
*/
|
|
394
|
+
private componentPropSegmentEntries;
|
|
395
|
+
/**
|
|
396
|
+
* Fold ordered prop segments into a single PHP expression via ONE
|
|
397
|
+
* variadic `array_merge(...)` call — later argument wins on string-key
|
|
398
|
+
* conflict, exactly like `{...a, ...b}` (verified empirically). Empty
|
|
399
|
+
* `'entries'` segments are dropped so a leading/trailing spread doesn't
|
|
400
|
+
* drag in a needless `[]` argument. Returns `'[]'` when every segment is
|
|
401
|
+
* empty (no props at all); returns the lone segment directly (no
|
|
402
|
+
* `array_merge` wrapper) when there is exactly one.
|
|
403
|
+
*/
|
|
404
|
+
private combineComponentPropSegments;
|
|
405
|
+
renderComponent(comp: IRComponent): string;
|
|
406
|
+
private childrenCaptureCounter;
|
|
407
|
+
/** Uniquifies the `presenceOrUndefined` temp binding (`bf_puN`) so two
|
|
408
|
+
* presence-folded attrs in one template don't collide. */
|
|
409
|
+
private presenceVarCounter;
|
|
410
|
+
private toTemplateName;
|
|
411
|
+
private renderIfStatement;
|
|
412
|
+
private renderFragment;
|
|
413
|
+
private renderSlot;
|
|
414
|
+
renderAsync(node: IRAsync): string;
|
|
415
|
+
/**
|
|
416
|
+
* AttrValue lowering for intrinsic-element attributes (Blade).
|
|
417
|
+
*/
|
|
418
|
+
private readonly elementAttrEmitter;
|
|
419
|
+
/**
|
|
420
|
+
* Lower a `style={{ … }}` object literal to a CSS string with dynamic values
|
|
421
|
+
* interpolated as Blade expressions, e.g. `{ backgroundColor: color }` →
|
|
422
|
+
* `background-color:{{ $bf->string($color) }}`. Returns null when the shape
|
|
423
|
+
* is unsupported or any value can't be lowered (caller falls through to
|
|
424
|
+
* BF101). (#1322)
|
|
425
|
+
*/
|
|
426
|
+
private tryLowerStyleObject;
|
|
427
|
+
/** HTML-attribute escape for static text inlined into a `"..."` attribute. */
|
|
428
|
+
private escapeAttrText;
|
|
429
|
+
private renderAttributes;
|
|
430
|
+
renderScopeMarker(_instanceIdExpr: string): string;
|
|
431
|
+
renderSlotMarker(slotId: string): string;
|
|
432
|
+
renderCondMarker(condId: string): string;
|
|
433
|
+
/**
|
|
434
|
+
* Convert a ParsedExpr AST to a Blade expression string for filter
|
|
435
|
+
* predicates. Wraps the shared ParsedExpr dispatcher with a
|
|
436
|
+
* `BladeFilterEmitter` carrying the predicate's loop param and any
|
|
437
|
+
* block-body local var aliases.
|
|
438
|
+
*/
|
|
439
|
+
private renderBladeFilterExpr;
|
|
440
|
+
private convertTemplateLiteralPartsToBlade;
|
|
441
|
+
/**
|
|
442
|
+
* Translate `${EXPR}` interpolations in a static template-part string into
|
|
443
|
+
* Blade variable references and concatenate them with the surrounding
|
|
444
|
+
* literal text. Each interpolated (non-literal) segment routes through
|
|
445
|
+
* `$bf->string(...)` — see the file header, "Stringification".
|
|
446
|
+
*/
|
|
447
|
+
private substituteJsInterpolationsToBlade;
|
|
448
|
+
/**
|
|
449
|
+
* Refuse JS expression shapes that have no idiomatic Blade representation:
|
|
450
|
+
* object literals (`style={{...}}`) and tagged-template-literal call
|
|
451
|
+
* expressions (`cn\`base \${tone()}\``). Records `BF101`. Returns `true`
|
|
452
|
+
* when the shape was rejected (caller should drop the attribute).
|
|
453
|
+
*/
|
|
454
|
+
private refuseUnsupportedAttrExpression;
|
|
455
|
+
/**
|
|
456
|
+
* Build the EmitContext seam the top-level `ParsedExpr` emitter depends on.
|
|
457
|
+
* Built as a private object (the adapter does NOT `implements BladeEmitContext`)
|
|
458
|
+
* so the wrapped bookkeeping — `_searchParamsLocals`, the const/record
|
|
459
|
+
* resolvers, BF101 recording, the filter-predicate entry — stays private and
|
|
460
|
+
* off the exported adapter's public type, matching the Go adapter's
|
|
461
|
+
* `emitCtx` and the `spreadCtx` / `memoCtx` seams below.
|
|
462
|
+
*/
|
|
463
|
+
private get emitCtx();
|
|
464
|
+
/**
|
|
465
|
+
* Build the narrow context the extracted spread lowering depends on. Passing
|
|
466
|
+
* a purpose-built object (rather than `this`) keeps the adapter's bookkeeping
|
|
467
|
+
* members private — they stay internal implementation detail, not part of the
|
|
468
|
+
* exported class's public surface.
|
|
469
|
+
*/
|
|
470
|
+
private get spreadCtx();
|
|
471
|
+
/** Build the narrow context the extracted memo seeding depends on. */
|
|
472
|
+
private get memoCtx();
|
|
473
|
+
private convertExpressionToBlade;
|
|
474
|
+
/**
|
|
475
|
+
* Convert a JS condition (an `if` / ternary / loop-filter test) to a Blade
|
|
476
|
+
* boolean expression, routing through `$bf->truthy(...)` unless the
|
|
477
|
+
* expression is structurally already boolean-shaped. See the file header,
|
|
478
|
+
* "JS truthiness".
|
|
479
|
+
*/
|
|
480
|
+
private convertConditionToBlade;
|
|
481
|
+
/**
|
|
482
|
+
* Shared helper: given the ORIGINAL JS expression (or its already-parsed
|
|
483
|
+
* tree) and its ALREADY-RENDERED Blade text, wrap the rendered text with
|
|
484
|
+
* `$bf->truthy(...)` unless the expression is structurally boolean-shaped.
|
|
485
|
+
* Split from `convertConditionToBlade` so a caller that already lowered the
|
|
486
|
+
* expression for another purpose (e.g. the `presenceOrUndefined` temp bind)
|
|
487
|
+
* doesn't lower it twice.
|
|
488
|
+
*/
|
|
489
|
+
private wrapConditionExpr;
|
|
490
|
+
/**
|
|
491
|
+
* Render a full ParsedExpr tree to Blade for top-level (non-filter)
|
|
492
|
+
* expressions where identifiers are signals / template vars.
|
|
493
|
+
*/
|
|
494
|
+
private renderParsedExprToBlade;
|
|
495
|
+
/** Whether `name` (a signal getter or prop) holds a string value. Carried
|
|
496
|
+
* for parity with the Perl-family adapters; the Blade emitters don't
|
|
497
|
+
* consume it (`===`/`!==` always routes through `$bf->eq`/`$bf->neq`
|
|
498
|
+
* regardless of operand type). */
|
|
499
|
+
private _isStringValueName;
|
|
500
|
+
/**
|
|
501
|
+
* Parse `cond ? value : undefined` (or `: null`), returning the
|
|
502
|
+
* condition/consequent source spans, else `null`. Used for the
|
|
503
|
+
* attribute-omission rule (#1897).
|
|
504
|
+
*/
|
|
505
|
+
parseUndefinedAlternateTernary(expr: string): {
|
|
506
|
+
condition: string;
|
|
507
|
+
consequent: string;
|
|
508
|
+
} | null;
|
|
509
|
+
isBooleanTypedPropRef(expr: string): boolean;
|
|
510
|
+
/**
|
|
511
|
+
* Whether an attribute-value expression should route through
|
|
512
|
+
* `$bf->bool_str` (vs. plain `$bf->string`) at its interpolation site.
|
|
513
|
+
* `isExplicitStringCall` is checked FIRST and short-circuits the other
|
|
514
|
+
* three: an explicit `String(x)` call already lowers to `$bf->string(x)`,
|
|
515
|
+
* which correctly stringifies a real boolean on its own (see the PHP
|
|
516
|
+
* runtime's `string()` helper's bool branch), so layering `$bf->bool_str`
|
|
517
|
+
* on top would run PHP truthiness over the ALREADY-STRINGIFIED text
|
|
518
|
+
* instead of the original boolean. See `isExplicitStringCall`'s docstring
|
|
519
|
+
* in `boolean-result.ts` for the full double-wrap failure mode this
|
|
520
|
+
* guards against.
|
|
521
|
+
*/
|
|
522
|
+
private shouldBoolStr;
|
|
523
|
+
/**
|
|
524
|
+
* Inline a const (any scope) whose initializer is a pure numeric or
|
|
525
|
+
* single-quoted string literal (`const totalPages = 5`, #1897
|
|
526
|
+
* pagination) — function-scope consts never reach the per-render
|
|
527
|
+
* context, so a bare reference would resolve to an undefined variable.
|
|
528
|
+
*/
|
|
529
|
+
private _resolveLiteralConst;
|
|
530
|
+
private _resolveStaticRecordLiteral;
|
|
531
|
+
private _resolveModuleStringConst;
|
|
532
|
+
private _recordExprBF101;
|
|
533
|
+
/** Internal hook for higher-order: predicate body re-uses the filter emitter. */
|
|
534
|
+
private _renderBladeFilterExprPublic;
|
|
535
|
+
}
|
|
536
|
+
export declare const bladeAdapter: BladeAdapter;
|
|
537
|
+
//# sourceMappingURL=blade-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blade-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/blade-adapter.ts"],"names":[],"mappings":"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"}
|