@@ -40,42 +40,95 @@ function getOutDir(): string {
4040}
4141
4242/**
43- * Load the configuration from the compiler
44- * @param compiler The compiler to load the configuration from
45- * @returns The configuration
43+ * Build the tsx arguments for running TypeScript files.
44+ * Used by nodemon to execute the TypeScript entry point.
45+ *
46+ * @param opinionated - Whether to use opinionated configuration (tsconfig-paths)
47+ * @returns The tsx arguments array
4648 */
47- async function opinionatedConfig ( ) : Promise < Array < string > > {
49+ async function buildTsxArgs ( opinionated : boolean ) : Promise < Array < string > > {
4850 const { entryPoint } = await Compiler . loadConfig ( ) ;
49- const config = [
51+
52+ if ( opinionated ) {
53+ return [ "-r" , "tsconfig-paths/register" , `./src/${ entryPoint } .ts` ] ;
54+ }
55+
56+ return [ `./src/${ entryPoint } .ts` ] ;
57+ }
58+
59+ /**
60+ * Build the nodemon arguments for development mode.
61+ * Uses nodemon for file watching and tsx for TypeScript execution.
62+ * This combination ensures proper signal handling for graceful shutdown.
63+ *
64+ * Options:
65+ * - --quiet: Suppress nodemon verbose output, show only ExpressoTS logs (default)
66+ * - --signal SIGTERM: Ensure proper signal forwarding for graceful shutdown
67+ * - --delay 500ms: Debounce file changes to avoid rapid restarts
68+ *
69+ * @param opinionated - Whether to use opinionated configuration
70+ * @param verbose - Whether to show verbose nodemon output (for debugging)
71+ * @returns The nodemon arguments array
72+ */
73+ async function buildDevArgs (
74+ opinionated : boolean ,
75+ verbose : boolean = false ,
76+ ) : Promise < Array < string > > {
77+ const tsxArgs = await buildTsxArgs ( opinionated ) ;
78+
79+ const args : Array < string > = [ ] ;
80+
81+ // Suppress nodemon output unless verbose mode is enabled
82+ if ( ! verbose ) {
83+ args . push ( "--quiet" ) ;
84+ }
85+
86+ // Core nodemon configuration
87+ args . push (
88+ "--signal" ,
89+ "SIGTERM" , // Use SIGTERM for graceful shutdown
90+ "--delay" ,
91+ "500ms" , // Debounce rapid file changes
5092 "--watch" ,
51- "-r" ,
52- "tsconfig-paths/register" ,
53- `./src/${ entryPoint } .ts` ,
54- ] ;
55- return config ;
93+ "src" ,
94+ "--ext" ,
95+ "ts,json" ,
96+ "--ignore" ,
97+ "src/**/*.spec.ts" ,
98+ "--ignore" ,
99+ "src/**/*.test.ts" ,
100+ "--exec" ,
101+ `tsx ${ tsxArgs . join ( " " ) } ` ,
102+ ) ;
103+
104+ return args ;
56105}
57106
58107/**
59- * Load the configuration from the compiler
60- * @param compiler The compiler to load the configuration from
61- * @returns The configuration
108+ * Dev command options interface
62109 */
63- async function nonOpinionatedConfig ( ) : Promise < Array < string > > {
64- const { entryPoint } = await Compiler . loadConfig ( ) ;
65- const config = [ "--watch" , `./src/${ entryPoint } .ts` ] ;
66- return config ;
110+ interface DevCommandOptions {
111+ verbose ?: boolean ;
67112}
68113
69114/**
70115 * Dev command module
71- * @type {CommandModule<object, object > }
116+ * @type {CommandModule<object, DevCommandOptions > }
72117 * @returns The command module
73118 */
74- export const devCommand : CommandModule < object , object > = {
119+ export const devCommand : CommandModule < object , DevCommandOptions > = {
75120 command : "dev" ,
76121 describe : "Start development server." ,
77- handler : async ( ) => {
78- await runCommand ( { command : "dev" } ) ;
122+ builder : {
123+ verbose : {
124+ alias : "v" ,
125+ type : "boolean" ,
126+ default : false ,
127+ description : "Show verbose nodemon output for debugging" ,
128+ } ,
129+ } ,
130+ handler : async ( argv ) => {
131+ await runCommand ( { command : "dev" , verbose : argv . verbose } ) ;
79132 } ,
80133} ;
81134
@@ -179,27 +232,29 @@ const clearScreen = () => {
179232 spawn ( command , { stdio : "inherit" , shell : true } ) ;
180233} ;
181234
235+ /**
236+ * Run command options
237+ */
238+ interface RunCommandOptions {
239+ command : string ;
240+ verbose ?: boolean ;
241+ }
242+
182243/**
183244 * Helper function to run a command
184- * @param command The command to run
245+ * @param options The command options
185246 */
186247export const runCommand = async ( {
187248 command,
188- } : {
189- command : string ;
190- } ) : Promise < void > => {
249+ verbose = false ,
250+ } : RunCommandOptions ) : Promise < void > => {
191251 const { opinionated, entryPoint } = await Compiler . loadConfig ( ) ;
192252 const outDir = getOutDir ( ) ;
193253
194254 try {
195255 switch ( command ) {
196256 case "dev" :
197- execCmd (
198- "tsx" ,
199- opinionated
200- ? await opinionatedConfig ( )
201- : await nonOpinionatedConfig ( ) ,
202- ) ;
257+ await execCmd ( "nodemon" , await buildDevArgs ( opinionated , verbose ) ) ;
203258 break ;
204259 case "build" :
205260 if ( ! outDir ) {
@@ -233,7 +288,7 @@ export const runCommand = async ({
233288 config = [ `./${ outDir } /${ entryPoint } .js` ] ;
234289 }
235290 clearScreen ( ) ;
236- execCmd ( "node" , config ) ;
291+ await execCmd ( "node" , config ) ;
237292 break ;
238293 }
239294 default :
0 commit comments