Files
hyperframes/packages/cli/src/utils/project.ts
T
Miguel Ángel 627e9476af fix(cli): a failed command sends one error report, naming its real cause (#4586)
* fix(cli): a failed command sends one cli_error, naming its real cause

* chore(cli): drop a comment the parameter name already says

* fix(cli): a failure caught by its own command is still reported, and only once

* test(cli): normalize-audio test passes the flags it requires

* style(cli): format the failure tracking test

* fix(cli): one function unwraps and reports a command failure, once

* docs(cli): shorten the command failure reporter's comment
2026-09-27 13:06:42 +00:00

78 lines
2.1 KiB
TypeScript

import { failCommand } from "./commandResult.js";
import { existsSync, statSync } from "node:fs";
import { resolve, basename } from "node:path";
import { errorBox } from "../ui/format.js";
import { trackCommandFailure } from "../telemetry/events.js";
export interface ProjectDir {
dir: string;
name: string;
indexPath: string;
}
export interface ResolveProjectOptions {
requireIndex?: boolean;
}
export class InvalidProjectError extends Error {
readonly title: string;
readonly hint?: string;
readonly suggestion?: string;
constructor(title: string, hint?: string, suggestion?: string) {
super(title);
this.name = "InvalidProjectError";
this.title = title;
this.hint = hint;
this.suggestion = suggestion;
}
}
export function resolveProjectOrThrow(
dirArg: string | undefined,
options: ResolveProjectOptions = {},
): ProjectDir {
const trimmed = dirArg?.trim();
if (trimmed === "#") {
throw new InvalidProjectError(
"Invalid project directory: #",
"# is a URL fragment, not a project path.",
"Run hyperframes preview . from your project directory.",
);
}
const dir = resolve(dirArg ?? ".");
const name = basename(dir);
const indexPath = resolve(dir, "index.html");
if (!existsSync(dir) || !statSync(dir).isDirectory()) {
throw new InvalidProjectError("Not a directory: " + dir);
}
if (options.requireIndex !== false && !existsSync(indexPath)) {
throw new InvalidProjectError(
"No composition found in " + dir,
"No index.html file found.",
"Run npx hyperframes init to create a new composition.",
);
}
return { dir, name, indexPath };
}
export function resolveProject(
dirArg: string | undefined,
options: ResolveProjectOptions = {},
): ProjectDir {
try {
return resolveProjectOrThrow(dirArg, options);
} catch (err) {
if (err instanceof InvalidProjectError) {
// Reported here: several commands catch this failure and never reach the executable boundary.
trackCommandFailure(process.argv[2] ?? "unknown", err);
errorBox(err.title, err.hint, err.suggestion);
failCommand(1, err);
}
throw err;
}
}