olova 2.0.72 → 2.0.74

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/compiler.js CHANGED
@@ -1,4 +1,4 @@
1
- import { parse } from '@babel/parser';
1
+ import { parse, parseExpression } from '@babel/parser';
2
2
  import generatorModule from '@babel/generator';
3
3
  import traverseModule from '@babel/traverse';
4
4
  import * as t from '@babel/types';
@@ -655,6 +655,33 @@ function parseModule(code) {
655
655
  plugins: ["typescript", "jsx"]
656
656
  });
657
657
  }
658
+ function parseStandaloneExpression(code) {
659
+ return parseExpression(code, {
660
+ plugins: ["typescript", "jsx"]
661
+ });
662
+ }
663
+ function createExpressionFile(expression) {
664
+ return t.file(
665
+ t.program([
666
+ t.expressionStatement(t.cloneNode(expression, true))
667
+ ])
668
+ );
669
+ }
670
+ function nodeHasJsx(node) {
671
+ if (!node) {
672
+ return false;
673
+ }
674
+ let found = false;
675
+ t.traverseFast(node, (child) => {
676
+ if (found) {
677
+ return;
678
+ }
679
+ if (t.isJSXElement(child) || t.isJSXFragment(child)) {
680
+ found = true;
681
+ }
682
+ });
683
+ return found;
684
+ }
658
685
  function runCompilePhase(phase, fn) {
659
686
  try {
660
687
  return fn();
@@ -1022,25 +1049,7 @@ function collectScriptInfo(script) {
1022
1049
  });
1023
1050
  addNestedMutationNotify(ast, stateNames);
1024
1051
  const nodeContainsJsx = (node) => {
1025
- if (!node) {
1026
- return false;
1027
- }
1028
- let found = false;
1029
- try {
1030
- const generated = generate2(node).code;
1031
- const code = t.isBlockStatement(node) ? `(() => ${generated})` : `(${generated})`;
1032
- traverse(parseModule(code), {
1033
- JSXElement() {
1034
- found = true;
1035
- },
1036
- JSXFragment() {
1037
- found = true;
1038
- }
1039
- });
1040
- } catch {
1041
- found = false;
1042
- }
1043
- return found;
1052
+ return nodeHasJsx(node);
1044
1053
  };
1045
1054
  traverse(ast, {
1046
1055
  FunctionDeclaration(path) {
@@ -1062,12 +1071,8 @@ function collectScriptInfo(script) {
1062
1071
  allSignalNames,
1063
1072
  jsxFunctionNames
1064
1073
  );
1065
- const parsed = parseModule(`(${transformed.code})`);
1066
- const statement = parsed.program.body[0];
1067
- if (statement && t.isExpressionStatement(statement)) {
1068
- path.replaceWith(statement.expression);
1069
- path.skip();
1070
- }
1074
+ path.replaceWith(parseStandaloneExpression(transformed.code));
1075
+ path.skip();
1071
1076
  },
1072
1077
  JSXFragment(path) {
1073
1078
  const transformed = transformExpression(
@@ -1076,12 +1081,8 @@ function collectScriptInfo(script) {
1076
1081
  allSignalNames,
1077
1082
  jsxFunctionNames
1078
1083
  );
1079
- const parsed = parseModule(`(${transformed.code})`);
1080
- const statement = parsed.program.body[0];
1081
- if (statement && t.isExpressionStatement(statement)) {
1082
- path.replaceWith(statement.expression);
1083
- path.skip();
1084
- }
1084
+ path.replaceWith(parseStandaloneExpression(transformed.code));
1085
+ path.skip();
1085
1086
  }
1086
1087
  });
1087
1088
  if (needsGlobalRuntime) {
@@ -1115,7 +1116,7 @@ function collectScriptInfo(script) {
1115
1116
  };
1116
1117
  }
1117
1118
  function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames = /* @__PURE__ */ new Set(), scopeId) {
1118
- const ast = parseModule(`(${expr})`);
1119
+ const ast = createExpressionFile(parseStandaloneExpression(expr));
1119
1120
  let containsJsx = false;
1120
1121
  const jsxScopeAttr = createScopeAttr(scopeId);
1121
1122
  traverse(ast, {
@@ -1177,16 +1178,7 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
1177
1178
  const toStringCall = (input) => t.callExpression(t.identifier("__olovaToString"), [input]);
1178
1179
  const escapeCall = (input) => t.callExpression(t.identifier("__olovaEscape"), [input]);
1179
1180
  const expressionContainsJsx = (input) => {
1180
- let found = false;
1181
- traverse(parseModule(`(${generate2(input).code})`), {
1182
- JSXElement() {
1183
- found = true;
1184
- },
1185
- JSXFragment() {
1186
- found = true;
1187
- }
1188
- });
1189
- return found;
1181
+ return nodeHasJsx(input);
1190
1182
  };
1191
1183
  const expressionProducesHtml = (input) => {
1192
1184
  if (!input) {
@@ -1196,19 +1188,18 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
1196
1188
  return true;
1197
1189
  }
1198
1190
  let found = false;
1199
- traverse(parseModule(`(${generate2(input).code})`), {
1200
- CallExpression(path) {
1201
- if (t.isIdentifier(path.node.callee) && jsxFunctionNames.has(path.node.callee.name)) {
1191
+ t.traverseFast(input, (child) => {
1192
+ if (found || !t.isCallExpression(child)) {
1193
+ return;
1194
+ }
1195
+ if (t.isIdentifier(child.callee) && jsxFunctionNames.has(child.callee.name)) {
1196
+ found = true;
1197
+ return;
1198
+ }
1199
+ if (t.isMemberExpression(child.callee) && t.isIdentifier(child.callee.property) && (child.callee.property.name === "map" || child.callee.property.name === "flatMap")) {
1200
+ const [firstArg] = child.arguments;
1201
+ if (t.isIdentifier(firstArg) && jsxFunctionNames.has(firstArg.name) || nodeContainsRenderableFunction(firstArg, jsxFunctionNames)) {
1202
1202
  found = true;
1203
- path.stop();
1204
- return;
1205
- }
1206
- if (t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.property) && (path.node.callee.property.name === "map" || path.node.callee.property.name === "flatMap")) {
1207
- const [firstArg] = path.node.arguments;
1208
- if (t.isIdentifier(firstArg) && jsxFunctionNames.has(firstArg.name) || nodeContainsRenderableFunction(firstArg, jsxFunctionNames)) {
1209
- found = true;
1210
- path.stop();
1211
- }
1212
1203
  }
1213
1204
  }
1214
1205
  });
@@ -1251,7 +1242,7 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
1251
1242
  return walkMember(name);
1252
1243
  };
1253
1244
  const transformEmbeddedJsxExpression = (input) => {
1254
- const wrapped = parseModule(`(${generate2(input).code})`);
1245
+ const wrapped = createExpressionFile(input);
1255
1246
  let containsNestedJsx = false;
1256
1247
  traverse(wrapped, {
1257
1248
  JSXElement(path) {
@@ -1434,17 +1425,16 @@ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames
1434
1425
  }
1435
1426
  });
1436
1427
  const statement = ast.program.body[0];
1437
- if (!statement || !t.isExpressionStatement(statement)) {
1428
+ const expression = statement && t.isExpressionStatement(statement) ? statement.expression : null;
1429
+ if (!expression) {
1438
1430
  return { code: expr.trim(), containsJsx };
1439
1431
  }
1440
- containsJsx = containsJsx || expressionProducesHtml(statement.expression);
1441
- return { code: generate2(statement.expression).code, containsJsx };
1432
+ containsJsx = containsJsx || expressionProducesHtml(expression);
1433
+ return { code: generate2(expression).code, containsJsx };
1442
1434
  }
1443
1435
  function parseExpressionNode(expr) {
1444
1436
  try {
1445
- const ast = parseModule(`(${expr})`);
1446
- const statement = ast.program.body[0];
1447
- return statement && t.isExpressionStatement(statement) ? statement.expression : null;
1437
+ return parseStandaloneExpression(expr);
1448
1438
  } catch {
1449
1439
  return null;
1450
1440
  }
@@ -1514,11 +1504,41 @@ function jsxToTemplateHtml(node) {
1514
1504
  return "";
1515
1505
  }
1516
1506
  function replaceTemplateExpressions(input, replacer) {
1507
+ const rawTextTags = /* @__PURE__ */ new Set(["pre", "code", "textarea", "kbd", "samp"]);
1517
1508
  let output = "";
1518
1509
  let index = 0;
1519
1510
  let inTag = false;
1520
1511
  let tagQuote = null;
1521
1512
  let tagBraceDepth = 0;
1513
+ let tagStart = -1;
1514
+ const rawTextStack = [];
1515
+ const updateRawTextStack = (rawTag) => {
1516
+ const trimmed = rawTag.trim();
1517
+ if (!trimmed || trimmed.startsWith("!") || trimmed.startsWith("?")) {
1518
+ return;
1519
+ }
1520
+ const closing = trimmed.startsWith("/");
1521
+ const content = closing ? trimmed.slice(1).trim() : trimmed;
1522
+ const selfClosing = /\/\s*$/.test(content);
1523
+ const nameMatch = content.match(/^([A-Za-z][A-Za-z0-9:-]*)/);
1524
+ if (!nameMatch) {
1525
+ return;
1526
+ }
1527
+ const tagName = nameMatch[1].toLowerCase();
1528
+ if (!rawTextTags.has(tagName)) {
1529
+ return;
1530
+ }
1531
+ if (closing) {
1532
+ const stackIndex = rawTextStack.lastIndexOf(tagName);
1533
+ if (stackIndex >= 0) {
1534
+ rawTextStack.splice(stackIndex, 1);
1535
+ }
1536
+ return;
1537
+ }
1538
+ if (!selfClosing) {
1539
+ rawTextStack.push(tagName);
1540
+ }
1541
+ };
1522
1542
  while (index < input.length) {
1523
1543
  const char = input[index];
1524
1544
  const prev = index > 0 ? input[index - 1] : "";
@@ -1539,12 +1559,19 @@ function replaceTemplateExpressions(input, replacer) {
1539
1559
  tagBraceDepth -= 1;
1540
1560
  } else if (char === ">" && tagBraceDepth === 0) {
1541
1561
  inTag = false;
1562
+ updateRawTextStack(input.slice(tagStart + 1, index));
1542
1563
  }
1543
1564
  index += 1;
1544
1565
  continue;
1545
1566
  }
1546
1567
  if (char === "<") {
1547
1568
  inTag = true;
1569
+ tagStart = index;
1570
+ output += char;
1571
+ index += 1;
1572
+ continue;
1573
+ }
1574
+ if (rawTextStack.length > 0) {
1548
1575
  output += char;
1549
1576
  index += 1;
1550
1577
  continue;
@@ -1632,90 +1659,75 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
1632
1659
  const ifBindings = [];
1633
1660
  html = replaceTemplateExpressions(html, (expr) => {
1634
1661
  try {
1635
- const ast = parseModule(`(${expr})`);
1636
- const exprNode = ast.program.body[0];
1637
- if (t.isExpressionStatement(exprNode)) {
1638
- const node = exprNode.expression;
1639
- const containsJsxNode = (n) => {
1640
- let found = false;
1641
- traverse(parseModule(`(${generate2(n).code})`), {
1642
- JSXElement() {
1643
- found = true;
1644
- },
1645
- JSXFragment() {
1646
- found = true;
1647
- }
1662
+ const node = parseStandaloneExpression(expr);
1663
+ const containsJsxNode = (n) => nodeHasJsx(n);
1664
+ if (t.isLogicalExpression(node) && node.operator === "&&") {
1665
+ if (containsJsxNode(node.right)) {
1666
+ const id = `i${counters.if++}`;
1667
+ const condition = generate2(node.left).code;
1668
+ const trueBranchHtml = jsxToTemplateHtml(node.right);
1669
+ ifBindings.push({
1670
+ id,
1671
+ expr: transformExpression(
1672
+ condition,
1673
+ stateNames,
1674
+ allSignalNames,
1675
+ jsxFunctionNames,
1676
+ scopeId
1677
+ ).code,
1678
+ trueBranch: transformTemplate(
1679
+ trueBranchHtml,
1680
+ stateNames,
1681
+ allSignalNames,
1682
+ jsxFunctionNames,
1683
+ componentNames,
1684
+ counters,
1685
+ scopeId,
1686
+ devWarnings,
1687
+ filename
1688
+ )
1648
1689
  });
1649
- return found;
1650
- };
1651
- if (t.isLogicalExpression(node) && node.operator === "&&") {
1652
- if (containsJsxNode(node.right)) {
1653
- const id = `i${counters.if++}`;
1654
- const condition = generate2(node.left).code;
1655
- const trueBranchHtml = jsxToTemplateHtml(node.right);
1656
- ifBindings.push({
1657
- id,
1658
- expr: transformExpression(
1659
- condition,
1660
- stateNames,
1661
- allSignalNames,
1662
- jsxFunctionNames,
1663
- scopeId
1664
- ).code,
1665
- trueBranch: transformTemplate(
1666
- trueBranchHtml,
1667
- stateNames,
1668
- allSignalNames,
1669
- jsxFunctionNames,
1670
- componentNames,
1671
- counters,
1672
- scopeId,
1673
- devWarnings,
1674
- filename
1675
- )
1676
- });
1677
- return `__O_IF_${id}__`;
1678
- }
1679
- } else if (t.isConditionalExpression(node)) {
1680
- if (containsJsxNode(node.consequent) || containsJsxNode(node.alternate)) {
1681
- const id = `i${counters.if++}`;
1682
- const condition = generate2(node.test).code;
1683
- const trueBranchHtml = containsJsxNode(node.consequent) ? jsxToTemplateHtml(node.consequent) : `{${generate2(node.consequent).code}}`;
1684
- const falseBranchHtml = containsJsxNode(node.alternate) ? jsxToTemplateHtml(node.alternate) : `{${generate2(node.alternate).code}}`;
1685
- ifBindings.push({
1686
- id,
1687
- expr: transformExpression(
1688
- condition,
1689
- stateNames,
1690
- allSignalNames,
1691
- jsxFunctionNames,
1692
- scopeId
1693
- ).code,
1694
- trueBranch: transformTemplate(
1695
- trueBranchHtml,
1696
- stateNames,
1697
- allSignalNames,
1698
- jsxFunctionNames,
1699
- componentNames,
1700
- counters,
1701
- scopeId,
1702
- devWarnings,
1703
- filename
1704
- ),
1705
- falseBranch: transformTemplate(
1706
- falseBranchHtml,
1707
- stateNames,
1708
- allSignalNames,
1709
- jsxFunctionNames,
1710
- componentNames,
1711
- counters,
1712
- scopeId,
1713
- devWarnings,
1714
- filename
1715
- )
1716
- });
1717
- return `__O_IF_${id}__`;
1718
- }
1690
+ return `__O_IF_${id}__`;
1691
+ }
1692
+ } else if (t.isConditionalExpression(node)) {
1693
+ if (containsJsxNode(node.consequent) || containsJsxNode(node.alternate)) {
1694
+ const id = `i${counters.if++}`;
1695
+ const condition = generate2(node.test).code;
1696
+ const trueBranchHtml = containsJsxNode(node.consequent) ? jsxToTemplateHtml(node.consequent) : `{${generate2(node.consequent).code}}`;
1697
+ const falseBranchHtml = containsJsxNode(node.alternate) ? jsxToTemplateHtml(node.alternate) : `{${generate2(node.alternate).code}}`;
1698
+ ifBindings.push({
1699
+ id,
1700
+ expr: transformExpression(
1701
+ condition,
1702
+ stateNames,
1703
+ allSignalNames,
1704
+ jsxFunctionNames,
1705
+ scopeId
1706
+ ).code,
1707
+ trueBranch: transformTemplate(
1708
+ trueBranchHtml,
1709
+ stateNames,
1710
+ allSignalNames,
1711
+ jsxFunctionNames,
1712
+ componentNames,
1713
+ counters,
1714
+ scopeId,
1715
+ devWarnings,
1716
+ filename
1717
+ ),
1718
+ falseBranch: transformTemplate(
1719
+ falseBranchHtml,
1720
+ stateNames,
1721
+ allSignalNames,
1722
+ jsxFunctionNames,
1723
+ componentNames,
1724
+ counters,
1725
+ scopeId,
1726
+ devWarnings,
1727
+ filename
1728
+ )
1729
+ });
1730
+ return `__O_IF_${id}__`;
1719
1731
  }
1720
1732
  }
1721
1733
  } catch {
@@ -1861,9 +1873,7 @@ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionName
1861
1873
  const rawValue = node.attrs.fallback;
1862
1874
  if (rawValue.startsWith("{") && rawValue.endsWith("}")) {
1863
1875
  const fallbackExpression = rawValue.slice(1, -1).trim();
1864
- const fallbackAst = parseModule(`(${fallbackExpression})`);
1865
- const fallbackStatement = fallbackAst.program.body[0];
1866
- const fallbackNode = fallbackStatement && t.isExpressionStatement(fallbackStatement) ? fallbackStatement.expression : null;
1876
+ const fallbackNode = parseExpressionNode(fallbackExpression);
1867
1877
  if (fallbackNode && (t.isJSXElement(fallbackNode) || t.isJSXFragment(fallbackNode))) {
1868
1878
  slotFactories.push({
1869
1879
  varName: `__slot_${id}_fallback`,