apisculptify
Version:
Apisculptify is a powerful Node.js tool designed to automate the process of generating RESTful APIs for your TypeORM models. With Apisculptify, you can quickly create a new project, define your TypeORM models, and effortlessly generate CRUD (Create, Read,
178 lines (163 loc) • 5.77 kB
text/typescript
import { Project, SourceFile } from "ts-morph";
import fs, { writeFileSync } from "fs";
import path from "path";
const inquirer = require("inquirer");
inquirer.registerPrompt("directory", require("inquirer-select-directory"));
export function bootstrap() {
-inquirer
.prompt([
{
type: "directory",
name: "path",
message: "Navigate to your initialized directory`s src folder?",
options: {
displayFiles: false,
},
basePath: process.cwd(),
},
])
.then(async function (data: any) {
let selectedpath = data.path;
await run(selectedpath);
});
}
async function run(selectedpath: string) {
const project = new Project();
let sources = [];
sources.push(selectedpath + "/model/*.model.ts");
const sourceFiles = project.addSourceFilesAtPaths(sources);
let classMap = await createClassMap(sourceFiles);
var lastIndexBuild = __dirname.lastIndexOf("build");
var basePath = path.resolve(__dirname.substring(0, lastIndexBuild));
for (let key of classMap.keys()) {
let keywords = {
MODEL_NAME: key,
MODEL_NAME_WITHOUT_KEY: key.replace(/Model$/, ""),
MODEL_NAME_WITH_DASH: convertWithDash(key.toString()),
MODEL_NAME_WITHOUT_KEY_LOWER: key
.replace(/Model$/, "")
.toLowerCase(),
DYNAMIC_IMPORTS: "",
ENRICH: "",
};
console.log(key + " processed");
generateController(keywords, selectedpath, basePath);
generateRouter(keywords, selectedpath, basePath);
generateService(keywords, classMap.get(key), selectedpath, basePath);
}
}
function generateController(
keywords: any,
selectedpath: string,
basePath: string
) {
let template = fs
.readFileSync(basePath + "/templates/generic.controller.txt")
.toString();
template = replaceVariable(template, keywords);
writeFile(
template,
selectedpath + "/controllers/",
keywords.MODEL_NAME_WITH_DASH + ".controller.ts"
);
}
function generateRouter(keywords: any, selectedpath: string, basePath: string) {
let template = fs
.readFileSync(basePath + "/templates/generic.router.txt")
.toString();
template = replaceVariable(template, keywords);
writeFile(
template,
selectedpath + "/router/",
keywords.MODEL_NAME_WITH_DASH + ".router.ts"
);
}
function generateService(
keywords: any,
properties: any,
selectedpath: string,
basePath: string
) {
for (let prop of properties) {
if (prop.isForeignKey) {
keywords.DYNAMIC_IMPORTS =
keywords.DYNAMIC_IMPORTS +
`\nimport { ${prop.type.replace(
/Model$/,
""
)}Service } from "./${convertWithDash(prop.type)}.service";`;
keywords.ENRICH =
keywords.ENRICH +
`\nif (model.${prop.property} && model.${prop.property}.uid) {
const ${prop.property} = await ${prop.type.replace(
/Model$/,
""
)}Service.getByUid(model.${prop.property}.uid)
if (!${prop.property}) {
throw new Error("${prop.property} not found");
}
model.${prop.property} = ${prop.property}
}`;
}
}
let template = fs
.readFileSync(basePath + "/templates/generic.service.txt")
.toString();
template = replaceVariable(template, keywords);
writeFile(
template,
selectedpath + "/service/",
keywords.MODEL_NAME_WITH_DASH + ".service.ts"
);
}
function writeFile(template: string, destination: string, fileName: string) {
writeFileSync(destination + fileName, template);
}
function replaceVariable(template: string, keywords: any): string {
for (let key in keywords) {
template = template.replaceAll(`[[${key}]]`, keywords[key]);
}
return template;
}
function convertWithDash(input: string): string {
let trimWord = input.replace(/Model$/, "");
return trimWord.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
async function createClassMap(
sourceFiles: SourceFile[]
): Promise<Map<any, any>> {
let classMap = new Map();
for (let sourceFile of sourceFiles) {
let className = sourceFile.getClasses()[0].getName();
let properties = [];
if (!classMap.has(className)) {
for (let prop of sourceFile.getClasses()[0].getProperties()) {
var newObj = {
property: prop.getName(),
type: "",
isForeignKey: false,
};
if (prop.getType().getText().includes(".")) {
const importStatement = prop.getType().getText();
const lastDotIndex = importStatement.lastIndexOf(".");
const lastSubstring = importStatement.substring(
lastDotIndex + 1
);
newObj.type = lastSubstring;
} else {
newObj.type = prop.getType().getText();
}
properties.push(newObj);
}
classMap.set(className, properties);
}
}
for (let key of classMap.keys()) {
for (let prop of classMap.get(key)) {
if (classMap.has(prop.type)) {
prop.isForeignKey = true;
}
}
}
return classMap;
}