From a5c5d4643b118344d99da687a9d21c43915a62d1 Mon Sep 17 00:00:00 2001 From: Shukaaa Date: Tue, 20 Jan 2026 00:28:45 +0100 Subject: [PATCH] feat: implement --force and --config path for init command --- ROADMAP.md | 2 +- bin/commands/init.command.ts | 50 ++++--- bin/commands/interfaces/swerr-command.ts | 6 - bin/commands/run.command.ts | 161 ++++++++++++----------- bin/swerr.ts | 19 +-- 5 files changed, 123 insertions(+), 115 deletions(-) delete mode 100644 bin/commands/interfaces/swerr-command.ts diff --git a/ROADMAP.md b/ROADMAP.md index b4eb992..9876934 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,8 +3,8 @@ - implement unit tests for converters # 1.1 Refinement -- Better cmd description and help texts - Alternative config path via `--config` flag +- `--force` flag to overwrite existing output files - `@error` tag should not be required in JSDoc comments with default config - You can change this behavior via config if needed - `sourceFile.requireErrorTag` config option diff --git a/bin/commands/init.command.ts b/bin/commands/init.command.ts index 7157b92..eae3b9b 100644 --- a/bin/commands/init.command.ts +++ b/bin/commands/init.command.ts @@ -1,4 +1,3 @@ -import {SwerrCommand} from "./interfaces/swerr-command.js"; import path from "path"; import * as fs from "node:fs"; import {LogUtils} from "@swerr/core"; @@ -6,9 +5,10 @@ import {SWERR_CONFIG_FILE} from "../config.js"; const initConfigTemplate = `import {markdownConverter, htmlConverter} from "@swerr/converter" +// See more configuration options at https://swerr.apidocumentation.com/guide/introduction/config export default { sourceFile: { - inputDir: "./src/errors", // Directory to scan for error definitions + inputDir: "./src", // Directory to scan for error definitions meta: { projectName: "Your Application Name", description: "The Application description", @@ -19,7 +19,7 @@ export default { }, options: { ignoreDirs: [], // Directories to ignore during scanning (optional) - whitelistExtensions: [".js"] // File extensions to include during scanning (optional) + whitelistExtensions: [".js", ".ts"] // File extensions to include during scanning (optional) } }, converter: [ // Example converters @@ -38,23 +38,35 @@ export default { ] }`; -export const initCommand: SwerrCommand<[string, string]> = { - command: "init", - description: "Create a basic swerr config file.", - action: async () => { - const configFilePath = path.resolve(process.cwd(), SWERR_CONFIG_FILE); - - if (fs.existsSync(configFilePath)) { - LogUtils.error(`A ${SWERR_CONFIG_FILE} file already exists in the current directory.`); - process.exit(1); - } - +export const initCommand = async (options: { force?: boolean; config?: string }) => { + const configFilePath = path.resolve(process.cwd(), options.config ?? SWERR_CONFIG_FILE); + const targetName = path.basename(configFilePath); + + const existedBefore = fs.existsSync(configFilePath); + if (existedBefore && !options.force) { try { - await fs.promises.writeFile(configFilePath, initConfigTemplate); - LogUtils.success(SWERR_CONFIG_FILE + " file has been created successfully."); - } catch (err) { - LogUtils.error(`Failed to create ${SWERR_CONFIG_FILE} file: ${err}`); - process.exit(1); + const stat = fs.lstatSync(configFilePath); + if (stat.isDirectory()) { + LogUtils.error(`A directory named ${targetName} already exists in the current directory.`); + } else { + LogUtils.error(`A ${targetName} file already exists in the current directory.`); + } + } catch { + LogUtils.error(`A ${targetName} entry already exists in the current directory.`); + } + process.exit(1); + } + + try { + await fs.promises.mkdir(path.dirname(configFilePath), { recursive: true }); + await fs.promises.writeFile(configFilePath, initConfigTemplate, { encoding: "utf8" }); + if (existedBefore && options.force) { + LogUtils.success(`${targetName} file has been overwritten successfully.`); + } else { + LogUtils.success(`${targetName} file has been created successfully.`); } + } catch (err) { + LogUtils.error(`Failed to create ${targetName} file: ${err}`); + process.exit(1); } } \ No newline at end of file diff --git a/bin/commands/interfaces/swerr-command.ts b/bin/commands/interfaces/swerr-command.ts deleted file mode 100644 index 0bd7db3..0000000 --- a/bin/commands/interfaces/swerr-command.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Action = (...args: T) => void; -export interface SwerrCommand { - command: string; - description: string; - action: Action; -} \ No newline at end of file diff --git a/bin/commands/run.command.ts b/bin/commands/run.command.ts index 07059c1..f6947bf 100644 --- a/bin/commands/run.command.ts +++ b/bin/commands/run.command.ts @@ -1,4 +1,3 @@ -import {SwerrCommand} from "./interfaces/swerr-command.js"; import path from "path"; import * as fs from "node:fs"; import {scanJsdocs} from "../extraction/swerr-scan.js"; @@ -8,92 +7,94 @@ import {existsSync} from "node:fs"; import {pathToFileURL} from "node:url"; import {LogUtils, SwerrConfig, SwerrScheme} from "@swerr/core"; -export const runCommand: SwerrCommand<[string, string]> = { - command: "run [configPath]", - description: "Create swerr documentation based on the config file.", - action: async (configPath: string | undefined) => { - const swerrConfig = await config(configPath); - const sourceDir = swerrConfig?.sourceFile?.inputDir || null - const outputDir = swerrConfig?.sourceFile?.export?.outputDir || null - LogUtils.success("Swerr Configuration loaded."); - - if (!sourceDir || !outputDir) { - LogUtils.error("Source and output directories must be specified either via configuration file."); - process.exit(1); - } - - const absoluteSourceDir = path.resolve(process.cwd(), sourceDir); - const absoluteOutputDir = path.resolve(process.cwd(), outputDir); - - const sourceExists = fs.existsSync(absoluteSourceDir); - if (!sourceExists) { - LogUtils.error(`Source directory "${absoluteSourceDir}" does not exist.`); - process.exit(1); - } - - try { - await fs.promises.mkdir(absoluteOutputDir, {recursive: true}); - } catch (err) { - LogUtils.error(`Failed to create output directory "${absoluteOutputDir}": ${err}`); - process.exit(1); - } - - scanJsdocs(absoluteSourceDir, swerrConfig?.sourceFile.options || {}).then(async result => { - LogUtils.info(`Scanned ${result.blocks.length} JSDocs block(s) from ${result.scannedFiles} file(s).`); - const scheme = translateToSourceScheme(result, swerrConfig) - LogUtils.info(`Translated scan result to swerr Scheme with ${scheme.errors.length} error(s).`); - await saveSourceScheme(swerrConfig!, absoluteOutputDir, scheme); - await runConverter(swerrConfig!, scheme); - }).catch(err => { - LogUtils.error(`Error during scanning: ${err}`); - }) - } +export const runCommand = async (configPath: string | undefined) => { + const swerrConfig = await config(configPath); + const sourceDir = swerrConfig?.sourceFile?.inputDir || null + const outputDir = swerrConfig?.sourceFile?.export?.outputDir || null + + if (!swerrConfig) { + LogUtils.error(`Swerr configuration file not found. Please create a ${SWERR_CONFIG_FILE} file in the current directory or specify a custom path using --config option.`); + process.exit(1); + } + + if (!sourceDir || !outputDir) { + LogUtils.error("Source and output directories must be specified either via configuration file."); + process.exit(1); + } + + LogUtils.success("Swerr Configuration loaded."); + + const absoluteSourceDir = path.resolve(process.cwd(), sourceDir); + const absoluteOutputDir = path.resolve(process.cwd(), outputDir); + + const sourceExists = fs.existsSync(absoluteSourceDir); + if (!sourceExists) { + LogUtils.error(`Source directory "${absoluteSourceDir}" does not exist.`); + process.exit(1); + } + + try { + await fs.promises.mkdir(absoluteOutputDir, {recursive: true}); + } catch (err) { + LogUtils.error(`Failed to create output directory "${absoluteOutputDir}": ${err}`); + process.exit(1); + } + + scanJsdocs(absoluteSourceDir, swerrConfig?.sourceFile.options || {}).then(async result => { + LogUtils.info(`Scanned ${result.blocks.length} JSDocs block(s) from ${result.scannedFiles} file(s).`); + const scheme = translateToSourceScheme(result, swerrConfig) + LogUtils.info(`Translated scan result to swerr Scheme with ${scheme.errors.length} error(s).`); + await saveSourceScheme(swerrConfig!, absoluteOutputDir, scheme); + await runConverter(swerrConfig!, scheme); + }).catch(err => { + LogUtils.error(`Error during scanning: ${err}`); + }) } async function config(configPath: string | undefined): Promise { - try { - if (!configPath) { - configPath = path.resolve(process.cwd(), SWERR_CONFIG_FILE); - } else { - configPath = path.resolve(process.cwd(), configPath); - } - let cfg: SwerrConfig | null = null; - - if (existsSync(configPath)) { - LogUtils.info(`Loading configuration from ${configPath}`); - try { - const imported = await import(pathToFileURL(configPath).href); - cfg = imported.default ?? imported; - return cfg; - } catch (err) { - LogUtils.error(`Failed to load configuration from ${configPath}: ${err}`); - process.exit(1); - } - } - - return null; - } catch (err) { - LogUtils.error(`Error loading configuration: ${err}`); - process.exit(1); - } + try { + if (!configPath) { + configPath = path.resolve(process.cwd(), SWERR_CONFIG_FILE); + } else { + configPath = path.resolve(process.cwd(), configPath); + } + let cfg: SwerrConfig | null = null; + + if (existsSync(configPath)) { + LogUtils.info(`Loading configuration from ${configPath}`); + try { + const imported = await import(pathToFileURL(configPath).href); + cfg = imported.default ?? imported; + return cfg; + } catch (err) { + LogUtils.error(`Failed to load configuration from ${configPath}: ${err}`); + process.exit(1); + } + } + + return null; + } catch (err) { + LogUtils.error(`Error loading configuration: ${err}`); + process.exit(1); + } } async function saveSourceScheme(config: SwerrConfig, absoluteOutputDir: string, scheme: SwerrScheme) { - if (!config?.sourceFile?.export?.saveToFile) return - const fileName = config?.sourceFile?.export?.fileName || "swerr-docs.json"; - const outputFilePath = path.join(absoluteOutputDir, fileName); - const docContent = JSON.stringify(scheme, null, 2); - try { - await fs.promises.writeFile(outputFilePath, docContent, "utf8"); - LogUtils.success(`Swerr Source File written to ${outputFilePath}`); - } catch (err) { - console.error(`Failed to write documentation to "${outputFilePath}":`, err); - process.exit(1); - } + if (!config?.sourceFile?.export?.saveToFile) return + const fileName = config?.sourceFile?.export?.fileName || "swerr-docs.json"; + const outputFilePath = path.join(absoluteOutputDir, fileName); + const docContent = JSON.stringify(scheme, null, 2); + try { + await fs.promises.writeFile(outputFilePath, docContent, "utf8"); + LogUtils.success(`Swerr Source File written to ${outputFilePath}`); + } catch (err) { + console.error(`Failed to write documentation to "${outputFilePath}":`, err); + process.exit(1); + } } async function runConverter(config: SwerrConfig, scheme: SwerrScheme) { - for (const converter of config.converter) { - await converter.factory(converter.config, scheme); - } + for (const converter of config.converter) { + await converter.factory(converter.config, scheme); + } } \ No newline at end of file diff --git a/bin/swerr.ts b/bin/swerr.ts index 4156fd0..a4af0f6 100644 --- a/bin/swerr.ts +++ b/bin/swerr.ts @@ -19,15 +19,16 @@ cli .description("Create documentation from your errors.") .version(packageJson.version); -const commandModules = [ - runCommand, initCommand -]; +cli + .command("init") + .description("Create a basic swerr config file.") + .option("-f, --force", "Overwrite existing config file if it exists.") + .option("-c, --config ", "Path to save the swerr config file.", "swerr.config.js") + .action(initCommand); -for (const commandModule of commandModules) { - cli - .command(commandModule.command) - .description(commandModule.description) - .action(commandModule.action); -} +cli + .command("run [configPath]") + .description("Create swerr documentation based on the config file.") + .action(runCommand); cli.parse();