UNPKG

c4dslbuilder

Version:

A CLI tool designed to compile a folder structure of markdowns and mermaid files into a site, pdf, single file markdown or a collection of markdowns with links - inspired by c4builder

263 lines (262 loc) 10.9 kB
import Configstore from 'configstore'; import inquirer from 'inquirer'; import chalk from 'chalk'; import path from 'path'; import { CliLogger } from './cli-logger.js'; import * as Constants from '../types/constants.js'; export class ConfigManager { logger; constructor(logger = new CliLogger(ConfigManager.name)) { this.logger = logger; } openConfigStore() { let config; try { config = new Configstore(process.cwd().split(path.sep).splice(1).join('_'), {}, { configPath: path.join(process.cwd(), Constants.CONFIG_FILENAME), }); } catch (error) { this.logger.error('Error accessing config store.', error); return null; } return config; } getStoredValue(key) { const config = this.openConfigStore(); if (!config) { this.logger.error('Failed to open config store.'); return undefined; } try { const configValue = config.get(key); if (configValue === undefined) { this.logger.warn(`Configuration key ${key} not found.`); return undefined; } return configValue; } catch (error) { this.logger.error(`Error retrieving config value for ${key}.`, error); return undefined; } } printConfigValue(title, value) { this.logger.log(`${title.padEnd(40)} : ${value}`); } /* c8 ignore next 6 -- @preserve -- c8 stubbornly refuses to acknowledge we're testing this */ boolValueToString(value) { if (value) { return 'Yes'; } return 'No'; } numValueToString(value) { return value.toString(); } getPrintValue(value) { return value?.trim?.() && value !== 'undefined' ? chalk.green(value) : chalk.red('Not set'); } isValidProjectName(input) { return (/^[\w\s-]{2,}$/.test(input.trim()) || 'Project name must be at least 2 characters and only contain letters, numbers, spaces, hyphens, or underscores.'); } isValidUrl(input) { if (!input?.trim()) return true; try { new URL(input); return true; } catch { return 'Please enter a valid URL.'; } } getStrConfigValue(key) { const configValue = this.getStoredValue(key); if (typeof configValue === 'string') { return configValue; } this.logger.info(`Expected string for ${key}, but got ${typeof configValue}`); return ''; } getBoolConfigValue(key) { const configValue = this.getStoredValue(key); if (typeof configValue === 'boolean') { return configValue; } // cater for string representations of booleans if (typeof configValue === 'string') { if (configValue.toLowerCase() === 'true' || configValue.toLowerCase() === 'yes') { return true; } if (configValue.toLowerCase() === 'false' || configValue.toLowerCase() === 'no') { return false; } } this.logger.info(`Expected boolean for ${key}, but got ${typeof configValue}`); return false; } getNumConfigValue(key) { const configValue = this.getStoredValue(key); const num = typeof configValue === 'number' ? configValue : Number(configValue); if (Number.isNaN(num)) { this.logger.info(`Expected number for ${key}, but got ${typeof configValue}`); return 0; } return num; } setConfigValue(key, value) { const config = this.openConfigStore(); if (!config) { this.logger.error('Failed to open config store.'); return; } config.set(key, value); } deleteConfig(key) { const config = this.openConfigStore(); if (!config) { this.logger.error('Failed to open config store.'); return; } config.delete(key); } resetConfig() { const config = this.openConfigStore(); if (!config) { this.logger.error('Failed to open config store.'); return; } const projectName = this.getStrConfigValue('projectName'); config.clear(); config.set('projectName', projectName); this.logger.log(chalk.yellow(`✅ Configuration has been reset for project ${projectName}.`)); } listConfig() { this.logger.log(chalk.cyan('Current Configuration\n')); this.printConfigValue('Project name', this.getPrintValue(this.getStrConfigValue('projectName'))); this.printConfigValue('Homepage Name', this.getPrintValue(this.getStrConfigValue('homepageName'))); this.printConfigValue('Root Folder', this.getPrintValue(this.getStrConfigValue('rootFolder'))); this.printConfigValue('Destination Folder', this.getPrintValue(this.getStrConfigValue('distFolder'))); this.printConfigValue('Structurizr DSL CLI to use', this.getPrintValue(this.getStrConfigValue('dslCli'))); this.printConfigValue('Where Structurizr starts looking for diagrams to extract', this.getPrintValue(this.getStrConfigValue('workspaceDsl'))); this.printConfigValue('Embed Mermaid diagrams?', this.getPrintValue(this.boolValueToString(this.getBoolConfigValue('embedMermaidDiagrams')))); this.printConfigValue('PDF CSS', this.getPrintValue(this.getStrConfigValue('pdfCss'))); this.printConfigValue('Port Number', this.getPrintValue(this.numValueToString(this.getNumConfigValue('servePort')))); this.printConfigValue('Repo URL', this.getPrintValue(this.getStrConfigValue('repoName'))); this.printConfigValue('Docsify stylesheet', this.getPrintValue(this.getStrConfigValue('webTheme'))); this.printConfigValue('Enable website search', this.getPrintValue(this.boolValueToString(this.getBoolConfigValue('webSearch')))); this.printConfigValue('Docsify template', this.getPrintValue(this.getStrConfigValue('docsifyTemplate'))); } async setConfig() { this.logger.log(chalk.cyan('Configure your project settings:\n')); const answers = await inquirer.prompt([ { type: 'input', name: 'projectName', message: 'Project name:', default: this.getStrConfigValue('projectName'), validate: this.isValidProjectName.bind(this), }, { type: 'input', name: 'homepageName', message: 'Homepage name:', default: this.getStrConfigValue('homepageName') || 'Overview', }, { type: 'input', name: 'rootFolder', message: 'Root folder:', default: this.getStrConfigValue('rootFolder') || Constants.DEFAULT_ROOT, }, { type: 'input', name: 'distFolder', message: 'Destination folder:', default: this.getStrConfigValue('distFolder') || Constants.DEFAULT_DIST, }, { type: 'confirm', name: 'embedMermaidDiagrams', message: 'Embed Mermaid diagrams? (Set this to NO / false to replace mermaid diagrams with a link to an image)', default: this.getBoolConfigValue('embedMermaidDiagrams') || true, }, { type: 'list', name: 'dslCli', message: 'Which Structurizr CLI would you prefer to use:', choices: ['structurizr-cli', 'docker'], default: this.getStrConfigValue('dslCli') || 'structurizr-cli', }, { type: 'input', name: 'workspaceDsl', message: 'Where should the Structurizr CLI start looking when exporting diagrams:', default: this.getStrConfigValue('workspaceDsl') || 'workspace.dsl', }, { type: 'input', name: 'pdfCss', message: 'PDF CSS file path:', default: this.getStrConfigValue('pdfCss') || '_resources/pdf.css', }, { type: 'number', name: 'servePort', message: 'Port number:', default: this.getNumConfigValue('servePort') || 3030, }, { type: 'input', name: 'repoName', message: 'Repository URL:', default: this.getStrConfigValue('repoName') || '', validate: this.isValidUrl.bind(this), }, { type: 'input', name: 'webTheme', message: 'Website Docsify theme (URL):', default: this.getStrConfigValue('webTheme') || 'https://unpkg.com/docsify/lib/themes/vue.css', validate: this.isValidUrl.bind(this), }, { type: 'confirm', name: 'webSearch', message: 'Enable website search?', default: this.getBoolConfigValue('webSearch') || true, }, { type: 'input', name: 'docsifyTemplate', message: 'Local path to a custom Docsify template:', default: this.getStrConfigValue('docsifyTemplate') || '', }, ]); Object.entries(answers).forEach(([key, value]) => { this.setConfigValue(key, String(value)); }); this.logger.log(chalk.green('\n✅ Configuration updated successfully.')); } async getAllStoredConfig() { return { projectName: this.getStrConfigValue('projectName'), homepageName: this.getStrConfigValue('homepageName'), rootFolder: this.getStrConfigValue('rootFolder'), distFolder: this.getStrConfigValue('distFolder'), dslCli: this.getStrConfigValue('dslCli') === 'docker' ? 'docker' : 'structurizr-cli', workspaceDsl: this.getStrConfigValue('workspaceDsl'), embedMermaidDiagrams: this.getBoolConfigValue('embedMermaidDiagrams'), pdfCss: this.getStrConfigValue('pdfCss'), servePort: this.getNumConfigValue('servePort'), repoName: this.getStrConfigValue('repoName'), webTheme: this.getStrConfigValue('webTheme'), webSearch: this.getBoolConfigValue('webSearch'), docsifyTemplate: this.getStrConfigValue('docsifyTemplate'), serve: this.getBoolConfigValue('serve'), generateWebsite: this.getBoolConfigValue('generateWebsite'), }; } }