@ripple-ts/prettier-plugin 0.2.195 → 0.2.197
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/index.js +93 -41
- package/src/index.test.js +103 -0
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.197",
|
|
4
4
|
"description": "Ripple plugin for Prettier",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"prettier": "^3.6.2",
|
|
28
|
-
"ripple": "0.2.
|
|
28
|
+
"ripple": "0.2.197"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {},
|
|
31
31
|
"files": [
|
package/src/index.js
CHANGED
|
@@ -85,6 +85,26 @@ export const printers = {
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
if (node.type === 'ScriptContent' && node.content) {
|
|
89
|
+
return async (textToDoc) => {
|
|
90
|
+
try {
|
|
91
|
+
// Format JS/TS using Prettier's textToDoc
|
|
92
|
+
const body = await textToDoc(node.content, {
|
|
93
|
+
parser: 'babel-ts',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Return complete element with tags
|
|
97
|
+
// return ['<script>', indent([hardline, formattedContent]), hardline, '</script>'];
|
|
98
|
+
return body;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// If JS/TS has syntax errors, return original unformatted content
|
|
101
|
+
console.error('Error formatting JS/TS inside <script>:', error);
|
|
102
|
+
return node.content;
|
|
103
|
+
// return ['<script>', indent([hardline, node.content]), hardline, '</script>'];
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
return null;
|
|
89
109
|
},
|
|
90
110
|
getVisitorKeys(node) {
|
|
@@ -98,6 +118,7 @@ export const printers = {
|
|
|
98
118
|
'css', // Handled by embed()
|
|
99
119
|
'raw',
|
|
100
120
|
'regex',
|
|
121
|
+
'content', // Handled by embed() for <script> tags
|
|
101
122
|
]);
|
|
102
123
|
|
|
103
124
|
const keys = Object.keys(node).filter((key) => {
|
|
@@ -1290,6 +1311,12 @@ function printRippleNode(node, path, options, print, args) {
|
|
|
1290
1311
|
break;
|
|
1291
1312
|
}
|
|
1292
1313
|
|
|
1314
|
+
case 'TSInstantiationExpression': {
|
|
1315
|
+
// Explicit type instantiation: foo<Type>, identity<string>
|
|
1316
|
+
nodeContent = concat([path.call(print, 'expression'), path.call(print, 'typeArguments')]);
|
|
1317
|
+
break;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1293
1320
|
case 'JSXExpressionContainer': {
|
|
1294
1321
|
nodeContent = concat(['{', path.call(print, 'expression'), '}']);
|
|
1295
1322
|
break;
|
|
@@ -1869,7 +1896,7 @@ function printRippleNode(node, path, options, print, args) {
|
|
|
1869
1896
|
case 'TSTypeOperator': {
|
|
1870
1897
|
const operator = node.operator;
|
|
1871
1898
|
const type = path.call(print, 'typeAnnotation');
|
|
1872
|
-
nodeContent =
|
|
1899
|
+
nodeContent = concat([operator, ' ', type]);
|
|
1873
1900
|
break;
|
|
1874
1901
|
}
|
|
1875
1902
|
|
|
@@ -2175,7 +2202,7 @@ function printExportNamedDeclaration(node, path, options, print) {
|
|
|
2175
2202
|
|
|
2176
2203
|
function printComponent(node, path, options, print, innerCommentParts = []) {
|
|
2177
2204
|
// Use arrays instead of string concatenation
|
|
2178
|
-
const signatureParts = ['component ', node.id.name];
|
|
2205
|
+
const signatureParts = node.id ? ['component ', node.id.name] : ['component'];
|
|
2179
2206
|
|
|
2180
2207
|
// Add TypeScript generics if present
|
|
2181
2208
|
if (node.typeParameters) {
|
|
@@ -3137,6 +3164,11 @@ function printTryStatement(node, path, options, print) {
|
|
|
3137
3164
|
parts.push('try ');
|
|
3138
3165
|
parts.push(path.call(print, 'block'));
|
|
3139
3166
|
|
|
3167
|
+
if (node.pending) {
|
|
3168
|
+
parts.push(' pending ');
|
|
3169
|
+
parts.push(path.call(print, 'pending'));
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3140
3172
|
if (node.handler) {
|
|
3141
3173
|
parts.push(' catch');
|
|
3142
3174
|
if (node.handler.param) {
|
|
@@ -3153,11 +3185,6 @@ function printTryStatement(node, path, options, print) {
|
|
|
3153
3185
|
parts.push(path.call(print, 'finalizer'));
|
|
3154
3186
|
}
|
|
3155
3187
|
|
|
3156
|
-
if (node.pending) {
|
|
3157
|
-
parts.push(' pending ');
|
|
3158
|
-
parts.push(path.call(print, 'pending'));
|
|
3159
|
-
}
|
|
3160
|
-
|
|
3161
3188
|
return parts;
|
|
3162
3189
|
}
|
|
3163
3190
|
|
|
@@ -3168,8 +3195,22 @@ function printClassBody(node, path, options, print) {
|
|
|
3168
3195
|
|
|
3169
3196
|
const members = path.map(print, 'body');
|
|
3170
3197
|
|
|
3171
|
-
//
|
|
3172
|
-
|
|
3198
|
+
// Build content with proper blank line handling
|
|
3199
|
+
const contentParts = [];
|
|
3200
|
+
for (let i = 0; i < members.length; i++) {
|
|
3201
|
+
if (i > 0) {
|
|
3202
|
+
// Check if we should add a blank line between members
|
|
3203
|
+
const prevNode = node.body[i - 1];
|
|
3204
|
+
const currNode = node.body[i];
|
|
3205
|
+
if (shouldAddBlankLine(prevNode, currNode)) {
|
|
3206
|
+
contentParts.push(line);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
contentParts.push(line);
|
|
3210
|
+
contentParts.push(members[i]);
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
return group(['{', indent(concat(contentParts)), line, '}']);
|
|
3173
3214
|
}
|
|
3174
3215
|
|
|
3175
3216
|
function printPropertyDefinition(node, path, options, print) {
|
|
@@ -3572,6 +3613,9 @@ function printTSTypeParameterDeclaration(node, path, options, print) {
|
|
|
3572
3613
|
if (i > 0) parts.push(', ');
|
|
3573
3614
|
parts.push(paramList[i]);
|
|
3574
3615
|
}
|
|
3616
|
+
if (node.params.length === 1 && node.extra?.trailingComma !== undefined) {
|
|
3617
|
+
parts.push(',');
|
|
3618
|
+
}
|
|
3575
3619
|
parts.push('>');
|
|
3576
3620
|
return parts;
|
|
3577
3621
|
}
|
|
@@ -3598,15 +3642,38 @@ function printTSTypeParameterInstantiation(node, path, options, print) {
|
|
|
3598
3642
|
return '';
|
|
3599
3643
|
}
|
|
3600
3644
|
|
|
3601
|
-
const parts = [];
|
|
3602
|
-
parts.push('<');
|
|
3603
3645
|
const paramList = path.map(print, 'params');
|
|
3646
|
+
|
|
3647
|
+
// Check if any param has line breaks (e.g., contains object types)
|
|
3648
|
+
const hasBreakingParam = paramList.some((param) => willBreak(param));
|
|
3649
|
+
|
|
3650
|
+
// Build inline version: <T, U>
|
|
3651
|
+
const inlineParts = ['<'];
|
|
3604
3652
|
for (let i = 0; i < paramList.length; i++) {
|
|
3605
|
-
if (i > 0)
|
|
3653
|
+
if (i > 0) inlineParts.push(', ');
|
|
3654
|
+
inlineParts.push(paramList[i]);
|
|
3655
|
+
}
|
|
3656
|
+
inlineParts.push('>');
|
|
3657
|
+
|
|
3658
|
+
// If any param breaks, use the breaking version with proper indentation
|
|
3659
|
+
if (hasBreakingParam) {
|
|
3660
|
+
// Build breaking version: <\n T,\n U\n>
|
|
3661
|
+
const breakingParts = [];
|
|
3662
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
3663
|
+
if (i > 0) breakingParts.push(',', hardline);
|
|
3664
|
+
breakingParts.push(paramList[i]);
|
|
3665
|
+
}
|
|
3666
|
+
return group(concat(['<', indent(concat([hardline, ...breakingParts])), hardline, '>']));
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
// Otherwise use group to allow natural breaking
|
|
3670
|
+
const parts = [];
|
|
3671
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
3672
|
+
if (i > 0) parts.push(',', line);
|
|
3606
3673
|
parts.push(paramList[i]);
|
|
3607
3674
|
}
|
|
3608
|
-
|
|
3609
|
-
return concat(parts);
|
|
3675
|
+
|
|
3676
|
+
return group(concat(['<', indent(concat([softline, ...parts])), softline, '>']));
|
|
3610
3677
|
}
|
|
3611
3678
|
|
|
3612
3679
|
function printSwitchStatement(node, path, options, print) {
|
|
@@ -4725,33 +4792,19 @@ function printMemberExpressionSimple(node, options, computed = false) {
|
|
|
4725
4792
|
|
|
4726
4793
|
function printElement(node, path, options, print) {
|
|
4727
4794
|
const tagName = printMemberExpressionSimple(node.id, options);
|
|
4728
|
-
|
|
4729
4795
|
const elementLeadingComments = getElementLeadingComments(node);
|
|
4730
|
-
const openingTagEndIndex =
|
|
4731
|
-
node?.metadata && typeof node.metadata.openingTagEnd === 'number'
|
|
4732
|
-
? node.metadata.openingTagEnd
|
|
4733
|
-
: null;
|
|
4734
|
-
const nodeStartIndex = typeof node.start === 'number' ? node.start : null;
|
|
4735
|
-
const nodeEndIndex = typeof node.end === 'number' ? node.end : null;
|
|
4736
4796
|
|
|
4737
4797
|
// `metadata.elementLeadingComments` may include comments that actually appear *inside* the element
|
|
4738
4798
|
// body (after the opening tag). Those must not be hoisted before the element.
|
|
4739
|
-
const outerElementLeadingComments =
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
(comment) =>
|
|
4749
|
-
typeof comment.start === 'number' &&
|
|
4750
|
-
comment.start >= openingTagEndIndex &&
|
|
4751
|
-
comment.start < nodeEndIndex,
|
|
4752
|
-
)
|
|
4753
|
-
: [];
|
|
4754
|
-
|
|
4799
|
+
const outerElementLeadingComments = elementLeadingComments.filter(
|
|
4800
|
+
(comment) => typeof comment.start !== 'number' || comment.start < node.start,
|
|
4801
|
+
);
|
|
4802
|
+
const innerElementBodyComments = elementLeadingComments.filter(
|
|
4803
|
+
(comment) =>
|
|
4804
|
+
typeof comment.start === 'number' &&
|
|
4805
|
+
comment.start >= node.openingElement.end &&
|
|
4806
|
+
comment.start < node.end,
|
|
4807
|
+
);
|
|
4755
4808
|
const metadataCommentParts =
|
|
4756
4809
|
outerElementLeadingComments.length > 0
|
|
4757
4810
|
? createElementLevelCommentParts(outerElementLeadingComments)
|
|
@@ -4842,9 +4895,7 @@ function printElement(node, path, options, print) {
|
|
|
4842
4895
|
const finalChildren = [];
|
|
4843
4896
|
const sortedInnerElementBodyComments =
|
|
4844
4897
|
innerElementBodyComments.length > 0
|
|
4845
|
-
? innerElementBodyComments
|
|
4846
|
-
.slice()
|
|
4847
|
-
.sort((a, b) => (a.start ?? 0) - (b.start ?? 0))
|
|
4898
|
+
? innerElementBodyComments.slice().sort((a, b) => (a.start ?? 0) - (b.start ?? 0))
|
|
4848
4899
|
: [];
|
|
4849
4900
|
let innerElementBodyCommentIndex = 0;
|
|
4850
4901
|
|
|
@@ -4915,7 +4966,8 @@ function printElement(node, path, options, print) {
|
|
|
4915
4966
|
let insertedBodyCommentsBetween = false;
|
|
4916
4967
|
if (innerElementBodyCommentIndex < sortedInnerElementBodyComments.length) {
|
|
4917
4968
|
const currentChildEnd = getNodeEndIndex(currentChild);
|
|
4918
|
-
const nextChildStart =
|
|
4969
|
+
const nextChildStart =
|
|
4970
|
+
nextChild && typeof nextChild.start === 'number' ? nextChild.start : null;
|
|
4919
4971
|
if (typeof currentChildEnd === 'number') {
|
|
4920
4972
|
const commentsBetween = [];
|
|
4921
4973
|
while (innerElementBodyCommentIndex < sortedInnerElementBodyComments.length) {
|
package/src/index.test.js
CHANGED
|
@@ -1729,6 +1729,58 @@ files = [...(files ?? []), ...dt.files];`;
|
|
|
1729
1729
|
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
1730
1730
|
expect(result).toBeWithNewline(expected);
|
|
1731
1731
|
});
|
|
1732
|
+
|
|
1733
|
+
it('should preserve <script> tags', async () => {
|
|
1734
|
+
const expected = `<script>
|
|
1735
|
+
const i = 2;
|
|
1736
|
+
</script>`;
|
|
1737
|
+
|
|
1738
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
1739
|
+
expect(result).toBeWithNewline(expected);
|
|
1740
|
+
});
|
|
1741
|
+
|
|
1742
|
+
it('should preserve component as a named or an anonymous property', async () => {
|
|
1743
|
+
const expected = `const UI = {
|
|
1744
|
+
span: component Span() {
|
|
1745
|
+
<span>{'Hello from Span'}</span>
|
|
1746
|
+
},
|
|
1747
|
+
button: component({ children }) {
|
|
1748
|
+
<button>
|
|
1749
|
+
<children />
|
|
1750
|
+
</button>
|
|
1751
|
+
},
|
|
1752
|
+
};`;
|
|
1753
|
+
|
|
1754
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
1755
|
+
expect(result).toBeWithNewline(expected);
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
it('should preserve the order of try / pending / catch block', async () => {
|
|
1759
|
+
const expected = `component Test() {
|
|
1760
|
+
let items: TrackedArray<string> | null = null;
|
|
1761
|
+
let error: string | null = null;
|
|
1762
|
+
|
|
1763
|
+
async function* throwingIterable() {
|
|
1764
|
+
throw new Error('Async error');
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
try {
|
|
1768
|
+
items = await TrackedArray.fromAsync(throwingIterable());
|
|
1769
|
+
for (const item of items) {
|
|
1770
|
+
<li>{item}</li>
|
|
1771
|
+
}
|
|
1772
|
+
} pending {
|
|
1773
|
+
<div>{'Loading...'}</div>
|
|
1774
|
+
} catch (e) {
|
|
1775
|
+
error = (e as Error).message;
|
|
1776
|
+
} finally {
|
|
1777
|
+
<div>{'finally block'}</div>
|
|
1778
|
+
}
|
|
1779
|
+
}`;
|
|
1780
|
+
|
|
1781
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
1782
|
+
expect(result).toBeWithNewline(expected);
|
|
1783
|
+
});
|
|
1732
1784
|
});
|
|
1733
1785
|
|
|
1734
1786
|
describe('edge cases', () => {
|
|
@@ -2164,6 +2216,28 @@ const obj2 = #{
|
|
|
2164
2216
|
expect(result).toBeWithNewline(expected);
|
|
2165
2217
|
});
|
|
2166
2218
|
|
|
2219
|
+
it('should preserve comments inside js/ts blocks inside markup', async () => {
|
|
2220
|
+
const expected = `component App() {
|
|
2221
|
+
<button
|
|
2222
|
+
onClick={() => {
|
|
2223
|
+
@hasError = false;
|
|
2224
|
+
try {
|
|
2225
|
+
@hasError = false;
|
|
2226
|
+
// @ts-ignore
|
|
2227
|
+
obj['nonexistent']();
|
|
2228
|
+
} catch {
|
|
2229
|
+
// @hasError = true;
|
|
2230
|
+
}
|
|
2231
|
+
}}
|
|
2232
|
+
>
|
|
2233
|
+
{'Nonexistent'}
|
|
2234
|
+
</button>
|
|
2235
|
+
}`;
|
|
2236
|
+
|
|
2237
|
+
const result = await format(expected, { singleQuote: true, printWidth: 100 });
|
|
2238
|
+
expect(result).toBeWithNewline(expected);
|
|
2239
|
+
});
|
|
2240
|
+
|
|
2167
2241
|
it('should properly format array with various sized strings and 100 printWidth', async () => {
|
|
2168
2242
|
const expected = `component App() {
|
|
2169
2243
|
const d = [
|
|
@@ -2566,6 +2640,26 @@ const items = [] as unknown[];`;
|
|
|
2566
2640
|
expect(result).toBeWithNewline(expected);
|
|
2567
2641
|
});
|
|
2568
2642
|
|
|
2643
|
+
it('should format nested generics types', async () => {
|
|
2644
|
+
const expected = `component Test() {
|
|
2645
|
+
const [children, rest] = trackSplit<
|
|
2646
|
+
PropsWithChildren<{
|
|
2647
|
+
class: string;
|
|
2648
|
+
id: string;
|
|
2649
|
+
onClick: EventListener;
|
|
2650
|
+
}>,
|
|
2651
|
+
keyof PropsWithChildren<{
|
|
2652
|
+
class: string;
|
|
2653
|
+
id: string;
|
|
2654
|
+
onClick: EventListener;
|
|
2655
|
+
}>
|
|
2656
|
+
>(props as Props, ['children']);
|
|
2657
|
+
}`;
|
|
2658
|
+
|
|
2659
|
+
const result = await format(expected, { singleQuote: true });
|
|
2660
|
+
expect(result).toBeWithNewline(expected);
|
|
2661
|
+
});
|
|
2662
|
+
|
|
2569
2663
|
it('should format TypeScript tuple types (TSTupleType)', async () => {
|
|
2570
2664
|
const input = `type T = [string, number, boolean];`;
|
|
2571
2665
|
const expected = `type T = [string, number, boolean];`;
|
|
@@ -2650,6 +2744,15 @@ const items = [] as unknown[];`;
|
|
|
2650
2744
|
expect(result).toBeWithNewline(expected);
|
|
2651
2745
|
});
|
|
2652
2746
|
|
|
2747
|
+
it('should keep the TSInstantiationExpression ', async () => {
|
|
2748
|
+
const expected = `component Test() {
|
|
2749
|
+
const items = (Promise<string[]>).reject(new Error('Async error'));
|
|
2750
|
+
}`;
|
|
2751
|
+
|
|
2752
|
+
const result = await format(expected, { singleQuote: true });
|
|
2753
|
+
expect(result).toBeWithNewline(expected);
|
|
2754
|
+
});
|
|
2755
|
+
|
|
2653
2756
|
it('should format TSNonNullExpression in complex expressions', async () => {
|
|
2654
2757
|
const input = `function getValue(x?:string){return x!.toUpperCase()}`;
|
|
2655
2758
|
const expected = `function getValue(x?: string) {
|