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
JavaScript
"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