text: import fs from 'fs'
  
  import concurrently from 'concurrently'
  import terminalLink from 'terminal-link'
  
  import { getConfig, getConfigPath, shutdownPort } from '@redwoodjs/internal'
  
  import { getPaths } from '../lib'
  import c from '../lib/colors'
  import { generatePrismaClient } from '../lib/generatePrismaClient'
  import checkForBabelConfig from '../middleware/checkForBabelConfig'
  
  export const command = 'dev [side..]'
  export const description = 'Start development servers for api, and web'
  export const builder = (yargs) => {
    yargs
      .positional('side', {
        choices: ['api', 'web'],
        default: ['api', 'web'],
        description: 'Which dev server(s) to start',
        type: 'array',
      })
      .option('forward', {
        alias: 'fwd',
        description:
          'String of one or more Webpack DevServer config options, for example: `--fwd="--port=1234 --no-open"`',
        type: 'string',
      })
      .option('generate', {
        type: 'boolean',
        default: true,
        description: 'Generate artifacts',
      })
      .option('watchNodeModules', {
        type: 'boolean',
        description: 'Reload on changes to node_modules',
      })
      .middleware(checkForBabelConfig)
      .epilogue(
        `Also see the ${terminalLink(
          'Redwood CLI Reference',
          'https://redwoodjs.com/reference/command-line-interface#dev'
        )}`
      )
  }
  
  export const handler = async ({
    side = ['api', 'web'],
    forward = '',
    generate = true,
    watchNodeModules = process.env.RWJS_WATCH_NODE_MODULES === '1',
  }) => {
    const rwjsPaths = getPaths()
  
    if (side.includes('api')) {
      try {
        await generatePrismaClient({
          verbose: false,
          force: false,
          schema: rwjsPaths.api.dbSchema,
        })
      } catch (e) {
        console.error(c.error(e.message))
      }
  
      try {
        await shutdownPort(getConfig().api.port)
      } catch (e) {
        console.error(
          `Error whilst shutting down "api" port: ${c.error(e.message)}`
        )
      }
    }
  
    if (side.includes('web')) {
      try {
        await shutdownPort(getConfig().web.port)
      } catch (e) {
        console.error(
          `Error whilst shutting down "web" port: ${c.error(e.message)}`
        )
      }
    }
  
    const webpackDevConfig = require.resolve(
      '@redwoodjs/core/config/webpack.development.js'
    )
  
    const redwoodConfigPath = getConfigPath()
  
    /** @type {Record<string, import('concurrently').CommandObj>} */
    const jobs = {
      api: {
        name: 'api',
        command: `yarn cross-env NODE_ENV=development NODE_OPTIONS=--enable-source-maps yarn nodemon --watch "${redwoodConfigPath}" --exec "yarn rw-api-server-watch"`,
        prefixColor: 'cyan',
        runWhen: () => fs.existsSync(rwjsPaths.api.src),
      },
      web: {
        name: 'web',
        command: `cd "${
          rwjsPaths.web.base
        }" && yarn cross-env NODE_ENV=development RWJS_WATCH_NODE_MODULES=${
          watchNodeModules ? '1' : ''
        } webpack serve --config "${webpackDevConfig}" ${forward}`,
        prefixColor: 'blue',
        runWhen: () => fs.existsSync(rwjsPaths.web.src),
      },
      gen: {
        name: 'gen',
        command: 'yarn rw-gen-watch',
        prefixColor: 'green',
        runWhen: () => generate,
      },
    }
  
    // TODO: Convert jobs to an array and supply cwd command.
    concurrently(
      Object.keys(jobs)
        .map((job) => {
          if (side.includes(job) || job === 'gen') {
            return jobs[job]
          }
        })
        .filter((job) => job && job.runWhen()),
      {
        prefix: '{name} |',
        timestampFormat: 'HH:mm:ss',
      }
    ).catch((e) => {
      if (typeof e?.message !== 'undefined') {
        console.error(c.error(e.message))
        process.exit(1)
      }
    })
  }
  