UNPKG

@deepbrainspace/nx-surrealdb

Version:

NX plugin for SurrealDB migrations with modular architecture

232 lines (230 loc) • 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = default_1; const tslib_1 = require("tslib"); const devkit_1 = require("@nx/devkit"); const path = tslib_1.__importStar(require("path")); const fs = tslib_1.__importStar(require("fs")); const child_process_1 = require("child_process"); const tree_utils_1 = require("../../lib/filesystem/tree-utils"); async function default_1(tree, options) { const normalizedOptions = normalizeOptions(tree, options); devkit_1.logger.info(`šŸš€ Importing module: ${normalizedOptions.module}`); devkit_1.logger.info(`šŸ“¦ From package: ${normalizedOptions.packagePath}`); // Extract package if it's an archive const extractedPath = await extractPackage(normalizedOptions); // Validate package structure await validatePackage(extractedPath, normalizedOptions.module); // Determine target module name and path const targetModuleName = determineTargetModuleName(normalizedOptions, extractedPath); // Check if target module already exists await checkTargetModule(tree, normalizedOptions, targetModuleName); // Load and validate dependencies if (!normalizedOptions.skipDependencyCheck) { await validateDependencies(tree, normalizedOptions, extractedPath); } // Import migration files await importMigrationFiles(tree, normalizedOptions, extractedPath, targetModuleName); // Merge configuration if (normalizedOptions.mergeConfig) { await mergeModuleConfiguration(tree, normalizedOptions, extractedPath, targetModuleName); } // Skip formatFiles for this generator since it causes test timeouts devkit_1.logger.info(`āœ… Module '${normalizedOptions.module}' imported successfully as '${targetModuleName}'!`); return () => { devkit_1.logger.info(` šŸŽ‰ Import completed! Module: ${normalizedOptions.module} → ${targetModuleName} Location: ${normalizedOptions.initPath}/${targetModuleName} Next steps: 1. Review the imported migrations in ${normalizedOptions.initPath}/${targetModuleName} 2. Check configuration updates in ${normalizedOptions.initPath}/config.json 3. Verify dependencies are satisfied 4. Run: nx run your-project:migrate --module ${targetModuleName} `); }; } function normalizeOptions(tree, options) { return { ...options, targetModule: options.targetModule || '', targetNumber: options.targetNumber || 0, initPath: options.initPath || 'database', configPath: options.configPath || '', overwrite: options.overwrite ?? false, skipDependencyCheck: options.skipDependencyCheck ?? false, mergeConfig: options.mergeConfig ?? true, }; } async function extractPackage(options) { const packagePath = path.resolve(options.packagePath); if (!fs.existsSync(packagePath)) { throw new Error(`Package not found: ${packagePath}`); } const stat = fs.statSync(packagePath); if (stat.isDirectory()) { devkit_1.logger.info(`šŸ“ Using directory package: ${packagePath}`); return packagePath; } // Extract archive to temporary directory const tempDir = path.join(process.cwd(), '.tmp', 'module-import', Date.now().toString()); fs.mkdirSync(tempDir, { recursive: true }); try { if (packagePath.endsWith('.tar.gz') || packagePath.endsWith('.tgz')) { devkit_1.logger.info(`šŸ“¦ Extracting tar archive...`); (0, child_process_1.execSync)(`tar -xzf "${packagePath}" -C "${tempDir}"`, { stdio: 'inherit' }); } else if (packagePath.endsWith('.zip')) { devkit_1.logger.info(`šŸ“¦ Extracting zip archive...`); (0, child_process_1.execSync)(`unzip -q "${packagePath}" -d "${tempDir}"`, { stdio: 'inherit' }); } else { throw new Error(`Unsupported package format: ${packagePath}`); } // Find the extracted module directory const extracted = fs.readdirSync(tempDir); if (extracted.length === 0) { throw new Error('Archive appears to be empty'); } return path.join(tempDir, extracted[0]); } catch (error) { // Cleanup on error if (fs.existsSync(tempDir)) { fs.rmSync(tempDir, { recursive: true, force: true }); } throw error; } } async function validatePackage(extractedPath, _moduleName) { if (!fs.existsSync(extractedPath)) { throw new Error(`Extracted package path not found: ${extractedPath}`); } // Check for required files const requiredFiles = ['package.json', 'migrations']; for (const file of requiredFiles) { const filePath = path.join(extractedPath, file); if (!fs.existsSync(filePath)) { throw new Error(`Package is missing required file/directory: ${file}`); } } // Validate package.json const packageJsonPath = path.join(extractedPath, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); if (!packageJson.metadata?.moduleName) { throw new Error('Package.json is missing module metadata'); } devkit_1.logger.info(`šŸ“‹ Package validation passed`); devkit_1.logger.info(` Module: ${packageJson.metadata.moduleName}`); devkit_1.logger.info(` Version: ${packageJson.version}`); devkit_1.logger.info(` Description: ${packageJson.description}`); } function determineTargetModuleName(options, extractedPath) { if (options.targetModule) { return options.targetModule; } if (options.targetNumber > 0) { const packageJsonPath = path.join(extractedPath, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); const originalName = packageJson.metadata.moduleName; // Extract the name part (without number prefix) const namePart = originalName.replace(/^\d+_/, ''); return `${String(options.targetNumber).padStart(3, '0')}_${namePart}`; } // Use original module name const packageJsonPath = path.join(extractedPath, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); return packageJson.metadata.moduleName; } async function checkTargetModule(tree, options, targetModuleName) { const targetPath = path.join(options.initPath, targetModuleName); if (tree.exists(targetPath)) { if (!options.overwrite) { throw new Error(`Target module '${targetModuleName}' already exists. Use --overwrite to replace it.`); } else { devkit_1.logger.warn(`āš ļø Overwriting existing module: ${targetModuleName}`); } } } async function validateDependencies(tree, options, extractedPath) { const moduleConfigPath = path.join(extractedPath, 'module.config.json'); if (!fs.existsSync(moduleConfigPath)) { devkit_1.logger.warn(`āš ļø No module configuration found - skipping dependency validation`); return; } const moduleConfig = JSON.parse(fs.readFileSync(moduleConfigPath, 'utf-8')); const moduleName = Object.keys(moduleConfig)[0]; const config = moduleConfig[moduleName]; if (!config.dependencies || config.dependencies.length === 0) { devkit_1.logger.info(`āœ… Module has no dependencies`); return; } // Check if dependencies exist in target project const targetConfigPath = options.configPath || path.join(options.initPath, 'config.json'); const targetConfig = tree_utils_1.TreeUtils.readJsonFile(tree, targetConfigPath); if (targetConfig) { const missingDeps = (config.dependencies || []).filter(dep => !targetConfig.modules || !targetConfig.modules[dep]); if (missingDeps.length > 0) { throw new Error(`Missing dependencies: ${missingDeps.join(', ')}. ` + `Install these modules first or use --skipDependencyCheck to override.`); } } else { devkit_1.logger.warn(`āš ļø No target configuration found - cannot validate dependencies`); } devkit_1.logger.info(`āœ… All dependencies satisfied: ${config.dependencies.join(', ')}`); } async function importMigrationFiles(tree, options, extractedPath, targetModuleName) { const sourceMigrationsPath = path.join(extractedPath, 'migrations'); const targetMigrationsPath = path.join(options.initPath, targetModuleName); // Create target directory tree_utils_1.TreeUtils.ensureDirectory(tree, targetMigrationsPath); // Copy migration files const migrationFiles = fs.readdirSync(sourceMigrationsPath); for (const file of migrationFiles) { const sourceFilePath = path.join(sourceMigrationsPath, file); const targetFilePath = path.join(targetMigrationsPath, file); const stat = fs.statSync(sourceFilePath); if (stat.isFile() && file.endsWith('.surql')) { const content = fs.readFileSync(sourceFilePath, 'utf-8'); tree.write(targetFilePath, content); devkit_1.logger.info(`šŸ“„ Imported: ${file}`); } } devkit_1.logger.info(`āœ… Imported ${migrationFiles.length} migration files`); } async function mergeModuleConfiguration(tree, options, extractedPath, targetModuleName) { const moduleConfigPath = path.join(extractedPath, 'module.config.json'); if (!fs.existsSync(moduleConfigPath)) { devkit_1.logger.warn(`āš ļø No module configuration to merge`); return; } const moduleConfig = JSON.parse(fs.readFileSync(moduleConfigPath, 'utf-8')); const originalModuleName = Object.keys(moduleConfig)[0]; const config = moduleConfig[originalModuleName]; const targetConfigPath = options.configPath || path.join(options.initPath, 'config.json'); let targetConfig = { modules: {}, settings: { configFormat: 'json', useTransactions: true, }, }; // Load existing config if it exists in Tree const existingConfig = tree_utils_1.TreeUtils.readJsonFile(tree, targetConfigPath); if (existingConfig) { targetConfig = existingConfig; } // Add the new module configuration targetConfig.modules[targetModuleName] = { ...config, // Update any self-references in dependencies dependencies: config.dependencies?.map(dep => (dep === originalModuleName ? targetModuleName : dep)) || [], }; // Write updated configuration tree_utils_1.TreeUtils.writeJsonFile(tree, targetConfigPath, targetConfig); devkit_1.logger.info(`āœ… Configuration merged for module: ${targetModuleName}`); } //# sourceMappingURL=generator.js.map