prettier-plugin-hug-call-arguments 0.1.7 → 0.1.9
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 +66 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +123 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,8 +120,47 @@ db.updateTable("User").set({
|
|
|
120
120
|
## How it works
|
|
121
121
|
|
|
122
122
|
This plugin wraps Prettier's built-in `estree` printer and only overrides the
|
|
123
|
-
`print` step for `CallExpression` and
|
|
124
|
-
match one of a few narrow, specific
|
|
123
|
+
`print` step for `CallExpression`, `ArrayExpression`, and
|
|
124
|
+
`ArrowFunctionExpression` nodes that match one of a few narrow, specific
|
|
125
|
+
shapes.
|
|
126
|
+
|
|
127
|
+
**Preserving a layout you already chose:**
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
// input
|
|
131
|
+
foo(
|
|
132
|
+
1,
|
|
133
|
+
2,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// with this plugin — kept exactly as written
|
|
137
|
+
foo(
|
|
138
|
+
1,
|
|
139
|
+
2,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// bar(1, 2, 3) stays on one line — nothing to preserve, so it's printed
|
|
143
|
+
// the normal, printWidth-aware way
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Prettier already does this for object literals by default (`objectWrap:
|
|
147
|
+
"preserve"`): write `{` with a newline before the first property and it
|
|
148
|
+
stays multi-line, even if it would fit on one line. This plugin extends that
|
|
149
|
+
same "respect how the author wrote it" behavior to call arguments, array
|
|
150
|
+
elements, and arrow function parameter lists, which Prettier doesn't
|
|
151
|
+
normally preserve — those always auto-collapse to fit `printWidth`
|
|
152
|
+
regardless of the original formatting.
|
|
153
|
+
|
|
154
|
+
- applies once there are at least two items with a newline right after the
|
|
155
|
+
opening `(`/`[` and before the first one — a single item has no "one per
|
|
156
|
+
line" layout to preserve, so that's left to this plugin's other hugging
|
|
157
|
+
behavior, or Prettier's own
|
|
158
|
+
- takes priority over every other feature below: a layout you explicitly
|
|
159
|
+
chose is never fought by this plugin's own hugging heuristics
|
|
160
|
+
- bails on comments anywhere in the list, a sparse array, TS type
|
|
161
|
+
arguments/parameters, or (for parameters) anything but a plain arrow
|
|
162
|
+
function — a `function` expression/declaration has a name and block body
|
|
163
|
+
to reproduce too, which is more than this narrow check takes on
|
|
125
164
|
|
|
126
165
|
**Hugging a call-wrapped callback:**
|
|
127
166
|
|
|
@@ -141,6 +180,31 @@ match one of a few narrow, specific shapes.
|
|
|
141
180
|
- there's no blank line between arguments, no comments on the relevant
|
|
142
181
|
nodes, and no TS type arguments
|
|
143
182
|
|
|
183
|
+
**Hugging an arrow function's concise body:**
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
// input
|
|
187
|
+
const f = (data) =>
|
|
188
|
+
pgBoss.then((boss) => {
|
|
189
|
+
return boss.send(jobName, data);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// with this plugin
|
|
193
|
+
const f = (data) => pgBoss.then((boss) => {
|
|
194
|
+
return boss.send(jobName, data);
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- the arrow has a concise (non-block) body, no type parameters, return type,
|
|
199
|
+
or predicate, and default `arrowParens`
|
|
200
|
+
- the body is a `CallExpression` that needs multi-line printing (Prettier
|
|
201
|
+
already hugs directly after `=>` for other body types — objects, arrays,
|
|
202
|
+
JSX, template literals — so there's no gap to fix there)
|
|
203
|
+
- the resulting line fits `printWidth`; otherwise this falls back to
|
|
204
|
+
Prettier's default (a hardline after `=>`, body indented on its own line).
|
|
205
|
+
The arrow's own parameter list can still break onto its own line first the
|
|
206
|
+
normal way — only the `=> body` boundary is affected
|
|
207
|
+
|
|
144
208
|
**Flattening a method chain:**
|
|
145
209
|
|
|
146
210
|
- every link is a plain, non-optional `.name(...)` or `[expr](...)` call down
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ const doc_1 = require("prettier/doc");
|
|
|
59
59
|
// what `require`/interop resolves to, and its printer object is what we
|
|
60
60
|
// spread and delegate to for every node we don't special-case.
|
|
61
61
|
const estree = __importStar(require("prettier/plugins/estree"));
|
|
62
|
-
const { group, indent, line, softline, join, ifBreak, breakParent } = doc_1.builders;
|
|
62
|
+
const { group, indent, line, softline, hardline, join, ifBreak, breakParent, } = doc_1.builders;
|
|
63
63
|
const { willBreak } = doc_1.utils;
|
|
64
64
|
// `estree.printers.estree` is typed as `Printer<any>` by Prettier, which is
|
|
65
65
|
// assignable to `Printer<ESNode>` on its own — no cast needed.
|
|
@@ -209,6 +209,122 @@ function fitsWhenFlattened(doc, options) {
|
|
|
209
209
|
.split("\n")
|
|
210
210
|
.every((line) => line.length <= options.printWidth);
|
|
211
211
|
}
|
|
212
|
+
function getTrailingComma(options, kind) {
|
|
213
|
+
if (options.trailingComma === "all") {
|
|
214
|
+
return ",";
|
|
215
|
+
}
|
|
216
|
+
if (options.trailingComma === "es5" && kind === "array") {
|
|
217
|
+
return ",";
|
|
218
|
+
}
|
|
219
|
+
return "";
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Prettier already preserves this for object literals by default
|
|
223
|
+
* (`objectWrap: "preserve"`): write `{` with a newline before the first
|
|
224
|
+
* property and it stays multi-line, even if it would otherwise fit on one
|
|
225
|
+
* line. Prettier doesn't extend that courtesy to call arguments, array
|
|
226
|
+
* elements, or parameter lists — those always auto-collapse to fit
|
|
227
|
+
* `printWidth` regardless of how they were originally written. This
|
|
228
|
+
* reproduces the same "respect how the author wrote it" behavior for those
|
|
229
|
+
* three, so the plugin's own hugging heuristics never fight a layout the
|
|
230
|
+
* developer explicitly chose by hand.
|
|
231
|
+
*
|
|
232
|
+
* Only applies once there are at least two items — with a single item,
|
|
233
|
+
* there's no "one per line" layout to preserve, and that case is already
|
|
234
|
+
* handled by this plugin's other hugging behavior (or Prettier's own).
|
|
235
|
+
* Bails on anything that would make correctly locating the opening bracket
|
|
236
|
+
* or reproducing the layout unsafe: comments anywhere in the list, a sparse
|
|
237
|
+
* array, or (for calls) TS type arguments.
|
|
238
|
+
*/
|
|
239
|
+
function tryPreserveMultilineList(path, options, print, kind) {
|
|
240
|
+
const { node } = path;
|
|
241
|
+
let items;
|
|
242
|
+
let openChar;
|
|
243
|
+
let closeChar;
|
|
244
|
+
let searchFrom;
|
|
245
|
+
let prefixDoc = "";
|
|
246
|
+
let suffixDoc = "";
|
|
247
|
+
let printItems;
|
|
248
|
+
if (kind === "call") {
|
|
249
|
+
if (node.type !== "CallExpression" ||
|
|
250
|
+
node.optional ||
|
|
251
|
+
node.typeArguments ||
|
|
252
|
+
node.typeParameters ||
|
|
253
|
+
!node.callee ||
|
|
254
|
+
!isSimpleCallee(node.callee) ||
|
|
255
|
+
// Let chain-flattening handle the outermost call of a chain instead —
|
|
256
|
+
// this only steps in for a plain, non-chained call.
|
|
257
|
+
(node.callee.type === "MemberExpression" &&
|
|
258
|
+
node.callee.object?.type === "CallExpression")) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
items = node.arguments ?? [];
|
|
262
|
+
openChar = "(";
|
|
263
|
+
closeChar = ")";
|
|
264
|
+
searchFrom = node.callee.end;
|
|
265
|
+
prefixDoc = print("callee");
|
|
266
|
+
printItems = () => path.map(print, "arguments");
|
|
267
|
+
}
|
|
268
|
+
else if (kind === "array") {
|
|
269
|
+
if (node.type !== "ArrayExpression") {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
items = node.elements ?? [];
|
|
273
|
+
openChar = "[";
|
|
274
|
+
closeChar = "]";
|
|
275
|
+
searchFrom = node.start;
|
|
276
|
+
printItems = () => path.map(print, "elements");
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// Arrow functions only — a plain `function` expression/declaration also
|
|
280
|
+
// has a name and a block body to reproduce, which is more than this
|
|
281
|
+
// narrow check is worth taking on.
|
|
282
|
+
if (node.type !== "ArrowFunctionExpression" ||
|
|
283
|
+
node.typeParameters ||
|
|
284
|
+
node.returnType ||
|
|
285
|
+
node.predicate ||
|
|
286
|
+
!node.body ||
|
|
287
|
+
hasComment(node.body)) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
items = node.params ?? [];
|
|
291
|
+
openChar = "(";
|
|
292
|
+
closeChar = ")";
|
|
293
|
+
searchFrom = node.start;
|
|
294
|
+
prefixDoc = node.async ? "async " : "";
|
|
295
|
+
suffixDoc = [" => ", print("body")];
|
|
296
|
+
printItems = () => path.map(print, "params");
|
|
297
|
+
}
|
|
298
|
+
if (items.length < 2 ||
|
|
299
|
+
hasComment(node) ||
|
|
300
|
+
items.some((item) => !item || hasComment(item)) ||
|
|
301
|
+
searchFrom === undefined) {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
const openIndex = options.originalText.indexOf(openChar, searchFrom);
|
|
305
|
+
const firstStart = items[0]?.start;
|
|
306
|
+
if (openIndex === -1 || firstStart === undefined) {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
const between = options.originalText.slice(openIndex + 1, firstStart);
|
|
310
|
+
if (!/\n/.test(between)) {
|
|
311
|
+
// Not originally multi-line — nothing to preserve; let this plugin's
|
|
312
|
+
// other hugging attempts, or Prettier's own default, decide.
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
const printedItems = printItems();
|
|
316
|
+
const listDoc = [
|
|
317
|
+
openChar,
|
|
318
|
+
indent([
|
|
319
|
+
hardline,
|
|
320
|
+
join([",", hardline], printedItems),
|
|
321
|
+
getTrailingComma(options, kind),
|
|
322
|
+
]),
|
|
323
|
+
hardline,
|
|
324
|
+
closeChar,
|
|
325
|
+
];
|
|
326
|
+
return [breakParent, prefixDoc, listDoc, suffixDoc];
|
|
327
|
+
}
|
|
212
328
|
function tryHugWrappedCallback(path, options, print) {
|
|
213
329
|
const { node } = path;
|
|
214
330
|
if (node.type !== "CallExpression" || node.optional) {
|
|
@@ -458,7 +574,12 @@ const plugin = {
|
|
|
458
574
|
estree: {
|
|
459
575
|
...estreePrinter,
|
|
460
576
|
print(path, options, print, args) {
|
|
461
|
-
|
|
577
|
+
// Preserving a layout the developer explicitly chose by hand takes
|
|
578
|
+
// priority over this plugin's own hugging heuristics.
|
|
579
|
+
const hugged = tryPreserveMultilineList(path, options, print, "call") ??
|
|
580
|
+
tryPreserveMultilineList(path, options, print, "array") ??
|
|
581
|
+
tryPreserveMultilineList(path, options, print, "params") ??
|
|
582
|
+
tryHugWrappedCallback(path, options, print) ??
|
|
462
583
|
tryHugArrowBody(path, options, print) ??
|
|
463
584
|
tryHugChainArgument(path, options, print);
|
|
464
585
|
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.9",
|
|
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",
|