@docker-doctor/cli 0.2.0 → 0.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/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_src = require('./src-BUYNqQd8.cjs');
2
+ const require_src = require('./src-TEhFWhpk.cjs');
3
3
  let node_fs_promises = require("node:fs/promises");
4
4
  node_fs_promises = require_src.__toESM(node_fs_promises, 1);
5
5
  let node_path = require("node:path");
@@ -9,10 +9,418 @@ node_os = require_src.__toESM(node_os, 1);
9
9
  let node_readline = require("node:readline");
10
10
  node_readline = require_src.__toESM(node_readline, 1);
11
11
  let node_timers_promises = require("node:timers/promises");
12
- let chalk = require("chalk");
13
- chalk = require_src.__toESM(chalk, 1);
12
+ let node_process = require("node:process");
13
+ node_process = require_src.__toESM(node_process, 1);
14
+ let node_tty = require("node:tty");
15
+ node_tty = require_src.__toESM(node_tty, 1);
14
16
  let commander = require("commander");
15
17
 
18
+ //#region ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
19
+ const ANSI_BACKGROUND_OFFSET = 10;
20
+ const wrapAnsi16 = (offset = 0) => (code) => `\u001B[${code + offset}m`;
21
+ const wrapAnsi256 = (offset = 0) => (code) => `\u001B[${38 + offset};5;${code}m`;
22
+ const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`;
23
+ const styles$1 = {
24
+ modifier: {
25
+ reset: [0, 0],
26
+ bold: [1, 22],
27
+ dim: [2, 22],
28
+ italic: [3, 23],
29
+ underline: [4, 24],
30
+ overline: [53, 55],
31
+ inverse: [7, 27],
32
+ hidden: [8, 28],
33
+ strikethrough: [9, 29]
34
+ },
35
+ color: {
36
+ black: [30, 39],
37
+ red: [31, 39],
38
+ green: [32, 39],
39
+ yellow: [33, 39],
40
+ blue: [34, 39],
41
+ magenta: [35, 39],
42
+ cyan: [36, 39],
43
+ white: [37, 39],
44
+ blackBright: [90, 39],
45
+ gray: [90, 39],
46
+ grey: [90, 39],
47
+ redBright: [91, 39],
48
+ greenBright: [92, 39],
49
+ yellowBright: [93, 39],
50
+ blueBright: [94, 39],
51
+ magentaBright: [95, 39],
52
+ cyanBright: [96, 39],
53
+ whiteBright: [97, 39]
54
+ },
55
+ bgColor: {
56
+ bgBlack: [40, 49],
57
+ bgRed: [41, 49],
58
+ bgGreen: [42, 49],
59
+ bgYellow: [43, 49],
60
+ bgBlue: [44, 49],
61
+ bgMagenta: [45, 49],
62
+ bgCyan: [46, 49],
63
+ bgWhite: [47, 49],
64
+ bgBlackBright: [100, 49],
65
+ bgGray: [100, 49],
66
+ bgGrey: [100, 49],
67
+ bgRedBright: [101, 49],
68
+ bgGreenBright: [102, 49],
69
+ bgYellowBright: [103, 49],
70
+ bgBlueBright: [104, 49],
71
+ bgMagentaBright: [105, 49],
72
+ bgCyanBright: [106, 49],
73
+ bgWhiteBright: [107, 49]
74
+ }
75
+ };
76
+ const modifierNames = Object.keys(styles$1.modifier);
77
+ const foregroundColorNames = Object.keys(styles$1.color);
78
+ const backgroundColorNames = Object.keys(styles$1.bgColor);
79
+ const colorNames = [...foregroundColorNames, ...backgroundColorNames];
80
+ function assembleStyles() {
81
+ const codes = /* @__PURE__ */ new Map();
82
+ for (const [groupName, group] of Object.entries(styles$1)) {
83
+ for (const [styleName, style] of Object.entries(group)) {
84
+ styles$1[styleName] = {
85
+ open: `\u001B[${style[0]}m`,
86
+ close: `\u001B[${style[1]}m`
87
+ };
88
+ group[styleName] = styles$1[styleName];
89
+ codes.set(style[0], style[1]);
90
+ }
91
+ Object.defineProperty(styles$1, groupName, {
92
+ value: group,
93
+ enumerable: false
94
+ });
95
+ }
96
+ Object.defineProperty(styles$1, "codes", {
97
+ value: codes,
98
+ enumerable: false
99
+ });
100
+ styles$1.color.close = "\x1B[39m";
101
+ styles$1.bgColor.close = "\x1B[49m";
102
+ styles$1.color.ansi = wrapAnsi16();
103
+ styles$1.color.ansi256 = wrapAnsi256();
104
+ styles$1.color.ansi16m = wrapAnsi16m();
105
+ styles$1.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
106
+ styles$1.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
107
+ styles$1.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
108
+ Object.defineProperties(styles$1, {
109
+ rgbToAnsi256: {
110
+ value(red, green, blue) {
111
+ if (red === green && green === blue) {
112
+ if (red < 8) return 16;
113
+ if (red > 248) return 231;
114
+ return Math.round((red - 8) / 247 * 24) + 232;
115
+ }
116
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
117
+ },
118
+ enumerable: false
119
+ },
120
+ hexToRgb: {
121
+ value(hex) {
122
+ const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
123
+ if (!matches) return [
124
+ 0,
125
+ 0,
126
+ 0
127
+ ];
128
+ let [colorString] = matches;
129
+ if (colorString.length === 3) colorString = [...colorString].map((character) => character + character).join("");
130
+ const integer = Number.parseInt(colorString, 16);
131
+ return [
132
+ integer >> 16 & 255,
133
+ integer >> 8 & 255,
134
+ integer & 255
135
+ ];
136
+ },
137
+ enumerable: false
138
+ },
139
+ hexToAnsi256: {
140
+ value: (hex) => styles$1.rgbToAnsi256(...styles$1.hexToRgb(hex)),
141
+ enumerable: false
142
+ },
143
+ ansi256ToAnsi: {
144
+ value(code) {
145
+ if (code < 8) return 30 + code;
146
+ if (code < 16) return 90 + (code - 8);
147
+ let red;
148
+ let green;
149
+ let blue;
150
+ if (code >= 232) {
151
+ red = ((code - 232) * 10 + 8) / 255;
152
+ green = red;
153
+ blue = red;
154
+ } else {
155
+ code -= 16;
156
+ const remainder = code % 36;
157
+ red = Math.floor(code / 36) / 5;
158
+ green = Math.floor(remainder / 6) / 5;
159
+ blue = remainder % 6 / 5;
160
+ }
161
+ const value = Math.max(red, green, blue) * 2;
162
+ if (value === 0) return 30;
163
+ let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
164
+ if (value === 2) result += 60;
165
+ return result;
166
+ },
167
+ enumerable: false
168
+ },
169
+ rgbToAnsi: {
170
+ value: (red, green, blue) => styles$1.ansi256ToAnsi(styles$1.rgbToAnsi256(red, green, blue)),
171
+ enumerable: false
172
+ },
173
+ hexToAnsi: {
174
+ value: (hex) => styles$1.ansi256ToAnsi(styles$1.hexToAnsi256(hex)),
175
+ enumerable: false
176
+ }
177
+ });
178
+ return styles$1;
179
+ }
180
+ const ansiStyles = assembleStyles();
181
+
182
+ //#endregion
183
+ //#region ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
184
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : node_process.default.argv) {
185
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
186
+ const position = argv.indexOf(prefix + flag);
187
+ const terminatorPosition = argv.indexOf("--");
188
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
189
+ }
190
+ const { env } = node_process.default;
191
+ let flagForceColor;
192
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) flagForceColor = 0;
193
+ else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) flagForceColor = 1;
194
+ function envForceColor() {
195
+ if ("FORCE_COLOR" in env) {
196
+ if (env.FORCE_COLOR === "true") return 1;
197
+ if (env.FORCE_COLOR === "false") return 0;
198
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
199
+ }
200
+ }
201
+ function translateLevel(level) {
202
+ if (level === 0) return false;
203
+ return {
204
+ level,
205
+ hasBasic: true,
206
+ has256: level >= 2,
207
+ has16m: level >= 3
208
+ };
209
+ }
210
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
211
+ const noFlagForceColor = envForceColor();
212
+ if (noFlagForceColor !== void 0) flagForceColor = noFlagForceColor;
213
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
214
+ if (forceColor === 0) return 0;
215
+ if (sniffFlags) {
216
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) return 3;
217
+ if (hasFlag("color=256")) return 2;
218
+ }
219
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) return 1;
220
+ if (haveStream && !streamIsTTY && forceColor === void 0) return 0;
221
+ const min = forceColor || 0;
222
+ if (env.TERM === "dumb") return min;
223
+ if (node_process.default.platform === "win32") {
224
+ const osRelease = node_os.default.release().split(".");
225
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
226
+ return 1;
227
+ }
228
+ if ("CI" in env) {
229
+ if ([
230
+ "GITHUB_ACTIONS",
231
+ "GITEA_ACTIONS",
232
+ "CIRCLECI"
233
+ ].some((key) => key in env)) return 3;
234
+ if ([
235
+ "TRAVIS",
236
+ "APPVEYOR",
237
+ "GITLAB_CI",
238
+ "BUILDKITE",
239
+ "DRONE"
240
+ ].some((sign) => sign in env) || env.CI_NAME === "codeship") return 1;
241
+ return min;
242
+ }
243
+ if ("TEAMCITY_VERSION" in env) return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
244
+ if (env.COLORTERM === "truecolor") return 3;
245
+ if (env.TERM === "xterm-kitty") return 3;
246
+ if (env.TERM === "xterm-ghostty") return 3;
247
+ if (env.TERM === "wezterm") return 3;
248
+ if ("TERM_PROGRAM" in env) {
249
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
250
+ switch (env.TERM_PROGRAM) {
251
+ case "iTerm.app": return version >= 3 ? 3 : 2;
252
+ case "Apple_Terminal": return 2;
253
+ }
254
+ }
255
+ if (/-256(color)?$/i.test(env.TERM)) return 2;
256
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) return 1;
257
+ if ("COLORTERM" in env) return 1;
258
+ return min;
259
+ }
260
+ function createSupportsColor(stream, options = {}) {
261
+ return translateLevel(_supportsColor(stream, {
262
+ streamIsTTY: stream && stream.isTTY,
263
+ ...options
264
+ }));
265
+ }
266
+ const supportsColor = {
267
+ stdout: createSupportsColor({ isTTY: node_tty.default.isatty(1) }),
268
+ stderr: createSupportsColor({ isTTY: node_tty.default.isatty(2) })
269
+ };
270
+
271
+ //#endregion
272
+ //#region ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/utilities.js
273
+ function stringReplaceAll(string, substring, replacer) {
274
+ let index = string.indexOf(substring);
275
+ if (index === -1) return string;
276
+ const substringLength = substring.length;
277
+ let endIndex = 0;
278
+ let returnValue = "";
279
+ do {
280
+ returnValue += string.slice(endIndex, index) + substring + replacer;
281
+ endIndex = index + substringLength;
282
+ index = string.indexOf(substring, endIndex);
283
+ } while (index !== -1);
284
+ returnValue += string.slice(endIndex);
285
+ return returnValue;
286
+ }
287
+ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
288
+ let endIndex = 0;
289
+ let returnValue = "";
290
+ do {
291
+ const gotCR = string[index - 1] === "\r";
292
+ returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
293
+ endIndex = index + 1;
294
+ index = string.indexOf("\n", endIndex);
295
+ } while (index !== -1);
296
+ returnValue += string.slice(endIndex);
297
+ return returnValue;
298
+ }
299
+
300
+ //#endregion
301
+ //#region ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/index.js
302
+ const { stdout: stdoutColor, stderr: stderrColor } = supportsColor;
303
+ const GENERATOR = Symbol("GENERATOR");
304
+ const STYLER = Symbol("STYLER");
305
+ const IS_EMPTY = Symbol("IS_EMPTY");
306
+ const levelMapping = [
307
+ "ansi",
308
+ "ansi",
309
+ "ansi256",
310
+ "ansi16m"
311
+ ];
312
+ const styles = Object.create(null);
313
+ const applyOptions = (object, options = {}) => {
314
+ if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) throw new Error("The `level` option should be an integer from 0 to 3");
315
+ const colorLevel = stdoutColor ? stdoutColor.level : 0;
316
+ object.level = options.level === void 0 ? colorLevel : options.level;
317
+ };
318
+ const chalkFactory = (options) => {
319
+ const chalk = (...strings) => strings.join(" ");
320
+ applyOptions(chalk, options);
321
+ Object.setPrototypeOf(chalk, createChalk.prototype);
322
+ return chalk;
323
+ };
324
+ function createChalk(options) {
325
+ return chalkFactory(options);
326
+ }
327
+ Object.setPrototypeOf(createChalk.prototype, Function.prototype);
328
+ for (const [styleName, style] of Object.entries(ansiStyles)) styles[styleName] = { get() {
329
+ const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
330
+ Object.defineProperty(this, styleName, { value: builder });
331
+ return builder;
332
+ } };
333
+ styles.visible = { get() {
334
+ const builder = createBuilder(this, this[STYLER], true);
335
+ Object.defineProperty(this, "visible", { value: builder });
336
+ return builder;
337
+ } };
338
+ const getModelAnsi = (model, level, type, ...arguments_) => {
339
+ if (model === "rgb") {
340
+ if (level === "ansi16m") return ansiStyles[type].ansi16m(...arguments_);
341
+ if (level === "ansi256") return ansiStyles[type].ansi256(ansiStyles.rgbToAnsi256(...arguments_));
342
+ return ansiStyles[type].ansi(ansiStyles.rgbToAnsi(...arguments_));
343
+ }
344
+ if (model === "hex") return getModelAnsi("rgb", level, type, ...ansiStyles.hexToRgb(...arguments_));
345
+ return ansiStyles[type][model](...arguments_);
346
+ };
347
+ for (const model of [
348
+ "rgb",
349
+ "hex",
350
+ "ansi256"
351
+ ]) {
352
+ styles[model] = { get() {
353
+ const { level } = this;
354
+ return function(...arguments_) {
355
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansiStyles.color.close, this[STYLER]);
356
+ return createBuilder(this, styler, this[IS_EMPTY]);
357
+ };
358
+ } };
359
+ const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
360
+ styles[bgModel] = { get() {
361
+ const { level } = this;
362
+ return function(...arguments_) {
363
+ const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansiStyles.bgColor.close, this[STYLER]);
364
+ return createBuilder(this, styler, this[IS_EMPTY]);
365
+ };
366
+ } };
367
+ }
368
+ const proto = Object.defineProperties(() => {}, {
369
+ ...styles,
370
+ level: {
371
+ enumerable: true,
372
+ get() {
373
+ return this[GENERATOR].level;
374
+ },
375
+ set(level) {
376
+ this[GENERATOR].level = level;
377
+ }
378
+ }
379
+ });
380
+ const createStyler = (open, close, parent) => {
381
+ let openAll;
382
+ let closeAll;
383
+ if (parent === void 0) {
384
+ openAll = open;
385
+ closeAll = close;
386
+ } else {
387
+ openAll = parent.openAll + open;
388
+ closeAll = close + parent.closeAll;
389
+ }
390
+ return {
391
+ open,
392
+ close,
393
+ openAll,
394
+ closeAll,
395
+ parent
396
+ };
397
+ };
398
+ const createBuilder = (self, _styler, _isEmpty) => {
399
+ const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
400
+ Object.setPrototypeOf(builder, proto);
401
+ builder[GENERATOR] = self;
402
+ builder[STYLER] = _styler;
403
+ builder[IS_EMPTY] = _isEmpty;
404
+ return builder;
405
+ };
406
+ const applyStyle = (self, string) => {
407
+ if (self.level <= 0 || !string) return self[IS_EMPTY] ? "" : string;
408
+ let styler = self[STYLER];
409
+ if (styler === void 0) return string;
410
+ const { openAll, closeAll } = styler;
411
+ if (string.includes("\x1B")) while (styler !== void 0) {
412
+ string = stringReplaceAll(string, styler.close, styler.open);
413
+ styler = styler.parent;
414
+ }
415
+ const lfIndex = string.indexOf("\n");
416
+ if (lfIndex !== -1) string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
417
+ return openAll + string + closeAll;
418
+ };
419
+ Object.defineProperties(createChalk.prototype, styles);
420
+ const chalk = createChalk();
421
+ const chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
422
+
423
+ //#endregion
16
424
  //#region src/formatters/terminal.ts
17
425
  const printCodeFrame = (content, line, severityColor) => {
18
426
  if (!content || !line) return;
@@ -23,23 +431,23 @@ const printCodeFrame = (content, line, severityColor) => {
23
431
  const rawLine = lines[i - 1];
24
432
  const isTarget = i === line;
25
433
  const lineNumberStr = String(i).padStart(5, " ");
26
- if (isTarget) console.log(` ${severityColor(">")} ${chalk.default.bold(lineNumberStr)} │ ${chalk.default.white(rawLine)}`);
27
- else console.log(` ${chalk.default.dim(lineNumberStr)} │ ${chalk.default.dim(rawLine)}`);
434
+ if (isTarget) console.log(` ${severityColor(">")} ${chalk.bold(lineNumberStr)} │ ${chalk.white(rawLine)}`);
435
+ else console.log(` ${chalk.dim(lineNumberStr)} │ ${chalk.dim(rawLine)}`);
28
436
  }
29
437
  console.log();
30
438
  };
31
439
  const printDiscoveredFiles = (project) => {
32
440
  console.log(`\nDiscovered Files:`);
33
- console.log(` Dockerfile(s): ${project.dockerfiles.length ? project.dockerfiles.map((f) => chalk.default.cyan(f)).join(", ") : chalk.default.dim("None")}`);
34
- console.log(` Compose file(s): ${project.composeFiles.length ? project.composeFiles.map((f) => chalk.default.cyan(f)).join(", ") : chalk.default.dim("None")}`);
441
+ console.log(` Dockerfile(s): ${project.dockerfiles.length ? project.dockerfiles.map((f) => chalk.cyan(f)).join(", ") : chalk.dim("None")}`);
442
+ console.log(` Compose file(s): ${project.composeFiles.length ? project.composeFiles.map((f) => chalk.cyan(f)).join(", ") : chalk.dim("None")}`);
35
443
  };
36
444
  const printDiagnostics = (diagnostics, verbose, fileContents, categoryIssueCounts) => {
37
445
  if (diagnostics.length === 0) {
38
- console.log(`\n${chalk.default.green.bold("✔ No issues found! Your Docker setup looks healthy.")}`);
446
+ console.log(`\n${chalk.green.bold("✔ No issues found! Your Docker setup looks healthy.")}`);
39
447
  return;
40
448
  }
41
449
  if (!verbose) {
42
- console.log(`\n All ${chalk.default.bold(diagnostics.length)} issues\n`);
450
+ console.log(`\n All ${chalk.bold(diagnostics.length)} issues\n`);
43
451
  for (const cat of [
44
452
  "Security",
45
453
  "Performance",
@@ -49,16 +457,16 @@ const printDiagnostics = (diagnostics, verbose, fileContents, categoryIssueCount
49
457
  ]) {
50
458
  const count = categoryIssueCounts[cat];
51
459
  const issueLabel = count === 1 ? "1 issue" : `${count} issues`;
52
- console.log(` ${cat} › ${chalk.default.dim(issueLabel)}`);
460
+ console.log(` ${cat} › ${chalk.dim(issueLabel)}`);
53
461
  }
54
- console.log(`\n Run ${chalk.default.cyan("docker-doctor --verbose")} to list every error and warning`);
462
+ console.log(`\n Run ${chalk.cyan("docker-doctor --verbose")} to list every error and warning`);
55
463
  const ruleCounts = {};
56
464
  for (const d of diagnostics) ruleCounts[d.rule] = (ruleCounts[d.rule] || 0) + 1;
57
465
  const migrationRules = Object.entries(ruleCounts).filter(([_, count]) => count >= 5);
58
466
  if (migrationRules.length > 0) {
59
467
  console.log();
60
- console.log(` ${chalk.default.yellow("⚠ Migration-scale change: sample before you sweep")}`);
61
- for (const [rule, count] of migrationRules) console.log(` ${chalk.default.cyan(rule)} ×${count} across ${count} files`);
468
+ console.log(` ${chalk.yellow("⚠ Migration-scale change: sample before you sweep")}`);
469
+ for (const [rule, count] of migrationRules) console.log(` ${chalk.cyan(rule)} ×${count} across ${count} files`);
62
470
  console.log(` Fixing all of them at once is hard to review and prone to`);
63
471
  console.log(` subtle mistakes across the whole repo. Fix a representative`);
64
472
  console.log(` few first and confirm the recipe holds. Then get the code`);
@@ -67,30 +475,30 @@ const printDiagnostics = (diagnostics, verbose, fileContents, categoryIssueCount
67
475
  }
68
476
  return;
69
477
  }
70
- console.log(`\nFound ${chalk.default.bold(diagnostics.length)} issue(s):`);
478
+ console.log(`\nFound ${chalk.bold(diagnostics.length)} issue(s):`);
71
479
  const filesGrouped = {};
72
480
  for (const d of diagnostics) {
73
481
  if (!filesGrouped[d.file]) filesGrouped[d.file] = [];
74
482
  filesGrouped[d.file].push(d);
75
483
  }
76
484
  for (const [file, fileDiags] of Object.entries(filesGrouped)) {
77
- console.log(`\n${chalk.default.underline.bold(file)}`);
485
+ console.log(`\n${chalk.underline.bold(file)}`);
78
486
  for (const d of fileDiags) {
79
- let sevColor = chalk.default.cyan;
487
+ let sevColor = chalk.cyan;
80
488
  let prefix = "ℹ INFO";
81
489
  if (d.severity === "error") {
82
- sevColor = chalk.default.red.bold;
490
+ sevColor = chalk.red.bold;
83
491
  prefix = "✖ ERROR";
84
492
  } else if (d.severity === "warning") {
85
- sevColor = chalk.default.yellow;
493
+ sevColor = chalk.yellow;
86
494
  prefix = "⚠ WARN";
87
495
  }
88
496
  const lineInfo = d.line ? `:${d.line}` : "";
89
- console.log(` ${sevColor(prefix)} [${chalk.default.dim(d.rule)}]${lineInfo}`);
497
+ console.log(` ${sevColor(prefix)} [${chalk.dim(d.rule)}]${lineInfo}`);
90
498
  const content = fileContents[file];
91
499
  printCodeFrame(content, d.line, sevColor);
92
- console.log(` ${chalk.default.white(d.message)}`);
93
- console.log(` ${chalk.default.dim("Help:")} ${d.help}`);
500
+ console.log(` ${chalk.white(d.message)}`);
501
+ console.log(` ${chalk.dim("Help:")} ${d.help}`);
94
502
  console.log();
95
503
  }
96
504
  }
@@ -100,10 +508,10 @@ const getWhaleMascot = (score, border) => {
100
508
  let spout = " ";
101
509
  if (score >= 75) {
102
510
  eyes = "◠ ◠";
103
- spout = chalk.default.cyan(" \":\" ");
511
+ spout = chalk.cyan(" \":\" ");
104
512
  } else if (score >= 50) {
105
513
  eyes = "• •";
106
- spout = chalk.default.cyan(" . ");
514
+ spout = chalk.cyan(" . ");
107
515
  }
108
516
  return [
109
517
  spout,
@@ -113,15 +521,15 @@ const getWhaleMascot = (score, border) => {
113
521
  ];
114
522
  };
115
523
  const easeOutCubic = (x) => 1 - (1 - x) ** 3;
116
- const printScoreBox = async (score, label, categoryIssueCounts) => {
524
+ const printScoreBox = async (score, label, categoryIssueCounts, warningsCount, errorsCount) => {
117
525
  const { isTTY } = process.stdout;
118
526
  const shouldAnimate = isTTY && !process.env.CI && !process.env.NO_ANIMATION && process.env.TERM !== "dumb" && process.env.NODE_ENV !== "test";
119
- let scoreColor = chalk.default.red.bold;
120
- if (score >= 90) scoreColor = chalk.default.green.bold;
121
- else if (score >= 75) scoreColor = chalk.default.yellow.bold;
122
- else if (score >= 50) scoreColor = chalk.default.magenta.bold;
527
+ let scoreColor = chalk.red.bold;
528
+ if (score >= 90) scoreColor = chalk.green.bold;
529
+ else if (score >= 75) scoreColor = chalk.yellow.bold;
530
+ else if (score >= 50) scoreColor = chalk.magenta.bold;
123
531
  const whaleLines = getWhaleMascot(score, scoreColor);
124
- const shareUrl = `https://github.com/PunGrumpy/docker-doctor/share?s=${score}&w=${Object.values(categoryIssueCounts).reduce((a, b) => a + b, 0)}`;
532
+ const shareUrl = `https://docker-doctor.vercel.app/share?s=${score}&w=${warningsCount}&e=${errorsCount}`;
125
533
  if (shouldAnimate) {
126
534
  const frameCount = 20;
127
535
  const frameDelay = 15;
@@ -132,10 +540,10 @@ const printScoreBox = async (score, label, categoryIssueCounts) => {
132
540
  const currentScore = Math.round(score * progress);
133
541
  const filledBlocks = Math.round(currentScore / 2);
134
542
  const emptyBlocks = 50 - filledBlocks;
135
- const bar = scoreColor("█".repeat(filledBlocks)) + chalk.default.dim("░".repeat(emptyBlocks));
543
+ const bar = scoreColor("█".repeat(filledBlocks)) + chalk.dim("░".repeat(emptyBlocks));
136
544
  if (frame > 0) process.stdout.write("\x1B[4A\r");
137
545
  else console.log();
138
- process.stdout.write(` ${whaleLines[0]} ${scoreColor(`${currentScore} / 100`)} ${scoreColor(label)}\n ${whaleLines[1]} ${bar}\n ${whaleLines[2]} ${chalk.default.dim("Docker Doctor (https://github.com/PunGrumpy/docker-doctor)")}\n ${whaleLines[3]}\n`);
546
+ process.stdout.write(` ${whaleLines[0]} ${scoreColor(`${currentScore} / 100`)} ${scoreColor(label)}\n ${whaleLines[1]} ${bar}\n ${whaleLines[2]} ${chalk.dim("Docker Doctor (https://docker-doctor.vercel.app)")}\n ${whaleLines[3]}\n`);
139
547
  if (frame < frameCount) await (0, node_timers_promises.setTimeout)(frameDelay);
140
548
  }
141
549
  } finally {
@@ -144,24 +552,24 @@ const printScoreBox = async (score, label, categoryIssueCounts) => {
144
552
  } else {
145
553
  const filledBlocks = Math.round(score / 2);
146
554
  const emptyBlocks = 50 - filledBlocks;
147
- const bar = scoreColor("█".repeat(filledBlocks)) + chalk.default.dim("░".repeat(emptyBlocks));
555
+ const bar = scoreColor("█".repeat(filledBlocks)) + chalk.dim("░".repeat(emptyBlocks));
148
556
  console.log(`\n ${whaleLines[0]} ${scoreColor(`${score} / 100`)} ${scoreColor(label)}`);
149
557
  console.log(` ${whaleLines[1]} ${bar}`);
150
- console.log(` ${whaleLines[2]} ${chalk.default.dim("Docker Doctor (https://github.com/PunGrumpy/docker-doctor)")}`);
558
+ console.log(` ${whaleLines[2]} ${chalk.dim("Docker Doctor (https://docker-doctor.vercel.app)")}`);
151
559
  console.log(` ${whaleLines[3]}`);
152
560
  }
153
- console.log(`\n ${chalk.default.dim("────────────────────────────────────────────────────────────")}\n`);
154
- console.log(` Share: ${chalk.default.cyan(shareUrl)}`);
561
+ console.log(`\n ${chalk.dim("────────────────────────────────────────────────────────────")}\n`);
562
+ console.log(` Share: ${chalk.cyan(shareUrl)}`);
155
563
  console.log(` Tell others how you did on socials\n`);
156
- console.log(` Docs: ${chalk.default.cyan("https://github.com/PunGrumpy/docker-doctor/docs")}`);
564
+ console.log(` Docs: ${chalk.cyan("https://docker-doctor.vercel.app")}`);
157
565
  console.log(` Learn more about fixing issues, setting up CI/CD, and`);
158
566
  console.log(` configuring rules with a config file\n`);
159
- console.log(` GitHub: ${chalk.default.cyan("https://github.com/PunGrumpy/docker-doctor")}`);
567
+ console.log(` GitHub: ${chalk.cyan("https://github.com/PunGrumpy/docker-doctor")}`);
160
568
  console.log(` Report issues and star the repository!`);
161
569
  };
162
570
  const formatTerminal = async (diagnostics, score, label, project, verbose = false, fileContents = {}) => {
163
571
  if (verbose) {
164
- console.log(`\n${chalk.default.bold("Docker Doctor Diagnostics")}`);
572
+ console.log(`\n${chalk.bold("Docker Doctor Diagnostics")}`);
165
573
  console.log(`=================================`);
166
574
  printDiscoveredFiles(project);
167
575
  }
@@ -172,12 +580,16 @@ const formatTerminal = async (diagnostics, score, label, project, verbose = fals
172
580
  Performance: 0,
173
581
  Security: 0
174
582
  };
583
+ let warningsCount = 0;
584
+ let errorsCount = 0;
175
585
  for (const d of diagnostics) {
176
586
  const category = require_src.findRule(d.rule)?.category || "Best Practices";
177
587
  categoryIssueCounts[category] = (categoryIssueCounts[category] || 0) + 1;
588
+ if (d.severity === "warning") warningsCount += 1;
589
+ else if (d.severity === "error") errorsCount += 1;
178
590
  }
179
591
  printDiagnostics(diagnostics, verbose, fileContents, categoryIssueCounts);
180
- await printScoreBox(score, label, categoryIssueCounts);
592
+ await printScoreBox(score, label, categoryIssueCounts, warningsCount, errorsCount);
181
593
  };
182
594
 
183
595
  //#endregion
@@ -192,12 +604,12 @@ const askConfirm = (question, defaultYes = false) => {
192
604
  process.stdout.write("\x1B[?25l");
193
605
  const render = (firstTime = false) => {
194
606
  if (!firstTime) process.stdout.write("\x1B[3A\r");
195
- process.stdout.write(`\r\u001B[K ${chalk.default.green("✔")} ${chalk.default.bold(question)}\n`);
196
- const yesPrefix = value ? chalk.default.cyan("❯ ") : " ";
197
- const yesText = value ? chalk.default.cyan.bold("Yes") : chalk.default.dim("Yes");
607
+ process.stdout.write(`\r\u001B[K ${chalk.green("✔")} ${chalk.bold(question)}\n`);
608
+ const yesPrefix = value ? chalk.cyan("❯ ") : " ";
609
+ const yesText = value ? chalk.cyan.bold("Yes") : chalk.dim("Yes");
198
610
  process.stdout.write(`\r\u001B[K${yesPrefix}${yesText}\n`);
199
- const noPrefix = value ? " " : chalk.default.cyan("❯ ");
200
- const noText = value ? chalk.default.dim("No") : chalk.default.cyan.bold("No");
611
+ const noPrefix = value ? " " : chalk.cyan("❯ ");
612
+ const noText = value ? chalk.dim("No") : chalk.cyan.bold("No");
201
613
  process.stdout.write(`\r\u001B[K${noPrefix}${noText}\n`);
202
614
  };
203
615
  render(true);
@@ -220,7 +632,7 @@ const askConfirm = (question, defaultYes = false) => {
220
632
  } else if (key.name === "return" || key.name === "enter" || str === "\r" || str === "\n") {
221
633
  cleanup();
222
634
  process.stdout.write("\x1B[3A\r\x1B[K");
223
- process.stdout.write(` ${chalk.default.green("✔")} ${chalk.default.bold(question)} › ${value ? chalk.default.cyan("Yes") : chalk.default.dim("No")}\n`);
635
+ process.stdout.write(` ${chalk.green("✔")} ${chalk.bold(question)} › ${value ? chalk.cyan("Yes") : chalk.dim("No")}\n`);
224
636
  process.stdout.write("\r\x1B[K\n");
225
637
  process.stdout.write("\r\x1B[K\n");
226
638
  process.stdout.write("\x1B[2A");
@@ -244,12 +656,12 @@ const askSelect = (question, options, defaultIndex = 0) => {
244
656
  process.stdout.write("\x1B[?25l");
245
657
  const render = (firstTime = false) => {
246
658
  if (!firstTime) process.stdout.write(`\u001B[${options.length + 1}A\r`);
247
- process.stdout.write(`\r\u001B[K ${chalk.default.green("✔")} ${chalk.default.bold(question)}\n`);
659
+ process.stdout.write(`\r\u001B[K ${chalk.green("✔")} ${chalk.bold(question)}\n`);
248
660
  let i = 0;
249
661
  for (const option of options) {
250
662
  const isSelected = i === index;
251
- const prefix = isSelected ? chalk.default.cyan("❯ ") : " ";
252
- const text = isSelected ? chalk.default.cyan.bold(option) : chalk.default.dim(option);
663
+ const prefix = isSelected ? chalk.cyan("❯ ") : " ";
664
+ const text = isSelected ? chalk.cyan.bold(option) : chalk.dim(option);
253
665
  process.stdout.write(`\r\u001B[K${prefix}${text}\n`);
254
666
  i += 1;
255
667
  }
@@ -271,7 +683,7 @@ const askSelect = (question, options, defaultIndex = 0) => {
271
683
  } else if (key.name === "return" || key.name === "enter" || str === "\r" || str === "\n") {
272
684
  cleanup();
273
685
  process.stdout.write(`\u001B[${options.length + 1}A\r\u001B[K`);
274
- process.stdout.write(` ${chalk.default.green("✔")} ${chalk.default.bold(question)} › ${chalk.default.cyan(options[index])}\n`);
686
+ process.stdout.write(` ${chalk.green("✔")} ${chalk.bold(question)} › ${chalk.cyan(options[index])}\n`);
275
687
  for (const _ of options) process.stdout.write("\r\x1B[K\n");
276
688
  process.stdout.write(`\u001B[${options.length}A`);
277
689
  resolve(index);
@@ -308,12 +720,12 @@ jobs:
308
720
  - name: Run docker-doctor
309
721
  run: bunx docker-doctor .
310
722
  `, "utf-8");
311
- console.log(`\n ${chalk.default.green("✨")} Created ${chalk.default.cyan(".github/workflows/docker-doctor.yml")}!`);
723
+ console.log(`\n ${chalk.green("✨")} Created ${chalk.cyan(".github/workflows/docker-doctor.yml")}!`);
312
724
  console.log(` Scan every pull request to prevent new Docker issues while you fix the backlog.`);
313
725
  }
314
726
  if (await askSelect("What would you like to do next?", ["View rules list", "Skip"]) === 0) {
315
- console.log(`\n ${chalk.default.bold("Available Rules:")}`);
316
- for (const r of require_src.allRules) console.log(` - ${chalk.default.cyan(r.key)}: ${r.message} (${chalk.default.dim(r.category)})`);
727
+ console.log(`\n ${chalk.bold("Available Rules:")}`);
728
+ for (const r of require_src.allRules) console.log(` - ${chalk.cyan(r.key)}: ${r.message} (${chalk.dim(r.category)})`);
317
729
  }
318
730
  } catch {}
319
731
  };
@@ -405,9 +817,9 @@ program.argument("[dir]", "directory to scan", ".").option("-v, --verbose", "sho
405
817
  };
406
818
  if (process.stdout.isTTY && !isSilent) {
407
819
  process.stdout.write("\x1B[?25l");
408
- process.stdout.write(`${chalk.default.cyan(spinnerFrames[0])} ${statusText}`);
820
+ process.stdout.write(`${chalk.cyan(spinnerFrames[0])} ${statusText}`);
409
821
  spinnerInterval = setInterval(() => {
410
- process.stdout.write(`\r\u001B[K${chalk.default.cyan(spinnerFrames[frameIndex])} ${statusText}`);
822
+ process.stdout.write(`\r\u001B[K${chalk.cyan(spinnerFrames[frameIndex])} ${statusText}`);
411
823
  frameIndex = (frameIndex + 1) % spinnerFrames.length;
412
824
  }, 80);
413
825
  }
@@ -433,21 +845,22 @@ program.argument("[dir]", "directory to scan", ".").option("-v, --verbose", "sho
433
845
  spinnerInterval = null;
434
846
  process.stdout.write("\r\x1B[K\x1B[?25h");
435
847
  }
436
- if (process.stdout.isTTY && !isSilent) console.log(`${chalk.default.green("✔")} Scanned ${projectFilesList.length} files in ${duration}s [~${concurrency} workers]`);
848
+ if (process.stdout.isTTY && !isSilent) console.log(`${chalk.green("✔")} Scanned ${projectFilesList.length} files in ${duration}s [~${concurrency} workers]`);
437
849
  if (options.score) {
438
850
  console.log(score);
439
- process.exit(score < 50 ? 1 : 0);
851
+ process.exitCode = score < 50 ? 1 : 0;
852
+ return;
440
853
  } else if (options.json) {
441
854
  const report = require_src.toJsonReport(filteredDiagnostics, score, label, project);
442
855
  console.log(JSON.stringify(report, null, 2));
443
856
  const hasErrors = filteredDiagnostics.some((d) => d.severity === "error");
444
- process.exit(hasErrors ? 1 : 0);
445
- } else {
446
- await formatTerminal(filteredDiagnostics, score, label, project, options.verbose, fileContents);
447
- const hasErrors = filteredDiagnostics.some((d) => d.severity === "error");
448
- if (process.stdout.isTTY && process.stdin.isTTY) await runInteractiveWizard();
449
- process.exit(hasErrors ? 1 : 0);
857
+ process.exitCode = hasErrors ? 1 : 0;
858
+ return;
450
859
  }
860
+ await formatTerminal(filteredDiagnostics, score, label, project, options.verbose, fileContents);
861
+ const hasErrors = filteredDiagnostics.some((d) => d.severity === "error");
862
+ if (process.stdout.isTTY && process.stdin.isTTY) await runInteractiveWizard();
863
+ process.exitCode = hasErrors ? 1 : 0;
451
864
  } finally {
452
865
  if (spinnerInterval !== null) {
453
866
  clearInterval(spinnerInterval);