@tsrx/prettier-plugin 0.3.26 → 0.3.27

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.26",
3
+ "version": "0.3.27",
4
4
  "description": "Ripple plugin for Prettier",
5
5
  "type": "module",
6
6
  "module": "src/index.js",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@types/node": "^24.3.0",
28
28
  "prettier": "^3.8.3",
29
- "ripple": "0.3.26"
29
+ "ripple": "0.3.27"
30
30
  },
31
31
  "dependencies": {
32
32
  "@tsrx/core": "0.0.7",
package/src/index.js CHANGED
@@ -6047,35 +6047,47 @@ function printElement(element, path, options, print) {
6047
6047
 
6048
6048
  const hasOpeningTagComments = openingTagCommentsSet.size > 0;
6049
6049
  let attrIndex = 0;
6050
+ let hasBreakingAttribute = false;
6051
+ const attrDocs = hasAttributes
6052
+ ? path.map((attrPath) => {
6053
+ const idx = attrIndex++;
6054
+ const commentsForAttr = openingTagCommentsByAttrIndex.get(idx);
6055
+ /** @type {Doc[]} */
6056
+ const parts = [];
6057
+ if (commentsForAttr) {
6058
+ for (const comment of commentsForAttr) {
6059
+ // Line comments (//) consume the rest of the line, so they must
6060
+ // use hardline to force a break. Block comments can use normal breaks.
6061
+ if (comment.type === 'Line') {
6062
+ parts.push(hardline);
6063
+ parts.push('//' + comment.value);
6064
+ } else if (comment.type === 'Block') {
6065
+ parts.push(attrLineBreak);
6066
+ parts.push('/*' + comment.value + '*/');
6067
+ }
6068
+ }
6069
+ }
6070
+ parts.push(attrLineBreak);
6071
+ const attrDoc = print(attrPath);
6072
+ parts.push(attrDoc);
6073
+ if (!hasBreakingAttribute && willBreak(attrDoc)) {
6074
+ hasBreakingAttribute = true;
6075
+ }
6076
+ return parts;
6077
+ }, 'attributes')
6078
+ : [];
6079
+ const shouldForceBreak = hasOpeningTagComments || hasBreakingAttribute;
6050
6080
  const openingTag = group([
6051
6081
  '<',
6052
6082
  tagName,
6053
6083
  hasAttributes
6054
6084
  ? indent([
6055
- ...path.map((attrPath) => {
6056
- const idx = attrIndex++;
6057
- const commentsForAttr = openingTagCommentsByAttrIndex.get(idx);
6058
- /** @type {Doc[]} */
6059
- const parts = [];
6060
- if (commentsForAttr) {
6061
- for (const comment of commentsForAttr) {
6062
- // Line comments (//) consume the rest of the line, so they must
6063
- // use hardline to force a break. Block comments can use normal breaks.
6064
- if (comment.type === 'Line') {
6065
- parts.push(hardline);
6066
- parts.push('//' + comment.value);
6067
- } else if (comment.type === 'Block') {
6068
- parts.push(attrLineBreak);
6069
- parts.push('/*' + comment.value + '*/');
6070
- }
6071
- }
6072
- }
6073
- parts.push(attrLineBreak);
6074
- parts.push(print(attrPath));
6075
- return parts;
6076
- }, 'attributes'),
6077
- // Force the group to break when there are line comments in the opening tag
6078
- ...(hasOpeningTagComments ? [breakParent] : []),
6085
+ ...attrDocs,
6086
+ // Force the group to break when there are line comments in the opening tag,
6087
+ // or when any attribute value would break (e.g. multiline objects, ternaries).
6088
+ // This ensures attributes are broken onto separate lines rather than breaking
6089
+ // expression values inline on the same line as the tag name.
6090
+ ...(shouldForceBreak ? [breakParent] : []),
6079
6091
  ])
6080
6092
  : '',
6081
6093
  // Add line break opportunity before > or />
package/src/index.test.js CHANGED
@@ -926,6 +926,71 @@ export component Test({ a, b }: Props) {}`;
926
926
  expect(result).toBeWithNewline(expected);
927
927
  });
928
928
 
929
+ it('should prefer breaking attributes over breaking expression values (multiline object)', async () => {
930
+ const input = `component App() {
931
+ <div class={styles.item} data-active={state.active ? "true" : "false"} style={{ gridTemplateColumns: Icon ? "16px minmax(0, 1fr) auto" : "minmax(0, 1fr) auto" }}>
932
+ {'content'}
933
+ </div>
934
+ }`;
935
+ const expected = `component App() {
936
+ <div
937
+ class={styles.item}
938
+ data-active={state.active ? 'true' : 'false'}
939
+ style={{
940
+ gridTemplateColumns: Icon
941
+ ? '16px minmax(0, 1fr) auto'
942
+ : 'minmax(0, 1fr) auto',
943
+ }}
944
+ >
945
+ {'content'}
946
+ </div>
947
+ }`;
948
+
949
+ const result = await format(input, { singleQuote: true, printWidth: 80 });
950
+ expect(result).toBeWithNewline(expected);
951
+ });
952
+
953
+ it('should prefer breaking attributes over breaking expression values (multiline object, bracketSameLine)', async () => {
954
+ const input = `component App() {
955
+ <div class={styles.item} data-active={state.active ? "true" : "false"} style={{ gridTemplateColumns: Icon ? "16px minmax(0, 1fr) auto" : "minmax(0, 1fr) auto" }}>
956
+ {'content'}
957
+ </div>
958
+ }`;
959
+ const expected = `component App() {
960
+ <div
961
+ class={styles.item}
962
+ data-active={state.active ? 'true' : 'false'}
963
+ style={{
964
+ gridTemplateColumns: Icon
965
+ ? '16px minmax(0, 1fr) auto'
966
+ : 'minmax(0, 1fr) auto',
967
+ }}>
968
+ {'content'}
969
+ </div>
970
+ }`;
971
+
972
+ const result = await format(input, {
973
+ singleQuote: true,
974
+ printWidth: 80,
975
+ bracketSameLine: true,
976
+ });
977
+ expect(result).toBeWithNewline(expected);
978
+ });
979
+
980
+ it('should keep attributes on same line when no attribute value breaks', async () => {
981
+ const input = `component App() {
982
+ <button class="test another" onClick={handler}>
983
+ {'Click Me'}
984
+ </button>
985
+ }`;
986
+ const expected = `component App() {
987
+ <button class="test another" onClick={handler}>{'Click Me'}</button>
988
+ }`;
989
+
990
+ const result = await format(input, { singleQuote: true, printWidth: 80 });
991
+ expect(result).toBeWithNewline(expected);
992
+ });
993
+
929
994
  it('should not format function parameter spread', async () => {
930
995
  const expected = `component Two({ arg1, ...rest }) {}`;
931
996