UNPKG

@entro314labs/starlight-document-converter

Version:

A comprehensive document converter for Astro Starlight that transforms various document formats into Starlight-compatible Markdown with proper frontmatter

749 lines (740 loc) 28.6 kB
#!/usr/bin/env node import { boxes, createBrandHeader, formatHelpSection } from "./chunk-RI3FHBT3.js"; import { DocumentConverter, detectStarlightConfig, isStarlightProject } from "./chunk-HMTAMXXQ.js"; // src/cli.ts import { existsSync as existsSync2, lstatSync, readdirSync } from "fs"; import { basename, dirname, relative, resolve as resolve2 } from "path"; import { cancel, confirm, intro, isCancel, multiselect, note, outro, select, spinner, text } from "@clack/prompts"; import { Command } from "commander"; import pc from "picocolors"; // src/utils/cli-helpers.ts import { existsSync } from "fs"; import { resolve } from "path"; function getSmartDefaults(cwd = process.cwd()) { try { const starlightConfig = detectStarlightConfig(cwd); const isStarlight = isStarlightProject(cwd); return { outputDir: starlightConfig.docsDir, isStarlightProject: isStarlight, title: starlightConfig.title, description: starlightConfig.description, recommendations: getRecommendations(cwd, starlightConfig, isStarlight) }; } catch { return { outputDir: "docs-output", isStarlightProject: false, title: "Documentation", description: "Documentation site", recommendations: [ "Using CLI-only mode. Install @astrojs/starlight for full integration features." ] }; } } function getRecommendations(cwd, config, isStarlight) { const recommendations = []; if (!isStarlight) { recommendations.push( "This doesn't appear to be a Starlight project. Consider installing @astrojs/starlight first." ); } if (!existsSync(resolve(cwd, config.docsDir))) { recommendations.push( `Content directory ${config.docsDir} doesn't exist yet. It will be created automatically.` ); } const importDirs = ["docs-import", "documents", "content-import"]; const existingImportDirs = importDirs.filter((dir) => existsSync(resolve(cwd, dir))); if (existingImportDirs.length === 0) { recommendations.push( 'Consider creating a "docs-import" directory to drop documents for conversion.' ); } else { recommendations.push(`Found existing import directories: ${existingImportDirs.join(", ")}`); } return recommendations; } function getOutputDirectory(userSpecified, cwd = process.cwd()) { if (userSpecified) { return userSpecified; } try { const starlightConfig = detectStarlightConfig(cwd); return starlightConfig.docsDir; } catch { return "docs-output"; } } function detectInputSources(cwd = process.cwd()) { const sources = []; try { const starlightConfig = detectStarlightConfig(cwd); if (starlightConfig.docsDir && existsSync(resolve(cwd, starlightConfig.docsDir))) { sources.push(starlightConfig.docsDir); } } catch { } const commonDirs = ["docs-import", "documents", "content-import", "drafts", "imports", "_import"]; for (const dir of commonDirs) { if (existsSync(resolve(cwd, dir))) { if (!sources.includes(dir)) { sources.push(dir); } } } return sources; } // src/cli.ts var program = new Command(); var getFormattedStats = (results) => { const successful = results.filter((r) => r.success).length; const skipped = results.filter((r) => r.skipped).length; const failed = results.filter((r) => !(r.success || r.skipped)).length; return { successful, skipped, failed, total: results.length }; }; var detectInputType = (inputPath) => { if (!existsSync2(inputPath)) { return "not-found"; } return lstatSync(inputPath).isDirectory() ? "directory" : "file"; }; var getSampleFiles = (dir, maxSamples = 5) => { try { const files = readdirSync(dir, { recursive: true }).filter((file) => typeof file === "string").filter((file) => { const ext = file.split(".").pop()?.toLowerCase(); return ["md", "mdx", "txt", "html", "htm", "docx", "doc", "rtf"].includes(ext || ""); }).slice(0, maxSamples); return files; } catch { return []; } }; function showProjectInfo(smartDefaults) { if (smartDefaults.isStarlightProject) { note( `\u2705 Detected Starlight project: ${pc.cyan(smartDefaults.title || "Documentation")}`, "Project Info" ); } else { note("\u26A0\uFE0F Starlight not detected - using fallback configuration", "Project Info"); } if (smartDefaults.recommendations.length > 0) { note(smartDefaults.recommendations.join("\n"), "Recommendations"); } } async function getInputPath(detectedSources, outputDir) { if (detectedSources.length > 0) { const isStarlightDir = detectedSources[0] === outputDir || detectedSources[0].includes("src/content/docs"); const message = isStarlightDir ? `Found Starlight content directory and ${detectedSources.length > 1 ? "other directories" : "import directories"}: ${detectedSources.map((d) => pc.cyan(d)).join(", ")}` : `Found document directories: ${detectedSources.map((d) => pc.cyan(d)).join(", ")}`; note(message, "Available Sources"); const sourceChoice = await select({ message: "Choose input source:", options: [ ...detectedSources.map((source, index) => { const isMainContentDir = index === 0 && isStarlightDir; return { value: source, label: `\u{1F4C1} ${source}`, hint: isMainContentDir ? "Your Starlight content directory" : "Document directory" }; }), { value: "custom", label: "\u270F\uFE0F Custom path", hint: "Specify a different path" } ] }); if (isCancel(sourceChoice)) { cancel("Operation cancelled"); process.exit(0); } if (sourceChoice === "custom") { return await getCustomPath("Enter path to convert:"); } return sourceChoice; } return await getCustomPath("What would you like to convert?"); } async function getCustomPath(message) { const customPath = await text({ message, placeholder: "./docs or ./document.md", validate: (value) => { if (!value) return "Please provide an input path"; const resolved = resolve2(value); const type = detectInputType(resolved); if (type === "not-found") return "Path does not exist"; return; } }); if (isCancel(customPath)) { cancel("Operation cancelled"); process.exit(0); } return customPath; } function showConversionPreview(resolvedInput, inputType) { if (inputType === "directory") { const sampleFiles = getSampleFiles(resolvedInput); if (sampleFiles.length > 0) { note( `Found ${sampleFiles.length} convertible files: ${sampleFiles.map((f) => ` \u2022 ${f}`).join("\n")}${sampleFiles.length === 5 ? "\n ... and potentially more" : ""}`, "Preview" ); } } else { note(`Converting single file: ${pc.cyan(basename(resolvedInput))}`, "Preview"); } } async function getAdvancedOptions() { const advancedOptions = await confirm({ message: "Configure advanced options?", initialValue: false }); const defaultOptions = { preserveStructure: true, generateTitles: true, generateDescriptions: true, addTimestamps: false, verbose: false, dryRun: false }; if (!advancedOptions || isCancel(advancedOptions)) { return defaultOptions; } const preserveStructure = await confirm({ message: "Preserve directory structure?", initialValue: true }); if (isCancel(preserveStructure)) { cancel("Operation cancelled"); process.exit(0); } const contentOptions = await multiselect({ message: "What should be auto-generated?", options: [ { value: "titles", label: "Titles from content", hint: "Extract titles from headings or filenames" }, { value: "descriptions", label: "Descriptions from content", hint: "Generate descriptions from first paragraph" }, { value: "timestamps", label: "Last updated timestamps", hint: "Add conversion date to frontmatter" } ], initialValues: ["titles", "descriptions"] }); if (isCancel(contentOptions)) { cancel("Operation cancelled"); process.exit(0); } const outputOptions = await multiselect({ message: "Output preferences:", options: [ { value: "verbose", label: "Verbose output", hint: "Show detailed conversion logs" }, { value: "dryRun", label: "Dry run", hint: "Preview changes without writing files" } ], initialValues: [] }); if (isCancel(outputOptions)) { cancel("Operation cancelled"); process.exit(0); } return { preserveStructure, generateTitles: contentOptions.includes("titles"), generateDescriptions: contentOptions.includes("descriptions"), addTimestamps: contentOptions.includes("timestamps"), verbose: outputOptions.includes("verbose"), dryRun: outputOptions.includes("dryRun") }; } function showResults(results, converterOptions) { const stats = getFormattedStats(results); if (stats.total > 0) { note( `${pc.green("\u2705 Successful:")} ${stats.successful} files ` + (stats.skipped > 0 ? `${pc.yellow("\u23ED\uFE0F Skipped:")} ${stats.skipped} files ` : "") + (stats.failed > 0 ? `${pc.red("\u274C Failed:")} ${stats.failed} files ` : "") + (converterOptions.dryRun ? pc.yellow("\u{1F9EA} Dry run - no files were modified") : ""), "Results" ); } const successfulResults = results.filter((r) => r.success).slice(0, 3); if (successfulResults.length > 0 && !converterOptions.dryRun) { note( `${successfulResults.map((r) => `\u2022 ${pc.cyan(relative(process.cwd(), r.inputPath))} \u2192 ${pc.green(relative(process.cwd(), r.outputPath))}`).join("\n")}`, "Sample conversions" ); } } async function interactiveConvert() { intro(pc.bgMagenta(pc.black(" Starlight Document Converter "))); const smartDefaults = getSmartDefaults(); const detectedSources = detectInputSources(); showProjectInfo(smartDefaults); const inputPath = await getInputPath(detectedSources, smartDefaults.outputDir); const resolvedInput = resolve2(inputPath); const inputType = detectInputType(resolvedInput); showConversionPreview(resolvedInput, inputType); const outputDir = await text({ message: "Where should the converted files be saved?", placeholder: smartDefaults.outputDir, initialValue: smartDefaults.outputDir }); if (isCancel(outputDir)) { cancel("Operation cancelled"); process.exit(0); } const advancedOptions = await getAdvancedOptions(); const converterOptions = { outputDir, ...advancedOptions }; const confirmConversion = await confirm({ message: `${converterOptions.dryRun ? "Preview" : "Convert"} ${inputType === "directory" ? "directory" : "file"}?` }); if (!confirmConversion || isCancel(confirmConversion)) { cancel("Operation cancelled"); process.exit(0); } const s = spinner(); s.start(`${converterOptions.dryRun ? "Previewing" : "Converting"} documents...`); try { const converter = new DocumentConverter(converterOptions); const results = inputType === "directory" ? await converter.convertDirectory(resolvedInput) : [await converter.convertFile(resolvedInput)]; s.stop(`Conversion ${converterOptions.dryRun ? "preview" : "completed"}!`); showResults(results, converterOptions); converter.printStats(); outro( `${pc.green("\u{1F389} All done!")} Your documents have been ${converterOptions.dryRun ? "previewed" : "converted successfully"}.` ); } catch (error) { s.stop("Conversion failed"); note(`${pc.red("\u274C Error:")} ${error}`, "Conversion failed"); process.exit(1); } } async function configurationWizard() { intro(pc.bgBlue(pc.black(" Starlight Integration Setup "))); const projectType = await select({ message: "What type of project are you setting up?", options: [ { value: "new", label: "New Starlight project", hint: "Complete setup with Astro config" }, { value: "existing", label: "Existing Starlight project", hint: "Add converter to existing setup" }, { value: "standalone", label: "Standalone CLI usage", hint: "Just use the command line tool" } ] }); if (isCancel(projectType)) { cancel("Setup cancelled"); return process.exit(0); } if (projectType === "standalone") { note( `You can now use the converter with: ${pc.cyan("npx starlight-convert <input> [options]")} For interactive mode: ${pc.cyan("npx starlight-convert")}`, "CLI Usage" ); outro("\u{1F389} Setup complete!"); return; } const inputDirs = await text({ message: "Which directories should be monitored for documents?", placeholder: "docs-import,documents,content-drafts", initialValue: "docs-import" }); if (isCancel(inputDirs)) { cancel("Setup cancelled"); return process.exit(0); } const inputDirsList = inputDirs.split(",").map((d) => d.trim()); const enableWatch = await confirm({ message: "Enable automatic file watching?", initialValue: true }); if (isCancel(enableWatch)) { cancel("Setup cancelled"); return process.exit(0); } const config = `import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import starlightDocumentConverter from 'starlight-document-converter'; export default defineConfig({ integrations: [ starlight({ title: 'My Documentation', description: 'Documentation powered by Starlight', social: { github: 'https://github.com/your-username/your-repo', }, sidebar: [ { label: 'Guides', items: [ { label: 'Getting Started', link: '/guides/getting-started/' }, ], }, { label: 'Reference', items: [ { label: 'API Reference', link: '/reference/api/' }, ], }, ], }), starlightDocumentConverter({ watch: ${enableWatch}, inputDirs: ${JSON.stringify(inputDirsList)}, converter: { outputDir: 'src/content/docs', preserveStructure: true, generateTitles: true, generateDescriptions: true, verbose: true } }) ], });`; note(`Add this to your ${pc.cyan("astro.config.mjs")}: ${pc.dim(config)}`, "Configuration"); const setupDirectories = await confirm({ message: "Create input directories now?", initialValue: true }); if (setupDirectories && !isCancel(setupDirectories)) { const s = spinner(); s.start("Creating directories..."); try { const { mkdir } = await import("fs/promises"); for (const dir of inputDirsList) { await mkdir(dir, { recursive: true }); } s.stop("Directories created!"); note( `Created directories: ${inputDirsList.map((d) => `\u2022 ${pc.green(d)}`).join("\n")} Drop your documents into these folders and they'll be automatically converted!`, "Next Steps" ); } catch (error) { s.stop("Failed to create directories"); note(`${pc.red("\u274C Error:")} Could not create directories: ${error}`, "Error"); } } outro(`${pc.green("\u{1F389} Setup complete!")} Your Starlight Document Converter is ready to use.`); } program.name("starlight-convert").description("\u{1F31F} Beautiful document converter for Starlight").version("1.7.0").action(async () => { await interactiveConvert(); }); program.command("convert").description("Convert documents interactively").action(async () => { await interactiveConvert(); }); program.command("batch").description("Convert documents in batch mode").argument("<input>", "Input file or directory to convert").option("-o, --output <dir>", "Output directory (auto-detected if not specified)").option("--no-preserve", "Don't preserve directory structure", false).option("--no-titles", "Don't auto-generate titles", false).option("--no-descriptions", "Don't auto-generate descriptions", false).option("--timestamps", "Add lastUpdated timestamps", false).option("--category <category>", "Default category for documents", "documentation").option("--fix-links", "Fix internal links during conversion", false).option("--process-images", "Process and copy images during conversion", false).option("--generate-toc", "Generate table of contents", false).option("--validate", "Validate content after conversion", false).option("-v, --verbose", "Show detailed output", false).option("--dry-run", "Preview changes without writing files", false).action(async (input, options) => { intro(pc.bgCyan(pc.black(" Batch Convert "))); const inputPath = resolve2(input); const inputType = detectInputType(inputPath); const outputDir = getOutputDirectory(options.output); if (inputType === "not-found") { note(`${pc.red("\u274C Error:")} Input path "${input}" does not exist`, "Error"); process.exit(1); } const smartDefaults = getSmartDefaults(); if (smartDefaults.isStarlightProject) { note(`\u2705 Detected Starlight project, using: ${pc.cyan(outputDir)}`, "Smart Detection"); } else { note(`\u26A0\uFE0F Using fallback output directory: ${pc.cyan(outputDir)}`, "Fallback Mode"); } const s = spinner(); s.start(`${options.dryRun ? "Previewing" : "Converting"} documents...`); try { const converter = new DocumentConverter({ outputDir, preserveStructure: options.preserve, generateTitles: options.titles, generateDescriptions: options.descriptions, addTimestamps: options.timestamps, defaultCategory: options.category, fixLinks: options.fixLinks, processImages: options.processImages, generateToc: options.generateToc, validateContent: options.validate, verbose: options.verbose, dryRun: options.dryRun }); const results = inputType === "directory" ? await converter.convertDirectory(inputPath) : [await converter.convertFile(inputPath)]; s.stop(`${options.dryRun ? "Preview" : "Conversion"} completed!`); const stats = getFormattedStats(results); note( `${pc.green("\u2705 Successful:")} ${stats.successful} files ` + (stats.skipped > 0 ? `${pc.yellow("\u23ED\uFE0F Skipped:")} ${stats.skipped} files ` : "") + (stats.failed > 0 ? `${pc.red("\u274C Failed:")} ${stats.failed} files ` : "") + (options.dryRun ? pc.yellow("\u{1F9EA} Dry run - no files were modified") : ""), "Results" ); converter.printStats(); outro("\u{1F389} Batch conversion completed!"); } catch (error) { s.stop("Conversion failed"); note(`${pc.red("\u274C Error:")} ${error}`, "Conversion failed"); process.exit(1); } }); program.command("setup").description("Interactive project setup wizard").action(async () => { await configurationWizard(); }); program.command("watch").description("Watch directory for changes").argument("<input>", "Directory to watch").option("-o, --output <dir>", "Output directory", "src/content/docs").option("-v, --verbose", "Show detailed output").action(async (input, options) => { intro(pc.bgGreen(pc.black(" File Watcher "))); const inputPath = resolve2(input); if (detectInputType(inputPath) !== "directory") { note(`${pc.red("\u274C Error:")} Watch requires a directory path`, "Error"); process.exit(1); } note(`Watching ${pc.cyan(relative(process.cwd(), inputPath))} for changes...`, "Monitoring"); const { watch } = await import("fs"); const converter = new DocumentConverter({ outputDir: options.output, verbose: options.verbose }); const watcher = watch(inputPath, { recursive: true }, async (eventType, filename) => { if (!filename || eventType !== "change") return; const ext = filename.split(".").pop()?.toLowerCase(); if (["docx", "doc", "txt", "html", "htm", "md", "rtf"].includes(ext || "")) { const s = spinner(); s.start(`Converting ${filename}...`); try { await converter.convertFile(resolve2(inputPath, filename)); s.stop(`${pc.green("\u2705")} Converted: ${filename}`); } catch (error) { s.stop(`${pc.red("\u274C")} Failed: ${filename}`); console.log(` ${pc.red("Error:")} ${error}`); } } }); process.on("SIGINT", () => { watcher.close(); outro("\u{1F44B} Stopped watching"); process.exit(0); }); }); program.command("repair").description("Repair frontmatter and content issues in existing Starlight files").argument("<input>", "Input file or directory to repair").option("-o, --output <dir>", "Output directory (defaults to input location)").option("--fix-links", "Fix internal links and references", false).option("--process-images", "Process and copy images to assets directory", false).option("--generate-toc", "Generate table of contents", false).option("--dry-run", "Preview repairs without making changes", false).option("-v, --verbose", "Show detailed output", false).action(async (input, options) => { intro(pc.bgYellow(pc.black(" Content Repair "))); const inputPath = resolve2(input); const inputType = detectInputType(inputPath); if (inputType === "not-found") { note(`${pc.red("\u274C Error:")} Input path "${input}" does not exist`, "Error"); process.exit(1); } try { const { repairSingleFile, repairDirectory, showRepairResults, createSpinner } = await import("./cli-commands-IAGYE2JW.js"); const repairOptions = { output: options.output || (inputType === "directory" ? inputPath : dirname(inputPath)), fixLinks: options.fixLinks, processImages: options.processImages, generateToc: options.generateToc, dryRun: options.dryRun, verbose: options.verbose }; const s = createSpinner("content repair", options.dryRun); if (inputType === "directory") { const stats = await repairDirectory(inputPath, repairOptions); s.stop(`${options.dryRun ? "Analysis" : "Repair"} completed!`); note(showRepairResults(stats, repairOptions), "Results"); } else { const result = await repairSingleFile(inputPath, repairOptions); s.stop(`${options.dryRun ? "Analysis" : "Repair"} completed!`); const stats = { filesProcessed: 1, totalRepaired: result.repaired ? 1 : 0, totalIssues: result.issues.length }; if (repairOptions.verbose && result.repaired) { console.log(` ${pc.cyan(basename(inputPath))}:`); result.issues.forEach((issue) => console.log(` \u2022 ${issue}`)); } note(showRepairResults(stats, repairOptions), "Results"); } outro(`\u{1F389} Content repair ${options.dryRun ? "analysis" : "completed"}!`); } catch (error) { note(`${pc.red("\u274C Error:")} ${error}`, "Repair failed"); process.exit(1); } }); program.command("validate").description("Validate Starlight content structure and quality").argument("<input>", "Input file or directory to validate").option("--fix-issues", "Automatically fix issues where possible", false).option("--show-details", "Show detailed validation results for all files", false).option("-v, --verbose", "Show verbose output with issue details", false).action(async (input, options) => { intro(pc.bgBlue(pc.black(" Content Validation "))); const inputPath = resolve2(input); const inputType = detectInputType(inputPath); if (inputType === "not-found") { note(`${pc.red("\u274C Error:")} Input path "${input}" does not exist`, "Error"); process.exit(1); } try { const { validateSingleFile, validateDirectory, showValidationResults, createSpinner } = await import("./cli-commands-IAGYE2JW.js"); const validateOptions = { fixIssues: options.fixIssues, showDetails: options.showDetails, verbose: options.verbose }; const s = createSpinner("content validation"); let stats; if (inputType === "directory") { stats = await validateDirectory(inputPath, validateOptions); } else { const result = await validateSingleFile(inputPath, validateOptions); stats = { totalFiles: 1, validFiles: result.valid ? 1 : 0, issueCount: result.validation.issues.filter((i) => i.type === "error").length, allIssues: result.valid ? [] : [{ file: basename(inputPath), validation: result.validation }] }; console.log( ` ${pc.cyan(basename(inputPath))}: ${result.valid ? pc.green("\u2705 Valid") : pc.red("\u274C Issues")}` ); if (result.validation.score) { const scoreColor = result.validation.score.overall === "good" ? pc.green : result.validation.score.overall === "fair" ? pc.yellow : pc.red; console.log(`Quality: ${scoreColor(result.validation.score.overall.toUpperCase())}`); } if (!result.valid) { result.validation.issues.forEach((issue) => { const icon = issue.type === "error" ? "\u274C" : issue.type === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F"; console.log(` ${icon} ${issue.message}`); }); } } s.stop("Validation completed!"); note(showValidationResults(stats), "Validation Summary"); if (options.fixIssues && stats.allIssues.length > 0) { const shouldFix = await confirm({ message: `Fix ${stats.allIssues.length} files with issues?`, initialValue: true }); if (shouldFix && !isCancel(shouldFix)) { note("Running automatic repair...", "Auto-fix"); const { repairDirectory, repairSingleFile } = await import("./cli-commands-IAGYE2JW.js"); const repairOptions = { output: inputType === "directory" ? inputPath : dirname(inputPath), fixLinks: false, processImages: false, generateToc: false, dryRun: false, verbose: false }; const repairSpinner = createSpinner("auto-repair"); if (inputType === "directory") { await repairDirectory(inputPath, repairOptions); } else { await repairSingleFile(inputPath, repairOptions); } repairSpinner.stop("Auto-repair completed!"); note( "Issues have been automatically fixed. Re-run validation to verify.", "Auto-fix Complete" ); } } const successRate = stats.totalFiles > 0 ? Math.round(stats.validFiles / stats.totalFiles * 100) : 100; outro( `\u{1F389} Validation completed! ${successRate >= 90 ? "Excellent quality!" : successRate >= 70 ? "Good quality." : "Consider improvements."}` ); } catch (error) { note(`${pc.red("\u274C Error:")} ${error}`, "Validation failed"); process.exit(1); } }); program.addHelpText("before", createBrandHeader("Starlight Document Converter", "1.7.0")); program.addHelpText( "after", formatHelpSection("Basic Commands", [ { name: "starlight-convert", description: "Interactive mode with smart detection and guided setup" }, { name: "starlight-convert setup", description: "Project setup wizard for new Astro Starlight projects" }, { name: "starlight-convert batch <input>", description: "Convert multiple files or entire directories" }, { name: "starlight-convert watch <input>", description: "Watch directory for changes and auto-convert" } ]) + formatHelpSection("Content Management", [ { name: "starlight-convert repair <input>", description: "Fix frontmatter, links, images, and content issues" }, { name: "starlight-convert validate <input>", description: "Validate content structure and quality scoring" } ]) + formatHelpSection("Common Options", [ { name: "--dry-run", description: "Preview changes without modifying any files" }, { name: "-v, --verbose", description: "Show detailed output and progress information" }, { name: "-o, --output <dir>", description: "Specify output directory for converted files" } ]) + formatHelpSection("Examples", [ { name: "Batch Conversion", description: "starlight-convert batch docs/ --generate-toc --fix-links" }, { name: "Content Repair", description: "starlight-convert repair content/ --process-images --dry-run" }, { name: "Quality Check", description: "starlight-convert validate docs/ --show-details --verbose" } ]) + ` ${boxes.info("For complete documentation and advanced usage examples, visit:\nhttps://github.com/entro314-labs/starlight-document-converter", "Documentation")} ` ); if (process.argv.length === 2) { interactiveConvert(); } else { program.parse(); } //# sourceMappingURL=cli.js.map