@tsrx/prettier-plugin 0.3.65 → 0.3.67

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": "@tsrx/prettier-plugin",
3
- "version": "0.3.65",
3
+ "version": "0.3.67",
4
4
  "description": "Ripple plugin for Prettier",
5
5
  "type": "module",
6
6
  "module": "src/index.js",
@@ -27,7 +27,7 @@
27
27
  "prettier": "^3.8.3"
28
28
  },
29
29
  "dependencies": {
30
- "@tsrx/core": "0.1.13"
30
+ "@tsrx/core": "0.1.15"
31
31
  },
32
32
  "files": [
33
33
  "src/"
package/src/index.js CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  /** @typedef {Partial<Pick<ParserOptions, 'singleQuote' | 'jsxSingleQuote' | 'semi' | 'trailingComma' | 'useTabs' | 'tabWidth' | 'singleAttributePerLine' | 'bracketSameLine' | 'bracketSpacing' | 'arrowParens' | 'originalText' | 'printWidth'>> & { locStart: (node: AST.NodeWithLocation) => number, locEnd: (node: AST.NodeWithLocation) => number }} RippleFormatOptions */
18
18
 
19
- /** @typedef {{ isInAttribute?: boolean, isInArray?: boolean, allowInlineObject?: boolean, isConditionalTest?: boolean, isNestedConditional?: boolean, suppressLeadingComments?: boolean, suppressExpressionLeadingComments?: boolean, isInlineContext?: boolean, isStatement?: boolean, isLogicalAndOr?: boolean, allowShorthandProperty?: boolean, isFirstChild?: boolean, skipComponentLabel?: boolean, noBreakInside?: boolean, expandLastArg?: boolean }} PrintArgs */
19
+ /** @typedef {{ isInAttribute?: boolean, isInArray?: boolean, allowInlineObject?: boolean, isConditionalTest?: boolean, isNestedConditional?: boolean, suppressLeadingComments?: boolean, suppressExpressionLeadingComments?: boolean, isInlineContext?: boolean, isStatement?: boolean, isLogicalAndOr?: boolean, allowShorthandProperty?: boolean, isFirstChild?: boolean, skipComponentLabel?: boolean, noBreakInside?: boolean, expandLastArg?: boolean, preferInlineSimpleUnionType?: boolean }} PrintArgs */
20
20
 
21
21
  import { parseModule } from '@tsrx/core';
22
22
  import { doc } from 'prettier';
@@ -35,6 +35,7 @@ const {
35
35
  breakParent,
36
36
  indentIfBreak,
37
37
  lineSuffix,
38
+ align,
38
39
  } = builders;
39
40
  const { replaceEndOfLine, willBreak } = utils;
40
41
 
@@ -1502,16 +1503,24 @@ function printRippleNode(node, path, options, print, args) {
1502
1503
  break;
1503
1504
 
1504
1505
  case 'TSAsExpression': {
1505
- nodeContent = [path.call(print, 'expression'), ' as ', path.call(print, 'typeAnnotation')];
1506
+ const typeAnnotation = path.call(
1507
+ (typePath) => print(typePath, { preferInlineSimpleUnionType: true }),
1508
+ 'typeAnnotation',
1509
+ );
1510
+ nodeContent = willBreak(typeAnnotation)
1511
+ ? [path.call(print, 'expression'), ' as', indent([line, typeAnnotation])]
1512
+ : [path.call(print, 'expression'), ' as ', typeAnnotation];
1506
1513
  break;
1507
1514
  }
1508
1515
 
1509
1516
  case 'TSSatisfiesExpression': {
1510
- nodeContent = [
1511
- path.call(print, 'expression'),
1512
- ' satisfies ',
1513
- path.call(print, 'typeAnnotation'),
1514
- ];
1517
+ const typeAnnotation = path.call(
1518
+ (typePath) => print(typePath, { preferInlineSimpleUnionType: true }),
1519
+ 'typeAnnotation',
1520
+ );
1521
+ nodeContent = willBreak(typeAnnotation)
1522
+ ? [path.call(print, 'expression'), ' satisfies', indent([line, typeAnnotation])]
1523
+ : [path.call(print, 'expression'), ' satisfies ', typeAnnotation];
1515
1524
  break;
1516
1525
  }
1517
1526
 
@@ -2137,8 +2146,7 @@ function printRippleNode(node, path, options, print, args) {
2137
2146
  break;
2138
2147
 
2139
2148
  case 'TSUnionType': {
2140
- const types = path.map(print, 'types');
2141
- nodeContent = join(' | ', types);
2149
+ nodeContent = printTSUnionType(node, path, print, args);
2142
2150
  break;
2143
2151
  }
2144
2152
 
@@ -2182,9 +2190,7 @@ function printRippleNode(node, path, options, print, args) {
2182
2190
 
2183
2191
  // Handle return type
2184
2192
  parts.push(' => ');
2185
- if (node.returnType) {
2186
- parts.push(path.call(print, 'returnType'));
2187
- } else if (node.typeAnnotation) {
2193
+ if (node.typeAnnotation) {
2188
2194
  parts.push(path.call(print, 'typeAnnotation'));
2189
2195
  }
2190
2196
 
@@ -4420,6 +4426,35 @@ function printTSTypeAliasDeclaration(node, path, options, print) {
4420
4426
  return group([head, ' =', indent([line, path.call(print, 'typeAnnotation')]), semi(options)]);
4421
4427
  }
4422
4428
 
4429
+ /**
4430
+ * Print a TypeScript union type
4431
+ * @param {AST.TSUnionType} node - The union node
4432
+ * @param {AstPath<AST.TSUnionType>} path - The AST path
4433
+ * @param {PrintFn} print - Print callback
4434
+ * @param {PrintArgs} [args] - Additional context arguments
4435
+ * @returns {Doc}
4436
+ */
4437
+ function printTSUnionType(node, path, print, args) {
4438
+ const types = path.map(print, 'types');
4439
+ const inlineDoc = join(' | ', types);
4440
+ const multilineDoc = [
4441
+ '| ',
4442
+ join(
4443
+ [hardline, '| '],
4444
+ types.map((typeDoc) => align(2, typeDoc)),
4445
+ ),
4446
+ ];
4447
+ const shouldBreak = node.types.some(
4448
+ (typeNode, index) => !wasOriginallySingleLine(typeNode) || willBreak(types[index]),
4449
+ );
4450
+
4451
+ if (args?.preferInlineSimpleUnionType && !types.some((typeDoc) => willBreak(typeDoc))) {
4452
+ return inlineDoc;
4453
+ }
4454
+
4455
+ return shouldBreak ? group(multilineDoc) : conditionalGroup([inlineDoc, multilineDoc]);
4456
+ }
4457
+
4423
4458
  /**
4424
4459
  * Print a TypeScript enum declaration
4425
4460
  * @param {AST.TSEnumDeclaration} node - The enum declaration node
@@ -5495,9 +5530,7 @@ function printTSConstructorType(node, path, options, print) {
5495
5530
  }
5496
5531
  parts.push(')');
5497
5532
  parts.push(' => ');
5498
- if (node.returnType) {
5499
- parts.push(path.call(print, 'returnType'));
5500
- } else if (node.typeAnnotation) {
5533
+ if (node.typeAnnotation) {
5501
5534
  parts.push(path.call(print, 'typeAnnotation'));
5502
5535
  }
5503
5536
  return parts;
package/src/index.test.js CHANGED
@@ -3035,6 +3035,41 @@ function test() {
3035
3035
  expect(result).toBeWithNewline(expected);
3036
3036
  });
3037
3037
 
3038
+ it('should preserve comments in destructured typed component parameters', async () => {
3039
+ const expected = `component Child({
3040
+ tr: &[count, tr],
3041
+ // test,
3042
+ }: {
3043
+ tr: [number, Tracked<number>];
3044
+ // test: (node: HTMLDivElement) => void;
3045
+ }) {
3046
+ <button
3047
+ onClick={() => {
3048
+ count++;
3049
+ tr[0]++;
3050
+ }}
3051
+ >
3052
+ {count}
3053
+ </button>
3054
+ }`;
3055
+
3056
+ const result = await format(expected);
3057
+ expect(result).toBeWithNewline(expected);
3058
+ });
3059
+
3060
+ it('should preserve comments in destructured typed function parameters', async () => {
3061
+ const expected = `function Child({
3062
+ tr: &[count, tr],
3063
+ // test,
3064
+ }: {
3065
+ tr: [number, Tracked<number>];
3066
+ // test: (node: HTMLDivElement) => void;
3067
+ }) {}`;
3068
+
3069
+ const result = await format(expected);
3070
+ expect(result).toBeWithNewline(expected);
3071
+ });
3072
+
3038
3073
  it('should preserve trailing comments in call arguments', async () => {
3039
3074
  const expected = `fn(
3040
3075
  arg1,
@@ -3782,6 +3817,70 @@ second"</pre>
3782
3817
  expect(result).toBeWithNewline(expected);
3783
3818
  });
3784
3819
 
3820
+ it('should normalize simple cast union types at print width 100', async () => {
3821
+ const input = `const alphaLink = container.querySelector('[data-route-id="alpha"]') as HTMLAnchorElement | null;
3822
+ const saveButton = container.querySelector('[data-action-id="save"]') as HTMLButtonElement | null;
3823
+ const deleteButton = container.querySelector('[data-action-id="delete"]') as | HTMLButtonElement
3824
+ | null;`;
3825
+
3826
+ const expected = `const alphaLink = container.querySelector('[data-route-id="alpha"]') as HTMLAnchorElement | null;
3827
+ const saveButton = container.querySelector('[data-action-id="save"]') as HTMLButtonElement | null;
3828
+ const deleteButton = container.querySelector(
3829
+ '[data-action-id="delete"]',
3830
+ ) as HTMLButtonElement | null;`;
3831
+
3832
+ const result = await format(input, { printWidth: 100, singleQuote: true });
3833
+ expect(result).toBeWithNewline(expected);
3834
+ });
3835
+
3836
+ it('should normalize simple cast union types at print width 80', async () => {
3837
+ const input = `const alphaLink = container.querySelector('[data-route-id="alpha"]') as HTMLAnchorElement | null;
3838
+ const saveButton = container.querySelector('[data-action-id="save"]') as HTMLButtonElement | null;
3839
+ const deleteButton = container.querySelector('[data-action-id="delete"]') as | HTMLButtonElement
3840
+ | null;`;
3841
+
3842
+ const expected = `const alphaLink = container.querySelector(
3843
+ '[data-route-id="alpha"]',
3844
+ ) as HTMLAnchorElement | null;
3845
+ const saveButton = container.querySelector(
3846
+ '[data-action-id="save"]',
3847
+ ) as HTMLButtonElement | null;
3848
+ const deleteButton = container.querySelector(
3849
+ '[data-action-id="delete"]',
3850
+ ) as HTMLButtonElement | null;`;
3851
+
3852
+ const result = await format(input, { printWidth: 80, singleQuote: true });
3853
+ expect(result).toBeWithNewline(expected);
3854
+ });
3855
+
3856
+ it('should format multiline TypeScript union object types like Prettier TypeScript', async () => {
3857
+ const input = `type SvgIconSource = { name: SvgIconName; data?: never } | {
3858
+ data: SvgIconData;
3859
+ name?: never;
3860
+ }`;
3861
+
3862
+ const expected = `type SvgIconSource =
3863
+ | { name: SvgIconName; data?: never }
3864
+ | {
3865
+ data: SvgIconData;
3866
+ name?: never;
3867
+ };`;
3868
+
3869
+ const result = await format(input);
3870
+ expect(result).toBeWithNewline(expected);
3871
+ });
3872
+
3873
+ it('should break long TypeScript union types with leading operators', async () => {
3874
+ const input = `type Source = SomeVeryLongTypeNameThatWillDefinitelyNotFit | AnotherVeryLongTypeNameThatWillDefinitelyNotFit;`;
3875
+
3876
+ const expected = `type Source =
3877
+ | SomeVeryLongTypeNameThatWillDefinitelyNotFit
3878
+ | AnotherVeryLongTypeNameThatWillDefinitelyNotFit;`;
3879
+
3880
+ const result = await format(input, { printWidth: 50 });
3881
+ expect(result).toBeWithNewline(expected);
3882
+ });
3883
+
3785
3884
  it('should not overindent multiline object type aliases', async () => {
3786
3885
  const input = `type ModuleShape = {
3787
3886
  default: ComponentType<{ value: string }>;