@macrulez/devtoolz 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/README.md +131 -8
- package/dist/cli.js +265 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/case-check/report.js +8 -6
- package/dist/commands/case-check/report.js.map +1 -1
- package/dist/commands/circular-imports/report.js +11 -12
- package/dist/commands/circular-imports/report.js.map +1 -1
- package/dist/commands/console-strip/report.js +9 -7
- package/dist/commands/console-strip/report.js.map +1 -1
- package/dist/commands/dead-exports/report.js +6 -4
- package/dist/commands/dead-exports/report.js.map +1 -1
- package/dist/commands/empty-catch/report.js +5 -3
- package/dist/commands/empty-catch/report.js.map +1 -1
- package/dist/commands/exports-doctor/report.js +7 -5
- package/dist/commands/exports-doctor/report.js.map +1 -1
- package/dist/commands/full-check/report.d.ts +3 -0
- package/dist/commands/full-check/report.js +55 -0
- package/dist/commands/full-check/report.js.map +1 -0
- package/dist/commands/full-check/run.d.ts +32 -0
- package/dist/commands/full-check/run.js +251 -0
- package/dist/commands/full-check/run.js.map +1 -0
- package/dist/commands/image-hash/cache.d.ts +14 -0
- package/dist/commands/image-hash/cache.js +40 -0
- package/dist/commands/image-hash/cache.js.map +1 -0
- package/dist/commands/image-hash/core.d.ts +56 -0
- package/dist/commands/image-hash/core.js +293 -0
- package/dist/commands/image-hash/core.js.map +1 -0
- package/dist/commands/image-hash/hash.d.ts +19 -0
- package/dist/commands/image-hash/hash.js +97 -0
- package/dist/commands/image-hash/hash.js.map +1 -0
- package/dist/commands/image-hash/report.d.ts +3 -0
- package/dist/commands/image-hash/report.js +94 -0
- package/dist/commands/image-hash/report.js.map +1 -0
- package/dist/commands/image-hash/run.d.ts +60 -0
- package/dist/commands/image-hash/run.js +315 -0
- package/dist/commands/image-hash/run.js.map +1 -0
- package/dist/commands/image-hash/sharp-loader.d.ts +59 -0
- package/dist/commands/image-hash/sharp-loader.js +79 -0
- package/dist/commands/image-hash/sharp-loader.js.map +1 -0
- package/dist/commands/orphan-tests/report.js +7 -5
- package/dist/commands/orphan-tests/report.js.map +1 -1
- package/dist/commands/readme-check/report.js +7 -5
- package/dist/commands/readme-check/report.js.map +1 -1
- package/dist/commands/scripts-check/report.js +7 -5
- package/dist/commands/scripts-check/report.js.map +1 -1
- package/dist/commands/stale-ts-ignore/report.js +7 -5
- package/dist/commands/stale-ts-ignore/report.js.map +1 -1
- package/dist/commands/strip-comments/report.js +6 -4
- package/dist/commands/strip-comments/report.js.map +1 -1
- package/dist/commands/todo-report/report.js +7 -5
- package/dist/commands/todo-report/report.js.map +1 -1
- package/dist/commands/unused-deps/report.js +7 -5
- package/dist/commands/unused-deps/report.js.map +1 -1
- package/dist/format/colors.d.ts +4 -0
- package/dist/format/colors.js +4 -3
- package/dist/format/colors.js.map +1 -1
- package/dist/format/diff-render.js +2 -2
- package/dist/format/diff-render.js.map +1 -1
- package/dist/format/file-list.d.ts +1 -9
- package/dist/format/file-list.js +5 -16
- package/dist/format/file-list.js.map +1 -1
- package/dist/format/findings-list.d.ts +1 -8
- package/dist/format/findings-list.js +5 -14
- package/dist/format/findings-list.js.map +1 -1
- package/dist/format/style.d.ts +20 -0
- package/dist/format/style.js +36 -0
- package/dist/format/style.js.map +1 -0
- package/dist/format/table.d.ts +8 -0
- package/dist/format/table.js +44 -0
- package/dist/format/table.js.map +1 -0
- package/dist/format/vibes.d.ts +4 -1
- package/dist/format/vibes.js +25 -10
- package/dist/format/vibes.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/walk.d.ts +2 -0
- package/dist/utils/walk.js +4 -3
- package/dist/utils/walk.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -127,30 +127,77 @@ looking for is never touched.
|
|
|
127
127
|
`.vue` files get an isolated per-file check (same technique as
|
|
128
128
|
`readme-check`'s own virtual-file typechecking) — a plain
|
|
129
129
|
`ts.Program` can't include `.vue` in a whole-project run at all.
|
|
130
|
+
- **`image-hash`** — generates [blurhash](https://blurha.sh) and/or
|
|
131
|
+
[thumbhash](https://evanw.github.io/thumbhash/) placeholders for raster
|
|
132
|
+
images (jpg, png, webp, gif, avif, tiff). Takes files and/or
|
|
133
|
+
directories — several of each, comma-separated too — and `-r` walks
|
|
134
|
+
subdirectories. Prints to stdout by default; `-o <file>` collects
|
|
135
|
+
everything into one file, `--per-file` / `--out-dir <dir>` writes a
|
|
136
|
+
file per image (`photo.jpg` → `photo.jpg.blurhash.txt`, with
|
|
137
|
+
`--suffix` and `--out-ext` to rename). Formats: `json`, `plain` (just
|
|
138
|
+
the hash text, no newline), `csv`, and `ts`/`js` modules you can import
|
|
139
|
+
straight into the app. Besides the two hashes it can produce the
|
|
140
|
+
dominant color and a tiny PNG preview (`-t all`), accepts `http(s)`
|
|
141
|
+
URLs and path lists from a file or stdin, and can keep an output file in
|
|
142
|
+
sync: `--cache` skips unchanged images, `--update --prune` merges into
|
|
143
|
+
the existing file, `--check` fails with exit code 1 when it is stale
|
|
144
|
+
(made for CI). `--dry-run` writes nothing and shows the result as a
|
|
145
|
+
table instead. Uses the native `sharp` library, which devtoolz
|
|
146
|
+
offers to install on first use — see Requirements.
|
|
147
|
+
- **`full-check`** — runs all thirteen other commands in one sweep, each in
|
|
148
|
+
its own safe read-only mode — the three that can write to disk
|
|
149
|
+
(`strip-comments`/`console-strip`/`case-check`) are always called as a
|
|
150
|
+
preview, `-y`/`--fix` are never passed. One summary table: which
|
|
151
|
+
commands are clean, which found something, which errored. Since
|
|
152
|
+
`stale-ts-ignore` is the one command that can make a bare `full-check`
|
|
153
|
+
noticeably slower, running it with no flags in a real terminal asks
|
|
154
|
+
first — `--skip stale-ts-ignore` skips both the question and the
|
|
155
|
+
command, `--no-prompt` runs everything without asking (the default
|
|
156
|
+
answer either way).
|
|
130
157
|
|
|
131
158
|
`strip-comments`, `console-strip`, and `case-check` share the same safety
|
|
132
159
|
model: `--dry-run` (or just running with neither `--dry-run` nor `-y`)
|
|
133
160
|
only previews, `-y`/`--yes` is required to actually write anything,
|
|
134
161
|
`--diff` shows a real unified diff per file. `dead-exports`,
|
|
135
162
|
`unused-deps`, `circular-imports`, `exports-doctor`, `readme-check`,
|
|
136
|
-
`empty-catch`, `todo-report`, `scripts-check`, `orphan-tests`,
|
|
137
|
-
`stale-ts-ignore` are all read-only — none of them ever
|
|
138
|
-
there's nothing to preview or apply.
|
|
163
|
+
`empty-catch`, `todo-report`, `scripts-check`, `orphan-tests`,
|
|
164
|
+
`stale-ts-ignore`, and `full-check` are all read-only — none of them ever
|
|
165
|
+
write anything, there's nothing to preview or apply. `image-hash` writes
|
|
166
|
+
files only when you name an output (`-o`, `--per-file`, `--out-dir`) and
|
|
167
|
+
prints to stdout otherwise. Every command supports `--json` for
|
|
139
168
|
machine-readable output.
|
|
140
169
|
|
|
141
170
|
## Tone
|
|
142
171
|
|
|
143
172
|
Not dead silent, not relentlessly jokey either — a small banner and some
|
|
144
173
|
personality when everything comes back clean, nothing cute mixed into the
|
|
145
|
-
actual findings list, which stays in aligned
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
174
|
+
actual findings list, which stays in aligned columns for quick scanning.
|
|
175
|
+
|
|
176
|
+
Output is colored with one palette across every command: file paths in
|
|
177
|
+
cyan with their `:line:column` dimmed, names and kinds in yellow, the
|
|
178
|
+
"N problems found" heading in bold red, what a command would do in bold
|
|
179
|
+
yellow and what it did in bold green, hints dimmed with the flags they
|
|
180
|
+
mention (`-y`, `--dry-run`) picked out in cyan, `✔`/`✖` in the
|
|
181
|
+
`full-check` table in green/red, and the diffs red/green as before.
|
|
182
|
+
|
|
183
|
+
Color turns itself off the moment output isn't a real interactive
|
|
184
|
+
terminal — piped, `CI` set, `NO_COLOR` set, `TERM=dumb`. `--color` (or
|
|
185
|
+
`FORCE_COLOR=1`) turns it back on anyway, which is what you want for
|
|
186
|
+
`devtoolz … --color | less -R` or a CI log that renders ANSI; `--plain`
|
|
187
|
+
turns color, the banner and the celebration copy all off, and `--quiet`
|
|
188
|
+
only drops the banner and clean-run output. The text is identical with
|
|
189
|
+
and without color — only escape codes are added.
|
|
150
190
|
|
|
151
191
|
## Requirements
|
|
152
192
|
|
|
153
193
|
- Node.js 20+
|
|
194
|
+
- `image-hash` only: the [`sharp`](https://sharp.pixelplumbing.com)
|
|
195
|
+
image library. It ships prebuilt binaries for the common platforms and
|
|
196
|
+
is not installed with devtoolz — the first `image-hash` run offers to
|
|
197
|
+
install it into `~/.devtoolz/deps` (`-y` / `--yes` agrees up front, which
|
|
198
|
+
is also what you want in CI; without a terminal and without `--yes` the
|
|
199
|
+
command stops and says so). A `sharp` you already have installed
|
|
200
|
+
alongside devtoolz is used as-is.
|
|
154
201
|
|
|
155
202
|
## Installation
|
|
156
203
|
|
|
@@ -199,6 +246,17 @@ devtoolz scripts-check # package.json scripts vs README/
|
|
|
199
246
|
devtoolz orphan-tests src # test files whose source disappeared
|
|
200
247
|
|
|
201
248
|
devtoolz stale-ts-ignore # find @ts-ignore comments suppressing nothing
|
|
249
|
+
|
|
250
|
+
devtoolz image-hash public/img # blurhash + thumbhash of every image, as JSON on stdout
|
|
251
|
+
devtoolz image-hash a.jpg,b.png,photos -r -t blurhash -f csv -o hashes.csv
|
|
252
|
+
devtoolz image-hash public/img -r -f plain --per-file # photo.jpg.blurhash.txt + photo.jpg.thumbhash.txt
|
|
253
|
+
devtoolz image-hash public/img -r -f ts -t thumbhash -o src/placeholders.ts --name placeholders
|
|
254
|
+
devtoolz image-hash public/img -r --dry-run # write nothing, show a table of the hashes
|
|
255
|
+
devtoolz image-hash public/img -r -t all --components auto -o hashes.json --cache --update --prune
|
|
256
|
+
devtoolz image-hash public/img -r -o hashes.json --check # CI: exit 1 if hashes.json is stale
|
|
257
|
+
|
|
258
|
+
devtoolz full-check # run every command, one summary table
|
|
259
|
+
devtoolz full-check --skip stale-ts-ignore # same, minus the expensive one
|
|
202
260
|
```
|
|
203
261
|
|
|
204
262
|
Every command has built-in `--help` — `devtoolz --help` lists every
|
|
@@ -382,6 +440,71 @@ Checked 1 @ts-ignore directive.
|
|
|
382
440
|
src/example.ts:2 @ts-ignore doesn't suppress anything — the line below it typechecks cleanly without it
|
|
383
441
|
```
|
|
384
442
|
|
|
443
|
+
`devtoolz full-check` against a small package with a few real, unrelated
|
|
444
|
+
problems scattered across it:
|
|
445
|
+
|
|
446
|
+
```
|
|
447
|
+
✖ strip-comments 1 found
|
|
448
|
+
✔ console-strip clean
|
|
449
|
+
✖ dead-exports 1 found
|
|
450
|
+
✔ case-check clean
|
|
451
|
+
✔ exports-doctor clean
|
|
452
|
+
✔ readme-check clean
|
|
453
|
+
✔ unused-deps clean
|
|
454
|
+
✔ circular-imports clean
|
|
455
|
+
✖ empty-catch 1 found
|
|
456
|
+
✔ todo-report clean
|
|
457
|
+
✖ scripts-check 3 found
|
|
458
|
+
✔ orphan-tests clean
|
|
459
|
+
✔ stale-ts-ignore clean
|
|
460
|
+
|
|
461
|
+
9 clean, 4 found something — see `devtoolz <command> --help` for full detail on any of them.
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
`devtoolz image-hash public/img -r -f plain --per-file` writing a text file
|
|
465
|
+
per hash next to each image:
|
|
466
|
+
|
|
467
|
+
```
|
|
468
|
+
🧰 devtoolz
|
|
469
|
+
|
|
470
|
+
Hashed 2 images.
|
|
471
|
+
|
|
472
|
+
Wrote 4 files:
|
|
473
|
+
public/img/hero.jpg.blurhash.txt
|
|
474
|
+
public/img/hero.jpg.thumbhash.txt
|
|
475
|
+
public/img/logo.png.blurhash.txt
|
|
476
|
+
public/img/logo.png.thumbhash.txt
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
`devtoolz image-hash hero.jpg -t blurhash` printing JSON to stdout, ready to
|
|
480
|
+
pipe or paste:
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"hero.jpg": {
|
|
485
|
+
"width": 1600,
|
|
486
|
+
"height": 900,
|
|
487
|
+
"blurhash": "LEHV6nWB2yk8pyo0adR*.7kCMdnj"
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
`devtoolz image-hash public/img -r -t blurhash --dry-run` shows what would be
|
|
493
|
+
generated as a table and writes nothing:
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
┌─────────────────────┬─────────┬──────────────────────────────┐
|
|
497
|
+
│ File │ Size │ BlurHash │
|
|
498
|
+
├─────────────────────┼─────────┼──────────────────────────────┤
|
|
499
|
+
│ public/img/blue.png │ 120×300 │ L704c9gSfQgSf:fRfQfRfQfQfQfQ │
|
|
500
|
+
│ public/img/red.jpg │ 200×100 │ L6T9R{,YfQ,Y|cjtfQjtfQfQfQfQ │
|
|
501
|
+
└─────────────────────┴─────────┴──────────────────────────────┘
|
|
502
|
+
|
|
503
|
+
Hashed 2 images.
|
|
504
|
+
|
|
505
|
+
(dry run — nothing written)
|
|
506
|
+
```
|
|
507
|
+
|
|
385
508
|
## Development
|
|
386
509
|
|
|
387
510
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
3
4
|
import { resolve } from 'node:path';
|
|
5
|
+
import { createInterface } from 'node:readline/promises';
|
|
4
6
|
import { runStripComments } from './commands/strip-comments/run.js';
|
|
5
7
|
import { renderStripCommentsReport } from './commands/strip-comments/report.js';
|
|
6
8
|
import { runConsoleStrip } from './commands/console-strip/run.js';
|
|
@@ -28,6 +30,12 @@ import { runOrphanTests } from './commands/orphan-tests/run.js';
|
|
|
28
30
|
import { renderOrphanTestsReport } from './commands/orphan-tests/report.js';
|
|
29
31
|
import { runStaleTsIgnore } from './commands/stale-ts-ignore/run.js';
|
|
30
32
|
import { renderStaleTsIgnoreReport } from './commands/stale-ts-ignore/report.js';
|
|
33
|
+
import { runFullCheck, FULL_CHECK_COMMAND_NAMES, SLOW_FULL_CHECK_COMMAND, } from './commands/full-check/run.js';
|
|
34
|
+
import { renderFullCheckReport } from './commands/full-check/report.js';
|
|
35
|
+
import { runImageHash } from './commands/image-hash/run.js';
|
|
36
|
+
import { renderImageHashReport } from './commands/image-hash/report.js';
|
|
37
|
+
import { DEFAULT_CACHE_FILE, DEFAULT_IMAGE_EXTENSIONS, ImageHashUsageError, OUTPUT_FORMATS, parseComponents, parseExtensions, parseFileList, parseFormat, parseMaxPixels, parseSampleSize, parseTypes, } from './commands/image-hash/core.js';
|
|
38
|
+
import { createStyle } from './format/style.js';
|
|
31
39
|
function wrapText(text, width) {
|
|
32
40
|
const words = text.split(' ');
|
|
33
41
|
const lines = [];
|
|
@@ -153,6 +161,7 @@ program
|
|
|
153
161
|
.option('--json', 'machine-readable output', false)
|
|
154
162
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
155
163
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
164
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
156
165
|
.action((paths, options) => {
|
|
157
166
|
const report = runStripComments({
|
|
158
167
|
paths,
|
|
@@ -172,6 +181,7 @@ program
|
|
|
172
181
|
const text = renderStripCommentsReport(report, {
|
|
173
182
|
quiet: options.quiet,
|
|
174
183
|
plain: options.plain,
|
|
184
|
+
color: options.color,
|
|
175
185
|
dryRun: options.dryRun,
|
|
176
186
|
});
|
|
177
187
|
if (text)
|
|
@@ -195,6 +205,7 @@ program
|
|
|
195
205
|
.option('--json', 'machine-readable output', false)
|
|
196
206
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
197
207
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
208
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
198
209
|
.action((paths, options) => {
|
|
199
210
|
const report = runConsoleStrip({
|
|
200
211
|
paths,
|
|
@@ -215,6 +226,7 @@ program
|
|
|
215
226
|
const text = renderConsoleStripReport(report, {
|
|
216
227
|
quiet: options.quiet,
|
|
217
228
|
plain: options.plain,
|
|
229
|
+
color: options.color,
|
|
218
230
|
});
|
|
219
231
|
if (text)
|
|
220
232
|
console.log(text);
|
|
@@ -235,6 +247,7 @@ program
|
|
|
235
247
|
.option('--json', 'machine-readable output', false)
|
|
236
248
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
237
249
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
250
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
238
251
|
.action((paths, options) => {
|
|
239
252
|
const report = runDeadExports({
|
|
240
253
|
paths,
|
|
@@ -250,7 +263,11 @@ program
|
|
|
250
263
|
console.log(JSON.stringify(report, null, 2));
|
|
251
264
|
}
|
|
252
265
|
else {
|
|
253
|
-
const text = renderDeadExportsReport(report, {
|
|
266
|
+
const text = renderDeadExportsReport(report, {
|
|
267
|
+
quiet: options.quiet,
|
|
268
|
+
plain: options.plain,
|
|
269
|
+
color: options.color,
|
|
270
|
+
});
|
|
254
271
|
if (text)
|
|
255
272
|
console.log(text);
|
|
256
273
|
}
|
|
@@ -268,6 +285,7 @@ program
|
|
|
268
285
|
.option('--json', 'machine-readable output', false)
|
|
269
286
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
270
287
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
288
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
271
289
|
.action((paths, options) => {
|
|
272
290
|
const report = runCircularImports({
|
|
273
291
|
paths,
|
|
@@ -284,6 +302,7 @@ program
|
|
|
284
302
|
const text = renderCircularImportsReport(report, {
|
|
285
303
|
quiet: options.quiet,
|
|
286
304
|
plain: options.plain,
|
|
305
|
+
color: options.color,
|
|
287
306
|
});
|
|
288
307
|
if (text)
|
|
289
308
|
console.log(text);
|
|
@@ -302,6 +321,7 @@ program
|
|
|
302
321
|
.option('--json', 'machine-readable output', false)
|
|
303
322
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
304
323
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
324
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
305
325
|
.action((dir, options) => {
|
|
306
326
|
const report = runUnusedDeps({
|
|
307
327
|
dir: resolve(dir),
|
|
@@ -315,7 +335,11 @@ program
|
|
|
315
335
|
console.log(JSON.stringify(report, null, 2));
|
|
316
336
|
}
|
|
317
337
|
else {
|
|
318
|
-
const text = renderUnusedDepsReport(report, {
|
|
338
|
+
const text = renderUnusedDepsReport(report, {
|
|
339
|
+
quiet: options.quiet,
|
|
340
|
+
plain: options.plain,
|
|
341
|
+
color: options.color,
|
|
342
|
+
});
|
|
319
343
|
if (text)
|
|
320
344
|
console.log(text);
|
|
321
345
|
}
|
|
@@ -337,6 +361,7 @@ program
|
|
|
337
361
|
.option('--json', 'machine-readable output', false)
|
|
338
362
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
339
363
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
364
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
340
365
|
.action((paths, options) => {
|
|
341
366
|
const report = runCaseCheck({
|
|
342
367
|
paths,
|
|
@@ -354,7 +379,11 @@ program
|
|
|
354
379
|
console.log(JSON.stringify(report, null, 2));
|
|
355
380
|
}
|
|
356
381
|
else {
|
|
357
|
-
const text = renderCaseCheckReport(report, {
|
|
382
|
+
const text = renderCaseCheckReport(report, {
|
|
383
|
+
quiet: options.quiet,
|
|
384
|
+
plain: options.plain,
|
|
385
|
+
color: options.color,
|
|
386
|
+
});
|
|
358
387
|
if (text)
|
|
359
388
|
console.log(text);
|
|
360
389
|
}
|
|
@@ -367,13 +396,18 @@ program
|
|
|
367
396
|
.option('--json', 'machine-readable output', false)
|
|
368
397
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
369
398
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
399
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
370
400
|
.action((dir, options) => {
|
|
371
401
|
const report = runExportsDoctor({ dir: resolve(dir) });
|
|
372
402
|
if (options.json) {
|
|
373
403
|
console.log(JSON.stringify(report, null, 2));
|
|
374
404
|
}
|
|
375
405
|
else {
|
|
376
|
-
const text = renderExportsDoctorReport(report, {
|
|
406
|
+
const text = renderExportsDoctorReport(report, {
|
|
407
|
+
quiet: options.quiet,
|
|
408
|
+
plain: options.plain,
|
|
409
|
+
color: options.color,
|
|
410
|
+
});
|
|
377
411
|
if (text)
|
|
378
412
|
console.log(text);
|
|
379
413
|
}
|
|
@@ -389,6 +423,7 @@ program
|
|
|
389
423
|
.option('--json', 'machine-readable output', false)
|
|
390
424
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
391
425
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
426
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
392
427
|
.action((dir, options) => {
|
|
393
428
|
const report = runReadmeCheck({
|
|
394
429
|
dir: resolve(dir),
|
|
@@ -400,7 +435,11 @@ program
|
|
|
400
435
|
console.log(JSON.stringify(report, null, 2));
|
|
401
436
|
}
|
|
402
437
|
else {
|
|
403
|
-
const text = renderReadmeCheckReport(report, {
|
|
438
|
+
const text = renderReadmeCheckReport(report, {
|
|
439
|
+
quiet: options.quiet,
|
|
440
|
+
plain: options.plain,
|
|
441
|
+
color: options.color,
|
|
442
|
+
});
|
|
404
443
|
if (text)
|
|
405
444
|
console.log(text);
|
|
406
445
|
}
|
|
@@ -417,6 +456,7 @@ program
|
|
|
417
456
|
.option('--json', 'machine-readable output', false)
|
|
418
457
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
419
458
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
459
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
420
460
|
.action((paths, options) => {
|
|
421
461
|
const report = runEmptyCatch({
|
|
422
462
|
paths,
|
|
@@ -429,7 +469,11 @@ program
|
|
|
429
469
|
console.log(JSON.stringify(report, null, 2));
|
|
430
470
|
}
|
|
431
471
|
else {
|
|
432
|
-
const text = renderEmptyCatchReport(report, {
|
|
472
|
+
const text = renderEmptyCatchReport(report, {
|
|
473
|
+
quiet: options.quiet,
|
|
474
|
+
plain: options.plain,
|
|
475
|
+
color: options.color,
|
|
476
|
+
});
|
|
433
477
|
if (text)
|
|
434
478
|
console.log(text);
|
|
435
479
|
}
|
|
@@ -448,6 +492,7 @@ program
|
|
|
448
492
|
.option('--json', 'machine-readable output', false)
|
|
449
493
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
450
494
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
495
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
451
496
|
.action((paths, options) => {
|
|
452
497
|
const report = runTodoReport({
|
|
453
498
|
paths,
|
|
@@ -462,7 +507,11 @@ program
|
|
|
462
507
|
console.log(JSON.stringify(report, null, 2));
|
|
463
508
|
}
|
|
464
509
|
else {
|
|
465
|
-
const text = renderTodoReportReport(report, {
|
|
510
|
+
const text = renderTodoReportReport(report, {
|
|
511
|
+
quiet: options.quiet,
|
|
512
|
+
plain: options.plain,
|
|
513
|
+
color: options.color,
|
|
514
|
+
});
|
|
466
515
|
if (text)
|
|
467
516
|
console.log(text);
|
|
468
517
|
}
|
|
@@ -476,6 +525,7 @@ program
|
|
|
476
525
|
.option('--json', 'machine-readable output', false)
|
|
477
526
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
478
527
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
528
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
479
529
|
.action((dir, options) => {
|
|
480
530
|
const report = runScriptsCheck({ dir: resolve(dir), files: options.file });
|
|
481
531
|
if (options.json) {
|
|
@@ -485,6 +535,7 @@ program
|
|
|
485
535
|
const text = renderScriptsCheckReport(report, {
|
|
486
536
|
quiet: options.quiet,
|
|
487
537
|
plain: options.plain,
|
|
538
|
+
color: options.color,
|
|
488
539
|
});
|
|
489
540
|
if (text)
|
|
490
541
|
console.log(text);
|
|
@@ -506,6 +557,7 @@ program
|
|
|
506
557
|
.option('--json', 'machine-readable output', false)
|
|
507
558
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
508
559
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
560
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
509
561
|
.action((paths, options) => {
|
|
510
562
|
const report = runOrphanTests({
|
|
511
563
|
paths,
|
|
@@ -522,7 +574,11 @@ program
|
|
|
522
574
|
console.log(JSON.stringify(report, null, 2));
|
|
523
575
|
}
|
|
524
576
|
else {
|
|
525
|
-
const text = renderOrphanTestsReport(report, {
|
|
577
|
+
const text = renderOrphanTestsReport(report, {
|
|
578
|
+
quiet: options.quiet,
|
|
579
|
+
plain: options.plain,
|
|
580
|
+
color: options.color,
|
|
581
|
+
});
|
|
526
582
|
if (text)
|
|
527
583
|
console.log(text);
|
|
528
584
|
}
|
|
@@ -538,6 +594,7 @@ program
|
|
|
538
594
|
.option('--json', 'machine-readable output', false)
|
|
539
595
|
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
540
596
|
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
597
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
541
598
|
.action((dir, options) => {
|
|
542
599
|
const report = runStaleTsIgnore({
|
|
543
600
|
dir: resolve(dir),
|
|
@@ -553,6 +610,206 @@ program
|
|
|
553
610
|
const text = renderStaleTsIgnoreReport(report, {
|
|
554
611
|
quiet: options.quiet,
|
|
555
612
|
plain: options.plain,
|
|
613
|
+
color: options.color,
|
|
614
|
+
});
|
|
615
|
+
if (text)
|
|
616
|
+
console.log(text);
|
|
617
|
+
}
|
|
618
|
+
process.exitCode = report.exitCode;
|
|
619
|
+
});
|
|
620
|
+
program
|
|
621
|
+
.command('image-hash')
|
|
622
|
+
.description('Generate blurhash/thumbhash placeholders (and dominant color, tiny preview) for raster images — to stdout, one file, or a file per image')
|
|
623
|
+
.argument('[paths...]', 'image files, directories and http(s) URLs (several allowed, comma-separated too)', [])
|
|
624
|
+
.option('--cwd <path>', 'root paths are resolved against', process.cwd())
|
|
625
|
+
.option('-r, --recursive', 'also walk subdirectories of every given directory', false)
|
|
626
|
+
.option('--files-from <file>', 'read more paths/URLs from a file, one per line (- = stdin, # starts a comment)')
|
|
627
|
+
.option('--ext <list>', 'comma-separated image extensions to pick up from directories', DEFAULT_IMAGE_EXTENSIONS.join(','))
|
|
628
|
+
.option('--ignore <glob>', 'extra ignore pattern (repeatable), on top of the built-in defaults', (val, prev) => [...prev, val], [])
|
|
629
|
+
.option('--no-respect-gitignore', "don't also honor the project's .gitignore")
|
|
630
|
+
.option('-t, --type <list>', 'what to generate, comma-separated: blurhash, thumbhash, color (dominant), preview (tiny PNG data URI); both = blurhash+thumbhash, all = everything', 'both')
|
|
631
|
+
.option('--components <XxY|auto>', 'blurhash components, each side 1-9, or auto to pick them by aspect ratio', '4x3')
|
|
632
|
+
.option('--size <px>', 'longest side the image is scaled down to before hashing (1-100)', '100')
|
|
633
|
+
.option('--max-pixels <n>', 'refuse images with more pixels than this (0 = no limit; default: sharp limit, ~268 million)')
|
|
634
|
+
.option('-f, --format <format>', `format of the generated hashes: ${OUTPUT_FORMATS.join(', ')} (plain = just the hash text); --json prints the full report instead`, 'json')
|
|
635
|
+
.option('--name <identifier>', 'exported constant name for --format ts/js', 'imageHashes')
|
|
636
|
+
.option('--key-base <dir>', 'make the file keys in the output relative to this directory')
|
|
637
|
+
.option('--key-prefix <text>', 'text put in front of every file key, e.g. / for URL-style keys')
|
|
638
|
+
.option('-o, --out <file>', 'write everything into one file instead of stdout')
|
|
639
|
+
.option('--update', 'merge into the existing -o file instead of replacing it', false)
|
|
640
|
+
.option('--prune', 'with --update, drop entries whose image no longer exists', false)
|
|
641
|
+
.option('--per-file', 'write one file per image, next to the image', false)
|
|
642
|
+
.option('--out-dir <dir>', 'write the per-image files into this directory (implies --per-file)')
|
|
643
|
+
.option('--suffix <text>', 'per-image file name suffix after the image file name, {type} = blurhash/thumbhash/hash (default: .{type})')
|
|
644
|
+
.option('--out-ext <ext>', 'per-image file extension (default: by --format, plain = .txt)')
|
|
645
|
+
.option('--check', 'write nothing; exit 1 if the output files are missing or differ from the images', false)
|
|
646
|
+
.option('--cache [file]', `skip images that did not change since the last run, remembered in this file (default: ${DEFAULT_CACHE_FILE})`)
|
|
647
|
+
.option('--concurrency <n>', 'images processed in parallel', '4')
|
|
648
|
+
.option('--dry-run', 'write nothing — show the result as a table instead', false)
|
|
649
|
+
.option('-y, --yes', 'install the missing "sharp" image library without asking', false)
|
|
650
|
+
.option('--json', 'machine-readable output', false)
|
|
651
|
+
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
652
|
+
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
653
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
654
|
+
.action(async (paths, options) => {
|
|
655
|
+
const style = createStyle({ plain: options.plain, color: options.color });
|
|
656
|
+
const showProgress = !options.json && !options.quiet && Boolean(process.stderr.isTTY) && !options.plain;
|
|
657
|
+
let progressShown = false;
|
|
658
|
+
try {
|
|
659
|
+
const listed = options.filesFrom !== undefined
|
|
660
|
+
? parseFileList(readFileSync(options.filesFrom === '-' ? 0 : options.filesFrom, 'utf8'))
|
|
661
|
+
: [];
|
|
662
|
+
const report = await runImageHash({
|
|
663
|
+
paths: [...paths, ...listed],
|
|
664
|
+
cwd: resolve(options.cwd),
|
|
665
|
+
types: parseTypes(options.type),
|
|
666
|
+
recursive: options.recursive,
|
|
667
|
+
extensions: parseExtensions(options.ext),
|
|
668
|
+
ignoreGlobs: options.ignore,
|
|
669
|
+
respectGitignore: options.respectGitignore,
|
|
670
|
+
components: parseComponents(options.components),
|
|
671
|
+
size: parseSampleSize(options.size),
|
|
672
|
+
...(options.maxPixels !== undefined
|
|
673
|
+
? { maxPixels: parseMaxPixels(options.maxPixels) }
|
|
674
|
+
: {}),
|
|
675
|
+
format: parseFormat(options.format),
|
|
676
|
+
exportName: options.name,
|
|
677
|
+
...(options.keyBase !== undefined ? { keyBase: options.keyBase } : {}),
|
|
678
|
+
...(options.keyPrefix !== undefined ? { keyPrefix: options.keyPrefix } : {}),
|
|
679
|
+
...(options.out !== undefined ? { out: options.out } : {}),
|
|
680
|
+
update: options.update,
|
|
681
|
+
prune: options.prune,
|
|
682
|
+
perFile: options.perFile,
|
|
683
|
+
...(options.outDir !== undefined ? { outDir: options.outDir } : {}),
|
|
684
|
+
...(options.suffix !== undefined ? { suffix: options.suffix } : {}),
|
|
685
|
+
...(options.outExt !== undefined ? { outExtension: options.outExt } : {}),
|
|
686
|
+
check: options.check,
|
|
687
|
+
...(options.cache !== undefined
|
|
688
|
+
? { cache: options.cache === true ? DEFAULT_CACHE_FILE : String(options.cache) }
|
|
689
|
+
: {}),
|
|
690
|
+
concurrency: Math.max(1, Number.parseInt(options.concurrency, 10) || 4),
|
|
691
|
+
assumeYes: options.yes,
|
|
692
|
+
dryRun: options.dryRun,
|
|
693
|
+
...(showProgress
|
|
694
|
+
? {
|
|
695
|
+
onProgress: ({ done, total }) => {
|
|
696
|
+
if (total < 2)
|
|
697
|
+
return;
|
|
698
|
+
progressShown = true;
|
|
699
|
+
process.stderr.write(`\r${style.info(`Hashing ${done}/${total}`)}`);
|
|
700
|
+
},
|
|
701
|
+
}
|
|
702
|
+
: {}),
|
|
703
|
+
});
|
|
704
|
+
if (progressShown)
|
|
705
|
+
process.stderr.write('\r\x1b[K');
|
|
706
|
+
if (options.json) {
|
|
707
|
+
console.log(JSON.stringify(report, null, 2));
|
|
708
|
+
}
|
|
709
|
+
else if (report.stdout !== null) {
|
|
710
|
+
process.stdout.write(report.stdout);
|
|
711
|
+
for (const error of report.errors)
|
|
712
|
+
console.error(`${error.file} — ${error.message}`);
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
const text = renderImageHashReport(report, {
|
|
716
|
+
quiet: options.quiet,
|
|
717
|
+
plain: options.plain,
|
|
718
|
+
color: options.color,
|
|
719
|
+
});
|
|
720
|
+
if (text)
|
|
721
|
+
console.log(text);
|
|
722
|
+
}
|
|
723
|
+
process.exitCode = report.exitCode;
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
if (progressShown)
|
|
727
|
+
process.stderr.write('\r\x1b[K');
|
|
728
|
+
if (error instanceof ImageHashUsageError) {
|
|
729
|
+
console.error(`error: ${error.message}`);
|
|
730
|
+
process.exitCode = 2;
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
throw error;
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
// Only `full-check` ever prompts — every other command here is a single,
|
|
737
|
+
// fast, non-interactive check. `stale-ts-ignore` is the one command a
|
|
738
|
+
// bare `full-check` can turn into a genuinely slow run, so this asks
|
|
739
|
+
// about that ONE command specifically, not a general "run everything?"
|
|
740
|
+
// confirmation. Defaults to running it (Enter alone picks option 1) —
|
|
741
|
+
// matches what happens when there's no TTY to ask at all (see
|
|
742
|
+
// `shouldPrompt` below), so a script and an interactive "just press
|
|
743
|
+
// enter" run behave the same way.
|
|
744
|
+
// Deliberately a SINGLE `question()` call, no retry-on-invalid-input loop
|
|
745
|
+
// — found via real testing that a second `question()` on the same (or
|
|
746
|
+
// even a fresh) `readline/promises` interface can silently lose the
|
|
747
|
+
// answer and hang forever once more than one line was already available
|
|
748
|
+
// on stdin when the first question resolved (a real, reproducible
|
|
749
|
+
// readline quirk with piped/non-interactive input, not something specific
|
|
750
|
+
// to this code). One question, with an explicit default on anything else
|
|
751
|
+
// typed, sidesteps the whole bug class instead of risking it.
|
|
752
|
+
async function promptStaleTsIgnoreChoice() {
|
|
753
|
+
process.stderr.write('stale-ts-ignore runs a full project typecheck twice — this can take noticeably longer than the other commands on a large project.\n\n' +
|
|
754
|
+
' 1) Run it anyway (full sweep)\n' +
|
|
755
|
+
' 2) Skip it for this run\n\n');
|
|
756
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
757
|
+
let answer;
|
|
758
|
+
try {
|
|
759
|
+
answer = (await rl.question('Choice [1/2, Enter = 1]: ')).trim();
|
|
760
|
+
}
|
|
761
|
+
finally {
|
|
762
|
+
rl.close();
|
|
763
|
+
}
|
|
764
|
+
if (answer === '2')
|
|
765
|
+
return false;
|
|
766
|
+
if (answer !== '' && answer !== '1') {
|
|
767
|
+
process.stderr.write(`(didn't recognize "${answer}" — running it, same as Enter)\n`);
|
|
768
|
+
}
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
program
|
|
772
|
+
.command('full-check')
|
|
773
|
+
.description('Runs every other command in its own safe/read-only mode — a full diagnostic sweep, never applies a fix')
|
|
774
|
+
.argument('[dir]', 'directory every command scans/reads from', '.')
|
|
775
|
+
.option('--skip <command>', `skip a command by name (repeatable) — valid names: ${FULL_CHECK_COMMAND_NAMES.join(', ')}`, (val, prev) => [...prev, val], [])
|
|
776
|
+
.option('--no-prompt', "don't ask about stale-ts-ignore even in an interactive terminal — runs it unless --skip already excludes it")
|
|
777
|
+
.option('--json', 'machine-readable output', false)
|
|
778
|
+
.option('--quiet', 'suppress output when there is nothing to report', false)
|
|
779
|
+
.option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
|
|
780
|
+
.option('--color', 'force colored output even when piped or in CI', false)
|
|
781
|
+
.action(async (dir, options) => {
|
|
782
|
+
const skip = new Set(options.skip);
|
|
783
|
+
const shouldPrompt = options.prompt &&
|
|
784
|
+
!options.json &&
|
|
785
|
+
!skip.has(SLOW_FULL_CHECK_COMMAND) &&
|
|
786
|
+
Boolean(process.stdin.isTTY) &&
|
|
787
|
+
Boolean(process.stdout.isTTY);
|
|
788
|
+
if (shouldPrompt) {
|
|
789
|
+
const runIt = await promptStaleTsIgnoreChoice();
|
|
790
|
+
if (!runIt)
|
|
791
|
+
skip.add(SLOW_FULL_CHECK_COMMAND);
|
|
792
|
+
process.stderr.write('\n');
|
|
793
|
+
}
|
|
794
|
+
const showProgress = !options.json && !options.quiet;
|
|
795
|
+
const report = runFullCheck({
|
|
796
|
+
dir: resolve(dir),
|
|
797
|
+
skip: [...skip],
|
|
798
|
+
...(showProgress
|
|
799
|
+
? {
|
|
800
|
+
onCommandStart: (command) => process.stderr.write(`Running ${command}...\n`),
|
|
801
|
+
onProgress: (message) => process.stderr.write(`${message}\n`),
|
|
802
|
+
}
|
|
803
|
+
: {}),
|
|
804
|
+
});
|
|
805
|
+
if (options.json) {
|
|
806
|
+
console.log(JSON.stringify(report, null, 2));
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
const text = renderFullCheckReport(report, {
|
|
810
|
+
quiet: options.quiet,
|
|
811
|
+
plain: options.plain,
|
|
812
|
+
color: options.color,
|
|
556
813
|
});
|
|
557
814
|
if (text)
|
|
558
815
|
console.log(text);
|