UNPKG

typescript-scaffolder

Version:

![npm version](https://img.shields.io/npm/v/typescript-scaffolder) ### Unit Test Coverage: 97.12%

104 lines (103 loc) 4.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractInterfaceKeysFromFile = extractInterfaceKeysFromFile; exports.stripSurroundingQuotes = stripSurroundingQuotes; exports.generateEnum = generateEnum; exports.isValidIdentifier = isValidIdentifier; exports.generateEnums = generateEnums; exports.generateEnumsFromPath = generateEnumsFromPath; const file_system_1 = require("../utils/file-system"); const fs_1 = __importDefault(require("fs")); const node_path_1 = __importDefault(require("node:path")); const logger_1 = require("../utils/logger"); const ts_morph_1 = require("ts-morph"); /** * Extracts interface names and keys from a TypeScript file using ts-morph. */ function extractInterfaceKeysFromFile(filePath) { const funcName = 'extractInterfaceKeysFromFile'; logger_1.Logger.debug(funcName, `Parsing interfaces from: ${filePath}`); const project = new ts_morph_1.Project(); const sourceFile = project.addSourceFileAtPath(filePath); const interfaces = []; sourceFile.getInterfaces().forEach((iface) => { const name = iface.getName(); const keys = Array.from(new Set(iface .getProperties() .map(prop => stripSurroundingQuotes(prop.getName())))); interfaces.push({ name, keys }); }); return interfaces; } /** * If ts-morph returns a string-literal property name (e.g. "\"Card Number\""), * remove the surrounding quotes so downstream quotation is applied exactly once. */ function stripSurroundingQuotes(str) { const m = str.match(/^(["'`])(.*)\1$/); return m ? m[2] : str; } /** * Generates TypeScript enum string from interface key names. */ function generateEnum(name, keys) { const enumName = `${name}Keys`; const body = keys .map(original => { const raw = stripSurroundingQuotes(original); const namePart = isValidIdentifier(raw) ? raw : JSON.stringify(raw); const valuePart = JSON.stringify(raw); return ` ${namePart} = ${valuePart}`; }) .join(',\n'); return `export enum ${enumName} {\n${body}\n}\n`; } /** * Tests to make sure the identifier is a valid string using regex * @param str */ function isValidIdentifier(str) { return /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(str); } /** * Processes a single file and writes enums for interfaces. Keeping async for now * To resolve race condition issues on the thread */ async function generateEnums(filePath, relativePath, outputBaseDir) { const funcName = 'generateEnums'; logger_1.Logger.debug(funcName, `Generating enums for ${filePath}`); const outputDir = node_path_1.default.join(outputBaseDir, node_path_1.default.dirname(relativePath)); (0, file_system_1.ensureDir)(outputDir); const parsed = extractInterfaceKeysFromFile(filePath); if (parsed.length === 0) { logger_1.Logger.warn(funcName, `No interfaces found in ${filePath}`); return; } const enumsContent = parsed.map(i => generateEnum(i.name, i.keys)).join('\n'); const outPath = node_path_1.default.join(outputDir, node_path_1.default.basename(filePath, '.ts') + '.enums.ts'); fs_1.default.writeFileSync(outPath, enumsContent, 'utf-8'); logger_1.Logger.debug(funcName, `Generated: ${outPath}`); } /** * Parses a file structure housing interfaces and regenerates the directory tree with enums. * Only does TypeScript Interfaces for now. This needs to be async for proper chaining as the previous * Executions should save first * @param schemaDir * @param outputDir * @param ext */ async function generateEnumsFromPath(schemaDir, outputDir, ext = '.ts') { const funcName = 'generateEnumsFromPath'; logger_1.Logger.debug(funcName, `Walking directory for enums: ${schemaDir}`); (0, file_system_1.ensureDir)(outputDir); const tasks = []; (0, file_system_1.walkDirectory)(schemaDir, (filePath, relativePath) => { // Wrap in Promise.resolve in case generateEnums becomes async later const result = Promise.resolve(generateEnums(filePath, relativePath, outputDir)); tasks.push(result); }, ext); await Promise.all(tasks); }