UNPKG

typescript-scaffolder

Version:

![npm version](https://img.shields.io/npm/v/typescript-scaffolder) ![coverage](https://img.shields.io/badge/coverage-97.38%25-green)

139 lines (138 loc) 6.46 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.deriveMethodName = deriveMethodName; exports.generateSequenceRunner = generateSequenceRunner; exports.generateSequenceFromFile = generateSequenceFromFile; exports.generateSequencesFromPath = generateSequencesFromPath; const sequence_tree_builder_1 = require("../../utils/sequence-tree-builder"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const structure_validators_1 = require("../../utils/structure-validators"); const logger_1 = require("../../utils/logger"); const file_system_1 = require("../../utils/file-system"); /** * Used to create a normalised method name that reflects the REST API action * * @param node */ function deriveMethodName(node) { const endpoint = node.step.endpoint || ''; const parts = endpoint.split('/') .filter((p) => p && !p.startsWith(':')); const objectName = parts[parts.length - 1] || 'unknown'; let method = 'get'; // default if (node.type === 'fetchList') method = 'getAll'; if (node.type === 'action') { const actionStep = node.step; method = actionStep.method.toLowerCase(); } return `${method}_${objectName}`; } function interpolateTemplateObject(obj) { if (typeof obj === 'string') { const match = obj.match(/^{{(.+?)}}$/); return match ? match[1] : JSON.stringify(obj); } else if (Array.isArray(obj)) { return `[${obj.map(interpolateTemplateObject).join(', ')}]`; } else if (typeof obj === 'object' && obj !== null) { const entries = Object.entries(obj).map(([key, value]) => { return `${key}: ${interpolateTemplateObject(value)}`; }); return `{ ${entries.join(', ')} }`; } else { return JSON.stringify(obj); } } function generateSequenceRunner(sequence, tree, outputPath, serviceName) { const funcName = 'generateSequenceRunner'; logger_1.Logger.debug(funcName, 'Generating sequence file...'); const lines = []; lines.push(`// Auto-generated runner for sequence: ${sequence.name}`); lines.push(`// Service: ${serviceName}`); lines.push(`import { apiRegistry } from "../registry";`); lines.push(``); lines.push(`export async function run${sequence.name}() {`); function walk(node, depth = 1) { const indent = ' '.repeat(depth); switch (node.type) { case 'fetchList': { const fetchStep = node.step; const varName = fetchStep.extract?.as || 'result'; const service = node.step.service || serviceName; lines.push(`${indent}const response = await apiRegistry["${service}"].${deriveMethodName(node)}();`); lines.push(`${indent}const ${varName} = response${fetchStep.extract?.field ? `.${fetchStep.extract.field}` : ''};`); break; } case 'action': { const actionStep = node.step; const service = node.step.service || serviceName; const methodName = deriveMethodName(node); const paramMatches = (actionStep.endpoint.match(/:(\w+(?:\.\w+)?)/g) || []); const paramArgs = paramMatches.map(p => p.slice(1)); const methodWithBody = ['put', 'post', 'patch'].includes(actionStep.method?.toLowerCase()); const argsList = [...paramArgs]; if (methodWithBody && actionStep.body) { argsList.push(interpolateTemplateObject(actionStep.body)); } if (actionStep.extract) { lines.push(`${indent}const response = await apiRegistry["${service}"].${methodName}(${argsList.join(', ')});`); lines.push(`${indent}const ${actionStep.extract.as} = response.${actionStep.extract.field};`); } else { lines.push(`${indent}await apiRegistry["${service}"].${methodName}(${argsList.join(', ')});`); } break; } case 'loop': { const loopStep = node.step; const list = loopStep.over; const item = loopStep.itemName; lines.push(`${indent}for (const ${item} of ${list}) {`); node.children.forEach(child => walk(child, depth + 1)); lines.push(`${indent}}`); break; } } } tree.forEach(node => walk(node)); lines.push(`}`); const outputFile = path_1.default.resolve(outputPath, `${sequence.name}.runner.ts`); fs_1.default.writeFileSync(outputFile, lines.join('\n'), 'utf-8'); } function generateSequenceFromFile(filePath, outputDir) { const funcName = 'generateSequenceFromFile'; logger_1.Logger.debug(funcName, `Generating sequence from ${filePath} ...`); const fileContent = fs_1.default.readFileSync(filePath, 'utf-8'); const parsed = JSON.parse(fileContent); (0, structure_validators_1.assertSequences)(parsed); for (const sequence of parsed.sequences) { const tree = (0, sequence_tree_builder_1.buildTreeFromSequence)(sequence); generateSequenceRunner(sequence, tree, outputDir, parsed.serviceName); } } function generateSequencesFromPath(configDir, outputDir, subDir = 'sequences') { const funcName = 'generateSequencesFromPath'; logger_1.Logger.debug(funcName, `Generating sequences from ${configDir} to ${outputDir}`); const files = fs_1.default.readdirSync(configDir).filter(f => f.endsWith('.json')); for (const file of files) { const filePath = path_1.default.join(configDir, file); const content = fs_1.default.readFileSync(filePath, 'utf-8'); const parsed = JSON.parse(content); const serviceName = parsed.serviceName; if (!serviceName) { logger_1.Logger.warn(funcName, `Skipping file ${file} — missing 'serviceName' field`); continue; } const serviceOutputDir = path_1.default.join(outputDir, subDir); (0, file_system_1.ensureDir)(serviceOutputDir); generateSequenceFromFile(filePath, serviceOutputDir); } logger_1.Logger.info(funcName, 'Sequence file generation completed.'); }