@ripple-ts/prettier-plugin 0.2.156 → 0.2.158

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ripple-ts/prettier-plugin",
3
- "version": "0.2.156",
3
+ "version": "0.2.158",
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.156"
28
+ "ripple": "0.2.158"
29
29
  },
30
30
  "dependencies": {},
31
31
  "files": [
package/src/index.js CHANGED
@@ -1163,10 +1163,19 @@ function printRippleNode(node, path, options, print, args) {
1163
1163
  break;
1164
1164
  }
1165
1165
 
1166
+ case 'TSNonNullExpression': {
1167
+ nodeContent = concat([path.call(print, 'expression'), '!']);
1168
+ break;
1169
+ }
1170
+
1171
+ case 'JSXExpressionContainer': {
1172
+ nodeContent = concat(['{', path.call(print, 'expression'), '}']);
1173
+ break;
1174
+ }
1175
+
1166
1176
  case 'NewExpression':
1167
1177
  nodeContent = printNewExpression(node, path, options, print);
1168
1178
  break;
1169
-
1170
1179
  case 'TemplateLiteral':
1171
1180
  nodeContent = printTemplateLiteral(node, path, options, print);
1172
1181
  break;
@@ -1324,8 +1333,10 @@ function printRippleNode(node, path, options, print, args) {
1324
1333
  const trackedPrefix = node.tracked ? '@' : '';
1325
1334
  let identifierContent;
1326
1335
  if (node.typeAnnotation) {
1336
+ const optionalMarker = node.optional ? '?' : '';
1327
1337
  identifierContent = concat([
1328
1338
  trackedPrefix + node.name,
1339
+ optionalMarker,
1329
1340
  ': ',
1330
1341
  path.call(print, 'typeAnnotation'),
1331
1342
  ]);
@@ -1348,7 +1359,6 @@ function printRippleNode(node, path, options, print, args) {
1348
1359
  }
1349
1360
  break;
1350
1361
  }
1351
-
1352
1362
  case 'Literal':
1353
1363
  // Handle regex literals specially
1354
1364
  if (node.regex) {
@@ -1645,10 +1655,13 @@ function printRippleNode(node, path, options, print, args) {
1645
1655
  nodeContent = printTSPropertySignature(node, path, options, print);
1646
1656
  break;
1647
1657
 
1658
+ case 'TSMethodSignature':
1659
+ nodeContent = printTSMethodSignature(node, path, options, print);
1660
+ break;
1661
+
1648
1662
  case 'TSEnumMember':
1649
1663
  nodeContent = printTSEnumMember(node, path, options, print);
1650
1664
  break;
1651
-
1652
1665
  case 'TSLiteralType':
1653
1666
  nodeContent = path.call(print, 'literal');
1654
1667
  break;
@@ -3863,6 +3876,47 @@ function printTSPropertySignature(node, path, options, print) {
3863
3876
  return concat(parts);
3864
3877
  }
3865
3878
 
3879
+ function printTSMethodSignature(node, path, options, print) {
3880
+ const parts = [];
3881
+
3882
+ // Print the method name/key
3883
+ parts.push(path.call(print, 'key'));
3884
+
3885
+ // Add optional marker if present
3886
+ if (node.optional) {
3887
+ parts.push('?');
3888
+ }
3889
+
3890
+ // Add TypeScript generics/type parameters if present
3891
+ if (node.typeParameters) {
3892
+ const typeParams = path.call(print, 'typeParameters');
3893
+ if (Array.isArray(typeParams)) {
3894
+ parts.push(...typeParams);
3895
+ } else {
3896
+ parts.push(typeParams);
3897
+ }
3898
+ }
3899
+
3900
+ // Print parameters - use 'parameters' property for TypeScript signature nodes
3901
+ parts.push('(');
3902
+ if (node.parameters && node.parameters.length > 0) {
3903
+ const params = path.map(print, 'parameters');
3904
+ for (let i = 0; i < params.length; i++) {
3905
+ if (i > 0) parts.push(', ');
3906
+ parts.push(params[i]);
3907
+ }
3908
+ }
3909
+ parts.push(')');
3910
+
3911
+ // Return type annotation
3912
+ if (node.typeAnnotation) {
3913
+ parts.push(': ');
3914
+ parts.push(path.call(print, 'typeAnnotation'));
3915
+ }
3916
+
3917
+ return concat(parts);
3918
+ }
3919
+
3866
3920
  function printTSTypeReference(node, path, options, print) {
3867
3921
  const parts = [path.call(print, 'typeName')];
3868
3922
 
@@ -4276,29 +4330,59 @@ function printTsxCompat(node, path, options, print) {
4276
4330
  }
4277
4331
 
4278
4332
  // Print JSXElement children - they remain as JSX
4279
- // Filter out whitespace-only JSXText nodes
4333
+ // Filter out whitespace-only JSXText nodes and merge adjacent text-like nodes
4280
4334
  const finalChildren = [];
4335
+ let accumulatedText = '';
4281
4336
 
4282
4337
  for (let i = 0; i < node.children.length; i++) {
4283
4338
  const child = node.children[i];
4284
4339
 
4285
- // Skip whitespace-only JSXText nodes
4286
- if (child.type === 'JSXText' && !child.value.trim()) {
4287
- continue;
4288
- }
4340
+ // Check if this is a text-like node (JSXText or Identifier in JSX context)
4341
+ const isTextLike = child.type === 'JSXText' || child.type === 'Identifier';
4289
4342
 
4290
- const printedChild = path.call(print, 'children', i);
4291
- finalChildren.push(printedChild);
4343
+ if (isTextLike) {
4344
+ // Get the text content
4345
+ let text;
4346
+ if (child.type === 'JSXText') {
4347
+ text = child.value.trim();
4348
+ } else if (child.type === 'Identifier') {
4349
+ text = child.name;
4350
+ }
4292
4351
 
4293
- if (i < node.children.length - 1) {
4294
- // Only add hardline if the next child is not whitespace-only
4295
- const nextChild = node.children[i + 1];
4296
- if (nextChild && !(nextChild.type === 'JSXText' && !nextChild.value.trim())) {
4352
+ if (text) {
4353
+ if (accumulatedText) {
4354
+ accumulatedText += ' ' + text;
4355
+ } else {
4356
+ accumulatedText = text;
4357
+ }
4358
+ }
4359
+ } else {
4360
+ // Before adding non-text node, flush accumulated text
4361
+ if (accumulatedText) {
4362
+ if (finalChildren.length > 0) {
4363
+ finalChildren.push(hardline);
4364
+ }
4365
+ finalChildren.push(accumulatedText);
4366
+ accumulatedText = '';
4367
+ }
4368
+
4369
+ if (finalChildren.length > 0) {
4297
4370
  finalChildren.push(hardline);
4298
4371
  }
4372
+
4373
+ const printedChild = path.call(print, 'children', i);
4374
+ finalChildren.push(printedChild);
4299
4375
  }
4300
4376
  }
4301
4377
 
4378
+ // Don't forget any remaining accumulated text
4379
+ if (accumulatedText) {
4380
+ if (finalChildren.length > 0) {
4381
+ finalChildren.push(hardline);
4382
+ }
4383
+ finalChildren.push(accumulatedText);
4384
+ }
4385
+
4302
4386
  // Format the TsxCompat element
4303
4387
  const elementOutput = group([
4304
4388
  tagName,
@@ -4355,26 +4439,45 @@ function printJSXElement(node, path, options, print) {
4355
4439
  return concat(['<', tagName, attributesDoc, '></', tagName, '>']);
4356
4440
  }
4357
4441
 
4358
- // Format children - filter out empty text nodes
4442
+ // Format children - filter out empty text nodes and merge adjacent text nodes
4359
4443
  const childrenDocs = [];
4444
+ let currentText = '';
4445
+
4360
4446
  for (let i = 0; i < node.children.length; i++) {
4361
4447
  const child = node.children[i];
4362
4448
 
4363
4449
  if (child.type === 'JSXText') {
4364
- // Handle JSX text nodes - only include if not just whitespace
4365
- const text = child.value;
4366
- if (text.trim()) {
4367
- childrenDocs.push(text);
4450
+ // Accumulate text content, preserving spaces between words
4451
+ const trimmed = child.value.trim();
4452
+ if (trimmed) {
4453
+ if (currentText) {
4454
+ currentText += ' ' + trimmed;
4455
+ } else {
4456
+ currentText = trimmed;
4457
+ }
4368
4458
  }
4369
- } else if (child.type === 'JSXExpressionContainer') {
4370
- // Handle JSX expression containers
4371
- childrenDocs.push(concat(['{', path.call(print, 'children', i, 'expression'), '}']));
4372
4459
  } else {
4373
- // Handle nested JSX elements
4374
- childrenDocs.push(path.call(print, 'children', i));
4460
+ // If we have accumulated text, push it before the non-text node
4461
+ if (currentText) {
4462
+ childrenDocs.push(currentText);
4463
+ currentText = '';
4464
+ }
4465
+
4466
+ if (child.type === 'JSXExpressionContainer') {
4467
+ // Handle JSX expression containers
4468
+ childrenDocs.push(concat(['{', path.call(print, 'children', i, 'expression'), '}']));
4469
+ } else {
4470
+ // Handle nested JSX elements
4471
+ childrenDocs.push(path.call(print, 'children', i));
4472
+ }
4375
4473
  }
4376
4474
  }
4377
4475
 
4476
+ // Don't forget any remaining text
4477
+ if (currentText) {
4478
+ childrenDocs.push(currentText);
4479
+ }
4480
+
4378
4481
  // Check if content can be inlined (single text node or single expression)
4379
4482
  if (childrenDocs.length === 1 && typeof childrenDocs[0] === 'string') {
4380
4483
  return concat(['<', tagName, attributesDoc, '>', childrenDocs[0], '</', tagName, '>']);
package/src/index.test.js CHANGED
@@ -2282,6 +2282,44 @@ const items = [] as unknown[];`;
2282
2282
  expect(result).toBeWithNewline(expected);
2283
2283
  });
2284
2284
 
2285
+ it('should format TSMethodSignature in interfaces', async () => {
2286
+ const input = `interface API{get(path:string):Promise<Response>;post<T>(path:string,data:T):Promise<Response>;delete?(id:number):void}`;
2287
+ const expected = `interface API {
2288
+ get(path: string): Promise<Response>;
2289
+ post<T>(path: string, data: T): Promise<Response>;
2290
+ delete?(id: number): void;
2291
+ }`;
2292
+ const result = await format(input);
2293
+ expect(result).toBeWithNewline(expected);
2294
+ });
2295
+
2296
+ it('should format TSMethodSignature with type parameters', async () => {
2297
+ const input = `interface Collection{map<U>(fn:(item:T)=>U):U[];filter(predicate:(item:T)=>boolean):T[]}`;
2298
+ const expected = `interface Collection {\n map<U>(fn: (item: T) => U): U[];\n filter(predicate: (item: T) => boolean): T[];\n}`;
2299
+ const result = await format(input);
2300
+ expect(result).toBeWithNewline(expected);
2301
+ });
2302
+
2303
+ it('should format TSNonNullExpression', async () => {
2304
+ const input = `component Test(){let value:string|null=null;let length=value!.length;<div>{length}</div>}`;
2305
+ const expected = `component Test() {
2306
+ let value: string | null = null;
2307
+ let length = value!.length;
2308
+ <div>{length}</div>
2309
+ }`;
2310
+ const result = await format(input);
2311
+ expect(result).toBeWithNewline(expected);
2312
+ });
2313
+
2314
+ it('should format TSNonNullExpression in complex expressions', async () => {
2315
+ const input = `function getValue(x?:string){return x!.toUpperCase()}`;
2316
+ const expected = `function getValue(x?: string) {
2317
+ return x!.toUpperCase();
2318
+ }`;
2319
+ const result = await format(input);
2320
+ expect(result).toBeWithNewline(expected);
2321
+ });
2322
+
2285
2323
  it('should retain templated declarations', async () => {
2286
2324
  const expected = `function Wrapper() {
2287
2325
  return {
@@ -3774,6 +3812,82 @@ component Polygon() {
3774
3812
  const result = await format(input, { singleQuote: true });
3775
3813
  expect(result).toBeWithNewline(expected);
3776
3814
  });
3815
+
3816
+ it('should format JSXExpressionContainer with function calls', async () => {
3817
+ const input = `function foo(){return 123}component App(){<div><tsx:react>{foo()}</tsx:react></div>}`;
3818
+
3819
+ const expected = `function foo() {
3820
+ return 123;
3821
+ }
3822
+ component App() {
3823
+ <div>
3824
+ <tsx:react>
3825
+ {foo()}
3826
+ </tsx:react>
3827
+ </div>
3828
+ }`;
3829
+
3830
+ const result = await format(input);
3831
+ expect(result).toBeWithNewline(expected);
3832
+ });
3833
+
3834
+ it('should format JSXExpressionContainer with function calls', async () => {
3835
+ const input = `function foo(){return 123}component App(){<div><tsx:react>{foo()}<div>Hello world</div>Hello world</tsx:react></div>}`;
3836
+
3837
+ const expected = `function foo() {
3838
+ return 123;
3839
+ }
3840
+ component App() {
3841
+ <div>
3842
+ <tsx:react>
3843
+ {foo()}
3844
+ <div>Hello world</div>
3845
+ Hello world
3846
+ </tsx:react>
3847
+ </div>
3848
+ }`;
3849
+
3850
+ const result = await format(input);
3851
+ expect(result).toBeWithNewline(expected);
3852
+ });
3853
+
3854
+ it('should format JSXExpressionContainer with function calls #2', async () => {
3855
+ const input = `export component App() {
3856
+ <tsx:react>
3857
+ Hello world
3858
+ <DemoContext.Provider value={"Hello from Context!"}>
3859
+ <Child count={@count} />
3860
+ </DemoContext.Provider>
3861
+ </tsx:react>
3862
+ }`;
3863
+ const expected = `export component App() {
3864
+ <tsx:react>
3865
+ Hello world
3866
+ <DemoContext.Provider value={"Hello from Context!"}>
3867
+ <Child count={@count} />
3868
+ </DemoContext.Provider>
3869
+ </tsx:react>
3870
+ }`;
3871
+
3872
+ const result = await format(input);
3873
+ expect(result).toBeWithNewline(expected);
3874
+ });
3875
+ it('should format JSXExpressionContainer with complex expressions', async () => {
3876
+ const input = `component App(){let count=track(0);<tsx:react><div>{count*2+10}</div>{getMessage("test")}</tsx:react>}`;
3877
+
3878
+ const expected = `component App() {
3879
+ let count = track(0);
3880
+ <tsx:react>
3881
+ <div>
3882
+ {count * 2 + 10}
3883
+ </div>
3884
+ {getMessage('test')}
3885
+ </tsx:react>
3886
+ }`;
3887
+
3888
+ const result = await format(input, { singleQuote: true });
3889
+ expect(result).toBeWithNewline(expected);
3890
+ });
3777
3891
  });
3778
3892
  });
3779
3893
  });