netlify-cli 15.10.0-rc.1 → 15.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/bin/run.mjs +5 -6
  2. package/npm-shrinkwrap.json +3 -6
  3. package/package.json +2 -1
  4. package/src/commands/base-command.mjs +116 -295
  5. package/src/commands/build/build.mjs +1 -9
  6. package/src/commands/deploy/deploy.mjs +9 -23
  7. package/src/commands/dev/dev.mjs +17 -22
  8. package/src/commands/functions/functions-create.mjs +89 -118
  9. package/src/commands/functions/functions-invoke.mjs +7 -10
  10. package/src/commands/functions/functions-list.mjs +2 -2
  11. package/src/commands/init/init.mjs +1 -1
  12. package/src/commands/link/link.mjs +5 -5
  13. package/src/commands/serve/serve.mjs +6 -10
  14. package/src/commands/sites/sites-create-template.mjs +1 -1
  15. package/src/commands/sites/sites-create.mjs +1 -1
  16. package/src/lib/edge-functions/internal.mjs +3 -5
  17. package/src/lib/edge-functions/proxy.mjs +3 -27
  18. package/src/lib/functions/netlify-function.mjs +26 -1
  19. package/src/lib/functions/registry.mjs +14 -26
  20. package/src/lib/functions/runtimes/js/worker.mjs +1 -1
  21. package/src/lib/spinner.mjs +1 -1
  22. package/src/recipes/vscode/index.mjs +6 -24
  23. package/src/utils/command-helpers.mjs +7 -16
  24. package/src/utils/detect-server-settings.mjs +245 -133
  25. package/src/utils/framework-server.mjs +5 -6
  26. package/src/utils/functions/functions.mjs +5 -8
  27. package/src/utils/get-repo-data.mjs +6 -5
  28. package/src/utils/init/config-github.mjs +2 -2
  29. package/src/utils/init/config-manual.mjs +7 -24
  30. package/src/utils/init/frameworks.mjs +23 -0
  31. package/src/utils/init/utils.mjs +63 -62
  32. package/src/utils/proxy-server.mjs +4 -7
  33. package/src/utils/proxy.mjs +3 -4
  34. package/src/utils/read-repo-url.mjs +0 -4
  35. package/src/utils/run-build.mjs +32 -58
  36. package/src/utils/shell.mjs +7 -24
  37. package/src/utils/state-config.mjs +1 -5
  38. package/src/utils/static-server.mjs +0 -4
  39. package/src/utils/build-info.mjs +0 -100
@@ -5,12 +5,7 @@ import { exit, log } from '../command-helpers.mjs'
5
5
 
6
6
  import { createDeployKey, getBuildSettings, saveNetlifyToml, setupSite } from './utils.mjs'
7
7
 
8
- /**
9
- * Prompts for granting the netlify ssh public key access to your repo
10
- * @param {object} deployKey
11
- * @param {string} deployKey.public_key
12
- */
13
- const addDeployKey = async (deployKey) => {
8
+ const addDeployKey = async ({ deployKey }) => {
14
9
  log('\nGive this Netlify SSH public key access to your repository:\n')
15
10
  log(`\n${deployKey.public_key}\n\n`)
16
11
 
@@ -28,11 +23,6 @@ const addDeployKey = async (deployKey) => {
28
23
  }
29
24
  }
30
25
 
31
- /**
32
- * @param {object} config
33
- * @param {Awaited<ReturnType<import('../../utils/get-repo-data.mjs').default>>} config.repoData
34
- * @returns {Promise<string>}
35
- */
36
26
  const getRepoPath = async ({ repoData }) => {
37
27
  const { repoPath } = await inquirer.prompt([
38
28
  {
@@ -40,9 +30,6 @@ const getRepoPath = async ({ repoData }) => {
40
30
  name: 'repoPath',
41
31
  message: 'The SSH URL of the remote git repo:',
42
32
  default: repoData.url,
43
- /**
44
- * @param {string} url
45
- */
46
33
  validate: (url) => SSH_URL_REGEXP.test(url) || 'The URL provided does not use the SSH protocol',
47
34
  },
48
35
  ])
@@ -50,11 +37,7 @@ const getRepoPath = async ({ repoData }) => {
50
37
  return repoPath
51
38
  }
52
39
 
53
- /**
54
- * @param {string} deployHook
55
- * @returns
56
- */
57
- const addDeployHook = async (deployHook) => {
40
+ const addDeployHook = async ({ deployHook }) => {
58
41
  log('\nConfigure the following webhook for your repository:\n')
59
42
  log(`\n${deployHook}\n\n`)
60
43
  const { deployHookAdded } = await inquirer.prompt([
@@ -72,14 +55,14 @@ const addDeployHook = async (deployHook) => {
72
55
  /**
73
56
  * @param {object} config
74
57
  * @param {import('../../commands/base-command.mjs').default} config.command
75
- * @param {Awaited<ReturnType<import('../../utils/get-repo-data.mjs').default>>} config.repoData
58
+ * @param {*} config.repoData
76
59
  * @param {string} config.siteId
77
60
  */
78
61
  export default async function configManual({ command, repoData, siteId }) {
79
62
  const { netlify } = command
80
63
  const {
81
64
  api,
82
- cachedConfig: { configPath },
65
+ cachedConfig: { configPath, env },
83
66
  config,
84
67
  repositoryRoot,
85
68
  site: { root: siteRoot },
@@ -89,12 +72,12 @@ export default async function configManual({ command, repoData, siteId }) {
89
72
  repositoryRoot,
90
73
  siteRoot,
91
74
  config,
92
- command,
75
+ env,
93
76
  })
94
77
  await saveNetlifyToml({ repositoryRoot, config, configPath, baseDir, buildCmd, buildDir, functionsDir })
95
78
 
96
79
  const deployKey = await createDeployKey({ api })
97
- await addDeployKey(deployKey)
80
+ await addDeployKey({ deployKey })
98
81
 
99
82
  const repoPath = await getRepoPath({ repoData })
100
83
  const repo = {
@@ -116,7 +99,7 @@ export default async function configManual({ command, repoData, siteId }) {
116
99
  configPlugins: config.plugins,
117
100
  pluginsToInstall,
118
101
  })
119
- const deployHookAdded = await addDeployHook(updatedSite.deploy_hook)
102
+ const deployHookAdded = await addDeployHook({ deployHook: updatedSite.deploy_hook })
120
103
  if (!deployHookAdded) {
121
104
  exit()
122
105
  }
@@ -0,0 +1,23 @@
1
+ // @ts-check
2
+ import { listFrameworks } from '@netlify/framework-info'
3
+
4
+ export const getFrameworkInfo = async ({ baseDirectory, nodeVersion }) => {
5
+ const frameworks = await listFrameworks({ projectDir: baseDirectory, nodeVersion })
6
+ // several frameworks can be detected - first one has highest priority
7
+ if (frameworks.length !== 0) {
8
+ const [
9
+ {
10
+ build: { commands, directory },
11
+ name,
12
+ plugins,
13
+ },
14
+ ] = frameworks
15
+ return {
16
+ frameworkName: name,
17
+ frameworkBuildCommand: commands[0],
18
+ frameworkBuildDir: directory,
19
+ frameworkPlugins: plugins,
20
+ }
21
+ }
22
+ return {}
23
+ }
@@ -1,72 +1,64 @@
1
1
  // @ts-check
2
2
  import { writeFile } from 'fs/promises'
3
3
  import path from 'path'
4
+ import process from 'process'
4
5
 
5
6
  import cleanDeep from 'clean-deep'
6
7
  import inquirer from 'inquirer'
7
8
 
8
9
  import { fileExistsAsync } from '../../lib/fs.mjs'
9
10
  import { normalizeBackslash } from '../../lib/path.mjs'
10
- import { detectBuildSettings } from '../build-info.mjs'
11
11
  import { chalk, error as failAndExit, log, warn } from '../command-helpers.mjs'
12
12
 
13
+ import { getFrameworkInfo } from './frameworks.mjs'
14
+ import { detectNodeVersion } from './node-version.mjs'
13
15
  import { getRecommendPlugins, getUIPlugins } from './plugins.mjs'
14
16
 
15
- // these plugins represent runtimes that are
16
- // expected to be "automatically" installed. Even though
17
- // they can be installed on package/toml, we always
18
- // want them installed in the site settings. When installed
19
- // there our build will automatically install the latest without
20
- // user management of the versioning.
21
- const pluginsToAlwaysInstall = new Set(['@netlify/plugin-nextjs'])
22
-
23
- /**
24
- * Retrieve a list of plugins to auto install
25
- * @param {string[]=} pluginsInstalled
26
- * @param {string[]=} pluginsRecommended
27
- * @returns
28
- */
29
- export const getPluginsToAutoInstall = (pluginsInstalled = [], pluginsRecommended = []) =>
30
- pluginsRecommended.reduce(
31
- (acc, plugin) =>
32
- pluginsInstalled.includes(plugin) && !pluginsToAlwaysInstall.has(plugin) ? acc : [...acc, plugin],
33
-
34
- /** @type {string[]} */ ([]),
35
- )
17
+ const normalizeDir = ({ baseDirectory, defaultValue, dir }) => {
18
+ if (dir === undefined) {
19
+ return defaultValue
20
+ }
21
+
22
+ const relativeDir = path.relative(baseDirectory, dir)
23
+ return relativeDir || defaultValue
24
+ }
25
+
26
+ const getDefaultBase = ({ baseDirectory, repositoryRoot }) => {
27
+ if (baseDirectory !== repositoryRoot && baseDirectory.startsWith(repositoryRoot)) {
28
+ return path.relative(repositoryRoot, baseDirectory)
29
+ }
30
+ }
36
31
 
37
- /**
38
- *
39
- * @param {Partial<import('@netlify/build-info').Settings>} settings
40
- * @param {*} config
41
- * @param {import('../../commands/base-command.mjs').default} command
42
- */
43
- const normalizeSettings = (settings, config, command) => {
44
- const plugins = getPluginsToAutoInstall(settings.plugins_from_config_file, settings.plugins_recommended)
45
- const recommendedPlugins = getRecommendPlugins(plugins, config)
32
+ const getDefaultSettings = ({
33
+ baseDirectory,
34
+ config,
35
+ frameworkBuildCommand,
36
+ frameworkBuildDir,
37
+ frameworkPlugins,
38
+ repositoryRoot,
39
+ }) => {
40
+ const recommendedPlugins = getRecommendPlugins(frameworkPlugins, config)
41
+ const {
42
+ command: defaultBuildCmd = frameworkBuildCommand,
43
+ functions: defaultFunctionsDir,
44
+ publish: defaultBuildDir = frameworkBuildDir,
45
+ } = config.build
46
46
 
47
47
  return {
48
- defaultBaseDir: settings.baseDirectory ?? command.project.relativeBaseDirectory ?? '',
49
- defaultBuildCmd: config.build.command || settings.buildCommand,
50
- defaultBuildDir: settings.dist,
51
- defaultFunctionsDir: config.build.functions || 'netlify/functions',
48
+ defaultBaseDir: getDefaultBase({ repositoryRoot, baseDirectory }),
49
+ defaultBuildCmd,
50
+ defaultBuildDir: normalizeDir({ baseDirectory, dir: defaultBuildDir, defaultValue: '.' }),
51
+ defaultFunctionsDir: normalizeDir({ baseDirectory, dir: defaultFunctionsDir, defaultValue: 'netlify/functions' }),
52
52
  recommendedPlugins,
53
53
  }
54
54
  }
55
55
 
56
- /**
57
- *
58
- * @param {object} param0
59
- * @param {string} param0.defaultBaseDir
60
- * @param {string} param0.defaultBuildCmd
61
- * @param {string=} param0.defaultBuildDir
62
- * @returns
63
- */
64
56
  const getPromptInputs = ({ defaultBaseDir, defaultBuildCmd, defaultBuildDir }) => {
65
57
  const inputs = [
66
58
  defaultBaseDir !== undefined && {
67
59
  type: 'input',
68
60
  name: 'baseDir',
69
- message: 'Base directory `(blank for current dir):',
61
+ message: 'Base directory (e.g. projects/frontend):',
70
62
  default: defaultBaseDir,
71
63
  },
72
64
  {
@@ -87,22 +79,34 @@ const getPromptInputs = ({ defaultBaseDir, defaultBuildCmd, defaultBuildDir }) =
87
79
  return inputs.filter(Boolean)
88
80
  }
89
81
 
90
- /**
91
- * @param {object} param0
92
- * @param {*} param0.config
93
- * @param {import('../../commands/base-command.mjs').default} param0.command
94
- */
95
- export const getBuildSettings = async ({ command, config }) => {
96
- const settings = await detectBuildSettings(command)
97
- // TODO: add prompt for asking to choose the build command
98
- /** @type {Partial<import('@netlify/build-info').Settings>} */
99
- // eslint-disable-next-line unicorn/explicit-length-check
100
- const setting = settings.length > 0 ? settings[0] : {}
82
+ // `repositoryRoot === siteRoot` means the base directory wasn't detected by @netlify/config, so we use cwd()
83
+ const getBaseDirectory = ({ repositoryRoot, siteRoot }) =>
84
+ path.normalize(repositoryRoot) === path.normalize(siteRoot) ? process.cwd() : siteRoot
85
+
86
+ export const getBuildSettings = async ({ config, env, repositoryRoot, siteRoot }) => {
87
+ const baseDirectory = getBaseDirectory({ repositoryRoot, siteRoot })
88
+ const nodeVersion = await detectNodeVersion({ baseDirectory, env })
89
+ const {
90
+ frameworkBuildCommand,
91
+ frameworkBuildDir,
92
+ frameworkName,
93
+ frameworkPlugins = [],
94
+ } = await getFrameworkInfo({
95
+ baseDirectory,
96
+ nodeVersion,
97
+ })
101
98
  const { defaultBaseDir, defaultBuildCmd, defaultBuildDir, defaultFunctionsDir, recommendedPlugins } =
102
- await normalizeSettings(setting, config, command)
103
-
104
- if (recommendedPlugins.length !== 0 && setting.framework?.name) {
105
- log(`Configuring ${formatTitle(setting.framework?.name)} runtime...`)
99
+ await getDefaultSettings({
100
+ repositoryRoot,
101
+ config,
102
+ baseDirectory,
103
+ frameworkBuildCommand,
104
+ frameworkBuildDir,
105
+ frameworkPlugins,
106
+ })
107
+
108
+ if (recommendedPlugins.length !== 0) {
109
+ log(`Configuring ${formatTitle(frameworkName)} runtime...`)
106
110
  log()
107
111
  }
108
112
 
@@ -195,9 +199,6 @@ export const formatErrorMessage = ({ error, message }) => {
195
199
  return `${message} with error: ${chalk.red(errorMessage)}`
196
200
  }
197
201
 
198
- /**
199
- * @param {string} title
200
- */
201
202
  const formatTitle = (title) => chalk.cyan(title)
202
203
 
203
204
  export const createDeployKey = async ({ api }) => {
@@ -36,21 +36,19 @@ export const generateInspectSettings = (edgeInspect, edgeInspectBrk) => {
36
36
  /**
37
37
  *
38
38
  * @param {object} params
39
- * @param {string=} params.accountId
40
39
  * @param {*} params.addonsUrls
41
- * @param {import('../commands/types.js').NetlifyOptions["config"]} params.config
40
+ * @param {import('../commands/base-command.mjs').NetlifyOptions["config"]} params.config
42
41
  * @param {string} [params.configPath] An override for the Netlify config path
43
42
  * @param {boolean} params.debug
44
- * @param {import('../commands/types.js').NetlifyOptions["cachedConfig"]['env']} params.env
43
+ * @param {import('../commands/base-command.mjs').NetlifyOptions["cachedConfig"]['env']} params.env
45
44
  * @param {InspectSettings} params.inspectSettings
46
45
  * @param {() => Promise<object>} params.getUpdatedConfig
47
46
  * @param {string} params.geolocationMode
48
47
  * @param {string} params.geoCountry
49
48
  * @param {*} params.settings
50
49
  * @param {boolean} params.offline
51
- * @param {object} params.site
50
+ * @param {*} params.site
52
51
  * @param {*} params.siteInfo
53
- * @param {string} params.projectDir
54
52
  * @param {import('./state-config.mjs').default} params.state
55
53
  * @returns
56
54
  */
@@ -66,7 +64,6 @@ export const startProxyServer = async ({
66
64
  getUpdatedConfig,
67
65
  inspectSettings,
68
66
  offline,
69
- projectDir,
70
67
  settings,
71
68
  site,
72
69
  siteInfo,
@@ -83,7 +80,7 @@ export const startProxyServer = async ({
83
80
  getUpdatedConfig,
84
81
  inspectSettings,
85
82
  offline,
86
- projectDir,
83
+ projectDir: site.root,
87
84
  settings,
88
85
  state,
89
86
  siteInfo,
@@ -375,6 +375,7 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,
375
375
  proxy.before('web', 'stream', (req) => {
376
376
  // See https://github.com/http-party/node-http-proxy/issues/1219#issuecomment-511110375
377
377
  if (req.headers.expect) {
378
+ // eslint-disable-next-line no-underscore-dangle
378
379
  req.__expectHeader = req.headers.expect
379
380
  delete req.headers.expect
380
381
  }
@@ -401,7 +402,9 @@ const initializeProxy = async function ({ configPath, distDir, env, host, port,
401
402
  handleProxyRequest(req, proxyReq)
402
403
  }
403
404
 
405
+ // eslint-disable-next-line no-underscore-dangle
404
406
  if (req.__expectHeader) {
407
+ // eslint-disable-next-line no-underscore-dangle
405
408
  proxyReq.setHeader('Expect', req.__expectHeader)
406
409
  }
407
410
  if (req.originalBody) {
@@ -596,10 +599,6 @@ const onRequest = async (
596
599
  proxy.web(req, res, options)
597
600
  }
598
601
 
599
- /**
600
- * @param {import('./types.js').ServerSettings} settings
601
- * @returns
602
- */
603
602
  export const getProxyUrl = function (settings) {
604
603
  const scheme = settings.https ? 'https' : 'http'
605
604
  return `${scheme}://localhost:${settings.port}`
@@ -7,7 +7,6 @@ import fetch from 'node-fetch'
7
7
  const GITHUB = 'GitHub'
8
8
 
9
9
  /**
10
- * @param {string} _url
11
10
  * Takes a url like https://github.com/netlify-labs/all-the-functions/tree/master/functions/9-using-middleware
12
11
  * and returns https://api.github.com/repos/netlify-labs/all-the-functions/contents/functions/9-using-middleware
13
12
  */
@@ -37,9 +36,6 @@ const getRepoURLContents = async function (repoHost, ownerAndRepo, contentsPath)
37
36
  throw new Error('unsupported host ', repoHost)
38
37
  }
39
38
 
40
- /**
41
- * @param {string} _url
42
- */
43
39
  export const validateRepoURL = function (_url) {
44
40
  // TODO: use `url.URL()` instead
45
41
  // eslint-disable-next-line n/no-deprecated-api
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
  import { promises as fs } from 'fs'
3
- import path, { join } from 'path'
3
+ import path from 'path'
4
+ import process from 'process'
4
5
 
5
6
  import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from '../lib/edge-functions/consts.mjs'
6
7
  import { getPathInProject } from '../lib/settings.mjs'
@@ -11,14 +12,10 @@ import { INTERNAL_FUNCTIONS_FOLDER } from './functions/index.mjs'
11
12
 
12
13
  const netlifyBuildPromise = import('@netlify/build')
13
14
 
14
- /**
15
- * Copies `netlify.toml`, if one is defined, into the `.netlify` internal
16
- * directory and returns the path to its new location.
17
- * @param {string} configPath
18
- * @param {string} destinationFolder The folder where it should be copied to either the root of the repo or a package inside a monorepo
19
- */
20
- const copyConfig = async (configPath, destinationFolder) => {
21
- const newConfigPath = path.resolve(destinationFolder, getPathInProject(['netlify.toml']))
15
+ // Copies `netlify.toml`, if one is defined, into the `.netlify` internal
16
+ // directory and returns the path to its new location.
17
+ const copyConfig = async ({ configPath, siteRoot }) => {
18
+ const newConfigPath = path.resolve(siteRoot, getPathInProject(['netlify.toml']))
22
19
 
23
20
  try {
24
21
  await fs.copyFile(configPath, newConfigPath)
@@ -29,9 +26,6 @@ const copyConfig = async (configPath, destinationFolder) => {
29
26
  return newConfigPath
30
27
  }
31
28
 
32
- /**
33
- * @param {string} basePath
34
- */
35
29
  const cleanInternalDirectory = async (basePath) => {
36
30
  const ops = [INTERNAL_FUNCTIONS_FOLDER, INTERNAL_EDGE_FUNCTIONS_FOLDER, 'netlify.toml'].map((name) => {
37
31
  const fullPath = path.resolve(basePath, getPathInProject([name]))
@@ -42,52 +36,38 @@ const cleanInternalDirectory = async (basePath) => {
42
36
  await Promise.all(ops)
43
37
  }
44
38
 
45
- /**
46
- * @param {object} params
47
- * @param {import('../commands/base-command.mjs').default} params.command
48
- * @param {import('../commands/base-command.mjs').default} params.command
49
- * @param {*} params.options The flags of the command
50
- * @param {import('./types.js').ServerSettings} params.settings
51
- * @param {NodeJS.ProcessEnv} [params.env]
52
- * @param {'build' | 'dev'} [params.timeline]
53
- * @returns
54
- */
55
- export const runNetlifyBuild = async ({ command, env = {}, options, settings, timeline = 'build' }) => {
56
- const { cachedConfig, site } = command.netlify
57
-
39
+ const getBuildOptions = ({
40
+ cachedConfig,
41
+ options: { configPath, context, cwd = process.cwd(), debug, dry, offline, quiet, saveConfig },
42
+ }) => ({
43
+ cachedConfig,
44
+ configPath,
45
+ siteId: cachedConfig.siteInfo.id,
46
+ token: cachedConfig.token,
47
+ dry,
48
+ debug,
49
+ context,
50
+ mode: 'cli',
51
+ telemetry: false,
52
+ buffer: false,
53
+ offline,
54
+ cwd,
55
+ quiet,
56
+ saveConfig,
57
+ })
58
+
59
+ const runNetlifyBuild = async ({ cachedConfig, env, options, settings, site, timeline = 'build' }) => {
58
60
  const { default: buildSite, startDev } = await netlifyBuildPromise
59
-
60
- const sharedOptions = {
61
+ const sharedOptions = getBuildOptions({
61
62
  cachedConfig,
62
- configPath: cachedConfig.configPath,
63
- siteId: cachedConfig.siteInfo.id,
64
- token: cachedConfig.token,
65
- dry: options.dry,
66
- debug: options.debug,
67
- context: options.context,
68
- mode: 'cli',
69
- telemetry: false,
70
- buffer: false,
71
- offline: options.offline,
72
- cwd: cachedConfig.buildDir,
73
- quiet: options.quiet,
74
- saveConfig: options.saveConfig,
75
- }
76
-
63
+ options,
64
+ })
77
65
  const devCommand = async (settingsOverrides = {}) => {
78
- let cwd = command.workingDir
79
-
80
- if (command.project.workspace?.packages.length) {
81
- console.log('packages', settings.baseDirectory)
82
- cwd = join(command.project.jsWorkspaceRoot, settings.baseDirectory || '')
83
- }
84
-
85
66
  const { ipVersion } = await startFrameworkServer({
86
67
  settings: {
87
68
  ...settings,
88
69
  ...settingsOverrides,
89
70
  },
90
- cwd,
91
71
  })
92
72
 
93
73
  settings.frameworkHost = ipVersion === 6 ? '::1' : '127.0.0.1'
@@ -100,7 +80,7 @@ export const runNetlifyBuild = async ({ command, env = {}, options, settings, ti
100
80
 
101
81
  // Copy `netlify.toml` into the internal directory. This will be the new
102
82
  // location of the config file for the duration of the command.
103
- const tempConfigPath = await copyConfig(cachedConfig.configPath, command.workingDir)
83
+ const tempConfigPath = await copyConfig({ configPath: cachedConfig.configPath, siteRoot: site.root })
104
84
  const buildSiteOptions = {
105
85
  ...sharedOptions,
106
86
  outputConfigPath: tempConfigPath,
@@ -138,19 +118,13 @@ export const runNetlifyBuild = async ({ command, env = {}, options, settings, ti
138
118
  // Run Netlify Build using the `startDev` entry point.
139
119
  const { error: startDevError, success } = await startDev(devCommand, startDevOptions)
140
120
 
141
- if (!success && startDevError) {
121
+ if (!success) {
142
122
  error(`Could not start local development server\n\n${startDevError.message}\n\n${startDevError.stack}`)
143
123
  }
144
124
 
145
125
  return {}
146
126
  }
147
127
 
148
- /**
149
- * @param {Omit<Parameters<typeof runNetlifyBuild>[0], 'timeline'>} options
150
- */
151
128
  export const runDevTimeline = (options) => runNetlifyBuild({ ...options, timeline: 'dev' })
152
129
 
153
- /**
154
- * @param {Omit<Parameters<typeof runNetlifyBuild>[0], 'timeline'>} options
155
- */
156
130
  export const runBuildTimeline = (options) => runNetlifyBuild({ ...options, timeline: 'build' })
@@ -40,26 +40,17 @@ const cleanupBeforeExit = async ({ exitCode }) => {
40
40
  /**
41
41
  * Run a command and pipe stdout, stderr and stdin
42
42
  * @param {string} command
43
- * @param {object} options
44
- * @param {import('ora').Ora|null} [options.spinner]
45
- * @param {NodeJS.ProcessEnv} [options.env]
46
- * @param {string} [options.cwd]
43
+ * @param {NodeJS.ProcessEnv} env
47
44
  * @returns {execa.ExecaChildProcess<string>}
48
45
  */
49
- export const runCommand = (command, options = {}) => {
50
- const { cwd, env = {}, spinner = null } = options
46
+ export const runCommand = (command, env = {}, spinner = null) => {
51
47
  const commandProcess = execa.command(command, {
52
48
  preferLocal: true,
53
49
  // we use reject=false to avoid rejecting synchronously when the command doesn't exist
54
50
  reject: false,
55
- env: {
56
- // we want always colorful terminal outputs
57
- FORCE_COLOR: 'true',
58
- ...env,
59
- },
51
+ env,
60
52
  // windowsHide needs to be false for child process to terminate properly on Windows
61
53
  windowsHide: false,
62
- cwd,
63
54
  })
64
55
 
65
56
  // This ensures that an active spinner stays at the bottom of the commandline
@@ -91,16 +82,15 @@ export const runCommand = (command, options = {}) => {
91
82
  const [commandWithoutArgs] = command.split(' ')
92
83
  if (result.failed && isNonExistingCommandError({ command: commandWithoutArgs, error: result })) {
93
84
  log(
94
- `\n\n${NETLIFYDEVERR} Failed running command: ${command}. Please verify ${chalk.magenta(
95
- `'${commandWithoutArgs}'`,
96
- )} exists`,
85
+ NETLIFYDEVERR,
86
+ `Failed running command: ${command}. Please verify ${chalk.magenta(`'${commandWithoutArgs}'`)} exists`,
97
87
  )
98
88
  } else {
99
89
  const errorMessage = result.failed
100
90
  ? `${NETLIFYDEVERR} ${result.shortMessage}`
101
91
  : `${NETLIFYDEVWARN} "${command}" exited with code ${result.exitCode}`
102
92
 
103
- log(`\n\n${errorMessage}. Shutting down Netlify Dev server`)
93
+ log(`${errorMessage}. Shutting down Netlify Dev server`)
104
94
  }
105
95
 
106
96
  return await cleanupBeforeExit({ exitCode: 1 })
@@ -110,13 +100,6 @@ export const runCommand = (command, options = {}) => {
110
100
  return commandProcess
111
101
  }
112
102
 
113
- /**
114
- *
115
- * @param {object} config
116
- * @param {string} config.command
117
- * @param {*} config.error
118
- * @returns
119
- */
120
103
  const isNonExistingCommandError = ({ command, error: commandError }) => {
121
104
  // `ENOENT` is only returned for non Windows systems
122
105
  // See https://github.com/sindresorhus/execa/pull/447
@@ -125,7 +108,7 @@ const isNonExistingCommandError = ({ command, error: commandError }) => {
125
108
  }
126
109
 
127
110
  // if the command is a package manager we let it report the error
128
- if (['yarn', 'npm', 'pnpm'].includes(command)) {
111
+ if (['yarn', 'npm'].includes(command)) {
129
112
  return false
130
113
  }
131
114
 
@@ -11,11 +11,7 @@ import { getPathInProject } from '../lib/settings.mjs'
11
11
  const STATE_PATH = getPathInProject(['state.json'])
12
12
  const permissionError = "You don't have access to this file."
13
13
 
14
- /**
15
- * Finds location of `.netlify/state.json`
16
- * @param {string} cwd
17
- * @returns {string}
18
- */
14
+ // Finds location of `.netlify/state.json`
19
15
  const findStatePath = (cwd) => {
20
16
  const statePath = findUpSync([STATE_PATH], { cwd })
21
17
 
@@ -6,10 +6,6 @@ import Fastify from 'fastify'
6
6
 
7
7
  import { log, NETLIFYDEVLOG } from './command-helpers.mjs'
8
8
 
9
- /**
10
- * @param {object} config
11
- * @param {import('./types.js').ServerSettings} config.settings
12
- */
13
9
  export const startStaticServer = async ({ settings }) => {
14
10
  const server = Fastify()
15
11
  const rootPath = path.resolve(settings.dist)