@barefootjs/jsx 0.31.10 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -523,7 +523,7 @@ function convertNode(node, raw) {
523
523
  }
524
524
  if (n === undefined || Number.isNaN(n)) {
525
525
  const parsedDepth = convertNode(depthNode, raw);
526
- if (checkSupport(parsedDepth).supported) {
526
+ if (checkSupport(parsedDepth, "rendered").supported) {
527
527
  depthExpr = parsedDepth;
528
528
  flatDepth = 1;
529
529
  } else {
@@ -617,10 +617,12 @@ function convertNode(node, raw) {
617
617
  const k = objectLiteralKeyName(prop.name);
618
618
  if (k === null)
619
619
  return { kind: "unsupported", raw, reason: `Unsupported syntax: ${ts2.SyntaxKind[node.kind]}` };
620
- properties.push({ key: k.key, keyKind: k.keyKind, shorthand: false, value: convertNode(prop.initializer, raw) });
620
+ properties.push({ kind: "prop", key: k.key, keyKind: k.keyKind, shorthand: false, value: convertNode(prop.initializer, raw) });
621
621
  } else if (ts2.isShorthandPropertyAssignment(prop)) {
622
622
  const key = prop.name.text;
623
- properties.push({ key, keyKind: "identifier", shorthand: true, value: { kind: "identifier", name: key } });
623
+ properties.push({ kind: "prop", key, keyKind: "identifier", shorthand: true, value: { kind: "identifier", name: key } });
624
+ } else if (ts2.isSpreadAssignment(prop)) {
625
+ properties.push({ kind: "spread", expr: convertNode(prop.expression, raw) });
624
626
  } else {
625
627
  return { kind: "unsupported", raw, reason: `Unsupported syntax: ${ts2.SyntaxKind[node.kind]}` };
626
628
  }
@@ -1300,14 +1302,26 @@ function getUnaryOperatorString(op) {
1300
1302
  }
1301
1303
  }
1302
1304
  function isSupported(expr) {
1303
- return checkSupport(expr);
1305
+ return checkSupport(expr, "rendered");
1304
1306
  }
1305
- function checkSupport(expr) {
1307
+ function isSupportedValue(expr) {
1308
+ return checkSupport(expr, "value");
1309
+ }
1310
+ function checkSupport(expr, pos) {
1306
1311
  switch (expr.kind) {
1307
1312
  case "unsupported":
1308
1313
  return { supported: false, reason: expr.reason };
1309
- case "object-literal":
1310
- return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
1314
+ case "object-literal": {
1315
+ if (pos !== "value") {
1316
+ return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
1317
+ }
1318
+ for (const prop of expr.properties) {
1319
+ const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos);
1320
+ if (!propSupport.supported)
1321
+ return propSupport;
1322
+ }
1323
+ return { supported: true, level: "L2" };
1324
+ }
1311
1325
  case "identifier":
1312
1326
  return { supported: true, level: "L1" };
1313
1327
  case "literal":
@@ -1317,7 +1331,7 @@ function checkSupport(expr) {
1317
1331
  return { supported: false, reason: "Standalone arrow functions / regex literals are not supported" };
1318
1332
  case "array-literal": {
1319
1333
  for (const el of expr.elements) {
1320
- const elSupport = checkSupport(el);
1334
+ const elSupport = checkSupport(el, pos);
1321
1335
  if (!elSupport.supported)
1322
1336
  return elSupport;
1323
1337
  }
@@ -1330,16 +1344,16 @@ function checkSupport(expr) {
1330
1344
  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 */`
1331
1345
  };
1332
1346
  }
1333
- const objSupport = checkSupport(expr.object);
1347
+ const objSupport = checkSupport(expr.object, pos);
1334
1348
  if (!objSupport.supported)
1335
1349
  return objSupport;
1336
1350
  for (const arg of expr.args) {
1337
- const argSupport = checkSupport(arg);
1351
+ const argSupport = checkSupport(arg, pos);
1338
1352
  if (!argSupport.supported)
1339
1353
  return argSupport;
1340
1354
  }
1341
1355
  if (expr.method === "flat" && expr.depthExpr) {
1342
- const depthSupport = checkSupport(expr.depthExpr);
1356
+ const depthSupport = checkSupport(expr.depthExpr, pos);
1343
1357
  if (!depthSupport.supported)
1344
1358
  return depthSupport;
1345
1359
  }
@@ -1348,10 +1362,10 @@ function checkSupport(expr) {
1348
1362
  case "call": {
1349
1363
  const cb = asCallbackMethodCall(expr);
1350
1364
  if (cb) {
1351
- const objSupport = checkSupport(cb.object);
1365
+ const objSupport = checkSupport(cb.object, pos);
1352
1366
  if (!objSupport.supported)
1353
1367
  return objSupport;
1354
- const bodySupport = checkSupport(cb.arrow.body);
1368
+ const bodySupport = checkSupport(cb.arrow.body, pos);
1355
1369
  if (!bodySupport.supported) {
1356
1370
  return {
1357
1371
  supported: false,
@@ -1360,13 +1374,13 @@ function checkSupport(expr) {
1360
1374
  };
1361
1375
  }
1362
1376
  for (const rest of cb.args) {
1363
- const restSupport = checkSupport(rest);
1377
+ const restSupport = checkSupport(rest, pos);
1364
1378
  if (!restSupport.supported)
1365
1379
  return restSupport;
1366
1380
  }
1367
1381
  return { supported: true, level: "L5" };
1368
1382
  }
1369
- const calleeSupport = checkSupport(expr.callee);
1383
+ const calleeSupport = checkSupport(expr.callee, pos);
1370
1384
  if (!calleeSupport.supported) {
1371
1385
  return calleeSupport;
1372
1386
  }
@@ -1385,7 +1399,7 @@ function checkSupport(expr) {
1385
1399
  return { supported: true, level: "L1" };
1386
1400
  }
1387
1401
  for (const arg of expr.args) {
1388
- const argSupport = checkSupport(arg);
1402
+ const argSupport = checkSupport(arg, pos);
1389
1403
  if (!argSupport.supported) {
1390
1404
  return argSupport;
1391
1405
  }
@@ -1393,7 +1407,7 @@ function checkSupport(expr) {
1393
1407
  return { supported: true, level: "L2" };
1394
1408
  }
1395
1409
  case "member": {
1396
- const objSupport = checkSupport(expr.object);
1410
+ const objSupport = checkSupport(expr.object, pos);
1397
1411
  if (!objSupport.supported) {
1398
1412
  return objSupport;
1399
1413
  }
@@ -1403,19 +1417,19 @@ function checkSupport(expr) {
1403
1417
  return { supported: true, level: "L2" };
1404
1418
  }
1405
1419
  case "index-access": {
1406
- const objSupport = checkSupport(expr.object);
1420
+ const objSupport = checkSupport(expr.object, pos);
1407
1421
  if (!objSupport.supported)
1408
1422
  return objSupport;
1409
- const indexSupport = checkSupport(expr.index);
1423
+ const indexSupport = checkSupport(expr.index, pos);
1410
1424
  if (!indexSupport.supported)
1411
1425
  return indexSupport;
1412
1426
  return { supported: true, level: "L2" };
1413
1427
  }
1414
1428
  case "binary": {
1415
- const leftSupport = checkSupport(expr.left);
1429
+ const leftSupport = checkSupport(expr.left, pos);
1416
1430
  if (!leftSupport.supported)
1417
1431
  return leftSupport;
1418
- const rightSupport = checkSupport(expr.right);
1432
+ const rightSupport = checkSupport(expr.right, pos);
1419
1433
  if (!rightSupport.supported)
1420
1434
  return rightSupport;
1421
1435
  if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].includes(expr.op)) {
@@ -1427,7 +1441,7 @@ function checkSupport(expr) {
1427
1441
  return { supported: false, reason: `Unknown operator: ${expr.op}` };
1428
1442
  }
1429
1443
  case "unary": {
1430
- const argSupport = checkSupport(expr.argument);
1444
+ const argSupport = checkSupport(expr.argument, pos);
1431
1445
  if (!argSupport.supported)
1432
1446
  return argSupport;
1433
1447
  if (expr.op === "!") {
@@ -1439,25 +1453,25 @@ function checkSupport(expr) {
1439
1453
  return { supported: false, reason: `Unsupported unary operator: ${expr.op}` };
1440
1454
  }
1441
1455
  case "logical": {
1442
- const leftSupport = checkSupport(expr.left);
1456
+ const leftSupport = checkSupport(expr.left, pos);
1443
1457
  if (!leftSupport.supported)
1444
1458
  return leftSupport;
1445
1459
  if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
1446
1460
  return { supported: true, level: "L4" };
1447
1461
  }
1448
- const rightSupport = checkSupport(expr.right);
1462
+ const rightSupport = checkSupport(expr.right, pos);
1449
1463
  if (!rightSupport.supported)
1450
1464
  return rightSupport;
1451
1465
  return { supported: true, level: "L4" };
1452
1466
  }
1453
1467
  case "conditional": {
1454
- const testSupport = checkSupport(expr.test);
1468
+ const testSupport = checkSupport(expr.test, pos);
1455
1469
  if (!testSupport.supported)
1456
1470
  return testSupport;
1457
- const consSupport = checkSupport(expr.consequent);
1471
+ const consSupport = checkSupport(expr.consequent, pos);
1458
1472
  if (!consSupport.supported)
1459
1473
  return consSupport;
1460
- const altSupport = checkSupport(expr.alternate);
1474
+ const altSupport = checkSupport(expr.alternate, pos);
1461
1475
  if (!altSupport.supported)
1462
1476
  return altSupport;
1463
1477
  return { supported: true, level: "L4" };
@@ -1465,7 +1479,7 @@ function checkSupport(expr) {
1465
1479
  case "template-literal": {
1466
1480
  for (const part of expr.parts) {
1467
1481
  if (part.type === "expression") {
1468
- const partSupport = checkSupport(part.expr);
1482
+ const partSupport = checkSupport(part.expr, pos);
1469
1483
  if (!partSupport.supported)
1470
1484
  return partSupport;
1471
1485
  }
@@ -1618,7 +1632,7 @@ function isPureInit(e, pureCallNames) {
1618
1632
  case "array-literal":
1619
1633
  return e.elements.every(pure);
1620
1634
  case "object-literal":
1621
- return e.properties.every((p) => pure(p.value));
1635
+ return e.properties.every((p) => pure(p.kind === "spread" ? p.expr : p.value));
1622
1636
  case "call":
1623
1637
  return e.callee.kind === "identifier" && e.args.length === 0 && pureCallNames !== undefined && pureCallNames.has(e.callee.name);
1624
1638
  case "array-method":
@@ -1672,7 +1686,7 @@ function usesPerPath(name, expr) {
1672
1686
  }
1673
1687
  return add(walk(e.object), sum(e.args));
1674
1688
  case "object-literal":
1675
- return sum(e.properties.map((p) => p.value));
1689
+ return sum(e.properties.map((p) => p.kind === "spread" ? p.expr : p.value));
1676
1690
  case "arrow":
1677
1691
  return walk(e.body).max > 0 ? { min: 0, max: Number.POSITIVE_INFINITY } : { min: 0, max: 0 };
1678
1692
  }
@@ -1738,7 +1752,7 @@ function inlineBinding(expr, name, value) {
1738
1752
  case "object-literal":
1739
1753
  return {
1740
1754
  kind: "object-literal",
1741
- properties: e.properties.map((p) => ({ ...p, value: walk(p.value, enclosing) })),
1755
+ properties: e.properties.map((p) => p.kind === "spread" ? { ...p, expr: walk(p.expr, enclosing) } : { ...p, value: walk(p.value, enclosing) }),
1742
1756
  raw: e.raw
1743
1757
  };
1744
1758
  case "literal":
@@ -1971,7 +1985,7 @@ function materializeGetterCalls(expr, names) {
1971
1985
  return {
1972
1986
  kind: "object-literal",
1973
1987
  raw: expr.raw,
1974
- properties: expr.properties.map((p) => ({ ...p, value: rw(p.value) }))
1988
+ properties: expr.properties.map((p) => p.kind === "spread" ? { ...p, expr: rw(p.expr) } : { ...p, value: rw(p.value) })
1975
1989
  };
1976
1990
  case "arrow":
1977
1991
  return { kind: "arrow", params: expr.params, body: rw(expr.body) };
@@ -2029,7 +2043,7 @@ function freeVarsInBody(body, params) {
2029
2043
  return;
2030
2044
  case "object-literal":
2031
2045
  for (const p of e.properties)
2032
- visit(p.value, bound);
2046
+ visit(p.kind === "spread" ? p.expr : p.value, bound);
2033
2047
  return;
2034
2048
  case "array-method":
2035
2049
  if (e.method === "includes" || e.method === "join") {
@@ -2104,7 +2118,7 @@ function freeIdentifiers(expr) {
2104
2118
  return true;
2105
2119
  case "object-literal":
2106
2120
  for (const p of e.properties)
2107
- if (!visit(p.value, bound))
2121
+ if (!visit(p.kind === "spread" ? p.expr : p.value, bound))
2108
2122
  return false;
2109
2123
  return true;
2110
2124
  case "arrow": {
@@ -2251,10 +2265,17 @@ function toEvalNode(e) {
2251
2265
  case "object-literal": {
2252
2266
  const properties = [];
2253
2267
  for (const p of e.properties) {
2268
+ if (p.kind === "spread") {
2269
+ const spreadExpr = toEvalNode(p.expr);
2270
+ if (!spreadExpr)
2271
+ return null;
2272
+ properties.push({ kind: "spread", expr: spreadExpr });
2273
+ continue;
2274
+ }
2254
2275
  const value = toEvalNode(p.value);
2255
2276
  if (!value)
2256
2277
  return null;
2257
- properties.push({ key: p.key, value });
2278
+ properties.push({ kind: "prop", key: p.key, value });
2258
2279
  }
2259
2280
  return { kind: "object-literal", properties };
2260
2281
  }
@@ -10114,6 +10135,8 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
10114
10135
  let tz = null;
10115
10136
  const probeOptions = {};
10116
10137
  for (const prop of options.properties) {
10138
+ if (prop.kind === "spread")
10139
+ return null;
10117
10140
  if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
10118
10141
  return null;
10119
10142
  const value = String(prop.value.value);
@@ -11879,7 +11902,10 @@ function resolveCallbackMethodFunctionReferences(expr, analyzer, bound = EMPTY_B
11879
11902
  ...e.method === "flat" && e.depthExpr ? { depthExpr: visit2(e.depthExpr, bound2) } : {}
11880
11903
  };
11881
11904
  case "object-literal":
11882
- return { ...e, properties: e.properties.map((p) => ({ ...p, value: visit2(p.value, bound2) })) };
11905
+ return {
11906
+ ...e,
11907
+ properties: e.properties.map((p) => p.kind === "spread" ? { ...p, expr: visit2(p.expr, bound2) } : { ...p, value: visit2(p.value, bound2) })
11908
+ };
11883
11909
  case "arrow": {
11884
11910
  const inner = e.params.length === 0 ? bound2 : new Set([...bound2, ...e.params]);
11885
11911
  return { ...e, body: visit2(e.body, inner) };
@@ -23480,12 +23506,38 @@ function evalNode(node, ctx) {
23480
23506
  const baseResult = evalNode(node.expression, ctx);
23481
23507
  if (baseResult === undefined)
23482
23508
  return;
23483
- return UNRESOLVED;
23509
+ if (baseResult === UNRESOLVED || baseResult === null || typeof baseResult !== "object") {
23510
+ return UNRESOLVED;
23511
+ }
23512
+ const proto = Object.getPrototypeOf(baseResult);
23513
+ if (proto !== Object.prototype && proto !== null)
23514
+ return UNRESOLVED;
23515
+ const key = node.name.text;
23516
+ return Object.prototype.hasOwnProperty.call(baseResult, key) ? baseResult[key] : undefined;
23484
23517
  }
23485
23518
  if (ts22.isCallExpression(node)) {
23486
23519
  if (node.arguments.length === 0 && ts22.isIdentifier(node.expression) && node.expression.text in ctx.bindings) {
23487
23520
  return ctx.bindings[node.expression.text];
23488
23521
  }
23522
+ if (ts22.isPropertyAccessExpression(node.expression) && node.expression.name.text === "map" && node.arguments.length === 1) {
23523
+ const arrow = node.arguments[0];
23524
+ if (ts22.isArrowFunction(arrow) && arrow.parameters.length === 1 && ts22.isIdentifier(arrow.parameters[0].name) && !ts22.isBlock(arrow.body)) {
23525
+ const recv = evalNode(node.expression.expression, ctx);
23526
+ if (Array.isArray(recv)) {
23527
+ const paramName = arrow.parameters[0].name.text;
23528
+ const mapped = [];
23529
+ for (const item of recv) {
23530
+ const localBindings = { ...ctx.bindings, [paramName]: item };
23531
+ const v = evalNode(arrow.body, { ...ctx, bindings: localBindings });
23532
+ if (v === UNRESOLVED)
23533
+ return UNRESOLVED;
23534
+ mapped.push(v === undefined ? null : v);
23535
+ }
23536
+ return mapped;
23537
+ }
23538
+ }
23539
+ return UNRESOLVED;
23540
+ }
23489
23541
  if (ts22.isPropertyAccessExpression(node.expression) && node.expression.name.text === "join") {
23490
23542
  const recv = evalNode(node.expression.expression, ctx);
23491
23543
  if (Array.isArray(recv)) {
@@ -23989,7 +24041,7 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
23989
24041
 
23990
24042
  // src/ssr-seed-plan.ts
23991
24043
  function classify2(name, origin, expr, parsed, available) {
23992
- if (!isSupported(parsed).supported)
24044
+ if (!isSupportedValue(parsed).supported)
23993
24045
  return { kind: "opaque", name, origin };
23994
24046
  const frees = freeIdentifiers(parsed);
23995
24047
  if (frees === null)
@@ -24052,7 +24104,7 @@ function computeSsrSeedPlan(metadata) {
24052
24104
  }
24053
24105
  }
24054
24106
  const expr = signal.initialValue.trim();
24055
- steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify2(signal.getter, "signal", expr, resolveThroughLocalConsts(parseExpression(expr), localConsts), available));
24107
+ steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify2(signal.getter, "signal", expr, resolveThroughLocalConsts(signal.parsed ?? parseExpression(`(${expr})`), localConsts), available));
24056
24108
  available.add(signal.getter);
24057
24109
  }
24058
24110
  for (const memo of metadata.memos) {
@@ -24216,7 +24268,7 @@ function checkExpr(expr, loc, meta, bindings, matchers, errors, seen) {
24216
24268
  break;
24217
24269
  case "object-literal":
24218
24270
  for (const prop of expr.properties)
24219
- recurse(prop.value);
24271
+ recurse(prop.kind === "spread" ? prop.expr : prop.value);
24220
24272
  break;
24221
24273
  case "array-method":
24222
24274
  recurse(expr.object);
@@ -25599,6 +25651,26 @@ function isStringConcatBinary(op, left, right, isStringName) {
25599
25651
  function groupBinaryOperand(operand, emitted) {
25600
25652
  return operand.kind === "binary" || operand.kind === "logical" || operand.kind === "conditional" ? `(${emitted})` : emitted;
25601
25653
  }
25654
+ function groupObjectLiteralSegments(properties, literalOf, emit) {
25655
+ const segments = [];
25656
+ let run = [];
25657
+ const flushRun = () => {
25658
+ if (run.length > 0) {
25659
+ segments.push(literalOf(run));
25660
+ run = [];
25661
+ }
25662
+ };
25663
+ for (const p of properties) {
25664
+ if (p.kind === "spread") {
25665
+ flushRun();
25666
+ segments.push(emit(p.expr));
25667
+ } else {
25668
+ run.push(p);
25669
+ }
25670
+ }
25671
+ flushRun();
25672
+ return segments;
25673
+ }
25602
25674
  function emitParsedExpr(expr, emitter) {
25603
25675
  const emit = (child) => emitParsedExpr(child, emitter);
25604
25676
  switch (expr.kind) {
@@ -25713,72 +25785,6 @@ function collectLoopBoundNames(ir) {
25713
25785
  visit3(ir.root);
25714
25786
  return names;
25715
25787
  }
25716
- // src/signal-init-eval.ts
25717
- var BLOCKED_GLOBALS = [
25718
- "globalThis",
25719
- "window",
25720
- "document",
25721
- "Date",
25722
- "Math",
25723
- "crypto",
25724
- "performance",
25725
- "fetch",
25726
- "setTimeout",
25727
- "setInterval",
25728
- "require",
25729
- "process",
25730
- "Function"
25731
- ];
25732
- function isTransportable(value, ancestors = new Set) {
25733
- if (value === null)
25734
- return true;
25735
- const t = typeof value;
25736
- if (t === "boolean" || t === "number" || t === "string")
25737
- return true;
25738
- if (t !== "object")
25739
- return false;
25740
- if (ancestors.has(value))
25741
- return false;
25742
- ancestors.add(value);
25743
- try {
25744
- if (Array.isArray(value)) {
25745
- if (Object.keys(value).length !== value.length)
25746
- return false;
25747
- return value.every((el) => el !== undefined && isTransportable(el, ancestors));
25748
- }
25749
- const proto = Object.getPrototypeOf(value);
25750
- if (proto !== Object.prototype && proto !== null)
25751
- return false;
25752
- return Object.values(value).every((v) => isTransportable(v, ancestors));
25753
- } finally {
25754
- ancestors.delete(value);
25755
- }
25756
- }
25757
- function tryEvaluateSignalInit(expr, props) {
25758
- const src = expr.trim();
25759
- if (src === "")
25760
- return { ok: false };
25761
- let fn;
25762
- try {
25763
- fn = new Function("props", ...BLOCKED_GLOBALS, `'use strict'; return (
25764
- ${src}
25765
- );`);
25766
- } catch {
25767
- return { ok: false };
25768
- }
25769
- try {
25770
- const value = fn(props ?? {});
25771
- if (value === undefined)
25772
- return { ok: true, value: undefined };
25773
- return isTransportable(value) ? { ok: true, value } : { ok: false };
25774
- } catch {
25775
- return { ok: false };
25776
- }
25777
- }
25778
- function evaluateSignalInit(expr, props) {
25779
- const result = tryEvaluateSignalInit(expr, props);
25780
- return result.ok && result.value !== undefined ? result.value : null;
25781
- }
25782
25788
  // src/static-literal.ts
25783
25789
  function evaluateStaticLiteral(expr, bindings) {
25784
25790
  switch (expr.kind) {
@@ -25811,6 +25817,17 @@ function evaluateStaticLiteral(expr, bindings) {
25811
25817
  case "object-literal": {
25812
25818
  const out = {};
25813
25819
  for (const prop of expr.properties) {
25820
+ if (prop.kind === "spread") {
25821
+ const resolved2 = evaluateStaticLiteral(prop.expr, bindings);
25822
+ if (!resolved2)
25823
+ return null;
25824
+ if (resolved2.value === null || resolved2.value === undefined)
25825
+ continue;
25826
+ if (typeof resolved2.value !== "object" || Array.isArray(resolved2.value))
25827
+ return null;
25828
+ Object.assign(out, resolved2.value);
25829
+ continue;
25830
+ }
25814
25831
  const resolved = evaluateStaticLiteral(prop.value, bindings);
25815
25832
  if (!resolved)
25816
25833
  return null;
@@ -25880,6 +25897,8 @@ function matchQueryHrefCall(callee, args, localNames) {
25880
25897
  return null;
25881
25898
  const triples = [];
25882
25899
  for (const p of obj.properties) {
25900
+ if (p.kind === "spread")
25901
+ return null;
25883
25902
  const v = p.value;
25884
25903
  if (v.kind === "conditional" && isOmitBranch(v.alternate)) {
25885
25904
  triples.push({ guard: v.test, key: p.key, value: v.consequent });
@@ -26067,6 +26086,8 @@ function htmlPropValue(parsed) {
26067
26086
  if (parsed.properties.length !== 1)
26068
26087
  return null;
26069
26088
  const [prop] = parsed.properties;
26089
+ if (prop.kind === "spread")
26090
+ return null;
26070
26091
  if (prop.key !== "__html")
26071
26092
  return null;
26072
26093
  return prop.value;
@@ -28950,7 +28971,6 @@ function profileToJSON(profile) {
28950
28971
  registerBuiltinLoweringPlugins();
28951
28972
  export {
28952
28973
  tsNodeToParsedExpr,
28953
- tryEvaluateSignalInit,
28954
28974
  traceUpdatePath,
28955
28975
  testAdapter,
28956
28976
  stringifyParsedExpr,
@@ -28992,6 +29012,7 @@ export {
28992
29012
  joinProfilerEvents,
28993
29013
  isValueReferenceIdentifier,
28994
29014
  isValidHelperId,
29015
+ isSupportedValue,
28995
29016
  isSupported,
28996
29017
  isStringTypedOperand,
28997
29018
  isStringConcatBinary,
@@ -29004,6 +29025,7 @@ export {
29004
29025
  importsSearchParams,
29005
29026
  identifierPath,
29006
29027
  hasUnsafeStyleValue,
29028
+ groupObjectLiteralSegments,
29007
29029
  groupBinaryOperand,
29008
29030
  graphToJSON,
29009
29031
  getLoweringPlugins,
@@ -29043,7 +29065,6 @@ export {
29043
29065
  extractArrowBodyExpression,
29044
29066
  exprToString,
29045
29067
  evaluateStaticLiteral,
29046
- evaluateSignalInit,
29047
29068
  evaluateProfileGates,
29048
29069
  evalStringArrayJoin,
29049
29070
  envSignalReaderFor,
@@ -1 +1 @@
1
- {"version":3,"file":"query-href-lowering.d.ts","sourceRoot":"","sources":["../src/query-href-lowering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACxB,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,EAAE,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,aAAa,GAAG,IAAI,CAkBtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAAG,MAAM,EAAE,CAoBzF"}
1
+ {"version":3,"file":"query-href-lowering.d.ts","sourceRoot":"","sources":["../src/query-href-lowering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACxB,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,EAAE,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,aAAa,GAAG,IAAI,CAsBtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAAG,MAAM,EAAE,CAoBzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-seed-plan.d.ts","sourceRoot":"","sources":["../src/ssr-seed-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACnF,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/G;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAA;CAAE,CAAA;AAE/D,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AA4FD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CAqDpE"}
1
+ {"version":3,"file":"ssr-seed-plan.d.ts","sourceRoot":"","sources":["../src/ssr-seed-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACnF,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/G;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAA;CAAE,CAAA;AAE/D,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AAiGD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CAiEpE"}
@@ -1 +1 @@
1
- {"version":3,"file":"static-literal.d.ts","sourceRoot":"","sources":["../src/static-literal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,EAChB,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA+D3B;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAE9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,UAAU,GAAG,SAAS,EACnC,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,SAAS,EACvD,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACpD,OAAO,EAAE,GAAG,IAAI,CAYlB"}
1
+ {"version":3,"file":"static-literal.d.ts","sourceRoot":"","sources":["../src/static-literal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,EAChB,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA6E3B;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAE9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,UAAU,GAAG,SAAS,EACnC,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,SAAS,EACvD,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACpD,OAAO,EAAE,GAAG,IAAI,CAYlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"to-locale-date-lowering.d.ts","sourceRoot":"","sources":["../src/to-locale-date-lowering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,YAAY,CAAA;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAI1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,QAA4C,CAAA;AAKxE;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWhE;AASD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CACvB;AAmFD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,gBAAgB,GAAG,IAAI,CAOzB;AA0PD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,QAAQ,EAAE,UAAU,GACnB,YAAY,GAAG,IAAI,CAyErB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBtF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAMhC,CAAA"}
1
+ {"version":3,"file":"to-locale-date-lowering.d.ts","sourceRoot":"","sources":["../src/to-locale-date-lowering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,YAAY,CAAA;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAI1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,QAA4C,CAAA;AAKxE;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWhE;AASD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CACvB;AAmFD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,gBAAgB,GAAG,IAAI,CAOzB;AA0PD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,QAAQ,EAAE,UAAU,GACnB,YAAY,GAAG,IAAI,CA6ErB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBtF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAMhC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/jsx",
3
- "version": "0.31.10",
3
+ "version": "0.33.0",
4
4
  "description": "JSX compiler for BarefootJS - transforms JSX to server HTML + client JS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "directory": "packages/jsx"
50
50
  },
51
51
  "dependencies": {
52
- "@barefootjs/shared": "0.31.10"
52
+ "@barefootjs/shared": "0.33.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@barefootjs/client": ">=0.2.0",
@@ -1,6 +1,6 @@
1
1
  import { describe, test, expect } from 'bun:test'
2
2
  import ts from 'typescript'
3
- import { parseExpression, isSupported, exprToString, stringifyParsedExpr, parseBlockBody, foldBlockToExpr, extractArrowBodyExpression, parseStyleObjectEntries, parseProviderObjectLiteral, asCallbackMethodCall, containsHigherOrder } from '../expression-parser'
3
+ import { parseExpression, isSupported, isSupportedValue, exprToString, stringifyParsedExpr, parseBlockBody, foldBlockToExpr, extractArrowBodyExpression, parseStyleObjectEntries, parseProviderObjectLiteral, asCallbackMethodCall, containsHigherOrder } from '../expression-parser'
4
4
  import { collectAllTypeRanges, reconstructWithoutTypes } from '../strip-types'
5
5
 
6
6
  describe('expression-parser', () => {
@@ -395,12 +395,23 @@ describe('expression-parser', () => {
395
395
  expect((parseExpression('true') as { raw?: string }).raw).toBeUndefined()
396
396
  })
397
397
 
398
- test('falls through to unsupported for spread / computed-key object literals', () => {
399
- // A spread member is not a plain map property preserves the
400
- // pre-A-1 `unsupported` behaviour (byte-identical).
401
- expect(parseExpression('({ ...rest, a: 1 })').kind).toBe('unsupported')
402
- // Computed key `[k]: v` can't resolve to a static name.
398
+ test('falls through to unsupported for computed-key object literals; spread parses as object-literal (#2696 Step 2)', () => {
399
+ // Computed key `[k]: v` can't resolve to a static namestill
400
+ // unsupported.
403
401
  expect(parseExpression('({ [k]: 1 })').kind).toBe('unsupported')
402
+ // A spread member (#2696 Step 2) is now a structured `{ kind:
403
+ // 'spread', expr }` entry, in source order alongside any `prop`
404
+ // entries — no longer the pre-#2696 blanket `unsupported`.
405
+ const spread = parseExpression('({ ...rest, a: 1 })')
406
+ expect(spread).toMatchObject({
407
+ kind: 'object-literal',
408
+ properties: [
409
+ { kind: 'spread', expr: { kind: 'identifier', name: 'rest' } },
410
+ { kind: 'prop', key: 'a', shorthand: false, value: { kind: 'literal', value: 1 } },
411
+ ],
412
+ })
413
+ // A method / getter / setter still isn't a plain map — still refuses.
414
+ expect(parseExpression('({ f() { return 1 } })').kind).toBe('unsupported')
404
415
  })
405
416
 
406
417
  test('a bare `{ … }` at statement position is a block, not an object literal', () => {
@@ -1402,6 +1413,32 @@ describe('expression-parser', () => {
1402
1413
  expect(result.supported).toBe(false)
1403
1414
  })
1404
1415
 
1416
+ // #2696 review: `pos` propagates by pure inheritance, with no per-shape
1417
+ // forcing to `value` — a RENDERED entry point's behaviour must stay
1418
+ // byte-identical to before `isSupportedValue` existed, on every shape
1419
+ // that reaches a container-contents position (an array-literal element,
1420
+ // an array-method/callback receiver, a callback body). `isSupportedValue`
1421
+ // admits the identical trees, since the whole tree it's entered on is a
1422
+ // `value` position (a signal/memo initializer).
1423
+ test('a populated object-literal array-literal element is refused at RENDERED, supported at VALUE', () => {
1424
+ const expr = parseExpression('[{ a: 1 }]')
1425
+ expect(expr.kind).toBe('array-literal')
1426
+ expect(isSupported(expr).supported).toBe(false)
1427
+ expect(isSupportedValue(expr).supported).toBe(true)
1428
+ })
1429
+
1430
+ test('an array-of-objects .join() receiver is refused at RENDERED, supported at VALUE', () => {
1431
+ const expr = parseExpression("[{ a: 1 }].join(',')")
1432
+ expect(isSupported(expr).supported).toBe(false)
1433
+ expect(isSupportedValue(expr).supported).toBe(true)
1434
+ })
1435
+
1436
+ test('a .map() callback body returning an object literal is refused at RENDERED, supported at VALUE', () => {
1437
+ const expr = parseExpression('items.map(t => ({ a: t }))')
1438
+ expect(isSupported(expr).supported).toBe(false)
1439
+ expect(isSupportedValue(expr).supported).toBe(true)
1440
+ })
1441
+
1405
1442
  test('L5: filter() with simple predicate IS supported', () => {
1406
1443
  const expr = parseExpression('items().filter(x => x.done)')
1407
1444
  const result = isSupported(expr)
@@ -98,12 +98,26 @@ describe('serializeParsedExpr', () => {
98
98
  })
99
99
  })
100
100
 
101
- test('map body: object literal carries key + value (no keyKind / shorthand)', () => {
101
+ test('map body: object literal carries kind + key + value (no keyKind / shorthand)', () => {
102
+ // #2696 Step 2: each entry now carries its own `kind` tag ('prop' here —
103
+ // no source spread in this body) so the evaluator can distinguish a
104
+ // `prop` from a `spread` entry without re-deriving it from field shape.
102
105
  expect(evalJSON('({ id: item.id, n: item.n })')).toEqual({
103
106
  kind: 'object-literal',
104
107
  properties: [
105
- { key: 'id', value: { kind: 'member', object: { kind: 'identifier', name: 'item' }, property: 'id' } },
106
- { key: 'n', value: { kind: 'member', object: { kind: 'identifier', name: 'item' }, property: 'n' } },
108
+ { kind: 'prop', key: 'id', value: { kind: 'member', object: { kind: 'identifier', name: 'item' }, property: 'id' } },
109
+ { kind: 'prop', key: 'n', value: { kind: 'member', object: { kind: 'identifier', name: 'item' }, property: 'n' } },
110
+ ],
111
+ })
112
+ })
113
+
114
+ test('map body: object literal carries a spread entry as `{ kind: "spread", expr }`', () => {
115
+ // The todo-app motivating shape (#2696): `{ ...t, editing: false }`.
116
+ expect(evalJSON("({ ...t, editing: false })")).toEqual({
117
+ kind: 'object-literal',
118
+ properties: [
119
+ { kind: 'spread', expr: { kind: 'identifier', name: 't' } },
120
+ { kind: 'prop', key: 'editing', value: { kind: 'literal', value: false } },
107
121
  ],
108
122
  })
109
123
  })