UNPKG

credl-parser-evaluator

Version:

TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations

470 lines 16.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fileUtils = void 0; exports.checkFileExists = checkFileExists; exports.checkFileExistsSync = checkFileExistsSync; exports.getFileStats = getFileStats; exports.getFileStatsSync = getFileStatsSync; exports.ensureDirectory = ensureDirectory; exports.ensureDirectorySync = ensureDirectorySync; exports.isDirectoryEmpty = isDirectoryEmpty; exports.safeWriteFile = safeWriteFile; exports.safeReadFile = safeReadFile; exports.resolvePath = resolvePath; exports.relativePath = relativePath; exports.getFileExtension = getFileExtension; exports.getBaseName = getBaseName; exports.changeExtension = changeExtension; exports.findFiles = findFiles; exports.findCREDLFiles = findCREDLFiles; exports.validateFile = validateFile; exports.validateMultipleFiles = validateMultipleFiles; exports.copyFile = copyFile; exports.moveFile = moveFile; exports.deleteFile = deleteFile; exports.deleteDirectory = deleteDirectory; exports.createTempFile = createTempFile; const fs_1 = require("fs"); const path_1 = require("path"); const glob_1 = require("glob"); // File existence and metadata utilities async function checkFileExists(filePath) { try { await fs_1.promises.access(filePath); return true; } catch { return false; } } function checkFileExistsSync(filePath) { return (0, fs_1.existsSync)(filePath); } async function getFileStats(filePath) { try { const stats = await fs_1.promises.stat(filePath); return convertStatsToFileStats(stats, filePath); } catch { return null; } } function getFileStatsSync(filePath) { try { const stats = (0, fs_1.statSync)(filePath); return convertStatsToFileStats(stats, filePath); } catch { return null; } } function convertStatsToFileStats(stats, filePath) { return { size: stats.size, modified: stats.mtime, created: stats.birthtime, isFile: stats.isFile(), isDirectory: stats.isDirectory(), permissions: { readable: checkReadable(filePath), writable: checkWritable(filePath), executable: checkExecutable(filePath) } }; } function checkReadable(filePath) { try { require('fs').accessSync(filePath, require('fs').constants.R_OK); return true; } catch { return false; } } function checkWritable(filePath) { try { require('fs').accessSync(filePath, require('fs').constants.W_OK); return true; } catch { return false; } } function checkExecutable(filePath) { try { require('fs').accessSync(filePath, require('fs').constants.X_OK); return true; } catch { return false; } } // Directory operations async function ensureDirectory(dirPath) { try { await fs_1.promises.mkdir(dirPath, { recursive: true }); } catch (error) { if (error instanceof Error && 'code' in error && error.code !== 'EEXIST') { throw new Error(`Failed to create directory ${dirPath}: ${error.message}`); } } } function ensureDirectorySync(dirPath) { try { require('fs').mkdirSync(dirPath, { recursive: true }); } catch (error) { if (error instanceof Error && 'code' in error && error.code !== 'EEXIST') { throw new Error(`Failed to create directory ${dirPath}: ${error.message}`); } } } async function isDirectoryEmpty(dirPath) { try { const files = await fs_1.promises.readdir(dirPath); return files.length === 0; } catch { return true; // Consider non-existent directories as empty } } // Safe file operations async function safeWriteFile(filePath, content, options = {}) { const { backup = false, backupSuffix = '.bak', createDirectories = true, overwrite = true, encoding = 'utf8' } = options; // Resolve to absolute path const absolutePath = (0, path_1.resolve)(filePath); const directory = (0, path_1.dirname)(absolutePath); // Create directories if needed if (createDirectories) { await ensureDirectory(directory); } // Check if file exists and handle overwrite const fileExists = await checkFileExists(absolutePath); if (fileExists && !overwrite) { throw new Error(`File already exists and overwrite is disabled: ${filePath}`); } // Create backup if requested if (backup && fileExists) { const backupPath = absolutePath + backupSuffix; await fs_1.promises.copyFile(absolutePath, backupPath); } // Write the file try { await fs_1.promises.writeFile(absolutePath, content, encoding); } catch (error) { throw new Error(`Failed to write file ${filePath}: ${error instanceof Error ? error.message : String(error)}`); } } async function safeReadFile(filePath, encoding = 'utf8') { try { const absolutePath = (0, path_1.resolve)(filePath); // Check if file exists if (!await checkFileExists(absolutePath)) { throw new Error(`File not found: ${filePath}`); } // Check if it's actually a file const stats = await getFileStats(absolutePath); if (!stats?.isFile) { throw new Error(`Path is not a file: ${filePath}`); } // Read the file return await fs_1.promises.readFile(absolutePath, encoding); } catch (error) { if (error instanceof Error && error.message.includes('File not found')) { throw error; } throw new Error(`Failed to read file ${filePath}: ${error instanceof Error ? error.message : String(error)}`); } } // Path utilities function resolvePath(inputPath, basePath) { if (basePath) { return (0, path_1.resolve)(basePath, inputPath); } return (0, path_1.resolve)(inputPath); } function relativePath(from, to) { return (0, path_1.relative)(from, to); } function getFileExtension(filePath) { return (0, path_1.extname)(filePath).toLowerCase(); } function getBaseName(filePath, ext) { return (0, path_1.basename)(filePath, ext); } function changeExtension(filePath, newExt) { const dir = (0, path_1.dirname)(filePath); const name = (0, path_1.basename)(filePath, (0, path_1.extname)(filePath)); const extension = newExt.startsWith('.') ? newExt : `.${newExt}`; return (0, path_1.join)(dir, name + extension); } // Glob pattern matching async function findFiles(pattern, options = {}) { const { cwd = process.cwd(), absolute = false, onlyFiles = true, onlyDirectories = false, ignore = [], caseSensitive = true } = options; try { const patterns = Array.isArray(pattern) ? pattern : [pattern]; const allFiles = []; for (const pat of patterns) { const files = await (0, glob_1.glob)(pat, { cwd, absolute, ignore, nocase: !caseSensitive, nodir: onlyFiles && !onlyDirectories }); allFiles.push(...files); } // Remove duplicates and sort return [...new Set(allFiles)].sort(); } catch (error) { throw new Error(`Failed to find files with pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`); } } async function findCREDLFiles(searchPath = '.', options = {}) { const { recursive = true } = options; const patterns = recursive ? ['**/*.credl', '**/*.yaml', '**/*.yml'] : ['*.credl', '*.yaml', '*.yml']; return await findFiles(patterns, { cwd: searchPath, absolute: true, onlyFiles: true, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); } async function validateFile(filePath, options = {}) { const { maxSize = 50 * 1024 * 1024, // 50MB default allowedExtensions = ['.credl', '.yaml', '.yml'], requireExtension = true, checkReadable = true } = options; const result = { isValid: true, errors: [], warnings: [], info: { path: (0, path_1.resolve)(filePath), size: 0, extension: '', lastModified: new Date() } }; try { // Check if file exists if (!await checkFileExists(filePath)) { result.errors.push(`File does not exist: ${filePath}`); result.isValid = false; return result; } // Get file stats const stats = await getFileStats(filePath); if (!stats) { result.errors.push(`Cannot access file: ${filePath}`); result.isValid = false; return result; } // Update info result.info.size = stats.size; result.info.lastModified = stats.modified; result.info.extension = getFileExtension(filePath); // Check if it's a file if (!stats.isFile) { result.errors.push(`Path is not a file: ${filePath}`); result.isValid = false; } // Check file size if (stats.size > maxSize) { result.errors.push(`File is too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max: ${(maxSize / 1024 / 1024).toFixed(2)}MB)`); result.isValid = false; } // Check if file is empty if (stats.size === 0) { result.warnings.push('File is empty'); } // Check file extension const extension = getFileExtension(filePath); if (requireExtension && !extension) { result.warnings.push('File has no extension'); } if (extension && allowedExtensions.length > 0 && !allowedExtensions.includes(extension)) { result.warnings.push(`Unexpected file extension: ${extension} (expected: ${allowedExtensions.join(', ')})`); } // Check readability if (checkReadable && !stats.permissions.readable) { result.errors.push(`File is not readable: ${filePath}`); result.isValid = false; } } catch (error) { result.errors.push(`File validation error: ${error instanceof Error ? error.message : String(error)}`); result.isValid = false; } return result; } // Batch file operations async function validateMultipleFiles(filePaths, options = {}) { const { continueOnError = true, maxConcurrent = 10 } = options; const results = []; // Process files in batches to avoid overwhelming the system for (let i = 0; i < filePaths.length; i += maxConcurrent) { const batch = filePaths.slice(i, i + maxConcurrent); const batchPromises = batch.map(async (filePath) => { try { return await validateFile(filePath); } catch (error) { const errorResult = { isValid: false, errors: [`Validation failed: ${error instanceof Error ? error.message : String(error)}`], warnings: [], info: { path: (0, path_1.resolve)(filePath), size: 0, extension: getFileExtension(filePath), lastModified: new Date() } }; if (!continueOnError) { throw error; } return errorResult; } }); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); } return results; } // File copying and moving utilities async function copyFile(source, destination, options = {}) { const { overwrite = false, createDirectories = true } = options; const sourcePath = (0, path_1.resolve)(source); const destPath = (0, path_1.resolve)(destination); // Check source exists if (!await checkFileExists(sourcePath)) { throw new Error(`Source file does not exist: ${source}`); } // Check if destination exists const destExists = await checkFileExists(destPath); if (destExists && !overwrite) { throw new Error(`Destination file already exists: ${destination}`); } // Create destination directory if needed if (createDirectories) { await ensureDirectory((0, path_1.dirname)(destPath)); } try { await fs_1.promises.copyFile(sourcePath, destPath); } catch (error) { throw new Error(`Failed to copy file: ${error instanceof Error ? error.message : String(error)}`); } } async function moveFile(source, destination, options = {}) { const { overwrite = false, createDirectories = true } = options; const sourcePath = (0, path_1.resolve)(source); const destPath = (0, path_1.resolve)(destination); // Check source exists if (!await checkFileExists(sourcePath)) { throw new Error(`Source file does not exist: ${source}`); } // Check if destination exists const destExists = await checkFileExists(destPath); if (destExists && !overwrite) { throw new Error(`Destination file already exists: ${destination}`); } // Create destination directory if needed if (createDirectories) { await ensureDirectory((0, path_1.dirname)(destPath)); } try { await fs_1.promises.rename(sourcePath, destPath); } catch (error) { throw new Error(`Failed to move file: ${error instanceof Error ? error.message : String(error)}`); } } // Cleanup utilities async function deleteFile(filePath, options = {}) { const { force = false } = options; const absolutePath = (0, path_1.resolve)(filePath); if (!await checkFileExists(absolutePath)) { if (!force) { throw new Error(`File does not exist: ${filePath}`); } return; // Silently succeed if force is true } try { await fs_1.promises.unlink(absolutePath); } catch (error) { throw new Error(`Failed to delete file: ${error instanceof Error ? error.message : String(error)}`); } } async function deleteDirectory(dirPath, options = {}) { const { recursive = false, force = false } = options; const absolutePath = (0, path_1.resolve)(dirPath); if (!await checkFileExists(absolutePath)) { if (!force) { throw new Error(`Directory does not exist: ${dirPath}`); } return; // Silently succeed if force is true } try { await fs_1.promises.rmdir(absolutePath, { recursive }); } catch (error) { throw new Error(`Failed to delete directory: ${error instanceof Error ? error.message : String(error)}`); } } // Temporary file utilities async function createTempFile(prefix = 'credl-', suffix = '.tmp') { const tmpDir = require('os').tmpdir(); const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); const tempFileName = `${prefix}${timestamp}-${random}${suffix}`; const tempPath = (0, path_1.join)(tmpDir, tempFileName); // Ensure the temp file doesn't already exist if (await checkFileExists(tempPath)) { return createTempFile(prefix, suffix); // Retry with different name } // Create empty file await fs_1.promises.writeFile(tempPath, '', 'utf8'); return tempPath; } // Export utility functions exports.fileUtils = { // Existence and stats exists: checkFileExists, existsSync: checkFileExistsSync, stats: getFileStats, statsSync: getFileStatsSync, // Directory operations ensureDir: ensureDirectory, ensureDirSync: ensureDirectorySync, isDirEmpty: isDirectoryEmpty, // Safe operations safeWrite: safeWriteFile, safeRead: safeReadFile, // Path utilities resolve: resolvePath, relative: relativePath, ext: getFileExtension, basename: getBaseName, changeExt: changeExtension, // File finding find: findFiles, findCREDL: findCREDLFiles, // Validation validate: validateFile, validateMultiple: validateMultipleFiles, // File operations copy: copyFile, move: moveFile, delete: deleteFile, deleteDir: deleteDirectory, // Temporary files createTemp: createTempFile }; //# sourceMappingURL=files.js.map