Files
izyim-mockapi/node_modules/json-server/lib/cli/run.js
T
2018-08-09 11:15:09 +08:00

245 lines
6.7 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const jph = require('json-parse-helpfulerror');
const _ = require('lodash');
const chalk = require('chalk');
const enableDestroy = require('server-destroy');
const pause = require('connect-pause');
const is = require('./utils/is');
const load = require('./utils/load');
const jsonServer = require('../server');
const example = {
posts: [{ id: 1, title: 'json-server', author: 'typicode' }],
comments: [{ id: 1, body: 'some comment', postId: 1 }],
profile: { name: 'typicode' }
};
function prettyPrint(argv, object, rules) {
const root = `http://${argv.host}:${argv.port}`;
console.log();
console.log(chalk.bold(' Resources'));
for (let prop in object) {
console.log(` ${root}/${prop}`);
}
if (rules) {
console.log();
console.log(chalk.bold(' Other routes'));
for (var rule in rules) {
console.log(` ${rule} -> ${rules[rule]}`);
}
}
console.log();
console.log(chalk.bold(' Home'));
console.log(` ${root}`);
console.log();
}
function createApp(source, object, routes, middlewares, argv) {
const app = jsonServer.create();
let router;
const foreignKeySuffix = argv.foreignKeySuffix;
try {
router = jsonServer.router(is.JSON(source) ? source : object, foreignKeySuffix ? { foreignKeySuffix } : undefined);
} catch (e) {
console.log();
console.error(chalk.red(e.message.replace(/^/gm, ' ')));
process.exit(1);
}
const defaultsOpts = {
logger: !argv.quiet,
readOnly: argv.readOnly,
noCors: argv.noCors,
noGzip: argv.noGzip,
bodyParser: true
};
if (argv.static) {
defaultsOpts.static = path.join(process.cwd(), argv.static);
}
const defaults = jsonServer.defaults(defaultsOpts);
app.use(defaults);
if (routes) {
const rewriter = jsonServer.rewriter(routes);
app.use(rewriter);
}
if (middlewares) {
app.use(middlewares);
}
if (argv.delay) {
app.use(pause(argv.delay));
}
router.db._.id = argv.id;
app.db = router.db;
app.use(router);
return app;
}
module.exports = function (argv) {
const source = argv._[0];
let app;
let server;
if (!fs.existsSync(argv.snapshots)) {
console.log(`Error: snapshots directory ${argv.snapshots} doesn't exist`);
process.exit(1);
}
// noop log fn
if (argv.quiet) {
console.log = () => {};
}
console.log();
console.log(chalk.cyan(' \\{^_^}/ hi!'));
function start(cb) {
console.log();
// Be nice and create a default db.json if it doesn't exist
if (is.JSON(source) && !fs.existsSync(source)) {
console.log(chalk.yellow(` Oops, ${source} doesn't seem to exist`));
console.log(chalk.yellow(` Creating ${source} with some default data`));
console.log();
fs.writeFileSync(source, JSON.stringify(example, null, 2));
}
console.log(chalk.gray(' Loading', source));
// Load JSON, JS or HTTP database
load(source, (err, data) => {
if (err) throw err;
// Load additional routes
let routes;
if (argv.routes) {
console.log(chalk.gray(' Loading', argv.routes));
routes = JSON.parse(fs.readFileSync(argv.routes));
}
// Load middlewares
let middlewares;
if (argv.middlewares) {
middlewares = argv.middlewares.map(function (m) {
console.log(chalk.gray(' Loading', m));
return require(path.resolve(m));
});
}
// Done
console.log(chalk.gray(' Done'));
// Create app and server
app = createApp(source, data, routes, middlewares, argv);
server = app.listen(argv.port, argv.host);
// Enhance with a destroy function
enableDestroy(server);
// Display server informations
prettyPrint(argv, data, routes);
cb && cb();
});
}
// Start server
start(() => {
// Snapshot
console.log(chalk.gray(' Type s + enter at any time to create a snapshot of the database'));
// Support nohup
// https://github.com/typicode/json-server/issues/221
process.stdin.on('error', () => {
console.log(` Error, can't read from stdin`);
console.log(` Creating a snapshot from the CLI won't be possible`);
});
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => {
if (chunk.trim().toLowerCase() === 's') {
const filename = `db-${Date.now()}.json`;
const file = path.join(argv.snapshots, filename);
const state = app.db.getState();
fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8');
console.log(` Saved snapshot to ${path.relative(process.cwd(), file)}\n`);
}
});
// Watch files
if (argv.watch) {
console.log(chalk.gray(' Watching...'));
console.log();
const source = argv._[0];
// Can't watch URL
if (is.URL(source)) throw new Error("Can't watch URL");
// Watch .js or .json file
// Since lowdb uses atomic writing, directory is watched instead of file
const watchedDir = path.dirname(source);
let readError = false;
fs.watch(watchedDir, (event, file) => {
// https://github.com/typicode/json-server/issues/420
// file can be null
if (file) {
const watchedFile = path.resolve(watchedDir, file);
if (watchedFile === path.resolve(source)) {
if (is.JSON(watchedFile)) {
let obj;
try {
obj = jph.parse(fs.readFileSync(watchedFile));
if (readError) {
console.log(chalk.green(` Read error has been fixed :)`));
readError = false;
}
} catch (e) {
readError = true;
console.log(chalk.red(` Error reading ${watchedFile}`));
console.error(e.message);
return;
}
// Compare .json file content with in memory database
const isDatabaseDifferent = !_.isEqual(obj, app.db.getState());
if (isDatabaseDifferent) {
console.log(chalk.gray(` ${source} has changed, reloading...`));
server && server.destroy();
start();
}
}
}
}
});
// Watch routes
if (argv.routes) {
const watchedDir = path.dirname(argv.routes);
fs.watch(watchedDir, (event, file) => {
if (file) {
const watchedFile = path.resolve(watchedDir, file);
if (watchedFile === path.resolve(argv.routes)) {
console.log(chalk.gray(` ${argv.routes} has changed, reloading...`));
server && server.destroy();
start();
}
}
});
}
}
});
};