prettier-plugin-hug-call-arguments 0.1.9 → 0.1.11
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/README.md +22 -0
- package/dist/index.js +96 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -180,6 +180,28 @@ regardless of the original formatting.
|
|
|
180
180
|
- there's no blank line between arguments, no comments on the relevant
|
|
181
181
|
nodes, and no TS type arguments
|
|
182
182
|
|
|
183
|
+
**Hugging a call whose sole argument needs it:**
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
// input
|
|
187
|
+
firstValueFrom(client.getX({ fileKey: data.fileKey, frameCount: FRAME_COUNT }));
|
|
188
|
+
|
|
189
|
+
// with this plugin
|
|
190
|
+
firstValueFrom(client.getX({
|
|
191
|
+
fileKey: data.fileKey,
|
|
192
|
+
frameCount: FRAME_COUNT,
|
|
193
|
+
}));
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
- the call has exactly one argument, and it's directly an object or array
|
|
197
|
+
literal that needs multi-line printing
|
|
198
|
+
- Prettier already hugs this shape reliably *in isolation*, but its own
|
|
199
|
+
"does the opening line fit" check can still give up and break the call's
|
|
200
|
+
own parens too once this plugin has fused several outer layers together
|
|
201
|
+
(pushing the real column deeper than Prettier expected) — there's
|
|
202
|
+
essentially never a readability win to that fallback over just hugging
|
|
203
|
+
directly, so this always does instead
|
|
204
|
+
|
|
183
205
|
**Hugging an arrow function's concise body:**
|
|
184
206
|
|
|
185
207
|
```js
|
package/dist/index.js
CHANGED
|
@@ -67,7 +67,15 @@ const estreePrinter = estree.printers.estree;
|
|
|
67
67
|
function hasComment(node) {
|
|
68
68
|
return Boolean(node?.comments && node.comments.length > 0);
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* The terminal thing worth hugging at the bottom of a chain of wrapping
|
|
72
|
+
* calls: a function (the common `catchAsync(cb)` case), or a plain
|
|
73
|
+
* object/array literal directly (e.g. `firstValueFrom(client.getX({ ... }))`
|
|
74
|
+
* — the object isn't itself wrapped in a function, but it's exactly what
|
|
75
|
+
* Prettier would hug directly if it were the argument of a plain, unwrapped
|
|
76
|
+
* call).
|
|
77
|
+
*/
|
|
78
|
+
function isHuggableLiteral(node) {
|
|
71
79
|
if (!node) {
|
|
72
80
|
return false;
|
|
73
81
|
}
|
|
@@ -80,7 +88,7 @@ function isHuggableFunction(node) {
|
|
|
80
88
|
body?.type === "ObjectExpression" ||
|
|
81
89
|
body?.type === "ArrayExpression");
|
|
82
90
|
}
|
|
83
|
-
return
|
|
91
|
+
return node.type === "ObjectExpression" || node.type === "ArrayExpression";
|
|
84
92
|
}
|
|
85
93
|
/**
|
|
86
94
|
* A short, unbreakable-looking companion argument — the kind lodash-style
|
|
@@ -118,7 +126,7 @@ function couldExpandArg(node, depth = 0) {
|
|
|
118
126
|
if (!node || depth > 4) {
|
|
119
127
|
return false;
|
|
120
128
|
}
|
|
121
|
-
if (
|
|
129
|
+
if (isHuggableLiteral(node)) {
|
|
122
130
|
return true;
|
|
123
131
|
}
|
|
124
132
|
if (node.type === "CallExpression" &&
|
|
@@ -194,6 +202,35 @@ function wouldBreakStandalone(doc, options) {
|
|
|
194
202
|
}
|
|
195
203
|
return doc_1.printer.printDocToString(doc, options).formatted.includes("\n");
|
|
196
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Prettier's own doc printer doesn't reliably re-check `printWidth` for
|
|
207
|
+
* plain content that comes *after* a forced break inside a conditionalGroup
|
|
208
|
+
* alternative — this is the same class of limitation their own
|
|
209
|
+
* `isHopefullyShortCallArgument` hack works around (see
|
|
210
|
+
* https://github.com/prettier/prettier/issues/2456). So rather than trust
|
|
211
|
+
* `conditionalGroup` to reject an overflowing candidate on its own, render it
|
|
212
|
+
* standalone and check every line ourselves.
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* Prettier's own member/call-chain printer makes its "should I break at the
|
|
216
|
+
* dots" decision using context (the surrounding call, the real column) this
|
|
217
|
+
* plugin's manually-reconstructed docs don't provide when they print a
|
|
218
|
+
* callee in isolation via `print("callee")` — which can make a callee that
|
|
219
|
+
* vanilla Prettier would never break on its own (see the linked issue)
|
|
220
|
+
* break at its dots anyway once disconnected from that context. Rendering
|
|
221
|
+
* it standalone at an effectively unlimited width and using the resulting
|
|
222
|
+
* plain text instead sidesteps the whole problem: nothing left to make that
|
|
223
|
+
* decision differently. If something still forces a real break even at
|
|
224
|
+
* unlimited width (a comment, most likely), this returns `undefined` and
|
|
225
|
+
* the caller should bail rather than risk it.
|
|
226
|
+
*/
|
|
227
|
+
function renderFlatOrUndefined(doc, options) {
|
|
228
|
+
const { formatted } = doc_1.printer.printDocToString(doc, {
|
|
229
|
+
...options,
|
|
230
|
+
printWidth: Number.MAX_SAFE_INTEGER,
|
|
231
|
+
});
|
|
232
|
+
return formatted.includes("\n") ? undefined : formatted;
|
|
233
|
+
}
|
|
197
234
|
/**
|
|
198
235
|
* Prettier's own doc printer doesn't reliably re-check `printWidth` for
|
|
199
236
|
* plain content that comes *after* a forced break inside a conditionalGroup
|
|
@@ -312,6 +349,13 @@ function tryPreserveMultilineList(path, options, print, kind) {
|
|
|
312
349
|
// other hugging attempts, or Prettier's own default, decide.
|
|
313
350
|
return undefined;
|
|
314
351
|
}
|
|
352
|
+
if (kind === "call") {
|
|
353
|
+
const flatPrefix = renderFlatOrUndefined(prefixDoc, options);
|
|
354
|
+
if (flatPrefix === undefined) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
prefixDoc = flatPrefix;
|
|
358
|
+
}
|
|
315
359
|
const printedItems = printItems();
|
|
316
360
|
const listDoc = [
|
|
317
361
|
openChar,
|
|
@@ -325,6 +369,49 @@ function tryPreserveMultilineList(path, options, print, kind) {
|
|
|
325
369
|
];
|
|
326
370
|
return [breakParent, prefixDoc, listDoc, suffixDoc];
|
|
327
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* A call with exactly one argument that's directly an object or array
|
|
374
|
+
* literal — `getX({ ... })` — is already hugged reliably by Prettier on its
|
|
375
|
+
* own, *in isolation*. But Prettier's own "hug the sole argument" mechanism
|
|
376
|
+
* is still gated on whether the opening line fits at the real column, and
|
|
377
|
+
* gives up (breaking the call's own parens too) when it doesn't — which
|
|
378
|
+
* becomes very possible once this plugin has already fused several outer
|
|
379
|
+
* layers together (e.g. `firstValueFrom(client.getX({ ... }))`, once
|
|
380
|
+
* `firstValueFrom(...)` hugs, pushes `getX(`'s real column much deeper).
|
|
381
|
+
* There's essentially never a readability win to giving up like that
|
|
382
|
+
* instead of just hugging directly, so this always does — but, like the
|
|
383
|
+
* plugin's other hugging features, only once the argument actually needs
|
|
384
|
+
* multi-line printing; a short object/array that already fits is left
|
|
385
|
+
* alone.
|
|
386
|
+
*/
|
|
387
|
+
function tryHugSoleLiteralArg(path, options, print) {
|
|
388
|
+
const { node } = path;
|
|
389
|
+
if (node.type !== "CallExpression" ||
|
|
390
|
+
node.optional ||
|
|
391
|
+
node.typeArguments ||
|
|
392
|
+
node.typeParameters ||
|
|
393
|
+
!node.callee ||
|
|
394
|
+
!isSimpleCallee(node.callee)) {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
const args = node.arguments ?? [];
|
|
398
|
+
const [only] = args;
|
|
399
|
+
if (args.length !== 1 ||
|
|
400
|
+
!only ||
|
|
401
|
+
hasComment(only) ||
|
|
402
|
+
(only.type !== "ObjectExpression" && only.type !== "ArrayExpression")) {
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
const argDoc = path.map(print, "arguments")[0];
|
|
406
|
+
if (argDoc === undefined || !wouldBreakStandalone(argDoc, options)) {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
const calleeDoc = renderFlatOrUndefined(print("callee"), options);
|
|
410
|
+
if (calleeDoc === undefined) {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
return [breakParent, calleeDoc, "(", argDoc, ")"];
|
|
414
|
+
}
|
|
328
415
|
function tryHugWrappedCallback(path, options, print) {
|
|
329
416
|
const { node } = path;
|
|
330
417
|
if (node.type !== "CallExpression" || node.optional) {
|
|
@@ -360,12 +447,16 @@ function tryHugWrappedCallback(path, options, print) {
|
|
|
360
447
|
!wouldBreakStandalone(expandDoc, options)) {
|
|
361
448
|
return undefined;
|
|
362
449
|
}
|
|
450
|
+
const calleeDoc = renderFlatOrUndefined(print("callee"), options);
|
|
451
|
+
if (calleeDoc === undefined) {
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
363
454
|
const beforeDocs = printedArgs.slice(0, expandIndex);
|
|
364
455
|
const afterDocs = printedArgs.slice(expandIndex + 1);
|
|
365
456
|
const beforeWithCommas = beforeDocs.flatMap((doc) => [doc, ", "]);
|
|
366
457
|
const afterWithCommas = afterDocs.flatMap((doc) => [", ", doc]);
|
|
367
458
|
const primaryDoc = [
|
|
368
|
-
|
|
459
|
+
calleeDoc,
|
|
369
460
|
"(",
|
|
370
461
|
...beforeWithCommas,
|
|
371
462
|
group(expandDoc, { shouldBreak: true }),
|
|
@@ -580,6 +671,7 @@ const plugin = {
|
|
|
580
671
|
tryPreserveMultilineList(path, options, print, "array") ??
|
|
581
672
|
tryPreserveMultilineList(path, options, print, "params") ??
|
|
582
673
|
tryHugWrappedCallback(path, options, print) ??
|
|
674
|
+
tryHugSoleLiteralArg(path, options, print) ??
|
|
583
675
|
tryHugArrowBody(path, options, print) ??
|
|
584
676
|
tryHugChainArgument(path, options, print);
|
|
585
677
|
return hugged === undefined
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prettier-plugin-hug-call-arguments",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Prettier plugin: hug the last call argument even when it's wrapped in another call, e.g. app.delete(\"/x\", catchAsync(async (req, res) => { ... })). Fixes https://github.com/prettier/prettier/issues/11080",
|
|
5
5
|
"author": "soso tsertsvadze",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"prettier": ">=3.0.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@types/node": "^26.
|
|
53
|
+
"@types/node": "^26.4.0",
|
|
54
54
|
"prettier": "^3.9.6",
|
|
55
55
|
"typescript": "^7.0.2"
|
|
56
56
|
}
|