UNPKG

@primexop/pbk

Version:

Primexop Backend Kit - A powerful TypeScript utility for managing backend projects with features like B2F Portal integration, cross-project validation, and Next.js support

203 lines (194 loc) 7.73 kB
import path from "path"; import fs from "fs"; import { stripComments } from "./jsonFile.js"; /** * Creates a visually distinct, colorful error message for config file not found errors * @param configPath - Path to the config file that was not found * @returns Formatted error message string */ function createConfigNotFoundErrorMessage(configPath) { const separator = "=".repeat(80); const title = "CONFIG FILE NOT FOUND ERROR"; // Color codes for terminal const red = "\x1b[31m"; const yellow = "\x1b[33m"; const cyan = "\x1b[36m"; const reset = "\x1b[0m"; const bold = "\x1b[1m"; // Create suggested config example const configExample = JSON.stringify({ "projects": [ { "projectName": "Your Project", "projectBaseDirPath": "C:/path/to/your/project", "sections": [ { "sectionName": "Backend", "repository": { "name": "your-backend-repo", "path": "https://github.com/your-org/your-repo" }, "localPath": "C:/path/to/your/backend", "isZodCreator": true } ] } ], "b2fPortal": false, "checkCrossProjectImports": true }, null, 2); return ` ${red}${separator}${reset} ${red}${bold} ${title} ${reset} ${red}${separator}${reset} ${yellow}The configuration file could not be found at:${reset} ${cyan}${configPath}${reset} ${yellow}Please create a pbk.config.json file at the root of your project with the following structure:${reset} ${configExample} ${yellow}To specify a different config location, provide the path when calling pbkInit():${reset} ${cyan}pbkInit({ configPath: './path/to/your/config.json' })${reset} ${red}${separator}${reset} `; } /** * Creates a visually distinct, colorful error message for validation errors * @param message - The error message content * @returns Formatted error message string */ function createValidationErrorMessage(message) { const separator = "=".repeat(80); const title = "CONFIG VALIDATION ERROR"; // Color codes for terminal const red = "\x1b[31m"; const yellow = "\x1b[33m"; const green = "\x1b[32m"; const reset = "\x1b[0m"; const bold = "\x1b[1m"; return ` ${red}${separator}${reset} ${red}${bold} ${title} ${reset} ${red}${separator}${reset} ${yellow}${message}${reset} ${green}Please review your configuration file and fix the validation issue.${reset} ${red}${separator}${reset} `; } /** * Loads and validates the PBK configuration from a JSON file * @param configPath - Path to the config file (optional, defaults to pbk.config.json in current directory) * @returns Validated TPbkConfig object * @throws Error if the configuration is invalid or file doesn't exist */ export function loadConfig(configPath = path.join(process.cwd(), 'pbk.config.json')) { try { // Check if file exists if (!fs.existsSync(configPath)) { const errorMessage = createConfigNotFoundErrorMessage(configPath); console.error(errorMessage); throw new Error(`Configuration file not found: ${configPath}`); } // Read file and strip comments const configString = fs.readFileSync(configPath, 'utf8'); const strippedConfig = stripComments(configString); // Parse JSON file const config = JSON.parse(strippedConfig); // Validate the entire config structure try { validateConfig(config); } catch (validationError) { if (validationError instanceof Error) { const errorMessage = createValidationErrorMessage(validationError.message); console.error(errorMessage); } throw validationError; } return config; } catch (error) { if (error instanceof SyntaxError) { const errorMessage = createValidationErrorMessage(`Invalid JSON in configuration file: ${error.message}`); console.error(errorMessage); throw new Error(`Invalid JSON in configuration file: ${error.message}`); } throw error; } } /** * Validates the configuration object * @param config - The configuration object to validate * @throws Error with detailed message if validation fails */ function validateConfig(config) { // Check if config has projects array if (!config || !Array.isArray(config.projects)) { throw new Error('Configuration must contain a "projects" array'); } // Check for b2fPortal and checkCrossProjectImports fields if (typeof config.b2fPortal !== 'boolean') { throw new Error('Configuration must contain a "b2fPortal" boolean field'); } if (typeof config.checkCrossProjectImports !== 'boolean') { throw new Error('Configuration must contain a "checkCrossProjectImports" boolean field'); } // Validate each project for (let i = 0; i < config.projects.length; i++) { const project = config.projects[i]; validateProject(project, i); } } /** * Validates a project configuration * @param project - The project to validate * @param index - Index in projects array for error messages */ function validateProject(project, index) { // Required fields if (!project.projectName) { throw new Error(`Project at index ${index} is missing "projectName"`); } if (!project.projectBaseDirPath) { throw new Error(`Project "${project.projectName}" is missing "projectBaseDirPath"`); } if (!Array.isArray(project.sections)) { throw new Error(`Project "${project.projectName}" must contain a "sections" array`); } // Validate each section for (let j = 0; j < project.sections.length; j++) { validateSection(project.sections[j], project.projectName, j); } } /** * Validates a project section configuration * @param section - The section to validate * @param projectName - Parent project name for error messages * @param index - Index in sections array for error messages */ function validateSection(section, projectName, index) { // Required fields if (!section.sectionName) { throw new Error(`Section at index ${index} in project "${projectName}" is missing "sectionName"`); } if (!section.repository) { throw new Error(`Section "${section.sectionName}" in project "${projectName}" is missing "repository"`); } if (!section.repository.name) { throw new Error(`Section "${section.sectionName}" in project "${projectName}" has repository missing "name"`); } if (!section.repository.path) { throw new Error(`Section "${section.sectionName}" in project "${projectName}" has repository missing "path"`); } if (!section.localPath) { throw new Error(`Section "${section.sectionName}" in project "${projectName}" is missing "localPath"`); } if (section.isZodCreator === undefined) { throw new Error(`Section "${section.sectionName}" in project "${projectName}" is missing "isZodCreator"`); } // Type checks if (typeof section.isZodCreator !== 'boolean') { throw new Error(`Section "${section.sectionName}" in project "${projectName}": "isZodCreator" must be a boolean`); } if (section.needNextJsPatch !== undefined && typeof section.needNextJsPatch !== 'boolean') { throw new Error(`Section "${section.sectionName}" in project "${projectName}": "needNextJsPatch" must be a boolean if provided`); } }