UNPKG

legal-markdown-js

Version:

Node.js implementation of LegalMarkdown for processing legal documents with markdown and YAML - Complete feature parity with Ruby version

233 lines • 10.1 kB
#!/usr/bin/env node /** * Interactive CLI for Legal Markdown Processing * * This module provides an interactive command-line interface for Legal Markdown * processing, guiding users through file selection, format options, and processing * configuration with intuitive prompts. * * Features: * - Interactive file selection with recursive directory scanning * - Multiple output format selection (HTML, PDF, Markdown, Metadata) * - Conditional processing options based on selected formats * - CSS file selection for HTML/PDF styling * - Configuration confirmation and summary display * - Integration with existing CliService for processing * * @example * ```bash * # Launch interactive mode * legal-md-ui * ``` */ import { Command } from 'commander'; import chalk from 'chalk'; import { selectInputFile } from './prompts/file-selector.js'; import { selectOutputFormats } from './prompts/output-format.js'; import { promptProcessingOptions } from './prompts/processing-options.js'; import { selectCssFile } from './prompts/css-selector.js'; import { promptOutputFilename } from './prompts/filename.js'; import { promptArchiveOptions } from './prompts/archive-options.js'; import { confirmConfiguration } from './prompts/confirmation.js'; import { handleFirstTimeUserExperience } from './prompts/ftux-handler.js'; import { InteractiveService } from './service.js'; import { formatSuccessMessage, formatErrorMessage } from './utils/format-helpers.js'; import { readFileSync } from '../../utils/index.js'; import { parseYamlFrontMatter } from '../../core/parsers/yaml-parser.js'; import { extractForceCommands, parseForceCommands, applyForceCommands, } from '../../core/parsers/force-commands-parser.js'; import { CliService } from '../service.js'; import * as path from 'path'; import { RESOLVED_PATHS } from '../../constants/index.js'; /** * Run FTUX (First-Time User Experience) mode * * Provides a guided onboarding experience for new users with options to: * - Set up configuration files * - Try demo examples * - Get help and tutorials * - Browse for files manually * * @returns Promise that resolves when FTUX completes * @throws Error when FTUX fails or user cancels */ async function runFtuxMode() { try { console.log(chalk.bold.blue('\n🌟 Legal Markdown - First-Time User Experience\n')); console.log(chalk.gray("Welcome! Let's get you started with Legal Markdown processing.\n")); // Run the FTUX flow const selectedFile = await handleFirstTimeUserExperience(); // If a file was selected from FTUX, continue with normal processing if (selectedFile) { console.log(chalk.green(`\nāœ… Selected: ${selectedFile}`)); console.log(chalk.gray('Continuing with normal processing flow...\n')); // Continue with the normal interactive flow starting from step 2 await continueInteractiveFlow(selectedFile); } } catch (error) { if (error instanceof Error && error.message.includes('User force closed')) { console.log(chalk.yellow('\nšŸ‘‹ Thanks for trying Legal Markdown!\n')); return; } console.log(formatErrorMessage(error instanceof Error ? error.message : String(error))); process.exit(1); } } /** * Continue interactive flow from a selected file (used after FTUX) * * @param inputFile - The file selected from FTUX */ async function continueInteractiveFlow(inputFile) { // Check for force commands in the file's frontmatter try { const content = readFileSync(inputFile); const { metadata } = parseYamlFrontMatter(content); const forceCommandsStr = extractForceCommands(metadata); if (forceCommandsStr) { console.log(chalk.cyan('\nšŸ“‹ Found force commands in document. Executing automatic configuration...\n')); // Parse the force commands const forceCommands = parseForceCommands(forceCommandsStr, metadata); if (forceCommands) { // Apply force commands using CliService directly const baseOptions = { debug: false, yamlOnly: false, noHeaders: false, noClauses: false, noReferences: false, noImports: false, noMixins: false, noReset: false, noIndent: false, throwOnYamlError: false, basePath: path.dirname(inputFile), }; // Apply force commands to base options const options = applyForceCommands(baseOptions, forceCommands); // Determine output path const outputBaseName = forceCommands.output || path.basename(inputFile, path.extname(inputFile)); const outputDir = forceCommands.outputPath || RESOLVED_PATHS.DEFAULT_OUTPUT_DIR; // Process file with forced configuration - silent to avoid duplicate success messages const cliService = new CliService({ ...options, silent: true, }); // Determine output file path - add extension to prevent path.basename from truncating name // CliService will replace this extension with the appropriate ones for each format const outputFile = path.join(outputDir, `${outputBaseName}.pdf`); // Process once - CliService will handle all requested formats and highlight versions automatically await cliService.processFile(inputFile, outputFile); // Build list of expected output files for display const outputFiles = []; if (forceCommands.pdf) { outputFiles.push(path.join(outputDir, `${outputBaseName}.pdf`)); if (forceCommands.highlight) { outputFiles.push(path.join(outputDir, `${outputBaseName}.HIGHLIGHT.pdf`)); } } if (forceCommands.html) { outputFiles.push(path.join(outputDir, `${outputBaseName}.html`)); if (forceCommands.highlight) { outputFiles.push(path.join(outputDir, `${outputBaseName}.HIGHLIGHT.html`)); } } // Show success message console.log(formatSuccessMessage(outputFiles)); return; } } } catch (error) { // If there's any error parsing force commands, continue with normal flow console.log(chalk.gray('No force commands found or error parsing. Continuing with interactive mode...\n')); } // Normal interactive flow // Step 2: Select output formats const outputFormats = await selectOutputFormats(); // Step 3: Configure processing options const processingOptions = await promptProcessingOptions(outputFormats); // Step 4: Select CSS file (if needed) const cssFile = await selectCssFile(outputFormats); // Step 5: Enter output filename const outputFilename = await promptOutputFilename(inputFile); // Step 6: Configure archive options const archiveResult = await promptArchiveOptions(); const archiveOptions = { enabled: archiveResult.enableArchiving, directory: archiveResult.archiveDirectory, }; // Build configuration const config = { inputFile, outputFilename, outputFormats, processingOptions, archiveOptions, cssFile, }; // Step 7: Confirm configuration const confirmed = await confirmConfiguration(config); if (!confirmed) { console.log(chalk.yellow('\nāŒ Operation cancelled.\n')); return; } // Step 8: Process files const service = new InteractiveService(config); const result = await service.processFile(inputFile); // Step 9: Show results console.log(formatSuccessMessage(result.outputFiles, result.archiveResult)); } /** * Main interactive flow that guides users through the complete Legal Markdown processing workflow * * This function orchestrates the entire interactive experience by: * 1. Displaying welcome message and scanning for input files * 2. Prompting for file selection from available documents * 3. Collecting output format preferences (PDF, HTML, Markdown, Metadata) * 4. Gathering processing options based on selected formats * 5. Optional CSS file selection for styling * 6. Output filename specification * 7. Archive options configuration * 8. Configuration confirmation and processing execution * * @returns Promise that resolves when the interactive flow completes * @throws Error when processing fails or user cancels operation */ async function runInteractiveMode() { try { console.log(chalk.bold.blue('\nšŸŽÆ Legal Markdown Interactive CLI\n')); console.log(chalk.gray('Follow the prompts to configure your document processing.\n')); // Step 1: Select input file const inputFile = await selectInputFile(); // Continue with the rest of the flow await continueInteractiveFlow(inputFile); } catch (error) { if (error instanceof Error && error.message.includes('User force closed')) { console.log(chalk.yellow('\nšŸ‘‹ Goodbye!\n')); return; } console.log(formatErrorMessage(error instanceof Error ? error.message : String(error))); process.exit(1); } } // Create program const program = new Command(); // Setup CLI information program .name('legal-md-ui') .description('Interactive CLI for Legal Markdown document processing') .version('0.1.0') .option('--ftux', 'Launch First-Time User Experience (setup wizard)') .action(async (options) => { if (options.ftux) { await runFtuxMode(); } else { await runInteractiveMode(); } }); // Parse command line arguments program.parse(); //# sourceMappingURL=index.js.map