From 114bfbb5cb04bdb38ba5fbf80f9747ef00036aff Mon Sep 17 00:00:00 2001 From: Ray Mck <76886945+aaseenib@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:13:51 +0000 Subject: [PATCH] cli: add prettier, npmignore, watch, and changesets setup (#332 #333 #334 #335) --- packages/cli/.npmignore | 8 ++++ packages/cli/.prettierignore | 3 ++ packages/cli/.prettierrc | 7 ++++ packages/cli/CHANGELOG.md | 7 ++++ packages/cli/src/build-watch.ts | 48 +++++++++++++++++++++++ packages/cli/src/changeset-helper.ts | 49 +++++++++++++++++++++++ packages/cli/src/npmignore-check.ts | 46 ++++++++++++++++++++++ packages/cli/src/prettier-config.ts | 58 ++++++++++++++++++++++++++++ 8 files changed, 226 insertions(+) create mode 100644 packages/cli/.npmignore create mode 100644 packages/cli/.prettierignore create mode 100644 packages/cli/.prettierrc create mode 100644 packages/cli/CHANGELOG.md create mode 100644 packages/cli/src/build-watch.ts create mode 100644 packages/cli/src/changeset-helper.ts create mode 100644 packages/cli/src/npmignore-check.ts create mode 100644 packages/cli/src/prettier-config.ts diff --git a/packages/cli/.npmignore b/packages/cli/.npmignore new file mode 100644 index 0000000..7cbe5da --- /dev/null +++ b/packages/cli/.npmignore @@ -0,0 +1,8 @@ +src/ +tests/ +coverage/ +*.config.ts +*.config.js +.prettierrc +.prettierignore +tsconfig.json diff --git a/packages/cli/.prettierignore b/packages/cli/.prettierignore new file mode 100644 index 0000000..2d0c064 --- /dev/null +++ b/packages/cli/.prettierignore @@ -0,0 +1,3 @@ +dist/ +node_modules/ +coverage/ diff --git a/packages/cli/.prettierrc b/packages/cli/.prettierrc new file mode 100644 index 0000000..4b93f4e --- /dev/null +++ b/packages/cli/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "printWidth": 100, + "trailingComma": "all", + "tabWidth": 2 +} diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md new file mode 100644 index 0000000..f380b56 --- /dev/null +++ b/packages/cli/CHANGELOG.md @@ -0,0 +1,7 @@ +# @stellar-explain/cli + +## 0.1.0 + +### Initial release + +- CLI for querying and explaining Stellar transactions via the Stellar Explain API diff --git a/packages/cli/src/build-watch.ts b/packages/cli/src/build-watch.ts new file mode 100644 index 0000000..42e78c3 --- /dev/null +++ b/packages/cli/src/build-watch.ts @@ -0,0 +1,48 @@ +/** + * Spawns the TypeScript compiler in watch mode for CLI development. + * Issue #333 — Add a build:watch script for CLI development. + */ + +import { spawn, ChildProcess } from "child_process"; +import path from "path"; + +interface WatchOptions { + cwd?: string; + silent?: boolean; +} + +export function startWatcher(opts: WatchOptions = {}): ChildProcess { + const cwd = opts.cwd ?? path.resolve(__dirname, ".."); + const tsc = process.platform === "win32" ? "tsc.cmd" : "tsc"; + + const child = spawn(tsc, ["--watch", "--preserveWatchOutput"], { + cwd, + stdio: opts.silent ? "ignore" : "inherit", + shell: false, + }); + + child.on("error", (err) => { + console.error("Failed to start tsc watcher:", err.message); + process.exit(1); + }); + + child.on("exit", (code) => { + if (code !== null && code !== 0) { + console.error(`tsc exited with code ${code}`); + } + }); + + return child; +} + +export function stopWatcher(child: ChildProcess): void { + if (!child.killed) child.kill("SIGTERM"); +} + +if (require.main === module) { + const watcher = startWatcher(); + process.on("SIGINT", () => { + stopWatcher(watcher); + process.exit(0); + }); +} diff --git a/packages/cli/src/changeset-helper.ts b/packages/cli/src/changeset-helper.ts new file mode 100644 index 0000000..3ed0f07 --- /dev/null +++ b/packages/cli/src/changeset-helper.ts @@ -0,0 +1,49 @@ +/** + * Reads and validates the Changesets config for the CLI package. + * Issue #335 — Add Changesets for CLI version management. + */ + +import fs from "fs"; +import path from "path"; + +interface ChangesetConfig { + $schema: string; + changelog: string; + commit: boolean; + access: "public" | "restricted"; + baseBranch: string; + updateInternalDependencies: "patch" | "minor"; + ignore: string[]; +} + +export function loadChangesetConfig(root: string): ChangesetConfig | null { + const configPath = path.join(root, ".changeset", "config.json"); + if (!fs.existsSync(configPath)) return null; + try { + return JSON.parse(fs.readFileSync(configPath, "utf-8")) as ChangesetConfig; + } catch { + return null; + } +} + +export function validateChangesetConfig(config: ChangesetConfig): string[] { + const errors: string[] = []; + if (config.access !== "public") errors.push(`access should be "public"`); + if (config.baseBranch !== "main") errors.push(`baseBranch should be "main"`); + if (config.commit !== false) errors.push(`commit should be false`); + return errors; +} + +export function checkChangesetSetup(root: string): void { + const config = loadChangesetConfig(root); + if (!config) { + console.error("Missing .changeset/config.json"); + process.exit(1); + } + const errors = validateChangesetConfig(config); + if (errors.length > 0) { + console.error("Changeset config issues:\n" + errors.join("\n")); + process.exit(1); + } + console.log("Changeset config OK"); +} diff --git a/packages/cli/src/npmignore-check.ts b/packages/cli/src/npmignore-check.ts new file mode 100644 index 0000000..16b3f9a --- /dev/null +++ b/packages/cli/src/npmignore-check.ts @@ -0,0 +1,46 @@ +/** + * Verifies .npmignore excludes dev-only paths from the published package. + * Issue #334 — Add .npmignore to keep published package lean. + */ + +import fs from "fs"; +import path from "path"; + +const REQUIRED_EXCLUSIONS = [ + "src/", + "tests/", + "coverage/", + "*.config.ts", + "*.config.js", + ".prettierrc", + ".prettierignore", + "tsconfig.json", +]; + +export function loadNpmIgnore(root: string): string[] { + const filePath = path.join(root, ".npmignore"); + if (!fs.existsSync(filePath)) return []; + return fs + .readFileSync(filePath, "utf-8") + .split("\n") + .map((l) => l.trim()) + .filter(Boolean); +} + +export function findMissingExclusions(entries: string[]): string[] { + return REQUIRED_EXCLUSIONS.filter((req) => !entries.includes(req)); +} + +export function checkNpmIgnore(root: string): void { + const entries = loadNpmIgnore(root); + if (entries.length === 0) { + console.error(".npmignore is missing or empty"); + process.exit(1); + } + const missing = findMissingExclusions(entries); + if (missing.length > 0) { + console.error("Missing exclusions in .npmignore:\n" + missing.join("\n")); + process.exit(1); + } + console.log(".npmignore OK — all required exclusions present"); +} diff --git a/packages/cli/src/prettier-config.ts b/packages/cli/src/prettier-config.ts new file mode 100644 index 0000000..ea0b416 --- /dev/null +++ b/packages/cli/src/prettier-config.ts @@ -0,0 +1,58 @@ +/** + * Validates that the Prettier config exists and matches expected rules. + * Issue #332 — Add Prettier config to the CLI package. + */ + +import fs from "fs"; +import path from "path"; + +interface PrettierConfig { + semi: boolean; + singleQuote: boolean; + printWidth: number; + trailingComma: string; + tabWidth: number; +} + +const EXPECTED: PrettierConfig = { + semi: true, + singleQuote: false, + printWidth: 100, + trailingComma: "all", + tabWidth: 2, +}; + +export function loadPrettierConfig(root: string): PrettierConfig | null { + const configPath = path.join(root, ".prettierrc"); + if (!fs.existsSync(configPath)) return null; + try { + return JSON.parse(fs.readFileSync(configPath, "utf-8")) as PrettierConfig; + } catch { + return null; + } +} + +export function validatePrettierConfig(config: PrettierConfig): string[] { + const errors: string[] = []; + for (const [key, expected] of Object.entries(EXPECTED)) { + const actual = config[key as keyof PrettierConfig]; + if (actual !== expected) { + errors.push(`"${key}": expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); + } + } + return errors; +} + +export function checkPrettierSetup(root: string): void { + const config = loadPrettierConfig(root); + if (!config) { + console.error("Missing .prettierrc in", root); + process.exit(1); + } + const errors = validatePrettierConfig(config); + if (errors.length > 0) { + console.error("Prettier config mismatch:\n" + errors.join("\n")); + process.exit(1); + } + console.log("Prettier config OK"); +}