Skip to content

Commit 4cab066

Browse files
Add chalk dependency and enhance CLI output styling with colorful messages
1 parent 69fab7f commit 4cab066

File tree

5 files changed

+454
-100
lines changed

5 files changed

+454
-100
lines changed

package-lock.json

+2-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
},
8888
"dependencies": {
8989
"body-parser": "^2.2.0",
90+
"chalk": "^4.1.2",
9091
"connect-pause": "^0.1.0",
9192
"cors": "^2.8.5",
9293
"express": "^5.1.0",

src/bin/index.ts

+111-60
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ import fs from 'fs';
2424
import { createServer } from '../lib';
2525
import { CliArgs, ServerOptions } from '../types';
2626
import { fileExists } from '../utils/utils';
27+
import {
28+
styles,
29+
createHeader,
30+
createBox,
31+
formatList,
32+
formatHelp,
33+
formatError,
34+
} from '../utils/cli-styles';
2735

2836
// Default server configuration
2937
const DEFAULT_PORT = 3000;
@@ -80,10 +88,22 @@ function showVersion(): void {
8088
const packageJsonPath = path.join(__dirname, '../../package.json');
8189
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
8290

83-
console.log(`@webmasterdevlin/json-server v${packageJson.version}`);
91+
console.log(
92+
createBox(
93+
'⚡️ @webmasterdevlin/json-server',
94+
[
95+
'',
96+
`${styles.primary('Version:')} ${styles.highlight(packageJson.version)}`,
97+
'',
98+
`${styles.secondary('TypeScript-powered REST API mock server')}`,
99+
'',
100+
],
101+
'default'
102+
)
103+
);
84104
} catch (error) {
85105
const errorMessage = error instanceof Error ? error.message : String(error);
86-
console.error('Error reading package version:', errorMessage);
106+
console.error(formatError('Version Error', `Error reading package version: ${errorMessage}`));
87107
process.exit(1);
88108
}
89109
}
@@ -92,50 +112,46 @@ function showVersion(): void {
92112
* Display help information with usage examples
93113
*/
94114
function showHelp(): void {
95-
console.log(`
96-
╭───────────────────────────────────────────────────────────────────────────╮
97-
│ │
98-
│ @webmasterdevlin/json-server CLI │
99-
│ │
100-
╰───────────────────────────────────────────────────────────────────────────╯
101-
102-
A TypeScript implementation of json-server with additional features.
103-
104-
Usage:
105-
json-server [options] <source>
106-
107-
Options:
108-
--port, -p Set port [default: 3000]
109-
--host, -H Set host [default: "localhost"]
110-
--watch, -w Watch for changes [default: false]
111-
--routes, -r Path to routes file [string]
112-
--middlewares, -m Path to middlewares files [array]
113-
--static, -s Path to static files [array]
114-
--read-only, --ro Allow only GET requests [default: false]
115-
--no-cors, --nc Disable CORS [default: false]
116-
--no-gzip, --ng Disable GZIP compression [default: false]
117-
--enable-api-prefix, --api Enable /api/* prefix [default: false]
118-
--delay, -d Add delay to responses (ms) [number]
119-
--id, -i Set database id field [default: "id"]
120-
--foreignKeySuffix Set foreign key suffix [default: "_id"]
121-
--quiet, -q Suppress log messages [default: false]
122-
--help, -h Show help [boolean]
123-
--version, -v Show version [boolean]
124-
125-
Examples:
126-
json-server db.json
127-
json-server db.json --port 3001
128-
json-server db.json --watch
129-
json-server db.json --routes routes.json
130-
json-server db.json --routes routes.js
131-
json-server db.json --delay 1000
132-
json-server db.json --id _id
133-
json-server db.json --enable-api-prefix # Enable /api/* routes
134-
json-server http://example.com/db.json
135-
136-
For more information, visit:
137-
https://github.com/webmasterdevlin/json-server
138-
`);
115+
// Create sections for the help display
116+
const sections: Record<string, string> = {
117+
Usage: `${styles.command('json-server')} ${styles.info('[options]')} ${styles.highlight('<source>')}`,
118+
119+
Options: formatList([
120+
`${styles.key('--port, -p')} ${styles.value('Set port')}${styles.muted(' [default: 3000]')}`,
121+
`${styles.key('--host, -H')} ${styles.value('Set host')}${styles.muted(' [default: "localhost"]')}`,
122+
`${styles.key('--watch, -w')} ${styles.value('Watch for changes')}${styles.muted(' [default: false]')}`,
123+
`${styles.key('--routes, -r')} ${styles.value('Path to routes file')}${styles.muted(' [string]')}`,
124+
`${styles.key('--middlewares, -m')} ${styles.value('Path to middlewares files')}${styles.muted(' [array]')}`,
125+
`${styles.key('--static, -s')} ${styles.value('Path to static files')}${styles.muted(' [array]')}`,
126+
`${styles.key('--read-only, --ro')} ${styles.value('Allow only GET requests')}${styles.muted(' [default: false]')}`,
127+
`${styles.key('--no-cors, --nc')} ${styles.value('Disable CORS')}${styles.muted(' [default: false]')}`,
128+
`${styles.key('--no-gzip, --ng')} ${styles.value('Disable GZIP compression')}${styles.muted(' [default: false]')}`,
129+
`${styles.key('--enable-api-prefix, --api')} ${styles.value('Enable /api/* prefix')}${styles.muted(' [default: false]')}`,
130+
`${styles.key('--delay, -d')} ${styles.value('Add delay to responses (ms)')}${styles.muted(' [number]')}`,
131+
`${styles.key('--id, -i')} ${styles.value('Set database id field')}${styles.muted(' [default: "id"]')}`,
132+
`${styles.key('--foreignKeySuffix')} ${styles.value('Set foreign key suffix')}${styles.muted(' [default: "_id"]')}`,
133+
`${styles.key('--quiet, -q')} ${styles.value('Suppress log messages')}${styles.muted(' [default: false]')}`,
134+
`${styles.key('--help, -h')} ${styles.value('Show help')}${styles.muted(' [boolean]')}`,
135+
`${styles.key('--version, -v')} ${styles.value('Show version')}${styles.muted(' [boolean]')}`,
136+
]),
137+
138+
Examples: formatList([
139+
`${styles.command('json-server')} ${styles.highlight('db.json')}`,
140+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--port 3001')}`,
141+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--watch')}`,
142+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--routes routes.json')}`,
143+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--routes routes.js')}`,
144+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--delay 1000')}`,
145+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--id _id')}`,
146+
`${styles.command('json-server')} ${styles.highlight('db.json')} ${styles.info('--enable-api-prefix')} ${styles.muted('# Enable /api/* routes')}`,
147+
`${styles.command('json-server')} ${styles.highlight('http://example.com/db.json')}`,
148+
]),
149+
150+
'More Information': `${styles.url('https://github.com/webmasterdevlin/json-server')}`,
151+
};
152+
153+
// Display the help sections
154+
console.log(formatHelp(sections));
139155
}
140156

141157
/**
@@ -158,19 +174,39 @@ async function main(): Promise<void> {
158174
const cliArgs = parseArgs();
159175
const args = process.argv.slice(2);
160176

177+
// Show welcome banner
178+
if (!cliArgs.quiet) {
179+
console.log(createHeader());
180+
}
181+
161182
// Get database source
162183
const source = getSourcePath(args);
163184
if (!source) {
164-
console.error('Error: No database source provided');
185+
console.error(
186+
formatError(
187+
'Missing Source',
188+
'No database source provided.',
189+
'Please specify a JSON file or URL to use as database.'
190+
)
191+
);
165192
showHelp();
166193
process.exit(1);
167194
}
168195

169196
// Check if source file exists (if it's a local file)
170197
if (source.startsWith('http://') || source.startsWith('https://')) {
171-
console.log(`Using remote database: ${source}`);
198+
console.log(
199+
styles.icons.database,
200+
styles.info(`Using remote database: ${styles.url(source)}`)
201+
);
172202
} else if (!fileExists(source)) {
173-
console.error(`Error: Database file not found: ${source}`);
203+
console.error(
204+
formatError(
205+
'Database Error',
206+
`Database file not found: ${source}`,
207+
'Make sure the file exists and you have permissions to read it.'
208+
)
209+
);
174210
process.exit(1);
175211
}
176212

@@ -201,9 +237,12 @@ async function main(): Promise<void> {
201237
// Load custom routes if specified
202238
if (cliArgs.routes) {
203239
if (!fileExists(cliArgs.routes)) {
204-
console.warn(`Warning: Routes file not found: ${cliArgs.routes}`);
240+
console.warn(
241+
styles.icons.warning,
242+
styles.warning(`Routes file not found: ${styles.highlight(cliArgs.routes)}`)
243+
);
205244
} else {
206-
server.loadRoutes(cliArgs.routes);
245+
await server.loadRoutes(cliArgs.routes);
207246
}
208247
}
209248

@@ -219,7 +258,7 @@ async function main(): Promise<void> {
219258
}
220259
} catch (error) {
221260
const errorMessage = error instanceof Error ? error.message : String(error);
222-
console.error('Error starting server:', errorMessage);
261+
console.error(formatError('Server Error', `Error starting server: ${errorMessage}`));
223262
process.exit(1);
224263
}
225264
}
@@ -235,37 +274,49 @@ function setupFileWatcher(dbPath: string, server: any): void {
235274
// Use Node's fs.watch API to monitor file changes
236275
fs.watch(dbPath, (eventType) => {
237276
if (eventType === 'change') {
238-
console.log(`Database file changed: ${dbPath}`);
277+
console.log(
278+
styles.icons.watch,
279+
styles.info(`${styles.highlight(dbPath)} changed, reloading database...`)
280+
);
239281
try {
240282
server.loadDatabase(dbPath);
241-
console.log('Database reloaded');
283+
console.log(styles.icons.success, styles.success('Database reloaded successfully'));
242284
} catch (error) {
243285
const errorMessage = error instanceof Error ? error.message : String(error);
244-
console.error('Error reloading database:', errorMessage);
286+
console.error(
287+
styles.icons.error,
288+
styles.error(`Error reloading database: ${errorMessage}`)
289+
);
245290
}
246291
}
247292
});
248293

249-
console.log(`Watching for changes: ${dbPath}`);
294+
console.log(
295+
styles.icons.watch,
296+
styles.info(`Watching for changes: ${styles.highlight(dbPath)}`)
297+
);
250298
} catch (error) {
251299
const errorMessage = error instanceof Error ? error.message : String(error);
252-
console.error('Error setting up file watcher:', errorMessage);
300+
console.error(
301+
styles.icons.error,
302+
styles.error(`Error setting up file watcher: ${errorMessage}`)
303+
);
253304
}
254305
}
255306

256307
// Handle process termination gracefully
257308
process.on('SIGINT', () => {
258-
console.log('\nReceived SIGINT. Shutting down...');
309+
console.log('\n' + styles.icons.stop, styles.info('Received SIGINT. Shutting down...'));
259310
process.exit(0);
260311
});
261312

262313
process.on('SIGTERM', () => {
263-
console.log('\nReceived SIGTERM. Shutting down...');
314+
console.log('\n' + styles.icons.stop, styles.info('Received SIGTERM. Shutting down...'));
264315
process.exit(0);
265316
});
266317

267318
// Start the application
268319
main().catch((error) => {
269-
console.error('Fatal error:', error.message);
320+
console.error(formatError('Fatal Error', error.message));
270321
process.exit(1);
271322
});

0 commit comments

Comments
 (0)