UNPKG

node-apis

Version:

🚀 Advanced TypeScript API generator with clean architecture, comprehensive testing, and automatic formatting. Generate production-ready Node.js APIs with complete integration test suites.

522 lines • 28.9 kB
"use strict"; /** * File generation service */ 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.generateTestFiles = exports.generateApiFiles = void 0; const path = __importStar(require("path")); const file_operations_1 = require("../filesystem/file.operations"); const directory_operations_1 = require("../filesystem/directory.operations"); const crud_templates_1 = require("../templates/crud.templates"); const custom_templates_1 = require("../templates/custom.templates"); const crud_validators_1 = require("../templates/crud.validators"); const custom_validators_1 = require("../templates/custom.validators"); const crud_controllers_1 = require("../templates/crud.controllers"); const custom_controllers_1 = require("../templates/custom.controllers"); // Custom services not generated - business logic is now in handlers const routes_templates_1 = require("../templates/routes.templates"); const repository_templates_1 = require("../templates/repository.templates"); const crud_tests_1 = require("../templates/crud.tests"); const custom_tests_1 = require("../templates/custom.tests"); const trpc_tests_1 = require("../templates/trpc.tests"); const t3_tests_1 = require("../templates/t3.tests"); /** * Gets the test types for each operation */ const getTestTypesForOperation = (operation) => { switch (operation) { case 'create': return ['success', 'validation', 'duplicate', 'unauthorized']; case 'get': return ['success', 'not-found', 'invalid-id', 'unauthorized']; case 'list': return ['success', 'validation', 'unauthorized']; case 'update': return ['success', 'validation', 'not-found', 'unauthorized']; case 'delete': return ['success', 'not-found', 'invalid-id', 'unauthorized']; default: return ['success', 'validation', 'errors']; } }; const services_tests_1 = require("../templates/services.tests"); /** * Generates TypeScript files based on API type (types + validators + controllers + services + repository + routes) */ const generateApiFiles = async ({ moduleName, modulePath, apiType, appendMode = false, }) => { const generatedFiles = []; const typesDir = path.join(modulePath, 'types'); const validatorsDir = path.join(modulePath, 'validators'); const controllersDir = path.join(modulePath, 'controllers'); const repositoryDir = path.join(modulePath, 'repository'); if (apiType.type === 'crud') { // Generate type files const crudFileNames = (0, crud_templates_1.getCrudFileNames)({ moduleName }); const crudValidatorFileNames = (0, crud_validators_1.getCrudValidatorFileNames)({ moduleName }); const crudControllerFileNames = (0, crud_controllers_1.getCrudControllerFileNames)({ moduleName }); const crudOperations = ['create', 'get', 'list', 'delete', 'update']; for (let i = 0; i < crudFileNames.length; i++) { const fileName = crudFileNames[i]; const validatorFileName = crudValidatorFileNames[i]; const controllerFileName = crudControllerFileNames[i]; const operation = crudOperations[i]; // Generate type file const typeFilePath = path.join(typesDir, fileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: typeFilePath }))) { const typeContent = (0, crud_templates_1.generateCrudFileContent)({ operation, moduleName }); await (0, file_operations_1.writeFile)({ filePath: typeFilePath, content: typeContent }); generatedFiles.push({ fileName, filePath: typeFilePath, content: typeContent }); } // Generate validator file const validatorFilePath = path.join(validatorsDir, validatorFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: validatorFilePath }))) { const validatorContent = (0, crud_validators_1.generateCrudValidatorContent)({ operation, moduleName }); await (0, file_operations_1.writeFile)({ filePath: validatorFilePath, content: validatorContent }); generatedFiles.push({ fileName: validatorFileName, filePath: validatorFilePath, content: validatorContent, }); } // Generate controller file const controllerFilePath = path.join(controllersDir, controllerFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: controllerFilePath }))) { const controllerContent = (0, crud_controllers_1.generateCrudControllerContent)({ operation, moduleName }); await (0, file_operations_1.writeFile)({ filePath: controllerFilePath, content: controllerContent }); generatedFiles.push({ fileName: controllerFileName, filePath: controllerFilePath, content: controllerContent, }); } // Skip service generation - business logic is now in handlers } } else if (apiType.type === 'custom' && apiType.customNames) { // Generate type files const customFileNames = (0, custom_templates_1.getCustomFileNames)({ customNames: apiType.customNames, moduleName, }); const customValidatorFileNames = (0, custom_validators_1.getCustomValidatorFileNames)({ customNames: apiType.customNames, moduleName, }); const customControllerFileNames = (0, custom_controllers_1.getCustomControllerFileNames)({ customNames: apiType.customNames, moduleName, }); // Custom service files not generated - business logic is now in handlers for (let i = 0; i < customFileNames.length; i++) { const fileName = customFileNames[i]; const validatorFileName = customValidatorFileNames[i]; const controllerFileName = customControllerFileNames[i]; const customName = apiType.customNames[i]; // Generate type file const typeFilePath = path.join(typesDir, fileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: typeFilePath }))) { const typeContent = (0, custom_templates_1.generateCustomFileContent)({ customName, moduleName }); await (0, file_operations_1.writeFile)({ filePath: typeFilePath, content: typeContent }); generatedFiles.push({ fileName, filePath: typeFilePath, content: typeContent }); } // Generate validator file const validatorFilePath = path.join(validatorsDir, validatorFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: validatorFilePath }))) { const validatorContent = (0, custom_validators_1.generateCustomValidatorContent)({ customName, moduleName }); await (0, file_operations_1.writeFile)({ filePath: validatorFilePath, content: validatorContent }); generatedFiles.push({ fileName: validatorFileName, filePath: validatorFilePath, content: validatorContent, }); } // Generate controller file const controllerFilePath = path.join(controllersDir, controllerFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: controllerFilePath }))) { const controllerContent = (0, custom_controllers_1.generateCustomControllerContent)({ customName, moduleName }); await (0, file_operations_1.writeFile)({ filePath: controllerFilePath, content: controllerContent }); generatedFiles.push({ fileName: controllerFileName, filePath: controllerFilePath, content: controllerContent, }); } // Skip service generation for custom APIs - business logic is now in handlers } } // Generate repository file const repositoryFileName = `${moduleName}.repository.ts`; const repositoryFilePath = path.join(repositoryDir, repositoryFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: repositoryFilePath }))) { const repositoryContent = (0, repository_templates_1.generateRepositoryContent)({ moduleName, apiType }); await (0, file_operations_1.writeFile)({ filePath: repositoryFilePath, content: repositoryContent }); generatedFiles.push({ fileName: repositoryFileName, filePath: repositoryFilePath, content: repositoryContent, }); } // Generate route file const routeFileName = `${moduleName}.routes.ts`; const routeFilePath = path.join(modulePath, routeFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: routeFilePath }))) { const routeContent = (0, routes_templates_1.generateRouteContent)({ moduleName, apiType }); await (0, file_operations_1.writeFile)({ filePath: routeFilePath, content: routeContent }); generatedFiles.push({ fileName: routeFileName, filePath: routeFilePath, content: routeContent, }); } return generatedFiles; }; exports.generateApiFiles = generateApiFiles; /** * Generates test files based on API type */ const generateTestFiles = async ({ moduleName, testPath, apiType, appendMode = false, trpcStyle = false, framework = 'express', }) => { const generatedFiles = []; const moduleTestDir = path.join(testPath, moduleName); if (apiType.type === 'crud') { const crudOperations = ['create', 'get', 'list', 'update', 'delete']; if (trpcStyle || framework === 't3') { // Generate tRPC-style tests if (framework === 't3') { // T3 framework: Use modular test structure (success/validation/failure folders) for (const operation of crudOperations) { const operationDir = path.join(moduleTestDir, operation); await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); // Create subdirectories for each test category const successDir = path.join(operationDir, 'success'); const validationDir = path.join(operationDir, 'validation'); const failureDir = path.join(operationDir, 'failure'); await (0, directory_operations_1.ensureDirectory)({ dirPath: successDir }); await (0, directory_operations_1.ensureDirectory)({ dirPath: validationDir }); await (0, directory_operations_1.ensureDirectory)({ dirPath: failureDir }); // Define test files for each category const testFiles = [ 'success/basic.test.ts', 'success/variations.test.ts', 'validation/required.test.ts', 'validation/types.test.ts', ]; // Add operation-specific failure tests if (operation === 'create') { testFiles.push('failure/duplicate.test.ts'); testFiles.push('failure/unauthorized.test.ts'); } else if (operation === 'list') { testFiles.push('failure/unauthorized.test.ts'); } else { // get, update, delete operations testFiles.push('failure/not-found.test.ts'); testFiles.push('failure/unauthorized.test.ts'); } // Generate each test file for (const testFile of testFiles) { const testFilePath = path.join(operationDir, testFile); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, t3_tests_1.generateT3TestContent)({ operation, testType: testFile, // Pass the full path like "success/basic.test.ts" moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testFile, filePath: testFilePath, content: testContent, }); } } } } else { // Regular tRPC: Use flat test structure const testTypes = ['procedure.test.ts', 'validation.test.ts', 'errors.test.ts']; for (const operation of crudOperations) { const operationDir = path.join(moduleTestDir, operation); await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); for (const testType of testTypes) { const testFilePath = path.join(operationDir, testType); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, trpc_tests_1.generateTrpcTestContent)({ operation, testType, moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testType, filePath: testFilePath, content: testContent, }); } } } } // Generate shared tRPC helpers (only for non-T3 frameworks) if (framework !== 't3') { const sharedDir = path.join(moduleTestDir, 'shared'); await (0, directory_operations_1.ensureDirectory)({ dirPath: sharedDir }); const helpersFilePath = path.join(sharedDir, 'trpc-helpers.ts'); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: helpersFilePath }))) { const helpersContent = (0, trpc_tests_1.generateTrpcTestContent)({ operation: '', testType: 'trpc-helpers.ts', moduleName }); await (0, file_operations_1.writeFile)({ filePath: helpersFilePath, content: helpersContent }); generatedFiles.push({ fileName: 'trpc-helpers.ts', filePath: helpersFilePath, content: helpersContent, }); } } } else { // Generate REST-style tests - multiple specialized test files per operation for (const operation of crudOperations) { const operationDir = path.join(moduleTestDir, operation); // Ensure operation directory exists await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); // Get test types for this operation const testTypes = getTestTypesForOperation(operation); for (const testType of testTypes) { const testFileName = `${testType}.test.ts`; const testFilePath = path.join(operationDir, testFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, crud_tests_1.generateCrudTestContent)({ operation, moduleName, testType: testType }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testFileName, filePath: testFilePath, content: testContent, }); } } } } } else if (apiType.type === 'custom' && apiType.customNames) { if (trpcStyle || framework === 't3') { // Generate tRPC-style tests for custom operations if (framework === 't3') { // T3 framework: Use modular test structure for custom operations for (const customName of apiType.customNames) { const operationDir = path.join(moduleTestDir, customName); await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); // Create subdirectories for each test category const successDir = path.join(operationDir, 'success'); const validationDir = path.join(operationDir, 'validation'); const failureDir = path.join(operationDir, 'failure'); await (0, directory_operations_1.ensureDirectory)({ dirPath: successDir }); await (0, directory_operations_1.ensureDirectory)({ dirPath: validationDir }); await (0, directory_operations_1.ensureDirectory)({ dirPath: failureDir }); // Define test files for custom operations (generic structure) const testFiles = [ 'success/basic.test.ts', 'success/variations.test.ts', 'validation/required.test.ts', 'validation/types.test.ts', 'failure/unauthorized.test.ts', // All custom operations get unauthorized test ]; // Generate each test file for (const testFile of testFiles) { const testFilePath = path.join(operationDir, testFile); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, t3_tests_1.generateT3TestContent)({ operation: customName, testType: testFile, moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testFile, filePath: testFilePath, content: testContent, }); } } } } else { // Regular tRPC: Use flat test structure const testTypes = ['procedure.test.ts', 'validation.test.ts', 'errors.test.ts']; for (const customName of apiType.customNames) { const operationDir = path.join(moduleTestDir, customName); await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); for (const testType of testTypes) { const testFilePath = path.join(operationDir, testType); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, trpc_tests_1.generateTrpcTestContent)({ operation: customName, testType, moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testType, filePath: testFilePath, content: testContent, }); } } } } } else { // Generate REST-style tests for custom operations for (const customName of apiType.customNames) { const operationDir = path.join(moduleTestDir, customName); // Ensure operation directory exists await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); // Get test types for custom operations const testTypes = ['success', 'validation', 'unauthorized']; for (const testType of testTypes) { const testFileName = `${testType}.test.ts`; const testFilePath = path.join(operationDir, testFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, custom_tests_1.generateCustomTestContent)({ customName, moduleName, testType: testType }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testFileName, filePath: testFilePath, content: testContent, }); } } } } } else if (apiType.type === 'services' && apiType.serviceNames) { if (trpcStyle || framework === 't3') { // Generate tRPC-style tests for service operations if (framework === 't3') { // T3 framework: Use modular test structure for service operations for (const serviceName of apiType.serviceNames) { const operationDir = path.join(moduleTestDir, serviceName); await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); // Create subdirectories for each test category const successDir = path.join(operationDir, 'success'); const validationDir = path.join(operationDir, 'validation'); const failureDir = path.join(operationDir, 'failure'); await (0, directory_operations_1.ensureDirectory)({ dirPath: successDir }); await (0, directory_operations_1.ensureDirectory)({ dirPath: validationDir }); await (0, directory_operations_1.ensureDirectory)({ dirPath: failureDir }); // Define test files for service operations const testFiles = [ 'success/basic.test.ts', 'success/variations.test.ts', 'validation/required.test.ts', 'validation/types.test.ts', 'failure/unauthorized.test.ts', ]; // Generate each test file for (const testFile of testFiles) { const testFilePath = path.join(operationDir, testFile); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, t3_tests_1.generateT3TestContent)({ operation: serviceName, testType: testFile, moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testFile, filePath: testFilePath, content: testContent, }); } } } } else { // Regular tRPC: Use flat test structure const testTypes = ['procedure.test.ts', 'validation.test.ts', 'errors.test.ts']; for (const serviceName of apiType.serviceNames) { const operationDir = path.join(moduleTestDir, serviceName); await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); for (const testType of testTypes) { const testFilePath = path.join(operationDir, testType); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { const testContent = (0, trpc_tests_1.generateTrpcTestContent)({ operation: serviceName, testType, moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testType, filePath: testFilePath, content: testContent, }); } } } } } else { // Generate REST-style tests for service operations for (const serviceName of apiType.serviceNames) { const operationDir = path.join(moduleTestDir, serviceName); // Ensure operation directory exists await (0, directory_operations_1.ensureDirectory)({ dirPath: operationDir }); const testFileName = `${serviceName}.test.ts`; const testFilePath = path.join(operationDir, testFileName); if (!appendMode || !(await (0, file_operations_1.fileExists)({ filePath: testFilePath }))) { // Generate comprehensive test content for services const testContent = (0, services_tests_1.generateServiceComprehensiveTestContent)({ serviceName, moduleName }); await (0, file_operations_1.writeFile)({ filePath: testFilePath, content: testContent }); generatedFiles.push({ fileName: testFileName, filePath: testFilePath, content: testContent, }); } } } } return generatedFiles; }; exports.generateTestFiles = generateTestFiles; //# sourceMappingURL=file-generator.service.js.map