Skip to content

Commit

Permalink
reworked scripts and added lib for direct git install
Browse files Browse the repository at this point in the history
  • Loading branch information
notPlancha committed Apr 14, 2024
1 parent a641111 commit e1e5e57
Show file tree
Hide file tree
Showing 22 changed files with 1,085 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
**/*.log
node_modules
tmp
lib

.DS_Store
.idea
db.json
4 changes: 1 addition & 3 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"

npm test
. "$(dirname $0)/_/husky.sh"
5 changes: 5 additions & 0 deletions lib/cli/bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node
"use strict";

require('please-upgrade-node')(require('../../package.json'));
require('./')();
77 changes: 77 additions & 0 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use strict";

const yargs = require('yargs');
const run = require('./run');
const pkg = require('../../package.json');
module.exports = function () {
const argv = yargs.config('config').usage('$0 [options] <source>').options({
port: {
alias: 'p',
description: 'Set port',
default: 3000
},
host: {
alias: 'H',
description: 'Set host',
default: 'localhost'
},
watch: {
alias: 'w',
description: 'Watch file(s)'
},
routes: {
alias: 'r',
description: 'Path to routes file'
},
middlewares: {
alias: 'm',
array: true,
description: 'Paths to middleware files'
},
static: {
alias: 's',
description: 'Set static files directory'
},
'read-only': {
alias: 'ro',
description: 'Allow only GET requests'
},
'no-cors': {
alias: 'nc',
description: 'Disable Cross-Origin Resource Sharing'
},
'no-gzip': {
alias: 'ng',
description: 'Disable GZIP Content-Encoding'
},
snapshots: {
alias: 'S',
description: 'Set snapshots directory',
default: '.'
},
delay: {
alias: 'd',
description: 'Add delay to responses (ms)'
},
id: {
alias: 'i',
description: 'Set database id property (e.g. _id)',
default: 'id'
},
foreignKeySuffix: {
alias: 'fks',
description: 'Set foreign key suffix (e.g. _id as in post_id)',
default: 'Id'
},
quiet: {
alias: 'q',
description: 'Suppress log messages from output'
},
config: {
alias: 'c',
description: 'Path to config file',
default: 'json-server.json'
}
}).boolean('watch').boolean('read-only').boolean('quiet').boolean('no-cors').boolean('no-gzip').help('help').alias('help', 'h').version(pkg.version).alias('version', 'v').example('$0 db.json', '').example('$0 file.js', '').example('$0 http://example.com/db.json', '').epilog('https://github.com/typicode/json-server').require(1, 'Missing <source> argument').argv;
run(argv);
};
213 changes: 213 additions & 0 deletions lib/cli/run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
"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');
function prettyPrint(argv, object, rules) {
const root = `http://${argv.host}:${argv.port}`;
console.log();
console.log(chalk.bold(' Resources'));
for (const prop in object) {
// skip printing $schema nodes
if (prop === '$schema') continue;
console.log(` ${root}/${prop}`);
}
if (rules) {
console.log();
console.log(chalk.bold(' Other routes'));
for (const rule in rules) {
console.log(` ${rule} -> ${rules[rule]}`);
}
}
console.log();
console.log(chalk.bold(' Home'));
console.log(` ${root}`);
console.log();
}
function createApp(db, routes, middlewares, argv) {
const app = jsonServer.create();
const {
foreignKeySuffix
} = argv;
const router = jsonServer.router(db, foreignKeySuffix ? {
foreignKeySuffix
} : undefined);
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();
console.log(chalk.gray(' Loading', source));
server = undefined;

// create db and load object, JSON file, JS or HTTP database
return load(source).then(db => {
// 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(db, routes, middlewares, argv);
server = app.listen(argv.port, argv.host);

// Enhance with a destroy function
enableDestroy(server);

// Display server informations
prettyPrint(argv, db.getState(), routes);

// Catch and handle any error occurring in the server process
process.on('uncaughtException', error => {
if (error.errno === 'EADDRINUSE') console.log(chalk.red(`Cannot bind to the port ${error.port}. Please specify another port number either through --port argument or through the json-server.json configuration file`));else console.log('Some error occurred', error);
process.exit(1);
});
});
}

// Start server
start().then(() => {
// 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.FILE(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());
}
}
});
}
}
}).catch(err => {
console.log(err);
process.exit(1);
});
};
16 changes: 16 additions & 0 deletions lib/cli/utils/is.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use strict";

module.exports = {
FILE,
JS,
URL
};
function FILE(s) {
return !URL(s) && /\.json$/.test(s);
}
function JS(s) {
return !URL(s) && /\.c?js$/.test(s);
}
function URL(s) {
return /^(http|https):/.test(s);
}
Loading

0 comments on commit e1e5e57

Please sign in to comment.