UNPKG

@expressots/cli

Version:

Expressots CLI - modern, fast, lightweight nodejs web framework (@cli)

315 lines (314 loc) 11.9 kB
"use strict"; 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkPathStyle = exports.extractFirstWord = exports.getNameWithScaffoldPattern = exports.schematicFolder = exports.writeTemplate = exports.getHttpMethod = exports.splitTarget = exports.getFileNameWithoutExtension = exports.validateAndPrepareFile = void 0; const node_fs_1 = require("node:fs"); const nodePath = __importStar(require("node:path")); const mustache_1 = require("mustache"); const string_utils_1 = require("./string-utils"); const cli_ui_1 = require("../../utils/cli-ui"); const verify_file_exists_1 = require("../../utils/verify-file-exists"); const compiler_1 = __importDefault(require("../../utils/compiler")); /** * Create a template based on the schematic * @param fp * @returns the file created */ async function validateAndPrepareFile(fp) { const { sourceRoot, scaffoldSchematics, opinionated } = fp.expressoConfig; if (sourceRoot === "") { (0, cli_ui_1.printError)("You must specify a source root in your expressots.config.ts", "sourceRoot"); process.exit(1); } if (opinionated) { const folderSchematic = (0, exports.schematicFolder)(fp.schematic); const folderToScaffold = `${sourceRoot}/${folderSchematic}`; const { path, file, className, moduleName, modulePath } = await (0, exports.splitTarget)({ target: fp.target, schematic: fp.schematic, }); const outputPath = `${folderToScaffold}/${path}/${file}`; await (0, verify_file_exists_1.verifyIfFileExists)(outputPath, fp.schematic); (0, node_fs_1.mkdirSync)(`${folderToScaffold}/${path}`, { recursive: true }); return { path, file, className, moduleName, modulePath, outputPath, folderToScaffold, fileName: getFileNameWithoutExtension(file), schematic: fp.schematic, }; } const folderSchematic = ""; const folderToScaffold = `${sourceRoot}/${folderSchematic}`; const { path, file, className, moduleName, modulePath } = await (0, exports.splitTarget)({ target: fp.target, schematic: fp.schematic, }); const fileBaseSchema = scaffoldSchematics?.[fp.schematic]; const validateFileSchema = fileBaseSchema !== undefined ? file.replace(fp.schematic, fileBaseSchema) : file; const outputPath = `${folderToScaffold}/${path}/${validateFileSchema}`; await (0, verify_file_exists_1.verifyIfFileExists)(outputPath, fp.schematic); (0, node_fs_1.mkdirSync)(`${folderToScaffold}/${path}`, { recursive: true }); return { path, file, className, moduleName, modulePath, outputPath, folderToScaffold, fileName: getFileNameWithoutExtension(file), schematic: fileBaseSchema !== undefined ? fileBaseSchema : fp.schematic, }; } exports.validateAndPrepareFile = validateAndPrepareFile; /** * Get the file name without the extension * @param filePath * @returns the file name */ function getFileNameWithoutExtension(filePath) { return filePath.split(".")[0]; } exports.getFileNameWithoutExtension = getFileNameWithoutExtension; /** * Split the target into path, file, class name, module name and module path * @param target * @param schematic * @returns the split target */ const splitTarget = async ({ target, schematic, }) => { const pathContent = target .split("/") .filter((item) => item !== ""); const endsWithSlash = target.endsWith("/"); let path = ""; let fileName = ""; let module = ""; let modulePath = ""; if (target.includes("/") || target.includes("\\") || target.includes("//")) { if (schematic === "service") schematic = "controller"; if (schematic === "service" || (schematic === "controller" && pathContent.length > 4)) { (0, cli_ui_1.printError)("Max path depth is 4.", pathContent.join("/")); process.exit(1); } if (endsWithSlash) { fileName = pathContent[pathContent.length - 1]; path = pathContent.join("/"); module = pathContent.length == 1 ? pathContent[pathContent.length - 1] : pathContent[pathContent.length - 2]; modulePath = pathContent.slice(0, -1).join("/"); } else { fileName = pathContent[pathContent.length - 1]; path = pathContent.slice(0, -1).join("/"); module = pathContent.length == 2 ? pathContent[pathContent.length - 2] : pathContent[pathContent.length - 3]; modulePath = pathContent.slice(0, -2).join("/"); } return { path, file: `${await (0, exports.getNameWithScaffoldPattern)(fileName)}.${schematic}.ts`, className: (0, string_utils_1.anyCaseToPascalCase)(fileName), moduleName: module, modulePath, }; } else { if (schematic === "service") schematic = "controller"; // 1. Extract the name (first part of the target) const [name, ...remainingPath] = target.split("/"); // 2. Check if the name is camelCase or kebab-case const camelCaseRegex = /[A-Z]/; const kebabCaseRegex = /[_\-\s]+/; const isCamelCase = camelCaseRegex.test(name); const isKebabCase = kebabCaseRegex.test(name); if (isCamelCase || isKebabCase) { const [wordName, ...path] = name ? name .split(isCamelCase ? /(?=[A-Z])/ : kebabCaseRegex) .map((word) => word.toLowerCase()) : []; return { path: `${wordName}/${pathEdgeCase(path)}${pathEdgeCase(remainingPath)}`, file: `${await (0, exports.getNameWithScaffoldPattern)(name)}.${schematic}.ts`, className: (0, string_utils_1.anyCaseToPascalCase)(name), moduleName: wordName, modulePath: pathContent[0].split("-")[1], }; } // 3. Return the base case return { path: "", file: `${await (0, exports.getNameWithScaffoldPattern)(name)}.${schematic}.ts`, className: (0, string_utils_1.anyCaseToPascalCase)(name), moduleName: name, modulePath: "", }; } }; exports.splitTarget = splitTarget; /** * Write the template based on the http method * @param method - the http method * @returns decorator - the decorator to be used */ const getHttpMethod = (method) => { switch (method) { case "put": return "Put"; case "post": return "Post"; case "patch": return "Patch"; case "delete": return "Delete"; default: return "Get"; } }; exports.getHttpMethod = getHttpMethod; /** * Write the template based on the schematics * @param outputPath - the output path * @param template - the template to be used * @returns void */ const writeTemplate = ({ outputPath, template: { path, data }, }) => { (0, node_fs_1.writeFileSync)(outputPath, (0, mustache_1.render)((0, node_fs_1.readFileSync)(nodePath.join(__dirname, path), "utf8"), data)); }; exports.writeTemplate = writeTemplate; /** * Returns the folder where the schematic should be placed * @param schematic */ const schematicFolder = (schematic) => { switch (schematic) { case "usecase": return "useCases"; case "controller": return "useCases"; case "dto": return "useCases"; case "service": return "useCases"; case "provider": return "providers"; case "entity": return "entities"; case "middleware": return "providers/middlewares"; case "module": return "useCases"; } return undefined; }; exports.schematicFolder = schematicFolder; /** * Get the name with the scaffold pattern * @param name * @returns the name in the scaffold pattern */ const getNameWithScaffoldPattern = async (name) => { const configObject = await compiler_1.default.loadConfig(); switch (configObject.scaffoldPattern) { case "lowercase" /* Pattern.LOWER_CASE */: return (0, string_utils_1.anyCaseToLowerCase)(name); case "kebab-case" /* Pattern.KEBAB_CASE */: return (0, string_utils_1.anyCaseToKebabCase)(name); case "PascalCase" /* Pattern.PASCAL_CASE */: return (0, string_utils_1.anyCaseToPascalCase)(name); case "camelCase" /* Pattern.CAMEL_CASE */: return (0, string_utils_1.anyCaseToCamelCase)(name); } }; exports.getNameWithScaffoldPattern = getNameWithScaffoldPattern; /** * Get the path edge case * @param path * @returns the path edge case from the last element of the path */ const pathEdgeCase = (path) => { return `${path.join("/")}${path.length > 0 ? "/" : ""}`; }; /** * Extract the first word from a file and convert it to the scaffold pattern * @param file * @returns the first word in the scaffold pattern */ async function extractFirstWord(file) { const f = file.split(".")[0]; const regex = /(?:-|(?<=[a-z])(?=[A-Z]))/; const firstWord = f.split(regex)[0]; const config = await compiler_1.default.loadConfig(); switch (config.scaffoldPattern) { case "lowercase" /* Pattern.LOWER_CASE */: return (0, string_utils_1.anyCaseToLowerCase)(firstWord); case "kebab-case" /* Pattern.KEBAB_CASE */: return (0, string_utils_1.anyCaseToKebabCase)(firstWord); case "PascalCase" /* Pattern.PASCAL_CASE */: return (0, string_utils_1.anyCaseToPascalCase)(firstWord); case "camelCase" /* Pattern.CAMEL_CASE */: return (0, string_utils_1.anyCaseToCamelCase)(firstWord); } } exports.extractFirstWord = extractFirstWord; /** * Check if the path is a nested path, a single path or a sugar path * @param path * @returns the path style */ const checkPathStyle = (path) => { const singleOrNestedPathRegex = /\/|\\/; const sugarPathRegex = /^\w+-\w+$/; if (singleOrNestedPathRegex.test(path)) { return "nested" /* PathStyle.Nested */; } else if (sugarPathRegex.test(path)) { return "sugar" /* PathStyle.Sugar */; } else { return "single" /* PathStyle.Single */; } }; exports.checkPathStyle = checkPathStyle;