json-sort-cli 4.2.2 → 4.3.0

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/cli.js CHANGED
@@ -1,121 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // VARS
4
- // -----------------------------------------------------------------------------
5
-
3
+ import { fstatSync } from "node:fs";
6
4
  import { createRequire } from "node:module";
7
5
  import path from "node:path";
8
6
  import { glob } from "codsen-glob";
9
- import { codsenCLI } from "codsen-utils";
10
7
  import updateNotifier from "update-notifier";
8
+ import { formatParsedJson } from "./json-formatter.js";
11
9
  import { ProcessingError, processFiles } from "./process-files.js";
12
10
 
13
11
  const require1 = createRequire(import.meta.url);
14
12
  const pkg = require1("./package.json");
15
-
16
- const colours = {
17
- green: 32,
18
- grey: 90,
19
- red: 31,
20
- white: 37,
21
- yellow: 33,
22
- };
23
-
24
- function colour(str, colourCode) {
25
- return `\u001b[${colourCode}m${str}\u001b[39m`;
26
- }
27
-
28
13
  const prefix = "✨ json-sort-cli: ";
29
- const { log } = console;
30
- const cli = codsenCLI(
31
- `
32
- Usage
33
- $ jsonsort YOURFILE.json
34
- $ sortjson YOURFILE.json
35
- $ sortjson templatesfolder1 templatesfolder2 package.json
36
- or, just type "jsonsort" and it will let you pick a file.
37
-
38
- Options
39
- -n, --nodemodules Don't ignore any node_modules folders
40
- -t, --tabs Use tabs for JSON file indentation
41
- -i, --indentationCount How many spaces/tabs to use (default: 2 for spaces or 1 for tabs)
42
- -s, --silent Does not show the result per-file, only totals in the end
43
- -h, --help Shows this help
44
- -v, --version Shows the current version
45
- -a, --arrays Also sort any arrays if they contain only string elements
46
- -d, --dry Only list all the files about to be processed
47
- -p, --pack Exclude all package.json files
48
- -c, --ci Only exits with non-zero code if files COULD BE sorted
49
- -l, --lineEnding Set to "cr", "crlf" or "lf" to override the default
50
- (which is either EOL format used in the file, or Mac LF)
51
-
52
- Example
53
- Call anywhere using glob patterns. If you put them as string, this library
54
- will parse globs. If you put as system globs without quotes, your shell will expand them.
55
- `,
56
- {
57
- pkg,
58
- flags: {
59
- nodemodules: {
60
- type: "boolean",
61
- shortFlag: "n",
62
- default: false,
63
- },
64
- tabs: {
65
- type: "boolean",
66
- shortFlag: "t",
67
- default: false,
68
- },
69
- silent: {
70
- type: "boolean",
71
- shortFlag: "s",
72
- default: false,
73
- },
74
- arrays: {
75
- type: "boolean",
76
- shortFlag: "a",
77
- default: false,
78
- },
79
- pack: {
80
- type: "boolean",
81
- shortFlag: "p",
82
- default: false,
83
- },
84
- dry: {
85
- type: "boolean",
86
- shortFlag: "d",
87
- default: false,
88
- },
89
- ci: {
90
- type: "boolean",
91
- shortFlag: "c",
92
- default: false,
93
- },
94
- help: {
95
- type: "boolean",
96
- shortFlag: "h",
97
- default: false,
98
- },
99
- version: {
100
- type: "boolean",
101
- shortFlag: "v",
102
- default: false,
103
- },
104
- indentationCount: {
105
- type: "number",
106
- shortFlag: "i",
107
- },
108
- lineEnding: {
109
- type: "string",
110
- shortFlag: "l",
111
- },
112
- },
113
- },
114
- );
115
- updateNotifier({ pkg }).notify();
116
-
117
- const nonJsonFormats = ["yml", "toml", "yaml"]; // to save time
118
- const badFiles = [
14
+ const nonJsonFormats = new Set([".yml", ".toml", ".yaml"]);
15
+ const ignoredBasenames = new Set([
119
16
  ".DS_Store",
120
17
  "npm-debug.log",
121
18
  ".svn",
@@ -124,236 +21,523 @@ const badFiles = [
124
21
  ".lock-wscript",
125
22
  "package-lock.json",
126
23
  "npm-shrinkwrap.json",
127
- ];
24
+ "yarn.lock",
25
+ ]);
26
+ const flagDefinitions = {
27
+ arrays: { short: "a", type: "boolean" },
28
+ ci: { short: "c", type: "boolean" },
29
+ dry: { short: "d", type: "boolean" },
30
+ help: { short: "h", type: "boolean" },
31
+ indentationCount: { short: "i", type: "value" },
32
+ lineEnding: { short: "l", type: "value" },
33
+ nodemodules: { short: "n", type: "boolean" },
34
+ pack: { short: "p", type: "boolean" },
35
+ silent: { short: "s", type: "boolean" },
36
+ stdout: { type: "boolean" },
37
+ tabs: { short: "t", type: "boolean" },
38
+ version: { short: "v", type: "boolean" },
39
+ };
40
+ const shortToLong = new Map(
41
+ Object.entries(flagDefinitions)
42
+ .filter(([, definition]) => definition.short)
43
+ .map(([long, definition]) => [definition.short, long]),
44
+ );
45
+
46
+ const help = `
47
+ Usage
48
+ $ jsonsort YOURFILE.json
49
+ $ jsonsort YOURFILE.json --stdout
50
+ $ jsonsort - < YOURFILE.json
51
+ $ cat YOURFILE.json | jsonsort
52
+ $ sortjson templatesfolder1 templatesfolder2 package.json
53
+ $ jsonsort
54
+
55
+ With no file arguments, piped input is sorted to stdout. The "-" operand reads
56
+ stdin, and --stdout prints one matched file. These forms don't write files or mix
57
+ status messages into stdout. Otherwise, with no arguments, jsonsort recursively
58
+ sorts JSON files below the current directory. Discovered symbolic links are not
59
+ followed. An explicitly selected symbolic-link directory is resolved as the
60
+ selected root; symbolic-link files are rejected before reading.
61
+
62
+ Options
63
+ -n, --nodemodules Include JSON files inside node_modules; lockfiles remain excluded
64
+ -t, --tabs Use tabs for indentation
65
+ -i, --indentationCount Use 0-10 spaces or tabs (default: 2 spaces or 1 tab)
66
+ -s, --silent Suppress all terminal output; use the exit code for the result
67
+ -h, --help Show this help
68
+ -v, --version Show the current version
69
+ -a, --arrays Sort arrays that contain only strings
70
+ -d, --dry List candidate files without reading or writing them
71
+ -p, --pack Exclude package.json files
72
+ -c, --ci Check without writing; exit 9 when canonical output differs
73
+ -l, --lineEnding Use "cr", "crlf", or "lf" instead of the detected line ending
74
+ --stdout Print one sorted JSON document without writing files
128
75
 
129
- // 1. set defaults:
130
- let indentationCount = 2;
131
- if (cli.flags.tabs) {
132
- indentationCount = 1;
76
+ Use -- before a path that begins with a dash. Invalid options exit 1 before
77
+ file discovery. Processing failures exit 1 and don't stop independent files.
78
+
79
+ Example
80
+ $ cat data.json | jsonsort --arrays | other-command
81
+ `;
82
+
83
+ function argumentError(message) {
84
+ throw new TypeError(
85
+ `json-sort-cli/parseArguments(): [THROW_ID_01] ${message}`,
86
+ );
133
87
  }
134
- // 2. overwrite defaults with explicitly set value:
135
- if (cli.flags.indentationCount) {
136
- indentationCount = +cli.flags.indentationCount;
88
+
89
+ function requestsSilent(rawArguments) {
90
+ let optionsEnded = false;
91
+ for (const argument of rawArguments) {
92
+ if (optionsEnded) {
93
+ continue;
94
+ }
95
+ if (argument === "--") {
96
+ optionsEnded = true;
97
+ } else if (argument === "--silent") {
98
+ return true;
99
+ } else if (argument.startsWith("-") && !argument.startsWith("--")) {
100
+ for (const short of argument.slice(1)) {
101
+ const name = shortToLong.get(short);
102
+ if (!name) {
103
+ break;
104
+ }
105
+ if (name === "silent") {
106
+ return true;
107
+ }
108
+ if (flagDefinitions[name].type === "value") {
109
+ break;
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return false;
115
+ }
116
+
117
+ function parseArguments(rawArguments, { stdinIsPiped = false } = {}) {
118
+ const expanded = rawArguments.flatMap((argument) => {
119
+ const match = argument.match(/^-(i|l)\s+(.+)$/u);
120
+ return match ? [`-${match[1]}`, match[2]] : [argument];
121
+ });
122
+ const flags = Object.fromEntries(
123
+ Object.keys(flagDefinitions).map((name) => [name, false]),
124
+ );
125
+ flags.indentationCount = undefined;
126
+ flags.lineEnding = undefined;
127
+ const input = [];
128
+ const seen = new Set();
129
+ let optionsEnded = false;
130
+
131
+ function setFlag(name, value = true) {
132
+ if (seen.has(name)) {
133
+ argumentError(`Option --${name} was provided more than once`);
134
+ }
135
+ seen.add(name);
136
+ flags[name] = value;
137
+ }
138
+
139
+ for (let index = 0; index < expanded.length; index += 1) {
140
+ const argument = expanded[index];
141
+ if (optionsEnded) {
142
+ input.push(argument);
143
+ continue;
144
+ }
145
+ if (argument === "--") {
146
+ optionsEnded = true;
147
+ continue;
148
+ }
149
+ if (!argument.startsWith("-") || argument === "-") {
150
+ input.push(argument);
151
+ continue;
152
+ }
153
+
154
+ if (argument.startsWith("--")) {
155
+ const equalsIndex = argument.indexOf("=");
156
+ const name = argument.slice(
157
+ 2,
158
+ equalsIndex === -1 ? undefined : equalsIndex,
159
+ );
160
+ const definition = flagDefinitions[name];
161
+ if (!definition) {
162
+ argumentError(`Unknown option --${name}`);
163
+ }
164
+ if (definition.type === "boolean") {
165
+ if (equalsIndex !== -1) {
166
+ argumentError(`Option --${name} doesn't accept a value`);
167
+ }
168
+ setFlag(name);
169
+ continue;
170
+ }
171
+
172
+ let value =
173
+ equalsIndex === -1 ? undefined : argument.slice(equalsIndex + 1);
174
+ if (value === undefined) {
175
+ value = expanded[index + 1];
176
+ const flagShapedValue =
177
+ value?.startsWith("-") &&
178
+ !(name === "indentationCount" && /^-\d/u.test(value));
179
+ if (value === undefined || flagShapedValue) {
180
+ argumentError(`Option --${name} requires a value`);
181
+ }
182
+ index += 1;
183
+ }
184
+ if (value === "") {
185
+ argumentError(`Option --${name} requires a value`);
186
+ }
187
+ setFlag(name, value);
188
+ continue;
189
+ }
190
+
191
+ let shortFlags = argument.slice(1);
192
+ while (shortFlags.length) {
193
+ const short = shortFlags[0];
194
+ const name = shortToLong.get(short);
195
+ if (!name) {
196
+ argumentError(`Unknown option -${short}`);
197
+ }
198
+ const definition = flagDefinitions[name];
199
+ shortFlags = shortFlags.slice(1);
200
+ if (definition.type === "boolean") {
201
+ setFlag(name);
202
+ continue;
203
+ }
204
+
205
+ let value = shortFlags.replace(/^=/u, "");
206
+ shortFlags = "";
207
+ if (!value) {
208
+ value = expanded[index + 1];
209
+ const flagShapedValue =
210
+ value?.startsWith("-") &&
211
+ !(name === "indentationCount" && /^-\d/u.test(value));
212
+ if (value === undefined || flagShapedValue) {
213
+ argumentError(`Option -${short} requires a value`);
214
+ }
215
+ index += 1;
216
+ }
217
+ setFlag(name, value);
218
+ }
219
+ }
220
+
221
+ const defaultIndentation = flags.tabs ? 1 : 2;
222
+ const indentationCount =
223
+ flags.indentationCount === undefined
224
+ ? defaultIndentation
225
+ : Number(flags.indentationCount);
226
+ if (
227
+ !Number.isInteger(indentationCount) ||
228
+ indentationCount < 0 ||
229
+ indentationCount > 10
230
+ ) {
231
+ argumentError("indentationCount must be an integer from 0 to 10");
232
+ }
233
+ if (
234
+ flags.lineEnding !== undefined &&
235
+ !["cr", "crlf", "lf"].includes(flags.lineEnding)
236
+ ) {
237
+ argumentError('lineEnding must be "cr", "crlf", or "lf"');
238
+ }
239
+
240
+ const hasInput = input.length > 0;
241
+ const informational = flags.help || flags.version;
242
+ const printsJson =
243
+ flags.stdout || input.includes("-") || (!hasInput && stdinIsPiped);
244
+ if (!hasInput && expanded.length > 0 && !informational && !stdinIsPiped) {
245
+ argumentError(
246
+ "Provide at least one path, or run jsonsort with no arguments",
247
+ );
248
+ }
249
+ if (!informational && printsJson) {
250
+ for (const incompatible of ["ci", "dry", "silent"]) {
251
+ if (flags[incompatible]) {
252
+ argumentError(
253
+ `Option --${incompatible} cannot be used when printing sorted JSON to stdout`,
254
+ );
255
+ }
256
+ }
257
+ }
258
+
259
+ return {
260
+ flags,
261
+ hasInput,
262
+ indentationCount,
263
+ input: hasInput ? input : ["**/*.json"],
264
+ };
137
265
  }
138
266
 
139
- // Step #0. take care of the short -v and -h flags, which codsenCLI leaves
140
- // to us (it answers the long --version and --help on its own).
141
- // -----------------------------------------------------------------------------
267
+ function isCandidate(filePath) {
268
+ const basename = path.basename(filePath);
269
+ if (ignoredBasenames.has(basename)) {
270
+ return false;
271
+ }
142
272
 
143
- if (cli.flags.version) {
144
- log(pkg.version);
145
- process.exit(0);
146
- } else if (cli.flags.help) {
147
- log(cli.help);
148
- process.exit(0);
273
+ const extension = path.extname(filePath).toLowerCase();
274
+ return (
275
+ extension === ".json" ||
276
+ (basename.startsWith(".") && !nonJsonFormats.has(extension))
277
+ );
149
278
  }
150
279
 
151
- // Step #1. set up the cli
152
- // -----------------------------------------------------------------------------
280
+ function standardInputIsPiped() {
281
+ if (process.stdin.isTTY === true) {
282
+ return false;
283
+ }
284
+ try {
285
+ return !fstatSync(process.stdin.fd).isCharacterDevice();
286
+ } catch {
287
+ // A missing or inaccessible stdin is not a pipeline source.
288
+ return false;
289
+ }
290
+ }
153
291
 
154
- const { input } = cli;
155
- if (Array.isArray(input) && !input.length) {
156
- input.push("**/*.json");
292
+ async function readStdin() {
293
+ const chunks = [];
294
+ for await (const chunk of process.stdin) {
295
+ chunks.push(chunk);
296
+ }
297
+ return Buffer.concat(chunks);
157
298
  }
158
299
 
159
- // Step #2. query the glob and follow the pipeline
160
- // -----------------------------------------------------------------------------
161
-
162
- glob(
163
- [
164
- ...input,
165
- "!**/package-lock.json",
166
- "!**/yarn.lock",
167
- ...(cli.flags.nodemodules ? [] : ["!**/node_modules/**"]),
168
- ...(cli.flags.pack ? ["!**/package.json"] : []),
169
- ],
170
- {
171
- dot: true,
172
- expandDirectories: { files: [".*", "*.json"] },
173
- },
174
- )
175
- .then((paths) => {
176
- // flip out of the pipeline if there are no paths resolved
177
- if (paths.length === 0 && !cli.flags.silent) {
178
- log(
179
- `${colour(prefix, colours.grey)}${colour(
180
- "The inputs don't lead to any json files! Exiting.",
181
- colours.red,
182
- )}`,
300
+ async function discoverPaths(input, flags) {
301
+ const paths = await glob(
302
+ [
303
+ ...input,
304
+ "!**/package-lock.json",
305
+ "!**/npm-shrinkwrap.json",
306
+ "!**/yarn.lock",
307
+ ...(flags.nodemodules ? [] : ["!**/node_modules/**"]),
308
+ ...(flags.pack ? ["!**/package.json"] : []),
309
+ ],
310
+ {
311
+ dot: true,
312
+ expandDirectories: { files: [".*", "*.json", "*.JSON"] },
313
+ followSymbolicLinks: false,
314
+ },
315
+ );
316
+ return paths.filter(isCandidate);
317
+ }
318
+
319
+ async function resolveStdoutSource(input, flags) {
320
+ if (input.includes("-")) {
321
+ if (input.length !== 1) {
322
+ argumentError(
323
+ 'The standard-input operand "-" cannot be combined with file paths',
183
324
  );
184
- process.exit(0);
185
325
  }
186
- return paths;
187
- })
188
- .then((paths) =>
189
- paths.filter(
190
- (oneOfPaths) =>
191
- !oneOfPaths.includes("package-lock.json") &&
192
- !oneOfPaths.includes("yarn.lock"),
193
- ),
194
- )
195
- .then((paths) =>
196
- !cli.flags.nodemodules
197
- ? paths.filter((oneOfPaths) => !oneOfPaths.includes("node_modules"))
198
- : paths,
199
- )
200
- .then((paths) =>
201
- cli.flags.pack
202
- ? paths.filter((oneOfPaths) => !oneOfPaths.includes("package.json"))
203
- : paths,
204
- )
205
- .then((paths) =>
206
- paths.filter((singlePath) => {
207
- return (
208
- path.extname(singlePath) === ".json" ||
209
- (typeof path.basename(singlePath) === "string" &&
210
- path.basename(singlePath).startsWith(".") &&
211
- !nonJsonFormats.some((badExtension) =>
212
- path.extname(singlePath).includes(badExtension),
213
- ) &&
214
- !badFiles.some((badFile) =>
215
- path.basename(singlePath).includes(badFile),
216
- ))
326
+ return "-";
327
+ }
328
+
329
+ const paths = await discoverPaths(input, flags);
330
+ if (paths.length !== 1) {
331
+ throw new Error(
332
+ `Printing sorted JSON to stdout requires exactly one input; found ${paths.length}`,
333
+ );
334
+ }
335
+ return paths[0];
336
+ }
337
+
338
+ async function formatSource(source, options) {
339
+ let output;
340
+ await processFiles([source], {
341
+ ...options,
342
+ ci: true,
343
+ ...(source === "-" ? { read: readStdin } : {}),
344
+ transform(parsed, formatOptions) {
345
+ const prepared = formatParsedJson(
346
+ parsed,
347
+ formatOptions.contents,
348
+ formatOptions,
217
349
  );
218
- }),
219
- )
220
-
221
- .then((paths) => {
222
- if (cli.flags.dry && !cli.flags.silent) {
223
- log(
224
- `${colour(prefix, colours.grey)}${colour(
225
- "We'd try to sort the following files:",
226
- colours.yellow,
227
- )}\n${paths.join("\n")}`,
350
+ output = prepared.output;
351
+ return prepared;
352
+ },
353
+ });
354
+ return output;
355
+ }
356
+
357
+ function errorMessage(error) {
358
+ if (error instanceof ProcessingError) {
359
+ return error.failures.map((failure) => failure.message).join("\n");
360
+ }
361
+ return error instanceof Error ? error.message : String(error);
362
+ }
363
+
364
+ function handleStdoutError(error) {
365
+ if (error?.code === "EPIPE") {
366
+ return;
367
+ }
368
+ console.error(`${prefix}${errorMessage(error)}`);
369
+ process.exitCode = 1;
370
+ }
371
+
372
+ function createPrinter() {
373
+ const useColour = Boolean(process.stdout.isTTY && !process.env.NO_COLOR);
374
+ const colours = { green: 32, grey: 90, red: 31, white: 37, yellow: 33 };
375
+ const colour = (value, name) =>
376
+ useColour ? `\u001b[${colours[name]}m${value}\u001b[39m` : value;
377
+ return { colour };
378
+ }
379
+
380
+ async function main() {
381
+ const rawArguments = process.argv.slice(2);
382
+ const silentRequested = requestsSilent(rawArguments);
383
+ const stdinIsPiped = standardInputIsPiped();
384
+ let parsed;
385
+ try {
386
+ parsed = parseArguments(rawArguments, { stdinIsPiped });
387
+ } catch (error) {
388
+ if (!silentRequested) {
389
+ console.error(error.message);
390
+ }
391
+ process.exitCode = 1;
392
+ return;
393
+ }
394
+
395
+ const { flags, hasInput, indentationCount, input } = parsed;
396
+ if (flags.version) {
397
+ console.log(pkg.version);
398
+ return;
399
+ }
400
+ if (flags.help) {
401
+ console.log(help);
402
+ return;
403
+ }
404
+
405
+ const printsJson =
406
+ flags.stdout || input.includes("-") || (!hasInput && stdinIsPiped);
407
+
408
+ if (!flags.silent && !flags.ci && !printsJson && process.stdout.isTTY) {
409
+ try {
410
+ updateNotifier({ pkg }).notify();
411
+ } catch {}
412
+ }
413
+
414
+ const options = {
415
+ arrays: flags.arrays,
416
+ indentationCount,
417
+ lineEnding: flags.lineEnding || undefined,
418
+ pack: flags.pack,
419
+ tabs: flags.tabs,
420
+ };
421
+
422
+ if (printsJson) {
423
+ try {
424
+ const source = await resolveStdoutSource(hasInput ? input : ["-"], flags);
425
+ process.stdout.on("error", handleStdoutError);
426
+ process.stdout.write(await formatSource(source, options));
427
+ } catch (error) {
428
+ if (!flags.silent) {
429
+ console.error(`${prefix}${errorMessage(error)}`);
430
+ }
431
+ process.exitCode = 1;
432
+ }
433
+ return;
434
+ }
435
+
436
+ const { colour } = createPrinter();
437
+ let paths;
438
+ try {
439
+ paths = await discoverPaths(input, flags);
440
+ } catch (error) {
441
+ if (!flags.silent) {
442
+ console.error(`${prefix}${error}`);
443
+ }
444
+ process.exitCode = 1;
445
+ return;
446
+ }
447
+
448
+ if (!paths.length) {
449
+ if (!flags.silent) {
450
+ console.log(`${prefix}The inputs don't lead to any JSON files. Exiting.`);
451
+ }
452
+ return;
453
+ }
454
+
455
+ if (flags.dry) {
456
+ if (!flags.silent) {
457
+ console.log(
458
+ `${prefix}We'd try to sort the following files:\n${paths.join("\n")}`,
228
459
  );
229
- return;
230
460
  }
461
+ return;
462
+ }
231
463
 
232
- const options = {
233
- arrays: cli.flags.arrays,
234
- ci: cli.flags.ci,
235
- indentationCount,
236
- lineEnding: cli.flags.lineEnding,
237
- pack: cli.flags.pack,
238
- tabs: cli.flags.tabs,
464
+ try {
465
+ const { successful, unsorted } = await processFiles(paths, {
466
+ ...options,
467
+ ci: flags.ci,
239
468
  onOutcome(outcome) {
240
- if (cli.flags.silent) {
469
+ if (flags.silent) {
241
470
  return;
242
471
  }
243
472
  if (outcome.status === "failure") {
244
- log(
245
- `${colour(prefix, colours.grey)}${outcome.path} - ${colour(
246
- "BAD",
247
- colours.red,
248
- )} (${outcome.stage}) - ${outcome.error}`,
249
- );
250
- } else if (!cli.flags.ci) {
251
- log(
252
- `${colour(prefix, colours.grey)}${outcome.path} - ${colour(
253
- "OK",
254
- colours.green,
255
- )}`,
473
+ console.error(
474
+ `${prefix}${outcome.path} - BAD (${outcome.stage}) - ${outcome.error}`,
256
475
  );
476
+ } else if (!flags.ci) {
477
+ console.log(`${prefix}${outcome.path} - OK`);
257
478
  }
258
479
  },
259
- };
260
-
261
- return processFiles(paths, options)
262
- .then(({ successful, unsorted }) => {
263
- if (cli.flags.silent) {
264
- if (cli.flags.ci && unsorted.length) {
265
- process.exitCode = 9;
266
- }
267
- return;
268
- }
269
- if (cli.flags.ci) {
270
- if (unsorted.length) {
271
- log(
272
- `${colour(prefix, colours.grey)}${colour(
273
- "Unsorted files:",
274
- colours.red,
275
- )}\n${unsorted.join("\n")}`,
276
- );
277
- process.exitCode = 9;
278
- } else {
279
- log(
280
- `${colour(prefix, colours.grey)}${colour(
281
- "All files were already sorted:",
282
- colours.white,
283
- )}\n${successful.join("\n")}`,
284
- );
285
- }
286
- return;
287
- }
288
- log(
289
- `\n${colour(prefix, colours.grey)}${colour(
290
- `All ${successful.length} file${
291
- successful.length === 1 ? "" : "s"
292
- } sorted`,
293
- colours.green,
294
- )}`,
480
+ });
481
+
482
+ if (flags.silent) {
483
+ if (flags.ci && unsorted.length) {
484
+ process.exitCode = 9;
485
+ }
486
+ return;
487
+ }
488
+ if (flags.ci) {
489
+ if (unsorted.length) {
490
+ console.log(
491
+ `${prefix}${colour("Unsorted files:", "red")}\n${unsorted.join("\n")}`,
295
492
  );
296
- })
297
- .catch((error) => {
298
- if (!(error instanceof ProcessingError)) {
299
- throw error;
300
- }
301
- if (!cli.flags.silent) {
302
- if (cli.flags.ci) {
303
- const unsorted = new Set(error.unsorted);
304
- const alreadySorted = error.successful.filter(
305
- (filePath) => !unsorted.has(filePath),
306
- );
307
- if (alreadySorted.length) {
308
- log(
309
- `${colour(prefix, colours.grey)}${colour(
310
- `${alreadySorted.length} file${
311
- alreadySorted.length === 1 ? "" : "s"
312
- } already sorted:`,
313
- colours.green,
314
- )}\n${alreadySorted.join("\n")}`,
315
- );
316
- }
317
- if (error.unsorted.length) {
318
- log(
319
- `${colour(prefix, colours.grey)}${colour(
320
- "Unsorted files:",
321
- colours.red,
322
- )}\n${error.unsorted.join("\n")}`,
323
- );
324
- }
325
- } else if (error.successful.length) {
326
- log(
327
- `\n${colour(prefix, colours.grey)}${colour(
328
- `${error.successful.length} file${
329
- error.successful.length === 1 ? "" : "s"
330
- } sorted`,
331
- colours.green,
332
- )}`,
333
- );
334
- }
335
- log(
336
- `${colour(prefix, colours.grey)}${colour(
337
- `${error.failures.length} file${
338
- error.failures.length === 1 ? "" : "s"
339
- } could not be ${cli.flags.ci ? "checked" : "sorted"}`,
340
- colours.red,
341
- )} ${colour(
342
- ` - ${error.failures
343
- .map(({ path: failedPath }) => failedPath)
344
- .join(" - ")}`,
345
- colours.grey,
346
- )}`,
493
+ process.exitCode = 9;
494
+ } else {
495
+ console.log(
496
+ `${prefix}${colour("All files were already sorted:", "white")}\n${successful.join("\n")}`,
497
+ );
498
+ }
499
+ return;
500
+ }
501
+ console.log(
502
+ `\n${prefix}${colour(
503
+ `All ${successful.length} file${successful.length === 1 ? "" : "s"} sorted`,
504
+ "green",
505
+ )}`,
506
+ );
507
+ } catch (error) {
508
+ if (!(error instanceof ProcessingError)) {
509
+ if (!flags.silent) {
510
+ console.error(`${prefix}${error}`);
511
+ }
512
+ process.exitCode = 1;
513
+ return;
514
+ }
515
+
516
+ if (!flags.silent) {
517
+ if (flags.ci) {
518
+ const unsorted = new Set(error.unsorted);
519
+ const alreadySorted = error.successful.filter(
520
+ (filePath) => !unsorted.has(filePath),
521
+ );
522
+ if (alreadySorted.length) {
523
+ console.log(
524
+ `${prefix}${alreadySorted.length} file${alreadySorted.length === 1 ? "" : "s"} already sorted:\n${alreadySorted.join("\n")}`,
347
525
  );
348
526
  }
349
- process.exitCode = 1;
350
- });
351
- })
352
- .catch((err) => {
353
- if (!cli.flags.silent) {
354
- log(
355
- `${colour(prefix, colours.grey)}${colour("Oops!", colours.red)} ${err}`,
527
+ if (error.unsorted.length) {
528
+ console.log(`${prefix}Unsorted files:\n${error.unsorted.join("\n")}`);
529
+ }
530
+ } else if (error.successful.length) {
531
+ console.log(
532
+ `\n${prefix}${error.successful.length} file${error.successful.length === 1 ? "" : "s"} sorted`,
533
+ );
534
+ }
535
+ console.error(
536
+ `${prefix}${error.failures.length} file${error.failures.length === 1 ? "" : "s"} could not be ${flags.ci ? "checked" : "sorted"} - ${error.failures.map(({ path: failedPath }) => failedPath).join(" - ")}`,
356
537
  );
357
538
  }
358
539
  process.exitCode = 1;
359
- });
540
+ }
541
+ }
542
+
543
+ await main();