1+ // @ts -check
12const path = require ( 'path' )
23const process = require ( 'process' )
34const { promisify } = require ( 'util' )
@@ -53,53 +54,75 @@ const isNonExistingCommandError = ({ command, error }) => {
5354 )
5455}
5556
56- const startFrameworkServer = async function ( { settings } ) {
57- if ( settings . useStaticServer ) {
58- return await startStaticServer ( { settings } )
59- }
60-
61- log ( `${ NETLIFYDEVLOG } Starting Netlify Dev with ${ settings . framework || 'custom config' } ` )
62-
63- // we use reject=false to avoid rejecting synchronously when the command doesn't exist
64- const frameworkProcess = execa . command ( settings . command , {
57+ /**
58+ * Run a command and pipe stdout, stderr and stdin
59+ * @param {string } command
60+ * @param {NodeJS.ProcessEnv } env
61+ * @returns {execa.ExecaChildProcess<string> }
62+ */
63+ const runCommand = ( command , env = { } ) => {
64+ const commandProcess = execa . command ( command , {
6565 preferLocal : true ,
66+ // we use reject=false to avoid rejecting synchronously when the command doesn't exist
6667 reject : false ,
67- env : settings . env ,
68+ env,
6869 // windowsHide needs to be false for child process to terminate properly on Windows
6970 windowsHide : false ,
7071 } )
71- frameworkProcess . stdout . pipe ( stripAnsiCc . stream ( ) ) . pipe ( process . stdout )
72- frameworkProcess . stderr . pipe ( stripAnsiCc . stream ( ) ) . pipe ( process . stderr )
73- process . stdin . pipe ( frameworkProcess . stdin )
72+
73+ commandProcess . stdout . pipe ( stripAnsiCc . stream ( ) ) . pipe ( process . stdout )
74+ commandProcess . stderr . pipe ( stripAnsiCc . stream ( ) ) . pipe ( process . stderr )
75+ process . stdin . pipe ( commandProcess . stdin )
7476
7577 // we can't try->await->catch since we don't want to block on the framework server which
7678 // is a long running process
7779 // eslint-disable-next-line promise/catch-or-return,promise/prefer-await-to-then
78- frameworkProcess . then ( async ( ) => {
79- const result = await frameworkProcess
80- const [ commandWithoutArgs ] = settings . command . split ( ' ' )
80+ commandProcess . then ( async ( ) => {
81+ const result = await commandProcess
82+ const [ commandWithoutArgs ] = command . split ( ' ' )
8183 // eslint-disable-next-line promise/always-return
8284 if ( result . failed && isNonExistingCommandError ( { command : commandWithoutArgs , error : result } ) ) {
8385 log (
8486 NETLIFYDEVERR ,
85- `Failed launching framework server . Please verify ${ chalk . magenta ( `'${ commandWithoutArgs } '` ) } exists` ,
87+ `Failed running command: ${ command } . Please verify ${ chalk . magenta ( `'${ commandWithoutArgs } '` ) } exists` ,
8688 )
8789 } else {
8890 const errorMessage = result . failed
8991 ? `${ NETLIFYDEVERR } ${ result . shortMessage } `
90- : `${ NETLIFYDEVWARN } "${ settings . command } " exited with code ${ result . exitCode } `
92+ : `${ NETLIFYDEVWARN } "${ command } " exited with code ${ result . exitCode } `
9193
9294 log ( `${ errorMessage } . Shutting down Netlify Dev server` )
9395 }
9496 process . exit ( 1 )
9597 } )
9698 ; [ 'SIGINT' , 'SIGTERM' , 'SIGQUIT' , 'SIGHUP' , 'exit' ] . forEach ( ( signal ) => {
9799 process . on ( signal , ( ) => {
98- frameworkProcess . kill ( 'SIGTERM' , { forceKillAfterTimeout : 500 } )
100+ commandProcess . kill ( 'SIGTERM' , { forceKillAfterTimeout : 500 } )
99101 process . exit ( )
100102 } )
101103 } )
102104
105+ return commandProcess
106+ }
107+
108+ /**
109+ * Start a static server if the `useStaticServer` is provided or a framework specific server
110+ * @param {object } config
111+ * @param {import('../../utils/types').ServerSettings } config.settings
112+ * @returns {Promise<void> }
113+ */
114+ const startFrameworkServer = async function ( { settings } ) {
115+ if ( settings . useStaticServer ) {
116+ if ( settings . command ) {
117+ runCommand ( settings . command , settings . env )
118+ }
119+ return await startStaticServer ( { settings } )
120+ }
121+
122+ log ( `${ NETLIFYDEVLOG } Starting Netlify Dev with ${ settings . framework || 'custom config' } ` )
123+
124+ runCommand ( settings . command , settings . env )
125+
103126 try {
104127 const open = await waitPort ( {
105128 port : settings . frameworkPort ,
@@ -185,6 +208,7 @@ class DevCommand extends Command {
185208 const { api, config, site, siteInfo } = this . netlify
186209 config . dev = { ...config . dev }
187210 config . build = { ...config . build }
211+ /** @type {import('./types').DevConfig } */
188212 const devConfig = {
189213 framework : '#auto' ,
190214 ...( config . functionsDirectory && { functions : config . functionsDirectory } ) ,
@@ -207,6 +231,7 @@ class DevCommand extends Command {
207231 siteInfo,
208232 } )
209233
234+ /** @type {Partial<import('../../utils/types').ServerSettings> } */
210235 let settings = { }
211236 try {
212237 settings = await detectServerSettings ( devConfig , flags , site . root )
0 commit comments