UNPKG

@elsikora/setup-wizard

Version:

Setup Wizard - CLI scaffolding utility

230 lines (227 loc) 10.2 kB
#!/usr/bin/env node import { LICENSE_CONFIG } from '../../domain/constant/license-config.constant.js'; import { EModule } from '../../domain/enum/module.enum.js'; import { NodeCommandService } from '../../infrastructure/service/node-command.service.js'; import { LICENSE_FILE_NAMES } from '../constant/license-file-names.constant.js'; import { CliInterfaceServiceMapper } from '../mapper/cli-interface-service.mapper.js'; import { PackageJsonService } from './package-json.service.js'; /** * Service for generating and managing LICENSE files for projects. * Provides functionality to select a license type, generate the LICENSE file, * and update the package.json with license information. */ class LicenseModuleService { /** 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 License configuration */ config = null; /** * Initializes a new instance of the LicenseModuleService. * @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 license setup. * Checks for existing license files and asks if user wants to replace them. * @returns Promise resolving to true if setup should proceed, false otherwise */ async handleExistingSetup() { try { const existingLicense = await this.FILE_SYSTEM_SERVICE.isOneOfPathsExists(LICENSE_FILE_NAMES); if (!existingLicense) { return true; } const shouldReplace = await this.CLI_INTERFACE_SERVICE.confirm(`An existing license file was found (${existingLicense}). Would you like to replace it?`); if (!shouldReplace) { this.CLI_INTERFACE_SERVICE.warn("Keeping existing license file."); return false; } try { await this.FILE_SYSTEM_SERVICE.deleteFile(existingLicense); this.CLI_INTERFACE_SERVICE.success("Deleted existing license file."); return true; } catch (error) { this.CLI_INTERFACE_SERVICE.handleError("Failed to delete existing license file", error); return false; } } catch (error) { this.CLI_INTERFACE_SERVICE.handleError("Failed to check existing license setup", error); return false; } } /** * Installs and configures a LICENSE file. * Guides the user through selecting a license type and generating the file. * @returns Promise resolving to the module setup result */ async install() { try { this.config = await this.CONFIG_SERVICE.getModuleConfig(EModule.LICENSE); if (!(await this.shouldInstall())) { return { wasInstalled: false }; } if (!(await this.handleExistingSetup())) { return { wasInstalled: false }; } const setupResult = await this.generateNewLicense(this.config); this.displaySetupSummary(setupResult.isSuccess, setupResult.license, setupResult.author, setupResult.error); if (setupResult.isSuccess && setupResult.license) { return { customProperties: { author: setupResult.author, license: setupResult.license, year: new Date().getFullYear(), }, wasInstalled: true, }; } return { wasInstalled: setupResult.isSuccess }; } catch (error) { this.CLI_INTERFACE_SERVICE.handleError("Failed to complete license installation", error); throw error; } } /** * Determines if the LICENSE module should be installed. * Asks the user if they want to generate a LICENSE file for their project. * 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("Do you want to generate LICENSE for your project?", await this.CONFIG_SERVICE.isModuleEnabled(EModule.LICENSE)); } catch (error) { this.CLI_INTERFACE_SERVICE.handleError("Failed to get user confirmation", error); return false; } } /** * Creates a LICENSE file with the selected license type and author information. * Also updates the package.json license field. * @param license - The selected license type * @param savedAuthor - Previously saved author name, if any * @returns Promise resolving to an object containing the author name */ async createLicenseFile(license, savedAuthor) { try { let packageAuthor; try { packageAuthor = await this.PACKAGE_JSON_SERVICE.getProperty("author"); } catch { this.CLI_INTERFACE_SERVICE.warn("Failed to get author from package.json, using saved or default"); packageAuthor = undefined; } let authorName; if (savedAuthor) { authorName = savedAuthor; } else if (packageAuthor) { if (typeof packageAuthor === "object" && "name" in packageAuthor) { authorName = packageAuthor.name; } else if (typeof packageAuthor === "string" && packageAuthor.length > 0) { authorName = packageAuthor; } else { authorName = "Your Name"; } } else { authorName = "Your Name"; } authorName = await this.CLI_INTERFACE_SERVICE.text("Enter the copyright holder's name:", "Your Name", authorName); this.CLI_INTERFACE_SERVICE.startSpinner("Generating license file..."); const year = new Date().getFullYear().toString(); const licenseFileContent = LICENSE_CONFIG[license].template(year, authorName); await this.FILE_SYSTEM_SERVICE.writeFile("LICENSE", licenseFileContent); await this.PACKAGE_JSON_SERVICE.setProperty("license", license); this.CLI_INTERFACE_SERVICE.stopSpinner("License file generated"); return { author: authorName }; } catch (error) { this.CLI_INTERFACE_SERVICE.stopSpinner(); throw error; } } /** * Displays a summary of the LICENSE setup results. * Lists details about the generated license file. * @param isSuccess - Whether the setup was successful * @param license - The selected license type, if successful * @param author - The copyright holder's name, if successful * @param error - Optional error if setup failed */ displaySetupSummary(isSuccess, license, author, error) { const summary = []; const year = new Date().getFullYear().toString(); if (isSuccess && license) { summary.push("Successfully created configuration:", `✓ LICENSE file (${LICENSE_CONFIG[license].name})`, ``, `Updated package.json "license" field`, "", "License details:", `- Type: ${LICENSE_CONFIG[license].name}`, `- Author: ${author ?? "Your Name"}`, `- Year: ${year}`, ""); } else { summary.push("Failed configuration:", `✗ LICENSE - ${error?.message ?? "Unknown error"}`); } summary.push("", "Remember to:", "- Review the generated LICENSE file", "- Include license information in your documentation"); this.CLI_INTERFACE_SERVICE.note("License Setup Summary", summary.join("\n")); } /** * Generates a new LICENSE file. * @param savedConfig - Previously saved license configuration, if any * @returns Promise resolving to an object indicating success or failure with optional license, author, and error details */ async generateNewLicense(savedConfig) { try { const license = await this.selectLicense(savedConfig?.license); const result = await this.createLicenseFile(license, savedConfig?.author); return { author: result.author, isSuccess: true, license, }; } catch (error) { return { error: error, isSuccess: false, }; } } /** * Prompts the user to select a license type for their project. * @param savedLicense - Previously saved license type, if any * @returns Promise resolving to the selected license enum value */ async selectLicense(savedLicense) { try { const options = CliInterfaceServiceMapper.fromLicenseConfigsToSelectOptions(LICENSE_CONFIG); const initialValue = savedLicense ?? undefined; return await this.CLI_INTERFACE_SERVICE.select("Select a license for your project:", options, initialValue); } catch (error) { this.CLI_INTERFACE_SERVICE.handleError("Failed to select license", error); throw error; } } } export { LicenseModuleService }; //# sourceMappingURL=license-module.service.js.map