ripple 0.2.132 → 0.2.133

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.132",
6
+ "version": "0.2.133",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -81,6 +81,6 @@
81
81
  "typescript": "^5.9.2"
82
82
  },
83
83
  "peerDependencies": {
84
- "ripple": "0.2.132"
84
+ "ripple": "0.2.133"
85
85
  }
86
86
  }
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /** @import { Program } from 'estree' */
2
3
  /** @import {
3
4
  * CommentWithLocation,
@@ -28,6 +29,14 @@ function convert_from_jsx(node) {
28
29
  return node;
29
30
  }
30
31
 
32
+ function isWhitespaceTextNode(node) {
33
+ if (!node || node.type !== 'Text') {
34
+ return false;
35
+ }
36
+ const value = typeof node.value === 'string' ? node.value : typeof node.raw === 'string' ? node.raw : '';
37
+ return /^\s*$/.test(value);
38
+ }
39
+
31
40
  /**
32
41
  * Acorn parser plugin for Ripple syntax extensions
33
42
  * @param {RipplePluginConfig} [config] - Plugin configuration
@@ -42,6 +51,37 @@ function RipplePlugin(config) {
42
51
  class RippleParser extends Parser {
43
52
  /** @type {any[]} */
44
53
  #path = [];
54
+ #commentContextId = 0;
55
+
56
+ #createCommentMetadata() {
57
+ if (this.#path.length === 0) {
58
+ return null;
59
+ }
60
+
61
+ const container = this.#path[this.#path.length - 1];
62
+ if (!container || container.type !== 'Element') {
63
+ return null;
64
+ }
65
+
66
+ const children = Array.isArray(container.children) ? container.children : [];
67
+ const hasMeaningfulChildren = children.some((child) => child && !isWhitespaceTextNode(child));
68
+
69
+ if (hasMeaningfulChildren) {
70
+ return null;
71
+ }
72
+
73
+ container.metadata ??= {};
74
+ if (container.metadata.commentContainerId === undefined) {
75
+ container.metadata.commentContainerId = ++this.#commentContextId;
76
+ }
77
+
78
+ return {
79
+ containerId: container.metadata.commentContainerId,
80
+ containerType: container.type,
81
+ childIndex: children.length,
82
+ beforeMeaningfulChild: !hasMeaningfulChildren,
83
+ };
84
+ }
45
85
 
46
86
  /**
47
87
  * Helper method to get the element name from a JSX identifier or member expression
@@ -1083,6 +1123,7 @@ function RipplePlugin(config) {
1083
1123
 
1084
1124
  // Call onComment if it exists
1085
1125
  if (this.options.onComment) {
1126
+ const metadata = this.#createCommentMetadata();
1086
1127
  this.options.onComment(
1087
1128
  false,
1088
1129
  commentText,
@@ -1090,6 +1131,7 @@ function RipplePlugin(config) {
1090
1131
  commentEnd,
1091
1132
  startLoc,
1092
1133
  endLoc,
1134
+ metadata,
1093
1135
  );
1094
1136
  }
1095
1137
 
@@ -1120,6 +1162,7 @@ function RipplePlugin(config) {
1120
1162
 
1121
1163
  // Call onComment if it exists
1122
1164
  if (this.options.onComment) {
1165
+ const metadata = this.#createCommentMetadata();
1123
1166
  this.options.onComment(
1124
1167
  true,
1125
1168
  commentText,
@@ -1127,6 +1170,7 @@ function RipplePlugin(config) {
1127
1170
  commentEnd,
1128
1171
  startLoc,
1129
1172
  endLoc,
1173
+ metadata,
1130
1174
  );
1131
1175
  }
1132
1176
 
@@ -1246,7 +1290,8 @@ function RipplePlugin(config) {
1246
1290
  }
1247
1291
 
1248
1292
  element.attributes = open.attributes;
1249
- element.metadata = {};
1293
+ element.metadata ??= {};
1294
+ element.metadata.commentContainerId = ++this.#commentContextId;
1250
1295
 
1251
1296
  if (element.selfClosing) {
1252
1297
  this.#path.pop();
@@ -1643,7 +1688,7 @@ function RipplePlugin(config) {
1643
1688
  */
1644
1689
  function get_comment_handlers(source, comments, index = 0) {
1645
1690
  return {
1646
- onComment: (block, value, start, end, start_loc, end_loc) => {
1691
+ onComment: (block, value, start, end, start_loc, end_loc, metadata) => {
1647
1692
  if (block && /\n/.test(value)) {
1648
1693
  let a = start;
1649
1694
  while (a > 0 && source[a - 1] !== '\n') a -= 1;
@@ -1664,6 +1709,7 @@ function get_comment_handlers(source, comments, index = 0) {
1664
1709
  start: /** @type {import('acorn').Position} */ (start_loc),
1665
1710
  end: /** @type {import('acorn').Position} */ (end_loc),
1666
1711
  },
1712
+ context: metadata ?? null,
1667
1713
  });
1668
1714
  },
1669
1715
  add_comments: (ast) => {
@@ -1671,14 +1717,41 @@ function get_comment_handlers(source, comments, index = 0) {
1671
1717
 
1672
1718
  comments = comments
1673
1719
  .filter((comment) => comment.start >= index)
1674
- .map(({ type, value, start, end, loc }) => ({ type, value, start, end, loc }));
1720
+ .map(({ type, value, start, end, loc, context }) => ({ type, value, start, end, loc, context }));
1675
1721
 
1676
1722
  walk(ast, null, {
1677
1723
  _(node, { next, path }) {
1678
1724
  let comment;
1679
1725
 
1726
+ const metadata = /** @type {{ commentContainerId?: number, elementLeadingComments?: CommentWithLocation[] }} */ (node?.metadata);
1727
+
1728
+ if (metadata && metadata.commentContainerId !== undefined) {
1729
+ while (
1730
+ comments[0] &&
1731
+ comments[0].context &&
1732
+ comments[0].context.containerId === metadata.commentContainerId &&
1733
+ comments[0].context.beforeMeaningfulChild
1734
+ ) {
1735
+ const elementComment = /** @type {CommentWithLocation & { context?: any }} */ (comments.shift());
1736
+ (metadata.elementLeadingComments ||= []).push(elementComment);
1737
+ }
1738
+ }
1739
+
1680
1740
  while (comments[0] && comments[0].start < node.start) {
1681
1741
  comment = /** @type {CommentWithLocation} */ (comments.shift());
1742
+ if (comment.loc) {
1743
+ const ancestorElements = path
1744
+ .filter((ancestor) => ancestor && ancestor.type === 'Element' && ancestor.loc)
1745
+ .sort((a, b) => a.loc.start.line - b.loc.start.line);
1746
+
1747
+ const targetAncestor = ancestorElements.find((ancestor) => comment.loc.start.line < ancestor.loc.start.line);
1748
+
1749
+ if (targetAncestor) {
1750
+ targetAncestor.metadata ??= {};
1751
+ (targetAncestor.metadata.elementLeadingComments ||= []).push(comment);
1752
+ continue;
1753
+ }
1754
+ }
1682
1755
  (node.leadingComments ||= []).push(comment);
1683
1756
  }
1684
1757
 
@@ -1692,6 +1765,14 @@ function get_comment_handlers(source, comments, index = 0) {
1692
1765
  return;
1693
1766
  }
1694
1767
  }
1768
+ // Handle empty Element nodes the same way as empty BlockStatements
1769
+ if (node.type === 'Element' && (!node.children || node.children.length === 0)) {
1770
+ if (comments[0].start < node.end && comments[0].end < node.end) {
1771
+ comment = /** @type {CommentWithLocation} */ (comments.shift());
1772
+ (node.innerComments ||= []).push(comment);
1773
+ return;
1774
+ }
1775
+ }
1695
1776
  const parent = /** @type {any} */ (path.at(-1));
1696
1777
 
1697
1778
  if (parent === undefined || node.end !== parent.end) {