@elsikora/setup-wizard
Version:
Setup Wizard - CLI scaffolding utility
241 lines (238 loc) • 12.1 kB
JavaScript
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 { BRANCH_LINT_CONFIG_COMMANDS } from '../constant/branch-lint/commands.constant.js';
import { BRANCH_LINT_CONFIG } from '../constant/branch-lint/config.constant.js';
import { BRANCH_LINT_CONFIG_CORE_DEPENDENCIES } from '../constant/branch-lint/core-dependencies.constant.js';
import { BRANCH_LINT_CONFIG_FILE_NAME } from '../constant/branch-lint/file-name.constant.js';
import { BRANCH_LINT_CONFIG_FILE_NAMES } from '../constant/branch-lint/file-names.constant.js';
import { BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_FILE_PATH } from '../constant/branch-lint/husky-pre-push-file-path.constant.js';
import { BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_SCRIPT } from '../constant/branch-lint/husky-pre-push-script.constant.js';
import { BRANCH_LINT_CONFIG_MESSAGES } from '../constant/branch-lint/messages.constant.js';
import { BRANCH_LINT_CONFIG_PACKAGE_JSON_SCRIPT_NAMES } from '../constant/branch-lint/package-json-script-names.constant.js';
import { BRANCH_LINT_CONFIG_SCRIPTS } from '../constant/branch-lint/scripts.constant.js';
import { BRANCH_LINT_CONFIG_SUMMARY } from '../constant/branch-lint/summary.constant.js';
import { PackageJsonService } from './package-json.service.js';
/**
* Service for setting up and managing branch-lint configuration.
* Provides functionality to enforce consistent branch naming conventions
* and simplify branch creation using an interactive interface.
*/
class BranchLintModuleService {
/** 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 branch-lint module configuration */
config = null;
/**
* Initializes a new instance of the BranchLintModuleService.
* @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 branch-lint 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();
const packageJson = await this.PACKAGE_JSON_SERVICE.get();
// Check if package.json has branch-lint related configuration
const hasBranchScript = BRANCH_LINT_CONFIG_PACKAGE_JSON_SCRIPT_NAMES.some((scriptName) => packageJson.scripts?.[scriptName]);
if (hasBranchScript) {
existingFiles.push(BRANCH_LINT_CONFIG_MESSAGES.packageJsonBranchScript);
}
if (existingFiles.length > 0) {
const messageLines = [BRANCH_LINT_CONFIG_SUMMARY.existingFilesMessage];
messageLines.push("");
for (const file of existingFiles) {
messageLines.push(`- ${file}`);
}
messageLines.push("", BRANCH_LINT_CONFIG_SUMMARY.deleteFilesQuestion);
const shouldDelete = await this.CLI_INTERFACE_SERVICE.confirm(messageLines.join("\n"), true);
if (shouldDelete) {
await Promise.all(existingFiles.filter((file) => !file.startsWith("package.json")).map((file) => this.FILE_SYSTEM_SERVICE.deleteFile(file)));
// Remove branch-lint scripts from package.json if needed
if (hasBranchScript && packageJson.scripts) {
const newScripts = { ...packageJson.scripts };
for (const scriptName of BRANCH_LINT_CONFIG_PACKAGE_JSON_SCRIPT_NAMES) {
if (scriptName in newScripts) {
// eslint-disable-next-line @elsikora/typescript/no-dynamic-delete
delete newScripts[scriptName];
}
}
packageJson.scripts = newScripts;
await this.PACKAGE_JSON_SERVICE.set(packageJson);
}
}
else {
this.CLI_INTERFACE_SERVICE.warn(BRANCH_LINT_CONFIG_SUMMARY.existingConfigWarning);
return false;
}
}
return true;
}
/**
* Installs and configures branch-lint.
* Sets up configuration files, git hooks, and package.json scripts.
* @returns Promise resolving to the module setup result
*/
async install() {
try {
this.config = await this.CONFIG_SERVICE.getModuleConfig(EModule.BRANCH_LINT);
if (!(await this.shouldInstall())) {
return { wasInstalled: false };
}
if (!(await this.handleExistingSetup())) {
return { wasInstalled: false };
}
const isTicketIdEnabled = await this.shouldEnableTicketId();
await this.setupBranchLint(isTicketIdEnabled);
return {
customProperties: {
isTicketIdEnabled,
},
wasInstalled: true,
};
}
catch (error) {
this.CLI_INTERFACE_SERVICE.handleError(BRANCH_LINT_CONFIG_SUMMARY.installErrorMessage, error);
throw error;
}
}
/**
* Determines whether optional ticket-id placeholder should be enabled.
* Uses saved module configuration as default value.
* @returns Promise resolving to true when ticket-id placeholder should be enabled
*/
async shouldEnableTicketId() {
const isTicketIdEnabledByDefault = this.config?.isTicketIdEnabled ?? true;
try {
return await this.CLI_INTERFACE_SERVICE.confirm(BRANCH_LINT_CONFIG_SUMMARY.ticketIdConfirmationQuestion, isTicketIdEnabledByDefault);
}
catch (error) {
this.CLI_INTERFACE_SERVICE.handleError(BRANCH_LINT_CONFIG_SUMMARY.ticketIdConfirmationErrorMessage, error);
return isTicketIdEnabledByDefault;
}
}
/**
* Determines if branch-lint should be installed.
* Asks the user if they want to set up these tools 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(BRANCH_LINT_CONFIG_SUMMARY.confirmationQuestion, await this.CONFIG_SERVICE.isModuleEnabled(EModule.BRANCH_LINT));
}
catch (error) {
this.CLI_INTERFACE_SERVICE.handleError(BRANCH_LINT_CONFIG_SUMMARY.confirmationErrorMessage, error);
return false;
}
}
/**
* Creates the branch-lint configuration file.
* @param isTicketIdEnabled - Whether optional ticket-id placeholder should be enabled
*/
async createConfigs(isTicketIdEnabled) {
await this.FILE_SYSTEM_SERVICE.writeFile(BRANCH_LINT_CONFIG_FILE_NAME, BRANCH_LINT_CONFIG.template(isTicketIdEnabled), "utf8");
}
/**
* Displays a summary of the setup results.
* @param isTicketIdEnabled - Whether optional ticket-id placeholder is enabled
*/
displaySetupSummary(isTicketIdEnabled) {
const branchPattern = isTicketIdEnabled ? BRANCH_LINT_CONFIG_MESSAGES.branchPatternEnabledValue : BRANCH_LINT_CONFIG_MESSAGES.branchPatternDisabledValue;
const summary = [
BRANCH_LINT_CONFIG_MESSAGES.branchLintDescription,
"",
BRANCH_LINT_CONFIG_MESSAGES.generatedScriptsLabel,
`- npm run ${BRANCH_LINT_CONFIG_SCRIPTS.branch.name} ${BRANCH_LINT_CONFIG_MESSAGES.branchScriptDescription}`,
"",
BRANCH_LINT_CONFIG_MESSAGES.configurationFilesLabel,
`- ${BRANCH_LINT_CONFIG_FILE_NAME}`,
`- ${BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_FILE_PATH}`,
"",
`${BRANCH_LINT_CONFIG_MESSAGES.branchPatternLabel} ${branchPattern}`,
"",
BRANCH_LINT_CONFIG_MESSAGES.huskyHookSetupNote,
BRANCH_LINT_CONFIG_MESSAGES.branchCreationNote.replace("{script}", BRANCH_LINT_CONFIG_SCRIPTS.branch.name),
];
this.CLI_INTERFACE_SERVICE.note(BRANCH_LINT_CONFIG_SUMMARY.title, summary.join("\n"));
}
/**
* Finds existing branch-lint configuration files.
* @returns Promise resolving to an array of file paths for existing configuration files
*/
async findExistingConfigFiles() {
const existingFiles = [];
for (const file of BRANCH_LINT_CONFIG_FILE_NAMES) {
if (await this.FILE_SYSTEM_SERVICE.isPathExists(file)) {
existingFiles.push(file);
}
}
if (await this.FILE_SYSTEM_SERVICE.isPathExists(BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_FILE_PATH)) {
existingFiles.push(BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_FILE_PATH);
}
return existingFiles;
}
/**
* Sets up branch-lint.
* Installs dependencies, creates configuration files, and configures git hooks.
* @param isTicketIdEnabled - Whether optional ticket-id placeholder should be enabled
*/
async setupBranchLint(isTicketIdEnabled) {
this.CLI_INTERFACE_SERVICE.startSpinner(BRANCH_LINT_CONFIG_SUMMARY.setupStartMessage);
try {
await this.PACKAGE_JSON_SERVICE.installPackages(BRANCH_LINT_CONFIG_CORE_DEPENDENCIES, "latest", EPackageJsonDependencyType.DEV);
await this.createConfigs(isTicketIdEnabled);
await this.setupHusky();
await this.setupScripts();
this.CLI_INTERFACE_SERVICE.stopSpinner(BRANCH_LINT_CONFIG_SUMMARY.setupCompleteMessage);
this.displaySetupSummary(isTicketIdEnabled);
}
catch (error) {
this.CLI_INTERFACE_SERVICE.stopSpinner(BRANCH_LINT_CONFIG_SUMMARY.setupFailedMessage);
throw error;
}
}
/**
* Sets up Husky git hooks.
* Initializes Husky, adds prepare script, and creates pre-push hook.
*/
async setupHusky() {
// Initialize husky
await this.COMMAND_SERVICE.execute(BRANCH_LINT_CONFIG_COMMANDS.initHusky);
// Add prepare script if it doesn't exist
await this.PACKAGE_JSON_SERVICE.addScript(BRANCH_LINT_CONFIG_SCRIPTS.prepare.name, BRANCH_LINT_CONFIG_SCRIPTS.prepare.command);
await this.COMMAND_SERVICE.execute(BRANCH_LINT_CONFIG_COMMANDS.createHuskyDirectory);
// Create pre-push hook
await this.FILE_SYSTEM_SERVICE.writeFile(BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_FILE_PATH, BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_SCRIPT, "utf8");
await this.COMMAND_SERVICE.execute(BRANCH_LINT_CONFIG_COMMANDS.makePrePushExecutable(BRANCH_LINT_CONFIG_HUSKY_PRE_PUSH_FILE_PATH));
}
/**
* Sets up npm scripts for Branch-lint.
* Adds 'branch' script for using an interactive interface CLI.
*/
async setupScripts() {
await this.PACKAGE_JSON_SERVICE.addScript(BRANCH_LINT_CONFIG_SCRIPTS.branch.name, BRANCH_LINT_CONFIG_SCRIPTS.branch.command);
}
}
export { BranchLintModuleService };
//# sourceMappingURL=branch-lint-module.service.js.map