Skip to content

Commit ccf6772

Browse files
committed
Refactor CLI and add tests
1 parent e3a7db9 commit ccf6772

File tree

22 files changed

+368
-172
lines changed

22 files changed

+368
-172
lines changed

bin/index.js

Lines changed: 1 addition & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,2 @@
11
#!/usr/bin/env node
2-
var fs = require('fs')
3-
var path = require('path')
4-
var updateNotifier = require('update-notifier')
5-
var _db = require('underscore-db')
6-
var yargs = require('yargs')
7-
var chalk = require('chalk')
8-
var got = require('got')
9-
var pkg = require('../package.json')
10-
var jsonServer = require('../src')
11-
var jsMockGenerator = null
12-
13-
updateNotifier({packageName: pkg.name, packageVersion: pkg.version}).notify()
14-
15-
// Parse arguments
16-
var argv = yargs
17-
.usage('$0 [options] <source>')
18-
.options({
19-
port: {
20-
alias: 'p',
21-
description: 'Set port',
22-
default: 3000
23-
},
24-
host: {
25-
alias: 'H',
26-
description: 'Set host',
27-
default: '0.0.0.0'
28-
},
29-
watch: {
30-
alias: 'w',
31-
description: 'Reload database on JSON file change'
32-
},
33-
routes: {
34-
alias: 'r',
35-
description: 'Load routes file'
36-
},
37-
id: {
38-
description: 'Set database id property (e.g. _id)',
39-
default: 'id'
40-
}
41-
})
42-
.boolean('watch')
43-
.help('help').alias('help', 'h')
44-
.version(pkg.version).alias('version', 'v')
45-
.example('$0 db.json', '')
46-
.example('$0 file.js', '')
47-
.example('$0 http://example.com/db.json', '')
48-
.epilog('https://github.com/typicode/json-server')
49-
.require(1, 'Missing <source> argument')
50-
.argv
51-
52-
function showResources (hostname, port, object) {
53-
for (var prop in object) {
54-
console.log(chalk.gray(' http://' + hostname + ':' + port + '/') + chalk.cyan(prop))
55-
}
56-
}
57-
58-
function start (object, filename) {
59-
var port = process.env.PORT || argv.port
60-
var hostname = argv.host === '0.0.0.0' ? 'localhost' : argv.host
61-
62-
console.log()
63-
showResources(hostname, port, object)
64-
console.log()
65-
console.log(
66-
'You can now go to ' + chalk.gray('http://' + hostname + ':' + port)
67-
)
68-
console.log()
69-
console.log(
70-
'Enter ' + chalk.cyan('s') + ' at any time to create a snapshot of the db'
71-
)
72-
73-
// Snapshot
74-
process.stdin.resume()
75-
process.stdin.setEncoding('utf8')
76-
process.stdin.on('data', function (chunk) {
77-
if (chunk.trim().toLowerCase() === 's') {
78-
var file = 'db-' + Date.now() + '.json'
79-
_db.save(object, file)
80-
console.log('Saved snapshot to ' + chalk.cyan(file) + '\n')
81-
}
82-
})
83-
84-
// Router
85-
var router = jsonServer.router(filename ? filename : object)
86-
87-
// Watcher
88-
if (argv.watch) {
89-
console.log('Watching', chalk.cyan(source))
90-
91-
var db = router.db
92-
var watchedDir = path.dirname(filename)
93-
var watchedFile = path.basename(filename)
94-
95-
fs.watch(watchedDir, function (event, changedFile) {
96-
// lowdb generates 'rename' event on watchedFile
97-
// using it to know if file has been modified by the user
98-
if ((event === 'change' || event === 'rename') && (changedFile === watchedFile || changedFile === source)) {
99-
console.log(chalk.cyan(source), 'has changed, reloading database')
100-
101-
try {
102-
if (filename) {
103-
db.object = JSON.parse(fs.readFileSync(filename))
104-
} else {
105-
require.cache[jsMockGenerator] = null
106-
db.object = require(jsMockGenerator)()
107-
}
108-
showResources(hostname, port, db.object)
109-
} catch (e) {
110-
console.log('Can\'t parse', chalk.cyan(source))
111-
console.log(e.message)
112-
}
113-
114-
console.log()
115-
}
116-
})
117-
}
118-
console.log()
119-
120-
var server = jsonServer.create()
121-
server.use(jsonServer.defaults)
122-
123-
// Rewriter
124-
if (argv.routes) {
125-
var routes = JSON.parse(fs.readFileSync(process.cwd() + '/' + argv.routes))
126-
var rewriter = jsonServer.rewriter(routes)
127-
server.use(rewriter)
128-
}
129-
130-
server.use(router)
131-
132-
// Custom id
133-
router.db._.id = argv.id
134-
135-
server.listen(port, argv.host)
136-
}
137-
138-
// Set file and port
139-
var source = argv._[0]
140-
141-
// Say hi, load file and start server
142-
console.log(chalk.cyan(' {^_^} Hi!\n'))
143-
console.log('Loading database from ' + chalk.cyan(source))
144-
145-
// Remote source
146-
if (/^(http|https):/.test(source)) {
147-
got(source, function (err, data) {
148-
if (err) {
149-
console.log('Error', err)
150-
process.exit(1)
151-
}
152-
var object = JSON.parse(data)
153-
start(object)
154-
})
155-
// JSON file
156-
} else if (/\.json$/.test(source)) {
157-
var filename = process.cwd() + '/' + source
158-
var object = require(filename)
159-
start(object, filename)
160-
// JS file
161-
} else if (/\.js$/.test(source)) {
162-
jsMockGenerator = process.cwd() + '/' + source
163-
var object = require(jsMockGenerator)()
164-
start(object)
165-
}
2+
require('../src/cli')()

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "json-server",
33
"version": "0.7.20",
44
"description": "Serves JSON files through REST routes.",
5-
"main": "./src/index.js",
5+
"main": "./src/server/index.js",
66
"bin": "./bin/index.js",
77
"directories": {
88
"test": "test"
@@ -13,25 +13,26 @@
1313
"cors": "^2.3.0",
1414
"errorhandler": "^1.2.0",
1515
"express": "^4.9.5",
16-
"got": "^1.2.2",
16+
"got": "^3.3.0",
1717
"lodash": "^3.9.2",
1818
"lowdb": "^0.10.0",
1919
"method-override": "^2.1.2",
2020
"morgan": "^1.3.1",
2121
"node-uuid": "^1.4.2",
2222
"pluralize": "^1.1.2",
2323
"underscore-db": "^0.9.0",
24-
"update-notifier": "^0.2.2",
24+
"update-notifier": "^0.5.0",
2525
"yargs": "^3.10.0"
2626
},
2727
"devDependencies": {
2828
"husky": "^0.6.1",
2929
"mocha": "^2.2.4",
30+
"rimraf": "^2.4.1",
3031
"standard": "^3.8.0",
3132
"supertest": "~0.8.1"
3233
},
3334
"scripts": {
34-
"test": "standard && mocha -R spec test",
35+
"test": "NODE_ENV=test mocha -R spec test/**/*.js && standard",
3536
"start": "node bin",
3637
"prepush": "npm t"
3738
},

src/cli/index.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
var updateNotifier = require('update-notifier')
2+
var yargs = require('yargs')
3+
var run = require('./run')
4+
var pkg = require('../../package.json')
5+
6+
module.exports = function () {
7+
8+
updateNotifier({ pkg: pkg }).notify()
9+
10+
var argv = yargs
11+
.usage('$0 [options] <source>')
12+
.options({
13+
port: {
14+
alias: 'p',
15+
description: 'Set port',
16+
default: 3000
17+
},
18+
host: {
19+
alias: 'H',
20+
description: 'Set host',
21+
default: '0.0.0.0'
22+
},
23+
watch: {
24+
alias: 'w',
25+
description: 'Watch file(s)'
26+
},
27+
routes: {
28+
alias: 'r',
29+
description: 'Load routes file'
30+
},
31+
id: {
32+
description: 'Set database id property (e.g. _id)',
33+
default: 'id'
34+
}
35+
})
36+
.boolean('watch')
37+
.help('help').alias('help', 'h')
38+
.version(pkg.version).alias('version', 'v')
39+
.example('$0 db.json', '')
40+
.example('$0 file.js', '')
41+
.example('$0 http://example.com/db.json', '')
42+
.epilog('https://github.com/typicode/json-server')
43+
.require(1, 'Missing <source> argument')
44+
.argv
45+
46+
run(argv)
47+
48+
}

src/cli/run.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
var fs = require('fs')
2+
var chalk = require('chalk')
3+
var is = require('./utils/is')
4+
var load = require('./utils/load')
5+
var watch = require('./watch')
6+
var jsonServer = require('../server')
7+
8+
function prettyPrint (argv, object, rules) {
9+
var host = argv.host === '0.0.0.0' ? 'localhost' : argv.host
10+
var port = argv.port
11+
var root = 'http://' + host + ':' + port
12+
13+
console.log()
14+
console.log(chalk.bold(' Resources'))
15+
for (var prop in object) {
16+
console.log(' ' + root + '/' + prop)
17+
}
18+
19+
if (rules) {
20+
console.log()
21+
console.log(chalk.bold(' Other routes'))
22+
for (var rule in rules) {
23+
console.log(' ' + rule + ' -> ' + rules[rule])
24+
}
25+
}
26+
27+
console.log()
28+
console.log(chalk.bold(' Home'))
29+
console.log(' ' + root)
30+
console.log()
31+
}
32+
33+
function createServer (source, object, routes) {
34+
var server = jsonServer.create()
35+
36+
var router = jsonServer.router(
37+
is.JSON(source) ?
38+
source :
39+
object
40+
)
41+
42+
server.use(jsonServer.defaults)
43+
44+
if (routes) {
45+
var rewriter = jsonServer.rewriter(routes)
46+
server.use(rewriter)
47+
}
48+
49+
server.use(router)
50+
51+
return server
52+
}
53+
54+
module.exports = function (argv) {
55+
56+
var source = argv._[0]
57+
var server
58+
59+
console.log()
60+
console.log(chalk.cyan(' \\{^_^}/ hi!'))
61+
62+
function start () {
63+
console.log()
64+
console.log(chalk.gray(' Loading', source))
65+
66+
// Load JSON, JS or HTTP database
67+
load(source, function (err, data) {
68+
69+
if (err) throw err
70+
71+
// Load additional routes
72+
if (argv.routes) {
73+
console.log(chalk.gray(' Loading', argv.routes))
74+
var routes = JSON.parse(fs.readFileSync(argv.routes))
75+
}
76+
77+
console.log(chalk.gray(' Done'))
78+
79+
// Create server and listen
80+
server = createServer(source, data, routes).listen(argv.port, argv.host)
81+
82+
// Display server informations
83+
prettyPrint(argv, data, routes)
84+
})
85+
}
86+
87+
// Start server
88+
start()
89+
90+
// Watch files
91+
if (argv.watch) {
92+
console.log(chalk.gray(' Watching...'))
93+
console.log()
94+
watch(argv, function (file) {
95+
console.log(chalk.gray(' ' + file + ' has changed, reloading...'))
96+
// Restart server
97+
server && server.close()
98+
start()
99+
})
100+
}
101+
102+
}

src/cli/utils/is.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = {
2+
JSON: isJSON,
3+
JS: isJS,
4+
URL: isURL
5+
}
6+
7+
function isJSON (s) {
8+
return /\.json$/.test(s)
9+
}
10+
11+
function isJS (s) {
12+
return /\.js$/.test(s)
13+
}
14+
15+
function isURL (s) {
16+
return /^(http|https):/.test(s)
17+
}

src/cli/utils/load.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var path = require('path')
2+
var got = require('got')
3+
var is = require('./is')
4+
5+
module.exports = function (source, cb) {
6+
if (is.URL(source)) {
7+
// Load URL
8+
got(source, { json: true }, function (err, data) {
9+
cb(err, data)
10+
})
11+
} else {
12+
// Load JS or JSON
13+
var filename = path.resolve(source)
14+
delete require.cache[filename]
15+
var data = is.JSON(source) ? require(filename) : require(filename)()
16+
cb(null, data)
17+
}
18+
}

0 commit comments

Comments
 (0)