@manta-style/cli
Version:
A command line tool for Manta Style
395 lines (394 loc) • 22.7 kB
JavaScript
;
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);
});