@barefootjs/go-template 0.17.1 → 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/dist/adapter/expr/url-builder.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +134 -14
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +228 -82
- package/dist/adapter/lib/go-naming.d.ts +23 -0
- package/dist/adapter/lib/go-naming.d.ts.map +1 -1
- package/dist/adapter/type/type-codegen.d.ts +5 -1
- package/dist/adapter/type/type-codegen.d.ts.map +1 -1
- package/dist/adapter/value/parsed-literal-to-go.d.ts +13 -4
- package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
- package/dist/build.js +228 -82
- package/dist/conformance-pins.d.ts +12 -0
- package/dist/conformance-pins.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +247 -82
- package/package.json +3 -3
- package/src/__tests__/go-template-adapter.test.ts +388 -145
- package/src/__tests__/lowering-plugin.test.ts +56 -0
- package/src/adapter/expr/url-builder.ts +14 -3
- package/src/adapter/go-template-adapter.ts +511 -109
- package/src/adapter/lib/go-naming.ts +29 -0
- package/src/adapter/type/type-codegen.ts +22 -3
- package/src/adapter/value/parsed-literal-to-go.ts +82 -9
- package/src/conformance-pins.ts +135 -0
- package/src/index.ts +1 -0
|
@@ -49,6 +49,35 @@ export function capitalizeFieldName(name: string): string {
|
|
|
49
49
|
return name.charAt(0).toUpperCase() + name.slice(1)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Resolve ANY source property key — identifier or not (`data-priority`,
|
|
54
|
+
* `aria-label`, a numeric key) — to a valid Go struct field name. Splits on
|
|
55
|
+
* runs of characters that are invalid in a Go identifier (underscores are
|
|
56
|
+
* VALID and preserved, so a snake_case key round-trips to the exact same
|
|
57
|
+
* name `capitalizeFieldName` alone has always produced — `foo_bar` →
|
|
58
|
+
* `Foo_bar`, never `FooBar`; renaming it would break both existing member
|
|
59
|
+
* emission and consumers' hand-written constructors against generated
|
|
60
|
+
* types) and PascalCases each segment through `capitalizeFieldName`, so a
|
|
61
|
+
* hyphenated key gets a real field instead of being silently dropped
|
|
62
|
+
* (`data-priority` → `DataPriority`). A result that would start with a
|
|
63
|
+
* digit (numeric key `0`) is prefixed with `Field` (`Field0`), and a key
|
|
64
|
+
* with no usable characters at all falls back to `Field` — both keep the
|
|
65
|
+
* emitted struct compiling (not expected from real TS property names, but
|
|
66
|
+
* keeps the function total).
|
|
67
|
+
*
|
|
68
|
+
* Single source of truth for the source-key → Go-name mapping: used for
|
|
69
|
+
* struct-field generation (#2087 Phase B — `structFieldsFor`), inline-map
|
|
70
|
+
* baking (`bakeInlineObjectAsGoMap`), and the `member()` dot-access
|
|
71
|
+
* emitter, so the baked side and the accessor side can never disagree
|
|
72
|
+
* (PR #2089 review).
|
|
73
|
+
*/
|
|
74
|
+
export function goFieldNameForKey(key: string): string {
|
|
75
|
+
const parts = key.split(/[^A-Za-z0-9_]+/).filter(Boolean)
|
|
76
|
+
if (parts.length === 0) return 'Field'
|
|
77
|
+
const name = parts.map(capitalizeFieldName).join('')
|
|
78
|
+
return /^[0-9]/.test(name) ? `Field${name}` : name
|
|
79
|
+
}
|
|
80
|
+
|
|
52
81
|
/**
|
|
53
82
|
* Convert a slot ID (e.g., 's6') to a Go struct field suffix (e.g., 'Slot6').
|
|
54
83
|
* Keeps field names human-readable regardless of the internal slot ID format.
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* Free functions over a {@link GoEmitContext}. They resolve a prop/signal/const's
|
|
5
5
|
* type (`TypeInfo`, a raw type string, or — as a last resort — an inferred shape
|
|
6
6
|
* from a literal value) into the Go type used for its struct field. They read
|
|
7
|
-
*
|
|
7
|
+
* `state.localStructFields` / `state.localTypeAliases` (an ACTUAL Go-backed
|
|
8
|
+
* local type — a generated struct or a string-union alias) rather than the
|
|
9
|
+
* broader `state.localTypeNames` (every type definition, including a tuple
|
|
10
|
+
* alias no struct was ever emitted for — #2087); `inferTypeFromValue` is fully
|
|
11
|
+
* pure.
|
|
8
12
|
*/
|
|
9
13
|
|
|
10
14
|
import type { TypeInfo } from '@barefootjs/jsx'
|
|
@@ -42,7 +46,19 @@ export function typeInfoToGo(
|
|
|
42
46
|
case 'object':
|
|
43
47
|
return 'map[string]interface{}'
|
|
44
48
|
case 'interface':
|
|
45
|
-
|
|
49
|
+
// Gate on an ACTUAL backing (a generated struct — `localStructFields` —
|
|
50
|
+
// or a string-union alias — `localTypeAliases`, which emits `type X =
|
|
51
|
+
// string`), not mere presence in `localTypeNames`: the latter registers
|
|
52
|
+
// EVERY type definition unconditionally (#2087), including a tuple alias
|
|
53
|
+
// (`type Row = readonly [string, string]`) that `typeDefinitionToGo`
|
|
54
|
+
// can't turn into a struct (no object properties) and so never actually
|
|
55
|
+
// emits. Returning the bare name for one of those would reference an
|
|
56
|
+
// undeclared Go type (`[]Row`) and fail to compile — fall through to the
|
|
57
|
+
// generic-array/interface{} handling below instead, so a tuple-typed
|
|
58
|
+
// signal bakes as `[]interface{}` (each item itself an `interface{}`
|
|
59
|
+
// holding a `[]interface{}`) and the destructure `index`/`bf_slice`
|
|
60
|
+
// lowering still works via reflection regardless of the static type.
|
|
61
|
+
if (typeInfo.raw && (ctx.state.localStructFields.has(typeInfo.raw) || ctx.state.localTypeAliases.has(typeInfo.raw))) {
|
|
46
62
|
return typeInfo.raw
|
|
47
63
|
}
|
|
48
64
|
// Resolve a raw type string pattern (e.g. `Array<Todo>`).
|
|
@@ -76,7 +92,10 @@ export function tsTypeStringToGo(ctx: GoEmitContext, tsType: string): string {
|
|
|
76
92
|
}
|
|
77
93
|
const arrayMatch = t.match(/^Array<(.+)>$/)
|
|
78
94
|
if (arrayMatch) return `[]${tsTypeStringToGo(ctx, arrayMatch[1])}`
|
|
79
|
-
|
|
95
|
+
// Same backing gate as `typeInfoToGo`'s 'interface' case above — an
|
|
96
|
+
// unbacked local type name (a tuple alias with no struct fields) must not
|
|
97
|
+
// be returned bare, or the generated code references an undeclared type.
|
|
98
|
+
if (ctx.state.localStructFields.has(t) || ctx.state.localTypeAliases.has(t)) return t
|
|
80
99
|
return 'interface{}'
|
|
81
100
|
}
|
|
82
101
|
|
|
@@ -3,20 +3,77 @@
|
|
|
3
3
|
* for baking a signal's inline initial value into the SSR data context.
|
|
4
4
|
*
|
|
5
5
|
* Covers scalar literals, a unary-minus number, arrays of those, and object
|
|
6
|
-
* literals baked against a concrete local struct
|
|
6
|
+
* literals baked against a concrete local struct — including, per #2087, a
|
|
7
|
+
* struct property whose OWN value is a nested array or object literal
|
|
8
|
+
* (`{ id, cells: ['a', 'b'] }`, `{ id, user: { name: 'Ada' } }`): the struct's
|
|
9
|
+
* declared property TYPE (looked up from `ctx.state.currentTypeDefinitions`)
|
|
10
|
+
* threads through so a typed nested array bakes via the normal array branch
|
|
11
|
+
* below, and a nested INLINE object (one with no named Go struct —
|
|
12
|
+
* `typeInfoToGo`'s `'object'` case always falls back to
|
|
13
|
+
* `map[string]interface{}`) bakes as a capitalized-key Go map literal instead
|
|
14
|
+
* — the same convention `test-render.ts`'s harness-prop baking already uses
|
|
15
|
+
* for object elements, since `html/template`'s map field access is an exact
|
|
16
|
+
* case-sensitive `MapIndex`.
|
|
7
17
|
*
|
|
8
18
|
* Contract is **null-means-defer**: returns null for anything not reproduced
|
|
9
19
|
* exactly — an object whose target type isn't a known struct, a key the struct
|
|
10
|
-
* doesn't declare,
|
|
11
|
-
*
|
|
12
|
-
* then keeps `nil`.
|
|
20
|
+
* doesn't declare, an empty array, an identifier/call, or a numeric literal
|
|
21
|
+
* missing its `raw` token. The caller then keeps `nil`.
|
|
13
22
|
*/
|
|
14
23
|
|
|
15
|
-
import type { ParsedExpr, TypeInfo } from '@barefootjs/jsx'
|
|
24
|
+
import type { ParsedExpr, TypeDefinition, TypeInfo } from '@barefootjs/jsx'
|
|
16
25
|
|
|
17
26
|
import type { GoEmitContext } from '../emit-context.ts'
|
|
27
|
+
import { goFieldNameForKey } from '../lib/go-naming.ts'
|
|
18
28
|
import { typeInfoToGo } from '../type/type-codegen.ts'
|
|
19
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Look up a struct property's declared `TypeInfo` by source key, from the
|
|
32
|
+
* user's own `TypeDefinition` (not a synthesized struct — those only arise
|
|
33
|
+
* from an UNTYPED literal, which never carries nested object/array elements
|
|
34
|
+
* because `synthesizeStructFromSignal` requires every property value to be a
|
|
35
|
+
* scalar literal). Returns undefined when the struct name isn't a user type
|
|
36
|
+
* or the key isn't declared — callers treat that as "nested type unknown"
|
|
37
|
+
* and fall back to the generic/inline lowering.
|
|
38
|
+
*/
|
|
39
|
+
function structPropertyType(ctx: GoEmitContext, structGoType: string, key: string): TypeInfo | undefined {
|
|
40
|
+
const td = ctx.state.currentTypeDefinitions.find((t: TypeDefinition) => t.name === structGoType)
|
|
41
|
+
return td?.properties?.find(p => p.name === key)?.type
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Bake a nested INLINE object-literal property value — one whose declared
|
|
46
|
+
* type has no named Go struct (`user: { name: string }` lowers its field to
|
|
47
|
+
* `map[string]interface{}`, not a struct) — as a Go map literal with
|
|
48
|
+
* CAPITALIZED keys, recursing for further nesting. `html/template`'s dot
|
|
49
|
+
* access on a map value does an exact-string `MapIndex`, so a template
|
|
50
|
+
* action like `.User.Name` only resolves when the baked key is literally
|
|
51
|
+
* `"Name"`, not the source-cased `"name"` — mirrors the same convention
|
|
52
|
+
* `test-render.ts`'s `goArrayLiteralFromArray` uses for object elements
|
|
53
|
+
* passed as harness props.
|
|
54
|
+
*
|
|
55
|
+
* Keys are sanitized with `goFieldNameForKey` — the SAME function the
|
|
56
|
+
* accessor side (`buildSegmentAccessor` / `structFieldsFor`) uses — so a
|
|
57
|
+
* non-identifier key (`'data-x'`) bakes as `"DataX"` and dot access
|
|
58
|
+
* `.Meta.DataX` finds it. `capitalizeFieldName` alone would bake
|
|
59
|
+
* `"Data-x"`, a key no emitted accessor can ever reach (Copilot review,
|
|
60
|
+
* PR #2089).
|
|
61
|
+
*/
|
|
62
|
+
function bakeInlineObjectAsGoMap(ctx: GoEmitContext, expr: ParsedExpr): string | null {
|
|
63
|
+
if (expr.kind !== 'object-literal') return null
|
|
64
|
+
const entries: string[] = []
|
|
65
|
+
for (const prop of expr.properties) {
|
|
66
|
+
if (prop.shorthand) return null
|
|
67
|
+
const go =
|
|
68
|
+
prop.value.kind === 'object-literal'
|
|
69
|
+
? bakeInlineObjectAsGoMap(ctx, prop.value)
|
|
70
|
+
: parsedLiteralToGo(ctx, prop.value)
|
|
71
|
+
if (go === null) return null
|
|
72
|
+
entries.push(`${JSON.stringify(goFieldNameForKey(prop.key))}: ${go}`)
|
|
73
|
+
}
|
|
74
|
+
return `map[string]interface{}{${entries.join(', ')}}`
|
|
75
|
+
}
|
|
76
|
+
|
|
20
77
|
export function parsedLiteralToGo(
|
|
21
78
|
ctx: GoEmitContext,
|
|
22
79
|
expr: ParsedExpr,
|
|
@@ -79,10 +136,26 @@ export function parsedLiteralToGo(
|
|
|
79
136
|
// and defers the whole object.
|
|
80
137
|
const goField = structFields.get(prop.key)
|
|
81
138
|
if (!goField) return null
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (prop.value.kind === '
|
|
85
|
-
|
|
139
|
+
const propType = structPropertyType(ctx, goType, prop.key)
|
|
140
|
+
let go: string | null
|
|
141
|
+
if (prop.value.kind === 'array-literal') {
|
|
142
|
+
// A nested array property (`cells: readonly string[]`, #2087):
|
|
143
|
+
// thread the struct's own declared property type through so the
|
|
144
|
+
// array branch below bakes a properly-typed slice (`[]string{…}`)
|
|
145
|
+
// instead of deferring.
|
|
146
|
+
go = parsedLiteralToGo(ctx, prop.value, propType)
|
|
147
|
+
} else if (prop.value.kind === 'object-literal') {
|
|
148
|
+
// A nested object property (`user: { name: string }`, #2087): bake
|
|
149
|
+
// against a named struct if the declared type resolves to one,
|
|
150
|
+
// else fall back to the capitalized-key inline-map convention.
|
|
151
|
+
const nestedGoType = propType ? typeInfoToGo(ctx, propType) : undefined
|
|
152
|
+
go =
|
|
153
|
+
nestedGoType && ctx.state.localStructFields.has(nestedGoType)
|
|
154
|
+
? parsedLiteralToGo(ctx, prop.value, propType)
|
|
155
|
+
: bakeInlineObjectAsGoMap(ctx, prop.value)
|
|
156
|
+
} else {
|
|
157
|
+
go = parsedLiteralToGo(ctx, prop.value)
|
|
158
|
+
}
|
|
86
159
|
if (go === null) return null
|
|
87
160
|
entries.push(`${goField}: ${go}`)
|
|
88
161
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-fixture build-time contracts for shapes the Go template adapter
|
|
3
|
+
* intentionally refuses to lower. Lives here (not on the shared fixtures)
|
|
4
|
+
* so adding a new adapter doesn't require touching any cross-adapter
|
|
5
|
+
* file — every adapter declares its own refusal set against the
|
|
6
|
+
* canonical fixture corpus. Consumed by this package's own conformance
|
|
7
|
+
* test (as `expectedDiagnostics`) and by `bf compat` (issue-URL
|
|
8
|
+
* attribution).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ConformancePins } from '@barefootjs/jsx'
|
|
12
|
+
|
|
13
|
+
export const conformancePins: ConformancePins = {
|
|
14
|
+
// `style-object-dynamic` / `style-3-signals` no longer pinned — a
|
|
15
|
+
// `style={{ … }}` object literal now lowers to a CSS string with dynamic
|
|
16
|
+
// values interpolated (`background-color:{{.Color}};padding:8px`) via
|
|
17
|
+
// `tryLowerStyleObject` (#1322).
|
|
18
|
+
// Sibling-imported child component inside a loop body: the adapter
|
|
19
|
+
// emits `{{template "X" .}}` which only resolves if the user has
|
|
20
|
+
// compiled the sibling file and registered the template on the
|
|
21
|
+
// same instance. BF103 makes that requirement loud. (The barefoot
|
|
22
|
+
// CLI passes `siblingTemplatesRegistered: true` so CLI builds
|
|
23
|
+
// suppress the diagnostic — see compileJSX `siblingTemplatesRegistered`.)
|
|
24
|
+
'static-array-children': [{ code: 'BF103', severity: 'error' }],
|
|
25
|
+
// TodoApp / TodoAppSSR import `TodoItem` from a sibling file and
|
|
26
|
+
// call it inside a keyed `.map`. Same BF103 surface as
|
|
27
|
+
// `static-array-children` above — pinned at adapter level so the
|
|
28
|
+
// shared-component corpus stays adapter-neutral.
|
|
29
|
+
'todo-app': [{ code: 'BF103', severity: 'error' }],
|
|
30
|
+
'todo-app-ssr': [{ code: 'BF103', severity: 'error' }],
|
|
31
|
+
// `([emoji, users]) => ...` is an array-index tuple destructure — #2087
|
|
32
|
+
// Phase B's widened gate now admits this shape (`destructure-array-index-in-map`
|
|
33
|
+
// exercises the same `segments`-based lowering). The remaining refusal here
|
|
34
|
+
// is orthogonal: `entries` is a function-scope local const with a computed
|
|
35
|
+
// initializer (`Object.entries(props.reactions ?? {}).filter(...)`) that
|
|
36
|
+
// the Go adapter has no binding for (only a STRING-derived local resolves
|
|
37
|
+
// to a generated struct field, via `computeDerivedConstFields`/`isStringExpr`)
|
|
38
|
+
// — left unchecked this would silently execute-time-fail instead of
|
|
39
|
+
// building loud, so `renderLoop` raises BF101 for a bare-identifier loop
|
|
40
|
+
// array bound to such a const. See the `renderLoop` comment at the check
|
|
41
|
+
// site; Jinja / ERB apply the same narrow check for the same reason.
|
|
42
|
+
'static-array-from-props': [{ code: 'BF101', severity: 'error' }],
|
|
43
|
+
// Same computed-const array as above, plus the pre-existing BF103 (a
|
|
44
|
+
// sibling-imported child component used inside the loop body) — the
|
|
45
|
+
// destructure param itself no longer contributes a diagnostic.
|
|
46
|
+
'static-array-from-props-with-component': [
|
|
47
|
+
{ code: 'BF103', severity: 'error' },
|
|
48
|
+
{ code: 'BF101', severity: 'error' },
|
|
49
|
+
],
|
|
50
|
+
// (`style-3-signals` graduated alongside `style-object-dynamic` — see note
|
|
51
|
+
// above; the `style={{ … }}` object now lowers to a CSS string.)
|
|
52
|
+
// (`tagged-template-classname` graduated by #2092 — the tag resolves
|
|
53
|
+
// through the interleave-tag catalogue and desugars to an untagged
|
|
54
|
+
// template literal, so it lowers like any other className template.)
|
|
55
|
+
// #2038: a filter predicate whose body contains a NESTED callback call
|
|
56
|
+
// (`t => !picked().some(p => …)` / `t => picked().find(p => …)`). The
|
|
57
|
+
// evaluator refuses nested arrows and `renderFilterExpr` has no faithful
|
|
58
|
+
// Go form for the inner call (its `call` arm used to silently drop the
|
|
59
|
+
// arrow argument and render only the callee) — the compiler is loud
|
|
60
|
+
// instead of lossy. The `/* @client */` twin
|
|
61
|
+
// (`filter-nested-callback-predicate-client`) has no pin here: it must
|
|
62
|
+
// render clean on every adapter, which asserts the suppression contract.
|
|
63
|
+
// https://github.com/piconic-ai/barefootjs/issues/2038
|
|
64
|
+
'filter-nested-callback-predicate': [
|
|
65
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
66
|
+
],
|
|
67
|
+
'filter-nested-find-predicate': [
|
|
68
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
69
|
+
],
|
|
70
|
+
// #1310 / #2087: rest destructure in .map() callback. `isLowerableLoopDestructure`
|
|
71
|
+
// now admits every shape this fixture family exercises — each fixed/rest
|
|
72
|
+
// binding resolves via `buildSegmentAccessor`/`buildDestructureBindingMap`
|
|
73
|
+
// against a synthetic `$__bf_item0` range var (the reserved `__bf_item`
|
|
74
|
+
// name, depth-suffixed): a plain field → `$__bf_item0.Id`, an array-index
|
|
75
|
+
// step → `(index $__bf_item0 0)`, an array-rest → `(bf_slice $__bf_item0
|
|
76
|
+
// 1)` (composes under `.length` via `member()`'s generic `len <obj>` arm),
|
|
77
|
+
// and an object-rest member read (`rest.flag`) → `$__bf_item0.Flag`. A
|
|
78
|
+
// `{...rest}` SPREAD (`rest-destructure-object-spread-in-map`) routes
|
|
79
|
+
// through the new `bf_omit` runtime helper instead, so the residual omits
|
|
80
|
+
// exactly the sibling keys the pattern destructured out. No fixture in
|
|
81
|
+
// this family is pinned anymore — all six render to real Go / byte-exact
|
|
82
|
+
// HTML (`rest-destructure-object-in-map`, `rest-destructure-object-spread-in-map`,
|
|
83
|
+
// `rest-destructure-array-in-map`, `rest-destructure-nested-in-map`,
|
|
84
|
+
// `destructure-array-index-in-map`, `destructure-nested-object-in-map`).
|
|
85
|
+
// #1443: `[a, b].filter(Boolean).join(' ')` (registry Slot) now
|
|
86
|
+
// lowers to `bf_join (bf_filter_truthy (bf_arr ...)) " "`. No
|
|
87
|
+
// BF101 expected — pinned positively by the
|
|
88
|
+
// `branch-local-filter-join-go` template-output test below.
|
|
89
|
+
//
|
|
90
|
+
// #1448 Tier A — JS Array / String methods that the Go template
|
|
91
|
+
// adapter hasn't lowered yet. Each row drops once the
|
|
92
|
+
// corresponding method PR lands. Hono / CSR pass these out of
|
|
93
|
+
// the box (they evaluate JS at runtime) so the pin only applies
|
|
94
|
+
// here.
|
|
95
|
+
//
|
|
96
|
+
// `array-includes` / `string-includes` no longer pinned — both
|
|
97
|
+
// shapes lower via the shared `array-method` IR + the polymorphic
|
|
98
|
+
// `bf_includes` runtime helper that dispatches on
|
|
99
|
+
// `reflect.Kind()` (slice/array → element search, string →
|
|
100
|
+
// substring search). The condition-position lowering picks up
|
|
101
|
+
// the same emit through the `array-method` arm of
|
|
102
|
+
// `renderConditionExpr` (#1448 Tier A first PR).
|
|
103
|
+
//
|
|
104
|
+
// Remaining fixtures land at expression position and surface BF101
|
|
105
|
+
// via `convertExpressionToGo`. Distinct codes for the two paths is
|
|
106
|
+
// pre-existing adapter behaviour, not something this catalog
|
|
107
|
+
// should paper over — pinned literally here.
|
|
108
|
+
// `array-indexOf` / `array-lastIndexOf` no longer pinned —
|
|
109
|
+
// value-equality `bf_index_of` / `bf_last_index_of` Go runtime
|
|
110
|
+
// helpers handle the shape (#1448 Tier A second PR).
|
|
111
|
+
// `array-at` no longer pinned — the pre-existing `bf_at` runtime
|
|
112
|
+
// helper now lowers `.at(i)` (#1448 Tier A third PR).
|
|
113
|
+
// `array-concat` no longer pinned — the new `bf_concat` runtime
|
|
114
|
+
// helper merges two arrays into a single `[]any` (#1448 Tier A
|
|
115
|
+
// fourth PR).
|
|
116
|
+
// `array-slice` no longer pinned — the new `bf_slice` runtime
|
|
117
|
+
// helper carves out a sub-range with JS-compat clamping
|
|
118
|
+
// (#1448 Tier A fifth PR).
|
|
119
|
+
// `array-reverse` / `array-toReversed` no longer pinned —
|
|
120
|
+
// both share the `bf_reverse` helper since SSR templates
|
|
121
|
+
// render a snapshot and the JS mutate-vs-new distinction has
|
|
122
|
+
// no template-level meaning (#1448 Tier A sixth PR).
|
|
123
|
+
// `string-toLowerCase` / `string-toUpperCase` no longer pinned —
|
|
124
|
+
// pre-existing `bf_lower` / `bf_upper` runtime helpers wire to
|
|
125
|
+
// the JS method names at the adapter layer (#1448 Tier A
|
|
126
|
+
// seventh + eighth PRs).
|
|
127
|
+
// `string-trim` no longer pinned — pre-existing `bf_trim`
|
|
128
|
+
// (wraps `strings.TrimSpace`) handles the strip (#1448 Tier A
|
|
129
|
+
// ninth PR, closing out Tier A).
|
|
130
|
+
// #2073 follow-up: a function-reference `.map(format)` callback has no
|
|
131
|
+
// arrow body to serialize — not a CALLBACK_METHODS shape — so the
|
|
132
|
+
// UNSUPPORTED_METHODS gate refuses it with BF101 rather than emitting
|
|
133
|
+
// a broken template.
|
|
134
|
+
'array-map-function-reference': [{ code: 'BF101', severity: 'error' }],
|
|
135
|
+
}
|
package/src/index.ts
CHANGED