@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
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object-literal / conditional-spread → Blade hash lowering for the Blade
|
|
3
|
+
* template adapter.
|
|
4
|
+
*
|
|
5
|
+
* Ported from `packages/adapter-jinja/src/adapter/spread/spread-codegen.ts`.
|
|
6
|
+
* Free functions taking a `BladeSpreadContext` (built by the adapter's
|
|
7
|
+
* `spreadCtx` getter) so the cluster depends on the narrow seam — the
|
|
8
|
+
* recursive expression entry plus per-compile bookkeeping — rather than the
|
|
9
|
+
* whole adapter class.
|
|
10
|
+
*
|
|
11
|
+
* The conditional-spread / object-literal entries read the IR-carried
|
|
12
|
+
* structured `ParsedExpr` tree (#2018) instead of re-parsing the source with
|
|
13
|
+
* `ts.createSourceFile`. The condition and scalar values are threaded
|
|
14
|
+
* straight into `ctx.convertExpressionToBlade` as its `preParsed` argument,
|
|
15
|
+
* so no stringify→re-parse round-trip occurs. The `ts.factory` rebuild in
|
|
16
|
+
* `recordIndexAccessToBlade` only reconstructs the `IDENT[KEY]` node the
|
|
17
|
+
* shared `parseRecordIndexAccess` parser accepts; no source-text re-parse.
|
|
18
|
+
*
|
|
19
|
+
* Twig→Blade divergence: a Twig hash literal `{'k': v}` becomes a PHP array
|
|
20
|
+
* literal `['k' => v]` (mapping table) — `'key' => value` entries joined by
|
|
21
|
+
* `, `, wrapped in `[...]`, never `{...}`. Empty hash is `[]`, not `{}`.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import ts from 'typescript'
|
|
25
|
+
import { parseRecordIndexAccess, stringifyParsedExpr } from '@barefootjs/jsx'
|
|
26
|
+
import type { ParsedExpr } from '@barefootjs/jsx'
|
|
27
|
+
|
|
28
|
+
import type { BladeSpreadContext } from '../emit-context.ts'
|
|
29
|
+
import { escapeBladeSingleQuoted, bladeHashKey, bladeVar } from '../lib/blade-naming.ts'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Lower a conditional inline-object spread
|
|
33
|
+
* `COND ? { 'aria-describedby': describedBy } : {}`
|
|
34
|
+
* to a Blade inline ternary of PHP arrays
|
|
35
|
+
* `($bf->truthy($describedBy) ? ['aria-describedby' => $describedBy] : [])`.
|
|
36
|
+
* Reads the IR-carried structured `ParsedExpr` tree; both branches must be
|
|
37
|
+
* object literals; the condition + values route through
|
|
38
|
+
* `convertExpressionToBlade`. Returns `null` for any other shape so the
|
|
39
|
+
* caller falls back to its normal lowering.
|
|
40
|
+
*/
|
|
41
|
+
export function conditionalSpreadToBlade(
|
|
42
|
+
ctx: BladeSpreadContext,
|
|
43
|
+
expr: ParsedExpr | undefined,
|
|
44
|
+
): string | null {
|
|
45
|
+
if (!expr || expr.kind !== 'conditional') return null
|
|
46
|
+
const whenTrue = expr.consequent
|
|
47
|
+
const whenFalse = expr.alternate
|
|
48
|
+
if (whenTrue.kind !== 'object-literal' || whenFalse.kind !== 'object-literal') {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
// Thread the condition's carried `ParsedExpr` tree straight through as
|
|
52
|
+
// `preParsed` (#2018) — no stringify→re-parse round-trip.
|
|
53
|
+
const condBlade = ctx.convertConditionToBlade('', expr.test)
|
|
54
|
+
const trueBlade = objectLiteralToBladeDict(ctx, whenTrue)
|
|
55
|
+
const falseBlade = objectLiteralToBladeDict(ctx, whenFalse)
|
|
56
|
+
if (trueBlade === null || falseBlade === null) return null
|
|
57
|
+
// Blade's symbolic ternary (see `expr/emitters.ts`'s file header,
|
|
58
|
+
// divergence 1) — `(test ? a : b)`, not Jinja's `(a if test else b)`.
|
|
59
|
+
return `(${condBlade} ? ${trueBlade} : ${falseBlade})`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* (#1971) Lower a bare object-literal expression (`{ align: 'start' }`),
|
|
64
|
+
* carried as the IR's structured `ParsedExpr` tree, to a PHP array via
|
|
65
|
+
* `objectLiteralToBladeDict`, or null when it isn't a plain object literal.
|
|
66
|
+
* Used for inline object-literal child props (carousel `opts`).
|
|
67
|
+
*/
|
|
68
|
+
export function objectLiteralExprToBladeDict(
|
|
69
|
+
ctx: BladeSpreadContext,
|
|
70
|
+
expr: ParsedExpr | undefined,
|
|
71
|
+
): string | null {
|
|
72
|
+
if (!expr || expr.kind !== 'object-literal') return null
|
|
73
|
+
return objectLiteralToBladeDict(ctx, expr)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Convert a static object literal into a PHP array-literal string for a
|
|
78
|
+
* conditional spread. Only static string/identifier keys are allowed; values
|
|
79
|
+
* resolve via `convertExpressionToBlade` (or the `Record[propKey]` index
|
|
80
|
+
* lowering). Returns `null` for any computed/spread/dynamic key. Empty
|
|
81
|
+
* object → `[]`.
|
|
82
|
+
*/
|
|
83
|
+
export function objectLiteralToBladeDict(
|
|
84
|
+
ctx: BladeSpreadContext,
|
|
85
|
+
obj: Extract<ParsedExpr, { kind: 'object-literal' }>,
|
|
86
|
+
): string | null {
|
|
87
|
+
const entries: string[] = []
|
|
88
|
+
for (const prop of obj.properties) {
|
|
89
|
+
// Shorthand `{ a }` was a `ShorthandPropertyAssignment` (not a
|
|
90
|
+
// `PropertyAssignment`), so the former parser rejected it — keep refusing.
|
|
91
|
+
if (prop.shorthand) return null
|
|
92
|
+
// A numeric key (`{ 1: x }`) was rejected by the former parser (only
|
|
93
|
+
// identifier / string-literal names were accepted); `keyKind`
|
|
94
|
+
// distinguishes it from a same-text string `'1'` key.
|
|
95
|
+
if (prop.keyKind === 'numeric') return null
|
|
96
|
+
const key = prop.key
|
|
97
|
+
const val = prop.value
|
|
98
|
+
const indexed = recordIndexAccessToBlade(ctx, val)
|
|
99
|
+
if (
|
|
100
|
+
indexed === null &&
|
|
101
|
+
val.kind === 'index-access' &&
|
|
102
|
+
!isLiteralIndex(val.index)
|
|
103
|
+
) {
|
|
104
|
+
// Variable-index record access (`sizeMap[size]`) the static-inline
|
|
105
|
+
// path couldn't resolve (non-scalar value / non-const receiver).
|
|
106
|
+
// Record BF101 and bail so the spread surfaces the out-of-shape
|
|
107
|
+
// diagnostic, matching the Jinja port.
|
|
108
|
+
ctx.errors.push({
|
|
109
|
+
code: 'BF101',
|
|
110
|
+
severity: 'error',
|
|
111
|
+
message: `Spread object value '${stringifyParsedExpr(val)}' indexes a record map whose values aren't scalar literals — it can't lower to an inline Blade hash.`,
|
|
112
|
+
loc: { file: ctx.componentName + '.tsx', start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
113
|
+
suggestion: {
|
|
114
|
+
message: 'Index a record whose values are number/string literals, or move the spread into a \'use client\' component so hydration computes it.',
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
const valBlade =
|
|
120
|
+
indexed !== null
|
|
121
|
+
? indexed
|
|
122
|
+
// Thread the carried `val` tree straight through as `preParsed`
|
|
123
|
+
// (#2018) — no stringify→re-parse round-trip.
|
|
124
|
+
: ctx.convertExpressionToBlade('', val)
|
|
125
|
+
entries.push(`${bladeHashKey(key)} => ${valBlade}`)
|
|
126
|
+
}
|
|
127
|
+
return entries.length === 0 ? '[]' : `[${entries.join(', ')}]`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** True when a parsed index is a numeric or string literal (`arr[0]`, `m['k']`). */
|
|
131
|
+
function isLiteralIndex(index: ParsedExpr): boolean {
|
|
132
|
+
return (
|
|
133
|
+
index.kind === 'literal' &&
|
|
134
|
+
(index.literalType === 'number' || index.literalType === 'string')
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Lower a spread-object VALUE of the form `IDENT[KEY]` (CheckIcon's
|
|
140
|
+
* `sizeMap[size]`) to an inline indexed PHP array
|
|
141
|
+
* `['sm' => 16, 'md' => 20, ...][$size]`.
|
|
142
|
+
* Reuses the shared structural parse (`parseRecordIndexAccess`) — rebuilding
|
|
143
|
+
* the `IDENT[KEY]` node from the carried tree via `ts.factory` rather than
|
|
144
|
+
* re-parsing source text; this wrapper only does the single-quote escaping +
|
|
145
|
+
* bracket-index emit. PHP indexes an array literal with the SAME bracket
|
|
146
|
+
* syntax `[...][key]` a JS object index would use (verified empirically —
|
|
147
|
+
* `php -r 'var_dump(["a"=>1]["a"]);'` → `1`) — no Kolon-style divergence to
|
|
148
|
+
* steer around here. Unlike Twig's own hash `{…}[key]`, a MISSING key here
|
|
149
|
+
* would normally raise a PHP warning rather than resolve to `null` — this
|
|
150
|
+
* call site's caller (`objectLiteralToBladeDict`'s data-table lookup path,
|
|
151
|
+
* and `blade-adapter.ts`'s `${MAP}[key]` template-literal lowering) always
|
|
152
|
+
* wraps the result in `?? ''`/`?? null`, which silences an undefined-index
|
|
153
|
+
* warning exactly like it silences an undefined-variable one (see
|
|
154
|
+
* `blade-adapter.ts`'s file header, the `??` divergence).
|
|
155
|
+
*/
|
|
156
|
+
export function recordIndexAccessToBlade(ctx: BladeSpreadContext, val: ParsedExpr): string | null {
|
|
157
|
+
// The only shape `parseRecordIndexAccess` accepts is `IDENT[KEY]` with
|
|
158
|
+
// identifier object and index, so rebuild exactly that node from the
|
|
159
|
+
// carried tree via `ts.factory` — no source-text re-parse.
|
|
160
|
+
if (
|
|
161
|
+
val.kind !== 'index-access' ||
|
|
162
|
+
val.object.kind !== 'identifier' ||
|
|
163
|
+
val.index.kind !== 'identifier'
|
|
164
|
+
) {
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
const tsVal = ts.factory.createElementAccessExpression(
|
|
168
|
+
ts.factory.createIdentifier(val.object.name),
|
|
169
|
+
ts.factory.createIdentifier(val.index.name),
|
|
170
|
+
)
|
|
171
|
+
const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants ?? [], ctx.propsParams)
|
|
172
|
+
if (!parsed) return null
|
|
173
|
+
const entries = parsed.entries.map(e => {
|
|
174
|
+
const mapVal =
|
|
175
|
+
e.value.kind === 'number' ? e.value.text : `'${escapeBladeSingleQuoted(e.value.text)}'`
|
|
176
|
+
return `${bladeHashKey(e.key)} => ${mapVal}`
|
|
177
|
+
})
|
|
178
|
+
return `[${entries.join(', ')}][${bladeVar(parsed.indexPropName)}]`
|
|
179
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String-literal value lowering for the Blade template adapter.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `packages/adapter-jinja/src/adapter/value/parsed-literal.ts`.
|
|
5
|
+
* Byte-identical logic (the source-text parsing here is language-agnostic;
|
|
6
|
+
* only the caller's escaping of the resolved value into a template literal
|
|
7
|
+
* differs, and that happens at the call site in `blade-adapter.ts`, not
|
|
8
|
+
* here).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { evalStringArrayJoin, type TypeInfo } from '@barefootjs/jsx'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a const initializer's source text. Returns the unescaped string value
|
|
15
|
+
* when the whole initializer is a single pure string literal — single/double
|
|
16
|
+
* quoted, or a no-substitution backtick template (no `${}`) — else `null`.
|
|
17
|
+
* Only such a value can be inlined byte-for-byte; template literals with
|
|
18
|
+
* interpolation, numbers, objects, and `Record<T,string>` maps are excluded.
|
|
19
|
+
*/
|
|
20
|
+
export function parsePureStringLiteral(source: string): string | null {
|
|
21
|
+
let s = source.trim()
|
|
22
|
+
// Peel a single layer of wrapping parens.
|
|
23
|
+
while (s.startsWith('(') && s.endsWith(')')) s = s.slice(1, -1).trim()
|
|
24
|
+
const quote = s[0]
|
|
25
|
+
if ((quote === "'" || quote === '"') && s[s.length - 1] === quote) {
|
|
26
|
+
const body = s.slice(1, -1)
|
|
27
|
+
// Reject if an unescaped matching quote appears inside (not a single
|
|
28
|
+
// literal then).
|
|
29
|
+
if (containsUnescaped(body, quote)) return null
|
|
30
|
+
return unescapeStringLiteralBody(body)
|
|
31
|
+
}
|
|
32
|
+
if (quote === '`' && s[s.length - 1] === '`') {
|
|
33
|
+
const body = s.slice(1, -1)
|
|
34
|
+
if (body.includes('${')) return null
|
|
35
|
+
if (containsUnescaped(body, '`')) return null
|
|
36
|
+
return unescapeStringLiteralBody(body)
|
|
37
|
+
}
|
|
38
|
+
// `[<literals>].join(' ')` module consts (e.g. Switch's `trackStateClasses`)
|
|
39
|
+
// → inline the flattened string byte-for-byte. See `evalStringArrayJoin`.
|
|
40
|
+
return evalStringArrayJoin(source)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Whether `s` contains an unescaped occurrence of `ch`. */
|
|
44
|
+
export function containsUnescaped(s: string, ch: string): boolean {
|
|
45
|
+
for (let i = 0; i < s.length; i++) {
|
|
46
|
+
if (s[i] === '\\') { i++; continue }
|
|
47
|
+
if (s[i] === ch) return true
|
|
48
|
+
}
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Unescape a JS string-literal body's common escape sequences. */
|
|
53
|
+
export function unescapeStringLiteralBody(s: string): string {
|
|
54
|
+
return s.replace(/\\(.)/g, (_, c) => {
|
|
55
|
+
switch (c) {
|
|
56
|
+
case 'n': return '\n'
|
|
57
|
+
case 'r': return '\r'
|
|
58
|
+
case 't': return '\t'
|
|
59
|
+
case '0': return '\0'
|
|
60
|
+
default: return c
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** True when `type` is the `string` primitive. */
|
|
66
|
+
export function isStringTypeInfo(type: TypeInfo | undefined): boolean {
|
|
67
|
+
return type?.kind === 'primitive' && type.primitive === 'string'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** True when `initialValue` is a bare string-literal expression. */
|
|
71
|
+
export function isBareStringLiteral(initialValue: string | undefined): boolean {
|
|
72
|
+
if (!initialValue) return false
|
|
73
|
+
const v = initialValue.trim()
|
|
74
|
+
return (v.startsWith("'") && v.endsWith("'")) || (v.startsWith('"') && v.endsWith('"'))
|
|
75
|
+
}
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Blade build config factory for barefoot.config.ts
|
|
2
|
+
|
|
3
|
+
import type { BuildOptions } from '@barefootjs/jsx'
|
|
4
|
+
import { BladeAdapter } from './adapter/index.ts'
|
|
5
|
+
import type { BladeAdapterOptions } from './adapter/index.ts'
|
|
6
|
+
|
|
7
|
+
export interface BladeBuildOptions extends BuildOptions {
|
|
8
|
+
/** Adapter-specific options passed to BladeAdapter */
|
|
9
|
+
adapterOptions?: BladeAdapterOptions
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a BarefootBuildConfig for Blade (PHP) template projects.
|
|
14
|
+
*
|
|
15
|
+
* Uses structural typing — does not import BarefootBuildConfig to avoid a
|
|
16
|
+
* circular dependency between @barefootjs/blade and @barefootjs/cli.
|
|
17
|
+
*/
|
|
18
|
+
export function createConfig(options: BladeBuildOptions = {}) {
|
|
19
|
+
return {
|
|
20
|
+
adapter: new BladeAdapter(options.adapterOptions),
|
|
21
|
+
paths: options.paths,
|
|
22
|
+
components: options.components,
|
|
23
|
+
outDir: options.outDir,
|
|
24
|
+
minify: options.minify,
|
|
25
|
+
contentHash: options.contentHash,
|
|
26
|
+
externals: options.externals,
|
|
27
|
+
externalsBasePath: options.externalsBasePath,
|
|
28
|
+
bundleEntries: options.bundleEntries,
|
|
29
|
+
localImportPrefixes: options.localImportPrefixes,
|
|
30
|
+
outputLayout: options.outputLayout ?? {
|
|
31
|
+
templates: 'templates',
|
|
32
|
+
clientJs: 'client',
|
|
33
|
+
runtime: 'client',
|
|
34
|
+
},
|
|
35
|
+
postBuild: options.postBuild,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-fixture build-time contracts for shapes the Blade adapter
|
|
3
|
+
* intentionally refuses to lower. Mirrors Jinja's set — the lowering
|
|
4
|
+
* gates are shared code paths in the ported adapter (BF103/BF104 are
|
|
5
|
+
* structural: cross-template child registration / destructure-loop-param
|
|
6
|
+
* limits that apply identically regardless of target template language).
|
|
7
|
+
* Consumed by this package's own conformance test (as `expectedDiagnostics`)
|
|
8
|
+
* and by `bf compat` (issue-URL attribution).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ConformancePins } from '@barefootjs/jsx'
|
|
12
|
+
|
|
13
|
+
export const conformancePins: ConformancePins = {
|
|
14
|
+
// Sibling-imported child component in a loop body: emits a
|
|
15
|
+
// cross-template call needing separate registration. BF103 makes
|
|
16
|
+
// the requirement loud (same as Jinja).
|
|
17
|
+
'static-array-children': [{ code: 'BF103', severity: 'error' }],
|
|
18
|
+
// TodoApp / TodoAppSSR import `TodoItem` from a sibling file and
|
|
19
|
+
// call it inside a keyed `.map`. Same BF103 (imported child in
|
|
20
|
+
// `.map`) as Jinja.
|
|
21
|
+
'todo-app': [{ code: 'BF103', severity: 'error' }],
|
|
22
|
+
'todo-app-ssr': [{ code: 'BF103', severity: 'error' }],
|
|
23
|
+
// The `([emoji, users]) => …` array-destructure param itself now lowers
|
|
24
|
+
// (#2087 Phase B — see the destructure comment below), but the loop
|
|
25
|
+
// ARRAY is a function-scope computed const (`const entries =
|
|
26
|
+
// Object.entries(props.reactions ?? {}).filter(...)`) that the adapter
|
|
27
|
+
// can't bind as a template variable — refused loudly with BF101 (same
|
|
28
|
+
// check and policy as Jinja / ERB) instead of silently iterating zero
|
|
29
|
+
// times over an unbound name.
|
|
30
|
+
'static-array-from-props': [{ code: 'BF101', severity: 'error' }],
|
|
31
|
+
// Both BF103 (imported child) and BF101 (computed local-const loop
|
|
32
|
+
// array, as above) fire.
|
|
33
|
+
'static-array-from-props-with-component': [
|
|
34
|
+
{ code: 'BF103', severity: 'error' },
|
|
35
|
+
{ code: 'BF101', severity: 'error' },
|
|
36
|
+
],
|
|
37
|
+
// #2087 Phase B: every `.map()` destructure shape in the shared corpus
|
|
38
|
+
// now lowers on Blade via an `@php(...)` local built from the binding's
|
|
39
|
+
// structured `segments` path (`bladeLoopBindingAccessor` in
|
|
40
|
+
// `lib/blade-naming.ts`) — fixed bindings at any field/index depth
|
|
41
|
+
// (`destructure-array-index-in-map`, `destructure-nested-object-in-map`),
|
|
42
|
+
// array-rest via `$bf->slice` (`rest-destructure-array-in-map`,
|
|
43
|
+
// `rest-destructure-nested-in-map`), and object-rest via the new
|
|
44
|
+
// `$bf->omit` residual helper, read by member access
|
|
45
|
+
// (`rest-destructure-object-in-map`) or spread onto the element
|
|
46
|
+
// (`rest-destructure-object-spread-in-map`). No `expectedDiagnostics`
|
|
47
|
+
// pins remain for any of them — see `blade-adapter.ts`'s `renderLoop` for
|
|
48
|
+
// the still-refused shapes (bare-value rest use, `.filter().map()`
|
|
49
|
+
// chains, `__bf_`-prefixed names).
|
|
50
|
+
// (button/kbd graduated: the site/ui Button/Kbd `<Slot>` `{...props}` /
|
|
51
|
+
// `{...children.props}` component-spread now lowers via an
|
|
52
|
+
// `array_merge(...)` fold — see `blade-adapter.ts`'s `renderComponent` —
|
|
53
|
+
// instead of refusing with BF101, so these two no longer need a pin here.)
|
|
54
|
+
// (`tagged-template-classname` graduated by #2092 — the tag resolves
|
|
55
|
+
// through the interleave-tag catalogue and desugars to an untagged
|
|
56
|
+
// template literal, so it lowers like any other className template.)
|
|
57
|
+
// #2038: a filter predicate whose body contains a NESTED callback call
|
|
58
|
+
// (`t => !picked().some(p => …)` / `t => picked().find(p => …)`). Blade
|
|
59
|
+
// has no inline comprehension-with-nested-callback form usable from the
|
|
60
|
+
// evaluator-JSON `*_eval` payload mechanism (this adapter's ONLY
|
|
61
|
+
// higher-order-callback lowering path — see `blade-adapter.ts`'s file
|
|
62
|
+
// header, divergence 3), so the compiler is loud (BF101) instead of
|
|
63
|
+
// lossy, same as Jinja. The `/* @client */` twin
|
|
64
|
+
// (`filter-nested-callback-predicate-client`) has no pin here: it must
|
|
65
|
+
// render clean on every adapter, which asserts the suppression contract.
|
|
66
|
+
// https://github.com/piconic-ai/barefootjs/issues/2038
|
|
67
|
+
'filter-nested-callback-predicate': [
|
|
68
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
69
|
+
],
|
|
70
|
+
'filter-nested-find-predicate': [
|
|
71
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
72
|
+
],
|
|
73
|
+
// NB: TOP-LEVEL `.find` / `.findIndex` / `.findLast` / `.findLastIndex`
|
|
74
|
+
// (text position) are NOT pinned here — like Jinja (unlike mojo, which
|
|
75
|
+
// refuses them), Blade lowers them to `$bf->find_eval` / `find_index_eval`
|
|
76
|
+
// / etc. via the same evaluator-JSON mechanism as `.filter` / `.every` /
|
|
77
|
+
// `.some`, so they render. Only the NESTED-in-a-predicate form above is
|
|
78
|
+
// refused (#2038).
|
|
79
|
+
// #2073 follow-up (same as Jinja): a function-reference `.map(format)`
|
|
80
|
+
// callback has no arrow body to serialize — not a CALLBACK_METHODS shape
|
|
81
|
+
// (`asCallbackMethodCall` requires an arrow argument) — so the shared
|
|
82
|
+
// `isSupported`'s `UNSUPPORTED_METHODS` gate refuses it with the generic
|
|
83
|
+
// "Expression not supported" BF101 rather than emitting a broken
|
|
84
|
+
// template.
|
|
85
|
+
'array-map-function-reference': [{ code: 'BF101', severity: 'error' }],
|
|
86
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BarefootJS Blade (PHP) Template Adapter
|
|
3
|
+
*
|
|
4
|
+
* Generates Blade template files (.blade.php) from BarefootJS IR.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { BladeAdapter, bladeAdapter } from './adapter/index.ts'
|
|
8
|
+
export type { BladeAdapterOptions } from './adapter/index.ts'
|
|
9
|
+
export { conformancePins } from './conformance-pins.ts'
|