@barefootjs/jinja 0.18.4 → 0.18.5
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/array-method.d.ts.map +1 -1
- package/dist/adapter/expr/emitters.d.ts +2 -2
- package/dist/adapter/expr/emitters.d.ts.map +1 -1
- package/dist/adapter/index.js +47 -15
- package/dist/adapter/jinja-adapter.d.ts +8 -0
- package/dist/adapter/jinja-adapter.d.ts.map +1 -1
- package/dist/adapter/lib/constants.d.ts.map +1 -1
- package/dist/build.js +47 -15
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +49 -27
- package/dist/render-divergences.d.ts.map +1 -1
- package/package.json +3 -3
- package/python/barefootjs/runtime.py +64 -6
- package/python/tests/test_helper_vectors.py +6 -0
- package/python/tests/test_template_primitives.py +45 -0
- package/src/__tests__/jinja-adapter-unit.test.ts +21 -0
- package/src/adapter/expr/array-method.ts +19 -0
- package/src/adapter/expr/emitters.ts +19 -6
- package/src/adapter/jinja-adapter.ts +62 -8
- package/src/adapter/lib/constants.ts +3 -0
- package/src/conformance-pins.ts +0 -5
- package/src/render-divergences.ts +1 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array-method.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/array-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAA;AAGxB,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,
|
|
1
|
+
{"version":3,"file":"array-method.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/array-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAA;AAGxB,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAgJR;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,GAAG,OAAO,EAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAkBf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,EAC/B,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAMf;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CASxE;AAGD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,EACvC,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAmBR"}
|
|
@@ -77,7 +77,7 @@ export declare class JinjaFilterEmitter implements ParsedExprEmitter {
|
|
|
77
77
|
constructor(param: string, localVarMap: Map<string, string>, isStringName?: (n: string) => boolean, onUnsupported?: ((message: string, reason?: string) => void) | undefined);
|
|
78
78
|
identifier(name: string): string;
|
|
79
79
|
literal(value: string | number | boolean | null, literalType: LiteralType): string;
|
|
80
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
|
|
80
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string;
|
|
81
81
|
indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
82
82
|
call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
|
|
83
83
|
unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
@@ -112,7 +112,7 @@ export declare class JinjaTopLevelEmitter implements ParsedExprEmitter {
|
|
|
112
112
|
constructor(ctx: JinjaEmitContext);
|
|
113
113
|
identifier(name: string): string;
|
|
114
114
|
literal(value: string | number | boolean | null, literalType: LiteralType): string;
|
|
115
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
|
|
115
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string;
|
|
116
116
|
indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
117
117
|
call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
|
|
118
118
|
unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,YAAY,EAIlB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AA+B1D;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,iBAAiB;IAExD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAI7B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAPjC,YACmB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,YAAY,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAqB,EAIlD,aAAa,CAAC,GAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,aAAA,EACzE;IAEJ,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/B;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,CAKjF;IAED,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -187369,7 +187369,7 @@ function isAriaBooleanAttr(name) {
|
|
|
187369
187369
|
}
|
|
187370
187370
|
|
|
187371
187371
|
// src/adapter/jinja-adapter.ts
|
|
187372
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
187372
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
187373
187373
|
|
|
187374
187374
|
// src/adapter/lib/constants.ts
|
|
187375
187375
|
var JINJA_TEMPLATE_PRIMITIVES = {
|
|
@@ -187378,7 +187378,10 @@ var JINJA_TEMPLATE_PRIMITIVES = {
|
|
|
187378
187378
|
Number: { arity: 1, emit: (args) => `bf.number(${args[0]})` },
|
|
187379
187379
|
"Math.floor": { arity: 1, emit: (args) => `bf.floor(${args[0]})` },
|
|
187380
187380
|
"Math.ceil": { arity: 1, emit: (args) => `bf.ceil(${args[0]})` },
|
|
187381
|
-
"Math.round": { arity: 1, emit: (args) => `bf.round(${args[0]})` }
|
|
187381
|
+
"Math.round": { arity: 1, emit: (args) => `bf.round(${args[0]})` },
|
|
187382
|
+
"Math.min": { arity: 2, emit: (args) => `bf.min(${args[0]}, ${args[1]})` },
|
|
187383
|
+
"Math.max": { arity: 2, emit: (args) => `bf.max(${args[0]}, ${args[1]})` },
|
|
187384
|
+
"Math.abs": { arity: 1, emit: (args) => `bf.abs(${args[0]})` }
|
|
187382
187385
|
};
|
|
187383
187386
|
var JINJA_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(JINJA_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
187384
187387
|
|
|
@@ -187557,6 +187560,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
187557
187560
|
const recv = emit(object);
|
|
187558
187561
|
return `bf.trim(${recv})`;
|
|
187559
187562
|
}
|
|
187563
|
+
case "trimStart":
|
|
187564
|
+
case "trimEnd": {
|
|
187565
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
187566
|
+
const recv = emit(object);
|
|
187567
|
+
return `bf.${fn}(${recv})`;
|
|
187568
|
+
}
|
|
187560
187569
|
case "toFixed": {
|
|
187561
187570
|
const recv = emit(object);
|
|
187562
187571
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -187590,6 +187599,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
187590
187599
|
const newS = emit(args[1]);
|
|
187591
187600
|
return `bf.replace(${recv}, ${oldS}, ${newS})`;
|
|
187592
187601
|
}
|
|
187602
|
+
case "replaceAll": {
|
|
187603
|
+
const recv = emit(object);
|
|
187604
|
+
const oldS = emit(args[0]);
|
|
187605
|
+
const newS = emit(args[1]);
|
|
187606
|
+
return `bf.replace_all(${recv}, ${oldS}, ${newS})`;
|
|
187607
|
+
}
|
|
187593
187608
|
case "repeat": {
|
|
187594
187609
|
const recv = emit(object);
|
|
187595
187610
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -187732,11 +187747,11 @@ class JinjaFilterEmitter {
|
|
|
187732
187747
|
return "none";
|
|
187733
187748
|
return String(value);
|
|
187734
187749
|
}
|
|
187735
|
-
member(object, property, _computed, emit) {
|
|
187750
|
+
member(object, property, _computed, _optional, emit) {
|
|
187736
187751
|
if (property === "length") {
|
|
187737
187752
|
return `bf.length(${emit(object)})`;
|
|
187738
187753
|
}
|
|
187739
|
-
return `${emit(object)}
|
|
187754
|
+
return `${emit(object)}['${escapeJinjaSingleQuoted(property)}']`;
|
|
187740
187755
|
}
|
|
187741
187756
|
indexAccess(object, index, emit) {
|
|
187742
187757
|
return `${emit(object)}[${emit(index)}]`;
|
|
@@ -187838,7 +187853,7 @@ class JinjaTopLevelEmitter {
|
|
|
187838
187853
|
return "none";
|
|
187839
187854
|
return String(value);
|
|
187840
187855
|
}
|
|
187841
|
-
member(object, property, _computed, emit) {
|
|
187856
|
+
member(object, property, _computed, _optional, emit) {
|
|
187842
187857
|
if (object.kind === "identifier" && object.name === "props") {
|
|
187843
187858
|
return jinjaIdent(property);
|
|
187844
187859
|
}
|
|
@@ -187850,7 +187865,7 @@ class JinjaTopLevelEmitter {
|
|
|
187850
187865
|
const obj = emit(object);
|
|
187851
187866
|
if (property === "length")
|
|
187852
187867
|
return `bf.length(${obj})`;
|
|
187853
|
-
return `${obj}
|
|
187868
|
+
return `${obj}['${escapeJinjaSingleQuoted(property)}']`;
|
|
187854
187869
|
}
|
|
187855
187870
|
indexAccess(object, index, emit) {
|
|
187856
187871
|
return `${emit(object)}[${emit(index)}]`;
|
|
@@ -188257,6 +188272,7 @@ class JinjaAdapter extends BaseAdapter {
|
|
|
188257
188272
|
options;
|
|
188258
188273
|
errors = [];
|
|
188259
188274
|
inLoop = false;
|
|
188275
|
+
currentLoopKeyDepth = 0;
|
|
188260
188276
|
propsObjectName = null;
|
|
188261
188277
|
propsParams = [];
|
|
188262
188278
|
booleanTypedProps = new Set;
|
|
@@ -188333,7 +188349,7 @@ class JinjaAdapter extends BaseAdapter {
|
|
|
188333
188349
|
return this.renderElement(node);
|
|
188334
188350
|
}
|
|
188335
188351
|
emitText(node) {
|
|
188336
|
-
return node.value;
|
|
188352
|
+
return escapeHtml(node.value);
|
|
188337
188353
|
}
|
|
188338
188354
|
emitExpression(node) {
|
|
188339
188355
|
return this.renderExpression(node);
|
|
@@ -188564,7 +188580,7 @@ ${whenTrue}
|
|
|
188564
188580
|
const renderedChildren = this.renderChildren(loop.children);
|
|
188565
188581
|
const loopVar = loop.iterationShape === "keys" ? "__bf_item" : supportableDestructure ? "__bf_item" : param;
|
|
188566
188582
|
const indexLocalLines = [];
|
|
188567
|
-
if (loop.iterationShape === "keys") {
|
|
188583
|
+
if (loop.objectIteration) {} else if (loop.iterationShape === "keys") {
|
|
188568
188584
|
indexLocalLines.push(`{% set ${jinjaIdent(param)} = loop.index0 %}`);
|
|
188569
188585
|
} else if (loop.index) {
|
|
188570
188586
|
indexLocalLines.push(`{% set ${jinjaIdent(loop.index)} = loop.index0 %}`);
|
|
@@ -188584,13 +188600,17 @@ ${whenTrue}
|
|
|
188584
188600
|
}
|
|
188585
188601
|
const prevInLoop = this.inLoop;
|
|
188586
188602
|
this.inLoop = true;
|
|
188603
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
188604
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
188587
188605
|
const childrenUnderLoop = this.renderChildren(loop.children);
|
|
188606
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
188588
188607
|
this.inLoop = prevInLoop;
|
|
188589
188608
|
const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
|
|
188590
188609
|
${childrenUnderLoop}` : childrenUnderLoop;
|
|
188591
188610
|
const lines = [];
|
|
188592
188611
|
lines.push(`{{ bf.comment("loop:${loop.markerId}") | safe }}`);
|
|
188593
|
-
|
|
188612
|
+
const forHeader = loop.objectIteration === "entries" ? `{% for ${jinjaIdent(loop.index ?? param)}, ${jinjaIdent(param)} in ${array}.items() %}` : loop.objectIteration === "keys" ? `{% for ${jinjaIdent(param)} in ${array}.keys() %}` : loop.objectIteration === "values" ? `{% for ${jinjaIdent(param)} in ${array}.values() %}` : `{% for ${jinjaIdent(loopVar)} in ${array} %}`;
|
|
188613
|
+
lines.push(forHeader);
|
|
188594
188614
|
for (const il of indexLocalLines)
|
|
188595
188615
|
lines.push(il);
|
|
188596
188616
|
if (loop.filterPredicate) {
|
|
@@ -188662,9 +188682,20 @@ ${childrenUnderLoop}` : childrenUnderLoop;
|
|
|
188662
188682
|
renderComponent(comp) {
|
|
188663
188683
|
const segments = [{ kind: "entries", parts: [] }];
|
|
188664
188684
|
const currentEntries = () => this.componentPropSegmentEntries(segments);
|
|
188685
|
+
const namedSlotSetBlocks = [];
|
|
188665
188686
|
for (const p of comp.props) {
|
|
188666
188687
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
188667
188688
|
continue;
|
|
188689
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
188690
|
+
const prevInLoop = this.inLoop;
|
|
188691
|
+
this.inLoop = false;
|
|
188692
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
188693
|
+
this.inLoop = prevInLoop;
|
|
188694
|
+
const captureName = `bf_prop_${this.childrenCaptureCounter++}`;
|
|
188695
|
+
namedSlotSetBlocks.push(`{% set ${captureName} %}${slotBody}{% endset %}`);
|
|
188696
|
+
currentEntries().push(`${jinjaHashKey(p.name)}: ${captureName}`);
|
|
188697
|
+
continue;
|
|
188698
|
+
}
|
|
188668
188699
|
if (p.value.kind === "spread") {
|
|
188669
188700
|
const trimmed = p.value.expr.trim();
|
|
188670
188701
|
if (this.propsObjectName && this.propsObjectName === trimmed) {
|
|
@@ -188693,11 +188724,11 @@ ${childrenUnderLoop}` : childrenUnderLoop;
|
|
|
188693
188724
|
const captureName = `bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
188694
188725
|
currentEntries().push(`${jinjaHashKey("children")}: ${captureName}`);
|
|
188695
188726
|
const dict = this.combineComponentPropSegments(segments);
|
|
188696
|
-
return
|
|
188727
|
+
return `${namedSlotSetBlocks.join("")}{% set ${captureName} %}${childrenBody}{% endset %}{{ bf.render_child('${tplName}', ${dict}) | safe }}`;
|
|
188697
188728
|
}
|
|
188698
188729
|
const isEmpty = segments.every((s) => s.kind === "entries" && s.parts.length === 0);
|
|
188699
188730
|
const dictEntries = isEmpty ? "" : `, ${this.combineComponentPropSegments(segments)}`;
|
|
188700
|
-
return
|
|
188731
|
+
return `${namedSlotSetBlocks.join("")}{{ bf.render_child('${tplName}'${dictEntries}) | safe }}`;
|
|
188701
188732
|
}
|
|
188702
188733
|
childrenCaptureCounter = 0;
|
|
188703
188734
|
presenceVarCounter = 0;
|
|
@@ -188742,7 +188773,7 @@ ${alternate}
|
|
|
188742
188773
|
${children}`;
|
|
188743
188774
|
}
|
|
188744
188775
|
elementAttrEmitter = {
|
|
188745
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
188776
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
188746
188777
|
emitExpression: (value, name) => {
|
|
188747
188778
|
if (name === "style") {
|
|
188748
188779
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -188850,9 +188881,10 @@ ${name}="{{ bf.string(${val}) }}"
|
|
|
188850
188881
|
let attrName;
|
|
188851
188882
|
if (attr.name === "className")
|
|
188852
188883
|
attrName = "class";
|
|
188853
|
-
else if (attr.name === "key")
|
|
188854
|
-
|
|
188855
|
-
|
|
188884
|
+
else if (attr.name === "key") {
|
|
188885
|
+
const depth = this.currentLoopKeyDepth;
|
|
188886
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
188887
|
+
} else
|
|
188856
188888
|
attrName = attr.name;
|
|
188857
188889
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
188858
188890
|
if (lowered)
|
|
@@ -113,6 +113,14 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
|
|
|
113
113
|
private options;
|
|
114
114
|
private errors;
|
|
115
115
|
private inLoop;
|
|
116
|
+
/**
|
|
117
|
+
* `IRLoop.depth` of the loop currently being rendered (save/restore
|
|
118
|
+
* around `renderChildren(loop.children)`, mirroring `inLoop` above).
|
|
119
|
+
* `renderAttributes` reads this to derive the `key` → `data-key`/
|
|
120
|
+
* `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
|
|
121
|
+
* re-derived here (#2168 nested-loop-outer-binding).
|
|
122
|
+
*/
|
|
123
|
+
private currentLoopKeyDepth;
|
|
116
124
|
/**
|
|
117
125
|
* SolidJS-style props identifier (`function(props: P)`) and the
|
|
118
126
|
* analyzer-extracted prop names. Stashed at `generate()` entry so the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,EAKP,yBAAyB,EAE1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAE3B,KAAK,aAAa,EAClB,KAAK,UAAU,EAyBhB,MAAM,iBAAiB,CAAA;AAKxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAiCpD,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAEzD,qBAAa,YAAa,SAAQ,WAAY,YAAW,aAAa,CAAC,cAAc,CAAC;IACpF,IAAI,SAAU;IACd,SAAS,SAAW;IACpB,qBAAqB,UAAO;IAG5B,kBAAkB,EAAG,cAAc,CAAS;IAE5C;;;;;OAKG;IACH,kBAAkB,EAAE,yBAAyB,CAA2B;IAExE,OAAO,CAAC,aAAa,CAAa;IAClC;;;uCAGmC;IACnC,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAAiB;IAC/B;;;;;OAKG;IACH,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,iBAAiB,CAAyB;IAClD;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAAyB;IAEjD;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB,CAAiC;IAE3D;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB,CAAyB;IAEpD;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAAwB;IAEjD;;;;;OAKG;IACH,OAAO,CAAC,cAAc,CAAmC;IAEzD;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB,CAAyB;IAEtD,YAAY,OAAO,GAAE,mBAAwB,EAM5C;IAED,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,aAAa,CA4EzE;IAMD,OAAO,CAAC,2BAA2B;IAyBnC;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/B;IAMD,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,MAAM,CAE5F;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAUnE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAG7E,CAAA"}
|
package/dist/build.js
CHANGED
|
@@ -187369,7 +187369,7 @@ function isAriaBooleanAttr(name) {
|
|
|
187369
187369
|
}
|
|
187370
187370
|
|
|
187371
187371
|
// src/adapter/jinja-adapter.ts
|
|
187372
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
187372
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
187373
187373
|
|
|
187374
187374
|
// src/adapter/lib/constants.ts
|
|
187375
187375
|
var JINJA_TEMPLATE_PRIMITIVES = {
|
|
@@ -187378,7 +187378,10 @@ var JINJA_TEMPLATE_PRIMITIVES = {
|
|
|
187378
187378
|
Number: { arity: 1, emit: (args) => `bf.number(${args[0]})` },
|
|
187379
187379
|
"Math.floor": { arity: 1, emit: (args) => `bf.floor(${args[0]})` },
|
|
187380
187380
|
"Math.ceil": { arity: 1, emit: (args) => `bf.ceil(${args[0]})` },
|
|
187381
|
-
"Math.round": { arity: 1, emit: (args) => `bf.round(${args[0]})` }
|
|
187381
|
+
"Math.round": { arity: 1, emit: (args) => `bf.round(${args[0]})` },
|
|
187382
|
+
"Math.min": { arity: 2, emit: (args) => `bf.min(${args[0]}, ${args[1]})` },
|
|
187383
|
+
"Math.max": { arity: 2, emit: (args) => `bf.max(${args[0]}, ${args[1]})` },
|
|
187384
|
+
"Math.abs": { arity: 1, emit: (args) => `bf.abs(${args[0]})` }
|
|
187382
187385
|
};
|
|
187383
187386
|
var JINJA_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(JINJA_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
187384
187387
|
|
|
@@ -187557,6 +187560,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
187557
187560
|
const recv = emit(object);
|
|
187558
187561
|
return `bf.trim(${recv})`;
|
|
187559
187562
|
}
|
|
187563
|
+
case "trimStart":
|
|
187564
|
+
case "trimEnd": {
|
|
187565
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
187566
|
+
const recv = emit(object);
|
|
187567
|
+
return `bf.${fn}(${recv})`;
|
|
187568
|
+
}
|
|
187560
187569
|
case "toFixed": {
|
|
187561
187570
|
const recv = emit(object);
|
|
187562
187571
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -187590,6 +187599,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
187590
187599
|
const newS = emit(args[1]);
|
|
187591
187600
|
return `bf.replace(${recv}, ${oldS}, ${newS})`;
|
|
187592
187601
|
}
|
|
187602
|
+
case "replaceAll": {
|
|
187603
|
+
const recv = emit(object);
|
|
187604
|
+
const oldS = emit(args[0]);
|
|
187605
|
+
const newS = emit(args[1]);
|
|
187606
|
+
return `bf.replace_all(${recv}, ${oldS}, ${newS})`;
|
|
187607
|
+
}
|
|
187593
187608
|
case "repeat": {
|
|
187594
187609
|
const recv = emit(object);
|
|
187595
187610
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -187732,11 +187747,11 @@ class JinjaFilterEmitter {
|
|
|
187732
187747
|
return "none";
|
|
187733
187748
|
return String(value);
|
|
187734
187749
|
}
|
|
187735
|
-
member(object, property, _computed, emit) {
|
|
187750
|
+
member(object, property, _computed, _optional, emit) {
|
|
187736
187751
|
if (property === "length") {
|
|
187737
187752
|
return `bf.length(${emit(object)})`;
|
|
187738
187753
|
}
|
|
187739
|
-
return `${emit(object)}
|
|
187754
|
+
return `${emit(object)}['${escapeJinjaSingleQuoted(property)}']`;
|
|
187740
187755
|
}
|
|
187741
187756
|
indexAccess(object, index, emit) {
|
|
187742
187757
|
return `${emit(object)}[${emit(index)}]`;
|
|
@@ -187838,7 +187853,7 @@ class JinjaTopLevelEmitter {
|
|
|
187838
187853
|
return "none";
|
|
187839
187854
|
return String(value);
|
|
187840
187855
|
}
|
|
187841
|
-
member(object, property, _computed, emit) {
|
|
187856
|
+
member(object, property, _computed, _optional, emit) {
|
|
187842
187857
|
if (object.kind === "identifier" && object.name === "props") {
|
|
187843
187858
|
return jinjaIdent(property);
|
|
187844
187859
|
}
|
|
@@ -187850,7 +187865,7 @@ class JinjaTopLevelEmitter {
|
|
|
187850
187865
|
const obj = emit(object);
|
|
187851
187866
|
if (property === "length")
|
|
187852
187867
|
return `bf.length(${obj})`;
|
|
187853
|
-
return `${obj}
|
|
187868
|
+
return `${obj}['${escapeJinjaSingleQuoted(property)}']`;
|
|
187854
187869
|
}
|
|
187855
187870
|
indexAccess(object, index, emit) {
|
|
187856
187871
|
return `${emit(object)}[${emit(index)}]`;
|
|
@@ -188257,6 +188272,7 @@ class JinjaAdapter extends BaseAdapter {
|
|
|
188257
188272
|
options;
|
|
188258
188273
|
errors = [];
|
|
188259
188274
|
inLoop = false;
|
|
188275
|
+
currentLoopKeyDepth = 0;
|
|
188260
188276
|
propsObjectName = null;
|
|
188261
188277
|
propsParams = [];
|
|
188262
188278
|
booleanTypedProps = new Set;
|
|
@@ -188333,7 +188349,7 @@ class JinjaAdapter extends BaseAdapter {
|
|
|
188333
188349
|
return this.renderElement(node);
|
|
188334
188350
|
}
|
|
188335
188351
|
emitText(node) {
|
|
188336
|
-
return node.value;
|
|
188352
|
+
return escapeHtml(node.value);
|
|
188337
188353
|
}
|
|
188338
188354
|
emitExpression(node) {
|
|
188339
188355
|
return this.renderExpression(node);
|
|
@@ -188564,7 +188580,7 @@ ${whenTrue}
|
|
|
188564
188580
|
const renderedChildren = this.renderChildren(loop.children);
|
|
188565
188581
|
const loopVar = loop.iterationShape === "keys" ? "__bf_item" : supportableDestructure ? "__bf_item" : param;
|
|
188566
188582
|
const indexLocalLines = [];
|
|
188567
|
-
if (loop.iterationShape === "keys") {
|
|
188583
|
+
if (loop.objectIteration) {} else if (loop.iterationShape === "keys") {
|
|
188568
188584
|
indexLocalLines.push(`{% set ${jinjaIdent(param)} = loop.index0 %}`);
|
|
188569
188585
|
} else if (loop.index) {
|
|
188570
188586
|
indexLocalLines.push(`{% set ${jinjaIdent(loop.index)} = loop.index0 %}`);
|
|
@@ -188584,13 +188600,17 @@ ${whenTrue}
|
|
|
188584
188600
|
}
|
|
188585
188601
|
const prevInLoop = this.inLoop;
|
|
188586
188602
|
this.inLoop = true;
|
|
188603
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
188604
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
188587
188605
|
const childrenUnderLoop = this.renderChildren(loop.children);
|
|
188606
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
188588
188607
|
this.inLoop = prevInLoop;
|
|
188589
188608
|
const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
|
|
188590
188609
|
${childrenUnderLoop}` : childrenUnderLoop;
|
|
188591
188610
|
const lines = [];
|
|
188592
188611
|
lines.push(`{{ bf.comment("loop:${loop.markerId}") | safe }}`);
|
|
188593
|
-
|
|
188612
|
+
const forHeader = loop.objectIteration === "entries" ? `{% for ${jinjaIdent(loop.index ?? param)}, ${jinjaIdent(param)} in ${array}.items() %}` : loop.objectIteration === "keys" ? `{% for ${jinjaIdent(param)} in ${array}.keys() %}` : loop.objectIteration === "values" ? `{% for ${jinjaIdent(param)} in ${array}.values() %}` : `{% for ${jinjaIdent(loopVar)} in ${array} %}`;
|
|
188613
|
+
lines.push(forHeader);
|
|
188594
188614
|
for (const il of indexLocalLines)
|
|
188595
188615
|
lines.push(il);
|
|
188596
188616
|
if (loop.filterPredicate) {
|
|
@@ -188662,9 +188682,20 @@ ${childrenUnderLoop}` : childrenUnderLoop;
|
|
|
188662
188682
|
renderComponent(comp) {
|
|
188663
188683
|
const segments = [{ kind: "entries", parts: [] }];
|
|
188664
188684
|
const currentEntries = () => this.componentPropSegmentEntries(segments);
|
|
188685
|
+
const namedSlotSetBlocks = [];
|
|
188665
188686
|
for (const p of comp.props) {
|
|
188666
188687
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
188667
188688
|
continue;
|
|
188689
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
188690
|
+
const prevInLoop = this.inLoop;
|
|
188691
|
+
this.inLoop = false;
|
|
188692
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
188693
|
+
this.inLoop = prevInLoop;
|
|
188694
|
+
const captureName = `bf_prop_${this.childrenCaptureCounter++}`;
|
|
188695
|
+
namedSlotSetBlocks.push(`{% set ${captureName} %}${slotBody}{% endset %}`);
|
|
188696
|
+
currentEntries().push(`${jinjaHashKey(p.name)}: ${captureName}`);
|
|
188697
|
+
continue;
|
|
188698
|
+
}
|
|
188668
188699
|
if (p.value.kind === "spread") {
|
|
188669
188700
|
const trimmed = p.value.expr.trim();
|
|
188670
188701
|
if (this.propsObjectName && this.propsObjectName === trimmed) {
|
|
@@ -188693,11 +188724,11 @@ ${childrenUnderLoop}` : childrenUnderLoop;
|
|
|
188693
188724
|
const captureName = `bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
188694
188725
|
currentEntries().push(`${jinjaHashKey("children")}: ${captureName}`);
|
|
188695
188726
|
const dict = this.combineComponentPropSegments(segments);
|
|
188696
|
-
return
|
|
188727
|
+
return `${namedSlotSetBlocks.join("")}{% set ${captureName} %}${childrenBody}{% endset %}{{ bf.render_child('${tplName}', ${dict}) | safe }}`;
|
|
188697
188728
|
}
|
|
188698
188729
|
const isEmpty = segments.every((s) => s.kind === "entries" && s.parts.length === 0);
|
|
188699
188730
|
const dictEntries = isEmpty ? "" : `, ${this.combineComponentPropSegments(segments)}`;
|
|
188700
|
-
return
|
|
188731
|
+
return `${namedSlotSetBlocks.join("")}{{ bf.render_child('${tplName}'${dictEntries}) | safe }}`;
|
|
188701
188732
|
}
|
|
188702
188733
|
childrenCaptureCounter = 0;
|
|
188703
188734
|
presenceVarCounter = 0;
|
|
@@ -188742,7 +188773,7 @@ ${alternate}
|
|
|
188742
188773
|
${children}`;
|
|
188743
188774
|
}
|
|
188744
188775
|
elementAttrEmitter = {
|
|
188745
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
188776
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
188746
188777
|
emitExpression: (value, name) => {
|
|
188747
188778
|
if (name === "style") {
|
|
188748
188779
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -188850,9 +188881,10 @@ ${name}="{{ bf.string(${val}) }}"
|
|
|
188850
188881
|
let attrName;
|
|
188851
188882
|
if (attr.name === "className")
|
|
188852
188883
|
attrName = "class";
|
|
188853
|
-
else if (attr.name === "key")
|
|
188854
|
-
|
|
188855
|
-
|
|
188884
|
+
else if (attr.name === "key") {
|
|
188885
|
+
const depth = this.currentLoopKeyDepth;
|
|
188886
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
188887
|
+
} else
|
|
188856
188888
|
attrName = attr.name;
|
|
188857
188889
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
188858
188890
|
if (lowered)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA8F7B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -187369,7 +187369,7 @@ function isAriaBooleanAttr(name) {
|
|
|
187369
187369
|
}
|
|
187370
187370
|
|
|
187371
187371
|
// src/adapter/jinja-adapter.ts
|
|
187372
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
187372
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
187373
187373
|
|
|
187374
187374
|
// src/adapter/lib/constants.ts
|
|
187375
187375
|
var JINJA_TEMPLATE_PRIMITIVES = {
|
|
@@ -187378,7 +187378,10 @@ var JINJA_TEMPLATE_PRIMITIVES = {
|
|
|
187378
187378
|
Number: { arity: 1, emit: (args) => `bf.number(${args[0]})` },
|
|
187379
187379
|
"Math.floor": { arity: 1, emit: (args) => `bf.floor(${args[0]})` },
|
|
187380
187380
|
"Math.ceil": { arity: 1, emit: (args) => `bf.ceil(${args[0]})` },
|
|
187381
|
-
"Math.round": { arity: 1, emit: (args) => `bf.round(${args[0]})` }
|
|
187381
|
+
"Math.round": { arity: 1, emit: (args) => `bf.round(${args[0]})` },
|
|
187382
|
+
"Math.min": { arity: 2, emit: (args) => `bf.min(${args[0]}, ${args[1]})` },
|
|
187383
|
+
"Math.max": { arity: 2, emit: (args) => `bf.max(${args[0]}, ${args[1]})` },
|
|
187384
|
+
"Math.abs": { arity: 1, emit: (args) => `bf.abs(${args[0]})` }
|
|
187382
187385
|
};
|
|
187383
187386
|
var JINJA_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(JINJA_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
187384
187387
|
|
|
@@ -187557,6 +187560,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
187557
187560
|
const recv = emit(object);
|
|
187558
187561
|
return `bf.trim(${recv})`;
|
|
187559
187562
|
}
|
|
187563
|
+
case "trimStart":
|
|
187564
|
+
case "trimEnd": {
|
|
187565
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
187566
|
+
const recv = emit(object);
|
|
187567
|
+
return `bf.${fn}(${recv})`;
|
|
187568
|
+
}
|
|
187560
187569
|
case "toFixed": {
|
|
187561
187570
|
const recv = emit(object);
|
|
187562
187571
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -187590,6 +187599,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
187590
187599
|
const newS = emit(args[1]);
|
|
187591
187600
|
return `bf.replace(${recv}, ${oldS}, ${newS})`;
|
|
187592
187601
|
}
|
|
187602
|
+
case "replaceAll": {
|
|
187603
|
+
const recv = emit(object);
|
|
187604
|
+
const oldS = emit(args[0]);
|
|
187605
|
+
const newS = emit(args[1]);
|
|
187606
|
+
return `bf.replace_all(${recv}, ${oldS}, ${newS})`;
|
|
187607
|
+
}
|
|
187593
187608
|
case "repeat": {
|
|
187594
187609
|
const recv = emit(object);
|
|
187595
187610
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -187732,11 +187747,11 @@ class JinjaFilterEmitter {
|
|
|
187732
187747
|
return "none";
|
|
187733
187748
|
return String(value);
|
|
187734
187749
|
}
|
|
187735
|
-
member(object, property, _computed, emit) {
|
|
187750
|
+
member(object, property, _computed, _optional, emit) {
|
|
187736
187751
|
if (property === "length") {
|
|
187737
187752
|
return `bf.length(${emit(object)})`;
|
|
187738
187753
|
}
|
|
187739
|
-
return `${emit(object)}
|
|
187754
|
+
return `${emit(object)}['${escapeJinjaSingleQuoted(property)}']`;
|
|
187740
187755
|
}
|
|
187741
187756
|
indexAccess(object, index, emit) {
|
|
187742
187757
|
return `${emit(object)}[${emit(index)}]`;
|
|
@@ -187838,7 +187853,7 @@ class JinjaTopLevelEmitter {
|
|
|
187838
187853
|
return "none";
|
|
187839
187854
|
return String(value);
|
|
187840
187855
|
}
|
|
187841
|
-
member(object, property, _computed, emit) {
|
|
187856
|
+
member(object, property, _computed, _optional, emit) {
|
|
187842
187857
|
if (object.kind === "identifier" && object.name === "props") {
|
|
187843
187858
|
return jinjaIdent(property);
|
|
187844
187859
|
}
|
|
@@ -187850,7 +187865,7 @@ class JinjaTopLevelEmitter {
|
|
|
187850
187865
|
const obj = emit(object);
|
|
187851
187866
|
if (property === "length")
|
|
187852
187867
|
return `bf.length(${obj})`;
|
|
187853
|
-
return `${obj}
|
|
187868
|
+
return `${obj}['${escapeJinjaSingleQuoted(property)}']`;
|
|
187854
187869
|
}
|
|
187855
187870
|
indexAccess(object, index, emit) {
|
|
187856
187871
|
return `${emit(object)}[${emit(index)}]`;
|
|
@@ -188257,6 +188272,7 @@ class JinjaAdapter extends BaseAdapter {
|
|
|
188257
188272
|
options;
|
|
188258
188273
|
errors = [];
|
|
188259
188274
|
inLoop = false;
|
|
188275
|
+
currentLoopKeyDepth = 0;
|
|
188260
188276
|
propsObjectName = null;
|
|
188261
188277
|
propsParams = [];
|
|
188262
188278
|
booleanTypedProps = new Set;
|
|
@@ -188333,7 +188349,7 @@ class JinjaAdapter extends BaseAdapter {
|
|
|
188333
188349
|
return this.renderElement(node);
|
|
188334
188350
|
}
|
|
188335
188351
|
emitText(node) {
|
|
188336
|
-
return node.value;
|
|
188352
|
+
return escapeHtml(node.value);
|
|
188337
188353
|
}
|
|
188338
188354
|
emitExpression(node) {
|
|
188339
188355
|
return this.renderExpression(node);
|
|
@@ -188564,7 +188580,7 @@ ${whenTrue}
|
|
|
188564
188580
|
const renderedChildren = this.renderChildren(loop.children);
|
|
188565
188581
|
const loopVar = loop.iterationShape === "keys" ? "__bf_item" : supportableDestructure ? "__bf_item" : param;
|
|
188566
188582
|
const indexLocalLines = [];
|
|
188567
|
-
if (loop.iterationShape === "keys") {
|
|
188583
|
+
if (loop.objectIteration) {} else if (loop.iterationShape === "keys") {
|
|
188568
188584
|
indexLocalLines.push(`{% set ${jinjaIdent(param)} = loop.index0 %}`);
|
|
188569
188585
|
} else if (loop.index) {
|
|
188570
188586
|
indexLocalLines.push(`{% set ${jinjaIdent(loop.index)} = loop.index0 %}`);
|
|
@@ -188584,13 +188600,17 @@ ${whenTrue}
|
|
|
188584
188600
|
}
|
|
188585
188601
|
const prevInLoop = this.inLoop;
|
|
188586
188602
|
this.inLoop = true;
|
|
188603
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
188604
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
188587
188605
|
const childrenUnderLoop = this.renderChildren(loop.children);
|
|
188606
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
188588
188607
|
this.inLoop = prevInLoop;
|
|
188589
188608
|
const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
|
|
188590
188609
|
${childrenUnderLoop}` : childrenUnderLoop;
|
|
188591
188610
|
const lines = [];
|
|
188592
188611
|
lines.push(`{{ bf.comment("loop:${loop.markerId}") | safe }}`);
|
|
188593
|
-
|
|
188612
|
+
const forHeader = loop.objectIteration === "entries" ? `{% for ${jinjaIdent(loop.index ?? param)}, ${jinjaIdent(param)} in ${array}.items() %}` : loop.objectIteration === "keys" ? `{% for ${jinjaIdent(param)} in ${array}.keys() %}` : loop.objectIteration === "values" ? `{% for ${jinjaIdent(param)} in ${array}.values() %}` : `{% for ${jinjaIdent(loopVar)} in ${array} %}`;
|
|
188613
|
+
lines.push(forHeader);
|
|
188594
188614
|
for (const il of indexLocalLines)
|
|
188595
188615
|
lines.push(il);
|
|
188596
188616
|
if (loop.filterPredicate) {
|
|
@@ -188662,9 +188682,20 @@ ${childrenUnderLoop}` : childrenUnderLoop;
|
|
|
188662
188682
|
renderComponent(comp) {
|
|
188663
188683
|
const segments = [{ kind: "entries", parts: [] }];
|
|
188664
188684
|
const currentEntries = () => this.componentPropSegmentEntries(segments);
|
|
188685
|
+
const namedSlotSetBlocks = [];
|
|
188665
188686
|
for (const p of comp.props) {
|
|
188666
188687
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
188667
188688
|
continue;
|
|
188689
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
188690
|
+
const prevInLoop = this.inLoop;
|
|
188691
|
+
this.inLoop = false;
|
|
188692
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
188693
|
+
this.inLoop = prevInLoop;
|
|
188694
|
+
const captureName = `bf_prop_${this.childrenCaptureCounter++}`;
|
|
188695
|
+
namedSlotSetBlocks.push(`{% set ${captureName} %}${slotBody}{% endset %}`);
|
|
188696
|
+
currentEntries().push(`${jinjaHashKey(p.name)}: ${captureName}`);
|
|
188697
|
+
continue;
|
|
188698
|
+
}
|
|
188668
188699
|
if (p.value.kind === "spread") {
|
|
188669
188700
|
const trimmed = p.value.expr.trim();
|
|
188670
188701
|
if (this.propsObjectName && this.propsObjectName === trimmed) {
|
|
@@ -188693,11 +188724,11 @@ ${childrenUnderLoop}` : childrenUnderLoop;
|
|
|
188693
188724
|
const captureName = `bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
188694
188725
|
currentEntries().push(`${jinjaHashKey("children")}: ${captureName}`);
|
|
188695
188726
|
const dict = this.combineComponentPropSegments(segments);
|
|
188696
|
-
return
|
|
188727
|
+
return `${namedSlotSetBlocks.join("")}{% set ${captureName} %}${childrenBody}{% endset %}{{ bf.render_child('${tplName}', ${dict}) | safe }}`;
|
|
188697
188728
|
}
|
|
188698
188729
|
const isEmpty = segments.every((s) => s.kind === "entries" && s.parts.length === 0);
|
|
188699
188730
|
const dictEntries = isEmpty ? "" : `, ${this.combineComponentPropSegments(segments)}`;
|
|
188700
|
-
return
|
|
188731
|
+
return `${namedSlotSetBlocks.join("")}{{ bf.render_child('${tplName}'${dictEntries}) | safe }}`;
|
|
188701
188732
|
}
|
|
188702
188733
|
childrenCaptureCounter = 0;
|
|
188703
188734
|
presenceVarCounter = 0;
|
|
@@ -188742,7 +188773,7 @@ ${alternate}
|
|
|
188742
188773
|
${children}`;
|
|
188743
188774
|
}
|
|
188744
188775
|
elementAttrEmitter = {
|
|
188745
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
188776
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
188746
188777
|
emitExpression: (value, name) => {
|
|
188747
188778
|
if (name === "style") {
|
|
188748
188779
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -188850,9 +188881,10 @@ ${name}="{{ bf.string(${val}) }}"
|
|
|
188850
188881
|
let attrName;
|
|
188851
188882
|
if (attr.name === "className")
|
|
188852
188883
|
attrName = "class";
|
|
188853
|
-
else if (attr.name === "key")
|
|
188854
|
-
|
|
188855
|
-
|
|
188884
|
+
else if (attr.name === "key") {
|
|
188885
|
+
const depth = this.currentLoopKeyDepth;
|
|
188886
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
188887
|
+
} else
|
|
188856
188888
|
attrName = attr.name;
|
|
188857
188889
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
188858
188890
|
if (lowered)
|
|
@@ -189111,20 +189143,10 @@ var conformancePins = {
|
|
|
189111
189143
|
{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
|
|
189112
189144
|
],
|
|
189113
189145
|
"array-map-function-reference": [{ code: "BF101", severity: "error" }],
|
|
189114
|
-
"dangerous-inner-html": [{ code: "BF101", severity: "error" }]
|
|
189115
|
-
"string-replaceall": [{ code: "BF101", severity: "error" }]
|
|
189146
|
+
"dangerous-inner-html": [{ code: "BF101", severity: "error" }]
|
|
189116
189147
|
};
|
|
189117
189148
|
// src/render-divergences.ts
|
|
189118
|
-
var renderDivergences = {
|
|
189119
|
-
"html-entity-text": "`©` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes",
|
|
189120
|
-
"math-methods": "Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)",
|
|
189121
|
-
"static-attr-escape": 'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
|
|
189122
|
-
"object-entries-map": "`Object.entries(prop).map(([k, v]) => …)` renders an EMPTY list — the object-shaped prop silently produces zero iterations",
|
|
189123
|
-
"nested-loop-outer-binding": "nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`",
|
|
189124
|
-
"jsx-element-prop": "a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped",
|
|
189125
|
-
"string-slice": "`.slice()` on a STRING renders empty (array-slice helper misfires on strings)",
|
|
189126
|
-
"string-trim-sided": "`.trimStart()` / `.trimEnd()` render empty (no lowering)"
|
|
189127
|
-
};
|
|
189149
|
+
var renderDivergences = {};
|
|
189128
189150
|
export {
|
|
189129
189151
|
renderDivergences,
|
|
189130
189152
|
jinjaAdapter,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/jinja",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.5",
|
|
4
4
|
"description": "Jinja2 adapter for BarefootJS — compiles IR to .jinja templates and ships the Python BarefootJS rendering runtime; runs under any Python web framework (Flask, etc.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
"directory": "packages/adapter-jinja"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@barefootjs/shared": "0.18.
|
|
56
|
+
"@barefootjs/shared": "0.18.5"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@barefootjs/jsx": ">=0.2.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
63
|
-
"@barefootjs/jsx": "0.18.
|
|
63
|
+
"@barefootjs/jsx": "0.18.5",
|
|
64
64
|
"typescript": "^5.0.0"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -819,6 +819,29 @@ class BarefootJS:
|
|
|
819
819
|
# is -1, not -2). `floor(n + 0.5)` reproduces that for both signs.
|
|
820
820
|
return float(math.floor(n + 0.5))
|
|
821
821
|
|
|
822
|
+
def min(self, a: Any, b: Any) -> float:
|
|
823
|
+
"""`Math.min(a, b)` -- two-arg form only (#2168 math-methods).
|
|
824
|
+
JS returns NaN if either operand is NaN."""
|
|
825
|
+
x, y = js_number(a), js_number(b)
|
|
826
|
+
if _is_nan(x):
|
|
827
|
+
return x
|
|
828
|
+
if _is_nan(y):
|
|
829
|
+
return y
|
|
830
|
+
return x if x < y else y
|
|
831
|
+
|
|
832
|
+
def max(self, a: Any, b: Any) -> float:
|
|
833
|
+
x, y = js_number(a), js_number(b)
|
|
834
|
+
if _is_nan(x):
|
|
835
|
+
return x
|
|
836
|
+
if _is_nan(y):
|
|
837
|
+
return y
|
|
838
|
+
return x if x > y else y
|
|
839
|
+
|
|
840
|
+
def abs(self, value: Any) -> float:
|
|
841
|
+
"""`Math.abs()` (#2168 math-methods)."""
|
|
842
|
+
n = js_number(value)
|
|
843
|
+
return n if _is_nan(n) else abs(n)
|
|
844
|
+
|
|
822
845
|
# -----------------------------------------------------------------
|
|
823
846
|
# Array / String method helpers (#1448 Tier A)
|
|
824
847
|
# -----------------------------------------------------------------
|
|
@@ -933,12 +956,20 @@ class BarefootJS:
|
|
|
933
956
|
out.extend(b)
|
|
934
957
|
return out
|
|
935
958
|
|
|
936
|
-
def slice(self, recv: Any, start: Any, end: Any) ->
|
|
937
|
-
|
|
959
|
+
def slice(self, recv: Any, start: Any, end: Any) -> Any:
|
|
960
|
+
# `Array.prototype.slice(start, end?)` AND `String.prototype.slice`
|
|
961
|
+
# (the `string-slice` divergence, #2182) -- the adapter emits the
|
|
962
|
+
# same `bf.slice(recv, start, end)` call for both receiver shapes
|
|
963
|
+
# (it can't disambiguate string vs. array at compile time), so
|
|
964
|
+
# this dispatches on the Python type, mirroring `includes` above.
|
|
965
|
+
# `str` and `list` share slicing semantics (`recv[s:e]`) and
|
|
966
|
+
# `len()`, so one clamp computation serves both; `str` indexing
|
|
967
|
+
# is by Unicode code point already, matching JS except for
|
|
968
|
+
# astral-plane input (the same divergence boundary every other
|
|
969
|
+
# adapter's pad/trim helpers already accept).
|
|
970
|
+
if not isinstance(recv, (list, str)):
|
|
938
971
|
return []
|
|
939
972
|
length = len(recv)
|
|
940
|
-
if length == 0:
|
|
941
|
-
return []
|
|
942
973
|
s = start if start is not None else 0
|
|
943
974
|
if s < 0:
|
|
944
975
|
s = length + s
|
|
@@ -950,8 +981,8 @@ class BarefootJS:
|
|
|
950
981
|
e = max(e, 0)
|
|
951
982
|
e = min(e, length)
|
|
952
983
|
if s >= e:
|
|
953
|
-
return []
|
|
954
|
-
return list(recv[s:e])
|
|
984
|
+
return recv[0:0]
|
|
985
|
+
return recv[s:e] if isinstance(recv, str) else list(recv[s:e])
|
|
955
986
|
|
|
956
987
|
def reverse(self, recv: Any) -> list:
|
|
957
988
|
if not isinstance(recv, list):
|
|
@@ -1014,6 +1045,20 @@ class BarefootJS:
|
|
|
1014
1045
|
return ""
|
|
1015
1046
|
return js_string(recv).strip()
|
|
1016
1047
|
|
|
1048
|
+
def trim_start(self, recv: Any) -> str:
|
|
1049
|
+
"""`String.prototype.trimStart()` -- the one-sided sibling of `trim`
|
|
1050
|
+
above (#2183 follow-up)."""
|
|
1051
|
+
if recv is None or isinstance(recv, (list, dict)):
|
|
1052
|
+
return ""
|
|
1053
|
+
return js_string(recv).lstrip()
|
|
1054
|
+
|
|
1055
|
+
def trim_end(self, recv: Any) -> str:
|
|
1056
|
+
"""`String.prototype.trimEnd()` -- the one-sided sibling of `trim`
|
|
1057
|
+
above (#2183 follow-up)."""
|
|
1058
|
+
if recv is None or isinstance(recv, (list, dict)):
|
|
1059
|
+
return ""
|
|
1060
|
+
return js_string(recv).rstrip()
|
|
1061
|
+
|
|
1017
1062
|
def to_fixed(self, value: Any, digits: int = 0) -> str:
|
|
1018
1063
|
n = self.number(value)
|
|
1019
1064
|
if _is_nan(n):
|
|
@@ -1076,6 +1121,19 @@ class BarefootJS:
|
|
|
1076
1121
|
return s
|
|
1077
1122
|
return s[:i] + n + s[i + len(o) :]
|
|
1078
1123
|
|
|
1124
|
+
def replace_all(self, recv: Any, pattern: Any, replacement: Any) -> str:
|
|
1125
|
+
"""`String.prototype.replaceAll(pattern, replacement)`, string-pattern
|
|
1126
|
+
form only (#2182) -- every occurrence, the all-occurrences sibling of
|
|
1127
|
+
`replace` above. Python's own `str.replace(old, new)` (no count arg)
|
|
1128
|
+
is already global by default, including the empty-pattern-inserts-
|
|
1129
|
+
at-every-boundary edge case (`"abc".replace("", "X")` -> "XaXbXcX"),
|
|
1130
|
+
so it needs no hand-rolled loop the way the other runtimes' first-
|
|
1131
|
+
occurrence-only native replace does."""
|
|
1132
|
+
s = _scalar_or_empty(recv)
|
|
1133
|
+
o = js_string(pattern)
|
|
1134
|
+
n = js_string(replacement)
|
|
1135
|
+
return s.replace(o, n)
|
|
1136
|
+
|
|
1079
1137
|
def query(self, base: Any, *triples: Any) -> str:
|
|
1080
1138
|
"""`queryHref(base, {...})` (#2042) -- build `"$base?k=v&..."` from a
|
|
1081
1139
|
flat list of (guard, key, value) triples. A pair is included iff its
|
|
@@ -117,13 +117,19 @@ BINDINGS = {
|
|
|
117
117
|
"floor": bf.floor,
|
|
118
118
|
"ceil": bf.ceil,
|
|
119
119
|
"round": bf.round,
|
|
120
|
+
"min": bf.min,
|
|
121
|
+
"max": bf.max,
|
|
122
|
+
"abs": bf.abs,
|
|
120
123
|
"to_fixed": lambda *a: bf.to_fixed(*a),
|
|
121
124
|
"lower": bf.lc,
|
|
122
125
|
"upper": bf.uc,
|
|
123
126
|
"trim": bf.trim,
|
|
127
|
+
"trim_start": bf.trim_start,
|
|
128
|
+
"trim_end": bf.trim_end,
|
|
124
129
|
"starts_with": lambda *a: bf.starts_with(*a),
|
|
125
130
|
"ends_with": lambda *a: bf.ends_with(*a),
|
|
126
131
|
"replace": lambda *a: bf.replace(*a),
|
|
132
|
+
"replace_all": lambda *a: bf.replace_all(*a),
|
|
127
133
|
"repeat": lambda *a: bf.repeat(*a),
|
|
128
134
|
"pad_start": lambda *a: bf.pad_start(*a),
|
|
129
135
|
"pad_end": lambda *a: bf.pad_end(*a),
|
|
@@ -74,6 +74,27 @@ class TemplatePrimitivesTest(unittest.TestCase):
|
|
|
74
74
|
self.assertEqual(self.bf.round(-1.6), -2)
|
|
75
75
|
self.assertTrue(_is_nan(self.bf.round("not")))
|
|
76
76
|
|
|
77
|
+
def test_min_max_abs(self):
|
|
78
|
+
# `Math.min(a, b)` / `Math.max(a, b)` (two-arg forms only) and
|
|
79
|
+
# `Math.abs()` (#2168 math-methods). JS returns NaN if EITHER
|
|
80
|
+
# min/max operand is NaN.
|
|
81
|
+
self.assertEqual(self.bf.min(3, 7), 3)
|
|
82
|
+
self.assertEqual(self.bf.min(7, 3), 3)
|
|
83
|
+
self.assertEqual(self.bf.min(-2, -5), -5)
|
|
84
|
+
self.assertTrue(_is_nan(self.bf.min("not", 5)))
|
|
85
|
+
self.assertTrue(_is_nan(self.bf.min(5, "not")))
|
|
86
|
+
|
|
87
|
+
self.assertEqual(self.bf.max(3, 7), 7)
|
|
88
|
+
self.assertEqual(self.bf.max(7, 3), 7)
|
|
89
|
+
self.assertEqual(self.bf.max(-2, -5), -2)
|
|
90
|
+
self.assertTrue(_is_nan(self.bf.max("not", 5)))
|
|
91
|
+
self.assertTrue(_is_nan(self.bf.max(5, "not")))
|
|
92
|
+
|
|
93
|
+
self.assertEqual(self.bf.abs(-7.6), 7.6)
|
|
94
|
+
self.assertEqual(self.bf.abs(7.6), 7.6)
|
|
95
|
+
self.assertEqual(self.bf.abs(0), 0)
|
|
96
|
+
self.assertTrue(_is_nan(self.bf.abs("not")))
|
|
97
|
+
|
|
77
98
|
def test_includes_dispatch(self):
|
|
78
99
|
# `Array.prototype.includes(x)` + `String.prototype.includes(sub)`
|
|
79
100
|
# lower to the same `bf.includes(recv, elem)` shape -- see #1448
|
|
@@ -158,6 +179,18 @@ class TemplatePrimitivesTest(unittest.TestCase):
|
|
|
158
179
|
out.append("mutated")
|
|
159
180
|
self.assertEqual(src, ["a", "b", "c"])
|
|
160
181
|
|
|
182
|
+
def test_slice_string_receiver(self):
|
|
183
|
+
# The `string-slice` divergence (#2182): a string receiver used
|
|
184
|
+
# to fall through the array-only branch and return an empty
|
|
185
|
+
# list instead of a substring.
|
|
186
|
+
word = "barefootjs"
|
|
187
|
+
self.assertEqual(self.bf.slice(word, 0, 4), "bare")
|
|
188
|
+
self.assertEqual(self.bf.slice(word, -4, None), "otjs")
|
|
189
|
+
self.assertEqual(self.bf.slice(word, 4, None), "footjs")
|
|
190
|
+
self.assertEqual(self.bf.slice(word, 5, 2), "")
|
|
191
|
+
# Multi-byte: index by character, not byte.
|
|
192
|
+
self.assertEqual(self.bf.slice("héllo", 0, 2), "hé")
|
|
193
|
+
|
|
161
194
|
def test_reverse_mutation_isolation(self):
|
|
162
195
|
self.assertEqual(self.bf.reverse(["a", "b", "c"]), ["c", "b", "a"])
|
|
163
196
|
self.assertEqual(self.bf.reverse([]), [])
|
|
@@ -175,6 +208,18 @@ class TemplatePrimitivesTest(unittest.TestCase):
|
|
|
175
208
|
self.assertEqual(self.bf.trim({"a": 1}), "")
|
|
176
209
|
self.assertEqual(self.bf.trim(42), "42")
|
|
177
210
|
|
|
211
|
+
def test_trim_start_and_trim_end(self):
|
|
212
|
+
# The one-sided siblings of `trim` above (#2183). Padding BOTH
|
|
213
|
+
# sides so a swapped side fails visibly.
|
|
214
|
+
self.assertEqual(self.bf.trim_start(" padded "), "padded ")
|
|
215
|
+
self.assertEqual(self.bf.trim_end(" padded "), " padded")
|
|
216
|
+
self.assertEqual(self.bf.trim_start(""), "")
|
|
217
|
+
self.assertEqual(self.bf.trim_end(""), "")
|
|
218
|
+
self.assertEqual(self.bf.trim_start(None), "")
|
|
219
|
+
self.assertEqual(self.bf.trim_end(None), "")
|
|
220
|
+
self.assertEqual(self.bf.trim_start({"a": 1}), "")
|
|
221
|
+
self.assertEqual(self.bf.trim_end([1, 2]), "")
|
|
222
|
+
|
|
178
223
|
def test_split(self):
|
|
179
224
|
self.assertEqual(self.bf.split("a,b,c", ","), ["a", "b", "c"])
|
|
180
225
|
self.assertEqual(self.bf.split("a.b.c", "."), ["a", "b", "c"])
|
|
@@ -383,6 +383,27 @@ export function C(props: { count: number }) {
|
|
|
383
383
|
})
|
|
384
384
|
})
|
|
385
385
|
|
|
386
|
+
describe('JinjaAdapter - named-slot capture identifier safety (#2168 jsx-element-prop)', () => {
|
|
387
|
+
// A JSX-valued prop under a hyphenated name (`data-slot`, a valid JSX
|
|
388
|
+
// attribute name) must not leak into the `{% set %}` capture variable's
|
|
389
|
+
// identifier — Jinja variable names can't contain `-`. The capture
|
|
390
|
+
// identifier is purely counter-based (never derived from the prop name);
|
|
391
|
+
// the hash KEY passed to `render_child` still carries the real name,
|
|
392
|
+
// quoted via `jinjaHashKey`.
|
|
393
|
+
test('a hyphenated prop name does not appear in the capture variable', () => {
|
|
394
|
+
const { template } = compileAndGenerate(`
|
|
395
|
+
function Card(props) { return null }
|
|
396
|
+
export function Parent() {
|
|
397
|
+
return <Card data-slot={<strong>Title</strong>}>text</Card>
|
|
398
|
+
}
|
|
399
|
+
`)
|
|
400
|
+
expect(template).toContain('{% set bf_prop_0 %}')
|
|
401
|
+
expect(template).toContain("'data-slot': bf_prop_0")
|
|
402
|
+
expect(template).not.toContain('data-slot %}')
|
|
403
|
+
expect(template).not.toContain('data-slot_')
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
|
|
386
407
|
// #2038 nested-callback-predicate loudness is pinned at the shared
|
|
387
408
|
// conformance layer (workstream C): `filter-nested-callback-predicate` /
|
|
388
409
|
// `filter-nested-find-predicate` (BF101 via `expectedDiagnostics`) and
|
|
@@ -91,6 +91,15 @@ export function renderArrayMethod(
|
|
|
91
91
|
const recv = emit(object)
|
|
92
92
|
return `bf.trim(${recv})`
|
|
93
93
|
}
|
|
94
|
+
case 'trimStart':
|
|
95
|
+
case 'trimEnd': {
|
|
96
|
+
// `.trimStart()` / `.trimEnd()` — the one-sided siblings of
|
|
97
|
+
// `.trim()` (#2183 follow-up). Dedicated `bf.trim_start` /
|
|
98
|
+
// `bf.trim_end` helpers, not `bf.trim` with a flag.
|
|
99
|
+
const fn = method === 'trimStart' ? 'trim_start' : 'trim_end'
|
|
100
|
+
const recv = emit(object)
|
|
101
|
+
return `bf.${fn}(${recv})`
|
|
102
|
+
}
|
|
94
103
|
case 'toFixed': {
|
|
95
104
|
// `.toFixed(digits?)` — `bf.to_fixed` mirrors JS rounding +
|
|
96
105
|
// zero-padding (default 0 digits). #1897.
|
|
@@ -126,6 +135,16 @@ export function renderArrayMethod(
|
|
|
126
135
|
const newS = emit(args[1])
|
|
127
136
|
return `bf.replace(${recv}, ${oldS}, ${newS})`
|
|
128
137
|
}
|
|
138
|
+
case 'replaceAll': {
|
|
139
|
+
// `.replaceAll(old, new)` — string-pattern form, EVERY occurrence,
|
|
140
|
+
// via the dedicated `bf.replace_all` helper (not `bf.replace` with
|
|
141
|
+
// a flag) — the regex-pattern form is refused upstream at the
|
|
142
|
+
// parser, same as `.replace`. See #2182.
|
|
143
|
+
const recv = emit(object)
|
|
144
|
+
const oldS = emit(args[0])
|
|
145
|
+
const newS = emit(args[1])
|
|
146
|
+
return `bf.replace_all(${recv}, ${oldS}, ${newS})`
|
|
147
|
+
}
|
|
129
148
|
case 'repeat': {
|
|
130
149
|
const recv = emit(object)
|
|
131
150
|
const count = args.length === 0 ? '0' : emit(args[0])
|
|
@@ -141,7 +141,7 @@ export class JinjaFilterEmitter implements ParsedExprEmitter {
|
|
|
141
141
|
return String(value)
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string {
|
|
144
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string {
|
|
145
145
|
// `.length` — route through `bf.length` (handles both array element
|
|
146
146
|
// count and string char count, JS-compatibly). Jinja's builtin
|
|
147
147
|
// `|length` filter also faults trying to match JS semantics for every
|
|
@@ -149,8 +149,16 @@ export class JinjaFilterEmitter implements ParsedExprEmitter {
|
|
|
149
149
|
if (property === 'length') {
|
|
150
150
|
return `bf.length(${emit(object)})`
|
|
151
151
|
}
|
|
152
|
-
//
|
|
153
|
-
|
|
152
|
+
// Bracket/item access, NOT `.` attribute access: Jinja's default
|
|
153
|
+
// `getattr` semantics try a Python ATTRIBUTE first, falling back to a
|
|
154
|
+
// dict key only if no such attribute exists — so `group.items` (a
|
|
155
|
+
// dict key from the JS object) instead resolves to the built-in bound
|
|
156
|
+
// method `dict.items`, raising "not iterable" downstream instead of
|
|
157
|
+
// returning the key's value. `[...]` compiles through Jinja's
|
|
158
|
+
// `getitem`, which tries the KEY first, sidestepping any built-in
|
|
159
|
+
// dict method name (items/keys/values/get/pop/update/...) that would
|
|
160
|
+
// otherwise shadow a same-named JS object field.
|
|
161
|
+
return `${emit(object)}['${escapeJinjaSingleQuoted(property)}']`
|
|
154
162
|
}
|
|
155
163
|
|
|
156
164
|
indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string {
|
|
@@ -313,7 +321,7 @@ export class JinjaTopLevelEmitter implements ParsedExprEmitter {
|
|
|
313
321
|
return String(value)
|
|
314
322
|
}
|
|
315
323
|
|
|
316
|
-
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string {
|
|
324
|
+
member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string {
|
|
317
325
|
// `props.x` flattens to the bare context var the SSR caller binds each
|
|
318
326
|
// prop to (props arrive as individual top-level context entries, not a
|
|
319
327
|
// nested `props` dict).
|
|
@@ -331,8 +339,13 @@ export class JinjaTopLevelEmitter implements ParsedExprEmitter {
|
|
|
331
339
|
const obj = emit(object)
|
|
332
340
|
// `.length` → `bf.length` (array count or string char count, JS-compat).
|
|
333
341
|
if (property === 'length') return `bf.length(${obj})`
|
|
334
|
-
//
|
|
335
|
-
|
|
342
|
+
// Bracket/item access, NOT `.` attribute access — see the sibling
|
|
343
|
+
// `JinjaFilterEmitter.member()` for why: Jinja's `.` tries a Python
|
|
344
|
+
// ATTRIBUTE first, so a dict key that happens to share a name with a
|
|
345
|
+
// built-in dict method (`items`, `keys`, `values`, `get`, ...) resolves
|
|
346
|
+
// to the bound method instead of the value. `[...]` (Jinja `getitem`)
|
|
347
|
+
// tries the key first.
|
|
348
|
+
return `${obj}['${escapeJinjaSingleQuoted(property)}']`
|
|
336
349
|
}
|
|
337
350
|
|
|
338
351
|
indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string {
|
|
@@ -143,7 +143,7 @@ import {
|
|
|
143
143
|
} from '@barefootjs/jsx'
|
|
144
144
|
import { isAriaBooleanAttr, isBooleanResultExpr, isExplicitStringCall } from './boolean-result.ts'
|
|
145
145
|
import type { ParsedExpr, LoweringMatcher } from '@barefootjs/jsx'
|
|
146
|
-
import { BF_SLOT, BF_COND, BF_REGION } from '@barefootjs/shared'
|
|
146
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from '@barefootjs/shared'
|
|
147
147
|
|
|
148
148
|
import type { JinjaRenderCtx } from './lib/types.ts'
|
|
149
149
|
import { JINJA_PRIMITIVE_EMIT_MAP } from './lib/constants.ts'
|
|
@@ -206,6 +206,14 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
206
206
|
private options: Required<JinjaAdapterOptions>
|
|
207
207
|
private errors: CompilerError[] = []
|
|
208
208
|
private inLoop: boolean = false
|
|
209
|
+
/**
|
|
210
|
+
* `IRLoop.depth` of the loop currently being rendered (save/restore
|
|
211
|
+
* around `renderChildren(loop.children)`, mirroring `inLoop` above).
|
|
212
|
+
* `renderAttributes` reads this to derive the `key` → `data-key`/
|
|
213
|
+
* `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
|
|
214
|
+
* re-derived here (#2168 nested-loop-outer-binding).
|
|
215
|
+
*/
|
|
216
|
+
private currentLoopKeyDepth = 0
|
|
209
217
|
/**
|
|
210
218
|
* SolidJS-style props identifier (`function(props: P)`) and the
|
|
211
219
|
* analyzer-extracted prop names. Stashed at `generate()` entry so the
|
|
@@ -401,7 +409,9 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
401
409
|
}
|
|
402
410
|
|
|
403
411
|
emitText(node: IRText): string {
|
|
404
|
-
|
|
412
|
+
// IRText carries the entity-DECODED value (Phase 1 decodes JSX
|
|
413
|
+
// character references); re-escape for direct HTML emission.
|
|
414
|
+
return escapeHtml(node.value)
|
|
405
415
|
}
|
|
406
416
|
|
|
407
417
|
emitExpression(node: IRExpression): string {
|
|
@@ -777,7 +787,11 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
777
787
|
// `{...rest}` → `bf.spread_attrs(rest)` emit path both see only the
|
|
778
788
|
// keys NOT already destructured.
|
|
779
789
|
const indexLocalLines: string[] = []
|
|
780
|
-
if (loop.
|
|
790
|
+
if (loop.objectIteration) {
|
|
791
|
+
// `key`/`value` bind directly in the for-header (see `array` below)
|
|
792
|
+
// via Jinja's own dict iteration — no derived `loop.index0` local
|
|
793
|
+
// needed, unlike the array `iterationShape` cases.
|
|
794
|
+
} else if (loop.iterationShape === 'keys') {
|
|
781
795
|
indexLocalLines.push(`{% set ${jinjaIdent(param)} = loop.index0 %}`)
|
|
782
796
|
} else if (loop.index) {
|
|
783
797
|
indexLocalLines.push(`{% set ${jinjaIdent(loop.index)} = loop.index0 %}`)
|
|
@@ -802,10 +816,13 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
802
816
|
|
|
803
817
|
const prevInLoop = this.inLoop
|
|
804
818
|
this.inLoop = true
|
|
819
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth
|
|
820
|
+
this.currentLoopKeyDepth = loop.depth
|
|
805
821
|
// Re-render children now that inLoop is set (so nested components use the
|
|
806
822
|
// loop-child naming convention). renderedChildren above was computed with
|
|
807
823
|
// the previous flag; recompute under the loop flag.
|
|
808
824
|
const childrenUnderLoop = this.renderChildren(loop.children)
|
|
825
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth
|
|
809
826
|
this.inLoop = prevInLoop
|
|
810
827
|
void renderedChildren
|
|
811
828
|
|
|
@@ -822,7 +839,19 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
822
839
|
// Scoped per-call-site marker so sibling `.map()`s under the same parent
|
|
823
840
|
// each get their own reconciliation range.
|
|
824
841
|
lines.push(`{{ bf.comment("loop:${loop.markerId}") | safe }}`)
|
|
825
|
-
|
|
842
|
+
// `objectIteration` (#2168 object-entries-map): Python `dict` preserves
|
|
843
|
+
// JS `Object.entries()`'s insertion-order semantics natively, so this
|
|
844
|
+
// lowers straight to Jinja's own dict-iteration forms — no runtime
|
|
845
|
+
// helper needed. `.items()` binds `index` (the KEY) alongside `param`;
|
|
846
|
+
// `.keys()`/`.values()` bind `param` alone.
|
|
847
|
+
const forHeader = loop.objectIteration === 'entries'
|
|
848
|
+
? `{% for ${jinjaIdent(loop.index ?? param)}, ${jinjaIdent(param)} in ${array}.items() %}`
|
|
849
|
+
: loop.objectIteration === 'keys'
|
|
850
|
+
? `{% for ${jinjaIdent(param)} in ${array}.keys() %}`
|
|
851
|
+
: loop.objectIteration === 'values'
|
|
852
|
+
? `{% for ${jinjaIdent(param)} in ${array}.values() %}`
|
|
853
|
+
: `{% for ${jinjaIdent(loopVar)} in ${array} %}`
|
|
854
|
+
lines.push(forHeader)
|
|
826
855
|
for (const il of indexLocalLines) lines.push(il)
|
|
827
856
|
|
|
828
857
|
// Handle filter().map() pattern by wrapping children in if-condition
|
|
@@ -974,11 +1003,33 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
974
1003
|
type Segment = { kind: 'entries'; parts: string[] } | { kind: 'spread'; expr: string }
|
|
975
1004
|
const segments: Segment[] = [{ kind: 'entries', parts: [] }]
|
|
976
1005
|
const currentEntries = () => this.componentPropSegmentEntries(segments)
|
|
1006
|
+
// Named JSX-valued props OTHER than the reserved `children`
|
|
1007
|
+
// (`header={<strong>Title</strong>}`, #2168 jsx-element-prop) each get
|
|
1008
|
+
// their own `{% set %}` capture, prepended to the final returned
|
|
1009
|
+
// string below — same mechanism as the reserved children capture,
|
|
1010
|
+
// just keyed by the prop's own name instead of `children`.
|
|
1011
|
+
const namedSlotSetBlocks: string[] = []
|
|
977
1012
|
|
|
978
1013
|
for (const p of comp.props) {
|
|
979
1014
|
// Skip callback props (onXxx) and `ref` — both are client-only for
|
|
980
1015
|
// SSR (Hono renders neither; the client JS wires them at hydration).
|
|
981
1016
|
if ((p.name.match(/^on[A-Z]/) || p.name === 'ref') && p.value.kind === 'expression') continue
|
|
1017
|
+
if (p.value.kind === 'jsx-children' && p.name !== 'children') {
|
|
1018
|
+
const prevInLoop = this.inLoop
|
|
1019
|
+
this.inLoop = false
|
|
1020
|
+
const slotBody = this.renderChildren(p.value.children)
|
|
1021
|
+
this.inLoop = prevInLoop
|
|
1022
|
+
// Purely counter-based — NOT derived from `p.name` or `comp.slotId`.
|
|
1023
|
+
// A JSX prop name can contain characters (`data-slot`) that aren't a
|
|
1024
|
+
// valid Jinja `{% set %}` target, and `comp.slotId` alone would
|
|
1025
|
+
// collide across two named-slot props on the same component
|
|
1026
|
+
// invocation (unlike the reserved children slot, there's only ever
|
|
1027
|
+
// one of those per invocation).
|
|
1028
|
+
const captureName = `bf_prop_${this.childrenCaptureCounter++}`
|
|
1029
|
+
namedSlotSetBlocks.push(`{% set ${captureName} %}${slotBody}{% endset %}`)
|
|
1030
|
+
currentEntries().push(`${jinjaHashKey(p.name)}: ${captureName}`)
|
|
1031
|
+
continue
|
|
1032
|
+
}
|
|
982
1033
|
if (p.value.kind === 'spread') {
|
|
983
1034
|
const trimmed = p.value.expr.trim()
|
|
984
1035
|
// SolidJS-style props identifier (`function(props: P)`) has no
|
|
@@ -1042,12 +1093,12 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
1042
1093
|
const captureName = `bf_children_${comp.slotId ?? 'c' + this.childrenCaptureCounter++}`
|
|
1043
1094
|
currentEntries().push(`${jinjaHashKey('children')}: ${captureName}`)
|
|
1044
1095
|
const dict = this.combineComponentPropSegments(segments)
|
|
1045
|
-
return
|
|
1096
|
+
return `${namedSlotSetBlocks.join('')}{% set ${captureName} %}${childrenBody}{% endset %}{{ bf.render_child('${tplName}', ${dict}) | safe }}`
|
|
1046
1097
|
}
|
|
1047
1098
|
|
|
1048
1099
|
const isEmpty = segments.every(s => s.kind === 'entries' && s.parts.length === 0)
|
|
1049
1100
|
const dictEntries = isEmpty ? '' : `, ${this.combineComponentPropSegments(segments)}`
|
|
1050
|
-
return
|
|
1101
|
+
return `${namedSlotSetBlocks.join('')}{{ bf.render_child('${tplName}'${dictEntries}) | safe }}`
|
|
1051
1102
|
}
|
|
1052
1103
|
|
|
1053
1104
|
private childrenCaptureCounter = 0
|
|
@@ -1132,7 +1183,7 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
1132
1183
|
* AttrValue lowering for intrinsic-element attributes (Jinja).
|
|
1133
1184
|
*/
|
|
1134
1185
|
private readonly elementAttrEmitter: AttrValueEmitter = {
|
|
1135
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1186
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
1136
1187
|
emitExpression: (value, name) => {
|
|
1137
1188
|
// `style={{ … }}` object literal → a CSS string with dynamic values
|
|
1138
1189
|
// interpolated, instead of refusing the bare object with BF101 (#1322).
|
|
@@ -1337,7 +1388,10 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
|
|
|
1337
1388
|
// Rewrite JSX special-prop names to their HTML-attribute counterparts.
|
|
1338
1389
|
let attrName: string
|
|
1339
1390
|
if (attr.name === 'className') attrName = 'class'
|
|
1340
|
-
else if (attr.name === 'key')
|
|
1391
|
+
else if (attr.name === 'key') {
|
|
1392
|
+
const depth = this.currentLoopKeyDepth
|
|
1393
|
+
attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
|
|
1394
|
+
}
|
|
1341
1395
|
else attrName = attr.name
|
|
1342
1396
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
|
|
1343
1397
|
if (lowered) parts.push(lowered)
|
|
@@ -22,6 +22,9 @@ export const JINJA_TEMPLATE_PRIMITIVES: Record<string, PrimitiveSpec> = {
|
|
|
22
22
|
'Math.floor': { arity: 1, emit: (args) => `bf.floor(${args[0]})` },
|
|
23
23
|
'Math.ceil': { arity: 1, emit: (args) => `bf.ceil(${args[0]})` },
|
|
24
24
|
'Math.round': { arity: 1, emit: (args) => `bf.round(${args[0]})` },
|
|
25
|
+
'Math.min': { arity: 2, emit: (args) => `bf.min(${args[0]}, ${args[1]})` },
|
|
26
|
+
'Math.max': { arity: 2, emit: (args) => `bf.max(${args[0]}, ${args[1]})` },
|
|
27
|
+
'Math.abs': { arity: 1, emit: (args) => `bf.abs(${args[0]})` },
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
/**
|
package/src/conformance-pins.ts
CHANGED
|
@@ -104,9 +104,4 @@ export const conformancePins: ConformancePins = {
|
|
|
104
104
|
// the shape loudly instead of emitting entity-escaped markup that
|
|
105
105
|
// silently renders tags as text.
|
|
106
106
|
'dangerous-inner-html': [{ code: 'BF101', severity: 'error' }],
|
|
107
|
-
// Edge-case sweep (Priority 12): `.replaceAll` has no lowering yet —
|
|
108
|
-
// only first-occurrence `.replace` is wired to the runtime helpers.
|
|
109
|
-
// Refused with BF101 rather than reusing the first-only lowering,
|
|
110
|
-
// which would silently change semantics.
|
|
111
|
-
'string-replaceall': [{ code: 'BF101', severity: 'error' }],
|
|
112
107
|
}
|
|
@@ -14,21 +14,4 @@
|
|
|
14
14
|
|
|
15
15
|
import type { RenderDivergences } from '@barefootjs/jsx'
|
|
16
16
|
|
|
17
|
-
export const renderDivergences: RenderDivergences = {
|
|
18
|
-
'html-entity-text':
|
|
19
|
-
'`©` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes',
|
|
20
|
-
'math-methods':
|
|
21
|
-
'Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)',
|
|
22
|
-
'static-attr-escape':
|
|
23
|
-
'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
|
|
24
|
-
'object-entries-map':
|
|
25
|
-
'`Object.entries(prop).map(([k, v]) => …)` renders an EMPTY list — the object-shaped prop silently produces zero iterations',
|
|
26
|
-
'nested-loop-outer-binding':
|
|
27
|
-
'nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`',
|
|
28
|
-
'jsx-element-prop':
|
|
29
|
-
'a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped',
|
|
30
|
-
'string-slice':
|
|
31
|
-
'`.slice()` on a STRING renders empty (array-slice helper misfires on strings)',
|
|
32
|
-
'string-trim-sided':
|
|
33
|
-
'`.trimStart()` / `.trimEnd()` render empty (no lowering)',
|
|
34
|
-
}
|
|
17
|
+
export const renderDivergences: RenderDivergences = {}
|