File size: 3,080 Bytes
780c9fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env node

import path from "node:path";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";

import { runChecker } from "./checker.js";
import { MAX_COMPRESSION_DIFFERENCE_PERCENTAGE } from "./constants.js";
import { CONTENT_ROOT, CONTENT_TRANSLATED_ROOT } from "./env.js";

/**
 * @typedef {Object} FilecheckOptions
 * @property {string} [cwd] Explicit current-working-directory
 * @property {number} [maxCompressionDifferencePercentage] Max percentage for reduction after compression
 * @property {boolean} [saveCompression] If it can be compressed, save the result
 */

/**
 * @typedef {Object} FilecheckArgs
 * @property {string[]} [files] List of files and/or directories to check
 */

const defaultFiles = [CONTENT_ROOT, CONTENT_TRANSLATED_ROOT].filter(Boolean);

/**
 * Resolve file paths against the provided cwd.
 * @param {string[]} files
 * @param {string} cwd
 * @returns {string[]}
 */
function resolveFiles(files, cwd) {
  return (files || []).map((f) => path.resolve(cwd || process.cwd(), f));
}

const argv = yargs(hideBin(process.argv))
  .scriptName("filecheck")
  .version("0.0.0")
  .command("$0 [files...]", "Check image files")
  .option("cwd", {
    type: "string",
    describe: "Explicit current-working-directory",
    default: process.cwd(),
  })
  .option("max-compression-difference-percentage", {
    type: "number",
    describe: "Max percentage for reduction after compression",
    default: MAX_COMPRESSION_DIFFERENCE_PERCENTAGE,
  })
  .option("save-compression", {
    type: "boolean",
    describe: "If it can be compressed, save the result",
    default: false,
  })
  .positional("files", {
    type: "string",
    array: true,
    describe: "List of files and/or directories to check",
    default: defaultFiles,
  })
  .check((args) => {
    if (
      args.maxCompressionDifferencePercentage != null &&
      Number.isNaN(Number(args.maxCompressionDifferencePercentage))
    ) {
      throw new Error(
        "--max-compression-difference-percentage must be a number",
      );
    }
    return true;
  })
  .help()
  .strict()
  .parse();

/** @type {FilecheckArgs & FilecheckOptions} */
const optionsAndArgs = /** @type {any} */ (argv);

const cwd = optionsAndArgs.cwd || process.cwd();
/** @type {string[]} */
const files = resolveFiles(optionsAndArgs.files || [], cwd);

if (!files.length) {
  console.info("No files to check.");
  process.exit(0);
}

// yargs converts dashed flags to camelCase by default.
// Pull out the options we pass through to runChecker.
const { maxCompressionDifferencePercentage, saveCompression } = optionsAndArgs;

/** @type {FilecheckOptions} */
const checkerOptions = {
  cwd,
  maxCompressionDifferencePercentage,
  saveCompression,
};

const maybePromise = runChecker(files, checkerOptions);

// If runChecker returns a promise, let unhandled rejections surface clearly.
if (maybePromise && typeof maybePromise.then === "function") {
  maybePromise.catch((err) => {
    console.error(err instanceof Error ? err.stack || err.message : err);
    process.exitCode = 1;
  });
}