@vanhanit/pre-compress 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/args.js CHANGED
@@ -12,13 +12,18 @@ const OPTIONS = {
12
12
  version: { type: "boolean", short: "V" },
13
13
  help: { type: "boolean", short: "h" }
14
14
  };
15
- const HELP = [
16
- ["-f, --files <files...>", "the files to compress, supports glob patterns to match multiple files"],
17
- ["-d, --delete-larger", "delete compressed files that are not smaller than the source"],
18
- ["-s, --skip-existing", "don't compress if the compressed version is newer than the source"],
19
- ["-V, --version", "output the version number"],
20
- ["-h, --help", "display help for command"]
21
- ];
15
+ const DESCRIPTIONS = {
16
+ files: "the files to compress, supports glob patterns to match multiple files",
17
+ "delete-larger": "delete compressed files that are not smaller than the source",
18
+ "skip-existing": "don't compress if the compressed version is newer than the source",
19
+ version: "output the version number",
20
+ help: "display help for command"
21
+ };
22
+ function flags(name) {
23
+ const option = OPTIONS[name];
24
+ const value = option.type === "string" ? ` <${name}...>` : "";
25
+ return `-${option.short}, --${name}${value}`;
26
+ }
22
27
  function parse(args) {
23
28
  try {
24
29
  return parseArgs({ args: [...args], options: OPTIONS, allowPositionals: true });
@@ -40,20 +45,23 @@ export function parseArguments(args) {
40
45
  }
41
46
  const patterns = [...(values.files ?? []), ...positionals];
42
47
  if (patterns.length === 0) {
43
- throw new ArgumentError("required option '-f, --files <files...>' not specified");
48
+ throw new ArgumentError(`required option '${flags("files")}' not specified`);
44
49
  }
45
50
  return {
46
51
  name: "compress",
47
52
  patterns,
48
- deleteLarger: values["delete-larger"] ?? false,
49
- skipExisting: values["skip-existing"] ?? false
53
+ options: {
54
+ deleteLarger: values["delete-larger"] ?? false,
55
+ skipExisting: values["skip-existing"] ?? false
56
+ }
50
57
  };
51
58
  }
52
59
  /**
53
60
  * Returns the help text.
54
61
  */
55
62
  export function usage(name, description) {
56
- const width = Math.max(...HELP.map(([flags]) => flags.length));
57
- const options = HELP.map(([flags, text]) => ` ${flags.padEnd(width)} ${text}`);
63
+ const names = Object.keys(OPTIONS);
64
+ const width = Math.max(...names.map((option) => flags(option).length));
65
+ const options = names.map((option) => ` ${flags(option).padEnd(width)} ${DESCRIPTIONS[option]}`);
58
66
  return [`Usage: ${name} [options]`, "", description, "", "Options:", ...options].join("\n");
59
67
  }
package/dist/cli.js CHANGED
@@ -5,41 +5,29 @@ import { ArgumentError, parseArguments, usage } from "./args.js";
5
5
  import { compressFile, isCompressed } from "./compress.js";
6
6
  import { mapConcurrently } from "./concurrency.js";
7
7
  import { findFiles } from "./files.js";
8
- import { formatDuration, pluralize } from "./format.js";
9
8
  import { readPackageInfo } from "./package-info.js";
10
- function report({ file, size, outputs }, io) {
11
- for (const output of outputs) {
12
- if (output.status === "written") {
13
- io.log(`Compressed file ${styleText("yellow", file)} to ${styleText("green", output.file)}`);
14
- }
15
- else if (output.status === "skipped") {
16
- io.log(`Skipped file ${styleText("yellow", output.file)} because it is up to date`);
17
- }
18
- else {
19
- io.log(`Deleted file ${styleText("red", output.file)} because ${output.size} byte(s) is not smaller than ${size} byte(s)`);
20
- }
21
- }
22
- }
23
- async function compress(command, io) {
9
+ import { describeResult, summarize } from "./report.js";
10
+ const consoleIo = {
11
+ log: (message) => console.log(message),
12
+ error: (message) => console.error(message),
13
+ style: (color, text) => styleText(color, text)
14
+ };
15
+ async function compress({ patterns, options }, io) {
24
16
  const startTime = performance.now();
25
- const files = (await findFiles(command.patterns)).filter((file) => !isCompressed(file));
17
+ const files = (await findFiles(patterns)).filter((file) => !isCompressed(file));
26
18
  const results = await mapConcurrently(files, availableParallelism(), async (file) => {
27
- const result = await compressFile(file, command);
28
- report(result, io);
19
+ const result = await compressFile(file, options);
20
+ for (const line of describeResult(result, io.style)) {
21
+ io.log(line);
22
+ }
29
23
  return result;
30
24
  });
31
- const timeTakenMs = performance.now() - startTime;
32
- const outputs = results.flatMap((result) => result.outputs);
33
- const count = (status) => outputs.filter((output) => output.status === status).length;
34
- io.log(`Processed ${pluralize(files.length, "file")} in ${styleText("green", formatDuration(timeTakenMs))}: ` +
35
- `${styleText("green", String(count("written")))} written, ` +
36
- `${styleText("yellowBright", String(count("skipped")))} skipped, ` +
37
- `${styleText("red", String(count("deleted")))} deleted`);
25
+ io.log(summarize(results, performance.now() - startTime, io.style));
38
26
  }
39
27
  /**
40
28
  * Runs the command line interface with the arguments, without the node and script paths, and returns the exit code.
41
29
  */
42
- export async function run(args, io = console) {
30
+ export async function run(args, io = consoleIo) {
43
31
  const { command: name, version, description } = readPackageInfo();
44
32
  try {
45
33
  const command = parseArguments(args);
package/dist/main.js CHANGED
@@ -1,3 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { run } from "./cli.js";
2
+ import { configureThreadPool } from "./thread-pool.js";
3
+ // The CLI is loaded afterwards, so nothing can use the thread pool before it is sized.
4
+ configureThreadPool();
5
+ const { run } = await import("./cli.js");
3
6
  process.exitCode = await run(process.argv.slice(2));
package/dist/report.js ADDED
@@ -0,0 +1,30 @@
1
+ import { formatDuration, pluralize } from "./format.js";
2
+ function describeOutput(file, size, output, style) {
3
+ switch (output.status) {
4
+ case "written":
5
+ return `Compressed file ${style("yellow", file)} to ${style("green", output.file)}`;
6
+ case "skipped":
7
+ return `Skipped file ${style("yellow", output.file)} because it is up to date`;
8
+ case "deleted":
9
+ return `Deleted file ${style("red", output.file)} because ${output.size} byte(s) is not smaller than ${size} byte(s)`;
10
+ }
11
+ }
12
+ /**
13
+ * Returns a line for every compressed file written, skipped or deleted for a source file.
14
+ */
15
+ export function describeResult({ file, size, outputs }, style) {
16
+ return outputs.map((output) => describeOutput(file, size, output, style));
17
+ }
18
+ /**
19
+ * Returns a line counting what was done to the compressed files of all processed source files.
20
+ */
21
+ export function summarize(results, milliseconds, style) {
22
+ const counts = { written: 0, skipped: 0, deleted: 0 };
23
+ for (const output of results.flatMap((result) => result.outputs)) {
24
+ counts[output.status]++;
25
+ }
26
+ return (`Processed ${pluralize(results.length, "file")} in ${style("green", formatDuration(milliseconds))}: ` +
27
+ `${style("green", String(counts.written))} written, ` +
28
+ `${style("yellowBright", String(counts.skipped))} skipped, ` +
29
+ `${style("red", String(counts.deleted))} deleted`);
30
+ }
@@ -0,0 +1,9 @@
1
+ import { availableParallelism } from "node:os";
2
+ const DEFAULT_SIZE = 4;
3
+ /**
4
+ * Sizes libuv's thread pool, which runs the compression, to the available cores. Its default of 4 threads leaves the
5
+ * other cores idle. Takes effect only before the pool is first used, and keeps a size that is already set.
6
+ */
7
+ export function configureThreadPool(env = process.env, cores = availableParallelism()) {
8
+ env.UV_THREADPOOL_SIZE ??= String(Math.max(DEFAULT_SIZE, cores));
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vanhanit/pre-compress",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Compress static files to be served by a web server.",
5
5
  "author": "Thomas Vanhaniemi",
6
6
  "type": "module",