@barefootjs/go-template 0.18.5 → 0.18.7
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/analysis/static-child-loop-bake.d.ts +61 -0
- package/dist/adapter/analysis/static-child-loop-bake.d.ts.map +1 -0
- package/dist/adapter/analysis/static-element-loop-bake.d.ts +83 -0
- package/dist/adapter/analysis/static-element-loop-bake.d.ts.map +1 -0
- package/dist/adapter/go-template-adapter.d.ts +116 -2
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +339 -32
- package/dist/adapter/lib/compile-state.d.ts +8 -0
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/props/prop-classes.d.ts +28 -9
- package/dist/adapter/props/prop-classes.d.ts.map +1 -1
- package/dist/build.js +339 -32
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +341 -41
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/test-render.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/go-template-adapter.test.ts +707 -3
- package/src/adapter/analysis/static-child-loop-bake.ts +119 -0
- package/src/adapter/analysis/static-element-loop-bake.ts +211 -0
- package/src/adapter/go-template-adapter.ts +435 -25
- package/src/adapter/lib/compile-state.ts +9 -0
- package/src/adapter/props/prop-classes.ts +34 -9
- package/src/conformance-pins.ts +30 -31
- package/src/render-divergences.ts +12 -0
- package/src/test-render.ts +109 -6
package/dist/build.js
CHANGED
|
@@ -365,7 +365,7 @@ var init_path = __esm(() => {
|
|
|
365
365
|
import {
|
|
366
366
|
BaseAdapter,
|
|
367
367
|
isBooleanAttr,
|
|
368
|
-
parseExpression as
|
|
368
|
+
parseExpression as parseExpression4,
|
|
369
369
|
stringifyParsedExpr as stringifyParsedExpr2,
|
|
370
370
|
parseStyleObjectEntries,
|
|
371
371
|
isSupported,
|
|
@@ -383,7 +383,13 @@ import {
|
|
|
383
383
|
prepareLoweringMatchers,
|
|
384
384
|
envSignalReaderFor,
|
|
385
385
|
computeSsrSeedPlan,
|
|
386
|
-
isStringConcatBinary
|
|
386
|
+
isStringConcatBinary,
|
|
387
|
+
isDangerousInnerHtmlAttr,
|
|
388
|
+
resolveDangerousInnerHtml,
|
|
389
|
+
dangerousInnerHtmlMetacharViolation,
|
|
390
|
+
dangerousInnerHtmlDiagnostic,
|
|
391
|
+
collectLoopBoundNames as collectLoopBoundNames2,
|
|
392
|
+
evaluateStaticLiteral as evaluateStaticLiteral3
|
|
387
393
|
} from "@barefootjs/jsx";
|
|
388
394
|
import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
|
|
389
395
|
import { BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
@@ -689,6 +695,7 @@ class CompileState {
|
|
|
689
695
|
restPropsName = null;
|
|
690
696
|
moduleStringConsts = new Map;
|
|
691
697
|
localConstants = [];
|
|
698
|
+
staticLoopSourceBoundNames = new Set;
|
|
692
699
|
localHelperNames = new Set;
|
|
693
700
|
currentMemos = [];
|
|
694
701
|
currentTypeDefinitions = [];
|
|
@@ -857,6 +864,178 @@ function collectNestedComponents(node, result) {
|
|
|
857
864
|
}
|
|
858
865
|
}
|
|
859
866
|
|
|
867
|
+
// src/adapter/analysis/static-child-loop-bake.ts
|
|
868
|
+
import { evaluateStaticLiteral, parseExpression, resolveStaticLoopSource } from "@barefootjs/jsx";
|
|
869
|
+
function scalarToGoLiteral(value) {
|
|
870
|
+
if (typeof value === "string")
|
|
871
|
+
return `"${escapeGoString(value)}"`;
|
|
872
|
+
if (typeof value === "number")
|
|
873
|
+
return String(value);
|
|
874
|
+
if (typeof value === "boolean")
|
|
875
|
+
return value ? "true" : "false";
|
|
876
|
+
return null;
|
|
877
|
+
}
|
|
878
|
+
function analyzeBakeableStaticChildLoop(nested, localConstants, opts) {
|
|
879
|
+
if (!nested.loopParam || /^[{[]/.test(nested.loopParam))
|
|
880
|
+
return null;
|
|
881
|
+
const staticItemsResult = resolveStaticLoopSource(nested.loopArrayParsed, localConstants, opts);
|
|
882
|
+
if (staticItemsResult === null)
|
|
883
|
+
return null;
|
|
884
|
+
const items = [];
|
|
885
|
+
for (const item of staticItemsResult) {
|
|
886
|
+
const bindings = new Map([[nested.loopParam, item]]);
|
|
887
|
+
const inputFields = [];
|
|
888
|
+
for (const prop of nested.props) {
|
|
889
|
+
if (prop.isEventHandler)
|
|
890
|
+
continue;
|
|
891
|
+
if (prop.name.includes("-"))
|
|
892
|
+
continue;
|
|
893
|
+
const resolved = resolvePropValue(prop.value, bindings);
|
|
894
|
+
if (resolved === undefined)
|
|
895
|
+
return null;
|
|
896
|
+
const goValue = scalarToGoLiteral(resolved);
|
|
897
|
+
if (goValue === null)
|
|
898
|
+
return null;
|
|
899
|
+
inputFields.push({ goField: capitalizeFieldName(prop.name), goValue });
|
|
900
|
+
}
|
|
901
|
+
let dataKey = null;
|
|
902
|
+
if (nested.loopKey) {
|
|
903
|
+
const keyExpr = parseExpression(nested.loopKey);
|
|
904
|
+
const keyResolved = evaluateStaticLiteral(keyExpr, bindings);
|
|
905
|
+
if (keyResolved === null)
|
|
906
|
+
return null;
|
|
907
|
+
dataKey = String(keyResolved.value);
|
|
908
|
+
}
|
|
909
|
+
items.push({ inputFields, dataKey });
|
|
910
|
+
}
|
|
911
|
+
return { items };
|
|
912
|
+
}
|
|
913
|
+
function resolvePropValue(value, bindings) {
|
|
914
|
+
switch (value.kind) {
|
|
915
|
+
case "literal":
|
|
916
|
+
return value.value;
|
|
917
|
+
case "boolean-shorthand":
|
|
918
|
+
case "boolean-attr":
|
|
919
|
+
return true;
|
|
920
|
+
case "expression": {
|
|
921
|
+
if (!value.parsed)
|
|
922
|
+
return;
|
|
923
|
+
const resolved = evaluateStaticLiteral(value.parsed, bindings);
|
|
924
|
+
return resolved === null ? undefined : resolved.value;
|
|
925
|
+
}
|
|
926
|
+
default:
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/adapter/analysis/static-element-loop-bake.ts
|
|
932
|
+
import {
|
|
933
|
+
evaluateStaticLiteral as evaluateStaticLiteral2,
|
|
934
|
+
resolveStaticLoopSource as resolveStaticLoopSource2
|
|
935
|
+
} from "@barefootjs/jsx";
|
|
936
|
+
var ALLOWED_ATTR_EXPRESSION_KINDS = new Set([
|
|
937
|
+
"identifier",
|
|
938
|
+
"member",
|
|
939
|
+
"index-access",
|
|
940
|
+
"literal"
|
|
941
|
+
]);
|
|
942
|
+
function analyzeBakeableStaticElementLoop(loop, localConstants, opts) {
|
|
943
|
+
if (loop.childComponent)
|
|
944
|
+
return null;
|
|
945
|
+
if (loop.method === "flatMap" || loop.flatMapCallback)
|
|
946
|
+
return null;
|
|
947
|
+
if (!loop.param || /^[{[]/.test(loop.param))
|
|
948
|
+
return null;
|
|
949
|
+
if (loop.index && loop.index !== "_")
|
|
950
|
+
return null;
|
|
951
|
+
if (loop.paramBindings && loop.paramBindings.length > 0)
|
|
952
|
+
return null;
|
|
953
|
+
if (loop.filterPredicate || loop.sortComparator)
|
|
954
|
+
return null;
|
|
955
|
+
if (loop.iterationShape || loop.objectIteration)
|
|
956
|
+
return null;
|
|
957
|
+
if (loop.bodyIsMultiRoot || loop.bodyIsItemConditional)
|
|
958
|
+
return null;
|
|
959
|
+
if (!isFoldableTree(loop.children))
|
|
960
|
+
return null;
|
|
961
|
+
const items = resolveStaticLoopSource2(loop.arrayParsed, localConstants, opts);
|
|
962
|
+
if (items === null)
|
|
963
|
+
return null;
|
|
964
|
+
for (const item of items) {
|
|
965
|
+
const bindings = new Map([[loop.param, item]]);
|
|
966
|
+
if (!allExpressionsFoldFor(loop.children, bindings))
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
return { items };
|
|
970
|
+
}
|
|
971
|
+
function isFoldableTree(nodes) {
|
|
972
|
+
for (const node of nodes) {
|
|
973
|
+
switch (node.type) {
|
|
974
|
+
case "text":
|
|
975
|
+
case "expression":
|
|
976
|
+
continue;
|
|
977
|
+
case "element":
|
|
978
|
+
if (!isFoldableAttrs(node))
|
|
979
|
+
return false;
|
|
980
|
+
if (!isFoldableTree(node.children))
|
|
981
|
+
return false;
|
|
982
|
+
continue;
|
|
983
|
+
default:
|
|
984
|
+
return false;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return true;
|
|
988
|
+
}
|
|
989
|
+
function isFoldableAttrs(element) {
|
|
990
|
+
for (const attr of element.attrs) {
|
|
991
|
+
if (attr.clientOnly)
|
|
992
|
+
continue;
|
|
993
|
+
switch (attr.value.kind) {
|
|
994
|
+
case "literal":
|
|
995
|
+
case "boolean-attr":
|
|
996
|
+
case "boolean-shorthand":
|
|
997
|
+
continue;
|
|
998
|
+
case "expression":
|
|
999
|
+
if (!attr.value.parsed || !ALLOWED_ATTR_EXPRESSION_KINDS.has(attr.value.parsed.kind))
|
|
1000
|
+
return false;
|
|
1001
|
+
continue;
|
|
1002
|
+
default:
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
function allExpressionsFoldFor(nodes, bindings) {
|
|
1009
|
+
for (const node of nodes) {
|
|
1010
|
+
if (node.type === "expression") {
|
|
1011
|
+
if (node.clientOnly)
|
|
1012
|
+
continue;
|
|
1013
|
+
if (!node.parsed || !resolvesToScalar(node.parsed, bindings))
|
|
1014
|
+
return false;
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (node.type === "element") {
|
|
1018
|
+
for (const attr of node.attrs) {
|
|
1019
|
+
if (attr.clientOnly)
|
|
1020
|
+
continue;
|
|
1021
|
+
if (attr.value.kind !== "expression")
|
|
1022
|
+
continue;
|
|
1023
|
+
if (!attr.value.parsed || !resolvesToScalar(attr.value.parsed, bindings))
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
if (!allExpressionsFoldFor(node.children, bindings))
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return true;
|
|
1031
|
+
}
|
|
1032
|
+
function resolvesToScalar(expr, bindings) {
|
|
1033
|
+
const resolved = evaluateStaticLiteral2(expr, bindings);
|
|
1034
|
+
if (resolved === null)
|
|
1035
|
+
return false;
|
|
1036
|
+
return scalarToGoLiteral(resolved.value) !== null;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
860
1039
|
// src/adapter/expr/helper-inline.ts
|
|
861
1040
|
function inlineLocalHelperCall(ctx, jsExpr, callParsed) {
|
|
862
1041
|
if (ctx.state.localHelperNames.size === 0)
|
|
@@ -1044,7 +1223,7 @@ function forEachValueChild(n, visit) {
|
|
|
1044
1223
|
|
|
1045
1224
|
// src/adapter/expr/url-builder.ts
|
|
1046
1225
|
import {
|
|
1047
|
-
parseExpression,
|
|
1226
|
+
parseExpression as parseExpression2,
|
|
1048
1227
|
stringifyParsedExpr,
|
|
1049
1228
|
isValidHelperId
|
|
1050
1229
|
} from "@barefootjs/jsx";
|
|
@@ -1077,7 +1256,7 @@ function lowerRegisteredCall(ctx, jsExpr, preParsed) {
|
|
|
1077
1256
|
if (!call) {
|
|
1078
1257
|
if (!/^\s*[A-Za-z_$][\w$]*\s*\(/.test(jsExpr))
|
|
1079
1258
|
return null;
|
|
1080
|
-
const parsed =
|
|
1259
|
+
const parsed = parseExpression2(jsExpr);
|
|
1081
1260
|
if (parsed.kind !== "call")
|
|
1082
1261
|
return null;
|
|
1083
1262
|
call = parsed;
|
|
@@ -2171,7 +2350,7 @@ function propsAccessNameFromParsed2(ctx, node) {
|
|
|
2171
2350
|
|
|
2172
2351
|
// src/adapter/spread/spread-codegen.ts
|
|
2173
2352
|
import ts2 from "typescript";
|
|
2174
|
-
import { parseExpression as
|
|
2353
|
+
import { parseExpression as parseExpression3, parseRecordIndexAccess } from "@barefootjs/jsx";
|
|
2175
2354
|
function collectSpreadSlots(ctx, node) {
|
|
2176
2355
|
const result = [];
|
|
2177
2356
|
collectSpreadSlotsRecursive(ctx, node, result);
|
|
@@ -2278,7 +2457,7 @@ function parsedObjectLiteralToGoMap(parsed) {
|
|
|
2278
2457
|
}
|
|
2279
2458
|
function buildSpreadInitializer(ctx, spreadExpr, ir, parsed) {
|
|
2280
2459
|
const trimmed = spreadExpr.trim();
|
|
2281
|
-
const conditionalTree = parsed ??
|
|
2460
|
+
const conditionalTree = parsed ?? parseExpression3(trimmed);
|
|
2282
2461
|
const conditional = buildConditionalSpreadInitializer(ctx, conditionalTree, ir);
|
|
2283
2462
|
if (conditional !== undefined)
|
|
2284
2463
|
return conditional;
|
|
@@ -2309,7 +2488,7 @@ function buildSpreadInitializer(ctx, spreadExpr, ir, parsed) {
|
|
|
2309
2488
|
if (localConst?.value !== undefined) {
|
|
2310
2489
|
const initTrimmed = localConst.value.trim();
|
|
2311
2490
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(initTrimmed)) {
|
|
2312
|
-
const resolved = buildConditionalSpreadInitializer(ctx,
|
|
2491
|
+
const resolved = buildConditionalSpreadInitializer(ctx, parseExpression3(initTrimmed), ir);
|
|
2313
2492
|
if (resolved)
|
|
2314
2493
|
return resolved;
|
|
2315
2494
|
if (resolved === null)
|
|
@@ -2500,6 +2679,7 @@ function collectNillablePropNames(ctx, ir) {
|
|
|
2500
2679
|
}
|
|
2501
2680
|
|
|
2502
2681
|
// src/adapter/props/prop-classes.ts
|
|
2682
|
+
import { collectLoopBoundNames } from "@barefootjs/jsx";
|
|
2503
2683
|
function isStringTypeInfo(type) {
|
|
2504
2684
|
return type.kind === "primitive" && type.primitive === "string";
|
|
2505
2685
|
}
|
|
@@ -2520,6 +2700,13 @@ function collectStringValueNames(ir) {
|
|
|
2520
2700
|
if (isStringTypeInfo(p.type))
|
|
2521
2701
|
names.add(p.name);
|
|
2522
2702
|
}
|
|
2703
|
+
for (const c of ir.metadata.localConstants) {
|
|
2704
|
+
if (c.type !== null && isStringTypeInfo(c.type) || isBareStringLiteral(c.value)) {
|
|
2705
|
+
names.add(c.name);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
for (const bound of collectLoopBoundNames(ir))
|
|
2709
|
+
names.delete(bound);
|
|
2523
2710
|
return names;
|
|
2524
2711
|
}
|
|
2525
2712
|
|
|
@@ -2564,6 +2751,7 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2564
2751
|
return this.state.errors;
|
|
2565
2752
|
}
|
|
2566
2753
|
inLoop = false;
|
|
2754
|
+
bakedStaticChildLoopCache = new Map;
|
|
2567
2755
|
loopParamStack = [];
|
|
2568
2756
|
loopKeyDepthStack = [];
|
|
2569
2757
|
loopScalarItemStack = [];
|
|
@@ -2571,6 +2759,8 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2571
2759
|
loopVarRefCount = new Map;
|
|
2572
2760
|
loopBindingStack = [];
|
|
2573
2761
|
loopRestExcludeStack = [];
|
|
2762
|
+
staticLoopItemStack = [];
|
|
2763
|
+
staticLoopBakeFailed = false;
|
|
2574
2764
|
childComponentShapes = new Map;
|
|
2575
2765
|
childContextConsumers = new Map;
|
|
2576
2766
|
constructor(options = {}) {
|
|
@@ -2586,6 +2776,8 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2586
2776
|
this.state.restPropsName = ir.metadata.restPropsName ?? null;
|
|
2587
2777
|
this.state.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
2588
2778
|
this.state.localConstants = ir.metadata.localConstants ?? [];
|
|
2779
|
+
this.state.staticLoopSourceBoundNames = collectLoopBoundNames2(ir);
|
|
2780
|
+
this.bakedStaticChildLoopCache = new Map;
|
|
2589
2781
|
this.state.localHelperNames = new Set(this.state.localConstants.filter((c) => !c.isModule && c.containsArrow).map((c) => c.name));
|
|
2590
2782
|
this.state.currentMemos = ir.metadata.memos ?? [];
|
|
2591
2783
|
this.state.currentTypeDefinitions = ir.metadata.typeDefinitions ?? [];
|
|
@@ -2967,6 +3159,8 @@ ${goFields.join(`
|
|
|
2967
3159
|
lines.push(` ${fieldName} ${goType}`);
|
|
2968
3160
|
}
|
|
2969
3161
|
for (const nested of inputNested) {
|
|
3162
|
+
if (nested.loopMarkerId && this.getBakedStaticChildLoop(nested.loopMarkerId, nested, nested.loopArrayParsed, nested.loopParam, nested.loopKey))
|
|
3163
|
+
continue;
|
|
2970
3164
|
lines.push(` ${nested.name}s []${nested.name}Input`);
|
|
2971
3165
|
}
|
|
2972
3166
|
const takenInput = new Set(ir.metadata.propsParams.map((p) => capitalizeFieldName(p.name)));
|
|
@@ -3098,6 +3292,21 @@ ${goFields.join(`
|
|
|
3098
3292
|
const staticWithoutBody = staticNested.filter((n) => !n.bodyChildren || n.bodyChildren.length === 0);
|
|
3099
3293
|
for (const nested of staticWithoutBody) {
|
|
3100
3294
|
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
3295
|
+
const baked = nested.loopMarkerId ? this.getBakedStaticChildLoop(nested.loopMarkerId, nested, nested.loopArrayParsed, nested.loopParam, nested.loopKey) : null;
|
|
3296
|
+
if (baked) {
|
|
3297
|
+
lines.push(` ${varName} := make([]${nested.name}Props, ${baked.items.length})`);
|
|
3298
|
+
baked.items.forEach((item, i) => {
|
|
3299
|
+
const fields = item.inputFields.map((f) => `${f.goField}: ${f.goValue}`).join(", ");
|
|
3300
|
+
lines.push(` ${varName}[${i}] = New${nested.name}Props(${nested.name}Input{${fields}})`);
|
|
3301
|
+
lines.push(` ${varName}[${i}].BfParent = scopeID`);
|
|
3302
|
+
lines.push(` ${varName}[${i}].BfMount = "${nested.slotId}"`);
|
|
3303
|
+
if (item.dataKey !== null) {
|
|
3304
|
+
lines.push(` ${varName}[${i}].BfDataKey = ${JSON.stringify(item.dataKey)}`);
|
|
3305
|
+
}
|
|
3306
|
+
});
|
|
3307
|
+
lines.push("");
|
|
3308
|
+
continue;
|
|
3309
|
+
}
|
|
3101
3310
|
lines.push(` ${varName} := make([]${nested.name}Props, len(in.${nested.name}s))`);
|
|
3102
3311
|
lines.push(` for i, item := range in.${nested.name}s {`);
|
|
3103
3312
|
lines.push(` ${varName}[i] = New${nested.name}Props(item)`);
|
|
@@ -4151,7 +4360,8 @@ ${goFields.join(`
|
|
|
4151
4360
|
renderElement(element) {
|
|
4152
4361
|
const tag = element.tag;
|
|
4153
4362
|
const attrs = this.renderAttributes(element);
|
|
4154
|
-
const
|
|
4363
|
+
const dangerousHtml = this.renderDangerousInnerHtml(element);
|
|
4364
|
+
const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
|
|
4155
4365
|
let hydrationAttrs = "";
|
|
4156
4366
|
if (element.needsScope) {
|
|
4157
4367
|
hydrationAttrs += ` ${this.renderScopeMarker(".ScopeID")}`;
|
|
@@ -4186,6 +4396,22 @@ ${goFields.join(`
|
|
|
4186
4396
|
}
|
|
4187
4397
|
return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
|
|
4188
4398
|
}
|
|
4399
|
+
renderDangerousInnerHtml(element) {
|
|
4400
|
+
const resolution = resolveDangerousInnerHtml(element);
|
|
4401
|
+
if (!resolution)
|
|
4402
|
+
return null;
|
|
4403
|
+
if (resolution.kind === "dynamic") {
|
|
4404
|
+
this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
|
|
4405
|
+
return "";
|
|
4406
|
+
}
|
|
4407
|
+
const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
|
|
4408
|
+
if (violation) {
|
|
4409
|
+
const attr = element.attrs.find(isDangerousInnerHtmlAttr);
|
|
4410
|
+
this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
|
|
4411
|
+
return "";
|
|
4412
|
+
}
|
|
4413
|
+
return resolution.html;
|
|
4414
|
+
}
|
|
4189
4415
|
renderExpression(expr) {
|
|
4190
4416
|
if (expr.clientOnly) {
|
|
4191
4417
|
if (expr.slotId) {
|
|
@@ -4919,8 +5145,8 @@ ${goFields.join(`
|
|
|
4919
5145
|
const value = negated ? "false" : "true";
|
|
4920
5146
|
return `len (bf_filter ${arrayExpr} "${field}" ${value})`;
|
|
4921
5147
|
}
|
|
4922
|
-
renderPredicateCondition(pred, param) {
|
|
4923
|
-
return this.renderFilterExpr(pred, param);
|
|
5148
|
+
renderPredicateCondition(pred, param, datumField) {
|
|
5149
|
+
return this.renderFilterExpr(pred, param, new Map, datumField ?? undefined);
|
|
4924
5150
|
}
|
|
4925
5151
|
needsParens(expr) {
|
|
4926
5152
|
return expr.kind === "logical" || expr.kind === "unary" || expr.kind === "conditional";
|
|
@@ -4941,21 +5167,23 @@ ${goFields.join(`
|
|
|
4941
5167
|
}
|
|
4942
5168
|
return null;
|
|
4943
5169
|
}
|
|
4944
|
-
renderFilterExpr(expr, param, localVarMap = new Map) {
|
|
5170
|
+
renderFilterExpr(expr, param, localVarMap = new Map, datumField) {
|
|
4945
5171
|
if (this.filterExprDepth === 0)
|
|
4946
5172
|
this.filterExprUnsupported = false;
|
|
4947
5173
|
this.filterExprDepth++;
|
|
4948
5174
|
try {
|
|
4949
|
-
return this.renderFilterExprNode(expr, param, localVarMap);
|
|
5175
|
+
return this.renderFilterExprNode(expr, param, localVarMap, datumField);
|
|
4950
5176
|
} finally {
|
|
4951
5177
|
this.filterExprDepth--;
|
|
4952
5178
|
}
|
|
4953
5179
|
}
|
|
4954
|
-
renderFilterExprNode(expr, param, localVarMap) {
|
|
5180
|
+
renderFilterExprNode(expr, param, localVarMap, datumField) {
|
|
5181
|
+
const paramPrefix = datumField ? `.${datumField}` : "";
|
|
5182
|
+
const paramDot = paramPrefix || ".";
|
|
4955
5183
|
switch (expr.kind) {
|
|
4956
5184
|
case "identifier": {
|
|
4957
5185
|
if (expr.name === param) {
|
|
4958
|
-
return
|
|
5186
|
+
return paramDot;
|
|
4959
5187
|
}
|
|
4960
5188
|
const signal = localVarMap.get(expr.name);
|
|
4961
5189
|
if (signal) {
|
|
@@ -4973,24 +5201,24 @@ ${goFields.join(`
|
|
|
4973
5201
|
return String(expr.value);
|
|
4974
5202
|
case "member": {
|
|
4975
5203
|
if (expr.object.kind === "identifier" && expr.object.name === param) {
|
|
4976
|
-
return
|
|
5204
|
+
return `${paramPrefix}.${capitalizeFieldName(expr.property)}`;
|
|
4977
5205
|
}
|
|
4978
5206
|
if (expr.property === "length") {
|
|
4979
5207
|
const innerHO = this.higherOrderShapeOf(expr.object);
|
|
4980
5208
|
if (innerHO && innerHO.method === "filter") {
|
|
4981
|
-
const lenExpr = this.renderFilterLengthExpr(innerHO, (e) => this.renderFilterExpr(e, param, localVarMap));
|
|
5209
|
+
const lenExpr = this.renderFilterLengthExpr(innerHO, (e) => this.renderFilterExpr(e, param, localVarMap, datumField));
|
|
4982
5210
|
if (lenExpr)
|
|
4983
5211
|
return `(${lenExpr})`;
|
|
4984
5212
|
}
|
|
4985
5213
|
}
|
|
4986
|
-
const obj = this.renderFilterExpr(expr.object, param, localVarMap);
|
|
5214
|
+
const obj = this.renderFilterExpr(expr.object, param, localVarMap, datumField);
|
|
4987
5215
|
if (this.filterExprUnsupported)
|
|
4988
5216
|
return "false";
|
|
4989
5217
|
return `${obj}.${capitalizeFieldName(expr.property)}`;
|
|
4990
5218
|
}
|
|
4991
5219
|
case "call": {
|
|
4992
5220
|
if (expr.callee.kind === "member" && expr.callee.object.kind === "identifier" && expr.callee.object.name === param) {
|
|
4993
|
-
return
|
|
5221
|
+
return `${paramPrefix}.${capitalizeFieldName(expr.callee.property)}`;
|
|
4994
5222
|
}
|
|
4995
5223
|
if (expr.callee.kind === "identifier" && expr.args.length === 0) {
|
|
4996
5224
|
return `$.${capitalizeFieldName(expr.callee.name)}`;
|
|
@@ -4998,13 +5226,13 @@ ${goFields.join(`
|
|
|
4998
5226
|
if (asCallbackMethodCall3(expr) !== null) {
|
|
4999
5227
|
return this.refuseFilterExprNode(expr);
|
|
5000
5228
|
}
|
|
5001
|
-
const result = this.renderFilterExpr(expr.callee, param, localVarMap);
|
|
5229
|
+
const result = this.renderFilterExpr(expr.callee, param, localVarMap, datumField);
|
|
5002
5230
|
if (this.filterExprUnsupported)
|
|
5003
5231
|
return "false";
|
|
5004
5232
|
return result;
|
|
5005
5233
|
}
|
|
5006
5234
|
case "unary": {
|
|
5007
|
-
const arg = this.renderFilterExpr(expr.argument, param, localVarMap);
|
|
5235
|
+
const arg = this.renderFilterExpr(expr.argument, param, localVarMap, datumField);
|
|
5008
5236
|
if (this.filterExprUnsupported)
|
|
5009
5237
|
return "false";
|
|
5010
5238
|
if (expr.op === "!") {
|
|
@@ -5017,10 +5245,10 @@ ${goFields.join(`
|
|
|
5017
5245
|
return arg;
|
|
5018
5246
|
}
|
|
5019
5247
|
case "binary": {
|
|
5020
|
-
const left = this.renderFilterExpr(expr.left, param, localVarMap);
|
|
5248
|
+
const left = this.renderFilterExpr(expr.left, param, localVarMap, datumField);
|
|
5021
5249
|
if (this.filterExprUnsupported)
|
|
5022
5250
|
return "false";
|
|
5023
|
-
const right = this.renderFilterExpr(expr.right, param, localVarMap);
|
|
5251
|
+
const right = this.renderFilterExpr(expr.right, param, localVarMap, datumField);
|
|
5024
5252
|
if (this.filterExprUnsupported)
|
|
5025
5253
|
return "false";
|
|
5026
5254
|
switch (expr.op) {
|
|
@@ -5051,10 +5279,10 @@ ${goFields.join(`
|
|
|
5051
5279
|
}
|
|
5052
5280
|
}
|
|
5053
5281
|
case "logical": {
|
|
5054
|
-
const left = this.renderFilterExpr(expr.left, param, localVarMap);
|
|
5282
|
+
const left = this.renderFilterExpr(expr.left, param, localVarMap, datumField);
|
|
5055
5283
|
if (this.filterExprUnsupported)
|
|
5056
5284
|
return "false";
|
|
5057
|
-
const right = this.renderFilterExpr(expr.right, param, localVarMap);
|
|
5285
|
+
const right = this.renderFilterExpr(expr.right, param, localVarMap, datumField);
|
|
5058
5286
|
if (this.filterExprUnsupported)
|
|
5059
5287
|
return "false";
|
|
5060
5288
|
if (expr.op === "&&") {
|
|
@@ -5122,11 +5350,22 @@ ${goFields.join(`
|
|
|
5122
5350
|
if (trimmed === "null" || trimmed === "undefined") {
|
|
5123
5351
|
return '""';
|
|
5124
5352
|
}
|
|
5353
|
+
if (this.staticLoopItemStack.length > 0) {
|
|
5354
|
+
const top = this.staticLoopItemStack[this.staticLoopItemStack.length - 1];
|
|
5355
|
+
const parsedForBake = preParsed ?? parseExpression4(trimmed);
|
|
5356
|
+
const resolved = evaluateStaticLiteral3(parsedForBake, new Map([[top.param, top.item]]));
|
|
5357
|
+
const literal = resolved !== null ? scalarToGoLiteral(resolved.value) : null;
|
|
5358
|
+
if (literal !== null) {
|
|
5359
|
+
return literal;
|
|
5360
|
+
}
|
|
5361
|
+
this.staticLoopBakeFailed = true;
|
|
5362
|
+
return '""';
|
|
5363
|
+
}
|
|
5125
5364
|
const staticIndexed = this.resolveStaticRecordLiteralIndex(trimmed);
|
|
5126
5365
|
if (staticIndexed !== null) {
|
|
5127
5366
|
return staticIndexed;
|
|
5128
5367
|
}
|
|
5129
|
-
if (/^[A-Za-z_$][\w$]*$/.test(trimmed)) {
|
|
5368
|
+
if (!this.isLoopShadowedName(trimmed) && /^[A-Za-z_$][\w$]*$/.test(trimmed)) {
|
|
5130
5369
|
const litConst = (this.state.localConstants ?? []).find((c) => c.name === trimmed);
|
|
5131
5370
|
if (litConst?.value !== undefined) {
|
|
5132
5371
|
const v = litConst.value.trim();
|
|
@@ -5144,7 +5383,7 @@ ${goFields.join(`
|
|
|
5144
5383
|
if (inlined !== null) {
|
|
5145
5384
|
return this.convertExpressionToGo(stringifyParsedExpr2(inlined), out, inlined);
|
|
5146
5385
|
}
|
|
5147
|
-
const parsed = preParsed ??
|
|
5386
|
+
const parsed = preParsed ?? parseExpression4(trimmed);
|
|
5148
5387
|
const support = isSupported(parsed);
|
|
5149
5388
|
if (!support.supported) {
|
|
5150
5389
|
this.state.errors.push({
|
|
@@ -5162,10 +5401,15 @@ ${goFields.join(`
|
|
|
5162
5401
|
out.parsed = parsed;
|
|
5163
5402
|
return this.renderParsedExpr(parsed);
|
|
5164
5403
|
}
|
|
5404
|
+
isLoopShadowedName(name) {
|
|
5405
|
+
return this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name || this.loopVarRefCount.has(name) || this.isOuterLoopParam(name) || this.loopBindingStack.some((bindings) => bindings.has(name));
|
|
5406
|
+
}
|
|
5165
5407
|
resolveStaticRecordLiteralIndex(jsExpr) {
|
|
5166
5408
|
const m = /^([A-Za-z_$][\w$]*)\[\s*(?:'([^']*)'|"([^"]*)")\s*\]$/.exec(jsExpr) ?? /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/.exec(jsExpr);
|
|
5167
5409
|
if (!m)
|
|
5168
5410
|
return null;
|
|
5411
|
+
if (this.isLoopShadowedName(m[1]))
|
|
5412
|
+
return null;
|
|
5169
5413
|
const key = m[2] ?? m[3];
|
|
5170
5414
|
const constInfo = (this.state.localConstants ?? []).find((c) => c.name === m[1] && c.isModule);
|
|
5171
5415
|
if (constInfo?.value === undefined)
|
|
@@ -5271,7 +5515,7 @@ ${goFields.join(`
|
|
|
5271
5515
|
}
|
|
5272
5516
|
convertConditionToGo(jsCondition, preParsed) {
|
|
5273
5517
|
const trimmed = jsCondition.trim();
|
|
5274
|
-
const parsed = preParsed ??
|
|
5518
|
+
const parsed = preParsed ?? parseExpression4(trimmed);
|
|
5275
5519
|
const support = isSupported(parsed);
|
|
5276
5520
|
if (!support.supported) {
|
|
5277
5521
|
this.state.errors.push({
|
|
@@ -5492,6 +5736,29 @@ ${goFields.join(`
|
|
|
5492
5736
|
}
|
|
5493
5737
|
return;
|
|
5494
5738
|
}
|
|
5739
|
+
wrapperDatumField(loop) {
|
|
5740
|
+
if (!loop.childComponent)
|
|
5741
|
+
return null;
|
|
5742
|
+
for (const prop of loop.childComponent.props) {
|
|
5743
|
+
if (prop.isEventHandler)
|
|
5744
|
+
continue;
|
|
5745
|
+
if (prop.value.kind !== "expression")
|
|
5746
|
+
continue;
|
|
5747
|
+
const parsed = prop.value.parsed;
|
|
5748
|
+
const isBareParamRef = parsed ? parsed.kind === "identifier" && parsed.name === loop.param : prop.value.expr.trim() === loop.param;
|
|
5749
|
+
if (isBareParamRef)
|
|
5750
|
+
return capitalizeFieldName(prop.name);
|
|
5751
|
+
}
|
|
5752
|
+
return null;
|
|
5753
|
+
}
|
|
5754
|
+
getBakedStaticChildLoop(markerId, childComponent, arrayParsed, param, key) {
|
|
5755
|
+
if (this.bakedStaticChildLoopCache.has(markerId)) {
|
|
5756
|
+
return this.bakedStaticChildLoopCache.get(markerId) ?? null;
|
|
5757
|
+
}
|
|
5758
|
+
const result = analyzeBakeableStaticChildLoop({ props: childComponent.props, loopArrayParsed: arrayParsed, loopParam: param, loopKey: key }, this.state.localConstants, { isNameShadowed: (name) => this.state.staticLoopSourceBoundNames.has(name) });
|
|
5759
|
+
this.bakedStaticChildLoopCache.set(markerId, result);
|
|
5760
|
+
return result;
|
|
5761
|
+
}
|
|
5495
5762
|
renderLoop(loop) {
|
|
5496
5763
|
if (loop.clientOnly) {
|
|
5497
5764
|
return `{{bfComment "loop:${loop.markerId}"}}{{bfComment "/loop:${loop.markerId}"}}`;
|
|
@@ -5512,8 +5779,13 @@ ${goFields.join(`
|
|
|
5512
5779
|
}
|
|
5513
5780
|
});
|
|
5514
5781
|
}
|
|
5782
|
+
const bakedChildLoop = loop.childComponent ? this.getBakedStaticChildLoop(loop.markerId, loop.childComponent, loop.arrayParsed, loop.param, loop.key ?? undefined) : null;
|
|
5783
|
+
const bakedElementLoop = loop.childComponent ? null : analyzeBakeableStaticElementLoop(loop, this.state.localConstants, { isNameShadowed: (name) => this.state.staticLoopSourceBoundNames.has(name) });
|
|
5784
|
+
if (bakedElementLoop) {
|
|
5785
|
+
return this.renderUnrolledStaticElementLoop(loop, bakedElementLoop.items);
|
|
5786
|
+
}
|
|
5515
5787
|
const arrayName = loop.array.trim();
|
|
5516
|
-
if (/^[A-Za-z_$][\w$]*$/.test(arrayName)) {
|
|
5788
|
+
if (bakedChildLoop === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
|
|
5517
5789
|
const arrayConst = this.state.localConstants.find((c) => c.name === arrayName);
|
|
5518
5790
|
if (arrayConst && !arrayConst.isModule && arrayConst.parsed && !this.isStringExpr(arrayConst.parsed, new Set)) {
|
|
5519
5791
|
this.state.errors.push({
|
|
@@ -5527,7 +5799,7 @@ ${goFields.join(`
|
|
|
5527
5799
|
});
|
|
5528
5800
|
}
|
|
5529
5801
|
}
|
|
5530
|
-
let goArray = this.convertExpressionToGo(loop.array);
|
|
5802
|
+
let goArray = loop.childComponent ? "" : this.convertExpressionToGo(loop.array);
|
|
5531
5803
|
const param = loop.param;
|
|
5532
5804
|
let index = loop.index || "_";
|
|
5533
5805
|
let rangeIndex = index;
|
|
@@ -5595,7 +5867,8 @@ ${goFields.join(`
|
|
|
5595
5867
|
if (loop.filterPredicate) {
|
|
5596
5868
|
let filterCond;
|
|
5597
5869
|
if (loop.filterPredicate.predicate) {
|
|
5598
|
-
|
|
5870
|
+
const datumField = this.wrapperDatumField(loop);
|
|
5871
|
+
filterCond = this.renderPredicateCondition(loop.filterPredicate.predicate, loop.filterPredicate.param, datumField);
|
|
5599
5872
|
} else {
|
|
5600
5873
|
filterCond = "true";
|
|
5601
5874
|
}
|
|
@@ -5603,6 +5876,38 @@ ${goFields.join(`
|
|
|
5603
5876
|
}
|
|
5604
5877
|
return `{{bfComment "loop:${loop.markerId}"}}{{range $${rangeIndex}, $${rangeValue} := ${goArray}}}${itemMarker}${children}{{end}}{{bfComment "/loop:${loop.markerId}"}}`;
|
|
5605
5878
|
}
|
|
5879
|
+
renderUnrolledStaticElementLoop(loop, items) {
|
|
5880
|
+
this.inLoop = true;
|
|
5881
|
+
this.loopWrapperStack.push(false);
|
|
5882
|
+
this.loopKeyDepthStack.push(loop.depth);
|
|
5883
|
+
this.loopScalarItemStack.push(this.scalarLiteralLoopGoType(loop.arrayParsed, loop.itemType) !== null);
|
|
5884
|
+
this.loopParamStack.push(loop.param);
|
|
5885
|
+
let body = "";
|
|
5886
|
+
for (const item of items) {
|
|
5887
|
+
this.staticLoopItemStack.push({ param: loop.param, item });
|
|
5888
|
+
body += this.renderChildren(loop.children);
|
|
5889
|
+
this.staticLoopItemStack.pop();
|
|
5890
|
+
if (this.staticLoopBakeFailed) {
|
|
5891
|
+
this.staticLoopBakeFailed = false;
|
|
5892
|
+
this.state.errors.push({
|
|
5893
|
+
code: "BF101",
|
|
5894
|
+
severity: "error",
|
|
5895
|
+
message: `Loop array \`${loop.array.trim()}\` could not be fully unrolled — an expression in the loop body did not resolve against every item as the compile-time analysis expected.`,
|
|
5896
|
+
loc: loop.loc ?? this.makeLoc(),
|
|
5897
|
+
suggestion: {
|
|
5898
|
+
message: "This indicates a bug in the Go adapter's static-loop unrolling (#2224) rather than an unsupported source pattern; please file a bug with a reproduction."
|
|
5899
|
+
}
|
|
5900
|
+
});
|
|
5901
|
+
break;
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
this.loopParamStack.pop();
|
|
5905
|
+
this.loopScalarItemStack.pop();
|
|
5906
|
+
this.loopKeyDepthStack.pop();
|
|
5907
|
+
this.loopWrapperStack.pop();
|
|
5908
|
+
this.inLoop = false;
|
|
5909
|
+
return `{{bfComment "loop:${loop.markerId}"}}${body}{{bfComment "/loop:${loop.markerId}"}}`;
|
|
5910
|
+
}
|
|
5606
5911
|
loopItemMarker(loop) {
|
|
5607
5912
|
if (loop.bodyIsMultiRoot)
|
|
5608
5913
|
return `{{bfComment "bf-loop-i"}}`;
|
|
@@ -5722,7 +6027,7 @@ ${children}`;
|
|
|
5722
6027
|
const body = name.startsWith("aria-") ? `${name}="true"` : name;
|
|
5723
6028
|
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
|
5724
6029
|
}
|
|
5725
|
-
const parsed = value.parsed ??
|
|
6030
|
+
const parsed = value.parsed ?? parseExpression4(value.expr.trim());
|
|
5726
6031
|
if (parsed.kind === "conditional") {
|
|
5727
6032
|
const undef = (e) => e.kind === "identifier" && (e.name === "undefined" || e.name === "null") || e.kind === "literal" && (e.value === null || e.value === undefined);
|
|
5728
6033
|
const test = parsed.test;
|
|
@@ -5787,7 +6092,7 @@ ${children}`;
|
|
|
5787
6092
|
if (!entries)
|
|
5788
6093
|
return null;
|
|
5789
6094
|
for (const e of entries) {
|
|
5790
|
-
if (e.kind === "expr" && !isSupported(
|
|
6095
|
+
if (e.kind === "expr" && !isSupported(parseExpression4(e.expr)).supported)
|
|
5791
6096
|
return null;
|
|
5792
6097
|
}
|
|
5793
6098
|
return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:{{${this.convertExpressionToGo(e.expr)}}}`).join(";");
|
|
@@ -5797,6 +6102,8 @@ ${children}`;
|
|
|
5797
6102
|
for (const attr of element.attrs) {
|
|
5798
6103
|
if (attr.clientOnly)
|
|
5799
6104
|
continue;
|
|
6105
|
+
if (isDangerousInnerHtmlAttr(attr))
|
|
6106
|
+
continue;
|
|
5800
6107
|
let attrName;
|
|
5801
6108
|
if (attr.name === "className")
|
|
5802
6109
|
attrName = "class";
|
|
@@ -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,eA+H7B,CAAA"}
|