Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/action/actions/start.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { PackageManagerFactory } from "@lib/package-manager";
import { Messages } from "@lib/ui";

import { CLIError } from "@utils/errors";
import { getCwd, getModulePath } from "@utils/path";
import { runSafe } from "@utils/run-safe";

Expand Down Expand Up @@ -61,8 +62,8 @@ export class StartAction extends AbstractAction {

if (!cert && !key) return undefined;

if (!cert) throw new Error("No cert entered for SSL. Please enter a key with --cert.");
if (!key) throw new Error("No key entered for SSL. Please enter a key with --key.");
if (!cert) throw new CLIError("No cert entered for SSL. Please enter a cert with --cert.");
if (!key) throw new CLIError("No key entered for SSL. Please enter a key with --key.");

return {
cert,
Expand Down
6 changes: 2 additions & 4 deletions src/lib/config/config-loader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ describe("loadConfig", () => {

const { loadConfig: freshLoad } = await import("./config-loader");

await expect(freshLoad("/project")).rejects.toThrow(
"No config file found in directory: /project",
);
await expect(freshLoad("/project")).rejects.toThrow("Configuration file not found at path:");
});

it("should throw when config file cannot be parsed", async () => {
Expand All @@ -73,7 +71,7 @@ describe("loadConfig", () => {

const { loadConfig: freshLoad } = await import("./config-loader");

await expect(freshLoad("/project")).rejects.toThrow("Not able to read config file");
await expect(freshLoad("/project")).rejects.toThrow("File System Error [read config file]");
});

it("should throw on validation errors", async () => {
Expand Down
7 changes: 4 additions & 3 deletions src/lib/config/config-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { join } from "node:path";

import { CONFIG_FILE_NAME } from "@lib/constants";

import { CLIError, ConfigNotFoundError, FileSystemError } from "@utils/errors";
import { deepMerge } from "@utils/object";

import { CONFIG_DEFAULTS } from "./config-defaults";
Expand All @@ -20,7 +21,7 @@ const getConfigPath = (directory: string, name?: string) => {
const path = join(directory, n);
if (existsSync(path)) return path;
}
throw new Error(`No config file found in directory: ${directory}`);
throw new ConfigNotFoundError(join(directory, CONFIG_FILE_NAME));
}
};

Expand All @@ -39,14 +40,14 @@ export const loadConfig = async (
} catch {
rawData = noThrow ? CONFIG_DEFAULTS : null;
}
if (!rawData) throw new Error(`Not able to read config file : ${path}`);
if (!rawData) throw new FileSystemError("read config file", path);

const data = plainToInstance(Config, rawData, {
excludeExtraneousValues: true,
});
const errors = await validate(data);
if (errors.length > 0)
throw new Error(`Invalid config :\n${errors.toString().replace(/,/g, "\n")}`);
throw new CLIError(`Invalid config:\n${errors.toString().replace(/,/g, "\n")}`);

config = data;
return config;
Expand Down
5 changes: 3 additions & 2 deletions src/lib/http/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { REGISTRY_URL } from "@lib/constants";

import { RegistryAuthenticationError } from "@utils/errors";

import { HttpClient } from "./http-client";
import { Repository } from "./repository";

Expand All @@ -15,8 +17,7 @@ export const withAuth = (
},
) => {
if (!apiKey && force) {
console.error("No registry key found. Please use `nf login` to login");
throw new Error("No apikey found. Please use `nf login` to login");
throw new RegistryAuthenticationError();
}
return new Repository(
new HttpClient(REGISTRY_URL ?? "", {
Expand Down
16 changes: 5 additions & 11 deletions src/lib/http/repository.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ApiRequestError } from "@utils/errors";

import type { HttpClient, RequestOptions } from "./http-client";

export class Repository {
Expand Down Expand Up @@ -50,10 +52,7 @@ export class Repository {
): Promise<R> {
const res = await this._client[request](path, options);
const data = (await res.json()) as R;
if (!res.ok)
throw new Error(`Request failed with status code ${res.status}`, {
cause: data["error" as keyof R],
});
if (!res.ok) throw new ApiRequestError(res.status, data["error" as keyof R]);
return data;
}

Expand All @@ -64,9 +63,7 @@ export class Repository {
): Promise<Blob> {
const res = await this._client[request](path, options);
if (!res.ok)
throw new Error(`Request failed with status code ${res.status}`, {
cause: ((await res.json()) as { error: any })["error"],
});
throw new ApiRequestError(res.status, ((await res.json()) as { error: any })["error"]);
return await res.blob();
}

Expand All @@ -82,10 +79,7 @@ export class Repository {
options,
);
const data = (await res.json()) as R;
if (!res.ok)
throw new Error(`Request failed with status code ${res.status}`, {
cause: data["error" as keyof R],
});
if (!res.ok) throw new ApiRequestError(res.status, data["error" as keyof R]);
return data;
}
}
4 changes: 3 additions & 1 deletion src/lib/input/ask-inputs.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CLIError } from "@utils/errors";

export const getInputOrAsk = async <T>(
baseInput: T | undefined,
askCb: () => Promise<T>,
Expand All @@ -7,5 +9,5 @@ export const getInputOrAsk = async <T>(
const res = await askCb();
if (res !== undefined) return res;
if (defaultValue !== undefined) return defaultValue;
throw new Error("No input provided");
throw new CLIError("No input provided. Please provide a valid value.");
};
10 changes: 7 additions & 3 deletions src/lib/input/base-inputs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ describe("getStringInput", () => {

it("should throw on non-string value", () => {
const input = createInput([["name", 42]]);
expect(() => getStringInput(input, "name")).toThrow("Invalid type for name");
expect(() => getStringInput(input, "name")).toThrow(
"Invalid argument 'name'. Expected: string.",
);
});
});

Expand Down Expand Up @@ -61,7 +63,9 @@ describe("getBooleanInput", () => {

it("should throw on non-boolean value", () => {
const input = createInput([["strict", "yes"]]);
expect(() => getBooleanInput(input, "strict")).toThrow("Invalid type for strict");
expect(() => getBooleanInput(input, "strict")).toThrow(
"Invalid argument 'strict'. Expected: boolean.",
);
});
});

Expand Down Expand Up @@ -90,7 +94,7 @@ describe("getArrayInput", () => {

it("should throw on non-array value", () => {
const input = createInput([["libs", "not-array"]]);
expect(() => getArrayInput(input, "libs")).toThrow("Invalid type for libs");
expect(() => getArrayInput(input, "libs")).toThrow("Invalid argument 'libs'. Expected: array.");
});
});

Expand Down
8 changes: 5 additions & 3 deletions src/lib/input/base-inputs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { InvalidCommandArgumentError } from "@utils/errors";

import { type Input } from "./input.type";

export const getStringInput = (input: Input, field: string): string | undefined => {
const value = input.get(field)?.value;
if (value === undefined) return undefined;
if (typeof value === "string") return value;
throw new Error(`Invalid type for ${field}`);
throw new InvalidCommandArgumentError(field, "string");
};

export const getStringInputWithDefault = (
Expand All @@ -19,7 +21,7 @@ export const getBooleanInput = (input: Input, field: string): boolean | undefine
const value = input.get(field)?.value;
if (value === undefined) return undefined;
if (typeof value === "boolean") return value;
throw new Error(`Invalid type for ${field}`);
throw new InvalidCommandArgumentError(field, "boolean");
};

export const getBooleanInputWithDefault = (
Expand All @@ -34,7 +36,7 @@ export const getArrayInput = (input: Input, field: string): string[] | undefined
const value = input.get(field)?.value;
if (value === undefined) return undefined;
if (typeof value === "object" && Array.isArray(value)) return value;
throw new Error(`Invalid type for ${field}`);
throw new InvalidCommandArgumentError(field, "array");
};

export const getArrayInputWithDefault = (
Expand Down
8 changes: 5 additions & 3 deletions src/lib/input/inputs/create/type.input.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { getStringInput } from "../../base-inputs";
import { type Input } from "../../input.type";
import { getStringInput } from "@lib/input";
import { type Input } from "@lib/input";

import { InvalidCommandArgumentError } from "@utils/errors";

export const getCreateTypeInput = (inputs: Input): "component" | "system" => {
const res = getStringInput(inputs, "type");
if (res && ["component", "system"].includes(res)) return res as "component" | "system";
throw new Error("Invalid type. Please enter 'component' or 'system'.");
throw new InvalidCommandArgumentError("type", "'component' or 'system'");
};
12 changes: 8 additions & 4 deletions src/lib/manifest/manifest-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { join } from "node:path";

import { MANIFEST_FILE_NAME } from "@lib/constants";

import { ManifestError } from "@utils/errors";
import { deepMerge } from "@utils/object";

import { Manifest } from "./manifest.type";
Expand All @@ -14,7 +15,7 @@ const getManifestPath = (directory: string) => {
const path = join(directory, n);
if (existsSync(path)) return path;
}
throw new Error(`No manifest file found in directory: ${directory}`);
throw new ManifestError(`No manifest file found in directory: ${directory}`);
};

export const loadManifest = async (directory: string): Promise<Manifest> => {
Expand All @@ -26,14 +27,17 @@ export const loadManifest = async (directory: string): Promise<Manifest> => {
} catch {
rawData = null;
}
if (!rawData) throw new Error(`Not able to read manifest file : ${path}`);
if (!rawData) {
throw new ManifestError(`Unable to read or parse file at ${path}`);
}

const data = plainToInstance(Manifest, rawData, {
excludeExtraneousValues: true,
});

const errors = await validate(data);
if (errors.length > 0)
throw new Error(`Invalid manifest\n${errors.toString().replace(/,/g, "\n")}`);
if (errors.length > 0) {
throw new ManifestError(`Validation failed\n${errors.toString()}`);
}
return data;
};
2 changes: 1 addition & 1 deletion src/lib/package-manager/package-manager.factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe("PackageManagerFactory", () => {

it("should throw for unsupported package manager", () => {
expect(() => PackageManagerFactory.create("unknown")).toThrow(
"Package manager unknown is not managed.",
"Package manager 'unknown' is not managed/supported.",
);
});
});
Expand Down
4 changes: 3 additions & 1 deletion src/lib/package-manager/package-manager.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { resolve } from "path";

import { RunnerFactory } from "@lib/runner";

import { CLIError } from "@utils/errors";

import { PackageManager } from "./package-manager";
import { PM_CONFIGS } from "./package-manager-configs";
import { PackageManagerName } from "./package-manager-name";
Expand All @@ -18,7 +20,7 @@ export class PackageManagerFactory {
public static create(name: PackageManagerName | string): PackageManager {
const config = PM_CONFIGS[name as PackageManagerName];
if (!config) {
throw new Error(`Package manager ${name} is not managed.`);
throw new CLIError(`Package manager '${name}' is not managed/supported.`);
}

const runner = this.createRunner(name as PackageManagerName, config.binary);
Expand Down
5 changes: 3 additions & 2 deletions src/lib/package-manager/package-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createStderrLogger, createStdoutLogger } from "@lib/runner/process-logg
import { type RunOptions, type Runner } from "@lib/runner/runner";
import { Messages } from "@lib/ui";

import { CLIError } from "@utils/errors";
import { getCwd } from "@utils/path";
import { withSpinner } from "@utils/spinner";

Expand Down Expand Up @@ -168,7 +169,7 @@ export class PackageManager {

private assertSupports(feature: keyof PackageManagerCommands): void {
if (!this.commands[feature]) {
throw new Error(`Package manager "${this.name}" does not support "${feature}"`);
throw new CLIError(`Package manager "${this.name}" does not support "${feature}"`);
}
}

Expand All @@ -192,7 +193,7 @@ export class PackageManager {
flags: string[],
silent: boolean,
): string[] {
if (!this.commands.runFile) throw new Error("Package manager does not support runFile");
if (!this.commands.runFile) throw new CLIError("Package manager does not support runFile");
const args = [...flags, this.commands.runFile];
if (silent) args.push(this.commands.silentFlag);
args.push(script);
Expand Down
5 changes: 3 additions & 2 deletions src/lib/question/questions/number.question.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { number } from "@inquirer/prompts";

import { promptError } from "@utils/errors";
import { CLIError, promptError } from "@utils/errors";

interface NumberOptions {
default?: number;
Expand All @@ -27,6 +27,7 @@ export const askNumber = async (
required: options.required,
}).catch(promptError);

if (res === undefined || isNaN(res) || !isFinite(res)) throw new Error("Invalid number");
if (res === undefined || isNaN(res) || !isFinite(res))
throw new CLIError("Invalid number provided.");
return res;
};
5 changes: 3 additions & 2 deletions src/lib/registry/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GlobalConfigHandler } from "@lib/global-config";
import { type Repository, withAuth } from "@lib/http";
import { type FullManifest, type Manifest } from "@lib/manifest";

import { CLIError } from "@utils/errors";
import { getCwd } from "@utils/path";

export class Registry {
Expand Down Expand Up @@ -62,14 +63,14 @@ export class Registry {
private static _getPackageFile(filename: string, dir?: string): Promise<Blob> {
const path = join(getCwd(dir ?? "."), filename);
if (!fs.existsSync(path))
throw new Error(
throw new CLIError(
"Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.package`!",
);
try {
fs.accessSync(path, fs.constants.R_OK);
return fs.openAsBlob(path);
} catch {
throw new Error("Cannot read package file, please verify your file permissions!");
throw new CLIError("Cannot read package file, please verify your file permissions!");
}
}
}
3 changes: 2 additions & 1 deletion src/lib/runner/runner.factory.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CLIError } from "@utils/errors";
import { getModulePath, resolveCLINodeBinaryPath } from "@utils/path";

import { Runner } from "./runner";
Expand All @@ -20,7 +21,7 @@ export class RunnerFactory {
try {
return getModulePath("@angular-devkit/schematics-cli/bin/schematics.js");
} catch {
throw new Error("'schematics' binary path could not be found!");
throw new CLIError("'schematics' binary path could not be found!");
}
}
}
4 changes: 3 additions & 1 deletion src/lib/schematics/collection.factory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { RunnerFactory } from "@lib/runner";

import { CLIError } from "@utils/errors";

import { type AbstractCollection } from "./abstract.collection";
import { Collection } from "./collection";
import { NanoforgeCollection } from "./nanoforge.collection";
Expand All @@ -11,6 +13,6 @@ export class CollectionFactory {
if (collection === Collection.NANOFORGE) {
return new NanoforgeCollection(schematicRunner, directory);
}
throw new Error(`Unknown collection: ${collection}`);
throw new CLIError(`Unknown collection: ${collection}`);
}
}
Loading
Loading