UNPKG

rsi.cli

Version:

The swiss army knife for RSI related tasks.

256 lines (246 loc) 13 kB
#!/usr/bin/env node import { Logger } from "./utils/Logger"; import commandLineArgs from 'command-line-args'; import { printHelp, getVersion, service } from "./"; import * as vfs from "vinyl-fs"; import * as path from "path"; import { existsSync } from "fs"; import * as watch from "watch"; import * as inquirer from "inquirer"; import fs from "fs"; import { SERVICE_NAME_REGEX } from "./utils/regex"; let bundleDepenencies = {}; /* first - parse the main command */ const mainOptions = commandLineArgs([{ name: 'command', defaultOption: true }, { name: 'sourceFolder', type: String }], { stopAtFirstUnknown: true }) const argv = mainOptions._unknown || []; /* second - parse the command options */ if (mainOptions.command == 'bundle' || mainOptions.command == 'service') { const serviceCommands = commandLineArgs([{ name: 'command', defaultOption: true }], { argv, stopAtFirstUnknown: true }); const serviceArgv = serviceCommands._unknown || []; switch (serviceCommands.command) { case 'init': optioninitialise(serviceArgv); break; case 'validate': optionValidate(serviceArgv, mainOptions.command === 'bundle'); break; case 'release': optionRelease(); break; case 'render': optionRender(serviceArgv, mainOptions.command === 'bundle'); break; case 'markdown': case 'document': optiondocument(serviceArgv, mainOptions.command === 'bundle', serviceCommands.command === "document"); break; default: service.printHelp(); break; } } else { interface IOptions { version: Boolean } const Definitions = [{ name: 'version', alias: 'v', type: Boolean }]; try { const options: IOptions = commandLineArgs(Definitions, { argv }) as IOptions; (options.version) ? console.log(getVersion()) : printHelp(); } catch (error) { printHelp(); } } // initialises a new repository function optioninitialise(serviceArgv) { const serviceInitOpts = commandLineArgs([ { name: 'author', alias: 'a', type: String }, { name: 'description', alias: 'd', type: String }, { name: 'email', alias: 'e', type: String }, { name: 'name', alias: 'n', type: String }, { name: 'output', alias: 'o', type: String, defaultValue: "./" }, { name: 'version', alias: 'v', type: String }, ], { argv: serviceArgv }); const questions = [ { type: 'input', name: 'name', message: 'What is the service name?', default: serviceInitOpts.name, validate: (inp: string) => { if (!inp) return false; const isEmail = inp.match(/^\w+$/); return (isEmail !== null) || "not a valid name, only characters and numbers allowed"; } }, { type: 'input', name: 'version', message: 'What is the service version?', default: serviceInitOpts.version || "0.0.1" }, { type: 'input', name: 'description', message: 'Give a short desciption of your service?', default: serviceInitOpts.description || "some service" }, { type: 'input', name: 'author', message: 'Who authored the service?', default: serviceInitOpts.author || process.env.LOGNAME }, { type: 'input', name: 'email', message: 'What is the authors email?', default: serviceInitOpts.email, validate: (inp: string) => { if (!inp) return false; const isEmail = inp.match(/^\w+@\w+\.\w+$/); return (isEmail !== null) || "not a valid email address"; } }, { type: 'input', name: 'output', message: 'output directory', default: serviceInitOpts.output || "./myNewService" } ]; if (!(serviceInitOpts.author && serviceInitOpts.email && serviceInitOpts.name && serviceInitOpts.output)) Logger.info("Shortcut usage: $ rsi service init --name <servicename> --author \"<username>\" --email <email> --output <outputfolder>\n\r\n\r"); inquirer.prompt(questions as inquirer.Questions).then((answers) => { console.log(answers) service.init(answers as service.serviceMeta).pipe(vfs.dest(answers.output)); }); } //Renders documentation(markdown and html) based on schema function optiondocument(serviceArgv, bundle, html) { const serviceDocumentOpts = commandLineArgs([ { name: 'sourceFolder', alias: 's', type: String }, { name: 'output', alias: 'o', type: String }, { name: 'watch', alias: 'w', type: Boolean } ], { argv: serviceArgv }); let pathsObj = {}; let packageFile = JSON.parse(fs.readFileSync(path.join(serviceDocumentOpts.sourceFolder, "./package.json"), 'utf-8')); let packageInfo = { name: packageFile.name, version: packageFile.version, description: packageFile.description }; if (serviceDocumentOpts.sourceFolder && serviceDocumentOpts.output) { if (bundle) { let temp = createBundleObj(serviceDocumentOpts.sourceFolder); pathsObj['changelog'] = ((existsSync(path.join(serviceDocumentOpts.sourceFolder, "./changelog.md"))) ? path.join(serviceDocumentOpts.sourceFolder, "./changelog.md") : undefined); for (let def in temp) pathsObj[def] = { serviceDefinition: temp[def].definition, package: temp[def].package, changelog: temp[def].changelog }; } else { pathsObj[packageInfo.name] = { serviceDefinition: ((existsSync(path.join(serviceDocumentOpts.sourceFolder, "./src/serviceDefinition.json"))) ? path.join(serviceDocumentOpts.sourceFolder, "./src/serviceDefinition.json") : path.join(serviceDocumentOpts.sourceFolder, "./src/schema.json")), package: path.join(serviceDocumentOpts.sourceFolder, "./package.json"), changelog: ((existsSync(path.join(serviceDocumentOpts.sourceFolder, "./changelog.md"))) ? path.join(serviceDocumentOpts.sourceFolder, "./changelog.md") : undefined) } } service.renderDoc(pathsObj, packageInfo, html).then(data => { data.pipe(vfs.dest(serviceDocumentOpts.output)); Logger.success("Documented scuccessfully") });//.catch(err => Logger.error("Documentation failed:", JSON.stringify(err, undefined, 2))); for (let x in pathsObj) { // check if we are watching if (serviceDocumentOpts.watch) { watch.createMonitor(path.join(serviceDocumentOpts.sourceFolder, "./"), (monitor) => { monitor.files[pathsObj[x].serviceDefinition, pathsObj[x].changelog, pathsObj[x].package] Logger.info(`Watching ${serviceDocumentOpts.sourceFolder}`); monitor.on("changed", (where) => { // Handle file changes Logger.info(`Change detected in ${where}.`); service.renderDoc(pathsObj, packageInfo, html).then(data => { data.pipe(vfs.dest(serviceDocumentOpts.output)); Logger.success("Documented scuccessfully"); }).catch(err => Logger.error("Documentation failed:", JSON.stringify(err, undefined, 2))); }); }) } } } else { Logger.error("One or more parameter missing"); Logger.info("Usage: $ rsi service document --sourceFolder <pathToServiceFolder> --output <pathToOutputFolder>"); } } //Prepare service for release function optionRelease() { Logger.error("`release` command currently not supported"); } //Render UML from schema function optionRender(serviceArgv, bundle) { const serviceRenderOpts = commandLineArgs([ { name: 'sourceFolder', alias: 's', type: String }, { name: 'output', alias: 'o', type: String }, { name: 'watch', alias: 'w', type: Boolean } ], { argv: serviceArgv }); let pathObjs = {}; if (serviceRenderOpts.sourceFolder && serviceRenderOpts.output) { if (bundle) { let temp = createBundleObj(serviceRenderOpts.sourceFolder) for (let def in temp) pathObjs[def] = temp[def].definition; } else { pathObjs[JSON.parse(fs.readFileSync(path.join(serviceRenderOpts.sourceFolder, "./package.json"), 'utf-8')).name] = ((existsSync(path.join(serviceRenderOpts.sourceFolder, "./src/serviceDefinition.json"))) ? path.join(serviceRenderOpts.sourceFolder, "./src/serviceDefinition.json") : path.join(serviceRenderOpts.sourceFolder, "./src/schema.json")); } // const paths = { schema: path.join(serviceRenderOpts.sourceFolder, "./src/schema.json") } // render once // service.parseSchemas([paths.schema]).then(payload => { service.parseSchemas(pathObjs).then(payload => { service.render(payload).then(data => { data.pipe(vfs.dest(serviceRenderOpts.output)).on("data", (data) => Logger.success(`Rendered plantuml to ${path.join(serviceRenderOpts.output, data.basename)}.`)); }).catch(err => Logger.error("Rendering failed:", JSON.stringify(err, undefined, 2))); }); for (let x in pathObjs) { // check if we are watching if (serviceRenderOpts.watch) { watch.createMonitor(path.join(serviceRenderOpts.sourceFolder, "./"), (monitor) => { monitor.files[pathObjs[x].serviceDefinition] Logger.info(`Watching ${serviceRenderOpts.sourceFolder}`); monitor.on("changed", (where) => { // Handle file changes Logger.info(`Change detected in ${where}.`); service.parseSchemas([pathObjs[x].serviceDefinition]).then(payload => { service.render(payload).then(data => { data.pipe(vfs.dest(serviceRenderOpts.output)).on("data", (data) => Logger.success(`Rendered plantuml to ${path.join(serviceRenderOpts.output, data.basename)}.`)); }).catch(err => Logger.error("Rendering failed:", JSON.stringify(err, undefined, 2))); }); }); }) } } } else { Logger.error("One or more parameter missing"); Logger.info("Usage:\n$ rsi service render --sourceFolder <pathToServiceFolder> --output <pathToOutputFolder>"); } } //Validates the uploaded Service Definitions function optionValidate(serviceArgv, bundle) { const serviceValidateOpts = commandLineArgs([ { name: 'sourceFolder', alias: 's', type: String }, { name: 'watch', alias: 'w', type: Boolean } ], { argv: serviceArgv }); let pathObjs = {}; if (serviceValidateOpts.sourceFolder) { if (bundle) { let temp = createBundleObj(serviceValidateOpts.sourceFolder) for (let def in temp) pathObjs[def] = temp[def].definition; } else { pathObjs['CurrentService'] = ((existsSync(path.join(serviceValidateOpts.sourceFolder, "./src/serviceDefinition.json"))) ? path.join(serviceValidateOpts.sourceFolder, "./src/serviceDefinition.json") : path.join(serviceValidateOpts.sourceFolder, "./src/schema.json")); } for (let url in pathObjs) { service.validate(pathObjs[url]).then(data => Logger.success("Service Definition valid - " + url)).catch(err => Logger.error("Validation failed:", JSON.stringify(err, undefined, 2))); // check if we are watching if (serviceValidateOpts.watch) { watch.createMonitor(path.join(serviceValidateOpts.sourceFolder, "./"), (monitor) => { monitor.files[pathObjs[url]] Logger.info(`Watching ${serviceValidateOpts.sourceFolder}`); monitor.on("changed", (where) => { // Handle file changes Logger.info(`Change detected in ${where}.`); service.validate(pathObjs[url]).then(data => Logger.success("Service Definition valid - " + url)).catch(err => Logger.error("Validation failed:", JSON.stringify(err, undefined, 2))); }); }) } } } else { Logger.error("One or more parameter missing"); console.log("Usage: $ rsi service validate --sourceFolder <pathToServiceFolder>"); } } //Gets the path ways to the schema and package files for the dependencies function createBundleObj(paths) { const dependencies = JSON.parse(fs.readFileSync(path.join(paths, "./package.json"), 'utf-8')).dependencies; for (let def of filterDependencies(dependencies)) { bundleDepenencies[def] = { // definition: JSON.parse(fs.readFileSync(path.join(serviceValidateOpts.sourceFolder, '/node_modules/' + def + '/src/schema.json'), "utf-8")), // package: JSON.parse(fs.readFileSync(path.join(serviceValidateOpts.sourceFolder, '/node_modules/' + def + '/package.json'), "utf-8")) definition: ((existsSync(path.join(paths, `/node_modules/${def}/src/serviceDefinition.json`))) ? path.join(paths, `/node_modules/${def}/src/serviceDefinition.json`) : path.join(paths, `/node_modules/${def}/src/schema.json`)), package: path.join(paths, `/node_modules/${def}/package.json`), // changelog: path.join(paths, `/node_modules/${def}/CHANGELOG.md`) changelog: ((existsSync(path.join(paths, `/node_modules/${def}/CHANGELOG.md`))) ? path.join(paths, `/node_modules/${def}/CHANGELOG.md`) : undefined) }; } return bundleDepenencies; } //Checks dependencies are rsi export function filterDependencies(obj) { if (obj !== undefined) { const keys = Object.keys(obj).filter(dep => { return dep.match(SERVICE_NAME_REGEX); }); if (keys.length === 0) { throw new Error('no RSI or VIWI services found in dependencies!'); } else { return keys; } } else { throw new Error('undefined argument!'); } }