prettier-plugin-hug-call-arguments 0.1.1 → 0.1.2
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 +14 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +84 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,9 +55,8 @@ app.delete("/campgrounds/:id", catchAsync(async (req, res) => {
|
|
|
55
55
|
}));
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
It also flattens
|
|
59
|
-
|
|
60
|
-
look with query builders:
|
|
58
|
+
It also flattens method chains instead of breaking every `.method()` onto its
|
|
59
|
+
own line — a common look with query builders:
|
|
61
60
|
|
|
62
61
|
```js
|
|
63
62
|
// input
|
|
@@ -104,12 +103,18 @@ shapes.
|
|
|
104
103
|
|
|
105
104
|
**Flattening a method chain:**
|
|
106
105
|
|
|
107
|
-
- every link is a plain, non-
|
|
108
|
-
a simple base (an identifier, `this`, or a non-computed member chain)
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
-
|
|
112
|
-
|
|
106
|
+
- every link is a plain, non-optional `.name(...)` or `[expr](...)` call down
|
|
107
|
+
to a simple base (an identifier, `this`, or a non-computed member chain)
|
|
108
|
+
- each link's own arguments are printed exactly as Prettier would print them
|
|
109
|
+
standalone — a single object/array/function argument hugs the parens, a
|
|
110
|
+
multi-argument list breaks onto its own lines only if it doesn't fit — so
|
|
111
|
+
any number of links can break independently, correctly, with no special
|
|
112
|
+
casing needed
|
|
113
|
+
- the whole flattened candidate is still rendered and measured against
|
|
114
|
+
`printWidth` as a final check (catching the one thing per-link breaking
|
|
115
|
+
can't: many short, individually-fitting links whose *combined* length
|
|
116
|
+
still overflows); if any line would overflow, it falls back to Prettier's
|
|
117
|
+
normal chain layout
|
|
113
118
|
|
|
114
119
|
Every other node, and every `CallExpression` that doesn't match one of these
|
|
115
120
|
shapes, is delegated unchanged to Prettier's original printer.
|
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, conditionalGroup, indent, hardline, join, breakParent } = doc_1.builders;
|
|
62
|
+
const { group, conditionalGroup, indent, hardline, line, softline, 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.
|
|
@@ -122,6 +122,23 @@ function isSimpleCallee(callee) {
|
|
|
122
122
|
function hasBlankLineBetween(originalText, start, end) {
|
|
123
123
|
return /\n[^\S\n]*\n/.test(originalText.slice(start, end));
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* `willBreak` only sees *forced* breaks (a hardline, or a group Prettier
|
|
127
|
+
* already marked `shouldBreak: true` — e.g. an object literal whose first
|
|
128
|
+
* property was already on its own line in the source, via `objectWrap:
|
|
129
|
+
* "preserve"`). A conditional break — an object written on one line that's
|
|
130
|
+
* simply too long to fit — has neither, so `willBreak` misses it. Render the
|
|
131
|
+
* doc standalone and check whether it needed more than one line instead.
|
|
132
|
+
* This only decides *how many* links look like they'll break for our own
|
|
133
|
+
* "exactly one" heuristic; the actual safety net against overflow is
|
|
134
|
+
* `fitsWhenFlattened`, which measures the real assembled doc.
|
|
135
|
+
*/
|
|
136
|
+
function wouldBreakStandalone(doc, options) {
|
|
137
|
+
if (willBreak(doc)) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return doc_1.printer.printDocToString(doc, options).formatted.includes("\n");
|
|
141
|
+
}
|
|
125
142
|
function tryHugWrappedCallback(path, options, print) {
|
|
126
143
|
const { node } = path;
|
|
127
144
|
if (node.type !== "CallExpression" || node.optional) {
|
|
@@ -159,8 +176,8 @@ function tryHugWrappedCallback(path, options, print) {
|
|
|
159
176
|
// If the earlier args don't fit on one line, or the wrapped callback
|
|
160
177
|
// wouldn't break anyway, Prettier's default output is already fine.
|
|
161
178
|
if (lastDoc === undefined ||
|
|
162
|
-
headDocs.some((doc) =>
|
|
163
|
-
!
|
|
179
|
+
headDocs.some((doc) => wouldBreakStandalone(doc, options)) ||
|
|
180
|
+
!wouldBreakStandalone(lastDoc, options)) {
|
|
164
181
|
return undefined;
|
|
165
182
|
}
|
|
166
183
|
const headWithCommas = headDocs.flatMap((doc) => [doc, ", "]);
|
|
@@ -183,13 +200,39 @@ function tryHugWrappedCallback(path, options, print) {
|
|
|
183
200
|
}
|
|
184
201
|
const MAX_CHAIN_LINKS = 6;
|
|
185
202
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* (
|
|
190
|
-
*
|
|
203
|
+
* Builds the `(...)` doc for one link's argument list the same way a plain,
|
|
204
|
+
* standalone call would print it: a single argument is hugged directly
|
|
205
|
+
* against the parens (so an object/array still expands in place, e.g.
|
|
206
|
+
* `.set({ ... })`, not indented as its own nested line); more than one
|
|
207
|
+
* argument gets Prettier's normal "fits on one line, else one per line"
|
|
208
|
+
* group. Either way the break decision is left to Prettier's regular,
|
|
209
|
+
* width-aware group mechanics — not something this plugin pre-computes.
|
|
191
210
|
*/
|
|
192
|
-
function
|
|
211
|
+
function buildArgsDoc(argsDocs, trailingComma) {
|
|
212
|
+
if (argsDocs.length === 0) {
|
|
213
|
+
return "()";
|
|
214
|
+
}
|
|
215
|
+
const [only] = argsDocs;
|
|
216
|
+
if (argsDocs.length === 1 && only !== undefined) {
|
|
217
|
+
return ["(", only, ")"];
|
|
218
|
+
}
|
|
219
|
+
return group([
|
|
220
|
+
"(",
|
|
221
|
+
indent([softline, join([",", line], argsDocs)]),
|
|
222
|
+
ifBreak(trailingComma),
|
|
223
|
+
softline,
|
|
224
|
+
")",
|
|
225
|
+
]);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Walks down `node.callee.object` as long as it's a plain, non-optional
|
|
229
|
+
* `.name(...)` or `[expr](...)` call, printing each link's property access
|
|
230
|
+
* and arguments along the way. Returns `undefined` the moment anything
|
|
231
|
+
* doesn't match that shape (optional chaining, generics, comments, spread
|
|
232
|
+
* callee, ...), so the caller can safely fall back to Prettier's own chain
|
|
233
|
+
* printer.
|
|
234
|
+
*/
|
|
235
|
+
function collectChainLinks(path, print, options, depth) {
|
|
193
236
|
if (depth > MAX_CHAIN_LINKS) {
|
|
194
237
|
return undefined;
|
|
195
238
|
}
|
|
@@ -204,25 +247,35 @@ function collectChainLinks(path, print, depth) {
|
|
|
204
247
|
const { callee } = node;
|
|
205
248
|
if (!callee ||
|
|
206
249
|
callee.type !== "MemberExpression" ||
|
|
207
|
-
callee.computed ||
|
|
208
250
|
callee.optional ||
|
|
209
251
|
hasComment(callee)) {
|
|
210
252
|
return undefined;
|
|
211
253
|
}
|
|
212
254
|
const { property } = callee;
|
|
213
|
-
if (!property ||
|
|
255
|
+
if (!property || hasComment(property)) {
|
|
214
256
|
return undefined;
|
|
215
257
|
}
|
|
258
|
+
let accessDoc;
|
|
259
|
+
if (callee.computed) {
|
|
260
|
+
accessDoc = ["[", path.call(print, "callee", "property"), "]"];
|
|
261
|
+
}
|
|
262
|
+
else if (property.type === "Identifier") {
|
|
263
|
+
accessDoc = [".", property.name ?? ""];
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
const trailingComma = options.trailingComma === "all" ? "," : "";
|
|
216
269
|
const link = {
|
|
217
|
-
|
|
218
|
-
|
|
270
|
+
accessDoc,
|
|
271
|
+
argsDoc: buildArgsDoc(path.map(print, "arguments"), trailingComma),
|
|
219
272
|
};
|
|
220
273
|
const { object } = callee;
|
|
221
274
|
if (!object || hasComment(object)) {
|
|
222
275
|
return undefined;
|
|
223
276
|
}
|
|
224
277
|
if (object.type === "CallExpression") {
|
|
225
|
-
const inner = path.call((innerPath) => collectChainLinks(innerPath, print, depth + 1), "callee", "object");
|
|
278
|
+
const inner = path.call((innerPath) => collectChainLinks(innerPath, print, options, depth + 1), "callee", "object");
|
|
226
279
|
return inner === undefined
|
|
227
280
|
? undefined
|
|
228
281
|
: { links: [...inner.links, link], baseDoc: inner.baseDoc };
|
|
@@ -234,13 +287,14 @@ function collectChainLinks(path, print, depth) {
|
|
|
234
287
|
return { links: [link], baseDoc };
|
|
235
288
|
}
|
|
236
289
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
290
|
+
* Each link's own argument list already breaks correctly on its own — via
|
|
291
|
+
* Prettier's normal, width-aware group mechanics — because there's no group
|
|
292
|
+
* wrapping the whole chain forcing anything. The one thing that mechanism
|
|
293
|
+
* can't see is a chain of many short, individually-non-breaking links whose
|
|
294
|
+
* *combined* length still overflows `printWidth` (nothing internal to break
|
|
295
|
+
* on). So render the assembled candidate standalone and check every line
|
|
296
|
+
* before committing to it, falling back to Prettier's own chain layout if
|
|
297
|
+
* it doesn't fit.
|
|
244
298
|
*/
|
|
245
299
|
function fitsWhenFlattened(doc, options) {
|
|
246
300
|
const { formatted } = doc_1.printer.printDocToString(doc, options);
|
|
@@ -251,11 +305,12 @@ function fitsWhenFlattened(doc, options) {
|
|
|
251
305
|
/**
|
|
252
306
|
* Handles method chains like `db.updateTable("User").set({ ... }).where(
|
|
253
307
|
* "id", "=", user.id).execute()`, where Prettier's default chain printer
|
|
254
|
-
* breaks every `.method()` onto its own line as soon as
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
308
|
+
* breaks every `.method()` onto its own line as soon as any argument
|
|
309
|
+
* doesn't fit. This instead keeps the whole chain flat — `.foo(...).bar(
|
|
310
|
+
* ...).baz(...)` — and lets each link's own arguments break independently
|
|
311
|
+
* if they need to, falling back to Prettier's normal chain layout only if
|
|
312
|
+
* the flattened result doesn't fit the shape this plugin handles, or would
|
|
313
|
+
* overflow `printWidth`.
|
|
259
314
|
*/
|
|
260
315
|
function tryHugChainArgument(path, options, print) {
|
|
261
316
|
const { node } = path;
|
|
@@ -269,31 +324,23 @@ function tryHugChainArgument(path, options, print) {
|
|
|
269
324
|
}
|
|
270
325
|
if (!node.callee ||
|
|
271
326
|
node.callee.type !== "MemberExpression" ||
|
|
272
|
-
node.callee.computed ||
|
|
273
327
|
!node.callee.object ||
|
|
274
328
|
node.callee.object.type !== "CallExpression") {
|
|
275
329
|
return undefined;
|
|
276
330
|
}
|
|
277
|
-
const collected = collectChainLinks(path, print, 0);
|
|
331
|
+
const collected = collectChainLinks(path, print, options, 0);
|
|
278
332
|
if (!collected || collected.links.length < 2) {
|
|
279
333
|
return undefined;
|
|
280
334
|
}
|
|
281
335
|
const { links, baseDoc } = collected;
|
|
282
|
-
const breakingLinks = links.filter((link) => link.argsDocs.some((doc) => willBreak(doc)));
|
|
283
|
-
if (breakingLinks.length !== 1) {
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
336
|
const flatDoc = [
|
|
287
337
|
baseDoc,
|
|
288
|
-
...links.map((link) =>
|
|
289
|
-
const joinedArgs = link.argsDocs.flatMap((doc, index) => index === 0 ? [doc] : [", ", doc]);
|
|
290
|
-
return [".", link.name, "(", ...joinedArgs, ")"];
|
|
291
|
-
}),
|
|
338
|
+
...links.map((link) => [link.accessDoc, link.argsDoc]),
|
|
292
339
|
];
|
|
293
340
|
if (!fitsWhenFlattened(flatDoc, options)) {
|
|
294
341
|
return undefined;
|
|
295
342
|
}
|
|
296
|
-
return [breakParent, flatDoc];
|
|
343
|
+
return willBreak(flatDoc) ? [breakParent, flatDoc] : flatDoc;
|
|
297
344
|
}
|
|
298
345
|
const plugin = {
|
|
299
346
|
printers: {
|
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.2",
|
|
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",
|