ripple 0.2.134 → 0.2.136
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 +2 -2
- package/src/compiler/phases/1-parse/index.js +311 -69
- package/src/compiler/phases/3-transform/client/index.js +222 -61
- package/src/compiler/phases/3-transform/segments.js +16 -6
- package/src/runtime/internal/client/blocks.js +17 -2
- package/src/utils/builders.js +17 -0
- package/tests/client/basic/basic.components.test.ripple +30 -20
- package/tests/client/compiler/compiler.basic.test.ripple +91 -62
- package/tests/client/dynamic-elements.test.ripple +120 -1
- package/tests/setup-client.js +4 -4
- package/types/index.d.ts +11 -1
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.
|
|
6
|
+
"version": "0.2.136",
|
|
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.
|
|
84
|
+
"ripple": "0.2.136"
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -29,11 +29,34 @@ function convert_from_jsx(node) {
|
|
|
29
29
|
return node;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const regex_whitespace_only = /\s/;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Skip whitespace characters without skipping comments.
|
|
36
|
+
* This is needed because Acorn's skipSpace() also skips comments, which breaks
|
|
37
|
+
* parsing in certain contexts. Updates parser position and line tracking.
|
|
38
|
+
* @param {acorn.Parser} parser
|
|
39
|
+
*/
|
|
40
|
+
function skipWhitespace(parser) {
|
|
41
|
+
const originalStart = parser.start;
|
|
42
|
+
while (parser.start < parser.input.length && regex_whitespace_only.test(parser.input[parser.start])) {
|
|
43
|
+
parser.start++;
|
|
44
|
+
}
|
|
45
|
+
// Update line tracking if whitespace was skipped
|
|
46
|
+
if (parser.start !== originalStart) {
|
|
47
|
+
const lineInfo = acorn.getLineInfo(parser.input, parser.start);
|
|
48
|
+
parser.curLine = lineInfo.line;
|
|
49
|
+
parser.lineStart = parser.start - lineInfo.column;
|
|
50
|
+
parser.startLoc = lineInfo;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
function isWhitespaceTextNode(node) {
|
|
33
55
|
if (!node || node.type !== 'Text') {
|
|
34
56
|
return false;
|
|
35
57
|
}
|
|
36
|
-
const value =
|
|
58
|
+
const value =
|
|
59
|
+
typeof node.value === 'string' ? node.value : typeof node.raw === 'string' ? node.raw : '';
|
|
37
60
|
return /^\s*$/.test(value);
|
|
38
61
|
}
|
|
39
62
|
|
|
@@ -64,7 +87,9 @@ function RipplePlugin(config) {
|
|
|
64
87
|
}
|
|
65
88
|
|
|
66
89
|
const children = Array.isArray(container.children) ? container.children : [];
|
|
67
|
-
const hasMeaningfulChildren = children.some(
|
|
90
|
+
const hasMeaningfulChildren = children.some(
|
|
91
|
+
(child) => child && !isWhitespaceTextNode(child),
|
|
92
|
+
);
|
|
68
93
|
|
|
69
94
|
if (hasMeaningfulChildren) {
|
|
70
95
|
return null;
|
|
@@ -497,6 +522,25 @@ function RipplePlugin(config) {
|
|
|
497
522
|
|
|
498
523
|
return super.parseExprAtom(refDestructuringErrors, forNew, forInit);
|
|
499
524
|
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Override to track parenthesized expressions in metadata
|
|
528
|
+
* This allows the prettier plugin to preserve parentheses where they existed
|
|
529
|
+
*/
|
|
530
|
+
parseParenAndDistinguishExpression(canBeArrow, forInit) {
|
|
531
|
+
const startPos = this.start;
|
|
532
|
+
const expr = super.parseParenAndDistinguishExpression(canBeArrow, forInit);
|
|
533
|
+
|
|
534
|
+
// If the expression's start position is after the opening paren,
|
|
535
|
+
// it means it was wrapped in parentheses. Mark it in metadata.
|
|
536
|
+
if (expr && expr.start > startPos) {
|
|
537
|
+
expr.metadata ??= {};
|
|
538
|
+
expr.metadata.parenthesized = true;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return expr;
|
|
542
|
+
}
|
|
543
|
+
|
|
500
544
|
/**
|
|
501
545
|
* Parse `@(expression)` syntax for unboxing tracked values
|
|
502
546
|
* Creates a TrackedExpression node with the argument property
|
|
@@ -688,6 +732,7 @@ function RipplePlugin(config) {
|
|
|
688
732
|
this.exitScope();
|
|
689
733
|
|
|
690
734
|
this.next();
|
|
735
|
+
skipWhitespace(this);
|
|
691
736
|
this.finishNode(node, 'Component');
|
|
692
737
|
this.awaitPos = 0;
|
|
693
738
|
|
|
@@ -995,6 +1040,10 @@ function RipplePlugin(config) {
|
|
|
995
1040
|
}
|
|
996
1041
|
|
|
997
1042
|
jsx_parseElementName() {
|
|
1043
|
+
if (this.type?.label === 'jsxTagEnd') {
|
|
1044
|
+
return '';
|
|
1045
|
+
}
|
|
1046
|
+
|
|
998
1047
|
let node = this.jsx_parseNamespacedName();
|
|
999
1048
|
|
|
1000
1049
|
if (node.type === 'JSXNamespacedName') {
|
|
@@ -1004,8 +1053,39 @@ function RipplePlugin(config) {
|
|
|
1004
1053
|
if (this.eat(tt.dot)) {
|
|
1005
1054
|
let memberExpr = this.startNodeAt(node.start, node.loc && node.loc.start);
|
|
1006
1055
|
memberExpr.object = node;
|
|
1007
|
-
|
|
1008
|
-
|
|
1056
|
+
|
|
1057
|
+
// Check for .@[expression] syntax for tracked computed member access
|
|
1058
|
+
// After eating the dot, check if the current token is @ followed by [
|
|
1059
|
+
if (this.type.label === '@') {
|
|
1060
|
+
// Check if the next character after @ is [
|
|
1061
|
+
const nextChar = this.input.charCodeAt(this.pos);
|
|
1062
|
+
|
|
1063
|
+
if (nextChar === 91) { // [ character
|
|
1064
|
+
memberExpr.computed = true;
|
|
1065
|
+
|
|
1066
|
+
// Consume the @ token
|
|
1067
|
+
this.next();
|
|
1068
|
+
|
|
1069
|
+
// Now this.type should be bracketL
|
|
1070
|
+
// Consume the [ and parse the expression inside
|
|
1071
|
+
this.expect(tt.bracketL);
|
|
1072
|
+
|
|
1073
|
+
// Parse the expression inside brackets
|
|
1074
|
+
memberExpr.property = this.parseExpression();
|
|
1075
|
+
memberExpr.property.tracked = true;
|
|
1076
|
+
|
|
1077
|
+
// Expect closing bracket
|
|
1078
|
+
this.expect(tt.bracketR);
|
|
1079
|
+
} else {
|
|
1080
|
+
// @ not followed by [, treat as regular tracked identifier
|
|
1081
|
+
memberExpr.property = this.jsx_parseIdentifier();
|
|
1082
|
+
memberExpr.computed = false;
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
// Regular dot notation
|
|
1086
|
+
memberExpr.property = this.jsx_parseIdentifier();
|
|
1087
|
+
memberExpr.computed = false;
|
|
1088
|
+
}
|
|
1009
1089
|
while (this.eat(tt.dot)) {
|
|
1010
1090
|
let newMemberExpr = this.startNodeAt(
|
|
1011
1091
|
memberExpr.start,
|
|
@@ -1029,7 +1109,7 @@ function RipplePlugin(config) {
|
|
|
1029
1109
|
var t = this.jsx_parseExpressionContainer();
|
|
1030
1110
|
return (
|
|
1031
1111
|
'JSXEmptyExpression' === t.expression.type &&
|
|
1032
|
-
|
|
1112
|
+
this.raise(t.start, 'attributes must only be assigned a non-empty expression'),
|
|
1033
1113
|
t
|
|
1034
1114
|
);
|
|
1035
1115
|
case tok.jsxTagStart:
|
|
@@ -1202,14 +1282,14 @@ function RipplePlugin(config) {
|
|
|
1202
1282
|
this.raise(
|
|
1203
1283
|
this.pos,
|
|
1204
1284
|
'Unexpected token `' +
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1285
|
+
this.input[this.pos] +
|
|
1286
|
+
'`. Did you mean `' +
|
|
1287
|
+
(ch === 62 ? '>' : '}') +
|
|
1288
|
+
'` or ' +
|
|
1289
|
+
'`{"' +
|
|
1290
|
+
this.input[this.pos] +
|
|
1291
|
+
'"}' +
|
|
1292
|
+
'`?',
|
|
1213
1293
|
);
|
|
1214
1294
|
}
|
|
1215
1295
|
|
|
@@ -1519,6 +1599,7 @@ function RipplePlugin(config) {
|
|
|
1519
1599
|
|
|
1520
1600
|
this.#path.pop();
|
|
1521
1601
|
this.next();
|
|
1602
|
+
skipWhitespace(this);
|
|
1522
1603
|
return;
|
|
1523
1604
|
}
|
|
1524
1605
|
const node = this.parseElement();
|
|
@@ -1528,7 +1609,14 @@ function RipplePlugin(config) {
|
|
|
1528
1609
|
} else {
|
|
1529
1610
|
const node = this.parseStatement(null);
|
|
1530
1611
|
body.push(node);
|
|
1612
|
+
|
|
1613
|
+
// Ensure we're not in JSX context before recursing
|
|
1614
|
+
// This is important when elements are parsed at statement level
|
|
1615
|
+
if (this.curContext() === this.acornTypeScript.tokContexts.tc_expr) {
|
|
1616
|
+
this.context.pop();
|
|
1617
|
+
}
|
|
1531
1618
|
}
|
|
1619
|
+
|
|
1532
1620
|
this.parseTemplateBody(body);
|
|
1533
1621
|
}
|
|
1534
1622
|
|
|
@@ -1575,6 +1663,7 @@ function RipplePlugin(config) {
|
|
|
1575
1663
|
this.exitScope();
|
|
1576
1664
|
|
|
1577
1665
|
this.next();
|
|
1666
|
+
skipWhitespace(this);
|
|
1578
1667
|
this.finishNode(node, 'Component');
|
|
1579
1668
|
this.awaitPos = 0;
|
|
1580
1669
|
|
|
@@ -1687,6 +1776,16 @@ function RipplePlugin(config) {
|
|
|
1687
1776
|
* @returns {{ onComment: Function, add_comments: Function }} Comment handler functions
|
|
1688
1777
|
*/
|
|
1689
1778
|
function get_comment_handlers(source, comments, index = 0) {
|
|
1779
|
+
function getNextNonWhitespaceCharacter(text, startIndex) {
|
|
1780
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
1781
|
+
const char = text[i];
|
|
1782
|
+
if (char !== ' ' && char !== '\t' && char !== '\n' && char !== '\r') {
|
|
1783
|
+
return char;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return null;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1690
1789
|
return {
|
|
1691
1790
|
onComment: (block, value, start, end, start_loc, end_loc, metadata) => {
|
|
1692
1791
|
if (block && /\n/.test(value)) {
|
|
@@ -1717,34 +1816,66 @@ function get_comment_handlers(source, comments, index = 0) {
|
|
|
1717
1816
|
|
|
1718
1817
|
comments = comments
|
|
1719
1818
|
.filter((comment) => comment.start >= index)
|
|
1720
|
-
.map(({ type, value, start, end, loc, context }) => ({
|
|
1819
|
+
.map(({ type, value, start, end, loc, context }) => ({
|
|
1820
|
+
type,
|
|
1821
|
+
value,
|
|
1822
|
+
start,
|
|
1823
|
+
end,
|
|
1824
|
+
loc,
|
|
1825
|
+
context,
|
|
1826
|
+
}));
|
|
1721
1827
|
|
|
1722
1828
|
walk(ast, null, {
|
|
1723
1829
|
_(node, { next, path }) {
|
|
1724
1830
|
let comment;
|
|
1725
1831
|
|
|
1726
|
-
|
|
1832
|
+
const metadata =
|
|
1833
|
+
/** @type {{ commentContainerId?: number, elementLeadingComments?: CommentWithLocation[] }} */ (
|
|
1834
|
+
node?.metadata
|
|
1835
|
+
);
|
|
1727
1836
|
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1837
|
+
if (metadata && metadata.commentContainerId !== undefined) {
|
|
1838
|
+
while (
|
|
1839
|
+
comments[0] &&
|
|
1840
|
+
comments[0].context &&
|
|
1841
|
+
comments[0].context.containerId === metadata.commentContainerId &&
|
|
1842
|
+
comments[0].context.beforeMeaningfulChild
|
|
1843
|
+
) {
|
|
1844
|
+
const elementComment = /** @type {CommentWithLocation & { context?: any }} */ (
|
|
1845
|
+
comments.shift()
|
|
1846
|
+
);
|
|
1847
|
+
(metadata.elementLeadingComments ||= []).push(elementComment);
|
|
1738
1848
|
}
|
|
1849
|
+
}
|
|
1739
1850
|
|
|
1740
1851
|
while (comments[0] && comments[0].start < node.start) {
|
|
1741
1852
|
comment = /** @type {CommentWithLocation} */ (comments.shift());
|
|
1853
|
+
|
|
1854
|
+
// Skip leading comments for BlockStatement that is a function body
|
|
1855
|
+
// These comments should be dangling on the function instead
|
|
1856
|
+
if (node.type === 'BlockStatement') {
|
|
1857
|
+
const parent = path.at(-1);
|
|
1858
|
+
if (
|
|
1859
|
+
parent &&
|
|
1860
|
+
(parent.type === 'FunctionDeclaration' ||
|
|
1861
|
+
parent.type === 'FunctionExpression' ||
|
|
1862
|
+
parent.type === 'ArrowFunctionExpression') &&
|
|
1863
|
+
parent.body === node
|
|
1864
|
+
) {
|
|
1865
|
+
// This is a function body - don't attach comment, let it be handled by function
|
|
1866
|
+
(parent.comments ||= []).push(comment);
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1742
1871
|
if (comment.loc) {
|
|
1743
1872
|
const ancestorElements = path
|
|
1744
1873
|
.filter((ancestor) => ancestor && ancestor.type === 'Element' && ancestor.loc)
|
|
1745
1874
|
.sort((a, b) => a.loc.start.line - b.loc.start.line);
|
|
1746
1875
|
|
|
1747
|
-
const targetAncestor = ancestorElements.find(
|
|
1876
|
+
const targetAncestor = ancestorElements.find(
|
|
1877
|
+
(ancestor) => comment.loc.start.line < ancestor.loc.start.line,
|
|
1878
|
+
);
|
|
1748
1879
|
|
|
1749
1880
|
if (targetAncestor) {
|
|
1750
1881
|
targetAncestor.metadata ??= {};
|
|
@@ -1755,72 +1886,183 @@ function get_comment_handlers(source, comments, index = 0) {
|
|
|
1755
1886
|
(node.leadingComments ||= []).push(comment);
|
|
1756
1887
|
}
|
|
1757
1888
|
|
|
1758
|
-
|
|
1889
|
+
next();
|
|
1759
1890
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1891
|
+
if (comments[0]) {
|
|
1892
|
+
if (node.type === 'BlockStatement' && node.body.length === 0) {
|
|
1893
|
+
// Collect all comments that fall within this empty block
|
|
1894
|
+
while (comments[0] && comments[0].start < node.end && comments[0].end < node.end) {
|
|
1895
|
+
comment = /** @type {CommentWithLocation} */ (comments.shift());
|
|
1896
|
+
(node.innerComments ||= []).push(comment);
|
|
1897
|
+
}
|
|
1898
|
+
if (node.innerComments && node.innerComments.length > 0) {
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
// Handle empty Element nodes the same way as empty BlockStatements
|
|
1903
|
+
if (node.type === 'Element' && (!node.children || node.children.length === 0)) {
|
|
1904
|
+
if (comments[0].start < node.end && comments[0].end < node.end) {
|
|
1905
|
+
comment = /** @type {CommentWithLocation} */ (comments.shift());
|
|
1906
|
+
(node.innerComments ||= []).push(comment);
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
const parent = /** @type {any} */ (path.at(-1));
|
|
1912
|
+
|
|
1913
|
+
if (parent === undefined || node.end !== parent.end) {
|
|
1780
1914
|
const slice = source.slice(node.end, comments[0].start);
|
|
1781
1915
|
|
|
1782
1916
|
// Check if this node is the last item in an array-like structure
|
|
1783
1917
|
let is_last_in_array = false;
|
|
1784
1918
|
let array_prop = null;
|
|
1785
1919
|
|
|
1786
|
-
if (
|
|
1920
|
+
if (
|
|
1921
|
+
parent?.type === 'BlockStatement' ||
|
|
1922
|
+
parent?.type === 'Program' ||
|
|
1923
|
+
parent?.type === 'Component' ||
|
|
1924
|
+
parent?.type === 'ClassBody'
|
|
1925
|
+
) {
|
|
1787
1926
|
array_prop = 'body';
|
|
1788
|
-
} else if (parent?.type === '
|
|
1927
|
+
} else if (parent?.type === 'SwitchStatement') {
|
|
1928
|
+
array_prop = 'cases';
|
|
1929
|
+
} else if (parent?.type === 'SwitchCase') {
|
|
1930
|
+
array_prop = 'consequent';
|
|
1931
|
+
} else if (
|
|
1932
|
+
parent?.type === 'ArrayExpression' ||
|
|
1933
|
+
parent?.type === 'TrackedArrayExpression'
|
|
1934
|
+
) {
|
|
1789
1935
|
array_prop = 'elements';
|
|
1790
|
-
} else if (
|
|
1936
|
+
} else if (
|
|
1937
|
+
parent?.type === 'ObjectExpression' ||
|
|
1938
|
+
parent?.type === 'TrackedObjectExpression'
|
|
1939
|
+
) {
|
|
1791
1940
|
array_prop = 'properties';
|
|
1941
|
+
} else if (
|
|
1942
|
+
parent?.type === 'FunctionDeclaration' ||
|
|
1943
|
+
parent?.type === 'FunctionExpression' ||
|
|
1944
|
+
parent?.type === 'ArrowFunctionExpression'
|
|
1945
|
+
) {
|
|
1946
|
+
array_prop = 'params';
|
|
1947
|
+
} else if (
|
|
1948
|
+
parent?.type === 'CallExpression' ||
|
|
1949
|
+
parent?.type === 'OptionalCallExpression' ||
|
|
1950
|
+
parent?.type === 'NewExpression'
|
|
1951
|
+
) {
|
|
1952
|
+
array_prop = 'arguments';
|
|
1792
1953
|
}
|
|
1793
|
-
|
|
1794
1954
|
if (array_prop && Array.isArray(parent[array_prop])) {
|
|
1795
|
-
is_last_in_array =
|
|
1955
|
+
is_last_in_array =
|
|
1956
|
+
parent[array_prop].indexOf(node) === parent[array_prop].length - 1;
|
|
1796
1957
|
}
|
|
1797
1958
|
|
|
1798
1959
|
if (is_last_in_array) {
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1960
|
+
const isParam = array_prop === 'params';
|
|
1961
|
+
const isArgument = array_prop === 'arguments';
|
|
1962
|
+
if (isParam || isArgument) {
|
|
1963
|
+
while (comments.length) {
|
|
1964
|
+
const potentialComment = comments[0];
|
|
1965
|
+
if (parent && potentialComment.start >= parent.end) {
|
|
1966
|
+
break;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
const nextChar = getNextNonWhitespaceCharacter(source, potentialComment.end);
|
|
1970
|
+
if (nextChar === ')') {
|
|
1971
|
+
(node.trailingComments ||= []).push(
|
|
1972
|
+
/** @type {CommentWithLocation} */(comments.shift()),
|
|
1973
|
+
);
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
break;
|
|
1978
|
+
}
|
|
1979
|
+
} else {
|
|
1980
|
+
// Special case: There can be multiple trailing comments after the last node in a block,
|
|
1981
|
+
// and they can be separated by newlines
|
|
1982
|
+
let end = node.end;
|
|
1802
1983
|
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1984
|
+
while (comments.length) {
|
|
1985
|
+
const comment = comments[0];
|
|
1986
|
+
if (parent && comment.start >= parent.end) break;
|
|
1806
1987
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1988
|
+
(node.trailingComments ||= []).push(comment);
|
|
1989
|
+
comments.shift();
|
|
1990
|
+
end = comment.end;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
} else if (node.end <= comments[0].start) {
|
|
1994
|
+
const onlySimpleWhitespace = /^[,) \t]*$/.test(slice);
|
|
1995
|
+
const onlyWhitespace = /^\s*$/.test(slice);
|
|
1996
|
+
const hasBlankLine = /\n\s*\n/.test(slice);
|
|
1997
|
+
const nodeEndLine = node.loc?.end?.line ?? null;
|
|
1998
|
+
const commentStartLine = comments[0].loc?.start?.line ?? null;
|
|
1999
|
+
const isImmediateNextLine =
|
|
2000
|
+
nodeEndLine !== null &&
|
|
2001
|
+
commentStartLine !== null &&
|
|
2002
|
+
commentStartLine === nodeEndLine + 1;
|
|
2003
|
+
const isSwitchCaseSibling = array_prop === 'cases';
|
|
2004
|
+
|
|
2005
|
+
if (isSwitchCaseSibling && !is_last_in_array) {
|
|
2006
|
+
if (
|
|
2007
|
+
nodeEndLine !== null &&
|
|
2008
|
+
commentStartLine !== null &&
|
|
2009
|
+
nodeEndLine === commentStartLine
|
|
2010
|
+
) {
|
|
2011
|
+
node.trailingComments = [/** @type {CommentWithLocation} */ (comments.shift())];
|
|
2012
|
+
}
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
if (
|
|
2017
|
+
onlySimpleWhitespace ||
|
|
2018
|
+
(onlyWhitespace && !hasBlankLine && isImmediateNextLine)
|
|
2019
|
+
) {
|
|
2020
|
+
// Check if this is a block comment that's inline with the next statement
|
|
2021
|
+
// e.g., /** @type {SomeType} */ (a) = 5;
|
|
2022
|
+
// These should be leading comments, not trailing
|
|
2023
|
+
if (
|
|
2024
|
+
comments[0].type === 'Block' &&
|
|
2025
|
+
!is_last_in_array &&
|
|
2026
|
+
array_prop &&
|
|
2027
|
+
parent[array_prop]
|
|
2028
|
+
) {
|
|
2029
|
+
const currentIndex = parent[array_prop].indexOf(node);
|
|
2030
|
+
const nextSibling = parent[array_prop][currentIndex + 1];
|
|
2031
|
+
|
|
2032
|
+
if (nextSibling && nextSibling.loc) {
|
|
2033
|
+
const commentEndLine = comments[0].loc?.end?.line;
|
|
2034
|
+
const nextSiblingStartLine = nextSibling.loc?.start?.line;
|
|
2035
|
+
|
|
2036
|
+
// If comment ends on same line as next sibling starts, it's inline with next
|
|
2037
|
+
if (commentEndLine === nextSiblingStartLine) {
|
|
2038
|
+
// Leave it for next sibling's leading comments
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
// For function parameters, only attach as trailing comment if it's on the same line
|
|
2045
|
+
// Comments on next line after comma should be leading comments of next parameter
|
|
2046
|
+
const isParam = array_prop === 'params';
|
|
2047
|
+
if (isParam) {
|
|
2048
|
+
// Check if comment is on same line as the node
|
|
2049
|
+
const nodeEndLine = source.slice(0, node.end).split('\n').length;
|
|
2050
|
+
const commentStartLine = source.slice(0, comments[0].start).split('\n').length;
|
|
2051
|
+
if (nodeEndLine === commentStartLine) {
|
|
2052
|
+
node.trailingComments = [
|
|
2053
|
+
/** @type {CommentWithLocation} */ (comments.shift()),
|
|
2054
|
+
];
|
|
2055
|
+
}
|
|
2056
|
+
// Otherwise leave it for next parameter's leading comments
|
|
2057
|
+
} else {
|
|
2058
|
+
node.trailingComments = [/** @type {CommentWithLocation} */ (comments.shift())];
|
|
2059
|
+
}
|
|
1810
2060
|
}
|
|
1811
|
-
} else if (node.end <= comments[0].start && /^[,) \t]*$/.test(slice)) {
|
|
1812
|
-
node.trailingComments = [/** @type {CommentWithLocation} */ (comments.shift())];
|
|
1813
2061
|
}
|
|
1814
2062
|
}
|
|
1815
2063
|
}
|
|
1816
2064
|
},
|
|
1817
2065
|
});
|
|
1818
|
-
|
|
1819
|
-
// Special case: Trailing comments after the root node (which can only happen for expression tags or for Program nodes).
|
|
1820
|
-
// Adding them ensures that we can later detect the end of the expression tag correctly.
|
|
1821
|
-
if (comments.length > 0 && (comments[0].start >= ast.end || ast.type === 'Program')) {
|
|
1822
|
-
(ast.trailingComments ||= []).push(...comments.splice(0));
|
|
1823
|
-
}
|
|
1824
2066
|
},
|
|
1825
2067
|
};
|
|
1826
2068
|
}
|