@barefootjs/go-template 0.18.7 → 0.19.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/emit-context.d.ts +11 -5
- package/dist/adapter/emit-context.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +37 -4
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +175 -21
- package/dist/adapter/lib/compile-state.d.ts +16 -0
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/lib/types.d.ts +9 -0
- package/dist/adapter/lib/types.d.ts.map +1 -1
- package/dist/adapter/props/prop-types.d.ts +45 -0
- package/dist/adapter/props/prop-types.d.ts.map +1 -1
- package/dist/build.js +175 -21
- package/dist/index.js +175 -21
- package/package.json +3 -3
- package/src/__tests__/go-template-adapter.test.ts +169 -18
- package/src/adapter/emit-context.ts +14 -5
- package/src/adapter/go-template-adapter.ts +185 -20
- package/src/adapter/lib/compile-state.ts +18 -0
- package/src/adapter/lib/types.ts +9 -0
- package/src/adapter/props/prop-types.ts +178 -1
- package/src/adapter/value/value-lowering.ts +1 -1
- package/src/test-render.ts +18 -4
package/dist/build.js
CHANGED
|
@@ -364,7 +364,7 @@ var init_path = __esm(() => {
|
|
|
364
364
|
// src/adapter/go-template-adapter.ts
|
|
365
365
|
import {
|
|
366
366
|
BaseAdapter,
|
|
367
|
-
isBooleanAttr,
|
|
367
|
+
isBooleanAttr as isBooleanAttr2,
|
|
368
368
|
parseExpression as parseExpression4,
|
|
369
369
|
stringifyParsedExpr as stringifyParsedExpr2,
|
|
370
370
|
parseStyleObjectEntries,
|
|
@@ -706,6 +706,8 @@ class CompileState {
|
|
|
706
706
|
hoistedMemoLocals = new Map;
|
|
707
707
|
loweringMatchers = [];
|
|
708
708
|
nillablePropNames = new Set;
|
|
709
|
+
nullishConsumedPropNames = new Set;
|
|
710
|
+
omittableAttrConsumedPropNames = new Set;
|
|
709
711
|
stringValueNames = new Set;
|
|
710
712
|
rootScopeNodes = new Set;
|
|
711
713
|
memoBackedLoopSlice = new Map;
|
|
@@ -1455,7 +1457,7 @@ function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
|
|
|
1455
1457
|
return `in.${capitalizeFieldName(value)}`;
|
|
1456
1458
|
}
|
|
1457
1459
|
}
|
|
1458
|
-
const propName = ctx.extractPropNameFromInitialValue(value);
|
|
1460
|
+
const propName = ctx.extractPropNameFromInitialValue(value, preParsed);
|
|
1459
1461
|
if (propName && propsParams?.some((p) => p.name === propName)) {
|
|
1460
1462
|
return `in.${capitalizeFieldName(propName)}`;
|
|
1461
1463
|
}
|
|
@@ -2599,11 +2601,12 @@ function recordIndexAccessToGoMap(ctx, val, ir) {
|
|
|
2599
2601
|
}
|
|
2600
2602
|
|
|
2601
2603
|
// src/adapter/props/prop-types.ts
|
|
2604
|
+
import { isBooleanAttr } from "@barefootjs/jsx";
|
|
2602
2605
|
function buildPropTypeOverrides(ctx, ir) {
|
|
2603
2606
|
const overrides = new Map;
|
|
2604
2607
|
for (const signal of ir.metadata.signals) {
|
|
2605
2608
|
const propNames = [signal.initialValue];
|
|
2606
|
-
const extracted = ctx.extractPropNameFromInitialValue(signal.initialValue);
|
|
2609
|
+
const extracted = ctx.extractPropNameFromInitialValue(signal.initialValue, signal.parsed);
|
|
2607
2610
|
if (extracted)
|
|
2608
2611
|
propNames.push(extracted);
|
|
2609
2612
|
for (const propName of propNames) {
|
|
@@ -2660,11 +2663,96 @@ function collectToFixedPropNames(root) {
|
|
|
2660
2663
|
walk(root);
|
|
2661
2664
|
return names;
|
|
2662
2665
|
}
|
|
2666
|
+
var NULLISH_SCALAR_GO_TYPES = new Set(["string", "int", "float64", "bool"]);
|
|
2667
|
+
function collectNullishConsumedPropNames(ctx, ir) {
|
|
2668
|
+
const names = new Set;
|
|
2669
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null).map((p) => p.name));
|
|
2670
|
+
if (optionalParams.size === 0)
|
|
2671
|
+
return names;
|
|
2672
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2673
|
+
const propNameOfLeft = (left) => {
|
|
2674
|
+
if (left.kind === "identifier")
|
|
2675
|
+
return left.name;
|
|
2676
|
+
if (left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === propsObject) {
|
|
2677
|
+
return left.property;
|
|
2678
|
+
}
|
|
2679
|
+
return null;
|
|
2680
|
+
};
|
|
2681
|
+
const isZeroEquivalentLiteral = (right) => right.kind === "literal" && (right.value === "" || right.value === false || right.value === null || right.literalType === "number" && Number(right.value) === 0);
|
|
2682
|
+
const walk = (node) => {
|
|
2683
|
+
if (!node || typeof node !== "object")
|
|
2684
|
+
return;
|
|
2685
|
+
if (Array.isArray(node)) {
|
|
2686
|
+
for (const item of node)
|
|
2687
|
+
walk(item);
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
2690
|
+
const rec = node;
|
|
2691
|
+
if (rec.kind === "logical" && rec.op === "??" && rec.left && rec.right) {
|
|
2692
|
+
const propName = propNameOfLeft(rec.left);
|
|
2693
|
+
if (propName && optionalParams.has(propName) && !isZeroEquivalentLiteral(rec.right)) {
|
|
2694
|
+
names.add(propName);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
for (const value of Object.values(rec))
|
|
2698
|
+
walk(value);
|
|
2699
|
+
};
|
|
2700
|
+
walk(ir.root);
|
|
2701
|
+
for (const signal of ir.metadata.signals) {
|
|
2702
|
+
const match = ctx.extractPropFallback(signal.initialValue, signal.parsed);
|
|
2703
|
+
if (!match || !optionalParams.has(match.propName))
|
|
2704
|
+
continue;
|
|
2705
|
+
const f = match.goFallback;
|
|
2706
|
+
if (f === '""' || f === "false" || f === "nil" || Number(f) === 0)
|
|
2707
|
+
continue;
|
|
2708
|
+
names.add(match.propName);
|
|
2709
|
+
}
|
|
2710
|
+
return names;
|
|
2711
|
+
}
|
|
2712
|
+
function collectOmittableAttrConsumedPropNames(ctx, ir) {
|
|
2713
|
+
const names = new Set;
|
|
2714
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null).map((p) => p.name));
|
|
2715
|
+
if (optionalParams.size === 0)
|
|
2716
|
+
return names;
|
|
2717
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2718
|
+
const walk = (node) => {
|
|
2719
|
+
if (!node || typeof node !== "object")
|
|
2720
|
+
return;
|
|
2721
|
+
if (Array.isArray(node)) {
|
|
2722
|
+
for (const item of node)
|
|
2723
|
+
walk(item);
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2726
|
+
const rec = node;
|
|
2727
|
+
if (rec.type === "element" && Array.isArray(rec.attrs)) {
|
|
2728
|
+
for (const attr of rec.attrs) {
|
|
2729
|
+
if (attr.name === "class" || attr.name === "className" || attr.name === "style")
|
|
2730
|
+
continue;
|
|
2731
|
+
if (isBooleanAttr(attr.name))
|
|
2732
|
+
continue;
|
|
2733
|
+
if (attr.value?.kind !== "expression" || attr.value.presenceOrUndefined)
|
|
2734
|
+
continue;
|
|
2735
|
+
const bareId = String(attr.value.expr ?? "").trim();
|
|
2736
|
+
const propName = propsObject && bareId.startsWith(`${propsObject}.`) ? bareId.slice(propsObject.length + 1) : bareId;
|
|
2737
|
+
if (/^[A-Za-z_$][\w$]*$/.test(propName) && optionalParams.has(propName)) {
|
|
2738
|
+
names.add(propName);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
for (const value of Object.values(rec))
|
|
2743
|
+
walk(value);
|
|
2744
|
+
};
|
|
2745
|
+
walk(ir.root);
|
|
2746
|
+
return names;
|
|
2747
|
+
}
|
|
2663
2748
|
function resolvePropGoType(ctx, param, propTypeOverrides) {
|
|
2664
2749
|
const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
|
|
2665
2750
|
if (param.optional && ctx.state.localStructFields.has(base)) {
|
|
2666
2751
|
return "map[string]interface{}";
|
|
2667
2752
|
}
|
|
2753
|
+
if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
|
|
2754
|
+
return "interface{}";
|
|
2755
|
+
}
|
|
2668
2756
|
return base;
|
|
2669
2757
|
}
|
|
2670
2758
|
function collectNillablePropNames(ctx, ir) {
|
|
@@ -2743,8 +2831,8 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2743
2831
|
state: this.state,
|
|
2744
2832
|
convertExpressionToGo: (jsExpr, out, preParsed) => this.convertExpressionToGo(jsExpr, out, preParsed),
|
|
2745
2833
|
convertConditionToGo: (jsCondition, preParsed) => this.convertConditionToGo(jsCondition, preParsed),
|
|
2746
|
-
extractPropNameFromInitialValue: (initialValue) => this.extractPropNameFromInitialValue(initialValue),
|
|
2747
|
-
extractPropFallback: (initialValue) => this.extractPropFallback(initialValue),
|
|
2834
|
+
extractPropNameFromInitialValue: (initialValue, preParsed) => this.extractPropNameFromInitialValue(initialValue, preParsed),
|
|
2835
|
+
extractPropFallback: (initialValue, preParsed) => this.extractPropFallback(initialValue, preParsed),
|
|
2748
2836
|
resolveModuleStringConst: (name) => this.resolveModuleStringConst(name)
|
|
2749
2837
|
};
|
|
2750
2838
|
get errors() {
|
|
@@ -2796,6 +2884,10 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2796
2884
|
}
|
|
2797
2885
|
this.state.loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
2798
2886
|
augmentInheritedPropAccesses(ir);
|
|
2887
|
+
this.buildLocalTypeTables(ir, ir.metadata.componentName);
|
|
2888
|
+
this.state.nullishConsumedPropNames = collectNullishConsumedPropNames(this.emitCtx, ir);
|
|
2889
|
+
this.state.omittableAttrConsumedPropNames = collectOmittableAttrConsumedPropNames(this.emitCtx, ir);
|
|
2890
|
+
this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
|
|
2799
2891
|
}
|
|
2800
2892
|
generate(ir, options) {
|
|
2801
2893
|
this.state.componentName = ir.metadata.componentName;
|
|
@@ -2804,7 +2896,6 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2804
2896
|
this.state.templateVarCounter = 0;
|
|
2805
2897
|
this.state.pendingChildrenDefines = [];
|
|
2806
2898
|
this.primeCompileState(ir);
|
|
2807
|
-
this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
|
|
2808
2899
|
this.state.stringValueNames = collectStringValueNames(ir);
|
|
2809
2900
|
if (!options?.siblingTemplatesRegistered) {
|
|
2810
2901
|
this.checkImportedLoopChildComponents(ir);
|
|
@@ -3323,10 +3414,18 @@ ${goFields.join(`
|
|
|
3323
3414
|
this.emitStaticBodyWrappers(lines, ir, componentName, staticWithBody, emittedWrapperVars);
|
|
3324
3415
|
const propFallbackVars = this.collectPropFallbackVars(ir);
|
|
3325
3416
|
for (const [, info] of propFallbackVars) {
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3417
|
+
if (info.assertType) {
|
|
3418
|
+
const deref = info.assertType === "int" ? `bf.ToInt(in.${info.fieldName})` : info.assertType === "float64" ? `bf.ToFloat64(in.${info.fieldName})` : `in.${info.fieldName}.(${info.assertType})`;
|
|
3419
|
+
lines.push(` var ${info.varName} ${info.assertType} = ${info.goFallback}`);
|
|
3420
|
+
lines.push(` if in.${info.fieldName} != nil {`);
|
|
3421
|
+
lines.push(` ${info.varName} = ${deref}`);
|
|
3422
|
+
lines.push(` }`);
|
|
3423
|
+
} else {
|
|
3424
|
+
lines.push(` ${info.varName} := in.${info.fieldName}`);
|
|
3425
|
+
lines.push(` if ${info.varName} == ${info.zeroLiteral} {`);
|
|
3426
|
+
lines.push(` ${info.varName} = ${info.goFallback}`);
|
|
3427
|
+
lines.push(` }`);
|
|
3428
|
+
}
|
|
3330
3429
|
}
|
|
3331
3430
|
if (propFallbackVars.size > 0)
|
|
3332
3431
|
lines.push("");
|
|
@@ -3406,7 +3505,7 @@ ${goFields.join(`
|
|
|
3406
3505
|
const fieldName = capitalizeFieldName(signal.getter);
|
|
3407
3506
|
if (propFieldNames.has(fieldName))
|
|
3408
3507
|
continue;
|
|
3409
|
-
const fallbackMatch = this.extractPropFallback(signal.initialValue);
|
|
3508
|
+
const fallbackMatch = this.extractPropFallback(signal.initialValue, signal.parsed);
|
|
3410
3509
|
const hoisted = fallbackMatch ? propFallbackVars.get(fallbackMatch.propName) : undefined;
|
|
3411
3510
|
if (hoisted) {
|
|
3412
3511
|
lines.push(` ${fieldName}: ${hoisted.varName},`);
|
|
@@ -4252,8 +4351,9 @@ ${goFields.join(`
|
|
|
4252
4351
|
for (const nested of findNestedComponents(ir.root)) {
|
|
4253
4352
|
localTaken.add(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`);
|
|
4254
4353
|
}
|
|
4354
|
+
const propTypeOverrides = buildPropTypeOverrides(this.emitCtx, ir);
|
|
4255
4355
|
for (const signal of ir.metadata.signals) {
|
|
4256
|
-
const match = this.extractPropFallback(signal.initialValue);
|
|
4356
|
+
const match = this.extractPropFallback(signal.initialValue, signal.parsed);
|
|
4257
4357
|
if (!match)
|
|
4258
4358
|
continue;
|
|
4259
4359
|
if (result.has(match.propName))
|
|
@@ -4264,6 +4364,8 @@ ${goFields.join(`
|
|
|
4264
4364
|
if (goPropDefault(param.defaultValue) !== null)
|
|
4265
4365
|
continue;
|
|
4266
4366
|
const fieldName = capitalizeFieldName(match.propName);
|
|
4367
|
+
const concreteType = propTypeOverrides.get(param.name) ?? typeInfoToGo(this.emitCtx, param.type, param.defaultValue);
|
|
4368
|
+
const nullishLowered = NULLISH_SCALAR_GO_TYPES.has(concreteType) && resolvePropGoType(this.emitCtx, param, propTypeOverrides) === "interface{}";
|
|
4267
4369
|
let zeroLiteral;
|
|
4268
4370
|
if (match.goFallback === "true" || match.goFallback === "false") {
|
|
4269
4371
|
zeroLiteral = "false";
|
|
@@ -4274,20 +4376,31 @@ ${goFields.join(`
|
|
|
4274
4376
|
} else {
|
|
4275
4377
|
continue;
|
|
4276
4378
|
}
|
|
4277
|
-
if (
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4379
|
+
if (!nullishLowered) {
|
|
4380
|
+
if (match.goFallback === zeroLiteral)
|
|
4381
|
+
continue;
|
|
4382
|
+
if (zeroLiteral === "0" && Number(match.goFallback) === 0)
|
|
4383
|
+
continue;
|
|
4384
|
+
}
|
|
4281
4385
|
let varName = match.propName;
|
|
4282
4386
|
while (localTaken.has(varName) || GO_KEYWORDS.has(varName)) {
|
|
4283
4387
|
varName += "_";
|
|
4284
4388
|
}
|
|
4285
4389
|
localTaken.add(varName);
|
|
4286
|
-
result.set(match.propName, {
|
|
4390
|
+
result.set(match.propName, {
|
|
4391
|
+
varName,
|
|
4392
|
+
fieldName,
|
|
4393
|
+
goFallback: match.goFallback,
|
|
4394
|
+
zeroLiteral,
|
|
4395
|
+
...nullishLowered ? { assertType: concreteType } : {}
|
|
4396
|
+
});
|
|
4287
4397
|
}
|
|
4288
4398
|
return result;
|
|
4289
4399
|
}
|
|
4290
|
-
extractPropFallback(initialValue) {
|
|
4400
|
+
extractPropFallback(initialValue, preParsed) {
|
|
4401
|
+
const structural = preParsed ? this.extractPropFallbackFromParsed(preParsed) : null;
|
|
4402
|
+
if (structural)
|
|
4403
|
+
return structural;
|
|
4291
4404
|
if (!this.state.propsObjectName)
|
|
4292
4405
|
return null;
|
|
4293
4406
|
const trimmed = initialValue.trim();
|
|
@@ -4301,9 +4414,38 @@ ${goFields.join(`
|
|
|
4301
4414
|
return null;
|
|
4302
4415
|
return { propName: m[1], goFallback };
|
|
4303
4416
|
}
|
|
4304
|
-
|
|
4305
|
-
if (
|
|
4417
|
+
extractPropFallbackFromParsed(preParsed) {
|
|
4418
|
+
if (preParsed.kind !== "logical" || preParsed.op !== "??")
|
|
4419
|
+
return null;
|
|
4420
|
+
const left = preParsed.left;
|
|
4421
|
+
const propName = left.kind === "identifier" && !this.state.propsObjectName ? left.name : left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === this.state.propsObjectName ? left.property : null;
|
|
4422
|
+
if (!propName)
|
|
4423
|
+
return null;
|
|
4424
|
+
let right = preParsed.right;
|
|
4425
|
+
let negate = "";
|
|
4426
|
+
if (right.kind === "unary" && right.op === "-") {
|
|
4427
|
+
negate = "-";
|
|
4428
|
+
right = right.argument;
|
|
4429
|
+
}
|
|
4430
|
+
if (right.kind !== "literal")
|
|
4431
|
+
return null;
|
|
4432
|
+
if (negate && right.literalType !== "number")
|
|
4433
|
+
return null;
|
|
4434
|
+
if (right.literalType === "string") {
|
|
4435
|
+
return { propName, goFallback: JSON.stringify(right.value) };
|
|
4436
|
+
}
|
|
4437
|
+
const goFallback = goPropDefault(negate + (right.raw ?? String(right.value)));
|
|
4438
|
+
if (goFallback === null)
|
|
4306
4439
|
return null;
|
|
4440
|
+
return { propName, goFallback };
|
|
4441
|
+
}
|
|
4442
|
+
extractPropNameFromInitialValue(initialValue, preParsed) {
|
|
4443
|
+
if (!this.state.propsObjectName) {
|
|
4444
|
+
if (preParsed?.kind === "logical" && (preParsed.op === "??" || preParsed.op === "||") && preParsed.left.kind === "identifier") {
|
|
4445
|
+
return preParsed.left.name;
|
|
4446
|
+
}
|
|
4447
|
+
return null;
|
|
4448
|
+
}
|
|
4307
4449
|
const trimmed = initialValue.trim();
|
|
4308
4450
|
const name = this.state.propsObjectName;
|
|
4309
4451
|
const direct = new RegExp(`^${name}\\.(\\w+)(?:\\s*(?:\\?\\?|\\|\\|)\\s*.+)?$`);
|
|
@@ -4715,8 +4857,20 @@ ${goFields.join(`
|
|
|
4715
4857
|
const wrapRight = wrapIfMultiToken(emit(right));
|
|
4716
4858
|
if (op === "&&")
|
|
4717
4859
|
return `and ${wrapLeft} ${wrapRight}`;
|
|
4860
|
+
if (op === "??" && this.nillablePropNameOf(left) !== null) {
|
|
4861
|
+
return `bf_nullish ${wrapLeft} ${wrapRight}`;
|
|
4862
|
+
}
|
|
4718
4863
|
return `or ${wrapLeft} ${wrapRight}`;
|
|
4719
4864
|
}
|
|
4865
|
+
nillablePropNameOf(expr) {
|
|
4866
|
+
let name = null;
|
|
4867
|
+
if (expr.kind === "identifier") {
|
|
4868
|
+
name = expr.name;
|
|
4869
|
+
} else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
|
|
4870
|
+
name = expr.property;
|
|
4871
|
+
}
|
|
4872
|
+
return name !== null && this.state.nullishConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
|
|
4873
|
+
}
|
|
4720
4874
|
conditional(test, consequent, alternate, emit) {
|
|
4721
4875
|
const t = emit(test);
|
|
4722
4876
|
const c = this.renderConditionalBranch(consequent);
|
|
@@ -6022,7 +6176,7 @@ ${children}`;
|
|
|
6022
6176
|
if (css !== null)
|
|
6023
6177
|
return `style="${css}"`;
|
|
6024
6178
|
}
|
|
6025
|
-
if (
|
|
6179
|
+
if (isBooleanAttr2(name) || value.presenceOrUndefined) {
|
|
6026
6180
|
const { condition: goCond, preamble } = this.convertConditionToGo(value.expr, value.parsed);
|
|
6027
6181
|
const body = name.startsWith("aria-") ? `${name}="true"` : name;
|
|
6028
6182
|
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
24
24
|
// src/adapter/go-template-adapter.ts
|
|
25
25
|
import {
|
|
26
26
|
BaseAdapter,
|
|
27
|
-
isBooleanAttr,
|
|
27
|
+
isBooleanAttr as isBooleanAttr2,
|
|
28
28
|
parseExpression as parseExpression4,
|
|
29
29
|
stringifyParsedExpr as stringifyParsedExpr2,
|
|
30
30
|
parseStyleObjectEntries,
|
|
@@ -366,6 +366,8 @@ class CompileState {
|
|
|
366
366
|
hoistedMemoLocals = new Map;
|
|
367
367
|
loweringMatchers = [];
|
|
368
368
|
nillablePropNames = new Set;
|
|
369
|
+
nullishConsumedPropNames = new Set;
|
|
370
|
+
omittableAttrConsumedPropNames = new Set;
|
|
369
371
|
stringValueNames = new Set;
|
|
370
372
|
rootScopeNodes = new Set;
|
|
371
373
|
memoBackedLoopSlice = new Map;
|
|
@@ -1115,7 +1117,7 @@ function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
|
|
|
1115
1117
|
return `in.${capitalizeFieldName(value)}`;
|
|
1116
1118
|
}
|
|
1117
1119
|
}
|
|
1118
|
-
const propName = ctx.extractPropNameFromInitialValue(value);
|
|
1120
|
+
const propName = ctx.extractPropNameFromInitialValue(value, preParsed);
|
|
1119
1121
|
if (propName && propsParams?.some((p) => p.name === propName)) {
|
|
1120
1122
|
return `in.${capitalizeFieldName(propName)}`;
|
|
1121
1123
|
}
|
|
@@ -2259,11 +2261,12 @@ function recordIndexAccessToGoMap(ctx, val, ir) {
|
|
|
2259
2261
|
}
|
|
2260
2262
|
|
|
2261
2263
|
// src/adapter/props/prop-types.ts
|
|
2264
|
+
import { isBooleanAttr } from "@barefootjs/jsx";
|
|
2262
2265
|
function buildPropTypeOverrides(ctx, ir) {
|
|
2263
2266
|
const overrides = new Map;
|
|
2264
2267
|
for (const signal of ir.metadata.signals) {
|
|
2265
2268
|
const propNames = [signal.initialValue];
|
|
2266
|
-
const extracted = ctx.extractPropNameFromInitialValue(signal.initialValue);
|
|
2269
|
+
const extracted = ctx.extractPropNameFromInitialValue(signal.initialValue, signal.parsed);
|
|
2267
2270
|
if (extracted)
|
|
2268
2271
|
propNames.push(extracted);
|
|
2269
2272
|
for (const propName of propNames) {
|
|
@@ -2320,11 +2323,96 @@ function collectToFixedPropNames(root) {
|
|
|
2320
2323
|
walk(root);
|
|
2321
2324
|
return names;
|
|
2322
2325
|
}
|
|
2326
|
+
var NULLISH_SCALAR_GO_TYPES = new Set(["string", "int", "float64", "bool"]);
|
|
2327
|
+
function collectNullishConsumedPropNames(ctx, ir) {
|
|
2328
|
+
const names = new Set;
|
|
2329
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null).map((p) => p.name));
|
|
2330
|
+
if (optionalParams.size === 0)
|
|
2331
|
+
return names;
|
|
2332
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2333
|
+
const propNameOfLeft = (left) => {
|
|
2334
|
+
if (left.kind === "identifier")
|
|
2335
|
+
return left.name;
|
|
2336
|
+
if (left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === propsObject) {
|
|
2337
|
+
return left.property;
|
|
2338
|
+
}
|
|
2339
|
+
return null;
|
|
2340
|
+
};
|
|
2341
|
+
const isZeroEquivalentLiteral = (right) => right.kind === "literal" && (right.value === "" || right.value === false || right.value === null || right.literalType === "number" && Number(right.value) === 0);
|
|
2342
|
+
const walk = (node) => {
|
|
2343
|
+
if (!node || typeof node !== "object")
|
|
2344
|
+
return;
|
|
2345
|
+
if (Array.isArray(node)) {
|
|
2346
|
+
for (const item of node)
|
|
2347
|
+
walk(item);
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
const rec = node;
|
|
2351
|
+
if (rec.kind === "logical" && rec.op === "??" && rec.left && rec.right) {
|
|
2352
|
+
const propName = propNameOfLeft(rec.left);
|
|
2353
|
+
if (propName && optionalParams.has(propName) && !isZeroEquivalentLiteral(rec.right)) {
|
|
2354
|
+
names.add(propName);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
for (const value of Object.values(rec))
|
|
2358
|
+
walk(value);
|
|
2359
|
+
};
|
|
2360
|
+
walk(ir.root);
|
|
2361
|
+
for (const signal of ir.metadata.signals) {
|
|
2362
|
+
const match = ctx.extractPropFallback(signal.initialValue, signal.parsed);
|
|
2363
|
+
if (!match || !optionalParams.has(match.propName))
|
|
2364
|
+
continue;
|
|
2365
|
+
const f = match.goFallback;
|
|
2366
|
+
if (f === '""' || f === "false" || f === "nil" || Number(f) === 0)
|
|
2367
|
+
continue;
|
|
2368
|
+
names.add(match.propName);
|
|
2369
|
+
}
|
|
2370
|
+
return names;
|
|
2371
|
+
}
|
|
2372
|
+
function collectOmittableAttrConsumedPropNames(ctx, ir) {
|
|
2373
|
+
const names = new Set;
|
|
2374
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null).map((p) => p.name));
|
|
2375
|
+
if (optionalParams.size === 0)
|
|
2376
|
+
return names;
|
|
2377
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2378
|
+
const walk = (node) => {
|
|
2379
|
+
if (!node || typeof node !== "object")
|
|
2380
|
+
return;
|
|
2381
|
+
if (Array.isArray(node)) {
|
|
2382
|
+
for (const item of node)
|
|
2383
|
+
walk(item);
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
const rec = node;
|
|
2387
|
+
if (rec.type === "element" && Array.isArray(rec.attrs)) {
|
|
2388
|
+
for (const attr of rec.attrs) {
|
|
2389
|
+
if (attr.name === "class" || attr.name === "className" || attr.name === "style")
|
|
2390
|
+
continue;
|
|
2391
|
+
if (isBooleanAttr(attr.name))
|
|
2392
|
+
continue;
|
|
2393
|
+
if (attr.value?.kind !== "expression" || attr.value.presenceOrUndefined)
|
|
2394
|
+
continue;
|
|
2395
|
+
const bareId = String(attr.value.expr ?? "").trim();
|
|
2396
|
+
const propName = propsObject && bareId.startsWith(`${propsObject}.`) ? bareId.slice(propsObject.length + 1) : bareId;
|
|
2397
|
+
if (/^[A-Za-z_$][\w$]*$/.test(propName) && optionalParams.has(propName)) {
|
|
2398
|
+
names.add(propName);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
for (const value of Object.values(rec))
|
|
2403
|
+
walk(value);
|
|
2404
|
+
};
|
|
2405
|
+
walk(ir.root);
|
|
2406
|
+
return names;
|
|
2407
|
+
}
|
|
2323
2408
|
function resolvePropGoType(ctx, param, propTypeOverrides) {
|
|
2324
2409
|
const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
|
|
2325
2410
|
if (param.optional && ctx.state.localStructFields.has(base)) {
|
|
2326
2411
|
return "map[string]interface{}";
|
|
2327
2412
|
}
|
|
2413
|
+
if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
|
|
2414
|
+
return "interface{}";
|
|
2415
|
+
}
|
|
2328
2416
|
return base;
|
|
2329
2417
|
}
|
|
2330
2418
|
function collectNillablePropNames(ctx, ir) {
|
|
@@ -2403,8 +2491,8 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2403
2491
|
state: this.state,
|
|
2404
2492
|
convertExpressionToGo: (jsExpr, out, preParsed) => this.convertExpressionToGo(jsExpr, out, preParsed),
|
|
2405
2493
|
convertConditionToGo: (jsCondition, preParsed) => this.convertConditionToGo(jsCondition, preParsed),
|
|
2406
|
-
extractPropNameFromInitialValue: (initialValue) => this.extractPropNameFromInitialValue(initialValue),
|
|
2407
|
-
extractPropFallback: (initialValue) => this.extractPropFallback(initialValue),
|
|
2494
|
+
extractPropNameFromInitialValue: (initialValue, preParsed) => this.extractPropNameFromInitialValue(initialValue, preParsed),
|
|
2495
|
+
extractPropFallback: (initialValue, preParsed) => this.extractPropFallback(initialValue, preParsed),
|
|
2408
2496
|
resolveModuleStringConst: (name) => this.resolveModuleStringConst(name)
|
|
2409
2497
|
};
|
|
2410
2498
|
get errors() {
|
|
@@ -2456,6 +2544,10 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2456
2544
|
}
|
|
2457
2545
|
this.state.loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
2458
2546
|
augmentInheritedPropAccesses(ir);
|
|
2547
|
+
this.buildLocalTypeTables(ir, ir.metadata.componentName);
|
|
2548
|
+
this.state.nullishConsumedPropNames = collectNullishConsumedPropNames(this.emitCtx, ir);
|
|
2549
|
+
this.state.omittableAttrConsumedPropNames = collectOmittableAttrConsumedPropNames(this.emitCtx, ir);
|
|
2550
|
+
this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
|
|
2459
2551
|
}
|
|
2460
2552
|
generate(ir, options) {
|
|
2461
2553
|
this.state.componentName = ir.metadata.componentName;
|
|
@@ -2464,7 +2556,6 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2464
2556
|
this.state.templateVarCounter = 0;
|
|
2465
2557
|
this.state.pendingChildrenDefines = [];
|
|
2466
2558
|
this.primeCompileState(ir);
|
|
2467
|
-
this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
|
|
2468
2559
|
this.state.stringValueNames = collectStringValueNames(ir);
|
|
2469
2560
|
if (!options?.siblingTemplatesRegistered) {
|
|
2470
2561
|
this.checkImportedLoopChildComponents(ir);
|
|
@@ -2983,10 +3074,18 @@ ${goFields.join(`
|
|
|
2983
3074
|
this.emitStaticBodyWrappers(lines, ir, componentName, staticWithBody, emittedWrapperVars);
|
|
2984
3075
|
const propFallbackVars = this.collectPropFallbackVars(ir);
|
|
2985
3076
|
for (const [, info] of propFallbackVars) {
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3077
|
+
if (info.assertType) {
|
|
3078
|
+
const deref = info.assertType === "int" ? `bf.ToInt(in.${info.fieldName})` : info.assertType === "float64" ? `bf.ToFloat64(in.${info.fieldName})` : `in.${info.fieldName}.(${info.assertType})`;
|
|
3079
|
+
lines.push(` var ${info.varName} ${info.assertType} = ${info.goFallback}`);
|
|
3080
|
+
lines.push(` if in.${info.fieldName} != nil {`);
|
|
3081
|
+
lines.push(` ${info.varName} = ${deref}`);
|
|
3082
|
+
lines.push(` }`);
|
|
3083
|
+
} else {
|
|
3084
|
+
lines.push(` ${info.varName} := in.${info.fieldName}`);
|
|
3085
|
+
lines.push(` if ${info.varName} == ${info.zeroLiteral} {`);
|
|
3086
|
+
lines.push(` ${info.varName} = ${info.goFallback}`);
|
|
3087
|
+
lines.push(` }`);
|
|
3088
|
+
}
|
|
2990
3089
|
}
|
|
2991
3090
|
if (propFallbackVars.size > 0)
|
|
2992
3091
|
lines.push("");
|
|
@@ -3066,7 +3165,7 @@ ${goFields.join(`
|
|
|
3066
3165
|
const fieldName = capitalizeFieldName(signal.getter);
|
|
3067
3166
|
if (propFieldNames.has(fieldName))
|
|
3068
3167
|
continue;
|
|
3069
|
-
const fallbackMatch = this.extractPropFallback(signal.initialValue);
|
|
3168
|
+
const fallbackMatch = this.extractPropFallback(signal.initialValue, signal.parsed);
|
|
3070
3169
|
const hoisted = fallbackMatch ? propFallbackVars.get(fallbackMatch.propName) : undefined;
|
|
3071
3170
|
if (hoisted) {
|
|
3072
3171
|
lines.push(` ${fieldName}: ${hoisted.varName},`);
|
|
@@ -3912,8 +4011,9 @@ ${goFields.join(`
|
|
|
3912
4011
|
for (const nested of findNestedComponents(ir.root)) {
|
|
3913
4012
|
localTaken.add(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`);
|
|
3914
4013
|
}
|
|
4014
|
+
const propTypeOverrides = buildPropTypeOverrides(this.emitCtx, ir);
|
|
3915
4015
|
for (const signal of ir.metadata.signals) {
|
|
3916
|
-
const match = this.extractPropFallback(signal.initialValue);
|
|
4016
|
+
const match = this.extractPropFallback(signal.initialValue, signal.parsed);
|
|
3917
4017
|
if (!match)
|
|
3918
4018
|
continue;
|
|
3919
4019
|
if (result.has(match.propName))
|
|
@@ -3924,6 +4024,8 @@ ${goFields.join(`
|
|
|
3924
4024
|
if (goPropDefault(param.defaultValue) !== null)
|
|
3925
4025
|
continue;
|
|
3926
4026
|
const fieldName = capitalizeFieldName(match.propName);
|
|
4027
|
+
const concreteType = propTypeOverrides.get(param.name) ?? typeInfoToGo(this.emitCtx, param.type, param.defaultValue);
|
|
4028
|
+
const nullishLowered = NULLISH_SCALAR_GO_TYPES.has(concreteType) && resolvePropGoType(this.emitCtx, param, propTypeOverrides) === "interface{}";
|
|
3927
4029
|
let zeroLiteral;
|
|
3928
4030
|
if (match.goFallback === "true" || match.goFallback === "false") {
|
|
3929
4031
|
zeroLiteral = "false";
|
|
@@ -3934,20 +4036,31 @@ ${goFields.join(`
|
|
|
3934
4036
|
} else {
|
|
3935
4037
|
continue;
|
|
3936
4038
|
}
|
|
3937
|
-
if (
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4039
|
+
if (!nullishLowered) {
|
|
4040
|
+
if (match.goFallback === zeroLiteral)
|
|
4041
|
+
continue;
|
|
4042
|
+
if (zeroLiteral === "0" && Number(match.goFallback) === 0)
|
|
4043
|
+
continue;
|
|
4044
|
+
}
|
|
3941
4045
|
let varName = match.propName;
|
|
3942
4046
|
while (localTaken.has(varName) || GO_KEYWORDS.has(varName)) {
|
|
3943
4047
|
varName += "_";
|
|
3944
4048
|
}
|
|
3945
4049
|
localTaken.add(varName);
|
|
3946
|
-
result.set(match.propName, {
|
|
4050
|
+
result.set(match.propName, {
|
|
4051
|
+
varName,
|
|
4052
|
+
fieldName,
|
|
4053
|
+
goFallback: match.goFallback,
|
|
4054
|
+
zeroLiteral,
|
|
4055
|
+
...nullishLowered ? { assertType: concreteType } : {}
|
|
4056
|
+
});
|
|
3947
4057
|
}
|
|
3948
4058
|
return result;
|
|
3949
4059
|
}
|
|
3950
|
-
extractPropFallback(initialValue) {
|
|
4060
|
+
extractPropFallback(initialValue, preParsed) {
|
|
4061
|
+
const structural = preParsed ? this.extractPropFallbackFromParsed(preParsed) : null;
|
|
4062
|
+
if (structural)
|
|
4063
|
+
return structural;
|
|
3951
4064
|
if (!this.state.propsObjectName)
|
|
3952
4065
|
return null;
|
|
3953
4066
|
const trimmed = initialValue.trim();
|
|
@@ -3961,9 +4074,38 @@ ${goFields.join(`
|
|
|
3961
4074
|
return null;
|
|
3962
4075
|
return { propName: m[1], goFallback };
|
|
3963
4076
|
}
|
|
3964
|
-
|
|
3965
|
-
if (
|
|
4077
|
+
extractPropFallbackFromParsed(preParsed) {
|
|
4078
|
+
if (preParsed.kind !== "logical" || preParsed.op !== "??")
|
|
4079
|
+
return null;
|
|
4080
|
+
const left = preParsed.left;
|
|
4081
|
+
const propName = left.kind === "identifier" && !this.state.propsObjectName ? left.name : left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === this.state.propsObjectName ? left.property : null;
|
|
4082
|
+
if (!propName)
|
|
4083
|
+
return null;
|
|
4084
|
+
let right = preParsed.right;
|
|
4085
|
+
let negate = "";
|
|
4086
|
+
if (right.kind === "unary" && right.op === "-") {
|
|
4087
|
+
negate = "-";
|
|
4088
|
+
right = right.argument;
|
|
4089
|
+
}
|
|
4090
|
+
if (right.kind !== "literal")
|
|
4091
|
+
return null;
|
|
4092
|
+
if (negate && right.literalType !== "number")
|
|
4093
|
+
return null;
|
|
4094
|
+
if (right.literalType === "string") {
|
|
4095
|
+
return { propName, goFallback: JSON.stringify(right.value) };
|
|
4096
|
+
}
|
|
4097
|
+
const goFallback = goPropDefault(negate + (right.raw ?? String(right.value)));
|
|
4098
|
+
if (goFallback === null)
|
|
3966
4099
|
return null;
|
|
4100
|
+
return { propName, goFallback };
|
|
4101
|
+
}
|
|
4102
|
+
extractPropNameFromInitialValue(initialValue, preParsed) {
|
|
4103
|
+
if (!this.state.propsObjectName) {
|
|
4104
|
+
if (preParsed?.kind === "logical" && (preParsed.op === "??" || preParsed.op === "||") && preParsed.left.kind === "identifier") {
|
|
4105
|
+
return preParsed.left.name;
|
|
4106
|
+
}
|
|
4107
|
+
return null;
|
|
4108
|
+
}
|
|
3967
4109
|
const trimmed = initialValue.trim();
|
|
3968
4110
|
const name = this.state.propsObjectName;
|
|
3969
4111
|
const direct = new RegExp(`^${name}\\.(\\w+)(?:\\s*(?:\\?\\?|\\|\\|)\\s*.+)?$`);
|
|
@@ -4375,8 +4517,20 @@ ${goFields.join(`
|
|
|
4375
4517
|
const wrapRight = wrapIfMultiToken(emit(right));
|
|
4376
4518
|
if (op === "&&")
|
|
4377
4519
|
return `and ${wrapLeft} ${wrapRight}`;
|
|
4520
|
+
if (op === "??" && this.nillablePropNameOf(left) !== null) {
|
|
4521
|
+
return `bf_nullish ${wrapLeft} ${wrapRight}`;
|
|
4522
|
+
}
|
|
4378
4523
|
return `or ${wrapLeft} ${wrapRight}`;
|
|
4379
4524
|
}
|
|
4525
|
+
nillablePropNameOf(expr) {
|
|
4526
|
+
let name = null;
|
|
4527
|
+
if (expr.kind === "identifier") {
|
|
4528
|
+
name = expr.name;
|
|
4529
|
+
} else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
|
|
4530
|
+
name = expr.property;
|
|
4531
|
+
}
|
|
4532
|
+
return name !== null && this.state.nullishConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
|
|
4533
|
+
}
|
|
4380
4534
|
conditional(test, consequent, alternate, emit) {
|
|
4381
4535
|
const t = emit(test);
|
|
4382
4536
|
const c = this.renderConditionalBranch(consequent);
|
|
@@ -5682,7 +5836,7 @@ ${children}`;
|
|
|
5682
5836
|
if (css !== null)
|
|
5683
5837
|
return `style="${css}"`;
|
|
5684
5838
|
}
|
|
5685
|
-
if (
|
|
5839
|
+
if (isBooleanAttr2(name) || value.presenceOrUndefined) {
|
|
5686
5840
|
const { condition: goCond, preamble } = this.convertConditionToGo(value.expr, value.parsed);
|
|
5687
5841
|
const body = name.startsWith("aria-") ? `${name}="true"` : name;
|
|
5688
5842
|
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/go-template",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Go html/template adapter for BarefootJS - generates Go template files from IR",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"directory": "packages/adapter-go-template"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@barefootjs/shared": "0.
|
|
52
|
+
"@barefootjs/shared": "0.19.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"@barefootjs/jsx": ">=0.2.0",
|
|
@@ -57,6 +57,6 @@
|
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@barefootjs/adapter-tests": "0.1.0",
|
|
60
|
-
"@barefootjs/jsx": "0.
|
|
60
|
+
"@barefootjs/jsx": "0.19.0"
|
|
61
61
|
}
|
|
62
62
|
}
|