@barefootjs/go-template 0.6.0 → 0.7.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/dist/adapter/go-template-adapter.d.ts +47 -2
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +123 -20
- package/dist/build.js +123 -20
- package/dist/index.js +123 -20
- package/package.json +2 -2
- package/src/__tests__/go-template-adapter.test.ts +150 -14
- package/src/__tests__/slot-dynamic-tag.test.ts +79 -0
- package/src/adapter/go-template-adapter.ts +280 -11
- package/src/test-render.ts +41 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generates Go html/template files from BarefootJS IR.
|
|
5
5
|
*/
|
|
6
|
-
import type { ComponentIR, IRNode, IRElement, IRText, IRExpression, IRConditional, IRLoop, IRComponent, IRFragment, IRSlot, ParsedExpr, SortComparator, TemplatePart, IRIfStatement, IRProvider, IRAsync, TemplatePrimitiveRegistry } from '@barefootjs/jsx';
|
|
6
|
+
import type { ComponentIR, IRNode, IRElement, IRText, IRExpression, IRConditional, IRLoop, IRComponent, IRFragment, IRSlot, ParsedExpr, SortComparator, ReduceOp, FlatDepth, FlatMapOp, TemplatePart, IRIfStatement, IRProvider, IRAsync, TemplatePrimitiveRegistry } from '@barefootjs/jsx';
|
|
7
7
|
import { BaseAdapter, type AdapterOutput, type AdapterGenerateOptions, type ParsedExprEmitter, type HigherOrderMethod, type ArrayMethod, type LiteralType, type IRNodeEmitter, type EmitIRNode } from '@barefootjs/jsx';
|
|
8
8
|
/**
|
|
9
9
|
* Go-template adapter's IRNode render context. Only `isRootOfClientComponent`
|
|
@@ -109,6 +109,20 @@ export declare class GoTemplateAdapter extends BaseAdapter implements ParsedExpr
|
|
|
109
109
|
/** Set during type generation when any emit references
|
|
110
110
|
* `template.HTML(...)`; toggles the `"html/template"` import. */
|
|
111
111
|
private usesHtmlTemplate;
|
|
112
|
+
/**
|
|
113
|
+
* Module-scope pure string-literal constants (`const X = 'literal'` at
|
|
114
|
+
* file top-level), keyed by name → resolved literal value. Populated at
|
|
115
|
+
* `generate()` entry from `ir.metadata.localConstants`. When an identifier
|
|
116
|
+
* in an expression resolves to one of these, the adapter inlines the
|
|
117
|
+
* literal value instead of emitting a struct-field reference
|
|
118
|
+
* (`{{.X}}`) — the field never exists on the Props struct, so without
|
|
119
|
+
* inlining Go's template engine fails with `can't evaluate field X`.
|
|
120
|
+
* Hono inlines it for free (it evaluates real JS); this restores parity.
|
|
121
|
+
* Only module-scope, pure string literals qualify — function-scope
|
|
122
|
+
* locals legitimately become template vars/props, and `Record<T,string>`
|
|
123
|
+
* indexed lookups / memos / signals are deliberately excluded.
|
|
124
|
+
*/
|
|
125
|
+
private moduleStringConsts;
|
|
112
126
|
constructor(options?: GoTemplateAdapterOptions);
|
|
113
127
|
/**
|
|
114
128
|
* Generate template output for a component.
|
|
@@ -469,7 +483,7 @@ export declare class GoTemplateAdapter extends BaseAdapter implements ParsedExpr
|
|
|
469
483
|
*/
|
|
470
484
|
private static GO_IDENTIFIER;
|
|
471
485
|
/** Go common initialisms that should be fully uppercased (https://go.dev/wiki/CodeReviewComments#initialisms) */
|
|
472
|
-
|
|
486
|
+
static GO_INITIALISMS: Set<string>;
|
|
473
487
|
/**
|
|
474
488
|
* (#1423) Go reserved keywords. When we hoist a local var named after
|
|
475
489
|
* a JSX prop, the prop name could collide with one of these — append
|
|
@@ -554,6 +568,34 @@ export declare class GoTemplateAdapter extends BaseAdapter implements ParsedExpr
|
|
|
554
568
|
* rebinds. Outside any loop the root *is* the dot, so we emit `.Field` (#1677).
|
|
555
569
|
*/
|
|
556
570
|
private rootFieldRef;
|
|
571
|
+
/**
|
|
572
|
+
* Build the module pure-string-const map from the IR's localConstants.
|
|
573
|
+
* A const qualifies only when it is module-scope (`isModule`) and its
|
|
574
|
+
* initializer parses to a single string literal (`ts.StringLiteral` or
|
|
575
|
+
* `ts.NoSubstitutionTemplateLiteral` — a backtick string with no `${}`).
|
|
576
|
+
* Template literals *with* interpolations, numeric/object initializers,
|
|
577
|
+
* `Record<T,string>` maps, memos, and signals are all excluded: only a
|
|
578
|
+
* pure compile-time string can be safely inlined byte-for-byte.
|
|
579
|
+
*/
|
|
580
|
+
private collectModuleStringConsts;
|
|
581
|
+
/**
|
|
582
|
+
* Parse a const initializer's source text. Returns the unescaped string
|
|
583
|
+
* value when the whole initializer is a single string literal (or a
|
|
584
|
+
* no-substitution template literal), else `null`. Uses the TS parser so
|
|
585
|
+
* escapes/quotes are resolved exactly as JS would, matching the value
|
|
586
|
+
* the Hono reference inlines at runtime.
|
|
587
|
+
*/
|
|
588
|
+
private parsePureStringLiteral;
|
|
589
|
+
/**
|
|
590
|
+
* Resolve an identifier to its inlined Go string literal when it names a
|
|
591
|
+
* module pure-string const. Returns the Go template literal form
|
|
592
|
+
* (`"<escaped>"`) so callers can drop it straight into a `{{...}}` action,
|
|
593
|
+
* or `null` when the name is not such a const (the caller then falls back
|
|
594
|
+
* to its normal field-ref lowering). The value is escaped for a Go
|
|
595
|
+
* double-quoted string literal — Go's `html/template` then applies the
|
|
596
|
+
* same contextual auto-escaping it applies to any literal, matching Hono.
|
|
597
|
+
*/
|
|
598
|
+
private resolveModuleStringConst;
|
|
557
599
|
literal(value: string | number | boolean | null, literalType: LiteralType): string;
|
|
558
600
|
call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
|
|
559
601
|
member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
|
|
@@ -567,6 +609,9 @@ export declare class GoTemplateAdapter extends BaseAdapter implements ParsedExpr
|
|
|
567
609
|
higherOrder(method: HigherOrderMethod, object: ParsedExpr, param: string, predicate: ParsedExpr, emit: (e: ParsedExpr) => string): string;
|
|
568
610
|
arrayMethod(method: ArrayMethod, object: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
|
|
569
611
|
sortMethod(method: 'sort' | 'toSorted', object: ParsedExpr, comparator: SortComparator, emit: (e: ParsedExpr) => string): string;
|
|
612
|
+
reduceMethod(method: 'reduce' | 'reduceRight', object: ParsedExpr, reduceOp: ReduceOp, emit: (e: ParsedExpr) => string): string;
|
|
613
|
+
flatMethod(object: ParsedExpr, depth: FlatDepth, emit: (e: ParsedExpr) => string): string;
|
|
614
|
+
flatMapMethod(object: ParsedExpr, op: FlatMapOp, emit: (e: ParsedExpr) => string): string;
|
|
570
615
|
unsupported(raw: string, _reason: string): string;
|
|
571
616
|
/**
|
|
572
617
|
* Extract field name and negation from a simple predicate.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"go-template-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/go-template-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EAEN,WAAW,EACX,UAAU,EACV,MAAM,EAON,UAAU,EAEV,cAAc,EACd,YAAY,EACZ,aAAa,EACb,UAAU,EACV,OAAO,
|
|
1
|
+
{"version":3,"file":"go-template-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/go-template-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -51,8 +51,20 @@ function emitBfSort(recv, c) {
|
|
|
51
51
|
});
|
|
52
52
|
return `bf_sort ${wrapIfMultiToken(recv)} ${groups.join(" ")}`;
|
|
53
53
|
}
|
|
54
|
+
function emitBfReduce(recv, op, direction) {
|
|
55
|
+
const keyName = op.key.kind === "field" ? capitalize(op.key.field) : "";
|
|
56
|
+
return `bf_reduce ${wrapIfMultiToken(recv)} "${op.op}" "${op.key.kind}" "${keyName}" "${op.type}" "${escapeGoString(op.init)}" "${direction}"`;
|
|
57
|
+
}
|
|
58
|
+
function escapeGoString(s) {
|
|
59
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
60
|
+
}
|
|
54
61
|
function capitalize(s) {
|
|
55
|
-
|
|
62
|
+
if (s.length === 0)
|
|
63
|
+
return s;
|
|
64
|
+
if (GoTemplateAdapter.GO_INITIALISMS.has(s.toLowerCase())) {
|
|
65
|
+
return s.toUpperCase();
|
|
66
|
+
}
|
|
67
|
+
return s[0].toUpperCase() + s.slice(1);
|
|
56
68
|
}
|
|
57
69
|
function slotIdToFieldSuffix(slotId) {
|
|
58
70
|
const cleanId = slotId.startsWith("^") ? slotId.slice(1) : slotId;
|
|
@@ -62,6 +74,18 @@ function slotIdToFieldSuffix(slotId) {
|
|
|
62
74
|
}
|
|
63
75
|
return cleanId.replace("slot_", "Slot");
|
|
64
76
|
}
|
|
77
|
+
var GO_REMEDIATION_OPTIONS = `Options:
|
|
78
|
+
1. Use @client directive for client-side evaluation
|
|
79
|
+
2. Pre-compute the value in Go code`;
|
|
80
|
+
function buildUnsupportedSuggestion(support) {
|
|
81
|
+
if (!support.reason)
|
|
82
|
+
return GO_REMEDIATION_OPTIONS;
|
|
83
|
+
if (support.selfContained)
|
|
84
|
+
return support.reason;
|
|
85
|
+
return `${support.reason}
|
|
86
|
+
|
|
87
|
+
${GO_REMEDIATION_OPTIONS}`;
|
|
88
|
+
}
|
|
65
89
|
var GO_TEMPLATE_PRIMITIVES = {
|
|
66
90
|
"JSON.stringify": { arity: 1, emit: (args) => `bf_json ${args[0]}` },
|
|
67
91
|
String: { arity: 1, emit: (args) => `bf_string ${args[0]}` },
|
|
@@ -93,6 +117,7 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
93
117
|
localStructFields = new Map;
|
|
94
118
|
synthStructTypes = new Map;
|
|
95
119
|
usesHtmlTemplate = false;
|
|
120
|
+
moduleStringConsts = new Map;
|
|
96
121
|
constructor(options = {}) {
|
|
97
122
|
super();
|
|
98
123
|
this.options = {
|
|
@@ -107,6 +132,7 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
107
132
|
this.templateVarCounter = 0;
|
|
108
133
|
this.propsObjectName = ir.metadata.propsObjectName;
|
|
109
134
|
this.restPropsName = ir.metadata.restPropsName ?? null;
|
|
135
|
+
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
110
136
|
if (!options?.siblingTemplatesRegistered) {
|
|
111
137
|
this.checkImportedLoopChildComponents(ir);
|
|
112
138
|
}
|
|
@@ -888,6 +914,12 @@ ${goFields.join(`
|
|
|
888
914
|
collectStaticChildInstancesRecursive(node, result, inLoop) {
|
|
889
915
|
if (node.type === "component") {
|
|
890
916
|
const comp = node;
|
|
917
|
+
if (comp.dynamicTag) {
|
|
918
|
+
for (const child of comp.children) {
|
|
919
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
920
|
+
}
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
891
923
|
if (comp.name !== "Portal" && !inLoop && comp.slotId) {
|
|
892
924
|
const suffix = slotIdToFieldSuffix(comp.slotId);
|
|
893
925
|
result.push({
|
|
@@ -1724,6 +1756,9 @@ ${goFields.join(`
|
|
|
1724
1756
|
return emitParsedExpr(expr, this);
|
|
1725
1757
|
}
|
|
1726
1758
|
identifier(name) {
|
|
1759
|
+
const inlined = this.resolveModuleStringConst(name);
|
|
1760
|
+
if (inlined !== null)
|
|
1761
|
+
return inlined;
|
|
1727
1762
|
const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
|
|
1728
1763
|
if (currentLoopParam && name === currentLoopParam)
|
|
1729
1764
|
return ".";
|
|
@@ -1745,6 +1780,48 @@ ${goFields.join(`
|
|
|
1745
1780
|
const prefix = this.loopParamStack.length > 0 ? "$." : ".";
|
|
1746
1781
|
return `${prefix}${this.capitalizeFieldName(name)}`;
|
|
1747
1782
|
}
|
|
1783
|
+
collectModuleStringConsts(constants) {
|
|
1784
|
+
const map = new Map;
|
|
1785
|
+
for (const c of constants ?? []) {
|
|
1786
|
+
if (!c.isModule)
|
|
1787
|
+
continue;
|
|
1788
|
+
if (c.value === undefined)
|
|
1789
|
+
continue;
|
|
1790
|
+
const literal = this.parsePureStringLiteral(c.value);
|
|
1791
|
+
if (literal !== null)
|
|
1792
|
+
map.set(c.name, literal);
|
|
1793
|
+
}
|
|
1794
|
+
return map;
|
|
1795
|
+
}
|
|
1796
|
+
parsePureStringLiteral(source) {
|
|
1797
|
+
const sf = ts.createSourceFile("__const.ts", `const __x = (${source});`, ts.ScriptTarget.Latest, false);
|
|
1798
|
+
const stmt = sf.statements[0];
|
|
1799
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
1800
|
+
return null;
|
|
1801
|
+
const decl = stmt.declarationList.declarations[0];
|
|
1802
|
+
let init = decl?.initializer;
|
|
1803
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
1804
|
+
init = init.expression;
|
|
1805
|
+
if (!init)
|
|
1806
|
+
return null;
|
|
1807
|
+
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
1808
|
+
return init.text;
|
|
1809
|
+
}
|
|
1810
|
+
return null;
|
|
1811
|
+
}
|
|
1812
|
+
resolveModuleStringConst(name) {
|
|
1813
|
+
if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
|
|
1814
|
+
return null;
|
|
1815
|
+
}
|
|
1816
|
+
if (this.loopVarRefCount.has(name))
|
|
1817
|
+
return null;
|
|
1818
|
+
if (this.isOuterLoopParam(name))
|
|
1819
|
+
return null;
|
|
1820
|
+
const value = this.moduleStringConsts.get(name);
|
|
1821
|
+
if (value === undefined)
|
|
1822
|
+
return null;
|
|
1823
|
+
return `"${this.escapeGoString(value)}"`;
|
|
1824
|
+
}
|
|
1748
1825
|
literal(value, literalType) {
|
|
1749
1826
|
if (literalType === "string")
|
|
1750
1827
|
return `"${value}"`;
|
|
@@ -1905,9 +1982,7 @@ ${goFields.join(`
|
|
|
1905
1982
|
message: `Higher-order method '.${method}' shape cannot be lowered to a Go template action`,
|
|
1906
1983
|
loc: this.makeLoc(),
|
|
1907
1984
|
suggestion: {
|
|
1908
|
-
message:
|
|
1909
|
-
1. Use @client directive for client-side evaluation
|
|
1910
|
-
2. Pre-compute the value in Go code`
|
|
1985
|
+
message: GO_REMEDIATION_OPTIONS
|
|
1911
1986
|
}
|
|
1912
1987
|
});
|
|
1913
1988
|
return `""`;
|
|
@@ -2027,6 +2102,26 @@ ${goFields.join(`
|
|
|
2027
2102
|
sortMethod(method, object, comparator, emit) {
|
|
2028
2103
|
return emitBfSort(emit(object), comparator);
|
|
2029
2104
|
}
|
|
2105
|
+
reduceMethod(method, object, reduceOp, emit) {
|
|
2106
|
+
const direction = method === "reduceRight" ? "right" : "left";
|
|
2107
|
+
return emitBfReduce(emit(object), reduceOp, direction);
|
|
2108
|
+
}
|
|
2109
|
+
flatMethod(object, depth, emit) {
|
|
2110
|
+
const d = depth === "infinity" ? -1 : depth;
|
|
2111
|
+
return `bf_flat ${wrapIfMultiToken(emit(object))} ${d}`;
|
|
2112
|
+
}
|
|
2113
|
+
flatMapMethod(object, op, emit) {
|
|
2114
|
+
const recv = wrapIfMultiToken(emit(object));
|
|
2115
|
+
const proj = op.projection;
|
|
2116
|
+
if (proj.kind === "tuple") {
|
|
2117
|
+
const pairs = proj.elements.map((l) => l.kind === "self" ? `"self" ""` : `"field" "${capitalize(l.field)}"`).join(" ");
|
|
2118
|
+
return `bf_flat_map_tuple ${recv} ${pairs}`;
|
|
2119
|
+
}
|
|
2120
|
+
if (proj.kind === "self") {
|
|
2121
|
+
return `bf_flat_map ${recv} "self" ""`;
|
|
2122
|
+
}
|
|
2123
|
+
return `bf_flat_map ${recv} "field" "${capitalize(proj.field)}"`;
|
|
2124
|
+
}
|
|
2030
2125
|
unsupported(raw, _reason) {
|
|
2031
2126
|
return `[UNSUPPORTED: ${raw}]`;
|
|
2032
2127
|
}
|
|
@@ -2449,13 +2544,7 @@ ${goFields.join(`
|
|
|
2449
2544
|
message: `Expression not supported: ${trimmed}`,
|
|
2450
2545
|
loc: this.makeLoc(),
|
|
2451
2546
|
suggestion: {
|
|
2452
|
-
message: support
|
|
2453
|
-
|
|
2454
|
-
Options:
|
|
2455
|
-
1. Use @client directive for client-side evaluation
|
|
2456
|
-
2. Pre-compute the value in Go code` : `Options:
|
|
2457
|
-
1. Use @client directive for client-side evaluation
|
|
2458
|
-
2. Pre-compute the value in Go code`
|
|
2547
|
+
message: buildUnsupportedSuggestion(support)
|
|
2459
2548
|
}
|
|
2460
2549
|
});
|
|
2461
2550
|
return `""`;
|
|
@@ -2484,9 +2573,7 @@ Options:
|
|
|
2484
2573
|
message: `Complex predicate in else-if is not supported: ${altIfStmt.condition}`,
|
|
2485
2574
|
loc: this.makeLoc(),
|
|
2486
2575
|
suggestion: {
|
|
2487
|
-
message:
|
|
2488
|
-
1. Use @client directive for client-side evaluation
|
|
2489
|
-
2. Pre-compute the value in Go code`
|
|
2576
|
+
message: GO_REMEDIATION_OPTIONS
|
|
2490
2577
|
}
|
|
2491
2578
|
});
|
|
2492
2579
|
}
|
|
@@ -2562,11 +2649,7 @@ Options:
|
|
|
2562
2649
|
message: `Condition not supported: ${trimmed}`,
|
|
2563
2650
|
loc: this.makeLoc(),
|
|
2564
2651
|
suggestion: {
|
|
2565
|
-
message: support
|
|
2566
|
-
|
|
2567
|
-
Options:
|
|
2568
|
-
1. Use @client directive for client-side evaluation
|
|
2569
|
-
2. Pre-compute the value in Go code` : "Expression contains unsupported syntax"
|
|
2652
|
+
message: buildUnsupportedSuggestion(support)
|
|
2570
2653
|
}
|
|
2571
2654
|
});
|
|
2572
2655
|
return { condition: `false`, preamble: "" };
|
|
@@ -2579,6 +2662,9 @@ Options:
|
|
|
2579
2662
|
switch (expr.kind) {
|
|
2580
2663
|
case "identifier":
|
|
2581
2664
|
{
|
|
2665
|
+
const inlined = this.resolveModuleStringConst(expr.name);
|
|
2666
|
+
if (inlined !== null)
|
|
2667
|
+
return plain(inlined);
|
|
2582
2668
|
const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
|
|
2583
2669
|
if (currentLoopParam && expr.name === currentLoopParam) {
|
|
2584
2670
|
return plain(".");
|
|
@@ -2601,6 +2687,20 @@ Options:
|
|
|
2601
2687
|
if (expr.callee.kind === "identifier" && expr.args.length === 0) {
|
|
2602
2688
|
return plain(this.rootFieldRef(expr.callee.name));
|
|
2603
2689
|
}
|
|
2690
|
+
if (expr.callee.kind === "identifier" && (identifierPath(expr.callee) ?? expr.callee.name) === "isValidElement" && expr.args.length === 1) {
|
|
2691
|
+
return this.renderConditionExpr(expr.args[0]);
|
|
2692
|
+
}
|
|
2693
|
+
if (expr.callee.kind === "identifier" && !this.templatePrimitives[identifierPath(expr.callee) ?? ""]) {
|
|
2694
|
+
const path = identifierPath(expr.callee) ?? expr.callee.name;
|
|
2695
|
+
this.errors.push({
|
|
2696
|
+
code: "BF102",
|
|
2697
|
+
severity: "error",
|
|
2698
|
+
message: `Predicate '${path}(...)' cannot be evaluated in a Go template. ` + `A server-side template cannot call user-defined JavaScript predicates.`,
|
|
2699
|
+
loc: this.makeLoc(),
|
|
2700
|
+
suggestion: { message: GO_REMEDIATION_OPTIONS }
|
|
2701
|
+
});
|
|
2702
|
+
return plain("false");
|
|
2703
|
+
}
|
|
2604
2704
|
return plain(this.renderParsedExpr(expr));
|
|
2605
2705
|
}
|
|
2606
2706
|
case "member": {
|
|
@@ -2812,6 +2912,9 @@ Options:
|
|
|
2812
2912
|
if (comp.name === "Portal") {
|
|
2813
2913
|
return this.renderPortalComponent(comp);
|
|
2814
2914
|
}
|
|
2915
|
+
if (comp.dynamicTag) {
|
|
2916
|
+
return this.renderChildren(comp.children);
|
|
2917
|
+
}
|
|
2815
2918
|
let templateCall;
|
|
2816
2919
|
if (this.inLoop) {
|
|
2817
2920
|
templateCall = `{{template "${comp.name}" .}}`;
|
|
@@ -2955,7 +3058,7 @@ ${children}`;
|
|
|
2955
3058
|
continue;
|
|
2956
3059
|
const branches = caseEntries.map(([k, v], i) => {
|
|
2957
3060
|
const head = i === 0 ? "{{if" : "{{else if";
|
|
2958
|
-
return `${head} eq ${keyExpr} ${JSON.stringify(k)}}}${v}`;
|
|
3061
|
+
return `${head} eq ${keyExpr} ${JSON.stringify(k)}}}${this.escapeAttrText(v)}`;
|
|
2959
3062
|
});
|
|
2960
3063
|
output += branches.join("") + "{{end}}";
|
|
2961
3064
|
}
|
package/dist/build.js
CHANGED
|
@@ -391,8 +391,20 @@ function emitBfSort(recv, c) {
|
|
|
391
391
|
});
|
|
392
392
|
return `bf_sort ${wrapIfMultiToken(recv)} ${groups.join(" ")}`;
|
|
393
393
|
}
|
|
394
|
+
function emitBfReduce(recv, op, direction) {
|
|
395
|
+
const keyName = op.key.kind === "field" ? capitalize(op.key.field) : "";
|
|
396
|
+
return `bf_reduce ${wrapIfMultiToken(recv)} "${op.op}" "${op.key.kind}" "${keyName}" "${op.type}" "${escapeGoString(op.init)}" "${direction}"`;
|
|
397
|
+
}
|
|
398
|
+
function escapeGoString(s) {
|
|
399
|
+
return s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
400
|
+
}
|
|
394
401
|
function capitalize(s) {
|
|
395
|
-
|
|
402
|
+
if (s.length === 0)
|
|
403
|
+
return s;
|
|
404
|
+
if (GoTemplateAdapter.GO_INITIALISMS.has(s.toLowerCase())) {
|
|
405
|
+
return s.toUpperCase();
|
|
406
|
+
}
|
|
407
|
+
return s[0].toUpperCase() + s.slice(1);
|
|
396
408
|
}
|
|
397
409
|
function slotIdToFieldSuffix(slotId) {
|
|
398
410
|
const cleanId = slotId.startsWith("^") ? slotId.slice(1) : slotId;
|
|
@@ -402,6 +414,18 @@ function slotIdToFieldSuffix(slotId) {
|
|
|
402
414
|
}
|
|
403
415
|
return cleanId.replace("slot_", "Slot");
|
|
404
416
|
}
|
|
417
|
+
var GO_REMEDIATION_OPTIONS = `Options:
|
|
418
|
+
1. Use @client directive for client-side evaluation
|
|
419
|
+
2. Pre-compute the value in Go code`;
|
|
420
|
+
function buildUnsupportedSuggestion(support) {
|
|
421
|
+
if (!support.reason)
|
|
422
|
+
return GO_REMEDIATION_OPTIONS;
|
|
423
|
+
if (support.selfContained)
|
|
424
|
+
return support.reason;
|
|
425
|
+
return `${support.reason}
|
|
426
|
+
|
|
427
|
+
${GO_REMEDIATION_OPTIONS}`;
|
|
428
|
+
}
|
|
405
429
|
var GO_TEMPLATE_PRIMITIVES = {
|
|
406
430
|
"JSON.stringify": { arity: 1, emit: (args) => `bf_json ${args[0]}` },
|
|
407
431
|
String: { arity: 1, emit: (args) => `bf_string ${args[0]}` },
|
|
@@ -433,6 +457,7 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
433
457
|
localStructFields = new Map;
|
|
434
458
|
synthStructTypes = new Map;
|
|
435
459
|
usesHtmlTemplate = false;
|
|
460
|
+
moduleStringConsts = new Map;
|
|
436
461
|
constructor(options = {}) {
|
|
437
462
|
super();
|
|
438
463
|
this.options = {
|
|
@@ -447,6 +472,7 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
447
472
|
this.templateVarCounter = 0;
|
|
448
473
|
this.propsObjectName = ir.metadata.propsObjectName;
|
|
449
474
|
this.restPropsName = ir.metadata.restPropsName ?? null;
|
|
475
|
+
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
450
476
|
if (!options?.siblingTemplatesRegistered) {
|
|
451
477
|
this.checkImportedLoopChildComponents(ir);
|
|
452
478
|
}
|
|
@@ -1228,6 +1254,12 @@ ${goFields.join(`
|
|
|
1228
1254
|
collectStaticChildInstancesRecursive(node, result, inLoop) {
|
|
1229
1255
|
if (node.type === "component") {
|
|
1230
1256
|
const comp = node;
|
|
1257
|
+
if (comp.dynamicTag) {
|
|
1258
|
+
for (const child of comp.children) {
|
|
1259
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1260
|
+
}
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1231
1263
|
if (comp.name !== "Portal" && !inLoop && comp.slotId) {
|
|
1232
1264
|
const suffix = slotIdToFieldSuffix(comp.slotId);
|
|
1233
1265
|
result.push({
|
|
@@ -2064,6 +2096,9 @@ ${goFields.join(`
|
|
|
2064
2096
|
return emitParsedExpr(expr, this);
|
|
2065
2097
|
}
|
|
2066
2098
|
identifier(name) {
|
|
2099
|
+
const inlined = this.resolveModuleStringConst(name);
|
|
2100
|
+
if (inlined !== null)
|
|
2101
|
+
return inlined;
|
|
2067
2102
|
const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
|
|
2068
2103
|
if (currentLoopParam && name === currentLoopParam)
|
|
2069
2104
|
return ".";
|
|
@@ -2085,6 +2120,48 @@ ${goFields.join(`
|
|
|
2085
2120
|
const prefix = this.loopParamStack.length > 0 ? "$." : ".";
|
|
2086
2121
|
return `${prefix}${this.capitalizeFieldName(name)}`;
|
|
2087
2122
|
}
|
|
2123
|
+
collectModuleStringConsts(constants) {
|
|
2124
|
+
const map = new Map;
|
|
2125
|
+
for (const c of constants ?? []) {
|
|
2126
|
+
if (!c.isModule)
|
|
2127
|
+
continue;
|
|
2128
|
+
if (c.value === undefined)
|
|
2129
|
+
continue;
|
|
2130
|
+
const literal = this.parsePureStringLiteral(c.value);
|
|
2131
|
+
if (literal !== null)
|
|
2132
|
+
map.set(c.name, literal);
|
|
2133
|
+
}
|
|
2134
|
+
return map;
|
|
2135
|
+
}
|
|
2136
|
+
parsePureStringLiteral(source) {
|
|
2137
|
+
const sf = ts.createSourceFile("__const.ts", `const __x = (${source});`, ts.ScriptTarget.Latest, false);
|
|
2138
|
+
const stmt = sf.statements[0];
|
|
2139
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
2140
|
+
return null;
|
|
2141
|
+
const decl = stmt.declarationList.declarations[0];
|
|
2142
|
+
let init = decl?.initializer;
|
|
2143
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
2144
|
+
init = init.expression;
|
|
2145
|
+
if (!init)
|
|
2146
|
+
return null;
|
|
2147
|
+
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
2148
|
+
return init.text;
|
|
2149
|
+
}
|
|
2150
|
+
return null;
|
|
2151
|
+
}
|
|
2152
|
+
resolveModuleStringConst(name) {
|
|
2153
|
+
if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
|
|
2154
|
+
return null;
|
|
2155
|
+
}
|
|
2156
|
+
if (this.loopVarRefCount.has(name))
|
|
2157
|
+
return null;
|
|
2158
|
+
if (this.isOuterLoopParam(name))
|
|
2159
|
+
return null;
|
|
2160
|
+
const value = this.moduleStringConsts.get(name);
|
|
2161
|
+
if (value === undefined)
|
|
2162
|
+
return null;
|
|
2163
|
+
return `"${this.escapeGoString(value)}"`;
|
|
2164
|
+
}
|
|
2088
2165
|
literal(value, literalType) {
|
|
2089
2166
|
if (literalType === "string")
|
|
2090
2167
|
return `"${value}"`;
|
|
@@ -2245,9 +2322,7 @@ ${goFields.join(`
|
|
|
2245
2322
|
message: `Higher-order method '.${method}' shape cannot be lowered to a Go template action`,
|
|
2246
2323
|
loc: this.makeLoc(),
|
|
2247
2324
|
suggestion: {
|
|
2248
|
-
message:
|
|
2249
|
-
1. Use @client directive for client-side evaluation
|
|
2250
|
-
2. Pre-compute the value in Go code`
|
|
2325
|
+
message: GO_REMEDIATION_OPTIONS
|
|
2251
2326
|
}
|
|
2252
2327
|
});
|
|
2253
2328
|
return `""`;
|
|
@@ -2367,6 +2442,26 @@ ${goFields.join(`
|
|
|
2367
2442
|
sortMethod(method, object, comparator, emit) {
|
|
2368
2443
|
return emitBfSort(emit(object), comparator);
|
|
2369
2444
|
}
|
|
2445
|
+
reduceMethod(method, object, reduceOp, emit) {
|
|
2446
|
+
const direction = method === "reduceRight" ? "right" : "left";
|
|
2447
|
+
return emitBfReduce(emit(object), reduceOp, direction);
|
|
2448
|
+
}
|
|
2449
|
+
flatMethod(object, depth, emit) {
|
|
2450
|
+
const d = depth === "infinity" ? -1 : depth;
|
|
2451
|
+
return `bf_flat ${wrapIfMultiToken(emit(object))} ${d}`;
|
|
2452
|
+
}
|
|
2453
|
+
flatMapMethod(object, op, emit) {
|
|
2454
|
+
const recv = wrapIfMultiToken(emit(object));
|
|
2455
|
+
const proj = op.projection;
|
|
2456
|
+
if (proj.kind === "tuple") {
|
|
2457
|
+
const pairs = proj.elements.map((l) => l.kind === "self" ? `"self" ""` : `"field" "${capitalize(l.field)}"`).join(" ");
|
|
2458
|
+
return `bf_flat_map_tuple ${recv} ${pairs}`;
|
|
2459
|
+
}
|
|
2460
|
+
if (proj.kind === "self") {
|
|
2461
|
+
return `bf_flat_map ${recv} "self" ""`;
|
|
2462
|
+
}
|
|
2463
|
+
return `bf_flat_map ${recv} "field" "${capitalize(proj.field)}"`;
|
|
2464
|
+
}
|
|
2370
2465
|
unsupported(raw, _reason) {
|
|
2371
2466
|
return `[UNSUPPORTED: ${raw}]`;
|
|
2372
2467
|
}
|
|
@@ -2789,13 +2884,7 @@ ${goFields.join(`
|
|
|
2789
2884
|
message: `Expression not supported: ${trimmed}`,
|
|
2790
2885
|
loc: this.makeLoc(),
|
|
2791
2886
|
suggestion: {
|
|
2792
|
-
message: support
|
|
2793
|
-
|
|
2794
|
-
Options:
|
|
2795
|
-
1. Use @client directive for client-side evaluation
|
|
2796
|
-
2. Pre-compute the value in Go code` : `Options:
|
|
2797
|
-
1. Use @client directive for client-side evaluation
|
|
2798
|
-
2. Pre-compute the value in Go code`
|
|
2887
|
+
message: buildUnsupportedSuggestion(support)
|
|
2799
2888
|
}
|
|
2800
2889
|
});
|
|
2801
2890
|
return `""`;
|
|
@@ -2824,9 +2913,7 @@ Options:
|
|
|
2824
2913
|
message: `Complex predicate in else-if is not supported: ${altIfStmt.condition}`,
|
|
2825
2914
|
loc: this.makeLoc(),
|
|
2826
2915
|
suggestion: {
|
|
2827
|
-
message:
|
|
2828
|
-
1. Use @client directive for client-side evaluation
|
|
2829
|
-
2. Pre-compute the value in Go code`
|
|
2916
|
+
message: GO_REMEDIATION_OPTIONS
|
|
2830
2917
|
}
|
|
2831
2918
|
});
|
|
2832
2919
|
}
|
|
@@ -2902,11 +2989,7 @@ Options:
|
|
|
2902
2989
|
message: `Condition not supported: ${trimmed}`,
|
|
2903
2990
|
loc: this.makeLoc(),
|
|
2904
2991
|
suggestion: {
|
|
2905
|
-
message: support
|
|
2906
|
-
|
|
2907
|
-
Options:
|
|
2908
|
-
1. Use @client directive for client-side evaluation
|
|
2909
|
-
2. Pre-compute the value in Go code` : "Expression contains unsupported syntax"
|
|
2992
|
+
message: buildUnsupportedSuggestion(support)
|
|
2910
2993
|
}
|
|
2911
2994
|
});
|
|
2912
2995
|
return { condition: `false`, preamble: "" };
|
|
@@ -2919,6 +3002,9 @@ Options:
|
|
|
2919
3002
|
switch (expr.kind) {
|
|
2920
3003
|
case "identifier":
|
|
2921
3004
|
{
|
|
3005
|
+
const inlined = this.resolveModuleStringConst(expr.name);
|
|
3006
|
+
if (inlined !== null)
|
|
3007
|
+
return plain(inlined);
|
|
2922
3008
|
const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
|
|
2923
3009
|
if (currentLoopParam && expr.name === currentLoopParam) {
|
|
2924
3010
|
return plain(".");
|
|
@@ -2941,6 +3027,20 @@ Options:
|
|
|
2941
3027
|
if (expr.callee.kind === "identifier" && expr.args.length === 0) {
|
|
2942
3028
|
return plain(this.rootFieldRef(expr.callee.name));
|
|
2943
3029
|
}
|
|
3030
|
+
if (expr.callee.kind === "identifier" && (identifierPath(expr.callee) ?? expr.callee.name) === "isValidElement" && expr.args.length === 1) {
|
|
3031
|
+
return this.renderConditionExpr(expr.args[0]);
|
|
3032
|
+
}
|
|
3033
|
+
if (expr.callee.kind === "identifier" && !this.templatePrimitives[identifierPath(expr.callee) ?? ""]) {
|
|
3034
|
+
const path = identifierPath(expr.callee) ?? expr.callee.name;
|
|
3035
|
+
this.errors.push({
|
|
3036
|
+
code: "BF102",
|
|
3037
|
+
severity: "error",
|
|
3038
|
+
message: `Predicate '${path}(...)' cannot be evaluated in a Go template. ` + `A server-side template cannot call user-defined JavaScript predicates.`,
|
|
3039
|
+
loc: this.makeLoc(),
|
|
3040
|
+
suggestion: { message: GO_REMEDIATION_OPTIONS }
|
|
3041
|
+
});
|
|
3042
|
+
return plain("false");
|
|
3043
|
+
}
|
|
2944
3044
|
return plain(this.renderParsedExpr(expr));
|
|
2945
3045
|
}
|
|
2946
3046
|
case "member": {
|
|
@@ -3152,6 +3252,9 @@ Options:
|
|
|
3152
3252
|
if (comp.name === "Portal") {
|
|
3153
3253
|
return this.renderPortalComponent(comp);
|
|
3154
3254
|
}
|
|
3255
|
+
if (comp.dynamicTag) {
|
|
3256
|
+
return this.renderChildren(comp.children);
|
|
3257
|
+
}
|
|
3155
3258
|
let templateCall;
|
|
3156
3259
|
if (this.inLoop) {
|
|
3157
3260
|
templateCall = `{{template "${comp.name}" .}}`;
|
|
@@ -3295,7 +3398,7 @@ ${children}`;
|
|
|
3295
3398
|
continue;
|
|
3296
3399
|
const branches = caseEntries.map(([k, v], i) => {
|
|
3297
3400
|
const head = i === 0 ? "{{if" : "{{else if";
|
|
3298
|
-
return `${head} eq ${keyExpr} ${JSON.stringify(k)}}}${v}`;
|
|
3401
|
+
return `${head} eq ${keyExpr} ${JSON.stringify(k)}}}${this.escapeAttrText(v)}`;
|
|
3299
3402
|
});
|
|
3300
3403
|
output += branches.join("") + "{{end}}";
|
|
3301
3404
|
}
|