UNPKG

nope-js-node

Version:

NoPE Runtime for Nodejs. For Browser-Support please use nope-browser

428 lines (383 loc) 10.7 kB
/** * Helper file to perform all releated task to create a project, * add services and modules to it. */ import { FOLDER_SPLIT, listFiles, createPath, createFile, replaceAll, insert, underscore, camelize, } from "../helpers/index.nodejs"; import { copyFile, readFile } from "fs/promises"; import { readFileSync } from "fs"; import { join, relative, resolve } from "path"; import * as handlebars from "handlebars"; import { existsSync } from "fs"; import { getNopeLogger, ILogger } from "../index.browser"; import { simpleGit } from "simple-git"; const dirName = join(__dirname, "..", "..", "lib", "templates"); const INSERT_MARKER = " !! Insert-Marker: Don't remove this line !!"; const packageDirName = join(__dirname, "..", ".."); const packageJson = JSON.parse( readFileSync(join(packageDirName, "package.json")).toString("utf-8") ); const currentNopeVersion = packageJson.version; function firstup(str: string) { str = camelize(str); return str[0].toUpperCase() + str.slice(1); } function toService(str: string) { str = firstup(str); if (str.endsWith("Service")) { return str; } return str + "Service"; } function toModule(str: string) { str = firstup(str); if (str.endsWith("Module")) { return str; } return str + "Module"; } function toInterface(str: string) { str = toModule(str); return "I" + str; } handlebars.registerHelper("underscore", function (str: string) { return underscore(str, true); }); handlebars.registerHelper("firstup", firstup); handlebars.registerHelper("now", function () { const now = new Date(); return ( now.getDay().toString() + "." + now.getMonth().toString() + "." + now.getFullYear().toString() ); }); handlebars.registerHelper("toService", toService); handlebars.registerHelper("toModule", toModule); handlebars.registerHelper("toInterface", toInterface); export interface IProjectFile { type: "python" | "typescript"; version: string; name: string; dirName: string; path: string; modules: { name: string; properties: string[]; events: string[]; methods: string[]; }[]; services: { name: string; }[]; description: string; author: { forename: string; surename: string; mail: string; }; depencies: string[]; git?: { repo: string; created: boolean; }; currentNopeVersion: typeof currentNopeVersion; } export function generateDefaultProject(): IProjectFile { return { type: "python", version: "1.0", name: "name", dirName: "", path: "", modules: [], services: [], description: "", author: { forename: "", surename: "", mail: "", }, depencies: [], currentNopeVersion: currentNopeVersion, }; } /** * Helper to define the relevant project files. * @returns */ async function _getProjectTemplates(type: "python" | "typescript") { const files = await listFiles(join(dirName, "projects", type), ".handlebars"); return files; } async function _getProjectFilesToCopy(type: "python" | "typescript") { const files = await listFiles(join(dirName, "projects", type)); const filesToCopy = files.filter((item) => { const isTemplate = item.includes(".handlebars"); return !isTemplate; }); return filesToCopy; } function _relativePath( settings: IProjectFile, type: "projects" | "services" | "modules", path: string, dataForTemplate: any ): string { const templatePath = dirName + FOLDER_SPLIT + type + FOLDER_SPLIT + settings.type; const file = path.slice(templatePath.length); const fileTemplate = replaceAll( file, FOLDER_SPLIT, "__FOLDER_SPLITTING_CHAR__" ); const render = handlebars.compile(fileTemplate); const result = render(dataForTemplate); const ret = replaceAll(result, "__FOLDER_SPLITTING_CHAR__", FOLDER_SPLIT); return ret; } async function _writeTemplate( path: string, templateFile: string, projectSettings: IProjectFile, type: "projects" | "services" | "modules", dataForTemplate: any, logger: ILogger = null ) { const render = handlebars.compile( await readFile(templateFile, { encoding: "utf-8" }) ); let filePath = path + _relativePath(projectSettings, type, templateFile, dataForTemplate); if (templateFile.endsWith(".extend.handlebars")) { filePath = filePath.slice(0, filePath.length - ".extend".length); } filePath = filePath.slice(0, filePath.length - ".handlebars".length); const content = render(dataForTemplate); if (templateFile.endsWith(".extend.handlebars") && existsSync(filePath)) { const text = await readFile(filePath, { encoding: "utf-8" }); const idxToInsert = text.includes(INSERT_MARKER) ? text.indexOf(INSERT_MARKER) + INSERT_MARKER.length : text.length; const contentToStore = insert(text, idxToInsert, "\n".repeat(1) + content); await createFile(filePath, contentToStore); if (logger) logger.info("updated\t", filePath); } else { await createFile(join(filePath), content); if (logger) logger.info("created\t", filePath); } } export async function createProject( projectSettings: IProjectFile, dir: string, logger: ILogger = null ) { const path = await createPath(join(dir, projectSettings.name)); const filesToCopy = await _getProjectFilesToCopy(projectSettings.type); const promises = filesToCopy.map(async (file) => { const dest = await createFile( path + _relativePath(projectSettings, "projects", file, projectSettings), "" ); await copyFile(file, dest); if (logger) logger.info("created\t", dest); }); await promises; // now we render the files. for (const templateFile of await _getProjectTemplates(projectSettings.type)) { await _writeTemplate( path, templateFile, projectSettings, "projects", projectSettings, logger ); } projectSettings.path = resolve(process.cwd(), path); await createFile( join(path, "nope.json"), JSON.stringify(projectSettings, undefined, 4) ); const git = simpleGit(path); // or await each step individually await git.init(); const files = await listFiles(path); const filesToCommit = files .filter((file) => !file.includes(FOLDER_SPLIT + ".git" + FOLDER_SPLIT)) .map((file) => relative(path, file)); // await git.add(files); await git.add(filesToCommit); await git.commit("initial commit"); if (projectSettings.git?.repo) { await git.addRemote("origin", projectSettings.git.repo); } if (logger) logger.info("created\t", join(path, "nope.json")); } export async function addServiceToProject( projectSettings: IProjectFile, service: { name: string }, logger: ILogger = null ) { let path = join(projectSettings.path, projectSettings.name); // List the relevant templates. const templates = await listFiles( join(dirName, "services", projectSettings.type), ".handlebars" ); const settings = Object.assign({}, service, { project: projectSettings }); for (const templateFile of templates) { await _writeTemplate( path, templateFile, projectSettings, "services", settings, logger ); } const files = await listFiles( join(dirName, "services", projectSettings.type) ); const filesToCopy = files.filter((item) => { const isTemplate = item.includes(".handlebars"); return !isTemplate; }); const promises = filesToCopy.map(async (file) => { const dest = await createFile( path + _relativePath(projectSettings, "services", file, settings), "" ); await copyFile(file, dest); if (logger) logger.info("created\t", dest); }); await promises; projectSettings.services.push(service); await createFile( join(projectSettings.path, "nope.json"), JSON.stringify(projectSettings, undefined, 4) ); } export async function addModuleToProject( projectSettings: IProjectFile, module: IProjectFile["modules"][0], logger: ILogger = null ) { let path = join(projectSettings.path, projectSettings.name); // List the relevant templates. const templates = await listFiles( join(dirName, "modules", projectSettings.type), ".handlebars" ); const settings = Object.assign({}, module, { project: projectSettings, }); for (const templateFile of templates) { await _writeTemplate( path, templateFile, projectSettings, "modules", settings, logger ); } const files = await listFiles(join(dirName, "modules", projectSettings.type)); const filesToCopy = files.filter((item) => { const isTemplate = item.includes(".handlebars"); return !isTemplate; }); const promises = filesToCopy.map(async (file) => { const dest = await createFile( path + _relativePath(projectSettings, "modules", file, settings), "" ); await copyFile(file, dest); if (logger) logger.info("created\t", dest); }); await promises; projectSettings.modules.push(module); await createFile( join(projectSettings.path, "nope.json"), JSON.stringify(projectSettings, undefined, 4) ); } if (require.main === module) { const logger = getNopeLogger("tool"); async function main() { const settings: IProjectFile = { author: { mail: "m.karkowski@zema.de", forename: "Martin", surename: "Karkowski", }, depencies: [], description: "Minimal Description for the Test", modules: [], path: "./temp/", services: [ { name: "createDatabase", }, ], name: "testPython", dirName: "test_python", version: "1.0", type: "python", currentNopeVersion: currentNopeVersion, }; await createProject(settings, "../temp", logger); await addServiceToProject( settings, { name: "deleteDatabase", }, logger ); await addModuleToProject( settings, { methods: ["sayHello", "getTime"], name: "HelloWorld", events: [], properties: ["lastGreetingMessage"], }, logger ); // Now create a Typescript folder: settings.type = "typescript"; settings.name = "testNodejs"; settings.name = "testNodejs"; settings.path = "./temp/"; await createProject(settings, "./temp", logger); await addServiceToProject( settings, { name: "deleteDatabase", }, logger ); await addModuleToProject( settings, { methods: ["sayHello", "getTime"], name: "HelloWorld", events: [], properties: ["lastGreetingMessage"], }, logger ); } main().catch(console.error); }