skaya
Version:
CLI SDK for full-stack automation: scaffold frontend, backend & blockchain. Future-ready for Web3, integrations, server components & logging.
270 lines (269 loc) • 13.6 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.saveProjectConfig = saveProjectConfig;
exports.saveProjectComponentConfig = saveProjectComponentConfig;
exports.getProjectComponentConfig = getProjectComponentConfig;
exports.updateComponentReferences = updateComponentReferences;
exports.logComponentCreation = logComponentCreation;
exports.readConfig = readConfig;
exports.readComponentImportConfig = readComponentImportConfig;
// src/utils/configLogger.ts
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const CONFIG_FILE = 'skaya.config.json';
const LOG_FILE = 'Skayalogs.log';
const DEFAULT_PROJECT_NAME = 'SkayaProject'; // Moved to a constant
/**
* Saves project type (frontend/backend/blockchain) to skaya.config.json.
* This function creates or updates the top-level project configuration.
* @param projectType - Project type (e.g., ProjectType.Frontend, ProjectType.Backend)
* @param name - Name of the project
* @param template - Template to use (optional, defaults to "custom")
*/
function saveProjectConfig(projectType, name, template) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const configPath = path_1.default.join(process.cwd(), CONFIG_FILE); // Corrected path to be in current working directory
console.log("Saving project config to:", configPath);
let config = {};
try {
const fileContent = yield fs_1.promises.readFile(configPath, 'utf-8');
if (fileContent.trim().length > 0) {
config = JSON.parse(fileContent);
}
}
catch (error) {
if (error.code !== 'ENOENT') {
throw new Error(`Error reading config file: ${error.message}`);
}
console.log(`Config file ${CONFIG_FILE} not found, creating a new one.`);
}
if (config[projectType]) {
console.log(`Project type ${projectType} already exists in config. Updating.`);
}
config[projectType] = {
name,
template: template || "custom",
createdAt: new Date().toISOString(),
components: ((_a = config[projectType]) === null || _a === void 0 ? void 0 : _a.components) || {} // Preserve existing components if updating
};
yield fs_1.promises.writeFile(configPath, JSON.stringify(config, null, 2));
}
catch (error) {
throw error;
}
});
}
/**
* Saves component-specific configuration for a given project type.
* This allows storing details about components within a project (e.g., 'auth', 'database').
* The configuration now supports tracking component relationships through imports.
* @param projectType - The ProjectType (e.g., ProjectType.Frontend)
* @param componentType - The ComponentType (e.g., ComponentType.UI_COMPONENT, ComponentType.API_COMPONENT)
* @param componentName - The name of the component (e.g., "auth", "paymentGateway")
* @param componentDetails - The configuration object for the specific component
*/
function saveProjectComponentConfig(projectType, componentType, componentName, componentDetails // Partial to allow for updates
) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
try {
let config = {};
const configPath = path_1.default.join(process.cwd(), CONFIG_FILE);
// Try to read existing config
try {
const data = yield fs_1.promises.readFile(configPath, 'utf-8');
config = JSON.parse(data);
}
catch (error) {
if (error.code === 'ENOENT') {
console.log(`Config file ${CONFIG_FILE} not found for component config, creating a new one.`);
}
else {
console.warn(`Warning: Could not parse existing config file for component config, creating new one. Error: ${error.message}`);
}
}
// Ensure the project type entry exists
if (!config[projectType]) {
config[projectType] = {
name: `${projectType}${DEFAULT_PROJECT_NAME}`,
template: "custom",
createdAt: new Date().toISOString(),
components: {}
};
console.log(`Initialized new entry for project type: ${projectType}`);
}
// Ensure the 'components' property exists within the project type
if (!config[projectType].components) {
config[projectType].components = {};
console.log(`Initialized 'components' property for project type: ${projectType}`);
}
// Get existing component config to merge
const existingComponentConfig = config[projectType].components[componentName] || {
source: 'template', // Default source if new
files: [],
componentType: componentType, // Set initial componentType
savedAt: new Date().toISOString(),
imports: [],
usedBy: []
};
// Process imports to add componentType if missing (though it should ideally be provided or inferred during generation)
// For now, we'll assume `imports` within `componentDetails` already has `componentType` if needed,
// or it will be determined by the system later.
const processedImports = (_a = componentDetails.imports) === null || _a === void 0 ? void 0 : _a.map(imp => (Object.assign(Object.assign({}, imp), { componentType: imp.componentType || undefined // Assuming it might be set or not
})));
// Assign the component configuration, merging with existing data
config[projectType].components[componentName] = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, existingComponentConfig), componentDetails), { componentType: componentType }), (processedImports && { imports: processedImports })), { savedAt: existingComponentConfig.savedAt || new Date().toISOString(), updatedAt: new Date().toISOString() // Always update updatedAt
});
// Write the updated config back to file
yield fs_1.promises.writeFile(configPath, JSON.stringify(config, null, 2));
// Update reverse references if imports changed or component is new
if ((processedImports === null || processedImports === void 0 ? void 0 : processedImports.length) || ((_b = existingComponentConfig.imports) === null || _b === void 0 ? void 0 : _b.length)) {
yield updateComponentReferences(projectType, componentName, processedImports || [], // New imports
existingComponentConfig.imports || [] // Old imports
);
}
}
catch (error) {
console.error(`❌ Failed to save component '${componentName}' configuration for '${projectType}':`, error);
throw error;
}
});
}
/**
* Retrieves the configuration for a specific component.
* @param projectType - The ProjectType (e.g., ProjectType.Frontend)
* @param componentName - The name of the component (e.g., "auth")
* @returns The ComponentConfig object or undefined if not found.
*/
function getProjectComponentConfig(projectType, componentName) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
try {
const config = yield readConfig();
return (_b = (_a = config[projectType]) === null || _a === void 0 ? void 0 : _a.components) === null || _b === void 0 ? void 0 : _b[componentName];
}
catch (error) {
console.error(`❌ Failed to get configuration for component '${componentName}' in '${projectType}':`, error);
return undefined;
}
});
}
// Helper function to update references in imported components
function updateComponentReferences(projectType_1, sourceComponent_1, newImports_1) {
return __awaiter(this, arguments, void 0, function* (projectType, sourceComponent, newImports, oldImports = []) {
var _a;
const config = yield readConfig();
const projectComponents = (_a = config[projectType]) === null || _a === void 0 ? void 0 : _a.components;
if (!projectComponents) {
console.warn(`No components found for project type ${projectType}.`);
return;
}
// Remove old references
for (const oldImp of oldImports) {
if (projectComponents[oldImp.name]) {
const importedComp = projectComponents[oldImp.name];
if (importedComp.usedBy) {
importedComp.usedBy = importedComp.usedBy.filter((comp) => comp !== sourceComponent);
}
}
}
// Add new references
for (const newImp of newImports) {
if (projectComponents[newImp.name]) {
const importedComp = projectComponents[newImp.name];
if (!importedComp.usedBy) {
importedComp.usedBy = [];
}
if (!importedComp.usedBy.includes(sourceComponent)) {
importedComp.usedBy.push(sourceComponent);
}
}
}
// Save the updated config
const configPath = path_1.default.join(process.cwd(), CONFIG_FILE);
yield fs_1.promises.writeFile(configPath, JSON.stringify(config, null, 2));
});
}
/**
* Logs component creation details to the log file.
* @param params - Component creation parameters
*/
function logComponentCreation(params) {
return __awaiter(this, void 0, void 0, function* () {
try {
const logEntry = Object.assign({ timestamp: params.timestamp || new Date().toISOString() }, params);
const logLine = JSON.stringify(logEntry) + '\n';
// Append to log file (creates it if doesn't exist)
yield fs_1.promises.appendFile(LOG_FILE, logLine);
}
catch (error) {
console.error('❌ Failed to log component creation:', error);
throw error;
}
});
}
/**
* Reads the configuration file.
* @returns A Promise that resolves to the Config object. Returns an empty object if the file doesn't exist or is invalid.
*/
function readConfig() {
return __awaiter(this, void 0, void 0, function* () {
try {
const configPath = path_1.default.join(process.cwd(), CONFIG_FILE); // Consistent path with save functions
const data = yield fs_1.promises.readFile(configPath, 'utf-8');
return JSON.parse(data);
}
catch (error) {
if (error.code === 'ENOENT') {
// console.log(`Config file ${CONFIG_FILE} not found. Returning empty config.`);
}
else {
console.error('❌ Failed to read configuration file:', error);
}
return {}; // Return empty object if file not found or parsing fails
}
});
}
/**
* Reads the component import configuration from the config file.
* Note: This function assumes that the `ComponentImportConfig` might be stored directly
* as a top-level property (e.g., `config.componentImports`). If component import configs
* are expected to be nested within project types, this function would need adjustment.
* @returns A Promise that resolves to the ComponentImportConfig object.
*/
function readComponentImportConfig() {
return __awaiter(this, void 0, void 0, function* () {
try {
const configPath = path_1.default.join(process.cwd(), CONFIG_FILE); // Consistent path with save functions
const data = yield fs_1.promises.readFile(configPath, 'utf-8');
const config = JSON.parse(data);
// This line assumes a top-level 'componentImports' key in your config.
// If component import configurations are stored differently (e.g., within a project type's 'components'),
// this logic would need to be updated.
return config.componentImports || {};
}
catch (error) {
if (error.code === 'ENOENT') {
// console.log(`Component import config file ${CONFIG_FILE} not found. Returning empty config.`);
}
else {
console.error('❌ Failed to read component import configuration:', error);
}
return {}; // Return empty object if file not found or parsing fails
}
});
}