UNPKG

@msugiura/rawsql-prisma

Version:

Prisma integration for rawsql-ts - Dynamic SQL generation with type safety and hierarchical JSON serialization

306 lines 12 kB
"use strict"; /** * SQL Static Analysis Library * * Provides unified API for static analysis of SQL files, including: * - SQL file discovery and loading * - Schema validation using SqlSchemaValidator * - JSON mapping compatibility validation * - Comprehensive validation reports */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SqlStaticAnalyzer = void 0; exports.analyzeSqlFiles = analyzeSqlFiles; exports.validateSqlFile = validateSqlFile; const rawsql_ts_1 = require("rawsql-ts"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); class SqlStaticAnalyzer { constructor(options) { this.options = options; this.tableColumnResolver = options.schemaResolver.createTableColumnResolver(); } /** * Discover all SQL files in the configured directory */ discoverSqlFiles() { const { sqlDirectory, debug } = this.options; if (!fs.existsSync(sqlDirectory)) { throw new Error(`SQL directory does not exist: ${sqlDirectory}`); } // Recursively find all SQL files const sqlFiles = []; const searchDirectory = (dir) => { const entries = fs.readdirSync(dir); for (const entry of entries) { const fullPath = path.join(dir, entry); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { searchDirectory(fullPath); } else if (stat.isFile() && entry.endsWith('.sql')) { const content = fs.readFileSync(fullPath, 'utf-8'); const baseName = path.basename(entry, '.sql'); const relativePath = path.relative(sqlDirectory, fullPath); sqlFiles.push({ filename: entry, fullPath, baseName, content, relativePath }); } } }; searchDirectory(sqlDirectory); if (debug) { console.log(`📄 Found ${sqlFiles.length} SQL files in ${sqlDirectory}:`, sqlFiles.map(f => f.relativePath || f.filename)); } return sqlFiles; } /** * Validate a single SQL file */ async validateSqlFile(sqlFile) { const { debug } = this.options; const result = { filename: sqlFile.filename, isValid: false, errors: [], hasJsonMapping: false }; try { if (debug) { console.log(`🔍 Validating ${sqlFile.filename}...`); } // Parse SQL content const parseResult = rawsql_ts_1.SelectQueryParser.parse(sqlFile.content); if (!parseResult) { result.errors.push('Failed to parse SQL content'); return result; } result.parseResult = parseResult; // Validate schema try { rawsql_ts_1.SqlSchemaValidator.validate(parseResult, this.tableColumnResolver); } catch (schemaError) { result.errors.push(`Schema validation failed: ${schemaError.message}`); return result; } // Check for JSON mapping const jsonPath = path.join(path.dirname(sqlFile.fullPath), `${sqlFile.baseName}.json`); result.hasJsonMapping = fs.existsSync(jsonPath); if (result.hasJsonMapping) { const jsonValidationResult = await this.validateJsonMapping(sqlFile, jsonPath, parseResult); result.jsonMappingValid = jsonValidationResult.isValid; result.jsonMappingErrors = jsonValidationResult.errors; } // Mark as valid if SQL parsing and schema validation passed result.isValid = result.errors.length === 0; if (debug && result.isValid) { console.log(`✅ ${sqlFile.filename}: Validation passed`); } } catch (error) { result.errors.push(`Unexpected error: ${error.message}`); } return result; } /** * Validate JSON mapping for a SQL file */ async validateJsonMapping(sqlFile, jsonPath, parseResult) { const { debug } = this.options; const result = { isValid: false, errors: [] }; try { if (debug) { console.log(`✅ Found JSON mapping: ${sqlFile.baseName}.json`); } // Read and parse JSON mapping using unified processor const jsonContent = fs.readFileSync(jsonPath, 'utf-8'); let jsonMapping; try { const rawMapping = JSON.parse(jsonContent); // Check if this is a Model-driven mapping (has typeInfo and structure) if (rawMapping.typeInfo && rawMapping.structure) { // Use the convertModelDrivenMapping function const conversionResult = (0, rawsql_ts_1.convertModelDrivenMapping)(rawMapping); jsonMapping = conversionResult.jsonMapping; } else if (rawMapping.rootName && rawMapping.rootEntity) { // Already in legacy format jsonMapping = rawMapping; } else { // Invalid format result.errors.push('JSON mapping must be either Model-driven format (with typeInfo and structure) or Legacy format (with rootName and rootEntity)'); return result; } } catch (parseError) { result.errors.push(`Failed to process JSON mapping: ${parseError.message}`); return result; } // Basic validation - the unified processor ensures correct structure if (!jsonMapping || !jsonMapping.rootName || !jsonMapping.rootEntity) { result.errors.push('Invalid JSON mapping structure after processing'); return result; } // Test JSON query building try { const jsonQueryBuilder = new rawsql_ts_1.PostgresJsonQueryBuilder(); const jsonQuery = jsonQueryBuilder.buildJsonQuery(parseResult, jsonMapping); if (!jsonQuery) { result.errors.push('Failed to build JSON query'); return result; } } catch (buildError) { result.errors.push(`JSON query building failed: ${buildError.message}`); return result; } result.isValid = true; if (debug) { console.log(`🎯 ${sqlFile.filename}: SQL + JSON mapping validation passed`); } } catch (error) { result.errors.push(`JSON mapping validation error: ${error.message}`); } return result; } /** * Validate all SQL files in the directory */ async validateAllSqlFiles() { const sqlFiles = this.discoverSqlFiles(); const results = []; for (const sqlFile of sqlFiles) { const result = await this.validateSqlFile(sqlFile); results.push(result); } return results; } /** * Generate comprehensive analysis report */ async generateAnalysisReport() { const results = await this.validateAllSqlFiles(); const totalFiles = results.length; const validFiles = results.filter(r => r.isValid).length; const invalidFiles = totalFiles - validFiles; const filesWithMapping = results.filter(r => r.hasJsonMapping).length; const validMappings = results.filter(r => r.hasJsonMapping && r.jsonMappingValid).length; const invalidMappings = filesWithMapping - validMappings; const summary = [ `📊 SQL Static Analysis Report`, `Total SQL files: ${totalFiles}`, `Valid SQL files: ${validFiles}`, `Invalid SQL files: ${invalidFiles}`, `Files with JSON mapping: ${filesWithMapping}`, `Valid JSON mappings: ${validMappings}`, `Invalid JSON mappings: ${invalidMappings}`, validFiles === totalFiles && validMappings === filesWithMapping ? '🎉 All validations passed!' : '⚠️ Some validations failed - check details below' ].join('\n'); return { totalFiles, validFiles, invalidFiles, filesWithMapping, validMappings, invalidMappings, results, summary }; } /** * Validate schema availability and table information */ validateSchemaInfo() { const result = { isValid: false, tableNames: [], errors: [] }; try { const tableNames = this.options.schemaResolver.getTableNames(); if (!tableNames || tableNames.length === 0) { result.errors.push('No tables found in schema'); return result; } result.tableNames = tableNames; result.isValid = true; if (this.options.debug) { console.log(`🏗️ Available tables: ${tableNames.join(', ')}`); } } catch (error) { result.errors.push(`Schema validation error: ${error.message}`); } return result; } } exports.SqlStaticAnalyzer = SqlStaticAnalyzer; /** * Convenience function for quick SQL static analysis */ async function analyzeSqlFiles(options) { const analyzer = new SqlStaticAnalyzer(options); return await analyzer.generateAnalysisReport(); } /** * Convenience function for validating a single SQL file */ async function validateSqlFile(sqlFilePath, schemaResolver) { const sqlDirectory = path.dirname(sqlFilePath); const filename = path.basename(sqlFilePath); const analyzer = new SqlStaticAnalyzer({ sqlDirectory, schemaResolver, debug: false }); const sqlFiles = analyzer.discoverSqlFiles(); const targetFile = sqlFiles.find(f => f.filename === filename); if (!targetFile) { return { filename, isValid: false, errors: [`File not found: ${sqlFilePath}`], hasJsonMapping: false }; } return await analyzer.validateSqlFile(targetFile); } //# sourceMappingURL=SqlStaticAnalyzer.js.map