@barefootjs/mojolicious 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/adapter/expr/array-method.d.ts.map +1 -1
- package/dist/adapter/expr/emitters.d.ts +2 -2
- package/dist/adapter/expr/emitters.d.ts.map +1 -1
- package/dist/adapter/index.js +151 -34
- package/dist/adapter/lib/constants.d.ts.map +1 -1
- package/dist/adapter/lib/static-value.d.ts +17 -0
- package/dist/adapter/lib/static-value.d.ts.map +1 -0
- package/dist/adapter/mojo-adapter.d.ts +23 -0
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/adapter/props/prop-classes.d.ts +23 -6
- package/dist/adapter/props/prop-classes.d.ts.map +1 -1
- package/dist/build.js +151 -34
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +154 -56
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/test-render.d.ts +0 -5
- package/dist/test-render.d.ts.map +1 -1
- package/lib/BarefootJS/Backend/Mojo.pm +1 -1
- package/lib/Mojolicious/Plugin/BarefootJS/DevReload.pm +1 -1
- package/lib/Mojolicious/Plugin/BarefootJS.pm +1 -1
- package/package.json +3 -3
- package/src/__tests__/mojo-adapter.test.ts +186 -5
- package/src/adapter/expr/array-method.ts +19 -0
- package/src/adapter/expr/emitters.ts +18 -3
- package/src/adapter/lib/constants.ts +3 -0
- package/src/adapter/lib/static-value.ts +47 -0
- package/src/adapter/mojo-adapter.ts +148 -16
- package/src/adapter/props/prop-classes.ts +27 -6
- package/src/conformance-pins.ts +26 -33
- package/src/render-divergences.ts +4 -20
- package/src/test-render.ts +21 -210
- package/src/__tests__/evaluate-signal-init.test.ts +0 -35
package/dist/build.js
CHANGED
|
@@ -19,7 +19,12 @@ import {
|
|
|
19
19
|
queryHrefArgs,
|
|
20
20
|
isValidHelperId,
|
|
21
21
|
sortComparatorFromArrow as sortComparatorFromArrow2,
|
|
22
|
-
isLowerableLoopDestructure
|
|
22
|
+
isLowerableLoopDestructure,
|
|
23
|
+
isDangerousInnerHtmlAttr,
|
|
24
|
+
resolveDangerousInnerHtml,
|
|
25
|
+
dangerousInnerHtmlMetacharViolation,
|
|
26
|
+
dangerousInnerHtmlDiagnostic,
|
|
27
|
+
resolveStaticLoopSource
|
|
23
28
|
} from "@barefootjs/jsx";
|
|
24
29
|
|
|
25
30
|
// src/adapter/boolean-result.ts
|
|
@@ -76,7 +81,7 @@ function isAriaBooleanAttr(name) {
|
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
// src/adapter/mojo-adapter.ts
|
|
79
|
-
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
84
|
+
import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
|
|
80
85
|
|
|
81
86
|
// src/adapter/lib/constants.ts
|
|
82
87
|
var MOJO_TEMPLATE_PRIMITIVES = {
|
|
@@ -85,7 +90,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
|
|
|
85
90
|
Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
|
|
86
91
|
"Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
|
|
87
92
|
"Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
|
|
88
|
-
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
|
|
93
|
+
"Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
|
|
94
|
+
"Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
|
|
95
|
+
"Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
|
|
96
|
+
"Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
|
|
89
97
|
};
|
|
90
98
|
var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
91
99
|
|
|
@@ -190,6 +198,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
190
198
|
const recv = emit(object);
|
|
191
199
|
return `bf->trim(${recv})`;
|
|
192
200
|
}
|
|
201
|
+
case "trimStart":
|
|
202
|
+
case "trimEnd": {
|
|
203
|
+
const fn = method === "trimStart" ? "trim_start" : "trim_end";
|
|
204
|
+
const recv = emit(object);
|
|
205
|
+
return `bf->${fn}(${recv})`;
|
|
206
|
+
}
|
|
193
207
|
case "toFixed": {
|
|
194
208
|
const recv = emit(object);
|
|
195
209
|
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
@@ -223,6 +237,12 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
223
237
|
const newS = emit(args[1]);
|
|
224
238
|
return `bf->replace(${recv}, ${oldS}, ${newS})`;
|
|
225
239
|
}
|
|
240
|
+
case "replaceAll": {
|
|
241
|
+
const recv = emit(object);
|
|
242
|
+
const oldS = emit(args[0]);
|
|
243
|
+
const newS = emit(args[1]);
|
|
244
|
+
return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
|
|
245
|
+
}
|
|
226
246
|
case "repeat": {
|
|
227
247
|
const recv = emit(object);
|
|
228
248
|
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
@@ -323,6 +343,42 @@ function renderFlatMethod(recv, depth, emit) {
|
|
|
323
343
|
return `bf->flat(${recv}, ${d})`;
|
|
324
344
|
}
|
|
325
345
|
|
|
346
|
+
// src/adapter/lib/static-value.ts
|
|
347
|
+
function escapePerlSingleQuote2(s) {
|
|
348
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
349
|
+
}
|
|
350
|
+
function staticValueToPerl(value) {
|
|
351
|
+
if (value === null || value === undefined)
|
|
352
|
+
return "undef";
|
|
353
|
+
if (typeof value === "boolean")
|
|
354
|
+
return null;
|
|
355
|
+
if (typeof value === "number")
|
|
356
|
+
return String(value);
|
|
357
|
+
if (typeof value === "string")
|
|
358
|
+
return `'${escapePerlSingleQuote2(value)}'`;
|
|
359
|
+
if (Array.isArray(value)) {
|
|
360
|
+
const items = [];
|
|
361
|
+
for (const el of value) {
|
|
362
|
+
const serialized = staticValueToPerl(el);
|
|
363
|
+
if (serialized === null)
|
|
364
|
+
return null;
|
|
365
|
+
items.push(serialized);
|
|
366
|
+
}
|
|
367
|
+
return `[${items.join(", ")}]`;
|
|
368
|
+
}
|
|
369
|
+
if (typeof value === "object") {
|
|
370
|
+
const entries = [];
|
|
371
|
+
for (const [key, val] of Object.entries(value)) {
|
|
372
|
+
const serialized = staticValueToPerl(val);
|
|
373
|
+
if (serialized === null)
|
|
374
|
+
return null;
|
|
375
|
+
entries.push(`${perlHashKey(key)} => ${serialized}`);
|
|
376
|
+
}
|
|
377
|
+
return `{ ${entries.join(", ")} }`;
|
|
378
|
+
}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
326
382
|
// src/adapter/expr/emitters.ts
|
|
327
383
|
import {
|
|
328
384
|
groupBinaryOperand,
|
|
@@ -381,7 +437,7 @@ class MojoFilterEmitter {
|
|
|
381
437
|
return "undef";
|
|
382
438
|
return String(value);
|
|
383
439
|
}
|
|
384
|
-
member(object, property, _computed, emit) {
|
|
440
|
+
member(object, property, _computed, _optional, emit) {
|
|
385
441
|
if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
|
|
386
442
|
return `scalar(@{${emit(object)}})`;
|
|
387
443
|
}
|
|
@@ -516,7 +572,7 @@ class MojoTopLevelEmitter {
|
|
|
516
572
|
return "undef";
|
|
517
573
|
return String(value);
|
|
518
574
|
}
|
|
519
|
-
member(object, property, _computed, emit) {
|
|
575
|
+
member(object, property, _computed, _optional, emit) {
|
|
520
576
|
if (object.kind === "identifier" && object.name === "props") {
|
|
521
577
|
return `$${property}`;
|
|
522
578
|
}
|
|
@@ -526,8 +582,13 @@ class MojoTopLevelEmitter {
|
|
|
526
582
|
return staticValue;
|
|
527
583
|
}
|
|
528
584
|
const obj = emit(object);
|
|
529
|
-
if (property === "length")
|
|
585
|
+
if (property === "length") {
|
|
586
|
+
const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
|
|
587
|
+
const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
|
|
588
|
+
if (isStringReceiver)
|
|
589
|
+
return `length(${obj})`;
|
|
530
590
|
return `scalar(@{${obj}})`;
|
|
591
|
+
}
|
|
531
592
|
return `${obj}->{${property}}`;
|
|
532
593
|
}
|
|
533
594
|
indexAccess(object, index, emit) {
|
|
@@ -914,6 +975,9 @@ function generateDerivedMemoSeed(ctx, ir) {
|
|
|
914
975
|
` : "";
|
|
915
976
|
}
|
|
916
977
|
|
|
978
|
+
// src/adapter/props/prop-classes.ts
|
|
979
|
+
import { collectLoopBoundNames } from "@barefootjs/jsx";
|
|
980
|
+
|
|
917
981
|
// src/adapter/value/parsed-literal.ts
|
|
918
982
|
function isStringTypeInfo(type) {
|
|
919
983
|
return type?.kind === "primitive" && type.primitive === "string";
|
|
@@ -950,6 +1014,12 @@ function collectStringValueNames(ir) {
|
|
|
950
1014
|
if (isStringTypeInfo(p.type))
|
|
951
1015
|
names.add(p.name);
|
|
952
1016
|
}
|
|
1017
|
+
for (const c of ir.metadata.localConstants) {
|
|
1018
|
+
if (isStringTypeInfo(c.type ?? undefined) || isBareStringLiteral(c.value))
|
|
1019
|
+
names.add(c.name);
|
|
1020
|
+
}
|
|
1021
|
+
for (const bound of collectLoopBoundNames(ir))
|
|
1022
|
+
names.delete(bound);
|
|
953
1023
|
return names;
|
|
954
1024
|
}
|
|
955
1025
|
|
|
@@ -976,6 +1046,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
976
1046
|
options;
|
|
977
1047
|
errors = [];
|
|
978
1048
|
inLoop = false;
|
|
1049
|
+
currentLoopKeyDepth = 0;
|
|
979
1050
|
propsObjectName = null;
|
|
980
1051
|
propsParams = [];
|
|
981
1052
|
booleanTypedProps = new Set;
|
|
@@ -1108,7 +1179,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1108
1179
|
return this.renderElement(node);
|
|
1109
1180
|
}
|
|
1110
1181
|
emitText(node) {
|
|
1111
|
-
return node.value;
|
|
1182
|
+
return escapeHtml(node.value);
|
|
1112
1183
|
}
|
|
1113
1184
|
emitExpression(node) {
|
|
1114
1185
|
return this.renderExpression(node);
|
|
@@ -1179,7 +1250,8 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1179
1250
|
renderElement(element) {
|
|
1180
1251
|
const tag = element.tag;
|
|
1181
1252
|
const attrs = this.renderAttributes(element);
|
|
1182
|
-
const
|
|
1253
|
+
const dangerousHtml = this.renderDangerousInnerHtml(element);
|
|
1254
|
+
const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
|
|
1183
1255
|
let hydrationAttrs = "";
|
|
1184
1256
|
if (element.needsScope) {
|
|
1185
1257
|
hydrationAttrs += ` ${this.renderScopeMarker("")}`;
|
|
@@ -1214,6 +1286,22 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1214
1286
|
}
|
|
1215
1287
|
return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
|
|
1216
1288
|
}
|
|
1289
|
+
renderDangerousInnerHtml(element) {
|
|
1290
|
+
const resolution = resolveDangerousInnerHtml(element);
|
|
1291
|
+
if (!resolution)
|
|
1292
|
+
return null;
|
|
1293
|
+
if (resolution.kind === "dynamic") {
|
|
1294
|
+
this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
|
|
1295
|
+
return "";
|
|
1296
|
+
}
|
|
1297
|
+
const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
|
|
1298
|
+
if (violation) {
|
|
1299
|
+
const attr = element.attrs.find(isDangerousInnerHtmlAttr);
|
|
1300
|
+
this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
|
|
1301
|
+
return "";
|
|
1302
|
+
}
|
|
1303
|
+
return resolution.html;
|
|
1304
|
+
}
|
|
1217
1305
|
renderExpression(expr) {
|
|
1218
1306
|
if (expr.clientOnly) {
|
|
1219
1307
|
if (expr.slotId) {
|
|
@@ -1221,7 +1309,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
1221
1309
|
}
|
|
1222
1310
|
return "";
|
|
1223
1311
|
}
|
|
1224
|
-
const perlExpr = this.convertExpressionToPerl(expr.expr);
|
|
1312
|
+
const perlExpr = this.convertExpressionToPerl(expr.expr, expr.parsed);
|
|
1225
1313
|
if (expr.slotId) {
|
|
1226
1314
|
return `<%== bf->text_start("${expr.slotId}") %><%= ${perlExpr} %><%== bf->text_end %>`;
|
|
1227
1315
|
}
|
|
@@ -1313,8 +1401,12 @@ ${whenTrue}
|
|
|
1313
1401
|
}
|
|
1314
1402
|
});
|
|
1315
1403
|
}
|
|
1404
|
+
const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
|
|
1405
|
+
isNameShadowed: (name) => this.loopBoundNames.has(name)
|
|
1406
|
+
});
|
|
1407
|
+
const staticArray = staticItems !== null ? staticValueToPerl(staticItems) : null;
|
|
1316
1408
|
const arrayName = loop.array.trim();
|
|
1317
|
-
if (/^[A-Za-z_$][\w$]*$/.test(arrayName)) {
|
|
1409
|
+
if (staticArray === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
|
|
1318
1410
|
const arrayConst = (this.localConstants ?? []).find((c) => c.name === arrayName);
|
|
1319
1411
|
if (arrayConst && !arrayConst.isModule && this.resolveLiteralConst(arrayName) === null) {
|
|
1320
1412
|
this.errors.push({
|
|
@@ -1328,7 +1420,7 @@ ${whenTrue}
|
|
|
1328
1420
|
});
|
|
1329
1421
|
}
|
|
1330
1422
|
}
|
|
1331
|
-
const rawArray = this.convertExpressionToPerl(loop.array);
|
|
1423
|
+
const rawArray = staticArray ?? this.convertExpressionToPerl(loop.array);
|
|
1332
1424
|
let sortedHoist = null;
|
|
1333
1425
|
let array = rawArray;
|
|
1334
1426
|
if (loop.sortComparator) {
|
|
@@ -1337,13 +1429,16 @@ ${whenTrue}
|
|
|
1337
1429
|
}
|
|
1338
1430
|
const param = loop.param;
|
|
1339
1431
|
const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
|
|
1340
|
-
const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1432
|
+
const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
|
|
1341
1433
|
for (const n of loopBound) {
|
|
1342
1434
|
this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
|
|
1343
1435
|
}
|
|
1344
1436
|
const prevInLoop = this.inLoop;
|
|
1345
1437
|
this.inLoop = true;
|
|
1438
|
+
const prevLoopKeyDepth = this.currentLoopKeyDepth;
|
|
1439
|
+
this.currentLoopKeyDepth = loop.depth;
|
|
1346
1440
|
const renderedChildren = this.renderChildren(loop.children);
|
|
1441
|
+
this.currentLoopKeyDepth = prevLoopKeyDepth;
|
|
1347
1442
|
this.inLoop = prevInLoop;
|
|
1348
1443
|
const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
|
|
1349
1444
|
${renderedChildren}` : renderedChildren;
|
|
@@ -1377,23 +1472,31 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1377
1472
|
}
|
|
1378
1473
|
lines.push(`% my $${sortedHoist} = ${sorted};`);
|
|
1379
1474
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1475
|
+
if (loop.objectIteration) {
|
|
1476
|
+
const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
|
|
1477
|
+
lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
|
|
1478
|
+
if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
|
|
1479
|
+
lines.push(`% my $${param} = ${array}->{${keyVar}};`);
|
|
1480
|
+
}
|
|
1481
|
+
} else {
|
|
1482
|
+
lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
|
|
1483
|
+
if (loop.iterationShape !== "keys") {
|
|
1484
|
+
if (supportableDestructure) {
|
|
1485
|
+
lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
|
|
1486
|
+
for (const b of loop.paramBindings ?? []) {
|
|
1487
|
+
const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
|
|
1488
|
+
if (b.rest?.kind === "object") {
|
|
1489
|
+
const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
|
|
1490
|
+
lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
|
|
1491
|
+
} else if (b.rest?.kind === "array") {
|
|
1492
|
+
lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
|
|
1493
|
+
} else {
|
|
1494
|
+
lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
|
|
1495
|
+
}
|
|
1393
1496
|
}
|
|
1497
|
+
} else {
|
|
1498
|
+
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1394
1499
|
}
|
|
1395
|
-
} else {
|
|
1396
|
-
lines.push(`% my $${param} = ${array}->[${indexVar}];`);
|
|
1397
1500
|
}
|
|
1398
1501
|
}
|
|
1399
1502
|
if (loop.filterPredicate) {
|
|
@@ -1448,9 +1551,20 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1448
1551
|
};
|
|
1449
1552
|
renderComponent(comp) {
|
|
1450
1553
|
const propParts = [];
|
|
1554
|
+
const namedSlotCaptures = [];
|
|
1451
1555
|
for (const p of comp.props) {
|
|
1452
1556
|
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
1453
1557
|
continue;
|
|
1558
|
+
if (p.value.kind === "jsx-children" && p.name !== "children") {
|
|
1559
|
+
const prevInLoop = this.inLoop;
|
|
1560
|
+
this.inLoop = false;
|
|
1561
|
+
const slotBody = this.renderChildren(p.value.children);
|
|
1562
|
+
this.inLoop = prevInLoop;
|
|
1563
|
+
const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
|
|
1564
|
+
namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
|
|
1565
|
+
propParts.push(`${perlHashKey(p.name)} => ${varName}`);
|
|
1566
|
+
continue;
|
|
1567
|
+
}
|
|
1454
1568
|
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
|
|
1455
1569
|
if (lowered)
|
|
1456
1570
|
propParts.push(lowered);
|
|
@@ -1467,9 +1581,9 @@ ${renderedChildren}` : renderedChildren;
|
|
|
1467
1581
|
const childrenBody = this.renderChildren(effectiveChildren);
|
|
1468
1582
|
this.inLoop = prevInLoop;
|
|
1469
1583
|
const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
1470
|
-
return
|
|
1584
|
+
return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
|
|
1471
1585
|
}
|
|
1472
|
-
return
|
|
1586
|
+
return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
|
|
1473
1587
|
}
|
|
1474
1588
|
childrenCaptureCounter = 0;
|
|
1475
1589
|
presenceVarCounter = 0;
|
|
@@ -1514,7 +1628,7 @@ ${alternate}
|
|
|
1514
1628
|
${children}`;
|
|
1515
1629
|
}
|
|
1516
1630
|
elementAttrEmitter = {
|
|
1517
|
-
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
1631
|
+
emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
|
|
1518
1632
|
emitExpression: (value, name) => {
|
|
1519
1633
|
if (name === "style") {
|
|
1520
1634
|
const css = this.tryLowerStyleObject(value.expr);
|
|
@@ -1569,7 +1683,7 @@ ${children}`;
|
|
|
1569
1683
|
if (ternaryHashref !== null) {
|
|
1570
1684
|
return `<%== bf->spread_attrs(${ternaryHashref}) %>`;
|
|
1571
1685
|
}
|
|
1572
|
-
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
|
|
1686
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.loopBoundNames.has(trimmed)) {
|
|
1573
1687
|
const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
|
|
1574
1688
|
if (localConst?.value !== undefined) {
|
|
1575
1689
|
const initTrimmed = localConst.value.trim();
|
|
@@ -1605,12 +1719,15 @@ ${children}`;
|
|
|
1605
1719
|
for (const attr of element.attrs) {
|
|
1606
1720
|
if (attr.clientOnly)
|
|
1607
1721
|
continue;
|
|
1722
|
+
if (isDangerousInnerHtmlAttr(attr))
|
|
1723
|
+
continue;
|
|
1608
1724
|
let attrName;
|
|
1609
1725
|
if (attr.name === "className")
|
|
1610
1726
|
attrName = "class";
|
|
1611
|
-
else if (attr.name === "key")
|
|
1612
|
-
|
|
1613
|
-
|
|
1727
|
+
else if (attr.name === "key") {
|
|
1728
|
+
const depth = this.currentLoopKeyDepth;
|
|
1729
|
+
attrName = depth > 0 ? `data-key-${depth}` : "data-key";
|
|
1730
|
+
} else
|
|
1614
1731
|
attrName = attr.name;
|
|
1615
1732
|
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
1616
1733
|
if (lowered)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAmI7B,CAAA"}
|