@ripple-ts/prettier-plugin 0.2.213 → 0.2.215
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 +4 -4
- package/src/index.js +113 -6
- package/src/index.test.js +113 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripple-ts/prettier-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.215",
|
|
4
4
|
"description": "Ripple plugin for Prettier",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"ripple",
|
|
12
12
|
"formatter"
|
|
13
13
|
],
|
|
14
|
-
"homepage": "https://
|
|
14
|
+
"homepage": "https://ripple-ts.com",
|
|
15
15
|
"author": "Dominic Gannaway",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/estree": "^1.0.8",
|
|
28
28
|
"@types/estree-jsx": "^1.0.5",
|
|
29
|
-
"prettier": "^3.
|
|
30
|
-
"ripple": "0.2.
|
|
29
|
+
"prettier": "^3.8.1",
|
|
30
|
+
"ripple": "0.2.215"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {},
|
|
33
33
|
"files": [
|
package/src/index.js
CHANGED
|
@@ -1505,6 +1505,11 @@ function printRippleNode(node, path, options, print, args) {
|
|
|
1505
1505
|
nodeContent = printMemberExpression(node, path, options, print);
|
|
1506
1506
|
break;
|
|
1507
1507
|
|
|
1508
|
+
case 'MetaProperty':
|
|
1509
|
+
// Prints import.meta, new.target, etc.
|
|
1510
|
+
nodeContent = [path.call(print, 'meta'), '.', path.call(print, 'property')];
|
|
1511
|
+
break;
|
|
1512
|
+
|
|
1508
1513
|
case 'Super':
|
|
1509
1514
|
nodeContent = 'super';
|
|
1510
1515
|
break;
|
|
@@ -1917,7 +1922,25 @@ function printRippleNode(node, path, options, print, args) {
|
|
|
1917
1922
|
break;
|
|
1918
1923
|
}
|
|
1919
1924
|
}
|
|
1920
|
-
|
|
1925
|
+
|
|
1926
|
+
// Control flow statements (if, for, while, etc.) get expanded empty blocks
|
|
1927
|
+
// to match standard Prettier behavior. Functions/methods keep `{}`.
|
|
1928
|
+
const blockParent = path.getParentNode();
|
|
1929
|
+
const isControlFlow =
|
|
1930
|
+
blockParent &&
|
|
1931
|
+
(blockParent.type === 'IfStatement' ||
|
|
1932
|
+
blockParent.type === 'ForStatement' ||
|
|
1933
|
+
blockParent.type === 'ForInStatement' ||
|
|
1934
|
+
blockParent.type === 'ForOfStatement' ||
|
|
1935
|
+
blockParent.type === 'WhileStatement' ||
|
|
1936
|
+
blockParent.type === 'DoWhileStatement' ||
|
|
1937
|
+
blockParent.type === 'SwitchCase');
|
|
1938
|
+
|
|
1939
|
+
if (isControlFlow) {
|
|
1940
|
+
nodeContent = ['{', hardline, '}'];
|
|
1941
|
+
} else {
|
|
1942
|
+
nodeContent = '{}';
|
|
1943
|
+
}
|
|
1921
1944
|
break;
|
|
1922
1945
|
}
|
|
1923
1946
|
|
|
@@ -2007,24 +2030,34 @@ function printRippleNode(node, path, options, print, args) {
|
|
|
2007
2030
|
nodeContent = result;
|
|
2008
2031
|
break;
|
|
2009
2032
|
}
|
|
2010
|
-
case 'LogicalExpression':
|
|
2033
|
+
case 'LogicalExpression': {
|
|
2034
|
+
const logicalParent = path.getParentNode();
|
|
2035
|
+
let logicalResult;
|
|
2011
2036
|
// Don't add indent if we're in a conditional test context
|
|
2012
2037
|
if (args?.isConditionalTest) {
|
|
2013
|
-
|
|
2038
|
+
logicalResult = group([
|
|
2014
2039
|
path.call((childPath) => print(childPath, { isConditionalTest: true }), 'left'),
|
|
2015
2040
|
' ',
|
|
2016
2041
|
node.operator,
|
|
2017
2042
|
[line, path.call((childPath) => print(childPath, { isConditionalTest: true }), 'right')],
|
|
2018
2043
|
]);
|
|
2019
2044
|
} else {
|
|
2020
|
-
|
|
2045
|
+
logicalResult = group([
|
|
2021
2046
|
path.call(print, 'left'),
|
|
2022
2047
|
' ',
|
|
2023
2048
|
node.operator,
|
|
2024
2049
|
indent([line, path.call(print, 'right')]),
|
|
2025
2050
|
]);
|
|
2026
2051
|
}
|
|
2052
|
+
|
|
2053
|
+
// Wrap in parentheses only if semantically necessary
|
|
2054
|
+
if (binaryExpressionNeedsParens(node, logicalParent)) {
|
|
2055
|
+
logicalResult = ['(', logicalResult, ')'];
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
nodeContent = logicalResult;
|
|
2027
2059
|
break;
|
|
2060
|
+
}
|
|
2028
2061
|
|
|
2029
2062
|
case 'ConditionalExpression': {
|
|
2030
2063
|
// Use Prettier's grouping to handle line breaking when exceeding printWidth
|
|
@@ -3640,6 +3673,7 @@ function printDoWhileStatement(node, path, options, print) {
|
|
|
3640
3673
|
parts.push('while (');
|
|
3641
3674
|
parts.push(test);
|
|
3642
3675
|
parts.push(')');
|
|
3676
|
+
parts.push(semi(options));
|
|
3643
3677
|
|
|
3644
3678
|
return parts;
|
|
3645
3679
|
}
|
|
@@ -5923,6 +5957,53 @@ function printElement(node, path, options, print) {
|
|
|
5923
5957
|
const isSelfClosing = !!node.selfClosing;
|
|
5924
5958
|
const hasAttributes = Array.isArray(node.attributes) && node.attributes.length > 0;
|
|
5925
5959
|
|
|
5960
|
+
// Collect comments that the parser attached to children but actually belong inside
|
|
5961
|
+
// the opening tag (positionally before openingElement.end). These should be printed
|
|
5962
|
+
// as leading comments before the appropriate attribute, not lifted to element-level.
|
|
5963
|
+
const openingTagCommentsSet = new Set();
|
|
5964
|
+
if (hasChildren && node.openingElement) {
|
|
5965
|
+
const openingEnd = node.openingElement.end;
|
|
5966
|
+
for (const child of node.children) {
|
|
5967
|
+
if (
|
|
5968
|
+
(child.type === 'Text' || child.type === 'Html') &&
|
|
5969
|
+
Array.isArray(child.leadingComments)
|
|
5970
|
+
) {
|
|
5971
|
+
for (const comment of child.leadingComments) {
|
|
5972
|
+
if (
|
|
5973
|
+
typeof comment.start === 'number' &&
|
|
5974
|
+
comment.start >= node.start &&
|
|
5975
|
+
comment.start < openingEnd
|
|
5976
|
+
) {
|
|
5977
|
+
openingTagCommentsSet.add(comment);
|
|
5978
|
+
}
|
|
5979
|
+
}
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
}
|
|
5983
|
+
|
|
5984
|
+
// Build a map from attribute index to comments that should precede that attribute
|
|
5985
|
+
const openingTagCommentsByAttrIndex = new Map();
|
|
5986
|
+
if (openingTagCommentsSet.size > 0 && hasAttributes) {
|
|
5987
|
+
const sortedOTC = [...openingTagCommentsSet].sort(
|
|
5988
|
+
(a, b) => /** @type {number} */ (a.start) - /** @type {number} */ (b.start),
|
|
5989
|
+
);
|
|
5990
|
+
let commentIdx = 0;
|
|
5991
|
+
for (let attrIdx = 0; attrIdx < node.attributes.length; attrIdx++) {
|
|
5992
|
+
const attr = node.attributes[attrIdx];
|
|
5993
|
+
const commentsForAttr = [];
|
|
5994
|
+
while (
|
|
5995
|
+
commentIdx < sortedOTC.length &&
|
|
5996
|
+
/** @type {number} */ (sortedOTC[commentIdx].start) < attr.start
|
|
5997
|
+
) {
|
|
5998
|
+
commentsForAttr.push(sortedOTC[commentIdx]);
|
|
5999
|
+
commentIdx++;
|
|
6000
|
+
}
|
|
6001
|
+
if (commentsForAttr.length > 0) {
|
|
6002
|
+
openingTagCommentsByAttrIndex.set(attrIdx, commentsForAttr);
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
|
|
5926
6007
|
if (isSelfClosing && !hasInnerComments && !hasAttributes) {
|
|
5927
6008
|
const elementDoc = group(['<', tagName, ' />']);
|
|
5928
6009
|
return metadataCommentParts.length > 0 ? [...metadataCommentParts, elementDoc] : elementDoc;
|
|
@@ -5935,14 +6016,36 @@ function printElement(node, path, options, print) {
|
|
|
5935
6016
|
|
|
5936
6017
|
const shouldUseSelfClosingSyntax = isSelfClosing || (!hasChildren && !hasInnerComments);
|
|
5937
6018
|
|
|
6019
|
+
const hasOpeningTagComments = openingTagCommentsSet.size > 0;
|
|
6020
|
+
let attrIndex = 0;
|
|
5938
6021
|
const openingTag = group([
|
|
5939
6022
|
'<',
|
|
5940
6023
|
tagName,
|
|
5941
6024
|
hasAttributes
|
|
5942
6025
|
? indent([
|
|
5943
6026
|
...path.map((attrPath) => {
|
|
5944
|
-
|
|
6027
|
+
const idx = attrIndex++;
|
|
6028
|
+
const commentsForAttr = openingTagCommentsByAttrIndex.get(idx);
|
|
6029
|
+
const parts = [];
|
|
6030
|
+
if (commentsForAttr) {
|
|
6031
|
+
for (const comment of commentsForAttr) {
|
|
6032
|
+
// Line comments (//) consume the rest of the line, so they must
|
|
6033
|
+
// use hardline to force a break. Block comments can use normal breaks.
|
|
6034
|
+
if (comment.type === 'Line') {
|
|
6035
|
+
parts.push(hardline);
|
|
6036
|
+
parts.push('//' + comment.value);
|
|
6037
|
+
} else if (comment.type === 'Block') {
|
|
6038
|
+
parts.push(attrLineBreak);
|
|
6039
|
+
parts.push('/*' + comment.value + '*/');
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
parts.push(attrLineBreak);
|
|
6044
|
+
parts.push(print(attrPath));
|
|
6045
|
+
return parts;
|
|
5945
6046
|
}, 'attributes'),
|
|
6047
|
+
// Force the group to break when there are line comments in the opening tag
|
|
6048
|
+
...(hasOpeningTagComments ? [breakParent] : []),
|
|
5946
6049
|
])
|
|
5947
6050
|
: '',
|
|
5948
6051
|
// Add line break opportunity before > or />
|
|
@@ -6045,7 +6148,11 @@ function printElement(node, path, options, print) {
|
|
|
6045
6148
|
|
|
6046
6149
|
if (hasTextLeadingComments) {
|
|
6047
6150
|
for (let j = 0; j < currentChild.leadingComments.length; j++) {
|
|
6048
|
-
|
|
6151
|
+
const comment = currentChild.leadingComments[j];
|
|
6152
|
+
// Don't lift comments that belong inside the opening tag (handled in attribute section)
|
|
6153
|
+
if (!openingTagCommentsSet.has(comment)) {
|
|
6154
|
+
fallbackElementComments.push(comment);
|
|
6155
|
+
}
|
|
6049
6156
|
}
|
|
6050
6157
|
}
|
|
6051
6158
|
|
package/src/index.test.js
CHANGED
|
@@ -269,6 +269,17 @@ export default component App() {
|
|
|
269
269
|
expect(formatted).toBeWithNewline(already_formatted);
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
+
it('should format import.meta expressions correctly', async () => {
|
|
273
|
+
const input = `export component Test(){if(import.meta.env.SSR){<div>{'Server'}</div>}}`;
|
|
274
|
+
const expected = `export component Test() {
|
|
275
|
+
if (import.meta.env.SSR) {
|
|
276
|
+
<div>{'Server'}</div>
|
|
277
|
+
}
|
|
278
|
+
}`;
|
|
279
|
+
const result = await format(input, { singleQuote: true });
|
|
280
|
+
expect(result).toBeWithNewline(expected);
|
|
281
|
+
});
|
|
282
|
+
|
|
272
283
|
it('should format a component with an object property notation component markup', async () => {
|
|
273
284
|
const expected = `component Card(props) {
|
|
274
285
|
<div class="card">
|
|
@@ -1017,7 +1028,8 @@ import { effect, track } from 'ripple';`;
|
|
|
1017
1028
|
});
|
|
1018
1029
|
|
|
1019
1030
|
it('does not add spaces around inlined array elements in destructured arguments', async () => {
|
|
1020
|
-
const expected = `for (const [key, value] of Object.entries(attributes).filter(([_key, value]) => value !== '')) {
|
|
1031
|
+
const expected = `for (const [key, value] of Object.entries(attributes).filter(([_key, value]) => value !== '')) {
|
|
1032
|
+
}
|
|
1021
1033
|
const [obj1, obj2] = arrayOfObjects;`;
|
|
1022
1034
|
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
1023
1035
|
expect(result).toBeWithNewline(expected);
|
|
@@ -2101,6 +2113,102 @@ files = [...(files ?? []), ...dt.files];`;
|
|
|
2101
2113
|
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
2102
2114
|
expect(result).toBeWithNewline(expected);
|
|
2103
2115
|
});
|
|
2116
|
+
|
|
2117
|
+
it('should preserve comments above attributes on dom elements', async () => {
|
|
2118
|
+
const expected = `component App() {
|
|
2119
|
+
<div
|
|
2120
|
+
// @ripple-ignore
|
|
2121
|
+
something="test"
|
|
2122
|
+
>
|
|
2123
|
+
{'test'}
|
|
2124
|
+
</div>
|
|
2125
|
+
}`;
|
|
2126
|
+
|
|
2127
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
2128
|
+
expect(result).toBeWithNewline(expected);
|
|
2129
|
+
});
|
|
2130
|
+
|
|
2131
|
+
it('should preserve comments above attributes on components', async () => {
|
|
2132
|
+
const expected = `component App() {
|
|
2133
|
+
<Child
|
|
2134
|
+
// @ripple-ignore
|
|
2135
|
+
something="test"
|
|
2136
|
+
>
|
|
2137
|
+
{'test'}
|
|
2138
|
+
</Child>
|
|
2139
|
+
}
|
|
2140
|
+
component Child({ something }) {
|
|
2141
|
+
<div>{something}</div>
|
|
2142
|
+
}`;
|
|
2143
|
+
|
|
2144
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
2145
|
+
expect(result).toBeWithNewline(expected);
|
|
2146
|
+
});
|
|
2147
|
+
|
|
2148
|
+
it('keeps parens in place when necessary for logical reasons with && and || operators', async () => {
|
|
2149
|
+
const expected = `function App() {
|
|
2150
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
2151
|
+
}
|
|
2152
|
+
}`;
|
|
2153
|
+
|
|
2154
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
2155
|
+
expect(result).toBeWithNewline(expected);
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
it('expands empty braces to new lines for for-in statements', async () => {
|
|
2159
|
+
const expected = `for (const key in obj) {
|
|
2160
|
+
}`;
|
|
2161
|
+
const result = await format(expected);
|
|
2162
|
+
expect(result).toBeWithNewline(expected);
|
|
2163
|
+
});
|
|
2164
|
+
|
|
2165
|
+
it('expands empty braces to new lines for for statements', async () => {
|
|
2166
|
+
const expected = `for (let i = 0; i < 10; i++) {
|
|
2167
|
+
}`;
|
|
2168
|
+
const result = await format(expected);
|
|
2169
|
+
expect(result).toBeWithNewline(expected);
|
|
2170
|
+
});
|
|
2171
|
+
|
|
2172
|
+
it('expands empty braces to new lines for while statements', async () => {
|
|
2173
|
+
const expected = `while (true) {
|
|
2174
|
+
}`;
|
|
2175
|
+
const result = await format(expected);
|
|
2176
|
+
expect(result).toBeWithNewline(expected);
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2179
|
+
it('expands empty braces to new lines for do-while statements', async () => {
|
|
2180
|
+
const expected = `do {
|
|
2181
|
+
} while (true);`;
|
|
2182
|
+
const result = await format(expected);
|
|
2183
|
+
expect(result).toBeWithNewline(expected);
|
|
2184
|
+
});
|
|
2185
|
+
|
|
2186
|
+
it('adds semicolon after do-while when semi option is true', async () => {
|
|
2187
|
+
const input = `do { console.log('x') } while (true)`;
|
|
2188
|
+
const expected = `do {
|
|
2189
|
+
console.log("x");
|
|
2190
|
+
} while (true);`;
|
|
2191
|
+
const result = await format(input);
|
|
2192
|
+
expect(result).toBeWithNewline(expected);
|
|
2193
|
+
});
|
|
2194
|
+
|
|
2195
|
+
it('omits semicolon after do-while when semi option is false', async () => {
|
|
2196
|
+
const input = `do { console.log('x') } while (true);`;
|
|
2197
|
+
const expected = `do {
|
|
2198
|
+
console.log("x")
|
|
2199
|
+
} while (true)`;
|
|
2200
|
+
const result = await format(input, { semi: false });
|
|
2201
|
+
expect(result).toBeWithNewline(expected);
|
|
2202
|
+
});
|
|
2203
|
+
|
|
2204
|
+
it('expands empty braces to new lines for switch case blocks', async () => {
|
|
2205
|
+
const expected = `switch (x) {
|
|
2206
|
+
case 1: {
|
|
2207
|
+
}
|
|
2208
|
+
}`;
|
|
2209
|
+
const result = await format(expected);
|
|
2210
|
+
expect(result).toBeWithNewline(expected);
|
|
2211
|
+
});
|
|
2104
2212
|
});
|
|
2105
2213
|
|
|
2106
2214
|
describe('edge cases', () => {
|
|
@@ -2767,8 +2875,10 @@ const obj2 = #{
|
|
|
2767
2875
|
const input = `for (const [i = 0, item] of items.entries()) {}
|
|
2768
2876
|
for (const {i = 0, item} of items.entries()) {}`;
|
|
2769
2877
|
|
|
2770
|
-
const expected = `for (const [i = 0, item] of items.entries()) {
|
|
2771
|
-
|
|
2878
|
+
const expected = `for (const [i = 0, item] of items.entries()) {
|
|
2879
|
+
}
|
|
2880
|
+
for (const { i = 0, item } of items.entries()) {
|
|
2881
|
+
}`;
|
|
2772
2882
|
|
|
2773
2883
|
const result = await format(input);
|
|
2774
2884
|
expect(result).toBeWithNewline(expected);
|