@barefootjs/jinja 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 +66 -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/boolean-result.d.ts +84 -0
- package/dist/adapter/boolean-result.d.ts.map +1 -0
- package/dist/adapter/emit-context.d.ts +106 -0
- package/dist/adapter/emit-context.d.ts.map +1 -0
- package/dist/adapter/expr/array-method.d.ts +75 -0
- package/dist/adapter/expr/array-method.d.ts.map +1 -0
- package/dist/adapter/expr/emitters.d.ts +143 -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 +189097 -0
- package/dist/adapter/jinja-adapter.d.ts +394 -0
- package/dist/adapter/jinja-adapter.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 +50 -0
- package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
- package/dist/adapter/lib/jinja-naming.d.ts +77 -0
- package/dist/adapter/lib/jinja-naming.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 +81 -0
- package/dist/adapter/memo/seed.d.ts.map +1 -0
- package/dist/adapter/props/prop-classes.d.ts +33 -0
- package/dist/adapter/props/prop-classes.d.ts.map +1 -0
- package/dist/adapter/spread/spread-codegen.d.ts +61 -0
- package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
- package/dist/adapter/value/parsed-literal.d.ts +28 -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 +189117 -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 +189118 -0
- package/package.json +66 -0
- package/python/VERSION +1 -0
- package/python/barefootjs/__init__.py +57 -0
- package/python/barefootjs/backend_jinja.py +125 -0
- package/python/barefootjs/evaluator.py +787 -0
- package/python/barefootjs/runtime.py +1334 -0
- package/python/barefootjs/search_params.py +89 -0
- package/python/pyproject.toml +32 -0
- package/python/tests/__init__.py +0 -0
- package/python/tests/test_eval_vectors.py +88 -0
- package/python/tests/test_evaluator.py +406 -0
- package/python/tests/test_helper_vectors.py +288 -0
- package/python/tests/test_omit.py +62 -0
- package/python/tests/test_props_attr.py +54 -0
- package/python/tests/test_query.py +41 -0
- package/python/tests/test_render.py +102 -0
- package/python/tests/test_render_child.py +96 -0
- package/python/tests/test_search_params.py +50 -0
- package/python/tests/test_spread_attrs.py +86 -0
- package/python/tests/test_template_primitives.py +347 -0
- package/python/tests/vector-divergences.json +42 -0
- package/src/__tests__/jinja-adapter-unit.test.ts +390 -0
- package/src/__tests__/jinja-adapter.test.ts +53 -0
- package/src/__tests__/jinja-counter.test.ts +62 -0
- package/src/__tests__/jinja-query-href.test.ts +99 -0
- package/src/__tests__/jinja-spread-attrs.test.ts +225 -0
- package/src/adapter/analysis/component-tree.ts +119 -0
- package/src/adapter/boolean-result.ts +176 -0
- package/src/adapter/emit-context.ts +118 -0
- package/src/adapter/expr/array-method.ts +346 -0
- package/src/adapter/expr/emitters.ts +608 -0
- package/src/adapter/expr/operand.ts +35 -0
- package/src/adapter/index.ts +6 -0
- package/src/adapter/jinja-adapter.ts +1747 -0
- package/src/adapter/lib/constants.ts +33 -0
- package/src/adapter/lib/ir-scope.ts +95 -0
- package/src/adapter/lib/jinja-naming.ts +114 -0
- package/src/adapter/lib/types.ts +30 -0
- package/src/adapter/memo/seed.ts +132 -0
- package/src/adapter/props/prop-classes.ts +65 -0
- package/src/adapter/spread/spread-codegen.ts +166 -0
- package/src/adapter/value/parsed-literal.ts +76 -0
- package/src/build.ts +37 -0
- package/src/conformance-pins.ts +101 -0
- package/src/index.ts +9 -0
- package/src/test-render.ts +714 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String-literal value lowering for the Jinja2 template adapter.
|
|
3
|
+
*
|
|
4
|
+
* Ported from `packages/adapter-xslate/src/adapter/value/parsed-literal.ts`.
|
|
5
|
+
* Pure functions over const-initializer source text and analyzer type info —
|
|
6
|
+
* no adapter instance state. Byte-identical logic to the Xslate copy (the
|
|
7
|
+
* source-text parsing here is language-agnostic; only the caller's escaping
|
|
8
|
+
* of the resolved value into a template literal differs, and that happens at
|
|
9
|
+
* the call site in `jinja-adapter.ts`, not here).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { evalStringArrayJoin, type TypeInfo } from '@barefootjs/jsx'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse a const initializer's source text. Returns the unescaped string value
|
|
16
|
+
* when the whole initializer is a single pure string literal — single/double
|
|
17
|
+
* quoted, or a no-substitution backtick template (no `${}`) — else `null`.
|
|
18
|
+
* Only such a value can be inlined byte-for-byte; template literals with
|
|
19
|
+
* interpolation, numbers, objects, and `Record<T,string>` maps are excluded.
|
|
20
|
+
*/
|
|
21
|
+
export function parsePureStringLiteral(source: string): string | null {
|
|
22
|
+
let s = source.trim()
|
|
23
|
+
// Peel a single layer of wrapping parens.
|
|
24
|
+
while (s.startsWith('(') && s.endsWith(')')) s = s.slice(1, -1).trim()
|
|
25
|
+
const quote = s[0]
|
|
26
|
+
if ((quote === "'" || quote === '"') && s[s.length - 1] === quote) {
|
|
27
|
+
const body = s.slice(1, -1)
|
|
28
|
+
// Reject if an unescaped matching quote appears inside (not a single
|
|
29
|
+
// literal then).
|
|
30
|
+
if (containsUnescaped(body, quote)) return null
|
|
31
|
+
return unescapeStringLiteralBody(body)
|
|
32
|
+
}
|
|
33
|
+
if (quote === '`' && s[s.length - 1] === '`') {
|
|
34
|
+
const body = s.slice(1, -1)
|
|
35
|
+
if (body.includes('${')) return null
|
|
36
|
+
if (containsUnescaped(body, '`')) return null
|
|
37
|
+
return unescapeStringLiteralBody(body)
|
|
38
|
+
}
|
|
39
|
+
// `[<literals>].join(' ')` module consts (e.g. Switch's `trackStateClasses`)
|
|
40
|
+
// → inline the flattened string byte-for-byte. See `evalStringArrayJoin`.
|
|
41
|
+
return evalStringArrayJoin(source)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Whether `s` contains an unescaped occurrence of `ch`. */
|
|
45
|
+
export function containsUnescaped(s: string, ch: string): boolean {
|
|
46
|
+
for (let i = 0; i < s.length; i++) {
|
|
47
|
+
if (s[i] === '\\') { i++; continue }
|
|
48
|
+
if (s[i] === ch) return true
|
|
49
|
+
}
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Unescape a JS string-literal body's common escape sequences. */
|
|
54
|
+
export function unescapeStringLiteralBody(s: string): string {
|
|
55
|
+
return s.replace(/\\(.)/g, (_, c) => {
|
|
56
|
+
switch (c) {
|
|
57
|
+
case 'n': return '\n'
|
|
58
|
+
case 'r': return '\r'
|
|
59
|
+
case 't': return '\t'
|
|
60
|
+
case '0': return '\0'
|
|
61
|
+
default: return c
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** True when `type` is the `string` primitive. */
|
|
67
|
+
export function isStringTypeInfo(type: TypeInfo | undefined): boolean {
|
|
68
|
+
return type?.kind === 'primitive' && type.primitive === 'string'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** True when `initialValue` is a bare string-literal expression. */
|
|
72
|
+
export function isBareStringLiteral(initialValue: string | undefined): boolean {
|
|
73
|
+
if (!initialValue) return false
|
|
74
|
+
const v = initialValue.trim()
|
|
75
|
+
return (v.startsWith("'") && v.endsWith("'")) || (v.startsWith('"') && v.endsWith('"'))
|
|
76
|
+
}
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Jinja2 build config factory for barefoot.config.ts
|
|
2
|
+
|
|
3
|
+
import type { BuildOptions } from '@barefootjs/jsx'
|
|
4
|
+
import { JinjaAdapter } from './adapter/index.ts'
|
|
5
|
+
import type { JinjaAdapterOptions } from './adapter/index.ts'
|
|
6
|
+
|
|
7
|
+
export interface JinjaBuildOptions extends BuildOptions {
|
|
8
|
+
/** Adapter-specific options passed to JinjaAdapter */
|
|
9
|
+
adapterOptions?: JinjaAdapterOptions
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a BarefootBuildConfig for Jinja2 template projects.
|
|
14
|
+
*
|
|
15
|
+
* Uses structural typing — does not import BarefootBuildConfig to avoid a
|
|
16
|
+
* circular dependency between @barefootjs/jinja and @barefootjs/cli.
|
|
17
|
+
*/
|
|
18
|
+
export function createConfig(options: JinjaBuildOptions = {}) {
|
|
19
|
+
return {
|
|
20
|
+
adapter: new JinjaAdapter(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,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-fixture build-time contracts for shapes the Jinja adapter
|
|
3
|
+
* intentionally refuses to lower. Mirrors xslate'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 xslate).
|
|
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 xslate.
|
|
21
|
+
'todo-app': [{ code: 'BF103', severity: 'error' }],
|
|
22
|
+
'todo-app-ssr': [{ code: 'BF103', severity: 'error' }],
|
|
23
|
+
// #2087 Phase A/B widened the destructure gate (`isLowerableLoopDestructure`)
|
|
24
|
+
// to admit array-index / nested-path fixed bindings, so the
|
|
25
|
+
// `([emoji, users]) => ...` / `([id, t]) => ...` params in these two
|
|
26
|
+
// fixtures no longer trip BF104 — the destructure itself now lowers
|
|
27
|
+
// cleanly to a native `{% set %}` accessor. But both fixtures' loop
|
|
28
|
+
// array is `entries`, a bare identifier bound to a function-scope local
|
|
29
|
+
// const with a NON-inlineable initializer
|
|
30
|
+
// (`Object.entries(props.x ?? {}).filter(...)`) — a pre-existing,
|
|
31
|
+
// orthogonal limitation this adapter has always had (it only ever
|
|
32
|
+
// INLINES a local const's value at its use site — numeric/string
|
|
33
|
+
// literal, or a static-record-literal lookup — never binds one as a
|
|
34
|
+
// `{% set %}` template local), just never reachable before because the
|
|
35
|
+
// narrower pre-#2087 gate refused the destructure param first. Left
|
|
36
|
+
// unhandled it would silently render an EMPTY list (Jinja's
|
|
37
|
+
// `ChainableUndefined` tolerates iterating an unbound name as zero
|
|
38
|
+
// iterations) instead of failing loudly, so `renderLoop` in
|
|
39
|
+
// `jinja-adapter.ts` now detects this shape and raises BF101 instead —
|
|
40
|
+
// see the "Loop array `<name>` is a local computed value" diagnostic.
|
|
41
|
+
// Fixing the underlying gap (computed-array-from-props as a loop
|
|
42
|
+
// source) is out of scope for #2087; tracked as a follow-up at
|
|
43
|
+
// https://github.com/piconic-ai/barefootjs/issues/2087.
|
|
44
|
+
'static-array-from-props': [
|
|
45
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2087' },
|
|
46
|
+
],
|
|
47
|
+
// Both BF103 (imported child) and BF101 (unresolvable computed loop
|
|
48
|
+
// array, see above) fire.
|
|
49
|
+
'static-array-from-props-with-component': [
|
|
50
|
+
{ code: 'BF103', severity: 'error' },
|
|
51
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2087' },
|
|
52
|
+
],
|
|
53
|
+
// Rest-destructure / structured-path `.map()` callbacks (#2087 Phase B):
|
|
54
|
+
// `isLowerableLoopDestructure` now admits fixed bindings at any
|
|
55
|
+
// field/index depth, array-rest (`bf.slice`), and object-rest whose uses
|
|
56
|
+
// are member reads or a `{...rest}` spread onto an intrinsic element
|
|
57
|
+
// (`bf.omit` builds the TRUE residual dict). Each binding becomes a
|
|
58
|
+
// native `{% set %}` local off the per-item var — see
|
|
59
|
+
// `renderLoop`/`jinjaAccessorFromSegments` in `jinja-adapter.ts`. So
|
|
60
|
+
// `rest-destructure-object-in-map`, `rest-destructure-object-spread-in-map`,
|
|
61
|
+
// `rest-destructure-array-in-map`, `rest-destructure-nested-in-map`,
|
|
62
|
+
// `destructure-array-index-in-map`, and `destructure-nested-object-in-map`
|
|
63
|
+
// all render clean now — none of them are pinned here.
|
|
64
|
+
// (button/kbd graduated: the site/ui Button/Kbd `<Slot>` `{...props}` /
|
|
65
|
+
// `{...children.props}` component-spread now lowers via nested
|
|
66
|
+
// `dict(base, **top)` calls — see `jinja-adapter.ts`'s `renderComponent`
|
|
67
|
+
// — instead of refusing with BF101, so these two no longer need a pin
|
|
68
|
+
// here.)
|
|
69
|
+
// (`tagged-template-classname` graduated by #2092 — the tag resolves
|
|
70
|
+
// through the interleave-tag catalogue and desugars to an untagged
|
|
71
|
+
// template literal, so it lowers like any other className template.)
|
|
72
|
+
// #2038: a filter predicate whose body contains a NESTED callback call
|
|
73
|
+
// (`t => !picked().some(p => …)` / `t => picked().find(p => …)`). Jinja
|
|
74
|
+
// has no inline comprehension-with-nested-callback form usable from the
|
|
75
|
+
// evaluator-JSON `*_eval` payload mechanism (this adapter's ONLY
|
|
76
|
+
// higher-order-callback lowering path — see `jinja-adapter.ts`'s file
|
|
77
|
+
// header, divergence 3), so the compiler is loud (BF101) instead of
|
|
78
|
+
// lossy, same as xslate. The `/* @client */` twin
|
|
79
|
+
// (`filter-nested-callback-predicate-client`) has no pin here: it must
|
|
80
|
+
// render clean on every adapter, which asserts the suppression contract.
|
|
81
|
+
// https://github.com/piconic-ai/barefootjs/issues/2038
|
|
82
|
+
'filter-nested-callback-predicate': [
|
|
83
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
84
|
+
],
|
|
85
|
+
'filter-nested-find-predicate': [
|
|
86
|
+
{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2038' },
|
|
87
|
+
],
|
|
88
|
+
// NB: TOP-LEVEL `.find` / `.findIndex` / `.findLast` / `.findLastIndex`
|
|
89
|
+
// (text position) are NOT pinned here — like xslate (unlike mojo, which
|
|
90
|
+
// refuses them), Jinja lowers them to `bf.find_eval` / `find_index_eval`
|
|
91
|
+
// / etc. via the same evaluator-JSON mechanism as `.filter` / `.every` /
|
|
92
|
+
// `.some`, so they render. Only the NESTED-in-a-predicate form above is
|
|
93
|
+
// refused (#2038).
|
|
94
|
+
// #2073 follow-up (same as xslate): a function-reference `.map(format)`
|
|
95
|
+
// callback has no arrow body to serialize — not a CALLBACK_METHODS shape
|
|
96
|
+
// (`asCallbackMethodCall` requires an arrow argument) — so the shared
|
|
97
|
+
// `isSupported`'s `UNSUPPORTED_METHODS` gate refuses it with the generic
|
|
98
|
+
// "Expression not supported" BF101 rather than emitting a broken
|
|
99
|
+
// template.
|
|
100
|
+
'array-map-function-reference': [{ code: 'BF101', severity: 'error' }],
|
|
101
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BarefootJS Jinja2 Template Adapter
|
|
3
|
+
*
|
|
4
|
+
* Generates Jinja2 template files (.jinja) from BarefootJS IR.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { JinjaAdapter, jinjaAdapter } from './adapter/index.ts'
|
|
8
|
+
export type { JinjaAdapterOptions } from './adapter/index.ts'
|
|
9
|
+
export { conformancePins } from './conformance-pins.ts'
|