UNPKG

@manta-style/cli

Version:
395 lines (394 loc) 22.7 kB
#!/usr/bin/env node "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; exports.__esModule = true; var express = require("express"); var path = require("path"); var program = require("commander"); var findRoot = require("find-root"); var snapshot_1 = require("./utils/snapshot"); var Table = require("cli-table"); var logUpdate = require("log-update"); var chalk_1 = require("chalk"); var chokidar = require("chokidar"); var qs = require("query-string"); var axios_1 = require("axios"); var PrettyError = require("pretty-error"); var clear = require("clear"); var inquirer_util_1 = require("./inquirer-util"); var core_1 = require("@manta-style/core"); var discovery_1 = require("./discovery"); var rollup_1 = require("rollup"); var Package = require("../package.json"); var pe = new PrettyError(); program .version(Package.version) .option('-c --configFile <file>', 'the config file to generate entry points') .option('-p --port <i> [3000]', 'To use a port different than 3000') .option('-o --outputFile <file>', 'Instead of setting up a server, output a single file for various purpose') .option('--proxyUrl <url>', 'To enable proxy for disabled endpoints') .option('--generateSnapshot <file>', 'To generate a API mock data snapshot (Not yet implemented.)') .option('--useSnapshot <file>', 'To launch a server with data snapshot') .option('-v --verbose', 'show debug information') .option('--official-plugins', 'show all available official plugins') .parse(process.argv); var officialPlugins = program.officialPlugins, configFile = program.configFile, outputFile = program.outputFile, port = program.port, generateSnapshot = program.generateSnapshot, useSnapshot = program.useSnapshot, _a = program.verbose, verbose = _a === void 0 ? false : _a, proxyUrl = program.proxyUrl; var queryOfficialPlugin = function (type) { return axios_1["default"].get("https://api.npms.io/v2/search?q=scope:manta-style+keywords:" + type); }; function showOfficialPluginList() { return __awaiter(this, void 0, void 0, function () { var _a, builderPlugins, mockPlugins, table; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, Promise.all([ queryOfficialPlugin('builder'), queryOfficialPlugin('mock'), ])]; case 1: _a = _b.sent(), builderPlugins = _a[0].data, mockPlugins = _a[1].data; table = new Table({ colors: false }); table.push([chalk_1["default"].yellow('Builders'), chalk_1["default"].yellow('Description')]); // @ts-ignore builderPlugins.results.forEach(function (item) { table.push([item.package.name || '', item.package.description || '']); }); table.push([chalk_1["default"].yellow('Mocks'), chalk_1["default"].yellow('Description')]); // @ts-ignore mockPlugins.results.forEach(function (item) { table.push([item.package.name || '', item.package.description || '']); }); console.log(table.toString()); process.exit(0); return [2 /*return*/]; } }); }); } (function () { return __awaiter(this, void 0, void 0, function () { function buildFromConfigFile(transpileModule) { if (transpileModule === void 0) { transpileModule = true; } return core.buildConfigFile({ configFilePath: path.resolve(configFile), destDir: tmpDir, transpileModule: transpileModule, verbose: verbose }); } function setupMockServer() { return __awaiter(this, void 0, void 0, function () { var app, compiledFilePath, compileConfig, endpoints, _loop_1, _i, endpoints_1, endpoint; return __generator(this, function (_a) { switch (_a.label) { case 0: if (server) { server.close(); } app = express(); return [4 /*yield*/, buildFromConfigFile()]; case 1: compiledFilePath = _a.sent(); delete require.cache[compiledFilePath]; compileConfig = require(compiledFilePath || ''); endpoints = core.generateEndpoints(compileConfig, { proxyUrl: proxyUrl }); _loop_1 = function (endpoint) { app[endpoint.method](endpoint.url, function (req, res) { return __awaiter(this, void 0, void 0, function () { var query, params, queryString, _a, setQuery, _b, setParam, _c, setPlugins, snapshotData, result; return __generator(this, function (_d) { switch (_d.label) { case 0: query = req.query, params = req.params; queryString = qs.stringify(query); _a = core_1.useQuery(), setQuery = _a[1]; _b = core_1.useParam(), setParam = _b[1]; _c = core_1.usePluginSystem(), setPlugins = _c[1]; setQuery(query); setParam(params); setPlugins(core.pluginSystem); if (!endpoint.enabled) return [3 /*break*/, 2]; if (isSnapshotMode) { snapshotData = snapshot.fetchSnapshot(endpoint.method, endpoint.url, queryString); if (snapshotData) { res.send(snapshotData); return [2 /*return*/]; } } return [4 /*yield*/, endpoint.callback(endpoint)]; case 1: result = _d.sent(); snapshot.updateSnapshot(endpoint.method, endpoint.url, queryString, result); res.send(result); return [3 /*break*/, 3]; case 2: if (endpoint.proxy) { axios_1["default"] .request({ method: endpoint.method, url: req.path, baseURL: endpoint.proxy, params: req.query }) .then(function (result) { snapshot.updateSnapshot(endpoint.method, endpoint.url, queryString, result.data); res.send(result.data); })["catch"](function (result) { res.status(502).send("\n <html>\n <head>\n <style>\n * {\n font-family: -apple-system,system-ui,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;\n }\n </style>\n </head>\n <body>\n <h2>Manta Style Proxy Error</h2>\n <p>Unable to connect to <strong>" + endpoint.url + "</strong></p>\n <p>Reason:</p>\n <blockquote>\n <p>" + result.message + "</p>\n </blockquote>\n </body>\n </html>\n "); }); return [2 /*return*/]; } else { res.status(404); res.send(); } _d.label = 3; case 3: return [2 /*return*/]; } }); }); }); }; for (_i = 0, endpoints_1 = endpoints; _i < endpoints_1.length; _i++) { endpoint = endpoints_1[_i]; _loop_1(endpoint); } server = app.listen(port || 3000); return [2 /*return*/]; } }); }); } function printMessage() { console.log("Manta Style launched at http://localhost:" + (port || 3000)); var table = new Table({ colors: false, head: ['Method', 'Endpoint', 'Mocked', 'Proxy'] }); var endpointTable = core.getEndpoints(); for (var _i = 0, endpointTable_1 = endpointTable; _i < endpointTable_1.length; _i++) { var row = endpointTable_1[_i]; table.push([ row.method.toUpperCase(), "http://localhost:" + (port || 3000) + row.url, row.enabled ? chalk_1["default"].green('Y') : row.proxy ? chalk_1["default"].yellow('~>') : chalk_1["default"].red('N'), row.proxy ? row.proxy + row.url : '', ]); } console.log(table.toString()); console.log("Press " + chalk_1["default"].bold('O') + " to configure selective mocking"); } function toggleSnapshotMode(showMessageOnly) { if (!showMessageOnly) { isSnapshotMode = !isSnapshotMode; } logUpdate(isSnapshotMode ? chalk_1["default"].yellow('[SNAPSHOT MODE]') + " Press " + chalk_1["default"].bold('S') + " to take a snapshot for other APIs. Press " + chalk_1["default"].bold('X') + " to disable Snapshot Mode" : chalk_1["default"].yellow('[FAKER MODE]') + " Press " + chalk_1["default"].bold('S') + " to take an instant snapshot"); } function selectiveMock() { return __awaiter(this, void 0, void 0, function () { var endpointTable, selection, _i, endpointTable_2, endpoint; return __generator(this, function (_a) { switch (_a.label) { case 0: clear(); endpointTable = core.getEndpoints(); return [4 /*yield*/, inquirer_util_1.multiSelect('Select endpoint to mock.', endpointTable.map(function (endpoint) { return ({ name: endpoint.method.toUpperCase() + " " + endpoint.url, value: endpoint }); }), endpointTable.filter(function (endpoint) { return endpoint.enabled; }))]; case 1: selection = _a.sent(); for (_i = 0, endpointTable_2 = endpointTable; _i < endpointTable_2.length; _i++) { endpoint = endpointTable_2[_i]; endpoint.enabled = selection.indexOf(endpoint) > -1; } clear(); printMessage(); console.log(''); toggleSnapshotMode(true); stdin.setRawMode && stdin.setRawMode(true); stdin.resume(); return [2 /*return*/]; } }); }); } var core, _a, tmpDir, resolvedPath, compiledPath, bundle, server, snapshotFilePath, snapshotWatcher, configFileWatcher, isSnapshotMode, snapshot, stdin; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = core_1.Core.bind; return [4 /*yield*/, discovery_1.findPlugins()]; case 1: core = new (_a.apply(core_1.Core, [void 0, _b.sent()]))(); if (!(core.builderPluginCount === 0)) return [3 /*break*/, 3]; console.log(chalk_1["default"].bold(chalk_1["default"].yellow("\nHi there! It seems that you don't have any builder plugins installed. Manta Style needs them to support different languages. Please check out the following table and install one.\n"))); return [4 /*yield*/, showOfficialPluginList()]; case 2: _b.sent(); _b.label = 3; case 3: if (!officialPlugins) return [3 /*break*/, 5]; return [4 /*yield*/, showOfficialPluginList()]; case 4: _b.sent(); _b.label = 5; case 5: if (!configFile) { console.log('Please specifiy a entry point config file by using --configFile.'); process.exit(1); } if (generateSnapshot && useSnapshot) { console.log('You cannot use --generateSnapshot and --useSnapshot at the same time.'); process.exit(1); } if (core.mockPluginCount === 0) { console.log(chalk_1["default"].bold(chalk_1["default"].yellow('\nNo mock plugin installed. You might want to run `ms --official-plugins` and install one.\n'))); } tmpDir = path.join(findRoot(process.cwd()), '.mantastyle-tmp'); if (!outputFile) return [3 /*break*/, 9]; resolvedPath = path.resolve(outputFile); return [4 /*yield*/, buildFromConfigFile(false)]; case 6: compiledPath = _b.sent(); console.log('Compiling to', outputFile, 'from', compiledPath); return [4 /*yield*/, rollup_1.rollup({ input: compiledPath })]; case 7: bundle = _b.sent(); return [4 /*yield*/, bundle.write({ file: path.basename(resolvedPath), dir: path.dirname(resolvedPath), format: 'cjs' })]; case 8: _b.sent(); process.exit(0); _b.label = 9; case 9: snapshotFilePath = path.join(path.dirname(configFile), 'ms.snapshot.json'); snapshotWatcher = chokidar.watch(snapshotFilePath); configFileWatcher = chokidar.watch(path.resolve(configFile)); isSnapshotMode = Boolean(useSnapshot); snapshot = useSnapshot ? snapshot_1.Snapshot.fromDisk(useSnapshot) : new snapshot_1.Snapshot(); snapshotWatcher.on('change', function () { snapshot.reloadFromFile(snapshotFilePath); }); configFileWatcher.on('change', function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: console.log(chalk_1["default"].yellowBright('\nUpdating config file...\n')); return [4 /*yield*/, setupMockServer()]; case 1: _a.sent(); clear(); console.log(chalk_1["default"].green('\nEndpoint config updated!\n')); printMessage(); return [2 /*return*/]; } }); }); }); return [4 /*yield*/, setupMockServer()]; case 10: _b.sent(); clear(); printMessage(); toggleSnapshotMode(true); stdin = process.stdin; stdin.on('data', function (key) { var keyCode = key.toString(); switch (keyCode) { case '\u0003': { process.exit(); break; } case 's': case 'S': { if (!isSnapshotMode) { toggleSnapshotMode(); } snapshot.writeToDisk(snapshotFilePath); break; } case 'x': case 'X': { if (isSnapshotMode) { snapshot.clearSnapshot(); toggleSnapshotMode(); } break; } case 'o': case 'O': { selectiveMock(); break; } } }); stdin.setRawMode && stdin.setRawMode(true); stdin.resume(); return [2 /*return*/]; } }); }); })()["catch"](function (exception) { if (exception instanceof Error) { if (verbose) { console.log(pe.render(exception)); } else { console.log(chalk_1["default"].red(exception.message + '\n')); } } else { console.log(chalk_1["default"].red('Unexpected Error caught. Please create an issue on https://github.com/Cryrivers/manta-style. Sorry for the inconvenience caused.\n')); } process.exit(1); });