UNPKG

@megaorm/cli

Version:

This package allows you to communicate with MegaORM via commands directly from the command line interface (CLI).

185 lines 8.31 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ModelHandler = exports.ModelHandlerError = void 0; const promises_1 = __importDefault(require("fs/promises")); const path_1 = require("path"); const text_1 = require("@megaorm/text"); const test_1 = require("@megaorm/test"); /** * Custom error class for handling errors related to model operations. */ class ModelHandlerError extends Error { } exports.ModelHandlerError = ModelHandlerError; /** * Checks if the provided name matches the pattern for a valid model file. * * @param name The name to check if it matches a model file pattern. * @returns `true` if the name matches the model file pattern (e.g., `User.js`), otherwise `false`. */ function isFile(name) { // Pattern example: User.js return /^[A-Z][a-z0-9]*([A-Z][a-z0-9]*)*\.(ts|js)$/.test(name); } /** * Checks if the provided name matches the pattern for a valid TypeScript output file (either a map or declaration file). * * @param name - The name to check if it matches a TypeScript output file pattern. * @returns `true` if the name matches the pattern for a `.js.map` or `.d.ts` file, otherwise `false`. */ function isMappedFile(name) { // Pattern example: User.map.js or User.d.ts return /^[A-Z][a-z0-9]*([A-Z][a-z0-9]*)*(\.js\.map|\.d\.ts)$/.test(name); } /** * Safely deletes multiple model files at the specified paths. * * This function calls `unlink` for each path provided in the array, attempting to * remove each corresponding file. * * @param paths An array of strings representing the absolute paths of the model files to be deleted. * @returns A promise that resolves when all files are removed. */ function unlink(paths) { return new Promise((resolve, reject) => { return Promise.all(paths.map((path) => promises_1.default.unlink(path))) .then(resolve) .catch(reject); }); } /** * Creates a model class name based on the table name. * * @param name The snake_case table name. * @returns A model class name. */ function toClassName(name) { return name .split('_') .map((part) => (0, text_1.toSingular)(part)) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(''); } /** * This class provides functionality for adding, and removing model files * */ class ModelHandler { /** * Instantiates a new ModelHandler. */ constructor() { this.assets = (0, path_1.resolve)(__dirname, '../../assets'); } /** * Collects the absolute paths of model files from the specified directory. * * @param path The directory path from which to collect model files. * @param map A boolean indicating whether to include mapped files (e.g., `.js.map`) in the resulting paths. * @returns A promise that resolves with an array of absolute paths to valid model files. * @throws `ModelHandlerError` if there is an issue collecting the files or if an invalid model file is encountered. */ collectPaths(path, map) { return new Promise((resolve, reject) => { promises_1.default.readdir(path) .then((result) => { // check if the directory is empty if ((0, test_1.isEmptyArr)(result)) return resolve(result); // filter map files const files = map ? result : result.filter((name) => !isMappedFile(name)); // validate each file for (const file of files) { if (!isFile(file)) { if (map && isMappedFile(file)) continue; return reject(new ModelHandlerError(`Invalid model file: ${String(file)}`)); } } return resolve(files.map((file) => (0, path_1.resolve)(path, file))); }) .catch((e) => reject(new ModelHandlerError(e.message))); }); } /** * Creates a new model file in the specified path. * * This method creates a new file for the specified table name in the given directory path. * You can choose to create a TypeScript file or a JavaScript file based on the provided boolean flag. * * @param name The snake_case table name for which the model is created. * @param path The directory path where the model file will be created. * @param ts A boolean indicating whether to create a TypeScript file (`true`) or a JavaScript file (`false`). * @returns A promise that resolves with a success message indicating the path of the created model file. * @throws `ModelHandlerError` If the table name is invalid, the path is empty, or if any errors occur during file operations. * @note The table name must be a snake_case table name, e.g., users, category_product. */ add(name, path, ts) { return new Promise((resolve, reject) => { if (!(0, test_1.isSnakeCase)(name)) { return reject(new ModelHandlerError(`Invalid table name: ${String(name)}`)); } if (!(0, test_1.isFullStr)(path)) { return reject(new ModelHandlerError(`Invalid path: ${String(path)}`)); } if (!(0, test_1.isBool)(ts)) ts = false; const ext = ts ? 'ts' : 'js'; const layout = ts ? 'ts/model.txt' : 'js/model.txt'; const content = (0, path_1.resolve)(this.assets, layout); promises_1.default.readFile(content, 'utf-8') .then((template) => { const className = toClassName(name); const fileName = className.concat('.', ext); const filePath = (0, path_1.resolve)(path, fileName); const fileContent = template .replace(/\[className\]/g, className) .replace(/\[tableName\]/g, name) .replace(/\[fileName\]/g, fileName); // Create model file promises_1.default.writeFile(filePath, fileContent, 'utf-8') .then(() => resolve(`Model added in: ${filePath}`)) .catch((e) => reject(new ModelHandlerError(e.message))); }) .catch((e) => reject(new ModelHandlerError(e.message))); }); } /** * Removes the model associated with the given table name from the specified folder path. * * @param name The snake_case table name whose associated model is to be removed. * @param path The directory path where the model files are located. * @returns A promise that resolves with a success message indicating how many model files were removed. * @throws `ModelHandlerError` If the table name is invalid, the path is empty. * @note The table name must be a snake_case table name, e.g., users, category_product. */ remove(name, path) { return new Promise((resolve, reject) => { if (!(0, test_1.isSnakeCase)(name)) { return reject(new ModelHandlerError(`Invalid table name: ${String(name)}`)); } if (!(0, test_1.isFullStr)(path)) { return reject(new ModelHandlerError(`Invalid path: ${String(path)}`)); } return this.collectPaths(path, true) .then((paths) => { const unlinkPaths = paths.filter((path) => new RegExp(`${toClassName(name)}\.(ts|js|js\.map|d\.ts)$`).test(path)); if (unlinkPaths.length === 0) { return Promise.reject(new ModelHandlerError(`No model found for table: ${name}`)); } const message = `${unlinkPaths.length} model file${unlinkPaths.length === 1 ? '' : 's'} removed in:\n${unlinkPaths.join('\n')}`; return unlink(unlinkPaths) .then(() => resolve(message)) .catch(reject); }) .catch(reject); }); } } exports.ModelHandler = ModelHandler; //# sourceMappingURL=ModelHandler.js.map