@barefootjs/jsx 0.18.4 → 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/adapters/dangerous-inner-html.d.ts +77 -0
- package/dist/adapters/dangerous-inner-html.d.ts.map +1 -0
- package/dist/adapters/loop-bound-names.d.ts +22 -0
- package/dist/adapters/loop-bound-names.d.ts.map +1 -0
- package/dist/adapters/parsed-expr-emitter.d.ts +2 -2
- package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/expression-parser.d.ts +2 -1
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1108 -307
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts +10 -0
- package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts +9 -0
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +9 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/shared.d.ts +45 -3
- package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts +1 -0
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts +10 -0
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/skeleton-paths.d.ts +38 -0
- package/dist/ir-to-client-js/control-flow/stringify/skeleton-paths.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/stringify/template-parse.d.ts +19 -0
- package/dist/ir-to-client-js/control-flow/stringify/template-parse.d.ts.map +1 -1
- package/dist/ir-to-client-js/csr-substitute.d.ts +24 -0
- package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +42 -0
- package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
- package/dist/ir-to-client-js/imports.d.ts +2 -2
- package/dist/ir-to-client-js/imports.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/static-array-child-init.d.ts +20 -1
- package/dist/ir-to-client-js/plan/static-array-child-init.d.ts.map +1 -1
- package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
- package/dist/ir-to-client-js/stringify/static-array-child-init.d.ts +1 -1
- package/dist/ir-to-client-js/types.d.ts +30 -0
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/ir-to-client-js/utils.d.ts +25 -0
- package/dist/ir-to-client-js/utils.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/signal-init-eval.d.ts +82 -0
- package/dist/signal-init-eval.d.ts.map +1 -0
- package/dist/static-literal.d.ts +45 -0
- package/dist/static-literal.d.ts.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +19 -13
- package/src/__tests__/create-selector.test.ts +110 -0
- package/src/__tests__/csr-template-loop-shadowing.test.ts +214 -0
- package/src/__tests__/dangerous-inner-html-resolver.test.ts +197 -0
- package/src/__tests__/event-delegation-index-param.test.ts +130 -0
- package/src/__tests__/expression-parser.test.ts +38 -0
- package/src/__tests__/inner-loop-svg-namespace.test.ts +245 -0
- package/src/__tests__/ir-walker.test.ts +1 -0
- package/src/__tests__/loop-bound-names.test.ts +102 -0
- package/src/__tests__/loop-plan-classification.test.ts +2 -0
- package/src/__tests__/map-function-reference.test.ts +303 -0
- package/src/__tests__/materialize-getter-calls.test.ts +1 -0
- package/src/__tests__/nested-loop-index-param.test.ts +285 -0
- package/src/__tests__/signal-init-eval.test.ts +138 -0
- package/src/__tests__/static-array-inner-loop-index-param.test.ts +180 -0
- package/src/__tests__/static-literal.test.ts +126 -0
- package/src/__tests__/string-concat-identifier.test.ts +55 -0
- package/src/adapters/dangerous-inner-html.ts +186 -0
- package/src/adapters/loop-bound-names.ts +80 -0
- package/src/adapters/parsed-expr-emitter.ts +19 -1
- package/src/analyzer.ts +6 -0
- package/src/expression-parser.ts +59 -25
- package/src/index.ts +19 -3
- package/src/ir-to-client-js/collect-elements.ts +18 -3
- package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +6 -0
- package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +21 -4
- package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +13 -1
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +1 -0
- package/src/ir-to-client-js/control-flow/plan/event-delegation.ts +10 -0
- package/src/ir-to-client-js/control-flow/plan/loop-child-arm.ts +9 -0
- package/src/ir-to-client-js/control-flow/plan/loop.ts +9 -0
- package/src/ir-to-client-js/control-flow/shared.ts +103 -6
- package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +31 -7
- package/src/ir-to-client-js/control-flow/stringify/inner-loop.ts +11 -2
- package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +5 -0
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +53 -10
- package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +25 -4
- package/src/ir-to-client-js/control-flow/stringify/skeleton-paths.ts +70 -0
- package/src/ir-to-client-js/control-flow/stringify/template-parse.ts +25 -1
- package/src/ir-to-client-js/csr-substitute.ts +54 -1
- package/src/ir-to-client-js/html-template.ts +292 -13
- package/src/ir-to-client-js/imports.ts +1 -0
- package/src/ir-to-client-js/plan/build-static-array-child-init.ts +12 -1
- package/src/ir-to-client-js/plan/static-array-child-init.ts +20 -1
- package/src/ir-to-client-js/reactivity.ts +6 -0
- package/src/ir-to-client-js/stringify/static-array-child-init.ts +11 -6
- package/src/ir-to-client-js/types.ts +31 -0
- package/src/ir-to-client-js/utils.ts +34 -1
- package/src/jsx-to-ir.ts +322 -48
- package/src/signal-init-eval.ts +165 -0
- package/src/static-literal.ts +128 -0
- package/src/types.ts +68 -0
package/dist/index.js
CHANGED
|
@@ -206,7 +206,6 @@ var UNSUPPORTED_METHODS = new Set([
|
|
|
206
206
|
"some",
|
|
207
207
|
"forEach",
|
|
208
208
|
"flatMap",
|
|
209
|
-
"replaceAll",
|
|
210
209
|
"charAt",
|
|
211
210
|
"charCodeAt",
|
|
212
211
|
"codePointAt",
|
|
@@ -481,6 +480,9 @@ function convertNode(node, raw) {
|
|
|
481
480
|
if (callee.property === "trim") {
|
|
482
481
|
return { kind: "array-method", method: "trim", object: callee.object, args };
|
|
483
482
|
}
|
|
483
|
+
if (callee.property === "trimStart" || callee.property === "trimEnd") {
|
|
484
|
+
return { kind: "array-method", method: callee.property, object: callee.object, args };
|
|
485
|
+
}
|
|
484
486
|
if (callee.property === "toFixed") {
|
|
485
487
|
return { kind: "array-method", method: "toFixed", object: callee.object, args };
|
|
486
488
|
}
|
|
@@ -506,24 +508,25 @@ function convertNode(node, raw) {
|
|
|
506
508
|
}
|
|
507
509
|
return { kind: "array-method", method: callee.property, object: callee.object, args };
|
|
508
510
|
}
|
|
509
|
-
if (callee.property === "replace") {
|
|
511
|
+
if (callee.property === "replace" || callee.property === "replaceAll") {
|
|
512
|
+
const method = callee.property;
|
|
510
513
|
if (args.length < 2) {
|
|
511
514
|
return {
|
|
512
515
|
kind: "unsupported",
|
|
513
516
|
raw,
|
|
514
|
-
reason:
|
|
517
|
+
reason: `\`.${method}(${args.length === 0 ? "" : "pattern"})\` needs both a pattern and a replacement — JS coerces the missing argument to the string "undefined", a degenerate result. Pass both arguments, or pre-compute the value before the template.`
|
|
515
518
|
};
|
|
516
519
|
}
|
|
517
520
|
const patternNode = node.arguments[0];
|
|
518
521
|
if (patternNode && ts2.isRegularExpressionLiteral(patternNode)) {
|
|
519
|
-
return { kind: "array-method", method
|
|
522
|
+
return { kind: "array-method", method, object: callee.object, args };
|
|
520
523
|
}
|
|
521
524
|
const badArg = args[0].kind === "unsupported" || args[0].kind === "object-literal" ? args[0] : args[1].kind === "unsupported" || args[1].kind === "object-literal" ? args[1] : undefined;
|
|
522
525
|
if (badArg) {
|
|
523
526
|
const reason = badArg.kind === "unsupported" ? badArg.reason : "Unsupported syntax: ObjectLiteralExpression";
|
|
524
527
|
return { kind: "unsupported", raw, reason };
|
|
525
528
|
}
|
|
526
|
-
return { kind: "array-method", method
|
|
529
|
+
return { kind: "array-method", method, object: callee.object, args };
|
|
527
530
|
}
|
|
528
531
|
if (callee.property === "repeat") {
|
|
529
532
|
return { kind: "array-method", method: "repeat", object: callee.object, args };
|
|
@@ -558,7 +561,7 @@ function convertNode(node, raw) {
|
|
|
558
561
|
if (ts2.isPropertyAccessExpression(node)) {
|
|
559
562
|
const object = convertNode(node.expression, raw);
|
|
560
563
|
const property = node.name.text;
|
|
561
|
-
return { kind: "member", object, property, computed: false };
|
|
564
|
+
return { kind: "member", object, property, computed: false, optional: !!node.questionDotToken };
|
|
562
565
|
}
|
|
563
566
|
if (ts2.isElementAccessExpression(node)) {
|
|
564
567
|
const object = convertNode(node.expression, raw);
|
|
@@ -567,10 +570,10 @@ function convertNode(node, raw) {
|
|
|
567
570
|
return { kind: "unsupported", raw, reason: "Element access with no index expression" };
|
|
568
571
|
}
|
|
569
572
|
if (ts2.isNumericLiteral(argNode)) {
|
|
570
|
-
return { kind: "member", object, property: argNode.text, computed: true };
|
|
573
|
+
return { kind: "member", object, property: argNode.text, computed: true, optional: !!node.questionDotToken };
|
|
571
574
|
}
|
|
572
575
|
if (ts2.isStringLiteral(argNode)) {
|
|
573
|
-
return { kind: "member", object, property: argNode.text, computed: true };
|
|
576
|
+
return { kind: "member", object, property: argNode.text, computed: true, optional: !!node.questionDotToken };
|
|
574
577
|
}
|
|
575
578
|
const index = convertNode(argNode, raw);
|
|
576
579
|
if (index.kind === "unsupported" || index.kind === "object-literal")
|
|
@@ -1121,7 +1124,7 @@ function substituteDestructuredFields(expr, fieldMap, syntheticParam, restName)
|
|
|
1121
1124
|
return e;
|
|
1122
1125
|
let node = { kind: "identifier", name: syntheticParam };
|
|
1123
1126
|
for (const segment of entry.path) {
|
|
1124
|
-
node = { kind: "member", object: node, property: segment, computed: false };
|
|
1127
|
+
node = { kind: "member", object: node, property: segment, computed: false, optional: false };
|
|
1125
1128
|
}
|
|
1126
1129
|
if (entry.defaultExpr) {
|
|
1127
1130
|
return { kind: "logical", op: "??", left: node, right: entry.defaultExpr };
|
|
@@ -1136,10 +1139,11 @@ function substituteDestructuredFields(expr, fieldMap, syntheticParam, restName)
|
|
|
1136
1139
|
kind: "member",
|
|
1137
1140
|
object: { kind: "identifier", name: syntheticParam },
|
|
1138
1141
|
property: e.property,
|
|
1139
|
-
computed: false
|
|
1142
|
+
computed: false,
|
|
1143
|
+
optional: e.optional
|
|
1140
1144
|
};
|
|
1141
1145
|
}
|
|
1142
|
-
return { kind: "member", object: walk(e.object), property: e.property, computed: e.computed };
|
|
1146
|
+
return { kind: "member", object: walk(e.object), property: e.property, computed: e.computed, optional: e.optional };
|
|
1143
1147
|
case "index-access":
|
|
1144
1148
|
return { kind: "index-access", object: walk(e.object), index: walk(e.index) };
|
|
1145
1149
|
case "binary":
|
|
@@ -1251,10 +1255,10 @@ function checkSupport(expr) {
|
|
|
1251
1255
|
return { supported: true, level: "L2" };
|
|
1252
1256
|
}
|
|
1253
1257
|
case "array-method": {
|
|
1254
|
-
if (expr.method === "replace" && expr.args[0]?.kind === "regex") {
|
|
1258
|
+
if ((expr.method === "replace" || expr.method === "replaceAll") && expr.args[0]?.kind === "regex") {
|
|
1255
1259
|
return {
|
|
1256
1260
|
supported: false,
|
|
1257
|
-
reason:
|
|
1261
|
+
reason: `String.prototype.${expr.method} supports only a string pattern + string replacement (the regex form is deferred); use a string pattern or wrap the expression in /* @client */`
|
|
1258
1262
|
};
|
|
1259
1263
|
}
|
|
1260
1264
|
const objSupport = checkSupport(expr.object);
|
|
@@ -1628,7 +1632,7 @@ function inlineBinding(expr, name, value) {
|
|
|
1628
1632
|
case "call":
|
|
1629
1633
|
return { kind: "call", callee: walk(e.callee, enclosing), args: e.args.map((a) => walk(a, enclosing)) };
|
|
1630
1634
|
case "member":
|
|
1631
|
-
return { kind: "member", object: walk(e.object, enclosing), property: e.property, computed: e.computed };
|
|
1635
|
+
return { kind: "member", object: walk(e.object, enclosing), property: e.property, computed: e.computed, optional: e.optional };
|
|
1632
1636
|
case "index-access":
|
|
1633
1637
|
return { kind: "index-access", object: walk(e.object, enclosing), index: walk(e.index, enclosing) };
|
|
1634
1638
|
case "binary":
|
|
@@ -1879,7 +1883,7 @@ function materializeGetterCalls(expr, names) {
|
|
|
1879
1883
|
alternate: rw(expr.alternate)
|
|
1880
1884
|
};
|
|
1881
1885
|
case "member":
|
|
1882
|
-
return { kind: "member", object: rw(expr.object), property: expr.property, computed: expr.computed };
|
|
1886
|
+
return { kind: "member", object: rw(expr.object), property: expr.property, computed: expr.computed, optional: expr.optional };
|
|
1883
1887
|
case "index-access":
|
|
1884
1888
|
return { kind: "index-access", object: rw(expr.object), index: rw(expr.index) };
|
|
1885
1889
|
case "template-literal":
|
|
@@ -2293,13 +2297,23 @@ function attrValueToString(value, opts) {
|
|
|
2293
2297
|
return null;
|
|
2294
2298
|
}
|
|
2295
2299
|
}
|
|
2300
|
+
function applyObjectIterationWrap(node, arrayExpr) {
|
|
2301
|
+
if (node.objectIteration === "entries")
|
|
2302
|
+
return `Object.entries(${arrayExpr})`;
|
|
2303
|
+
if (node.objectIteration === "keys")
|
|
2304
|
+
return `Object.keys(${arrayExpr})`;
|
|
2305
|
+
if (node.objectIteration === "values")
|
|
2306
|
+
return `Object.values(${arrayExpr})`;
|
|
2307
|
+
return arrayExpr;
|
|
2308
|
+
}
|
|
2296
2309
|
function buildChainedArrayExpr(elem) {
|
|
2297
|
-
|
|
2310
|
+
const chained = buildLoopChainExpr({
|
|
2298
2311
|
base: elem.array,
|
|
2299
2312
|
sortComparator: elem.sortComparator,
|
|
2300
2313
|
filterPredicate: elem.filterPredicate,
|
|
2301
2314
|
chainOrder: elem.chainOrder
|
|
2302
2315
|
});
|
|
2316
|
+
return applyObjectIterationWrap(elem, chained);
|
|
2303
2317
|
}
|
|
2304
2318
|
function loopOffsetTerms(offset) {
|
|
2305
2319
|
if (!offset)
|
|
@@ -2928,6 +2942,29 @@ function extractFreeIdentifiersFromText(text) {
|
|
|
2928
2942
|
const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
|
|
2929
2943
|
return extractFreeIdentifiersFromNode(expr);
|
|
2930
2944
|
}
|
|
2945
|
+
function extractFreeIdentifiersFromStatementText(text) {
|
|
2946
|
+
if (!text || text.trim().length === 0)
|
|
2947
|
+
return new Set;
|
|
2948
|
+
const sf = ts4.createSourceFile("__free_ids_stmt__.ts", text, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
|
|
2949
|
+
return extractFreeIdentifiersFromNode(sf);
|
|
2950
|
+
}
|
|
2951
|
+
function extractFreeIdentifiersFromTemplateText(template) {
|
|
2952
|
+
if (!template || template.length === 0)
|
|
2953
|
+
return new Set;
|
|
2954
|
+
const sf = ts4.createSourceFile("__free_ids_template__.ts", `(\`${template}\`);`, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TS);
|
|
2955
|
+
const stmt = sf.statements[0];
|
|
2956
|
+
if (!stmt || !ts4.isExpressionStatement(stmt))
|
|
2957
|
+
return new Set;
|
|
2958
|
+
const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
|
|
2959
|
+
if (!ts4.isTemplateExpression(expr))
|
|
2960
|
+
return new Set;
|
|
2961
|
+
const ids = new Set;
|
|
2962
|
+
for (const span of expr.templateSpans) {
|
|
2963
|
+
for (const id of extractFreeIdentifiersFromNode(span.expression))
|
|
2964
|
+
ids.add(id);
|
|
2965
|
+
}
|
|
2966
|
+
return ids;
|
|
2967
|
+
}
|
|
2931
2968
|
function extractMemoBodyExpr(computation) {
|
|
2932
2969
|
const arrowMatch = computation.match(/^\(\)\s*=>\s*(.+)$/s);
|
|
2933
2970
|
if (!arrowMatch)
|
|
@@ -2971,7 +3008,7 @@ function normalizeSignalInitial(signal, propsObjectName) {
|
|
|
2971
3008
|
}
|
|
2972
3009
|
|
|
2973
3010
|
// src/ir-to-client-js/html-template.ts
|
|
2974
|
-
import { BF_PARENT_SCOPE_PLACEHOLDER, BF_SCOPE } from "@barefootjs/shared";
|
|
3011
|
+
import { BF_PARENT_SCOPE_PLACEHOLDER, BF_SCOPE, escapeHtml } from "@barefootjs/shared";
|
|
2975
3012
|
function createStringProtector() {
|
|
2976
3013
|
const strings = [];
|
|
2977
3014
|
const protect = (s) => {
|
|
@@ -3073,6 +3110,24 @@ function applyIterationShape(node, arrayExpr, indexParam) {
|
|
|
3073
3110
|
callbackParam: `(${node.param})`
|
|
3074
3111
|
};
|
|
3075
3112
|
}
|
|
3113
|
+
if (node.objectIteration === "entries") {
|
|
3114
|
+
return {
|
|
3115
|
+
array: `Object.entries(${arrayExpr})`,
|
|
3116
|
+
callbackParam: node.index ? `([${node.index}, ${node.param}])` : `(${node.param}${indexParam})`
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
3119
|
+
if (node.objectIteration === "keys") {
|
|
3120
|
+
return {
|
|
3121
|
+
array: `Object.keys(${arrayExpr})`,
|
|
3122
|
+
callbackParam: `(${node.param})`
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
3125
|
+
if (node.objectIteration === "values") {
|
|
3126
|
+
return {
|
|
3127
|
+
array: `Object.values(${arrayExpr})`,
|
|
3128
|
+
callbackParam: `(${node.param})`
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3076
3131
|
return { array: arrayExpr, callbackParam: `(${node.param}${indexParam})` };
|
|
3077
3132
|
}
|
|
3078
3133
|
function childrenPropEntry(children, recurse) {
|
|
@@ -3132,7 +3187,7 @@ function renderTemplateAttrPart(attr, attrName, wrap, restSpreadNames) {
|
|
|
3132
3187
|
case "boolean-attr":
|
|
3133
3188
|
return attrName;
|
|
3134
3189
|
case "literal":
|
|
3135
|
-
return `${attrName}="${v.value}"`;
|
|
3190
|
+
return `${attrName}="${escapeHtml(v.value)}"`;
|
|
3136
3191
|
case "expression": {
|
|
3137
3192
|
const valExpr = wrap(v.expr);
|
|
3138
3193
|
return templateAttrExpr(attrName, valExpr, v.presenceOrUndefined);
|
|
@@ -3263,7 +3318,7 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
|
|
|
3263
3318
|
return `<${node.tag}${attrs ? " " + attrs : ""} />`;
|
|
3264
3319
|
}
|
|
3265
3320
|
case "text":
|
|
3266
|
-
return node.value;
|
|
3321
|
+
return escapeHtml(node.value);
|
|
3267
3322
|
case "expression":
|
|
3268
3323
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3269
3324
|
return "";
|
|
@@ -3371,7 +3426,7 @@ function buildLoopSkeletonTemplate(node, safe) {
|
|
|
3371
3426
|
const v = a.value;
|
|
3372
3427
|
switch (v.kind) {
|
|
3373
3428
|
case "literal":
|
|
3374
|
-
attrParts.push(`${toHtmlAttrName(a.name)}="${v.value}"`);
|
|
3429
|
+
attrParts.push(`${toHtmlAttrName(a.name)}="${escapeHtml(v.value)}"`);
|
|
3375
3430
|
break;
|
|
3376
3431
|
case "boolean-attr":
|
|
3377
3432
|
attrParts.push(toHtmlAttrName(a.name));
|
|
@@ -3406,7 +3461,7 @@ function buildLoopSkeletonTemplate(node, safe) {
|
|
|
3406
3461
|
return `<${node.tag}${attrs ? " " + attrs : ""} />`;
|
|
3407
3462
|
}
|
|
3408
3463
|
case "text":
|
|
3409
|
-
return node.value;
|
|
3464
|
+
return escapeHtml(node.value);
|
|
3410
3465
|
case "expression":
|
|
3411
3466
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3412
3467
|
return "";
|
|
@@ -3438,6 +3493,133 @@ function buildLoopSkeletonTemplate(node, safe) {
|
|
|
3438
3493
|
return assertNever(node);
|
|
3439
3494
|
}
|
|
3440
3495
|
}
|
|
3496
|
+
var SKELETON_PATH_HAZARD_TAGS = new Set([
|
|
3497
|
+
"table",
|
|
3498
|
+
"thead",
|
|
3499
|
+
"tbody",
|
|
3500
|
+
"tfoot",
|
|
3501
|
+
"caption",
|
|
3502
|
+
"colgroup",
|
|
3503
|
+
"col",
|
|
3504
|
+
"select",
|
|
3505
|
+
"optgroup",
|
|
3506
|
+
"p",
|
|
3507
|
+
"pre",
|
|
3508
|
+
"textarea",
|
|
3509
|
+
"listing",
|
|
3510
|
+
"template",
|
|
3511
|
+
"math"
|
|
3512
|
+
]);
|
|
3513
|
+
var SKELETON_PATH_FORCE_CLOSE_GROUPS = [
|
|
3514
|
+
new Set(["a"]),
|
|
3515
|
+
new Set(["button"]),
|
|
3516
|
+
new Set(["form"]),
|
|
3517
|
+
new Set(["option"]),
|
|
3518
|
+
new Set(["h1", "h2", "h3", "h4", "h5", "h6"]),
|
|
3519
|
+
new Set(["dd", "dt"]),
|
|
3520
|
+
new Set(["li"])
|
|
3521
|
+
];
|
|
3522
|
+
function skeletonForceCloseGroup(tag) {
|
|
3523
|
+
return SKELETON_PATH_FORCE_CLOSE_GROUPS.findIndex((group) => group.has(tag));
|
|
3524
|
+
}
|
|
3525
|
+
function computeSkeletonSlotPaths(node, safe) {
|
|
3526
|
+
const state = { elementPaths: new Map, textMarkerPaths: new Map, bailed: false };
|
|
3527
|
+
walkSkeletonPathNode(node, [], safe, state, new Set);
|
|
3528
|
+
if (state.bailed)
|
|
3529
|
+
return null;
|
|
3530
|
+
return { elementPaths: state.elementPaths, textMarkerPaths: state.textMarkerPaths };
|
|
3531
|
+
}
|
|
3532
|
+
function walkSkeletonPathNode(node, path, safe, state, forceCloseAncestors) {
|
|
3533
|
+
if (state.bailed || node.type !== "element")
|
|
3534
|
+
return;
|
|
3535
|
+
if (SKELETON_PATH_HAZARD_TAGS.has(node.tag)) {
|
|
3536
|
+
state.bailed = true;
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
const groupIdx = skeletonForceCloseGroup(node.tag);
|
|
3540
|
+
if (groupIdx >= 0 && forceCloseAncestors.has(groupIdx)) {
|
|
3541
|
+
state.bailed = true;
|
|
3542
|
+
return;
|
|
3543
|
+
}
|
|
3544
|
+
const flatChildren = flattenSkeletonChildren(node.children);
|
|
3545
|
+
if (VOID_ELEMENTS.has(node.tag) && flatChildren.length > 0) {
|
|
3546
|
+
state.bailed = true;
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
if (node.tag === "tr" && hasForeignTableRowContent(flatChildren)) {
|
|
3550
|
+
state.bailed = true;
|
|
3551
|
+
return;
|
|
3552
|
+
}
|
|
3553
|
+
if (node.slotId)
|
|
3554
|
+
state.elementPaths.set(node.slotId, path);
|
|
3555
|
+
const nextAncestors = groupIdx >= 0 ? new Set([...forceCloseAncestors, groupIdx]) : forceCloseAncestors;
|
|
3556
|
+
walkSkeletonPathChildren(flatChildren, path, safe, state, nextAncestors);
|
|
3557
|
+
}
|
|
3558
|
+
function hasForeignTableRowContent(children) {
|
|
3559
|
+
for (const child of children) {
|
|
3560
|
+
if (child.type === "text") {
|
|
3561
|
+
if (child.value.trim() !== "")
|
|
3562
|
+
return true;
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
if (child.type === "element" && child.tag !== "td" && child.tag !== "th") {
|
|
3566
|
+
return true;
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
return false;
|
|
3570
|
+
}
|
|
3571
|
+
function flattenSkeletonChildren(children) {
|
|
3572
|
+
const out = [];
|
|
3573
|
+
for (const child of children) {
|
|
3574
|
+
if (child.type === "fragment") {
|
|
3575
|
+
out.push(...flattenSkeletonChildren(child.children));
|
|
3576
|
+
} else {
|
|
3577
|
+
out.push(child);
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
return out;
|
|
3581
|
+
}
|
|
3582
|
+
function walkSkeletonPathChildren(children, parentPath, safe, state, forceCloseAncestors) {
|
|
3583
|
+
let idx = 0;
|
|
3584
|
+
let pendingText = false;
|
|
3585
|
+
for (const child of children) {
|
|
3586
|
+
if (state.bailed)
|
|
3587
|
+
return;
|
|
3588
|
+
switch (child.type) {
|
|
3589
|
+
case "text": {
|
|
3590
|
+
if (child.value === "")
|
|
3591
|
+
continue;
|
|
3592
|
+
if (!pendingText)
|
|
3593
|
+
idx += 1;
|
|
3594
|
+
pendingText = true;
|
|
3595
|
+
continue;
|
|
3596
|
+
}
|
|
3597
|
+
case "expression": {
|
|
3598
|
+
if (child.expr === "null" || child.expr === "undefined")
|
|
3599
|
+
continue;
|
|
3600
|
+
if (!child.slotId || !safe.reactiveTextSlotIds.has(child.slotId)) {
|
|
3601
|
+
state.bailed = true;
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
state.textMarkerPaths.set(child.slotId, [...parentPath, idx]);
|
|
3605
|
+
idx += 2;
|
|
3606
|
+
pendingText = false;
|
|
3607
|
+
continue;
|
|
3608
|
+
}
|
|
3609
|
+
case "element": {
|
|
3610
|
+
walkSkeletonPathNode(child, [...parentPath, idx], safe, state, forceCloseAncestors);
|
|
3611
|
+
idx += 1;
|
|
3612
|
+
pendingText = false;
|
|
3613
|
+
continue;
|
|
3614
|
+
}
|
|
3615
|
+
case "fragment":
|
|
3616
|
+
continue;
|
|
3617
|
+
default:
|
|
3618
|
+
state.bailed = true;
|
|
3619
|
+
return;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3441
3623
|
function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParams) {
|
|
3442
3624
|
const recurse = (n) => irToPlaceholderTemplate(n, restSpreadNames, loopDepth, loopParams);
|
|
3443
3625
|
const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
|
|
@@ -3458,7 +3640,7 @@ function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParam
|
|
|
3458
3640
|
return `<${node.tag}${attrs ? " " + attrs : ""} />`;
|
|
3459
3641
|
}
|
|
3460
3642
|
case "text":
|
|
3461
|
-
return node.value;
|
|
3643
|
+
return escapeHtml(node.value);
|
|
3462
3644
|
case "expression":
|
|
3463
3645
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3464
3646
|
return "";
|
|
@@ -3650,7 +3832,7 @@ function irToComponentTemplateWithOpts(node, opts) {
|
|
|
3650
3832
|
return templateAttrExpr(keyName, transformExpr(tmplStr));
|
|
3651
3833
|
}
|
|
3652
3834
|
case "literal":
|
|
3653
|
-
return `${keyName}="${v.value}"`;
|
|
3835
|
+
return `${keyName}="${escapeHtml(v.value)}"`;
|
|
3654
3836
|
default:
|
|
3655
3837
|
return "";
|
|
3656
3838
|
}
|
|
@@ -3660,7 +3842,7 @@ function irToComponentTemplateWithOpts(node, opts) {
|
|
|
3660
3842
|
case "boolean-attr":
|
|
3661
3843
|
return attrName;
|
|
3662
3844
|
case "literal":
|
|
3663
|
-
return `${attrName}="${v.value}"`;
|
|
3845
|
+
return `${attrName}="${escapeHtml(v.value)}"`;
|
|
3664
3846
|
case "expression":
|
|
3665
3847
|
return templateAttrExpr(attrName, transformExpr(v.expr, v.templateExpr), v.presenceOrUndefined);
|
|
3666
3848
|
case "template": {
|
|
@@ -3686,7 +3868,7 @@ function irToComponentTemplateWithOpts(node, opts) {
|
|
|
3686
3868
|
return `<${node.tag}${attrs ? " " + attrs : ""} />`;
|
|
3687
3869
|
}
|
|
3688
3870
|
case "text":
|
|
3689
|
-
return node.value;
|
|
3871
|
+
return escapeHtml(node.value);
|
|
3690
3872
|
case "expression":
|
|
3691
3873
|
if (node.expr === "null" || node.expr === "undefined")
|
|
3692
3874
|
return "";
|
|
@@ -3978,7 +4160,7 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
3978
4160
|
case "boolean-attr":
|
|
3979
4161
|
return attrName;
|
|
3980
4162
|
case "literal":
|
|
3981
|
-
return `${attrName}="${v.value}"`;
|
|
4163
|
+
return `${attrName}="${escapeHtml(v.value)}"`;
|
|
3982
4164
|
case "expression":
|
|
3983
4165
|
return templateAttrExpr(attrName, transformExpr(v.expr, v.templateExpr), v.presenceOrUndefined);
|
|
3984
4166
|
case "template": {
|
|
@@ -4004,7 +4186,7 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4004
4186
|
return `<${node.tag}${attrs ? " " + attrs : ""} />`;
|
|
4005
4187
|
}
|
|
4006
4188
|
case "text":
|
|
4007
|
-
return node.value;
|
|
4189
|
+
return escapeHtml(node.value);
|
|
4008
4190
|
case "expression":
|
|
4009
4191
|
if (node.expr === "null" || node.expr === "undefined")
|
|
4010
4192
|
return "";
|
|
@@ -4080,7 +4262,28 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4080
4262
|
return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg || (slotArg ? ", undefined" : "")}${slotArg})}`;
|
|
4081
4263
|
}
|
|
4082
4264
|
case "loop": {
|
|
4083
|
-
|
|
4265
|
+
const boundHere = new Set(opts.loopBoundNames ?? []);
|
|
4266
|
+
if (node.paramBindings && node.paramBindings.length > 0) {
|
|
4267
|
+
for (const b of node.paramBindings)
|
|
4268
|
+
boundHere.add(b.name);
|
|
4269
|
+
} else if (!node.param.startsWith("[") && !node.param.startsWith("{")) {
|
|
4270
|
+
boundHere.add(node.param);
|
|
4271
|
+
}
|
|
4272
|
+
if (node.index)
|
|
4273
|
+
boundHere.add(node.index);
|
|
4274
|
+
const childEnv = {
|
|
4275
|
+
...env,
|
|
4276
|
+
substitutions: new Map([...env.substitutions].filter(([name]) => !boundHere.has(name)))
|
|
4277
|
+
};
|
|
4278
|
+
const recurseInLoopBody = (n) => generateCsrTemplateWithOpts(n, {
|
|
4279
|
+
...opts,
|
|
4280
|
+
insideLoop: true,
|
|
4281
|
+
loopDepth: loopDepth + 1,
|
|
4282
|
+
inHoistedChildren: false,
|
|
4283
|
+
loopBoundNames: boundHere,
|
|
4284
|
+
csrEnv: childEnv
|
|
4285
|
+
});
|
|
4286
|
+
let childTemplate = node.children.map(recurseInLoopBody).join("");
|
|
4084
4287
|
if (node.bodyIsItemConditional && node.key) {
|
|
4085
4288
|
childTemplate = `${itemAnchorTemplate(node.key)}${childTemplate}`;
|
|
4086
4289
|
}
|
|
@@ -4094,7 +4297,7 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
4094
4297
|
if (node.flatMapCallback) {
|
|
4095
4298
|
let body = node.flatMapCallback.templateBody ?? node.flatMapCallback.body;
|
|
4096
4299
|
for (const frag of node.flatMapCallback.fragments) {
|
|
4097
|
-
const renderedIr =
|
|
4300
|
+
const renderedIr = recurseInLoopBody(frag.ir);
|
|
4098
4301
|
body = body.replace(frag.placeholder, `\`${renderedIr}\``);
|
|
4099
4302
|
}
|
|
4100
4303
|
body = applyPropsRewrite(body, propsObjectName ?? null);
|
|
@@ -5054,6 +5257,8 @@ function needsTypeBasedDetection(source) {
|
|
|
5054
5257
|
return true;
|
|
5055
5258
|
if (/\.map\s*\(/.test(source))
|
|
5056
5259
|
return true;
|
|
5260
|
+
if (source.includes("createSelector"))
|
|
5261
|
+
return true;
|
|
5057
5262
|
return false;
|
|
5058
5263
|
}
|
|
5059
5264
|
function findBrandPackageImportLoc(sourceFile, filePath) {
|
|
@@ -6030,6 +6235,7 @@ var CLIENT_EXPORTS = new Set([
|
|
|
6030
6235
|
"createEffect",
|
|
6031
6236
|
"createDisposableEffect",
|
|
6032
6237
|
"createMemo",
|
|
6238
|
+
"createSelector",
|
|
6033
6239
|
"createRoot",
|
|
6034
6240
|
"onCleanup",
|
|
6035
6241
|
"onMount",
|
|
@@ -8034,7 +8240,7 @@ function resolveFreeRefs(node, env) {
|
|
|
8034
8240
|
}
|
|
8035
8241
|
|
|
8036
8242
|
// src/jsx-to-ir.ts
|
|
8037
|
-
import { toHTMLAttrName } from "@barefootjs/shared";
|
|
8243
|
+
import { toHTMLAttrName, decodeEntities } from "@barefootjs/shared";
|
|
8038
8244
|
var CLIENT_DIRECTIVE_INTERIOR_RE2 = /^\s*@client\s*$/;
|
|
8039
8245
|
var BLOCK_COMMENT_RE2 = /\/\*([\s\S]*?)\*\//g;
|
|
8040
8246
|
function hasLeadingClientDirective(expr, sourceFile) {
|
|
@@ -8116,9 +8322,15 @@ function exprHasFunctionCalls(expr) {
|
|
|
8116
8322
|
return found;
|
|
8117
8323
|
}
|
|
8118
8324
|
function rewriteBarePropRefs2(text, expr, ctx) {
|
|
8119
|
-
|
|
8325
|
+
let propNames = getDestructuredPropNames(ctx);
|
|
8120
8326
|
if (!propNames)
|
|
8121
8327
|
return;
|
|
8328
|
+
if (ctx.loopParams.size > 0) {
|
|
8329
|
+
const filtered = new Set([...propNames].filter((n) => !ctx.loopParams.has(n)));
|
|
8330
|
+
if (filtered.size === 0)
|
|
8331
|
+
return;
|
|
8332
|
+
propNames = filtered;
|
|
8333
|
+
}
|
|
8122
8334
|
const extraPropRefs = collectBranchLocalPropRefsViaSubstitution(expr, ctx);
|
|
8123
8335
|
return rewriteBarePropRefs(text, expr, propNames, extraPropRefs);
|
|
8124
8336
|
}
|
|
@@ -8183,6 +8395,7 @@ function createTransformContext(analyzer) {
|
|
|
8183
8395
|
isRoot: true,
|
|
8184
8396
|
insideComponentChildren: false,
|
|
8185
8397
|
loopParams: new Set,
|
|
8398
|
+
loopDepth: 0,
|
|
8186
8399
|
patterns: {
|
|
8187
8400
|
signals: analyzer.signals.map((s) => ({
|
|
8188
8401
|
getter: s.getter,
|
|
@@ -8283,26 +8496,29 @@ function makeBindingEnv(ctx) {
|
|
|
8283
8496
|
function parseValueExpr(trimmed) {
|
|
8284
8497
|
return parseExpression(trimmed.startsWith("{") ? `(${trimmed})` : trimmed);
|
|
8285
8498
|
}
|
|
8286
|
-
|
|
8499
|
+
var EMPTY_BOUND = new Set;
|
|
8500
|
+
function attachParsedExpressions(node, analyzer, bound = EMPTY_BOUND) {
|
|
8501
|
+
const parse2 = (trimmed) => resolveCallbackMethodFunctionReferences(parseExpression(trimmed), analyzer, bound);
|
|
8502
|
+
const parseValue = (trimmed) => resolveCallbackMethodFunctionReferences(parseValueExpr(trimmed), analyzer, bound);
|
|
8287
8503
|
if (node.type === "expression") {
|
|
8288
8504
|
const trimmed = node.expr.trim();
|
|
8289
8505
|
if (trimmed)
|
|
8290
|
-
node.parsed =
|
|
8506
|
+
node.parsed = parse2(trimmed);
|
|
8291
8507
|
} else if (node.type === "conditional" || node.type === "if-statement") {
|
|
8292
8508
|
const trimmed = node.condition.trim();
|
|
8293
8509
|
if (trimmed)
|
|
8294
|
-
node.parsedCondition =
|
|
8510
|
+
node.parsedCondition = parse2(trimmed);
|
|
8295
8511
|
}
|
|
8296
8512
|
if (node.type === "element") {
|
|
8297
8513
|
for (const attr of node.attrs) {
|
|
8298
8514
|
if (attr.value.kind === "expression") {
|
|
8299
8515
|
const trimmed = attr.value.expr.trim();
|
|
8300
8516
|
if (trimmed)
|
|
8301
|
-
attr.value.parsed =
|
|
8517
|
+
attr.value.parsed = parseValue(trimmed);
|
|
8302
8518
|
} else if (attr.value.kind === "spread") {
|
|
8303
8519
|
const trimmed = attr.value.expr.trim();
|
|
8304
8520
|
if (trimmed)
|
|
8305
|
-
attr.value.parsed =
|
|
8521
|
+
attr.value.parsed = parse2(trimmed);
|
|
8306
8522
|
}
|
|
8307
8523
|
}
|
|
8308
8524
|
} else if (node.type === "component") {
|
|
@@ -8310,14 +8526,14 @@ function attachParsedExpressions(node) {
|
|
|
8310
8526
|
if (prop.value.kind === "expression") {
|
|
8311
8527
|
const trimmed = prop.value.expr.trim();
|
|
8312
8528
|
if (trimmed)
|
|
8313
|
-
prop.value.parsed =
|
|
8529
|
+
prop.value.parsed = parseValue(trimmed);
|
|
8314
8530
|
}
|
|
8315
8531
|
}
|
|
8316
8532
|
} else if (node.type === "provider") {
|
|
8317
8533
|
if (node.valueProp.value.kind === "expression") {
|
|
8318
8534
|
const trimmed = node.valueProp.value.expr.trim();
|
|
8319
8535
|
if (trimmed)
|
|
8320
|
-
node.valueProp.value.parsed =
|
|
8536
|
+
node.valueProp.value.parsed = parseValue(trimmed);
|
|
8321
8537
|
}
|
|
8322
8538
|
}
|
|
8323
8539
|
switch (node.type) {
|
|
@@ -8326,47 +8542,51 @@ function attachParsedExpressions(node) {
|
|
|
8326
8542
|
case "fragment":
|
|
8327
8543
|
case "provider":
|
|
8328
8544
|
for (const child of node.children)
|
|
8329
|
-
attachParsedExpressions(child);
|
|
8545
|
+
attachParsedExpressions(child, analyzer, bound);
|
|
8330
8546
|
break;
|
|
8331
8547
|
case "async":
|
|
8332
|
-
attachParsedExpressions(node.fallback);
|
|
8548
|
+
attachParsedExpressions(node.fallback, analyzer, bound);
|
|
8333
8549
|
for (const child of node.children)
|
|
8334
|
-
attachParsedExpressions(child);
|
|
8550
|
+
attachParsedExpressions(child, analyzer, bound);
|
|
8335
8551
|
break;
|
|
8336
8552
|
case "loop": {
|
|
8337
8553
|
const trimmedArray = node.array.trim();
|
|
8338
8554
|
if (trimmedArray)
|
|
8339
|
-
node.arrayParsed =
|
|
8555
|
+
node.arrayParsed = parse2(trimmedArray);
|
|
8556
|
+
const loopBound = new Set(bound);
|
|
8557
|
+
loopBound.add(node.param);
|
|
8558
|
+
if (node.index)
|
|
8559
|
+
loopBound.add(node.index);
|
|
8340
8560
|
for (const child of node.children)
|
|
8341
|
-
attachParsedExpressions(child);
|
|
8561
|
+
attachParsedExpressions(child, analyzer, loopBound);
|
|
8342
8562
|
if (node.childComponent) {
|
|
8343
8563
|
for (const child of node.childComponent.children)
|
|
8344
|
-
attachParsedExpressions(child);
|
|
8564
|
+
attachParsedExpressions(child, analyzer, loopBound);
|
|
8345
8565
|
}
|
|
8346
8566
|
for (const nested of node.nestedComponents ?? []) {
|
|
8347
8567
|
for (const child of nested.children)
|
|
8348
|
-
attachParsedExpressions(child);
|
|
8568
|
+
attachParsedExpressions(child, analyzer, loopBound);
|
|
8349
8569
|
}
|
|
8350
8570
|
for (const frag of node.flatMapCallback?.fragments ?? []) {
|
|
8351
|
-
attachParsedExpressions(frag.ir);
|
|
8571
|
+
attachParsedExpressions(frag.ir, analyzer, loopBound);
|
|
8352
8572
|
}
|
|
8353
8573
|
break;
|
|
8354
8574
|
}
|
|
8355
8575
|
case "conditional":
|
|
8356
|
-
attachParsedExpressions(node.whenTrue);
|
|
8357
|
-
attachParsedExpressions(node.whenFalse);
|
|
8576
|
+
attachParsedExpressions(node.whenTrue, analyzer, bound);
|
|
8577
|
+
attachParsedExpressions(node.whenFalse, analyzer, bound);
|
|
8358
8578
|
break;
|
|
8359
8579
|
case "if-statement":
|
|
8360
|
-
attachParsedExpressions(node.consequent);
|
|
8580
|
+
attachParsedExpressions(node.consequent, analyzer, bound);
|
|
8361
8581
|
if (node.alternate)
|
|
8362
|
-
attachParsedExpressions(node.alternate);
|
|
8582
|
+
attachParsedExpressions(node.alternate, analyzer, bound);
|
|
8363
8583
|
break;
|
|
8364
8584
|
}
|
|
8365
8585
|
}
|
|
8366
8586
|
function jsxToIR(analyzer) {
|
|
8367
8587
|
const root = buildIRRoot(analyzer);
|
|
8368
8588
|
if (root)
|
|
8369
|
-
attachParsedExpressions(root);
|
|
8589
|
+
attachParsedExpressions(root, analyzer);
|
|
8370
8590
|
return root;
|
|
8371
8591
|
}
|
|
8372
8592
|
function buildIRRoot(analyzer) {
|
|
@@ -8836,7 +9056,7 @@ function transformText(node, ctx) {
|
|
|
8836
9056
|
}
|
|
8837
9057
|
return {
|
|
8838
9058
|
type: "text",
|
|
8839
|
-
value: text,
|
|
9059
|
+
value: decodeEntities(text),
|
|
8840
9060
|
loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath)
|
|
8841
9061
|
};
|
|
8842
9062
|
}
|
|
@@ -9392,6 +9612,22 @@ function isIteratorShapeCall(node) {
|
|
|
9392
9612
|
return null;
|
|
9393
9613
|
return { array: node.expression.expression, shape: name };
|
|
9394
9614
|
}
|
|
9615
|
+
function isObjectIteratorCall(node) {
|
|
9616
|
+
if (!ts11.isCallExpression(node))
|
|
9617
|
+
return null;
|
|
9618
|
+
if (!ts11.isPropertyAccessExpression(node.expression))
|
|
9619
|
+
return null;
|
|
9620
|
+
if (!ts11.isIdentifier(node.expression.expression))
|
|
9621
|
+
return null;
|
|
9622
|
+
if (node.expression.expression.text !== "Object")
|
|
9623
|
+
return null;
|
|
9624
|
+
if (node.arguments.length !== 1)
|
|
9625
|
+
return null;
|
|
9626
|
+
const name = node.expression.name.text;
|
|
9627
|
+
if (name !== "entries" && name !== "keys" && name !== "values")
|
|
9628
|
+
return null;
|
|
9629
|
+
return { object: node.arguments[0], shape: name };
|
|
9630
|
+
}
|
|
9395
9631
|
function extractSortComparator(callback, _method, ctx) {
|
|
9396
9632
|
const outerRaw = ctx.getJS(callback);
|
|
9397
9633
|
const unsupported = () => ({
|
|
@@ -9437,8 +9673,23 @@ function extractSortComparator(callback, _method, ctx) {
|
|
|
9437
9673
|
};
|
|
9438
9674
|
}
|
|
9439
9675
|
function resolveSortComparatorIdentifier(name, ctx) {
|
|
9440
|
-
const constInfo = findLocalConst(name, ctx);
|
|
9441
|
-
const fnInfo = findLocalFunction(name, ctx);
|
|
9676
|
+
const constInfo = findLocalConst(name, ctx.analyzer);
|
|
9677
|
+
const fnInfo = findLocalFunction(name, ctx.analyzer);
|
|
9678
|
+
if (constInfo && fnInfo)
|
|
9679
|
+
return null;
|
|
9680
|
+
if (constInfo) {
|
|
9681
|
+
const ast = parseConstInitializer(constInfo);
|
|
9682
|
+
return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
|
|
9683
|
+
}
|
|
9684
|
+
if (fnInfo) {
|
|
9685
|
+
const ast = parseFunctionInfoAsExpr(fnInfo);
|
|
9686
|
+
return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
|
|
9687
|
+
}
|
|
9688
|
+
return null;
|
|
9689
|
+
}
|
|
9690
|
+
function resolveCallbackMethodFunctionReferenceIdentifier(name, analyzer) {
|
|
9691
|
+
const constInfo = findLocalConst(name, analyzer);
|
|
9692
|
+
const fnInfo = findLocalFunction(name, analyzer);
|
|
9442
9693
|
if (constInfo && fnInfo)
|
|
9443
9694
|
return null;
|
|
9444
9695
|
if (constInfo) {
|
|
@@ -9451,6 +9702,57 @@ function resolveSortComparatorIdentifier(name, ctx) {
|
|
|
9451
9702
|
}
|
|
9452
9703
|
return null;
|
|
9453
9704
|
}
|
|
9705
|
+
function resolveCallbackMethodFunctionReferences(expr, analyzer, bound = EMPTY_BOUND) {
|
|
9706
|
+
function visit2(e, bound2) {
|
|
9707
|
+
switch (e.kind) {
|
|
9708
|
+
case "literal":
|
|
9709
|
+
case "identifier":
|
|
9710
|
+
case "regex":
|
|
9711
|
+
case "unsupported":
|
|
9712
|
+
return e;
|
|
9713
|
+
case "call": {
|
|
9714
|
+
const callee = visit2(e.callee, bound2);
|
|
9715
|
+
const args = e.args.map((a) => visit2(a, bound2));
|
|
9716
|
+
if (callee.kind === "member" && !callee.computed && CALLBACK_METHODS.has(callee.property) && args[0]?.kind === "identifier" && !bound2.has(args[0].name)) {
|
|
9717
|
+
const resolved = resolveCallbackMethodFunctionReferenceIdentifier(args[0].name, analyzer);
|
|
9718
|
+
const arrow = resolved ? tsNodeToParsedExpr(resolved) : null;
|
|
9719
|
+
if (arrow && arrow.kind === "arrow")
|
|
9720
|
+
args[0] = arrow;
|
|
9721
|
+
}
|
|
9722
|
+
return { ...e, callee, args };
|
|
9723
|
+
}
|
|
9724
|
+
case "member":
|
|
9725
|
+
return { ...e, object: visit2(e.object, bound2) };
|
|
9726
|
+
case "index-access":
|
|
9727
|
+
return { ...e, object: visit2(e.object, bound2), index: visit2(e.index, bound2) };
|
|
9728
|
+
case "binary":
|
|
9729
|
+
case "logical":
|
|
9730
|
+
return { ...e, left: visit2(e.left, bound2), right: visit2(e.right, bound2) };
|
|
9731
|
+
case "unary":
|
|
9732
|
+
return { ...e, argument: visit2(e.argument, bound2) };
|
|
9733
|
+
case "conditional":
|
|
9734
|
+
return { ...e, test: visit2(e.test, bound2), consequent: visit2(e.consequent, bound2), alternate: visit2(e.alternate, bound2) };
|
|
9735
|
+
case "template-literal":
|
|
9736
|
+
return { ...e, parts: e.parts.map((p) => p.type === "expression" ? { ...p, expr: visit2(p.expr, bound2) } : p) };
|
|
9737
|
+
case "array-literal":
|
|
9738
|
+
return { ...e, elements: e.elements.map((el) => visit2(el, bound2)) };
|
|
9739
|
+
case "array-method":
|
|
9740
|
+
return {
|
|
9741
|
+
...e,
|
|
9742
|
+
object: visit2(e.object, bound2),
|
|
9743
|
+
args: e.args.map((a) => visit2(a, bound2)),
|
|
9744
|
+
...e.method === "flat" && e.depthExpr ? { depthExpr: visit2(e.depthExpr, bound2) } : {}
|
|
9745
|
+
};
|
|
9746
|
+
case "object-literal":
|
|
9747
|
+
return { ...e, properties: e.properties.map((p) => ({ ...p, value: visit2(p.value, bound2) })) };
|
|
9748
|
+
case "arrow": {
|
|
9749
|
+
const inner = e.params.length === 0 ? bound2 : new Set([...bound2, ...e.params]);
|
|
9750
|
+
return { ...e, body: visit2(e.body, inner) };
|
|
9751
|
+
}
|
|
9752
|
+
}
|
|
9753
|
+
}
|
|
9754
|
+
return visit2(expr, bound);
|
|
9755
|
+
}
|
|
9454
9756
|
function extractFilterPredicate(callback, ctx) {
|
|
9455
9757
|
if (!ts11.isArrowFunction(callback))
|
|
9456
9758
|
return { result: null };
|
|
@@ -9810,6 +10112,7 @@ function extractItemConditionalKey(cond) {
|
|
|
9810
10112
|
}
|
|
9811
10113
|
function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
9812
10114
|
const isNested = ctx.loopParams.size > 0;
|
|
10115
|
+
const depth = ctx.loopDepth;
|
|
9813
10116
|
const propAccess = node.expression;
|
|
9814
10117
|
const mapSource = propAccess.expression;
|
|
9815
10118
|
let array = "";
|
|
@@ -9822,6 +10125,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
9822
10125
|
let templateMapPreamble;
|
|
9823
10126
|
let typedMapPreamble;
|
|
9824
10127
|
let iterationShape;
|
|
10128
|
+
let objectIteration;
|
|
9825
10129
|
const setArray = (node2) => {
|
|
9826
10130
|
array = ctx.getJS(node2);
|
|
9827
10131
|
templateArray = rewriteBarePropRefs2(array, node2, ctx);
|
|
@@ -9836,6 +10140,12 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
9836
10140
|
} else if (iteratorInfo.shape === "keys") {
|
|
9837
10141
|
iterationShape = "keys";
|
|
9838
10142
|
}
|
|
10143
|
+
} else {
|
|
10144
|
+
const objectIteratorInfo = isObjectIteratorCall(mapSource);
|
|
10145
|
+
if (objectIteratorInfo) {
|
|
10146
|
+
chainSource = objectIteratorInfo.object;
|
|
10147
|
+
objectIteration = objectIteratorInfo.shape;
|
|
10148
|
+
}
|
|
9839
10149
|
}
|
|
9840
10150
|
const filterInfo = isFilterCall(chainSource);
|
|
9841
10151
|
const sortInfo = isSortCall(chainSource);
|
|
@@ -9910,13 +10220,11 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
9910
10220
|
setArray(innerSort.array);
|
|
9911
10221
|
}
|
|
9912
10222
|
} else {
|
|
9913
|
-
|
|
9914
|
-
arrayExpr = filterInfo.array;
|
|
10223
|
+
setArray(filterInfo.array);
|
|
9915
10224
|
}
|
|
9916
10225
|
}
|
|
9917
10226
|
} else {
|
|
9918
|
-
|
|
9919
|
-
arrayExpr = chainSource;
|
|
10227
|
+
setArray(chainSource);
|
|
9920
10228
|
}
|
|
9921
10229
|
const callback = node.arguments[0];
|
|
9922
10230
|
let param = "item";
|
|
@@ -9933,7 +10241,8 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
9933
10241
|
if (firstParam.type) {
|
|
9934
10242
|
paramType = firstParam.type.getText(ctx.sourceFile);
|
|
9935
10243
|
}
|
|
9936
|
-
|
|
10244
|
+
const isEntriesShape = iterationShape === "entries" || objectIteration === "entries";
|
|
10245
|
+
if (isEntriesShape && ts11.isArrayBindingPattern(firstParam.name)) {
|
|
9937
10246
|
const elements = firstParam.name.elements.filter((el) => !ts11.isOmittedExpression(el));
|
|
9938
10247
|
if (elements.length === 2 && ts11.isBindingElement(elements[0]) && ts11.isIdentifier(elements[0].name) && ts11.isBindingElement(elements[1]) && ts11.isIdentifier(elements[1].name)) {
|
|
9939
10248
|
index = elements[0].name.text;
|
|
@@ -9955,7 +10264,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
9955
10264
|
}
|
|
9956
10265
|
}
|
|
9957
10266
|
}
|
|
9958
|
-
if (callback.parameters.length > 1 && iterationShape !== "entries") {
|
|
10267
|
+
if (callback.parameters.length > 1 && iterationShape !== "entries" && objectIteration !== "entries") {
|
|
9959
10268
|
const secondParam = callback.parameters[1];
|
|
9960
10269
|
index = secondParam.name.getText(ctx.sourceFile);
|
|
9961
10270
|
if (secondParam.type) {
|
|
@@ -9970,6 +10279,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
9970
10279
|
}
|
|
9971
10280
|
if (index)
|
|
9972
10281
|
ctx.loopParams.add(index);
|
|
10282
|
+
ctx.loopDepth++;
|
|
9973
10283
|
const tryTransformRenderableBody = (expr) => {
|
|
9974
10284
|
if (!ts11.isBinaryExpression(expr))
|
|
9975
10285
|
return;
|
|
@@ -10066,6 +10376,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
10066
10376
|
}
|
|
10067
10377
|
if (index)
|
|
10068
10378
|
ctx.loopParams.delete(index);
|
|
10379
|
+
ctx.loopDepth--;
|
|
10069
10380
|
}
|
|
10070
10381
|
if (children.length === 0 && !flatMapCallback) {
|
|
10071
10382
|
return null;
|
|
@@ -10094,7 +10405,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
10094
10405
|
const callsReactive = exprCallsReactiveGetters(arrayExpr, ctx);
|
|
10095
10406
|
const hasCalls = exprHasFunctionCalls(arrayExpr);
|
|
10096
10407
|
const isDirectPropArray = method !== "flatMap" && isArrayExprDirectPropRef(arrayExpr, ctx);
|
|
10097
|
-
const isStaticArray = !isSignalOrMemoArray(array, ctx) && !isDirectPropArray && !hasCalls;
|
|
10408
|
+
const isStaticArray = !isSignalOrMemoArray(array, ctx) && !isDirectPropArray && !hasCalls && !objectIteration;
|
|
10098
10409
|
const nestedComponents = collectNestedComponents(children).filter((c) => c.name !== childComponent?.name);
|
|
10099
10410
|
return {
|
|
10100
10411
|
type: "loop",
|
|
@@ -10121,6 +10432,8 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
|
|
|
10121
10432
|
sortComparator,
|
|
10122
10433
|
chainOrder,
|
|
10123
10434
|
iterationShape,
|
|
10435
|
+
objectIteration,
|
|
10436
|
+
depth,
|
|
10124
10437
|
clientOnly: isClientOnly || undefined,
|
|
10125
10438
|
mapPreamble,
|
|
10126
10439
|
templateMapPreamble,
|
|
@@ -10363,7 +10676,7 @@ function getAttributeValue(attr, ctx) {
|
|
|
10363
10676
|
return AttrValueOf.booleanAttr();
|
|
10364
10677
|
}
|
|
10365
10678
|
if (ts11.isStringLiteral(attr.initializer)) {
|
|
10366
|
-
return AttrValueOf.literal(attr.initializer.text);
|
|
10679
|
+
return AttrValueOf.literal(decodeEntities(attr.initializer.text));
|
|
10367
10680
|
}
|
|
10368
10681
|
if (ts11.isJsxExpression(attr.initializer) && attr.initializer.expression) {
|
|
10369
10682
|
let expr = attr.initializer.expression;
|
|
@@ -10461,7 +10774,7 @@ function parseTemplateLiteral(expr, ctx) {
|
|
|
10461
10774
|
}
|
|
10462
10775
|
function tryResolveTemplateSpanFromConst(expr, ctx) {
|
|
10463
10776
|
if (ts11.isIdentifier(expr)) {
|
|
10464
|
-
const constInfo = findLocalConst(expr.text, ctx);
|
|
10777
|
+
const constInfo = findLocalConst(expr.text, ctx.analyzer);
|
|
10465
10778
|
if (!constInfo)
|
|
10466
10779
|
return null;
|
|
10467
10780
|
const ast = parseConstInitializer(constInfo);
|
|
@@ -10475,7 +10788,7 @@ function tryResolveTemplateSpanFromConst(expr, ctx) {
|
|
|
10475
10788
|
if (ts11.isElementAccessExpression(expr)) {
|
|
10476
10789
|
if (!ts11.isIdentifier(expr.expression))
|
|
10477
10790
|
return null;
|
|
10478
|
-
const constInfo = findLocalConst(expr.expression.text, ctx);
|
|
10791
|
+
const constInfo = findLocalConst(expr.expression.text, ctx.analyzer);
|
|
10479
10792
|
if (!constInfo)
|
|
10480
10793
|
return null;
|
|
10481
10794
|
const ast = parseConstInitializer(constInfo);
|
|
@@ -10501,16 +10814,16 @@ function tryResolveTemplateSpanFromConst(expr, ctx) {
|
|
|
10501
10814
|
}
|
|
10502
10815
|
return null;
|
|
10503
10816
|
}
|
|
10504
|
-
function findLocalConst(name,
|
|
10505
|
-
const matches =
|
|
10817
|
+
function findLocalConst(name, analyzer) {
|
|
10818
|
+
const matches = analyzer.localConstants.filter((c) => c.name === name);
|
|
10506
10819
|
if (matches.length === 0)
|
|
10507
10820
|
return;
|
|
10508
10821
|
const fnScoped = matches.filter((c) => !c.isModule);
|
|
10509
10822
|
const pool = fnScoped.length > 0 ? fnScoped : matches;
|
|
10510
10823
|
return pool[pool.length - 1];
|
|
10511
10824
|
}
|
|
10512
|
-
function findLocalFunction(name,
|
|
10513
|
-
const matches =
|
|
10825
|
+
function findLocalFunction(name, analyzer) {
|
|
10826
|
+
const matches = analyzer.localFunctions.filter((f) => f.name === name);
|
|
10514
10827
|
if (matches.length === 0)
|
|
10515
10828
|
return;
|
|
10516
10829
|
const fnScoped = matches.filter((f) => !f.isModule);
|
|
@@ -10552,7 +10865,9 @@ function hasDynamicTagBinding(name, sourceFile) {
|
|
|
10552
10865
|
return found;
|
|
10553
10866
|
}
|
|
10554
10867
|
function tryResolveIdentifierAsTemplateLiteral(ident, ctx) {
|
|
10555
|
-
|
|
10868
|
+
if (ctx.loopParams.has(ident.text))
|
|
10869
|
+
return null;
|
|
10870
|
+
const constInfo = findLocalConst(ident.text, ctx.analyzer);
|
|
10556
10871
|
if (!constInfo)
|
|
10557
10872
|
return null;
|
|
10558
10873
|
const ast = parseConstInitializer(constInfo);
|
|
@@ -10649,8 +10964,8 @@ function tryDesugarInterleaveTaggedTemplate(expr, ctx) {
|
|
|
10649
10964
|
return rewritten ?? expr;
|
|
10650
10965
|
}
|
|
10651
10966
|
function resolveInterleaveTagIdentifier(name, ctx) {
|
|
10652
|
-
const constInfo = findLocalConst(name, ctx);
|
|
10653
|
-
const fnInfo = findLocalFunction(name, ctx);
|
|
10967
|
+
const constInfo = findLocalConst(name, ctx.analyzer);
|
|
10968
|
+
const fnInfo = findLocalFunction(name, ctx.analyzer);
|
|
10654
10969
|
if (constInfo && fnInfo)
|
|
10655
10970
|
return null;
|
|
10656
10971
|
if (constInfo) {
|
|
@@ -11404,6 +11719,7 @@ function collectLoopChildEventsWithNesting(node, initialNestingStack = []) {
|
|
|
11404
11719
|
depth: scope.nestingStack.length + 1,
|
|
11405
11720
|
array: l.array,
|
|
11406
11721
|
param: l.param,
|
|
11722
|
+
index: l.index,
|
|
11407
11723
|
key: l.key,
|
|
11408
11724
|
markerId: l.markerId,
|
|
11409
11725
|
containerSlotId: scope.lastElementSlotId,
|
|
@@ -11494,64 +11810,215 @@ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings)
|
|
|
11494
11810
|
return attrs;
|
|
11495
11811
|
}
|
|
11496
11812
|
|
|
11497
|
-
// src/ir-to-client-js/
|
|
11498
|
-
var
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11813
|
+
// src/ir-to-client-js/control-flow/stringify/template-parse.ts
|
|
11814
|
+
var SVG_ROOT_TAGS = new Set([
|
|
11815
|
+
"svg",
|
|
11816
|
+
"path",
|
|
11817
|
+
"circle",
|
|
11818
|
+
"rect",
|
|
11819
|
+
"line",
|
|
11820
|
+
"polyline",
|
|
11821
|
+
"polygon",
|
|
11822
|
+
"ellipse",
|
|
11823
|
+
"text",
|
|
11824
|
+
"tspan",
|
|
11825
|
+
"textPath",
|
|
11826
|
+
"g",
|
|
11827
|
+
"defs",
|
|
11828
|
+
"use",
|
|
11829
|
+
"symbol",
|
|
11830
|
+
"switch",
|
|
11831
|
+
"clipPath",
|
|
11832
|
+
"mask",
|
|
11833
|
+
"marker",
|
|
11834
|
+
"pattern",
|
|
11835
|
+
"linearGradient",
|
|
11836
|
+
"radialGradient",
|
|
11837
|
+
"stop",
|
|
11838
|
+
"image",
|
|
11839
|
+
"foreignObject",
|
|
11840
|
+
"filter",
|
|
11841
|
+
"feBlend",
|
|
11842
|
+
"feColorMatrix",
|
|
11843
|
+
"feComposite",
|
|
11844
|
+
"feFlood",
|
|
11845
|
+
"feGaussianBlur",
|
|
11846
|
+
"feMerge",
|
|
11847
|
+
"feMergeNode",
|
|
11848
|
+
"feMorphology",
|
|
11849
|
+
"feOffset",
|
|
11850
|
+
"feTurbulence",
|
|
11851
|
+
"animate",
|
|
11852
|
+
"animateTransform",
|
|
11853
|
+
"animateMotion"
|
|
11854
|
+
]);
|
|
11855
|
+
function templateRootIsSvg(template) {
|
|
11856
|
+
const stripped = stripLeadingNonContent(template);
|
|
11857
|
+
const m = stripped.match(/^<\s*([A-Za-z][A-Za-z0-9-]*)/);
|
|
11858
|
+
if (m) {
|
|
11859
|
+
const tag = m[1];
|
|
11860
|
+
if (SVG_ROOT_TAGS.has(tag))
|
|
11861
|
+
return true;
|
|
11862
|
+
return SVG_ROOT_TAGS.has(tag.toLowerCase());
|
|
11545
11863
|
}
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
return
|
|
11864
|
+
const branches = extractConditionalBranchTemplates(stripped);
|
|
11865
|
+
if (branches === null || branches.length === 0)
|
|
11866
|
+
return false;
|
|
11867
|
+
return branches.every(templateRootIsSvg);
|
|
11550
11868
|
}
|
|
11551
|
-
function
|
|
11552
|
-
|
|
11869
|
+
function multiRootTemplateNeedsSvgWrap(template) {
|
|
11870
|
+
const m = stripLeadingNonContent(template).match(/^<\s*([A-Za-z][A-Za-z0-9-]*)/);
|
|
11871
|
+
if (m && m[1].toLowerCase() === "svg")
|
|
11872
|
+
return false;
|
|
11873
|
+
return templateRootIsSvg(template);
|
|
11553
11874
|
}
|
|
11554
|
-
function
|
|
11875
|
+
function stripLeadingNonContent(template) {
|
|
11876
|
+
let s = template.trimStart();
|
|
11877
|
+
while (s.startsWith("<!--")) {
|
|
11878
|
+
const end = s.indexOf("-->");
|
|
11879
|
+
if (end < 0)
|
|
11880
|
+
return s;
|
|
11881
|
+
s = s.slice(end + 3).trimStart();
|
|
11882
|
+
}
|
|
11883
|
+
return s;
|
|
11884
|
+
}
|
|
11885
|
+
function extractConditionalBranchTemplates(template) {
|
|
11886
|
+
if (!template.startsWith("${"))
|
|
11887
|
+
return null;
|
|
11888
|
+
const exprEnd = findInterpolationEnd(template, 2);
|
|
11889
|
+
if (exprEnd < 0)
|
|
11890
|
+
return null;
|
|
11891
|
+
const trailing = stripLeadingNonContent(template.slice(exprEnd + 1));
|
|
11892
|
+
if (trailing.length > 0)
|
|
11893
|
+
return null;
|
|
11894
|
+
const expr = template.slice(2, exprEnd);
|
|
11895
|
+
return findTopLevelTemplateLiterals(expr);
|
|
11896
|
+
}
|
|
11897
|
+
function emitTemplateCloneInline(template) {
|
|
11898
|
+
if (templateRootIsSvg(template)) {
|
|
11899
|
+
return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`<svg>${template}</svg>\`; return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`;
|
|
11900
|
+
}
|
|
11901
|
+
return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`${template}\`; return __tpl.content.firstElementChild.cloneNode(true)`;
|
|
11902
|
+
}
|
|
11903
|
+
function emitHoistedTemplateDecl(lines, indent, tplVar, skeletonTemplate) {
|
|
11904
|
+
const isSvg = templateRootIsSvg(skeletonTemplate);
|
|
11905
|
+
const html = isSvg ? `<svg>${skeletonTemplate}</svg>` : skeletonTemplate;
|
|
11906
|
+
lines.push(`${indent}const ${tplVar} = document.createElement('template')`);
|
|
11907
|
+
lines.push(`${indent}${tplVar}.innerHTML = \`${html}\``);
|
|
11908
|
+
}
|
|
11909
|
+
function hoistedCloneExpr(tplVar, skeletonTemplate) {
|
|
11910
|
+
return templateRootIsSvg(skeletonTemplate) ? `${tplVar}.content.firstElementChild.firstElementChild.cloneNode(true)` : `${tplVar}.content.firstElementChild.cloneNode(true)`;
|
|
11911
|
+
}
|
|
11912
|
+
function emitTemplateCloneLines(template, indent) {
|
|
11913
|
+
if (templateRootIsSvg(template)) {
|
|
11914
|
+
return [
|
|
11915
|
+
`${indent}const __tpl = document.createElement('template')`,
|
|
11916
|
+
`${indent}__tpl.innerHTML = \`<svg>${template}</svg>\``,
|
|
11917
|
+
`${indent}return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`
|
|
11918
|
+
];
|
|
11919
|
+
}
|
|
11920
|
+
return [
|
|
11921
|
+
`${indent}const __tpl = document.createElement('template')`,
|
|
11922
|
+
`${indent}__tpl.innerHTML = \`${template}\``,
|
|
11923
|
+
`${indent}return __tpl.content.firstElementChild.cloneNode(true)`
|
|
11924
|
+
];
|
|
11925
|
+
}
|
|
11926
|
+
function emitLoopItemElementSetup(lines, opts) {
|
|
11927
|
+
const { template, bodyIsMultiRoot, indent, singleRootLayout } = opts;
|
|
11928
|
+
const innerIndent = indent + " ";
|
|
11929
|
+
if (bodyIsMultiRoot) {
|
|
11930
|
+
lines.push(`${indent}let __el, __extras`);
|
|
11931
|
+
lines.push(`${indent}if (__existing) {`);
|
|
11932
|
+
lines.push(`${innerIndent}__el = __existing`);
|
|
11933
|
+
lines.push(`${indent}} else {`);
|
|
11934
|
+
for (const ln of emitMultiRootTemplateCloneLines(template, innerIndent, "__el", "__extras")) {
|
|
11935
|
+
lines.push(ln);
|
|
11936
|
+
}
|
|
11937
|
+
lines.push(`${innerIndent}__el.__bfExtras = __extras`);
|
|
11938
|
+
lines.push(`${indent}}`);
|
|
11939
|
+
return;
|
|
11940
|
+
}
|
|
11941
|
+
if (singleRootLayout === "inline") {
|
|
11942
|
+
const cloneExpr = emitTemplateCloneInline(template);
|
|
11943
|
+
lines.push(`${indent}const __el = __existing ?? (() => { ${cloneExpr} })()`);
|
|
11944
|
+
return;
|
|
11945
|
+
}
|
|
11946
|
+
lines.push(`${indent}const __el = __existing ?? (() => {`);
|
|
11947
|
+
for (const ln of emitTemplateCloneLines(template, innerIndent))
|
|
11948
|
+
lines.push(ln);
|
|
11949
|
+
lines.push(`${indent}})()`);
|
|
11950
|
+
}
|
|
11951
|
+
function emitMultiRootTemplateCloneLines(template, indent, varEl, varExtras) {
|
|
11952
|
+
const isSvg = multiRootTemplateNeedsSvgWrap(template);
|
|
11953
|
+
const innerHtmlExpr = isSvg ? `\`<svg>${template}</svg>\`` : `\`${template}\``;
|
|
11954
|
+
const parentExpr = isSvg ? `__tpl.content.firstElementChild` : `__tpl.content`;
|
|
11955
|
+
return [
|
|
11956
|
+
`${indent}const __tpl = document.createElement('template')`,
|
|
11957
|
+
`${indent}__tpl.innerHTML = ${innerHtmlExpr}`,
|
|
11958
|
+
`${indent}${varEl} = ${parentExpr}.firstElementChild.cloneNode(true)`,
|
|
11959
|
+
`${indent}${varExtras} = []`,
|
|
11960
|
+
`${indent}{ let __sib = ${parentExpr}.firstElementChild.nextElementSibling; while (__sib) { ${varExtras}.push(__sib.cloneNode(true)); __sib = __sib.nextElementSibling } }`
|
|
11961
|
+
];
|
|
11962
|
+
}
|
|
11963
|
+
|
|
11964
|
+
// src/ir-to-client-js/collect-elements.ts
|
|
11965
|
+
var EMPTY_RENDER_EXPRS = new Set(["null", "undefined", "false", "''", '""', "``"]);
|
|
11966
|
+
function domElementCount(node) {
|
|
11967
|
+
switch (node.type) {
|
|
11968
|
+
case "element":
|
|
11969
|
+
case "component":
|
|
11970
|
+
case "provider":
|
|
11971
|
+
case "async":
|
|
11972
|
+
return 1;
|
|
11973
|
+
case "text":
|
|
11974
|
+
return 0;
|
|
11975
|
+
case "expression":
|
|
11976
|
+
return EMPTY_RENDER_EXPRS.has(node.expr.trim()) ? 0 : null;
|
|
11977
|
+
case "loop":
|
|
11978
|
+
if (node.bodyIsItemConditional || node.method === "flatMap")
|
|
11979
|
+
return null;
|
|
11980
|
+
return `(${buildLoopChainExpr({
|
|
11981
|
+
base: node.array,
|
|
11982
|
+
sortComparator: node.sortComparator,
|
|
11983
|
+
filterPredicate: node.filterPredicate,
|
|
11984
|
+
chainOrder: node.chainOrder
|
|
11985
|
+
})}).length`;
|
|
11986
|
+
case "conditional": {
|
|
11987
|
+
const t = domElementCount(node.whenTrue);
|
|
11988
|
+
const f = domElementCount(node.whenFalse);
|
|
11989
|
+
if (t === null || f === null)
|
|
11990
|
+
return null;
|
|
11991
|
+
if (typeof t === "number" && typeof f === "number" && t === f)
|
|
11992
|
+
return t;
|
|
11993
|
+
return `(${node.condition} ? ${t} : ${f})`;
|
|
11994
|
+
}
|
|
11995
|
+
case "fragment":
|
|
11996
|
+
return sumElementCounts(node.children);
|
|
11997
|
+
default:
|
|
11998
|
+
return null;
|
|
11999
|
+
}
|
|
12000
|
+
}
|
|
12001
|
+
function sumElementCounts(nodes) {
|
|
12002
|
+
let staticCount = 0;
|
|
12003
|
+
const dynamic = [];
|
|
12004
|
+
for (const n of nodes) {
|
|
12005
|
+
const c = domElementCount(n);
|
|
12006
|
+
if (c === null)
|
|
12007
|
+
return null;
|
|
12008
|
+
if (typeof c === "number")
|
|
12009
|
+
staticCount += c;
|
|
12010
|
+
else
|
|
12011
|
+
dynamic.push(c);
|
|
12012
|
+
}
|
|
12013
|
+
if (dynamic.length === 0)
|
|
12014
|
+
return staticCount;
|
|
12015
|
+
const parts = staticCount > 0 ? [String(staticCount), ...dynamic] : dynamic;
|
|
12016
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" + ")})`;
|
|
12017
|
+
}
|
|
12018
|
+
function legacyElementCount(node) {
|
|
12019
|
+
return node.type === "element" || node.type === "component" || node.type === "provider" || node.type === "async" || node.type === "text" || node.type === "expression" && !node.reactive || node.type === "conditional" ? 1 : 0;
|
|
12020
|
+
}
|
|
12021
|
+
function computeLoopSiblingOffsets(root) {
|
|
11555
12022
|
const offsets = new Map;
|
|
11556
12023
|
const recordRun = (children, preceding) => {
|
|
11557
12024
|
for (const child of children) {
|
|
@@ -11672,11 +12139,13 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
|
|
|
11672
12139
|
arrayFreeIdentifiers: n.arrayFreeIdentifiers,
|
|
11673
12140
|
param: n.param,
|
|
11674
12141
|
paramBindings: n.paramBindings,
|
|
12142
|
+
index: n.index,
|
|
11675
12143
|
key: n.key,
|
|
11676
12144
|
markerId: n.markerId,
|
|
11677
12145
|
bodyIsMultiRoot: n.bodyIsMultiRoot,
|
|
11678
12146
|
bodyIsItemConditional: n.bodyIsItemConditional,
|
|
11679
12147
|
iterationShape: n.iterationShape,
|
|
12148
|
+
objectIteration: n.objectIteration,
|
|
11680
12149
|
containerSlotId: scope.parentSlotId,
|
|
11681
12150
|
template,
|
|
11682
12151
|
mapPreamble: n.mapPreamble,
|
|
@@ -11868,6 +12337,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
11868
12337
|
let template = "";
|
|
11869
12338
|
let staticItemTemplate;
|
|
11870
12339
|
let skeletonTemplate;
|
|
12340
|
+
let skeletonPaths;
|
|
11871
12341
|
if (l.childComponent) {
|
|
11872
12342
|
template = "";
|
|
11873
12343
|
if (l.isStaticArray && l.children[0]) {
|
|
@@ -11879,10 +12349,14 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
11879
12349
|
if (l.isStaticArray) {
|
|
11880
12350
|
staticItemTemplate = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0);
|
|
11881
12351
|
} else if (!useElementReconciliation && !l.bodyIsMultiRoot && !l.bodyIsItemConditional) {
|
|
11882
|
-
|
|
12352
|
+
const skeletonSafeSlots = {
|
|
11883
12353
|
reactiveAttrKeys: new Set(bindings.reactiveAttrs.map((a) => `${a.childSlotId}::${a.attrName}`)),
|
|
11884
12354
|
reactiveTextSlotIds: new Set(bindings.reactiveTexts.map((t) => t.slotId))
|
|
11885
|
-
}
|
|
12355
|
+
};
|
|
12356
|
+
skeletonTemplate = buildLoopSkeletonTemplate(l.children[0], skeletonSafeSlots) ?? undefined;
|
|
12357
|
+
if (skeletonTemplate && !templateRootIsSvg(skeletonTemplate)) {
|
|
12358
|
+
skeletonPaths = computeSkeletonSlotPaths(l.children[0], skeletonSafeSlots) ?? undefined;
|
|
12359
|
+
}
|
|
11886
12360
|
}
|
|
11887
12361
|
}
|
|
11888
12362
|
ctx.loopElements.push({
|
|
@@ -11898,9 +12372,11 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
|
|
|
11898
12372
|
bodyIsMultiRoot: l.bodyIsMultiRoot,
|
|
11899
12373
|
bodyIsItemConditional: l.bodyIsItemConditional,
|
|
11900
12374
|
iterationShape: l.iterationShape,
|
|
12375
|
+
objectIteration: l.objectIteration,
|
|
11901
12376
|
template,
|
|
11902
12377
|
staticItemTemplate,
|
|
11903
12378
|
skeletonTemplate,
|
|
12379
|
+
skeletonPaths,
|
|
11904
12380
|
childEventHandlers: childHandlers,
|
|
11905
12381
|
bindings,
|
|
11906
12382
|
childComponent: l.childComponent,
|
|
@@ -12097,6 +12573,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
|
|
|
12097
12573
|
bodyIsMultiRoot: n.bodyIsMultiRoot,
|
|
12098
12574
|
bodyIsItemConditional: n.bodyIsItemConditional,
|
|
12099
12575
|
iterationShape: n.iterationShape,
|
|
12576
|
+
objectIteration: n.objectIteration,
|
|
12100
12577
|
template: childTemplate,
|
|
12101
12578
|
containerSlotId: containerSlot,
|
|
12102
12579
|
mapPreamble: n.mapPreamble ?? null,
|
|
@@ -12690,6 +13167,7 @@ var RUNTIME_IMPORT_CANDIDATES = [
|
|
|
12690
13167
|
"__slot",
|
|
12691
13168
|
"__bfSlot",
|
|
12692
13169
|
"__bfText",
|
|
13170
|
+
"tAfter",
|
|
12693
13171
|
"beginTurn",
|
|
12694
13172
|
"endTurn"
|
|
12695
13173
|
];
|
|
@@ -14560,9 +15038,63 @@ import { BF_SCOPE as BF_SCOPE2, BF_HOST, BF_AT } from "@barefootjs/shared";
|
|
|
14560
15038
|
function loopKeyFn(loop) {
|
|
14561
15039
|
if (loop.key === null)
|
|
14562
15040
|
return "null";
|
|
14563
|
-
const params =
|
|
15041
|
+
const params = `${loop.param}${loop.index ? `, ${loop.index}` : ""}`;
|
|
14564
15042
|
return `(${params}) => String(${loop.key})`;
|
|
14565
15043
|
}
|
|
15044
|
+
function nestedLoopReferencesIndex(inner, comps, events) {
|
|
15045
|
+
const index = inner.index;
|
|
15046
|
+
if (!index)
|
|
15047
|
+
return false;
|
|
15048
|
+
const exprRefs = (text, precomputed) => {
|
|
15049
|
+
if (precomputed)
|
|
15050
|
+
return precomputed.has(index);
|
|
15051
|
+
if (!text)
|
|
15052
|
+
return false;
|
|
15053
|
+
return extractFreeIdentifiersFromText(text).has(index);
|
|
15054
|
+
};
|
|
15055
|
+
if (exprRefs(inner.key))
|
|
15056
|
+
return true;
|
|
15057
|
+
for (const t of inner.bindings.reactiveTexts) {
|
|
15058
|
+
if (exprRefs(t.expression, t.freeIdentifiers))
|
|
15059
|
+
return true;
|
|
15060
|
+
}
|
|
15061
|
+
for (const a of inner.bindings.reactiveAttrs) {
|
|
15062
|
+
if (exprRefs(a.expression, a.freeIdentifiers))
|
|
15063
|
+
return true;
|
|
15064
|
+
}
|
|
15065
|
+
if (inner.mapPreamble && extractFreeIdentifiersFromStatementText(inner.mapPreamble).has(index))
|
|
15066
|
+
return true;
|
|
15067
|
+
if (inner.template && extractFreeIdentifiersFromTemplateText(inner.template).has(index))
|
|
15068
|
+
return true;
|
|
15069
|
+
for (const ev of events) {
|
|
15070
|
+
if (exprRefs(ev.handler))
|
|
15071
|
+
return true;
|
|
15072
|
+
}
|
|
15073
|
+
for (const r of inner.bindings.refs) {
|
|
15074
|
+
if (exprRefs(r.callback))
|
|
15075
|
+
return true;
|
|
15076
|
+
}
|
|
15077
|
+
for (const c of comps) {
|
|
15078
|
+
for (const p of c.props) {
|
|
15079
|
+
if (exprRefs(attrValueToString(p.value)))
|
|
15080
|
+
return true;
|
|
15081
|
+
}
|
|
15082
|
+
if (c.children?.length) {
|
|
15083
|
+
const childrenExpr = irChildrenToJsExpr(c.children);
|
|
15084
|
+
if (childrenExpr && exprRefs(childrenExpr))
|
|
15085
|
+
return true;
|
|
15086
|
+
}
|
|
15087
|
+
}
|
|
15088
|
+
return false;
|
|
15089
|
+
}
|
|
15090
|
+
function nestedLoopIndexAlias(inner, syntheticIndexVar, paramHead, comps, events) {
|
|
15091
|
+
const index = inner.index;
|
|
15092
|
+
if (!index || index === paramHead)
|
|
15093
|
+
return null;
|
|
15094
|
+
if (!nestedLoopReferencesIndex(inner, comps, events))
|
|
15095
|
+
return null;
|
|
15096
|
+
return `const ${index} = ${syntheticIndexVar}`;
|
|
15097
|
+
}
|
|
14566
15098
|
function buildChildRefBindings(refs, loopParam, loopParamBindings) {
|
|
14567
15099
|
if (refs.length === 0)
|
|
14568
15100
|
return [];
|
|
@@ -14743,6 +15275,7 @@ function buildOuterNestedPlan(elem, comp) {
|
|
|
14743
15275
|
}
|
|
14744
15276
|
function buildInnerLoopNestedPlan(elem, innerLoop, innerComps) {
|
|
14745
15277
|
const outerIndexParam = elem.index || "__idx";
|
|
15278
|
+
const innerIndexParam = innerLoop.index || "__innerIdx";
|
|
14746
15279
|
const comps = innerComps.map((comp) => ({
|
|
14747
15280
|
componentName: comp.name,
|
|
14748
15281
|
selector: buildCompSelector(comp),
|
|
@@ -14759,7 +15292,8 @@ function buildInnerLoopNestedPlan(elem, innerLoop, innerComps) {
|
|
|
14759
15292
|
innerContainerSlotId: innerLoop.containerSlotId ?? null,
|
|
14760
15293
|
innerArrayExpr: innerLoop.array,
|
|
14761
15294
|
innerParam: innerLoop.param,
|
|
14762
|
-
|
|
15295
|
+
innerIndexParam,
|
|
15296
|
+
innerOffsetExpr: buildLoopChildIndexExpr(innerIndexParam, innerLoop.offset),
|
|
14763
15297
|
innerPreludeStatements: innerLoop.mapPreamble ? [innerLoop.mapPreamble] : [],
|
|
14764
15298
|
depth: innerLoop.depth,
|
|
14765
15299
|
comps
|
|
@@ -14776,9 +15310,11 @@ function buildComponentRootedInnerLoopPlan(elem, innerLoop, innerComps) {
|
|
|
14776
15310
|
containerVar: `_${varSlotId(elem.slotId)}`,
|
|
14777
15311
|
outerArrayExpr: elem.array,
|
|
14778
15312
|
outerParam: elem.param,
|
|
15313
|
+
outerIndexParam: elem.index,
|
|
14779
15314
|
outerPreludeStatements: elem.mapPreamble ? [elem.mapPreamble] : [],
|
|
14780
15315
|
innerArrayExpr: innerLoop.array,
|
|
14781
15316
|
innerParam: innerLoop.param,
|
|
15317
|
+
innerIndexParam: innerLoop.index,
|
|
14782
15318
|
innerPreludeStatements: innerLoop.mapPreamble ? [innerLoop.mapPreamble] : [],
|
|
14783
15319
|
depth: innerLoop.depth,
|
|
14784
15320
|
comps
|
|
@@ -14871,6 +15407,7 @@ function emitInnerLoopNested(lines, plan) {
|
|
|
14871
15407
|
innerContainerSlotId,
|
|
14872
15408
|
innerArrayExpr,
|
|
14873
15409
|
innerParam,
|
|
15410
|
+
innerIndexParam,
|
|
14874
15411
|
innerOffsetExpr,
|
|
14875
15412
|
innerPreludeStatements,
|
|
14876
15413
|
depth,
|
|
@@ -14889,7 +15426,7 @@ function emitInnerLoopNested(lines, plan) {
|
|
|
14889
15426
|
} else {
|
|
14890
15427
|
lines.push(` const __ic = __outerEl`);
|
|
14891
15428
|
}
|
|
14892
|
-
lines.push(` ${innerArrayExpr}.forEach((${innerParam},
|
|
15429
|
+
lines.push(` ${innerArrayExpr}.forEach((${innerParam}, ${innerIndexParam}) => {`);
|
|
14893
15430
|
lines.push(` const __innerEl = __ic.children[${innerOffsetExpr}]`);
|
|
14894
15431
|
lines.push(` if (!__innerEl) return`);
|
|
14895
15432
|
for (const stmt of innerPreludeStatements) {
|
|
@@ -14910,9 +15447,11 @@ function emitComponentRootedInnerLoop(lines, plan) {
|
|
|
14910
15447
|
containerVar,
|
|
14911
15448
|
outerArrayExpr,
|
|
14912
15449
|
outerParam,
|
|
15450
|
+
outerIndexParam,
|
|
14913
15451
|
outerPreludeStatements,
|
|
14914
15452
|
innerArrayExpr,
|
|
14915
15453
|
innerParam,
|
|
15454
|
+
innerIndexParam,
|
|
14916
15455
|
innerPreludeStatements,
|
|
14917
15456
|
depth,
|
|
14918
15457
|
comps
|
|
@@ -14926,11 +15465,11 @@ function emitComponentRootedInnerLoop(lines, plan) {
|
|
|
14926
15465
|
lines.push(` const ${scopesVar(i)} = qsaChildScopes(${containerVar}, ${comp.selector})`);
|
|
14927
15466
|
lines.push(` let ${cursorVar(i)} = 0`);
|
|
14928
15467
|
});
|
|
14929
|
-
lines.push(` ${outerArrayExpr}.forEach((${outerParam}) => {`);
|
|
15468
|
+
lines.push(` ${outerArrayExpr}.forEach((${outerParam}${outerIndexParam ? `, ${outerIndexParam}` : ""}) => {`);
|
|
14930
15469
|
for (const stmt of outerPreludeStatements) {
|
|
14931
15470
|
lines.push(` ${stmt}`);
|
|
14932
15471
|
}
|
|
14933
|
-
lines.push(` ${innerArrayExpr}.forEach((${innerParam}) => {`);
|
|
15472
|
+
lines.push(` ${innerArrayExpr}.forEach((${innerParam}${innerIndexParam ? `, ${innerIndexParam}` : ""}) => {`);
|
|
14934
15473
|
for (const stmt of innerPreludeStatements) {
|
|
14935
15474
|
lines.push(` ${stmt}`);
|
|
14936
15475
|
}
|
|
@@ -15082,6 +15621,7 @@ function buildBranchInnerLoopsPlan(args) {
|
|
|
15082
15621
|
...ev,
|
|
15083
15622
|
handler: wrapInner(ev.handler)
|
|
15084
15623
|
}));
|
|
15624
|
+
const indexAlias = nestedLoopIndexAlias(inner, `__bidxbr_${i}`, paramHead, inner.childComponents ?? [], inner.bindings.events);
|
|
15085
15625
|
const reactiveTexts = inner.bindings.reactiveTexts.map((text) => ({
|
|
15086
15626
|
slotId: text.slotId,
|
|
15087
15627
|
wrappedExpression: wrapBoth(text.expression),
|
|
@@ -15096,6 +15636,7 @@ function buildBranchInnerLoopsPlan(args) {
|
|
|
15096
15636
|
keyFn: loopKeyFn(inner),
|
|
15097
15637
|
paramHead,
|
|
15098
15638
|
paramUnwrap,
|
|
15639
|
+
indexAlias,
|
|
15099
15640
|
wrappedTemplate: inner.template,
|
|
15100
15641
|
wrappedKey,
|
|
15101
15642
|
keyDepth: 1,
|
|
@@ -15337,7 +15878,7 @@ function buildInnerLoopsPlan(args) {
|
|
|
15337
15878
|
const containerExpr = inner.containerSlotId ? `qsa(${parentElVar}, '[bf="${inner.containerSlotId}"]')` : parentElVar;
|
|
15338
15879
|
const refsParent = !!outerLoopParam && (inner.arrayFreeIdentifiers?.has(outerLoopParam) ?? false);
|
|
15339
15880
|
const useReactive = refsParent && !!inner.template;
|
|
15340
|
-
const emit = useReactive ? buildReactiveEmit(inner, level, wrapOuter) : buildStaticEmit(inner, level);
|
|
15881
|
+
const emit = useReactive ? buildReactiveEmit(inner, level, wrapOuter, uidSuffix) : buildStaticEmit(inner, level, uidSuffix);
|
|
15341
15882
|
const arrayExpr = useReactive ? wrapOuter(inner.array) : inner.array;
|
|
15342
15883
|
const childLevelsPlan = childLevels.length > 0 ? buildInnerLoopsPlan({
|
|
15343
15884
|
levels: childLevels,
|
|
@@ -15363,7 +15904,7 @@ function buildInnerLoopsPlan(args) {
|
|
|
15363
15904
|
}
|
|
15364
15905
|
return plan;
|
|
15365
15906
|
}
|
|
15366
|
-
function buildReactiveEmit(inner, level, wrapOuter) {
|
|
15907
|
+
function buildReactiveEmit(inner, level, wrapOuter, uidSuffix) {
|
|
15367
15908
|
const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
|
|
15368
15909
|
const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
|
|
15369
15910
|
const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
|
|
@@ -15410,6 +15951,9 @@ function buildReactiveEmit(inner, level, wrapOuter) {
|
|
|
15410
15951
|
};
|
|
15411
15952
|
});
|
|
15412
15953
|
const preludeStatements = [];
|
|
15954
|
+
const indexAlias = nestedLoopIndexAlias(inner, `__innerIdx${uidSuffix}`, paramHead, level.comps, level.events);
|
|
15955
|
+
if (indexAlias)
|
|
15956
|
+
preludeStatements.push(indexAlias);
|
|
15413
15957
|
if (paramUnwrap)
|
|
15414
15958
|
preludeStatements.push(paramUnwrap);
|
|
15415
15959
|
if (inner.mapPreamble)
|
|
@@ -15430,8 +15974,13 @@ function buildReactiveEmit(inner, level, wrapOuter) {
|
|
|
15430
15974
|
childRefs
|
|
15431
15975
|
};
|
|
15432
15976
|
}
|
|
15433
|
-
function buildStaticEmit(inner, level) {
|
|
15434
|
-
const preludeStatements =
|
|
15977
|
+
function buildStaticEmit(inner, level, uidSuffix) {
|
|
15978
|
+
const preludeStatements = [];
|
|
15979
|
+
const indexAlias = nestedLoopIndexAlias(inner, `__innerIdx${uidSuffix}`, inner.param, level.comps, level.events);
|
|
15980
|
+
if (indexAlias)
|
|
15981
|
+
preludeStatements.push(indexAlias);
|
|
15982
|
+
if (inner.mapPreamble)
|
|
15983
|
+
preludeStatements.push(inner.mapPreamble);
|
|
15435
15984
|
return {
|
|
15436
15985
|
mode: "static",
|
|
15437
15986
|
rawKey: inner.key ?? null,
|
|
@@ -15566,6 +16115,7 @@ function buildDynamicLoopDelegationPlan(elem, profileComponentName) {
|
|
|
15566
16115
|
param: elem.param,
|
|
15567
16116
|
paramBindings: elem.paramBindings,
|
|
15568
16117
|
key: elem.key,
|
|
16118
|
+
index: elem.index,
|
|
15569
16119
|
mapPreamble: elem.mapPreamble ?? null
|
|
15570
16120
|
})
|
|
15571
16121
|
};
|
|
@@ -15581,6 +16131,7 @@ function buildBranchLoopDelegationPlan(loop, cv, profileComponentName) {
|
|
|
15581
16131
|
param: loop.param,
|
|
15582
16132
|
paramBindings: loop.paramBindings,
|
|
15583
16133
|
key: loop.key,
|
|
16134
|
+
index: loop.index,
|
|
15584
16135
|
mapPreamble: loop.mapPreamble ?? null
|
|
15585
16136
|
})
|
|
15586
16137
|
};
|
|
@@ -15596,7 +16147,8 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
|
|
|
15596
16147
|
arrayExpr: buildChainedArrayExpr(elem),
|
|
15597
16148
|
param: elem.param,
|
|
15598
16149
|
mapPreamble: elem.mapPreamble ?? null,
|
|
15599
|
-
offset: elem.offset ?? null
|
|
16150
|
+
offset: elem.offset ?? null,
|
|
16151
|
+
indexParam: elem.index ?? null
|
|
15600
16152
|
}
|
|
15601
16153
|
};
|
|
15602
16154
|
}
|
|
@@ -15611,7 +16163,8 @@ function buildKeyedOrIndexLookup(args) {
|
|
|
15611
16163
|
paramBindings: args.paramBindings,
|
|
15612
16164
|
keyWithItem,
|
|
15613
16165
|
mapPreamble: args.mapPreamble,
|
|
15614
|
-
hasBindings
|
|
16166
|
+
hasBindings,
|
|
16167
|
+
indexParam: args.index
|
|
15615
16168
|
};
|
|
15616
16169
|
}
|
|
15617
16170
|
return {
|
|
@@ -15619,7 +16172,8 @@ function buildKeyedOrIndexLookup(args) {
|
|
|
15619
16172
|
arrayExpr: args.array,
|
|
15620
16173
|
param: args.param,
|
|
15621
16174
|
mapPreamble: args.mapPreamble,
|
|
15622
|
-
hasBindings
|
|
16175
|
+
hasBindings,
|
|
16176
|
+
indexParam: args.index
|
|
15623
16177
|
};
|
|
15624
16178
|
}
|
|
15625
16179
|
|
|
@@ -15929,151 +16483,6 @@ function emitReactiveChildProps(lines, ctx) {
|
|
|
15929
16483
|
}
|
|
15930
16484
|
}
|
|
15931
16485
|
|
|
15932
|
-
// src/ir-to-client-js/control-flow/stringify/template-parse.ts
|
|
15933
|
-
var SVG_ROOT_TAGS = new Set([
|
|
15934
|
-
"svg",
|
|
15935
|
-
"path",
|
|
15936
|
-
"circle",
|
|
15937
|
-
"rect",
|
|
15938
|
-
"line",
|
|
15939
|
-
"polyline",
|
|
15940
|
-
"polygon",
|
|
15941
|
-
"ellipse",
|
|
15942
|
-
"text",
|
|
15943
|
-
"tspan",
|
|
15944
|
-
"textPath",
|
|
15945
|
-
"g",
|
|
15946
|
-
"defs",
|
|
15947
|
-
"use",
|
|
15948
|
-
"symbol",
|
|
15949
|
-
"switch",
|
|
15950
|
-
"clipPath",
|
|
15951
|
-
"mask",
|
|
15952
|
-
"marker",
|
|
15953
|
-
"pattern",
|
|
15954
|
-
"linearGradient",
|
|
15955
|
-
"radialGradient",
|
|
15956
|
-
"stop",
|
|
15957
|
-
"image",
|
|
15958
|
-
"foreignObject",
|
|
15959
|
-
"filter",
|
|
15960
|
-
"feBlend",
|
|
15961
|
-
"feColorMatrix",
|
|
15962
|
-
"feComposite",
|
|
15963
|
-
"feFlood",
|
|
15964
|
-
"feGaussianBlur",
|
|
15965
|
-
"feMerge",
|
|
15966
|
-
"feMergeNode",
|
|
15967
|
-
"feMorphology",
|
|
15968
|
-
"feOffset",
|
|
15969
|
-
"feTurbulence",
|
|
15970
|
-
"animate",
|
|
15971
|
-
"animateTransform",
|
|
15972
|
-
"animateMotion"
|
|
15973
|
-
]);
|
|
15974
|
-
function templateRootIsSvg(template) {
|
|
15975
|
-
const stripped = stripLeadingNonContent(template);
|
|
15976
|
-
const m = stripped.match(/^<\s*([A-Za-z][A-Za-z0-9-]*)/);
|
|
15977
|
-
if (m) {
|
|
15978
|
-
const tag = m[1];
|
|
15979
|
-
if (SVG_ROOT_TAGS.has(tag))
|
|
15980
|
-
return true;
|
|
15981
|
-
return SVG_ROOT_TAGS.has(tag.toLowerCase());
|
|
15982
|
-
}
|
|
15983
|
-
const branches = extractConditionalBranchTemplates(stripped);
|
|
15984
|
-
if (branches === null || branches.length === 0)
|
|
15985
|
-
return false;
|
|
15986
|
-
return branches.every(templateRootIsSvg);
|
|
15987
|
-
}
|
|
15988
|
-
function stripLeadingNonContent(template) {
|
|
15989
|
-
let s = template.trimStart();
|
|
15990
|
-
while (s.startsWith("<!--")) {
|
|
15991
|
-
const end = s.indexOf("-->");
|
|
15992
|
-
if (end < 0)
|
|
15993
|
-
return s;
|
|
15994
|
-
s = s.slice(end + 3).trimStart();
|
|
15995
|
-
}
|
|
15996
|
-
return s;
|
|
15997
|
-
}
|
|
15998
|
-
function extractConditionalBranchTemplates(template) {
|
|
15999
|
-
if (!template.startsWith("${"))
|
|
16000
|
-
return null;
|
|
16001
|
-
const exprEnd = findInterpolationEnd(template, 2);
|
|
16002
|
-
if (exprEnd < 0)
|
|
16003
|
-
return null;
|
|
16004
|
-
const trailing = stripLeadingNonContent(template.slice(exprEnd + 1));
|
|
16005
|
-
if (trailing.length > 0)
|
|
16006
|
-
return null;
|
|
16007
|
-
const expr = template.slice(2, exprEnd);
|
|
16008
|
-
return findTopLevelTemplateLiterals(expr);
|
|
16009
|
-
}
|
|
16010
|
-
function emitTemplateCloneInline(template) {
|
|
16011
|
-
if (templateRootIsSvg(template)) {
|
|
16012
|
-
return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`<svg>${template}</svg>\`; return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`;
|
|
16013
|
-
}
|
|
16014
|
-
return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`${template}\`; return __tpl.content.firstElementChild.cloneNode(true)`;
|
|
16015
|
-
}
|
|
16016
|
-
function emitHoistedTemplateDecl(lines, indent, tplVar, skeletonTemplate) {
|
|
16017
|
-
const isSvg = templateRootIsSvg(skeletonTemplate);
|
|
16018
|
-
const html = isSvg ? `<svg>${skeletonTemplate}</svg>` : skeletonTemplate;
|
|
16019
|
-
lines.push(`${indent}const ${tplVar} = document.createElement('template')`);
|
|
16020
|
-
lines.push(`${indent}${tplVar}.innerHTML = \`${html}\``);
|
|
16021
|
-
}
|
|
16022
|
-
function hoistedCloneExpr(tplVar, skeletonTemplate) {
|
|
16023
|
-
return templateRootIsSvg(skeletonTemplate) ? `${tplVar}.content.firstElementChild.firstElementChild.cloneNode(true)` : `${tplVar}.content.firstElementChild.cloneNode(true)`;
|
|
16024
|
-
}
|
|
16025
|
-
function emitTemplateCloneLines(template, indent) {
|
|
16026
|
-
if (templateRootIsSvg(template)) {
|
|
16027
|
-
return [
|
|
16028
|
-
`${indent}const __tpl = document.createElement('template')`,
|
|
16029
|
-
`${indent}__tpl.innerHTML = \`<svg>${template}</svg>\``,
|
|
16030
|
-
`${indent}return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`
|
|
16031
|
-
];
|
|
16032
|
-
}
|
|
16033
|
-
return [
|
|
16034
|
-
`${indent}const __tpl = document.createElement('template')`,
|
|
16035
|
-
`${indent}__tpl.innerHTML = \`${template}\``,
|
|
16036
|
-
`${indent}return __tpl.content.firstElementChild.cloneNode(true)`
|
|
16037
|
-
];
|
|
16038
|
-
}
|
|
16039
|
-
function emitLoopItemElementSetup(lines, opts) {
|
|
16040
|
-
const { template, bodyIsMultiRoot, indent, singleRootLayout } = opts;
|
|
16041
|
-
const innerIndent = indent + " ";
|
|
16042
|
-
if (bodyIsMultiRoot) {
|
|
16043
|
-
lines.push(`${indent}let __el, __extras`);
|
|
16044
|
-
lines.push(`${indent}if (__existing) {`);
|
|
16045
|
-
lines.push(`${innerIndent}__el = __existing`);
|
|
16046
|
-
lines.push(`${indent}} else {`);
|
|
16047
|
-
for (const ln of emitMultiRootTemplateCloneLines(template, innerIndent, "__el", "__extras")) {
|
|
16048
|
-
lines.push(ln);
|
|
16049
|
-
}
|
|
16050
|
-
lines.push(`${innerIndent}__el.__bfExtras = __extras`);
|
|
16051
|
-
lines.push(`${indent}}`);
|
|
16052
|
-
return;
|
|
16053
|
-
}
|
|
16054
|
-
if (singleRootLayout === "inline") {
|
|
16055
|
-
const cloneExpr = emitTemplateCloneInline(template);
|
|
16056
|
-
lines.push(`${indent}const __el = __existing ?? (() => { ${cloneExpr} })()`);
|
|
16057
|
-
return;
|
|
16058
|
-
}
|
|
16059
|
-
lines.push(`${indent}const __el = __existing ?? (() => {`);
|
|
16060
|
-
for (const ln of emitTemplateCloneLines(template, innerIndent))
|
|
16061
|
-
lines.push(ln);
|
|
16062
|
-
lines.push(`${indent}})()`);
|
|
16063
|
-
}
|
|
16064
|
-
function emitMultiRootTemplateCloneLines(template, indent, varEl, varExtras) {
|
|
16065
|
-
const isSvg = templateRootIsSvg(template);
|
|
16066
|
-
const innerHtmlExpr = isSvg ? `\`<svg>${template}</svg>\`` : `\`${template}\``;
|
|
16067
|
-
const parentExpr = isSvg ? `__tpl.content.firstElementChild` : `__tpl.content`;
|
|
16068
|
-
return [
|
|
16069
|
-
`${indent}const __tpl = document.createElement('template')`,
|
|
16070
|
-
`${indent}__tpl.innerHTML = ${innerHtmlExpr}`,
|
|
16071
|
-
`${indent}${varEl} = ${parentExpr}.firstElementChild.cloneNode(true)`,
|
|
16072
|
-
`${indent}${varExtras} = []`,
|
|
16073
|
-
`${indent}{ let __sib = ${parentExpr}.firstElementChild.nextElementSibling; while (__sib) { ${varExtras}.push(__sib.cloneNode(true)); __sib = __sib.nextElementSibling } }`
|
|
16074
|
-
];
|
|
16075
|
-
}
|
|
16076
|
-
|
|
16077
16486
|
// src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts
|
|
16078
16487
|
function stringifyBranchEventBindings(lines, plan, indent) {
|
|
16079
16488
|
for (const slot of plan) {
|
|
@@ -16095,6 +16504,9 @@ function stringifyBranchInnerLoops(lines, plan, indent, pc) {
|
|
|
16095
16504
|
const uid = inner.uidSuffix;
|
|
16096
16505
|
lines.push(`${indent}{ const __bic${uid} = ${inner.containerExpr}`);
|
|
16097
16506
|
lines.push(`${indent}if (__bic${uid}) mapArray(() => ${inner.arrayExpr} || [], __bic${uid}, ${inner.keyFn}, (${inner.paramHead}, __bidx${uid}, __existing) => {`);
|
|
16507
|
+
if (inner.indexAlias) {
|
|
16508
|
+
lines.push(`${indent} ${inner.indexAlias}`);
|
|
16509
|
+
}
|
|
16098
16510
|
if (inner.paramUnwrap) {
|
|
16099
16511
|
lines.push(`${indent} ${inner.paramUnwrap}`);
|
|
16100
16512
|
}
|
|
@@ -16159,13 +16571,15 @@ function stringifyLoopChildArm(lines, arm, armIndent, pc) {
|
|
|
16159
16571
|
|
|
16160
16572
|
// src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
|
|
16161
16573
|
function stringifyReactiveEffects(lines, plan, opts) {
|
|
16162
|
-
const { indent, elVar, bodyIsMultiRoot } = opts;
|
|
16574
|
+
const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textIndexBySlot } = opts;
|
|
16163
16575
|
const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
|
|
16164
16576
|
const pc = plan.profileComponentName;
|
|
16165
16577
|
const bindingBfId = (slotId) => profileBindingId(pc, slotId);
|
|
16166
16578
|
for (const slot of plan.attrSlots) {
|
|
16167
16579
|
const varName = `__ra_${varSlotId(slot.slotId)}`;
|
|
16168
|
-
|
|
16580
|
+
const pIdx = elementIndexBySlot?.get(slot.slotId);
|
|
16581
|
+
const lookupExpr = pIdx !== undefined ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slot.slotId}"]')` : `${lookup}(${elVar}, '[bf="${slot.slotId}"]')`;
|
|
16582
|
+
lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
|
|
16169
16583
|
lines.push(`${indent}if (${varName}) {`);
|
|
16170
16584
|
for (const attr of slot.attrs) {
|
|
16171
16585
|
lines.push(`${indent} createEffect(() => {`);
|
|
@@ -16177,15 +16591,19 @@ function stringifyReactiveEffects(lines, plan, opts) {
|
|
|
16177
16591
|
lines.push(`${indent}} }`);
|
|
16178
16592
|
}
|
|
16179
16593
|
for (const text of plan.outerTexts) {
|
|
16180
|
-
emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId));
|
|
16594
|
+
emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId), textIndexBySlot?.get(text.slotId));
|
|
16181
16595
|
}
|
|
16182
16596
|
for (const cond of plan.conditionals) {
|
|
16183
16597
|
emitOuterConditional(lines, indent, elVar, cond, pc);
|
|
16184
16598
|
}
|
|
16185
16599
|
}
|
|
16186
|
-
function emitOuterText(lines, indent, elVar, text, bfId = "") {
|
|
16600
|
+
function emitOuterText(lines, indent, elVar, text, bfId = "", pIdx) {
|
|
16187
16601
|
const varName = `__rt_${varSlotId(text.slotId)}`;
|
|
16188
|
-
|
|
16602
|
+
if (pIdx !== undefined) {
|
|
16603
|
+
lines.push(`${indent}{ const ${varName} = __p ? tAfter(__p[${pIdx}]) : $t(${elVar}, '${text.slotId}')[0]`);
|
|
16604
|
+
} else {
|
|
16605
|
+
lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`);
|
|
16606
|
+
}
|
|
16189
16607
|
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`);
|
|
16190
16608
|
}
|
|
16191
16609
|
function emitOuterConditional(lines, indent, elVar, cond, pc) {
|
|
@@ -16217,6 +16635,41 @@ function emitArmText(lines, indent, text, pc) {
|
|
|
16217
16635
|
lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${profileBindingId(pc, text.slotId)}) }`);
|
|
16218
16636
|
}
|
|
16219
16637
|
|
|
16638
|
+
// src/ir-to-client-js/control-flow/stringify/skeleton-paths.ts
|
|
16639
|
+
function pathExpr(base, path) {
|
|
16640
|
+
let expr = base;
|
|
16641
|
+
for (const idx of path) {
|
|
16642
|
+
expr += ".firstChild";
|
|
16643
|
+
if (idx > 0)
|
|
16644
|
+
expr += ".nextSibling".repeat(idx);
|
|
16645
|
+
}
|
|
16646
|
+
return expr;
|
|
16647
|
+
}
|
|
16648
|
+
function buildSkeletonPathPlan(skeletonPaths, elVar, opts) {
|
|
16649
|
+
const arrayElems = [];
|
|
16650
|
+
const elementIndexBySlot = new Map;
|
|
16651
|
+
const textIndexBySlot = new Map;
|
|
16652
|
+
for (const slotId of opts.elementSlotIds) {
|
|
16653
|
+
if (elementIndexBySlot.has(slotId))
|
|
16654
|
+
continue;
|
|
16655
|
+
const path = skeletonPaths.elementPaths.get(slotId);
|
|
16656
|
+
if (!path)
|
|
16657
|
+
continue;
|
|
16658
|
+
elementIndexBySlot.set(slotId, arrayElems.length);
|
|
16659
|
+
arrayElems.push(pathExpr(elVar, path));
|
|
16660
|
+
}
|
|
16661
|
+
for (const slotId of opts.textSlotIds) {
|
|
16662
|
+
if (textIndexBySlot.has(slotId))
|
|
16663
|
+
continue;
|
|
16664
|
+
const path = skeletonPaths.textMarkerPaths.get(slotId);
|
|
16665
|
+
if (!path)
|
|
16666
|
+
continue;
|
|
16667
|
+
textIndexBySlot.set(slotId, arrayElems.length);
|
|
16668
|
+
arrayElems.push(pathExpr(elVar, path));
|
|
16669
|
+
}
|
|
16670
|
+
return { arrayElems, elementIndexBySlot, textIndexBySlot };
|
|
16671
|
+
}
|
|
16672
|
+
|
|
16220
16673
|
// src/ir-to-client-js/control-flow/stringify/component-loop.ts
|
|
16221
16674
|
function stringifyComponentLoop(lines, plan) {
|
|
16222
16675
|
const {
|
|
@@ -16276,11 +16729,13 @@ function emitNestedInit(lines, indent, parentVar, nc) {
|
|
|
16276
16729
|
function emitLoopChildRefs(lines, refs, opts) {
|
|
16277
16730
|
if (refs.length === 0)
|
|
16278
16731
|
return;
|
|
16279
|
-
const { indent, elVar, bodyIsMultiRoot } = opts;
|
|
16732
|
+
const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot } = opts;
|
|
16280
16733
|
const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
|
|
16281
16734
|
for (const ref of refs) {
|
|
16282
16735
|
const varName = `__rf_${varSlotId(ref.childSlotId)}`;
|
|
16283
|
-
|
|
16736
|
+
const pIdx = elementIndexBySlot?.get(ref.childSlotId);
|
|
16737
|
+
const lookupExpr = pIdx !== undefined ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${ref.childSlotId}"]')` : `${lookup}(${elVar}, '[bf="${ref.childSlotId}"]')`;
|
|
16738
|
+
lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
|
|
16284
16739
|
lines.push(`${indent}if (${varName}) ${emitRefCall(ref.callback, varName)} }`);
|
|
16285
16740
|
}
|
|
16286
16741
|
}
|
|
@@ -16360,10 +16815,31 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
|
|
|
16360
16815
|
singleRootLayout: "inline"
|
|
16361
16816
|
});
|
|
16362
16817
|
}
|
|
16818
|
+
let pathPlan = null;
|
|
16819
|
+
if (hoistedTpl && plan.skeletonPaths) {
|
|
16820
|
+
const elementSlotIds = [
|
|
16821
|
+
...reactiveEffects?.attrSlots.map((s) => s.slotId) ?? [],
|
|
16822
|
+
...childRefs.map((r) => r.childSlotId)
|
|
16823
|
+
];
|
|
16824
|
+
const textSlotIds = reactiveEffects?.outerTexts.map((t) => t.slotId) ?? [];
|
|
16825
|
+
if (elementSlotIds.length > 0 || textSlotIds.length > 0) {
|
|
16826
|
+
const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds });
|
|
16827
|
+
if (built.arrayElems.length > 0) {
|
|
16828
|
+
pathPlan = built;
|
|
16829
|
+
lines.push(`${bodyIndent}const __p = __existing ? null : [${built.arrayElems.join(", ")}]`);
|
|
16830
|
+
}
|
|
16831
|
+
}
|
|
16832
|
+
}
|
|
16363
16833
|
if (reactiveEffects !== null) {
|
|
16364
|
-
stringifyReactiveEffects(lines, reactiveEffects, {
|
|
16834
|
+
stringifyReactiveEffects(lines, reactiveEffects, {
|
|
16835
|
+
indent: bodyIndent,
|
|
16836
|
+
elVar: "__el",
|
|
16837
|
+
bodyIsMultiRoot,
|
|
16838
|
+
elementIndexBySlot: pathPlan?.elementIndexBySlot,
|
|
16839
|
+
textIndexBySlot: pathPlan?.textIndexBySlot
|
|
16840
|
+
});
|
|
16365
16841
|
}
|
|
16366
|
-
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
|
|
16842
|
+
emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
|
|
16367
16843
|
lines.push(`${bodyIndent}return __el`);
|
|
16368
16844
|
lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
|
|
16369
16845
|
}
|
|
@@ -16430,12 +16906,14 @@ function stringifyStaticLoop(lines, plan) {
|
|
|
16430
16906
|
lines.push(` let __iterEl = ${containerVar}.children[${childIndexExpr}]`);
|
|
16431
16907
|
if (csrMaterialize) {
|
|
16432
16908
|
lines.push(` if (!__iterEl) {`);
|
|
16909
|
+
const isSvg = csrMaterialize.bodyIsMultiRoot ? multiRootTemplateNeedsSvgWrap(csrMaterialize.itemTemplate) : templateRootIsSvg(csrMaterialize.itemTemplate);
|
|
16910
|
+
const itemHtml = isSvg ? `<svg>${csrMaterialize.itemTemplate}</svg>` : csrMaterialize.itemTemplate;
|
|
16433
16911
|
if (csrMaterialize.bodyIsMultiRoot) {
|
|
16434
16912
|
lines.push(` const __mtpl = document.createElement('template')`);
|
|
16435
|
-
lines.push(` __mtpl.innerHTML = \`${
|
|
16913
|
+
lines.push(` __mtpl.innerHTML = \`${itemHtml}\``);
|
|
16436
16914
|
lines.push(` const __anchor = ${containerVar}.children[${childIndexExpr}] ?? null`);
|
|
16437
16915
|
lines.push(` let __first = null`);
|
|
16438
|
-
lines.push(` let __sib = __mtpl.content.firstElementChild`);
|
|
16916
|
+
lines.push(` let __sib = __mtpl.content${isSvg ? ".firstElementChild" : ""}.firstElementChild`);
|
|
16439
16917
|
lines.push(` while (__sib) {`);
|
|
16440
16918
|
lines.push(` const __next = __sib.nextElementSibling`);
|
|
16441
16919
|
lines.push(` const __cloned = __sib.cloneNode(true)`);
|
|
@@ -16446,8 +16924,8 @@ function stringifyStaticLoop(lines, plan) {
|
|
|
16446
16924
|
lines.push(` __iterEl = __first`);
|
|
16447
16925
|
} else {
|
|
16448
16926
|
lines.push(` const __tpl = document.createElement('template')`);
|
|
16449
|
-
lines.push(` __tpl.innerHTML = \`${
|
|
16450
|
-
lines.push(` const __cloned = __tpl.content.firstElementChild`);
|
|
16927
|
+
lines.push(` __tpl.innerHTML = \`${itemHtml}\``);
|
|
16928
|
+
lines.push(` const __cloned = __tpl.content${isSvg ? ".firstElementChild" : ""}.firstElementChild`);
|
|
16451
16929
|
lines.push(` if (__cloned) {`);
|
|
16452
16930
|
lines.push(` const __anchor = ${containerVar}.children[${childIndexExpr}] ?? null`);
|
|
16453
16931
|
lines.push(` ${containerVar}.insertBefore(__cloned, __anchor)`);
|
|
@@ -16514,7 +16992,10 @@ function emitReactive(lines, inner, indent, pc) {
|
|
|
16514
16992
|
lines.push(`${innerIndent} __innerEl${uid}.__bfExtras = __innerExtras${uid}`);
|
|
16515
16993
|
lines.push(`${indent} }`);
|
|
16516
16994
|
} else {
|
|
16517
|
-
|
|
16995
|
+
const isSvg = templateRootIsSvg(emit.wrappedTemplate);
|
|
16996
|
+
const innerHtml = isSvg ? `<svg>${emit.wrappedTemplate}</svg>` : emit.wrappedTemplate;
|
|
16997
|
+
const childPath = isSvg ? ".firstElementChild.firstElementChild" : ".firstElementChild";
|
|
16998
|
+
lines.push(`${indent} let __innerEl${uid} = __existing ?? (() => { const __t = document.createElement('template'); __t.innerHTML = \`${innerHtml}\`; return __t.content${childPath}.cloneNode(true) })()`);
|
|
16518
16999
|
}
|
|
16519
17000
|
if (emit.wrappedKey) {
|
|
16520
17001
|
lines.push(`${indent} __innerEl${uid}.setAttribute('${keyAttrName(inner.keyDepth)}', String(${emit.wrappedKey}))`);
|
|
@@ -16662,6 +17143,13 @@ function withTurn(call, componentName, childSlotId, eventName) {
|
|
|
16662
17143
|
const id = JSON.stringify(`${componentName}#handler:${childSlotId}:${eventName}`);
|
|
16663
17144
|
return `beginTurn(${id}); try { ${call} } finally { endTurn() }`;
|
|
16664
17145
|
}
|
|
17146
|
+
function indexBindingLine(handler, indexParam, indexExpr) {
|
|
17147
|
+
if (!indexParam || indexParam === indexExpr)
|
|
17148
|
+
return null;
|
|
17149
|
+
if (!extractFreeIdentifiersFromText(handler).has(indexParam))
|
|
17150
|
+
return null;
|
|
17151
|
+
return `const ${indexParam} = ${indexExpr}`;
|
|
17152
|
+
}
|
|
16665
17153
|
function stringifyEventDelegation(lines, plan) {
|
|
16666
17154
|
const { containerVar, events, itemLookup, profileComponentName } = plan;
|
|
16667
17155
|
const eventsByName = new Map;
|
|
@@ -16707,8 +17195,9 @@ function stringifyEventDelegation(lines, plan) {
|
|
|
16707
17195
|
}
|
|
16708
17196
|
}
|
|
16709
17197
|
function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
16710
|
-
const { arrayExpr, param, keyWithItem, mapPreamble, hasBindings } = lookup;
|
|
17198
|
+
const { arrayExpr, param, keyWithItem, mapPreamble, hasBindings, indexParam } = lookup;
|
|
16711
17199
|
if (ev.nestedLoops.length === 0) {
|
|
17200
|
+
const idxLine2 = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === key)`);
|
|
16712
17201
|
ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('[${DATA_KEY}]')`);
|
|
16713
17202
|
ls.push(` if (li) {`);
|
|
16714
17203
|
ls.push(` const key = li.getAttribute('${DATA_KEY}')`);
|
|
@@ -16718,13 +17207,18 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
|
16718
17207
|
ls.push(` const ${param} = __bfLoopItem`);
|
|
16719
17208
|
if (mapPreamble)
|
|
16720
17209
|
ls.push(` ${mapPreamble}`);
|
|
17210
|
+
if (idxLine2)
|
|
17211
|
+
ls.push(` ${idxLine2}`);
|
|
16721
17212
|
ls.push(` ${handlerCall}`);
|
|
16722
17213
|
ls.push(` }`);
|
|
16723
17214
|
} else {
|
|
16724
17215
|
ls.push(` const ${param} = ${arrayExpr}.find(item => String(${keyWithItem}) === key)`);
|
|
16725
17216
|
if (mapPreamble)
|
|
16726
17217
|
ls.push(` ${mapPreamble}`);
|
|
16727
|
-
|
|
17218
|
+
if (idxLine2)
|
|
17219
|
+
ls.push(` if (${param}) { ${idxLine2}; ${handlerCall} }`);
|
|
17220
|
+
else
|
|
17221
|
+
ls.push(` if (${param}) ${handlerCall}`);
|
|
16728
17222
|
}
|
|
16729
17223
|
ls.push(` }`);
|
|
16730
17224
|
return;
|
|
@@ -16753,10 +17247,15 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
|
|
|
16753
17247
|
const allParams = [outerGuard, ...ev.nestedLoops.map((n) => n.param)];
|
|
16754
17248
|
if (mapPreamble)
|
|
16755
17249
|
ls.push(` ${mapPreamble}`);
|
|
16756
|
-
|
|
17250
|
+
const idxLine = indexBindingLine(ev.handler, indexParam, `${arrayExpr}.findIndex(item => String(${keyWithItem}) === outerKey)`);
|
|
17251
|
+
if (idxLine)
|
|
17252
|
+
ls.push(` if (${allParams.join(" && ")}) { ${idxLine}; ${handlerCall} }`);
|
|
17253
|
+
else
|
|
17254
|
+
ls.push(` if (${allParams.join(" && ")}) ${handlerCall}`);
|
|
16757
17255
|
}
|
|
16758
17256
|
function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
|
|
16759
|
-
const { arrayExpr, param, mapPreamble, hasBindings } = lookup;
|
|
17257
|
+
const { arrayExpr, param, mapPreamble, hasBindings, indexParam } = lookup;
|
|
17258
|
+
const idxLine = indexBindingLine(ev.handler, indexParam, "idx");
|
|
16760
17259
|
ls.push(` const li = ${varSlotId(ev.childSlotId)}El.closest('li, [bf-i]')`);
|
|
16761
17260
|
ls.push(` if (li && li.parentElement) {`);
|
|
16762
17261
|
ls.push(` const idx = Array.from(li.parentElement.children).indexOf(li)`);
|
|
@@ -16766,18 +17265,24 @@ function emitDynamicIndexLookup(ls, ev, handlerCall, lookup) {
|
|
|
16766
17265
|
ls.push(` const ${param} = __bfLoopItem`);
|
|
16767
17266
|
if (mapPreamble)
|
|
16768
17267
|
ls.push(` ${mapPreamble}`);
|
|
17268
|
+
if (idxLine)
|
|
17269
|
+
ls.push(` ${idxLine}`);
|
|
16769
17270
|
ls.push(` ${handlerCall}`);
|
|
16770
17271
|
ls.push(` }`);
|
|
16771
17272
|
} else {
|
|
16772
17273
|
ls.push(` const ${param} = ${arrayExpr}[idx]`);
|
|
16773
17274
|
if (mapPreamble)
|
|
16774
17275
|
ls.push(` ${mapPreamble}`);
|
|
16775
|
-
|
|
17276
|
+
if (idxLine)
|
|
17277
|
+
ls.push(` if (${param}) { ${idxLine}; ${handlerCall} }`);
|
|
17278
|
+
else
|
|
17279
|
+
ls.push(` if (${param}) ${handlerCall}`);
|
|
16776
17280
|
}
|
|
16777
17281
|
ls.push(` }`);
|
|
16778
17282
|
}
|
|
16779
17283
|
function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
|
|
16780
|
-
const { arrayExpr, param, mapPreamble, offset } = lookup;
|
|
17284
|
+
const { arrayExpr, param, mapPreamble, offset, indexParam } = lookup;
|
|
17285
|
+
const idxLine = indexBindingLine(ev.handler, indexParam, "__idx");
|
|
16781
17286
|
ls.push(` let __el = ${varSlotId(ev.childSlotId)}El`);
|
|
16782
17287
|
ls.push(` while (__el.parentElement && __el.parentElement !== ${containerVar}) __el = __el.parentElement`);
|
|
16783
17288
|
ls.push(` if (__el.parentElement === ${containerVar}) {`);
|
|
@@ -16786,7 +17291,10 @@ function emitStaticIndexLookup(ls, ev, handlerCall, lookup, containerVar) {
|
|
|
16786
17291
|
ls.push(` const ${param} = ${arrayExpr}[__idx]`);
|
|
16787
17292
|
if (mapPreamble)
|
|
16788
17293
|
ls.push(` ${mapPreamble}`);
|
|
16789
|
-
|
|
17294
|
+
if (idxLine)
|
|
17295
|
+
ls.push(` if (${param}) { ${idxLine}; ${handlerCall} }`);
|
|
17296
|
+
else
|
|
17297
|
+
ls.push(` if (${param}) ${handlerCall}`);
|
|
16790
17298
|
ls.push(` }`);
|
|
16791
17299
|
}
|
|
16792
17300
|
|
|
@@ -17046,6 +17554,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
|
|
|
17046
17554
|
mapPreambleWrapped: elem.mapPreamble ? wrap(elem.mapPreamble) : "",
|
|
17047
17555
|
template: elem.template,
|
|
17048
17556
|
skeletonTemplate: elem.skeletonTemplate,
|
|
17557
|
+
skeletonPaths: elem.skeletonPaths,
|
|
17049
17558
|
reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
|
|
17050
17559
|
childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
|
|
17051
17560
|
bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
|
|
@@ -20019,6 +20528,8 @@ function isStringTypedOperand(expr, isStringName) {
|
|
|
20019
20528
|
if (expr.kind === "member" && expr.object.kind === "identifier" && expr.object.name === "props") {
|
|
20020
20529
|
return isStringName(expr.property);
|
|
20021
20530
|
}
|
|
20531
|
+
if (expr.kind === "identifier")
|
|
20532
|
+
return isStringName(expr.name);
|
|
20022
20533
|
if (expr.kind === "binary" && expr.op === "+") {
|
|
20023
20534
|
return isStringTypedOperand(expr.left, isStringName) || isStringTypedOperand(expr.right, isStringName);
|
|
20024
20535
|
}
|
|
@@ -20044,7 +20555,7 @@ function emitParsedExpr(expr, emitter) {
|
|
|
20044
20555
|
return emitter.call(expr.callee, expr.args, emit);
|
|
20045
20556
|
}
|
|
20046
20557
|
case "member":
|
|
20047
|
-
return emitter.member(expr.object, expr.property, expr.computed, emit);
|
|
20558
|
+
return emitter.member(expr.object, expr.property, expr.computed, expr.optional, emit);
|
|
20048
20559
|
case "index-access":
|
|
20049
20560
|
return emitter.indexAccess(expr.object, expr.index, emit);
|
|
20050
20561
|
case "binary":
|
|
@@ -20078,6 +20589,221 @@ function emitParsedExpr(expr, emitter) {
|
|
|
20078
20589
|
}
|
|
20079
20590
|
}
|
|
20080
20591
|
}
|
|
20592
|
+
// src/adapters/loop-bound-names.ts
|
|
20593
|
+
function collectLoopBoundNames(ir) {
|
|
20594
|
+
const names = new Set;
|
|
20595
|
+
const visit3 = (node) => {
|
|
20596
|
+
if (!node)
|
|
20597
|
+
return;
|
|
20598
|
+
switch (node.type) {
|
|
20599
|
+
case "element":
|
|
20600
|
+
case "component":
|
|
20601
|
+
case "fragment":
|
|
20602
|
+
case "provider":
|
|
20603
|
+
for (const child of node.children)
|
|
20604
|
+
visit3(child);
|
|
20605
|
+
break;
|
|
20606
|
+
case "async":
|
|
20607
|
+
visit3(node.fallback);
|
|
20608
|
+
for (const child of node.children)
|
|
20609
|
+
visit3(child);
|
|
20610
|
+
break;
|
|
20611
|
+
case "loop":
|
|
20612
|
+
names.add(node.param);
|
|
20613
|
+
if (node.index)
|
|
20614
|
+
names.add(node.index);
|
|
20615
|
+
for (const binding of node.paramBindings ?? [])
|
|
20616
|
+
names.add(binding.name);
|
|
20617
|
+
if (node.filterPredicate)
|
|
20618
|
+
names.add(node.filterPredicate.param);
|
|
20619
|
+
for (const child of node.children)
|
|
20620
|
+
visit3(child);
|
|
20621
|
+
if (node.childComponent) {
|
|
20622
|
+
for (const child of node.childComponent.children)
|
|
20623
|
+
visit3(child);
|
|
20624
|
+
}
|
|
20625
|
+
for (const nested of node.nestedComponents ?? []) {
|
|
20626
|
+
for (const child of nested.children)
|
|
20627
|
+
visit3(child);
|
|
20628
|
+
}
|
|
20629
|
+
for (const frag of node.flatMapCallback?.fragments ?? []) {
|
|
20630
|
+
visit3(frag.ir);
|
|
20631
|
+
}
|
|
20632
|
+
break;
|
|
20633
|
+
case "conditional":
|
|
20634
|
+
visit3(node.whenTrue);
|
|
20635
|
+
visit3(node.whenFalse);
|
|
20636
|
+
break;
|
|
20637
|
+
case "if-statement":
|
|
20638
|
+
visit3(node.consequent);
|
|
20639
|
+
if (node.alternate)
|
|
20640
|
+
visit3(node.alternate);
|
|
20641
|
+
break;
|
|
20642
|
+
case "text":
|
|
20643
|
+
case "expression":
|
|
20644
|
+
case "slot":
|
|
20645
|
+
break;
|
|
20646
|
+
}
|
|
20647
|
+
};
|
|
20648
|
+
visit3(ir.root);
|
|
20649
|
+
return names;
|
|
20650
|
+
}
|
|
20651
|
+
// src/signal-init-eval.ts
|
|
20652
|
+
var BLOCKED_GLOBALS = [
|
|
20653
|
+
"globalThis",
|
|
20654
|
+
"window",
|
|
20655
|
+
"document",
|
|
20656
|
+
"Date",
|
|
20657
|
+
"Math",
|
|
20658
|
+
"crypto",
|
|
20659
|
+
"performance",
|
|
20660
|
+
"fetch",
|
|
20661
|
+
"setTimeout",
|
|
20662
|
+
"setInterval",
|
|
20663
|
+
"require",
|
|
20664
|
+
"process",
|
|
20665
|
+
"Function"
|
|
20666
|
+
];
|
|
20667
|
+
function isTransportable(value, ancestors = new Set) {
|
|
20668
|
+
if (value === null)
|
|
20669
|
+
return true;
|
|
20670
|
+
const t = typeof value;
|
|
20671
|
+
if (t === "boolean" || t === "number" || t === "string")
|
|
20672
|
+
return true;
|
|
20673
|
+
if (t !== "object")
|
|
20674
|
+
return false;
|
|
20675
|
+
if (ancestors.has(value))
|
|
20676
|
+
return false;
|
|
20677
|
+
ancestors.add(value);
|
|
20678
|
+
try {
|
|
20679
|
+
if (Array.isArray(value)) {
|
|
20680
|
+
if (Object.keys(value).length !== value.length)
|
|
20681
|
+
return false;
|
|
20682
|
+
return value.every((el) => el !== undefined && isTransportable(el, ancestors));
|
|
20683
|
+
}
|
|
20684
|
+
const proto = Object.getPrototypeOf(value);
|
|
20685
|
+
if (proto !== Object.prototype && proto !== null)
|
|
20686
|
+
return false;
|
|
20687
|
+
return Object.values(value).every((v) => isTransportable(v, ancestors));
|
|
20688
|
+
} finally {
|
|
20689
|
+
ancestors.delete(value);
|
|
20690
|
+
}
|
|
20691
|
+
}
|
|
20692
|
+
function tryEvaluateSignalInit(expr, props) {
|
|
20693
|
+
const src = expr.trim();
|
|
20694
|
+
if (src === "")
|
|
20695
|
+
return { ok: false };
|
|
20696
|
+
let fn;
|
|
20697
|
+
try {
|
|
20698
|
+
fn = new Function("props", ...BLOCKED_GLOBALS, `'use strict'; return (
|
|
20699
|
+
${src}
|
|
20700
|
+
);`);
|
|
20701
|
+
} catch {
|
|
20702
|
+
return { ok: false };
|
|
20703
|
+
}
|
|
20704
|
+
try {
|
|
20705
|
+
const value = fn(props ?? {});
|
|
20706
|
+
if (value === undefined)
|
|
20707
|
+
return { ok: true, value: undefined };
|
|
20708
|
+
return isTransportable(value) ? { ok: true, value } : { ok: false };
|
|
20709
|
+
} catch {
|
|
20710
|
+
return { ok: false };
|
|
20711
|
+
}
|
|
20712
|
+
}
|
|
20713
|
+
function evaluateSignalInit(expr, props) {
|
|
20714
|
+
const result = tryEvaluateSignalInit(expr, props);
|
|
20715
|
+
return result.ok && result.value !== undefined ? result.value : null;
|
|
20716
|
+
}
|
|
20717
|
+
// src/static-literal.ts
|
|
20718
|
+
function evaluateStaticLiteral(expr, bindings) {
|
|
20719
|
+
switch (expr.kind) {
|
|
20720
|
+
case "literal":
|
|
20721
|
+
return { value: expr.value };
|
|
20722
|
+
case "template-literal": {
|
|
20723
|
+
let out = "";
|
|
20724
|
+
for (const part of expr.parts) {
|
|
20725
|
+
if (part.type === "string") {
|
|
20726
|
+
out += part.value;
|
|
20727
|
+
continue;
|
|
20728
|
+
}
|
|
20729
|
+
const resolved = evaluateStaticLiteral(part.expr, bindings);
|
|
20730
|
+
if (!resolved)
|
|
20731
|
+
return null;
|
|
20732
|
+
out += String(resolved.value);
|
|
20733
|
+
}
|
|
20734
|
+
return { value: out };
|
|
20735
|
+
}
|
|
20736
|
+
case "array-literal": {
|
|
20737
|
+
const values = [];
|
|
20738
|
+
for (const element of expr.elements) {
|
|
20739
|
+
const resolved = evaluateStaticLiteral(element, bindings);
|
|
20740
|
+
if (!resolved)
|
|
20741
|
+
return null;
|
|
20742
|
+
values.push(resolved.value);
|
|
20743
|
+
}
|
|
20744
|
+
return { value: values };
|
|
20745
|
+
}
|
|
20746
|
+
case "object-literal": {
|
|
20747
|
+
const out = {};
|
|
20748
|
+
for (const prop of expr.properties) {
|
|
20749
|
+
const resolved = evaluateStaticLiteral(prop.value, bindings);
|
|
20750
|
+
if (!resolved)
|
|
20751
|
+
return null;
|
|
20752
|
+
out[prop.key] = resolved.value;
|
|
20753
|
+
}
|
|
20754
|
+
return { value: out };
|
|
20755
|
+
}
|
|
20756
|
+
case "unary": {
|
|
20757
|
+
const resolved = evaluateStaticLiteral(expr.argument, bindings);
|
|
20758
|
+
if (!resolved)
|
|
20759
|
+
return null;
|
|
20760
|
+
if (expr.op === "-")
|
|
20761
|
+
return typeof resolved.value === "number" ? { value: -resolved.value } : null;
|
|
20762
|
+
if (expr.op === "+")
|
|
20763
|
+
return typeof resolved.value === "number" ? { value: +resolved.value } : null;
|
|
20764
|
+
if (expr.op === "!")
|
|
20765
|
+
return { value: !resolved.value };
|
|
20766
|
+
return null;
|
|
20767
|
+
}
|
|
20768
|
+
case "identifier":
|
|
20769
|
+
return bindings?.has(expr.name) ? { value: bindings.get(expr.name) } : null;
|
|
20770
|
+
case "member": {
|
|
20771
|
+
const base = evaluateStaticLiteral(expr.object, bindings);
|
|
20772
|
+
if (!base || base.value === null || typeof base.value !== "object")
|
|
20773
|
+
return null;
|
|
20774
|
+
return { value: base.value[expr.property] };
|
|
20775
|
+
}
|
|
20776
|
+
case "index-access": {
|
|
20777
|
+
const base = evaluateStaticLiteral(expr.object, bindings);
|
|
20778
|
+
const index = evaluateStaticLiteral(expr.index, bindings);
|
|
20779
|
+
if (!base || !index || !Array.isArray(base.value) || typeof index.value !== "number")
|
|
20780
|
+
return null;
|
|
20781
|
+
return { value: base.value[index.value] };
|
|
20782
|
+
}
|
|
20783
|
+
default:
|
|
20784
|
+
return null;
|
|
20785
|
+
}
|
|
20786
|
+
}
|
|
20787
|
+
function isFullyStaticLiteral(expr) {
|
|
20788
|
+
return evaluateStaticLiteral(expr) !== null;
|
|
20789
|
+
}
|
|
20790
|
+
function resolveStaticLoopSource(arrayParsed, localConstants, opts) {
|
|
20791
|
+
if (!arrayParsed)
|
|
20792
|
+
return null;
|
|
20793
|
+
let target = arrayParsed;
|
|
20794
|
+
if (arrayParsed.kind === "identifier") {
|
|
20795
|
+
if (opts?.isNameShadowed?.(arrayParsed.name))
|
|
20796
|
+
return null;
|
|
20797
|
+
const local = localConstants?.find((c) => c.name === arrayParsed.name);
|
|
20798
|
+
if (!local || local.isModule || !local.parsed)
|
|
20799
|
+
return null;
|
|
20800
|
+
target = local.parsed;
|
|
20801
|
+
}
|
|
20802
|
+
const resolved = evaluateStaticLiteral(target);
|
|
20803
|
+
if (!resolved || !Array.isArray(resolved.value))
|
|
20804
|
+
return null;
|
|
20805
|
+
return resolved.value;
|
|
20806
|
+
}
|
|
20081
20807
|
// src/query-href-lowering.ts
|
|
20082
20808
|
function matchQueryHrefCall(callee, args, localNames) {
|
|
20083
20809
|
if (callee.kind !== "identifier" || !localNames.has(callee.name))
|
|
@@ -20213,6 +20939,71 @@ function emitAttrValue(value, emitter, name) {
|
|
|
20213
20939
|
}
|
|
20214
20940
|
}
|
|
20215
20941
|
}
|
|
20942
|
+
// src/adapters/dangerous-inner-html.ts
|
|
20943
|
+
var DANGEROUS_INNER_HTML_ATTR = "dangerouslySetInnerHTML";
|
|
20944
|
+
function isDangerousInnerHtmlAttr(attr) {
|
|
20945
|
+
return attr.name === DANGEROUS_INNER_HTML_ATTR;
|
|
20946
|
+
}
|
|
20947
|
+
function resolveDangerousInnerHtml(element) {
|
|
20948
|
+
const attr = element.attrs.find(isDangerousInnerHtmlAttr);
|
|
20949
|
+
if (!attr)
|
|
20950
|
+
return null;
|
|
20951
|
+
if (attr.clientOnly)
|
|
20952
|
+
return null;
|
|
20953
|
+
if (attr.value.kind !== "expression") {
|
|
20954
|
+
return { kind: "dynamic", expr: "", loc: attr.loc };
|
|
20955
|
+
}
|
|
20956
|
+
const parsed = attr.value.parsed ?? parseExpression(attr.value.expr.trim());
|
|
20957
|
+
const html = staticHtmlLiteral(parsed);
|
|
20958
|
+
if (html !== null)
|
|
20959
|
+
return { kind: "static", html };
|
|
20960
|
+
return { kind: "dynamic", expr: attr.value.expr, loc: attr.loc };
|
|
20961
|
+
}
|
|
20962
|
+
function staticHtmlLiteral(parsed) {
|
|
20963
|
+
if (parsed.kind !== "object-literal")
|
|
20964
|
+
return null;
|
|
20965
|
+
if (parsed.properties.length !== 1)
|
|
20966
|
+
return null;
|
|
20967
|
+
const [prop] = parsed.properties;
|
|
20968
|
+
if (prop.shorthand)
|
|
20969
|
+
return null;
|
|
20970
|
+
if (prop.key !== "__html")
|
|
20971
|
+
return null;
|
|
20972
|
+
if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
|
|
20973
|
+
return null;
|
|
20974
|
+
return prop.value.value;
|
|
20975
|
+
}
|
|
20976
|
+
var TEMPLATE_METACHAR_PATTERNS = {
|
|
20977
|
+
blade: /\{\{|\{!!|<\?|@\w|<\/?\s*x[-:]/,
|
|
20978
|
+
erb: /<%/,
|
|
20979
|
+
"go-template": /\{\{/,
|
|
20980
|
+
jinja: /\{\{|\{%|\{#/,
|
|
20981
|
+
minijinja: /\{\{|\{%|\{#/,
|
|
20982
|
+
mojolicious: /<%|^\s*%/m,
|
|
20983
|
+
twig: /\{\{|\{%|\{#/,
|
|
20984
|
+
xslate: /<:|^\s*:/m
|
|
20985
|
+
};
|
|
20986
|
+
function dangerousInnerHtmlMetacharViolation(html, adapterId) {
|
|
20987
|
+
const pattern = TEMPLATE_METACHAR_PATTERNS[adapterId];
|
|
20988
|
+
if (!pattern) {
|
|
20989
|
+
return `no template-metacharacter guard is defined for adapter '${adapterId}' — refusing rather than splicing unguarded`;
|
|
20990
|
+
}
|
|
20991
|
+
if (!pattern.test(html))
|
|
20992
|
+
return null;
|
|
20993
|
+
return `the literal HTML contains a sequence ${adapterId}'s own template compiler would interpret (not inert text once spliced into the template)`;
|
|
20994
|
+
}
|
|
20995
|
+
function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
|
|
20996
|
+
const detail = reason ? ` — ${reason}.` : "";
|
|
20997
|
+
return {
|
|
20998
|
+
code: "BF101",
|
|
20999
|
+
severity: "error",
|
|
21000
|
+
message: `dangerouslySetInnerHTML requires a compile-time string literal __html value on template adapters (e.g. { __html: '...' })${expr ? `: ${expr.trim()}` : ""}${detail}`,
|
|
21001
|
+
loc,
|
|
21002
|
+
suggestion: {
|
|
21003
|
+
message: "Dynamic or signal-derived HTML for dangerouslySetInnerHTML is only supported on Hono/CSR today (tracked separately: https://github.com/piconic-ai/barefootjs/issues/2215). Use an inline string literal, or defer it to the client with /* @client */ (e.g. dangerouslySetInnerHTML={/* @client */ { __html: expr }}) so hydration sets it instead of SSR."
|
|
21004
|
+
}
|
|
21005
|
+
};
|
|
21006
|
+
}
|
|
20216
21007
|
// src/combine-client-js.ts
|
|
20217
21008
|
import ts19 from "typescript";
|
|
20218
21009
|
var CHILD_PLACEHOLDER_RE = /import '\/\* @bf-child:(\w+) \*\/'/g;
|
|
@@ -23054,6 +23845,7 @@ function profileToJSON(profile) {
|
|
|
23054
23845
|
registerBuiltinLoweringPlugins();
|
|
23055
23846
|
export {
|
|
23056
23847
|
tsNodeToParsedExpr,
|
|
23848
|
+
tryEvaluateSignalInit,
|
|
23057
23849
|
traceUpdatePath,
|
|
23058
23850
|
testAdapter,
|
|
23059
23851
|
stringifyParsedExpr,
|
|
@@ -23061,7 +23853,9 @@ export {
|
|
|
23061
23853
|
serializeParsedExpr,
|
|
23062
23854
|
searchParamsLocalNames,
|
|
23063
23855
|
rewriteImportsForTemplate,
|
|
23856
|
+
resolveStaticLoopSource,
|
|
23064
23857
|
resolveSetters,
|
|
23858
|
+
resolveDangerousInnerHtml,
|
|
23065
23859
|
resetCompilerCounters,
|
|
23066
23860
|
renderImportMapHtml,
|
|
23067
23861
|
registerLoweringPlugin,
|
|
@@ -23096,6 +23890,8 @@ export {
|
|
|
23096
23890
|
isStringConcatBinary,
|
|
23097
23891
|
isLowerableObjectRestDestructure,
|
|
23098
23892
|
isLowerableLoopDestructure,
|
|
23893
|
+
isFullyStaticLiteral,
|
|
23894
|
+
isDangerousInnerHtmlAttr,
|
|
23099
23895
|
isBooleanAttr,
|
|
23100
23896
|
importsSearchParams,
|
|
23101
23897
|
identifierPath,
|
|
@@ -23136,6 +23932,8 @@ export {
|
|
|
23136
23932
|
extractFunctionParams,
|
|
23137
23933
|
extractArrowBodyExpression,
|
|
23138
23934
|
exprToString,
|
|
23935
|
+
evaluateStaticLiteral,
|
|
23936
|
+
evaluateSignalInit,
|
|
23139
23937
|
evaluateProfileGates,
|
|
23140
23938
|
evalStringArrayJoin,
|
|
23141
23939
|
envSignalReaderFor,
|
|
@@ -23148,6 +23946,8 @@ export {
|
|
|
23148
23946
|
diffStaticBudget,
|
|
23149
23947
|
diffProfiles,
|
|
23150
23948
|
describeFallback,
|
|
23949
|
+
dangerousInnerHtmlMetacharViolation,
|
|
23950
|
+
dangerousInnerHtmlDiagnostic,
|
|
23151
23951
|
createProgramForFile,
|
|
23152
23952
|
createProgramForCorpus,
|
|
23153
23953
|
createError,
|
|
@@ -23156,6 +23956,7 @@ export {
|
|
|
23156
23956
|
compileJSX,
|
|
23157
23957
|
combineParentChildClientJs,
|
|
23158
23958
|
collectModuleStringConsts,
|
|
23959
|
+
collectLoopBoundNames,
|
|
23159
23960
|
collectContextConsumers,
|
|
23160
23961
|
buildWhyUpdate,
|
|
23161
23962
|
buildStaticBudget,
|