UNPKG

@elsikora/setup-wizard

Version:

Setup Wizard - CLI scaffolding utility

257 lines (254 loc) 13.3 kB
#!/usr/bin/env node import { EModule } from '../../domain/enum/module.enum.js'; import { EPackageJsonDependencyType } from '../../domain/enum/package-json-dependency-type.enum.js'; import { NodeCommandService } from '../../infrastructure/service/node-command.service.js'; import { TYPESCRIPT_CONFIG_FILE_NAME } from '../constant/typescript/config-file-name.constant.js'; import { TYPESCRIPT_CONFIG } from '../constant/typescript/config.constant.js'; import { TYPESCRIPT_CONFIG_CORE_DEPENDENCIES } from '../constant/typescript/core-dependencies.constant.js'; import { TYPESCRIPT_CONFIG_FILE_NAMES } from '../constant/typescript/file-names.constant.js'; import { TYPESCRIPT_CONFIG_MESSAGES } from '../constant/typescript/messages.constant.js'; import { TYPESCRIPT_CONFIG_SCRIPTS } from '../constant/typescript/scripts.constant.js'; import { TYPESCRIPT_CONFIG_SUMMARY } from '../constant/typescript/summary.constant.js'; import { PackageJsonService } from './package-json.service.js'; /** * Service for setting up and managing TypeScript configuration. * Provides functionality to configure TypeScript compiler options, * clean architecture paths, and decorator support. */ class TypescriptModuleService { /** CLI interface service for user interaction */ CLI_INTERFACE_SERVICE; /** Command service for executing shell commands */ COMMAND_SERVICE; /** Configuration service for managing app configuration */ CONFIG_SERVICE; /** File system service for file operations */ FILE_SYSTEM_SERVICE; /** Service for managing package.json */ PACKAGE_JSON_SERVICE; /** Cached TypeScript configuration */ config = null; /** * Initializes a new instance of the TypescriptModuleService. * @param cliInterfaceService - Service for CLI user interactions * @param fileSystemService - Service for file system operations * @param configService - Service for managing app configuration */ constructor(cliInterfaceService, fileSystemService, configService) { this.CLI_INTERFACE_SERVICE = cliInterfaceService; this.FILE_SYSTEM_SERVICE = fileSystemService; this.COMMAND_SERVICE = new NodeCommandService(cliInterfaceService); this.PACKAGE_JSON_SERVICE = new PackageJsonService(fileSystemService, this.COMMAND_SERVICE); this.CONFIG_SERVICE = configService; } /** * Handles existing TypeScript setup. * Checks for existing configuration files and asks if user wants to remove them. * @returns Promise resolving to true if setup should proceed, false otherwise */ async handleExistingSetup() { const existingFiles = await this.findExistingConfigFiles(); if (existingFiles.length > 0) { const messageLines = [TYPESCRIPT_CONFIG_MESSAGES.existingFilesDetected]; messageLines.push(""); for (const file of existingFiles) { messageLines.push(`- ${file}`); } messageLines.push("", TYPESCRIPT_CONFIG_MESSAGES.deleteFilesQuestion); const shouldDelete = await this.CLI_INTERFACE_SERVICE.confirm(messageLines.join("\n"), true); if (shouldDelete) { await Promise.all(existingFiles.map((file) => this.FILE_SYSTEM_SERVICE.deleteFile(file))); } else { this.CLI_INTERFACE_SERVICE.warn(TYPESCRIPT_CONFIG_MESSAGES.existingFilesAborted); return false; } } return true; } /** * Installs and configures TypeScript. * Guides the user through setting up TypeScript compiler options. * @returns Promise resolving to the module setup result */ async install() { try { this.config = await this.CONFIG_SERVICE.getModuleConfig(EModule.TYPESCRIPT); if (!(await this.shouldInstall())) { return { wasInstalled: false }; } if (!(await this.handleExistingSetup())) { return { wasInstalled: false }; } const setupParameters = await this.setupTypescript(); return { customProperties: setupParameters, wasInstalled: true, }; } catch (error) { this.CLI_INTERFACE_SERVICE.handleError(TYPESCRIPT_CONFIG_MESSAGES.failedSetupError, error); throw error; } } /** * Determines if TypeScript should be installed. * Asks the user if they want to set up TypeScript configuration. * Uses the saved config value as default if it exists. * @returns Promise resolving to true if the module should be installed, false otherwise */ async shouldInstall() { try { return await this.CLI_INTERFACE_SERVICE.confirm(TYPESCRIPT_CONFIG_MESSAGES.confirmSetup, await this.CONFIG_SERVICE.isModuleEnabled(EModule.TYPESCRIPT)); } catch (error) { this.CLI_INTERFACE_SERVICE.handleError(TYPESCRIPT_CONFIG_MESSAGES.failedConfirmation, error); return false; } } /** * Creates TypeScript configuration file. * Generates the tsconfig.json file with user-specified options. * @param baseUrl - The base URL for module resolution * @param rootDirectory - The root directory of source files * @param outputDirectory - The output directory for compiled files * @param isCleanArchitectureEnabled - Whether to enable clean architecture paths * @param isDecoratorsEnabled - Whether to enable decorator support */ async createConfig(baseUrl, rootDirectory, outputDirectory, isCleanArchitectureEnabled, isDecoratorsEnabled) { await this.FILE_SYSTEM_SERVICE.writeFile(TYPESCRIPT_CONFIG_FILE_NAME, TYPESCRIPT_CONFIG.template(baseUrl, rootDirectory, outputDirectory, isCleanArchitectureEnabled, isDecoratorsEnabled), "utf8"); } /** * Displays a summary of the TypeScript setup results. * Lists configuration options, generated scripts, and files. * @param baseUrl - The configured base URL * @param rootDirectory - The configured root directory * @param outputDirectory - The configured output directory * @param isCleanArchitectureEnabled - Whether clean architecture was enabled * @param isDecoratorsEnabled - Whether decorators were enabled */ displaySetupSummary(baseUrl, rootDirectory, outputDirectory, isCleanArchitectureEnabled, isDecoratorsEnabled) { const summary = [TYPESCRIPT_CONFIG_MESSAGES.configurationCreated, "", TYPESCRIPT_CONFIG_MESSAGES.configurationOptionsLabel, TYPESCRIPT_CONFIG_MESSAGES.summaryBaseUrl(baseUrl), TYPESCRIPT_CONFIG_MESSAGES.summaryRootDir(rootDirectory), TYPESCRIPT_CONFIG_MESSAGES.summaryOutputDir(outputDirectory)]; if (isCleanArchitectureEnabled) { summary.push(TYPESCRIPT_CONFIG_MESSAGES.cleanArchitectureEnabled); } if (isDecoratorsEnabled) { summary.push(TYPESCRIPT_CONFIG_MESSAGES.decoratorsEnabled); } summary.push("", TYPESCRIPT_CONFIG_MESSAGES.generatedScriptsLabel, TYPESCRIPT_CONFIG_MESSAGES.tscBuildDescription, TYPESCRIPT_CONFIG_MESSAGES.tscCheckDescription, "", TYPESCRIPT_CONFIG_MESSAGES.generatedFilesLabel, `• ${TYPESCRIPT_CONFIG_FILE_NAME}`); this.CLI_INTERFACE_SERVICE.note(TYPESCRIPT_CONFIG_MESSAGES.setupCompleteTitle, summary.join("\n")); } /** * Finds existing TypeScript configuration files. * @returns Promise resolving to an array of file paths for existing configuration files */ async findExistingConfigFiles() { const existingFiles = []; for (const file of TYPESCRIPT_CONFIG_FILE_NAMES) { if (await this.FILE_SYSTEM_SERVICE.isPathExists(file)) { existingFiles.push(file); } } return existingFiles; } /** * Prompts the user for the base URL configuration. * @returns Promise resolving to the base URL */ async getBaseUrl() { const initialValue = this.config?.baseUrl ?? TYPESCRIPT_CONFIG_SUMMARY.baseUrlDefault; return await this.CLI_INTERFACE_SERVICE.text(TYPESCRIPT_CONFIG_MESSAGES.baseUrlPrompt, TYPESCRIPT_CONFIG_SUMMARY.baseUrlDefault, initialValue, (value) => { if (!value) { return TYPESCRIPT_CONFIG_MESSAGES.baseUrlRequired; } return !value.startsWith("./") && !value.startsWith("../") && value !== "." ? TYPESCRIPT_CONFIG_MESSAGES.baseUrlValidation : undefined; }); } /** * Prompts the user for the output directory configuration. * @returns Promise resolving to the output directory */ async getOutDir() { const initialValue = this.config?.outputDirectory ?? TYPESCRIPT_CONFIG_SUMMARY.outDirDefault; return await this.CLI_INTERFACE_SERVICE.text(TYPESCRIPT_CONFIG_MESSAGES.outDirPrompt, TYPESCRIPT_CONFIG_SUMMARY.outDirDefault, initialValue, (value) => { if (!value) { return TYPESCRIPT_CONFIG_MESSAGES.outDirRequired; } return !value.startsWith("./") && !value.startsWith("../") && value !== "." ? TYPESCRIPT_CONFIG_MESSAGES.outDirValidation : undefined; }); } /** * Prompts the user for the root directory configuration. * @returns Promise resolving to the root directory */ async getRootDir() { const initialValue = this.config?.rootDirectory ?? TYPESCRIPT_CONFIG_SUMMARY.rootDirDefault; return await this.CLI_INTERFACE_SERVICE.text(TYPESCRIPT_CONFIG_MESSAGES.rootDirPrompt, TYPESCRIPT_CONFIG_SUMMARY.rootDirDefault, initialValue, (value) => { if (!value) { return TYPESCRIPT_CONFIG_MESSAGES.rootDirRequired; } return !value.startsWith("./") && !value.startsWith("../") && value !== "." ? TYPESCRIPT_CONFIG_MESSAGES.rootDirValidation : undefined; }); } /** * Prompts the user if they want to use clean architecture. * @returns Promise resolving to true if clean architecture should be enabled */ async isCleanArchitectureEnabled() { const isConfirmedByDefault = this.config?.isCleanArchitectureEnabled === true; this.CLI_INTERFACE_SERVICE.info(TYPESCRIPT_CONFIG_MESSAGES.cleanArchitectureInfo); return await this.CLI_INTERFACE_SERVICE.confirm(TYPESCRIPT_CONFIG_MESSAGES.confirmCleanArchitecture, isConfirmedByDefault); } /** * Prompts the user if they want to enable decorators. * @returns Promise resolving to true if decorators should be enabled */ async isDecoratorsEnabled() { const isConfirmedByDefault = this.config?.isDecoratorsEnabled === true; return await this.CLI_INTERFACE_SERVICE.confirm(TYPESCRIPT_CONFIG_MESSAGES.confirmDecorators, isConfirmedByDefault); } /** * Sets up npm scripts for TypeScript. * Adds scripts for building and type checking. */ async setupScripts() { await this.PACKAGE_JSON_SERVICE.addScript(TYPESCRIPT_CONFIG_SCRIPTS.buildTypes.name, TYPESCRIPT_CONFIG_SCRIPTS.buildTypes.command); await this.PACKAGE_JSON_SERVICE.addScript(TYPESCRIPT_CONFIG_SCRIPTS.lintTypes.name, TYPESCRIPT_CONFIG_SCRIPTS.lintTypes.command); } /** * Sets up TypeScript configuration. * Collects user input, installs dependencies, creates config file, * and sets up scripts. * @returns Promise resolving to an object containing setup parameters */ async setupTypescript() { try { const parameters = {}; // Get configuration options from user const baseUrl = await this.getBaseUrl(); parameters.baseUrl = baseUrl; const rootDirectory = await this.getRootDir(); parameters.rootDirectory = rootDirectory; const outputDirectory = await this.getOutDir(); parameters.outputDirectory = outputDirectory; const isCleanArchitectureEnabled = await this.isCleanArchitectureEnabled(); parameters.isCleanArchitectureEnabled = isCleanArchitectureEnabled; const isDecoratorsEnabled = await this.isDecoratorsEnabled(); parameters.isDecoratorsEnabled = isDecoratorsEnabled; // Install and configure this.CLI_INTERFACE_SERVICE.startSpinner(TYPESCRIPT_CONFIG_MESSAGES.settingUpSpinner); await this.PACKAGE_JSON_SERVICE.installPackages(TYPESCRIPT_CONFIG_CORE_DEPENDENCIES, "latest", EPackageJsonDependencyType.DEV); await this.createConfig(baseUrl, rootDirectory, outputDirectory, isCleanArchitectureEnabled, isDecoratorsEnabled); await this.setupScripts(); this.CLI_INTERFACE_SERVICE.stopSpinner(TYPESCRIPT_CONFIG_MESSAGES.setupCompleteSpinner); this.displaySetupSummary(baseUrl, rootDirectory, outputDirectory, isCleanArchitectureEnabled, isDecoratorsEnabled); return parameters; } catch (error) { this.CLI_INTERFACE_SERVICE.stopSpinner(TYPESCRIPT_CONFIG_MESSAGES.failedSetupSpinner); throw error; } } } export { TypescriptModuleService }; //# sourceMappingURL=typescript-module.service.js.map