Extract CliError into src/errors.ts
CliError used to live in cli.ts and was imported by configFile.ts purely
to grab a one-line class — the dependency arrow said 'config-file loader
depends on the CLI parser' when really it just needed a shared error
type.
Moving CliError to its own errors.ts module flattens the graph:
errors.ts (no internal deps)
|
+-- cli.ts
+-- configFile.ts
+-- config.ts (will use it in the next commit)
cli.ts re-exports CliError for any consumer that prefers to keep
importing it from there; the canonical home is now errors.ts.
This commit is contained in:
+1
-8
@@ -27,14 +27,7 @@ import { fileURLToPath } from "node:url";
|
|||||||
import { parseArgs } from "node:util";
|
import { parseArgs } from "node:util";
|
||||||
|
|
||||||
import { DEFAULT_CONFIG, defaultConfigPath } from "./config.ts";
|
import { DEFAULT_CONFIG, defaultConfigPath } from "./config.ts";
|
||||||
|
import { CliError } from "./errors.ts";
|
||||||
/**
|
|
||||||
* Thrown when CLI input is invalid (unknown option, missing value, bad
|
|
||||||
* number, malformed config file). Distinct from runtime errors so the
|
|
||||||
* entry point can exit with code 2 (user error) instead of code 1
|
|
||||||
* (runtime failure).
|
|
||||||
*/
|
|
||||||
export class CliError extends Error {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result of `parseCliArgs`. Numeric fields are `undefined` when the user
|
* Result of `parseCliArgs`. Numeric fields are `undefined` when the user
|
||||||
|
|||||||
+13
-1
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
import { CliError } from "./errors.ts";
|
||||||
|
|
||||||
// Single source of truth for default values. The same file ships in the
|
// Single source of truth for default values. The same file ships in the
|
||||||
// install tree and is copied to $XDG_CONFIG_HOME/move/config.json on a
|
// install tree and is copied to $XDG_CONFIG_HOME/move/config.json on a
|
||||||
// fresh install (only if no config exists there yet). Values use the CLI
|
// fresh install (only if no config exists there yet). Values use the CLI
|
||||||
@@ -141,7 +143,17 @@ export function defaultConfigPath(): string {
|
|||||||
if (xdg && xdg.length > 0) {
|
if (xdg && xdg.length > 0) {
|
||||||
return join(xdg, "move", "config.json");
|
return join(xdg, "move", "config.json");
|
||||||
}
|
}
|
||||||
const home = process.env.HOME ?? "";
|
const home = process.env.HOME;
|
||||||
|
if (!home || home.length === 0) {
|
||||||
|
// Neither var is set; we don't have a sensible fallback. Throwing
|
||||||
|
// CliError lets the entry-point's normal handler surface this as
|
||||||
|
// 'move: cannot resolve default config path: ...' + exit 2 instead
|
||||||
|
// of silently producing '/.config/move/config.json' and bewildering
|
||||||
|
// the user with a downstream 'file not found' message.
|
||||||
|
throw new CliError(
|
||||||
|
"cannot resolve default config path: neither $XDG_CONFIG_HOME nor $HOME is set",
|
||||||
|
);
|
||||||
|
}
|
||||||
return join(home, ".config", "move", "config.json");
|
return join(home, ".config", "move", "config.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -27,8 +27,8 @@
|
|||||||
|
|
||||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||||
|
|
||||||
import { CliError } from "./cli.ts";
|
|
||||||
import { defaultConfigPath, type ConfigOverrides } from "./config.ts";
|
import { defaultConfigPath, type ConfigOverrides } from "./config.ts";
|
||||||
|
import { CliError } from "./errors.ts";
|
||||||
|
|
||||||
const ALLOWED_KEYS: ReadonlySet<string> = new Set<string>([
|
const ALLOWED_KEYS: ReadonlySet<string> = new Set<string>([
|
||||||
"moveInterval",
|
"moveInterval",
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* errors.ts
|
||||||
|
* ---------
|
||||||
|
* Cross-cutting error types used by parsers, loaders, and config resolution.
|
||||||
|
* Kept in its own module so feature modules can import error types without
|
||||||
|
* pulling in unrelated implementation code (e.g., the JSON loader doesn't
|
||||||
|
* need to depend on the CLI parser just to throw a typed error).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when user-supplied input is invalid: unknown CLI option, missing
|
||||||
|
* value, non-positive number, malformed config file, unresolvable default
|
||||||
|
* path, etc. Distinct from runtime errors so the top-level entry can exit
|
||||||
|
* with code 2 (user error) instead of code 1 (runtime failure).
|
||||||
|
*/
|
||||||
|
export class CliError extends Error {}
|
||||||
@@ -49,7 +49,13 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cliArgs.help) {
|
if (cliArgs.help) {
|
||||||
|
// printHelp() resolves defaultConfigPath(), which can throw CliError
|
||||||
|
// when neither $XDG_CONFIG_HOME nor $HOME is set.
|
||||||
|
try {
|
||||||
printHelp();
|
printHelp();
|
||||||
|
} catch (err: unknown) {
|
||||||
|
failUser(err);
|
||||||
|
}
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
if (cliArgs.version) {
|
if (cliArgs.version) {
|
||||||
|
|||||||
Reference in New Issue
Block a user