netlify-cli 15.9.1-rc.0 → 15.10.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 (36) hide show
  1. package/npm-shrinkwrap.json +236 -738
  2. package/package.json +6 -5
  3. package/src/commands/base-command.mjs +59 -195
  4. package/src/commands/deploy/deploy.mjs +9 -21
  5. package/src/commands/dev/dev.mjs +15 -21
  6. package/src/commands/functions/functions-create.mjs +3 -0
  7. package/src/commands/functions/functions-invoke.mjs +5 -8
  8. package/src/commands/init/init.mjs +1 -1
  9. package/src/commands/link/link.mjs +5 -5
  10. package/src/commands/serve/serve.mjs +5 -11
  11. package/src/commands/sites/sites-create-template.mjs +1 -1
  12. package/src/commands/sites/sites-create.mjs +1 -1
  13. package/src/functions-templates/typescript/hello-world/package-lock.json +6 -6
  14. package/src/lib/edge-functions/bootstrap.mjs +1 -1
  15. package/src/lib/edge-functions/headers.mjs +1 -0
  16. package/src/lib/edge-functions/internal.mjs +3 -5
  17. package/src/lib/edge-functions/proxy.mjs +4 -27
  18. package/src/lib/functions/runtimes/js/index.mjs +1 -1
  19. package/src/lib/functions/runtimes/js/worker.mjs +1 -1
  20. package/src/lib/spinner.mjs +1 -1
  21. package/src/utils/command-helpers.mjs +7 -16
  22. package/src/utils/detect-server-settings.mjs +198 -147
  23. package/src/utils/framework-server.mjs +2 -2
  24. package/src/utils/functions/functions.mjs +0 -7
  25. package/src/utils/get-repo-data.mjs +6 -5
  26. package/src/utils/init/config-github.mjs +2 -2
  27. package/src/utils/init/config-manual.mjs +7 -24
  28. package/src/utils/init/frameworks.mjs +23 -0
  29. package/src/utils/init/utils.mjs +63 -62
  30. package/src/utils/proxy-server.mjs +4 -7
  31. package/src/utils/proxy.mjs +0 -4
  32. package/src/utils/run-build.mjs +7 -58
  33. package/src/utils/shell.mjs +3 -14
  34. package/src/utils/state-config.mjs +1 -5
  35. package/src/utils/static-server.mjs +0 -4
  36. package/src/utils/build-info.mjs +0 -19
@@ -1,30 +1,26 @@
1
1
  // @ts-check
2
2
  import { readFile } from 'fs/promises'
3
3
  import { EOL } from 'os'
4
- import { dirname, relative, resolve } from 'path'
4
+ import path from 'path'
5
+ import process from 'process'
5
6
 
6
- import { getFramework, getSettings } from '@netlify/build-info'
7
+ import { Project } from '@netlify/build-info'
8
+ // eslint-disable-next-line import/extensions, n/no-missing-import
9
+ import { NodeFS } from '@netlify/build-info/node'
10
+ import { getFramework, listFrameworks } from '@netlify/framework-info'
7
11
  import fuzzy from 'fuzzy'
8
12
  import getPort from 'get-port'
9
13
  import inquirer from 'inquirer'
14
+ import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt'
10
15
 
11
- import { detectBuildSettings } from './build-info.mjs'
12
16
  import { NETLIFYDEVWARN, chalk, log } from './command-helpers.mjs'
13
17
  import { acquirePort } from './dev.mjs'
14
18
  import { getInternalFunctionsDir } from './functions/functions.mjs'
15
- import { getPluginsToAutoInstall } from './init/utils.mjs'
19
+ import { reportError } from './telemetry/report-error.mjs'
16
20
 
17
- /** @param {string} str */
18
21
  const formatProperty = (str) => chalk.magenta(`'${str}'`)
19
- /** @param {string} str */
20
22
  const formatValue = (str) => chalk.green(`'${str}'`)
21
23
 
22
- /**
23
- * @param {object} options
24
- * @param {string} options.keyFile
25
- * @param {string} options.certFile
26
- * @returns {Promise<{ key: string, cert: string, keyFilePath: string, certFilePath: string }>}
27
- */
28
24
  const readHttpsSettings = async (options) => {
29
25
  if (typeof options !== 'object' || !options.keyFile || !options.certFile) {
30
26
  throw new TypeError(
@@ -42,43 +38,43 @@ const readHttpsSettings = async (options) => {
42
38
  throw new TypeError(`Certificate file configuration should be a string`)
43
39
  }
44
40
 
45
- const [key, cert] = await Promise.allSettled([readFile(keyFile, 'utf-8'), readFile(certFile, 'utf-8')])
41
+ const [{ reason: keyError, value: key }, { reason: certError, value: cert }] = await Promise.allSettled([
42
+ readFile(keyFile, 'utf-8'),
43
+ readFile(certFile, 'utf-8'),
44
+ ])
46
45
 
47
- if (key.status === 'rejected') {
48
- throw new Error(`Error reading private key file: ${key.reason}`)
46
+ if (keyError) {
47
+ throw new Error(`Error reading private key file: ${keyError.message}`)
49
48
  }
50
- if (cert.status === 'rejected') {
51
- throw new Error(`Error reading certificate file: ${cert.reason}`)
49
+ if (certError) {
50
+ throw new Error(`Error reading certificate file: ${certError.message}`)
52
51
  }
53
52
 
54
- return { key: key.value, cert: cert.value, keyFilePath: resolve(keyFile), certFilePath: resolve(certFile) }
53
+ return { key, cert, keyFilePath: path.resolve(keyFile), certFilePath: path.resolve(certFile) }
55
54
  }
56
55
 
57
- /**
58
- * Validates a property inside the devConfig to be of a given type
59
- * @param {import('../commands/dev/types.js').DevConfig} devConfig The devConfig
60
- * @param {keyof import('../commands/dev/types.js').DevConfig} property The property to validate
61
- * @param {'string' | 'number'} type The type it should have
62
- */
63
- function validateProperty(devConfig, property, type) {
64
- // eslint-disable-next-line valid-typeof
65
- if (devConfig[property] && typeof devConfig[property] !== type) {
56
+ const validateStringProperty = ({ devConfig, property }) => {
57
+ if (devConfig[property] && typeof devConfig[property] !== 'string') {
66
58
  const formattedProperty = formatProperty(property)
67
59
  throw new TypeError(
68
- `Invalid ${formattedProperty} option provided in config. The value of ${formattedProperty} option must be of type ${type}`,
60
+ `Invalid ${formattedProperty} option provided in config. The value of ${formattedProperty} option must be a string`,
61
+ )
62
+ }
63
+ }
64
+
65
+ const validateNumberProperty = ({ devConfig, property }) => {
66
+ if (devConfig[property] && typeof devConfig[property] !== 'number') {
67
+ const formattedProperty = formatProperty(property)
68
+ throw new TypeError(
69
+ `Invalid ${formattedProperty} option provided in config. The value of ${formattedProperty} option must be an integer`,
69
70
  )
70
71
  }
71
72
  }
72
73
 
73
- /**
74
- *
75
- * @param {object} config
76
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
77
- */
78
74
  const validateFrameworkConfig = ({ devConfig }) => {
79
- validateProperty(devConfig, 'command', 'string')
80
- validateProperty(devConfig, 'port', 'number')
81
- validateProperty(devConfig, 'targetPort', 'number')
75
+ validateStringProperty({ devConfig, property: 'command' })
76
+ validateNumberProperty({ devConfig, property: 'port' })
77
+ validateNumberProperty({ devConfig, property: 'targetPort' })
82
78
 
83
79
  if (devConfig.targetPort && devConfig.targetPort === devConfig.port) {
84
80
  throw new Error(
@@ -89,11 +85,6 @@ const validateFrameworkConfig = ({ devConfig }) => {
89
85
  }
90
86
  }
91
87
 
92
- /**
93
- * @param {object} config
94
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
95
- * @param {number=} config.detectedPort
96
- */
97
88
  const validateConfiguredPort = ({ detectedPort, devConfig }) => {
98
89
  if (devConfig.port && devConfig.port === detectedPort) {
99
90
  const formattedPort = formatProperty('port')
@@ -106,22 +97,13 @@ const validateConfiguredPort = ({ detectedPort, devConfig }) => {
106
97
  const DEFAULT_PORT = 8888
107
98
  const DEFAULT_STATIC_PORT = 3999
108
99
 
109
- /**
110
- * Logs a message that it was unable to determine the dist directory and falls back to the workingDir
111
- * @param {string} workingDir
112
- */
113
- const getDefaultDist = (workingDir) => {
100
+ const getDefaultDist = () => {
114
101
  log(`${NETLIFYDEVWARN} Unable to determine public folder to serve files from. Using current working directory`)
115
102
  log(`${NETLIFYDEVWARN} Setup a netlify.toml file with a [dev] section to specify your dev server settings.`)
116
103
  log(`${NETLIFYDEVWARN} See docs at: https://cli.netlify.com/netlify-dev#project-detection`)
117
- return workingDir
104
+ return process.cwd()
118
105
  }
119
106
 
120
- /**
121
- * @param {object} config
122
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
123
- * @returns {Promise<number>}
124
- */
125
107
  const getStaticServerPort = async ({ devConfig }) => {
126
108
  const port = await acquirePort({
127
109
  configuredPort: devConfig.staticServerPort,
@@ -134,16 +116,16 @@ const getStaticServerPort = async ({ devConfig }) => {
134
116
 
135
117
  /**
136
118
  *
137
- * @param {object} config
138
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
139
- * @param {import('commander').OptionValues} config.flags
140
- * @param {string} config.workingDir
141
- * @returns {Promise<Omit<import('./types.js').BaseServerSettings, 'command'> & {command?: string}>}
119
+ * @param {object} param0
120
+ * @param {import('../commands/dev/types.js').DevConfig} param0.devConfig
121
+ * @param {import('commander').OptionValues} param0.options
122
+ * @param {string} param0.projectDir
123
+ * @returns {Promise<import('./types.js').BaseServerSettings>}
142
124
  */
143
- const handleStaticServer = async ({ devConfig, flags, workingDir }) => {
144
- validateProperty(devConfig, 'staticServerPort', 'number')
125
+ const handleStaticServer = async ({ devConfig, options, projectDir }) => {
126
+ validateNumberProperty({ devConfig, property: 'staticServerPort' })
145
127
 
146
- if (flags.dir) {
128
+ if (options.dir) {
147
129
  log(`${NETLIFYDEVWARN} Using simple static server because ${formatProperty('--dir')} flag was specified`)
148
130
  } else if (devConfig.framework === '#static') {
149
131
  log(
@@ -161,8 +143,8 @@ const handleStaticServer = async ({ devConfig, flags, workingDir }) => {
161
143
  )
162
144
  }
163
145
 
164
- const dist = flags.dir || devConfig.publish || getDefaultDist(workingDir)
165
- log(`${NETLIFYDEVWARN} Running static server from "${relative(dirname(workingDir), dist)}"`)
146
+ const dist = options.dir || devConfig.publish || getDefaultDist()
147
+ log(`${NETLIFYDEVWARN} Running static server from "${path.relative(path.dirname(projectDir), dist)}"`)
166
148
 
167
149
  const frameworkPort = await getStaticServerPort({ devConfig })
168
150
  return {
@@ -175,38 +157,118 @@ const handleStaticServer = async ({ devConfig, flags, workingDir }) => {
175
157
 
176
158
  /**
177
159
  * Retrieves the settings from a framework
178
- * @param {import('@netlify/build-info').Settings} settings
160
+ * @param {import('./types.js').FrameworkInfo} framework
179
161
  * @returns {import('./types.js').BaseServerSettings}
180
162
  */
181
- const getSettingsFromDetectedSettings = (settings) => ({
182
- command: settings.devCommand,
183
- frameworkPort: settings.frameworkPort,
184
- dist: settings.dist,
185
- framework: settings.framework.name,
186
- env: settings.env,
187
- pollingStrategies: settings.pollingStrategies,
188
- plugins: getPluginsToAutoInstall(settings.plugins_from_config_file, settings.plugins_recommended),
189
- })
163
+ const getSettingsFromFramework = (framework) => {
164
+ const {
165
+ build: { directory: dist },
166
+ dev: {
167
+ commands: [command],
168
+ pollingStrategies = [],
169
+ port: frameworkPort,
170
+ },
171
+ env = {},
172
+ name: frameworkName,
173
+ plugins,
174
+ staticAssetsDirectory: staticDir,
175
+ } = framework
176
+
177
+ return {
178
+ command,
179
+ frameworkPort,
180
+ dist: staticDir || dist,
181
+ framework: frameworkName,
182
+ env,
183
+ pollingStrategies: pollingStrategies.map(({ name }) => name),
184
+ plugins,
185
+ }
186
+ }
187
+
188
+ const hasDevCommand = (framework) => Array.isArray(framework.dev.commands) && framework.dev.commands.length !== 0
190
189
 
191
190
  /**
192
- * Uses @netlify/build-info to detect the dev settings and port based on the framework
193
- * and the build system that is used.
194
- * @param {import('../commands/base-command.mjs').default} command
191
+ * The new build setting detection with build systems and frameworks combined
192
+ * @param {string} projectDir
195
193
  */
196
- const detectFrameworkSettings = async (command) => {
197
- const settings = await detectBuildSettings(command)
198
- if (settings.length === 1) {
199
- return getSettingsFromDetectedSettings(settings[0])
194
+ const detectSettings = async (projectDir) => {
195
+ const fs = new NodeFS()
196
+ const project = new Project(fs, projectDir)
197
+
198
+ return await project.getBuildSettings()
199
+ }
200
+
201
+ /**
202
+ *
203
+ * @param {import('./types.js').BaseServerSettings | undefined} frameworkSettings
204
+ * @param {import('@netlify/build-info').Settings[]} newSettings
205
+ * @param {Record<string, Record<string, any>>} [metadata]
206
+ */
207
+ const detectChangesInNewSettings = (frameworkSettings, newSettings, metadata) => {
208
+ /** @type {string[]} */
209
+ const message = ['']
210
+ const [setting] = newSettings
211
+
212
+ if (frameworkSettings?.framework !== setting?.framework.name) {
213
+ message.push(
214
+ `- Framework does not match:`,
215
+ ` [old]: ${frameworkSettings?.framework}`,
216
+ ` [new]: ${setting?.framework.name}`,
217
+ '',
218
+ )
219
+ }
220
+
221
+ if (frameworkSettings?.command !== setting?.devCommand) {
222
+ message.push(
223
+ `- command does not match:`,
224
+ ` [old]: ${frameworkSettings?.command}`,
225
+ ` [new]: ${setting?.devCommand}`,
226
+ '',
227
+ )
228
+ }
229
+
230
+ if (frameworkSettings?.dist !== setting?.dist) {
231
+ message.push(`- dist does not match:`, ` [old]: ${frameworkSettings?.dist}`, ` [new]: ${setting?.dist}`, '')
232
+ }
233
+
234
+ if (frameworkSettings?.frameworkPort !== setting?.frameworkPort) {
235
+ message.push(
236
+ `- frameworkPort does not match:`,
237
+ ` [old]: ${frameworkSettings?.frameworkPort}`,
238
+ ` [new]: ${setting?.frameworkPort}`,
239
+ '',
240
+ )
241
+ }
242
+
243
+ if (message.length !== 0) {
244
+ reportError(
245
+ {
246
+ name: 'NewSettingsDetectionMismatch',
247
+ errorMessage: 'New Settings detection does not match old one',
248
+ message: message.join('\n'),
249
+ },
250
+ { severity: 'info', metadata },
251
+ )
252
+ }
253
+ }
254
+
255
+ const detectFrameworkSettings = async ({ projectDir }) => {
256
+ const projectFrameworks = await listFrameworks({ projectDir })
257
+ const frameworks = projectFrameworks.filter((framework) => hasDevCommand(framework))
258
+
259
+ if (frameworks.length === 1) {
260
+ return getSettingsFromFramework(frameworks[0])
200
261
  }
201
262
 
202
- if (settings.length > 1) {
263
+ if (frameworks.length > 1) {
203
264
  /** multiple matching detectors, make the user choose */
204
- const scriptInquirerOptions = formatSettingsArrForInquirer(settings)
205
- const { chosenSettings } = await inquirer.prompt({
206
- name: 'chosenSettings',
207
- message: `Multiple possible dev commands found`,
265
+ inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)
266
+ const scriptInquirerOptions = formatSettingsArrForInquirer(frameworks)
267
+ const { chosenFramework } = await inquirer.prompt({
268
+ name: 'chosenFramework',
269
+ message: `Multiple possible start commands found`,
208
270
  type: 'autocomplete',
209
- source(/** @type {string} */ _, input = '') {
271
+ source(_, input) {
210
272
  if (!input || input === '') {
211
273
  return scriptInquirerOptions
212
274
  }
@@ -214,31 +276,25 @@ const detectFrameworkSettings = async (command) => {
214
276
  return filterSettings(scriptInquirerOptions, input)
215
277
  },
216
278
  })
217
- // TODO: do better logging here with the framework command or port
218
279
  log(
219
280
  `Add ${formatProperty(
220
- `framework = "${chosenSettings.framework.id}"`,
281
+ `framework = "${chosenFramework.id}"`,
221
282
  )} to the [dev] section of your netlify.toml to avoid this selection prompt next time`,
222
283
  )
223
284
 
224
- return getSettingsFromDetectedSettings(chosenSettings)
285
+ return getSettingsFromFramework(chosenFramework)
225
286
  }
226
287
  }
227
288
 
228
- /**
229
- * @param {import('../commands/dev/types.js').DevConfig} devConfig
230
- */
231
- const hasCommandAndTargetPort = (devConfig) => devConfig.command && devConfig.targetPort
289
+ const hasCommandAndTargetPort = ({ devConfig }) => devConfig.command && devConfig.targetPort
232
290
 
233
291
  /**
234
292
  * Creates settings for the custom framework
235
- * @param {object} config
236
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
237
- * @param {string} config.workingDir
293
+ * @param {*} param0
238
294
  * @returns {import('./types.js').BaseServerSettings}
239
295
  */
240
- const handleCustomFramework = ({ devConfig, workingDir }) => {
241
- if (!hasCommandAndTargetPort(devConfig)) {
296
+ const handleCustomFramework = ({ devConfig }) => {
297
+ if (!hasCommandAndTargetPort({ devConfig })) {
242
298
  throw new Error(
243
299
  `${formatProperty('command')} and ${formatProperty('targetPort')} properties are required when ${formatProperty(
244
300
  'framework',
@@ -248,20 +304,13 @@ const handleCustomFramework = ({ devConfig, workingDir }) => {
248
304
  return {
249
305
  command: devConfig.command,
250
306
  frameworkPort: devConfig.targetPort,
251
- dist: devConfig.publish || getDefaultDist(workingDir),
307
+ dist: devConfig.publish || getDefaultDist(),
252
308
  framework: '#custom',
253
309
  pollingStrategies: devConfig.pollingStrategies || [],
254
310
  }
255
311
  }
256
312
 
257
- /**
258
- * Merges the framework settings with the devConfig
259
- * @param {object} config
260
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
261
- * @param {string} config.workingDir
262
- * @param {Partial<import('./types.js').BaseServerSettings>=} config.frameworkSettings
263
- */
264
- const mergeSettings = async ({ devConfig, frameworkSettings = {}, workingDir }) => {
313
+ const mergeSettings = async ({ devConfig, frameworkSettings = {} }) => {
265
314
  const {
266
315
  command: frameworkCommand,
267
316
  dist,
@@ -278,7 +327,7 @@ const mergeSettings = async ({ devConfig, frameworkSettings = {}, workingDir })
278
327
  return {
279
328
  command,
280
329
  frameworkPort: useStaticServer ? await getStaticServerPort({ devConfig }) : frameworkPort,
281
- dist: devConfig.publish || dist || getDefaultDist(workingDir),
330
+ dist: devConfig.publish || dist || getDefaultDist(),
282
331
  framework,
283
332
  env,
284
333
  pollingStrategies,
@@ -288,65 +337,68 @@ const mergeSettings = async ({ devConfig, frameworkSettings = {}, workingDir })
288
337
 
289
338
  /**
290
339
  * Handles a forced framework and retrieves the settings for it
291
- * @param {object} config
292
- * @param {import('../commands/dev/types.js').DevConfig} config.devConfig
293
- * @param {import('@netlify/build-info').Project} config.project
294
- * @param {string} config.workingDir
295
- * @param {string=} config.workspacePackage
340
+ * @param {*} param0
296
341
  * @returns {Promise<import('./types.js').BaseServerSettings>}
297
342
  */
298
- const handleForcedFramework = async ({ devConfig, project, workingDir, workspacePackage }) => {
343
+ const handleForcedFramework = async ({ devConfig, projectDir }) => {
299
344
  // this throws if `devConfig.framework` is not a supported framework
300
- const framework = await getFramework(devConfig.framework, project)
301
- const settings = await getSettings(framework, project, workspacePackage || '')
302
- const frameworkSettings = getSettingsFromDetectedSettings(settings)
303
- return mergeSettings({ devConfig, workingDir, frameworkSettings })
345
+ const frameworkSettings = getSettingsFromFramework(await getFramework(devConfig.framework, { projectDir }))
346
+ return mergeSettings({ devConfig, frameworkSettings })
304
347
  }
305
348
 
306
349
  /**
307
350
  * Get the server settings based on the flags and the devConfig
308
351
  * @param {import('../commands/dev/types.js').DevConfig} devConfig
309
- * @param {import('commander').OptionValues} flags
310
- * @param {import('../commands/base-command.mjs').default} command
352
+ * @param {import('commander').OptionValues} options
353
+ * @param {string} projectDir
354
+ * @param {Record<string, Record<string, any>>} [metadata]
311
355
  * @returns {Promise<import('./types.js').ServerSettings>}
312
356
  */
313
-
314
- const detectServerSettings = async (devConfig, flags, command) => {
315
- validateProperty(devConfig, 'framework', 'string')
357
+ const detectServerSettings = async (devConfig, options, projectDir, metadata) => {
358
+ validateStringProperty({ devConfig, property: 'framework' })
316
359
 
317
360
  /** @type {Partial<import('./types.js').BaseServerSettings>} */
318
361
  let settings = {}
319
362
 
320
- if (flags.dir || devConfig.framework === '#static') {
363
+ if (options.dir || devConfig.framework === '#static') {
321
364
  // serving files statically without a framework server
322
- settings = await handleStaticServer({ flags, devConfig, workingDir: command.workingDir })
365
+ settings = await handleStaticServer({ options, devConfig, projectDir })
323
366
  } else if (devConfig.framework === '#auto') {
324
367
  // this is the default CLI behavior
325
368
 
326
- const runDetection = !hasCommandAndTargetPort(devConfig)
327
- const frameworkSettings = runDetection ? await detectFrameworkSettings(command) : undefined
369
+ const runDetection = !hasCommandAndTargetPort({ devConfig })
370
+ const frameworkSettings = runDetection ? await detectFrameworkSettings({ projectDir }) : undefined
371
+ const newSettings = runDetection ? await detectSettings(projectDir) : undefined
372
+
373
+ // just report differences in the settings
374
+ detectChangesInNewSettings(frameworkSettings, newSettings || [], {
375
+ ...metadata,
376
+ settings: {
377
+ projectDir,
378
+ devConfig,
379
+ options,
380
+ old: frameworkSettings,
381
+ settings: newSettings,
382
+ },
383
+ })
384
+
328
385
  if (frameworkSettings === undefined && runDetection) {
329
386
  log(`${NETLIFYDEVWARN} No app server detected. Using simple static server`)
330
- settings = await handleStaticServer({ flags, devConfig, workingDir: command.workingDir })
387
+ settings = await handleStaticServer({ options, devConfig, projectDir })
331
388
  } else {
332
389
  validateFrameworkConfig({ devConfig })
333
- settings = await mergeSettings({ devConfig, frameworkSettings, workingDir: command.workingDir })
390
+ settings = await mergeSettings({ devConfig, frameworkSettings })
334
391
  }
335
392
 
336
- settings.plugins = frameworkSettings?.plugins
393
+ settings.plugins = frameworkSettings && frameworkSettings.plugins
337
394
  } else if (devConfig.framework === '#custom') {
338
395
  validateFrameworkConfig({ devConfig })
339
396
  // when the users wants to configure `command` and `targetPort`
340
- settings = handleCustomFramework({ devConfig, workingDir: command.workingDir })
397
+ settings = handleCustomFramework({ devConfig })
341
398
  } else if (devConfig.framework) {
342
399
  validateFrameworkConfig({ devConfig })
343
400
  // this is when the user explicitly configures a framework, e.g. `framework = "gatsby"`
344
- settings = await handleForcedFramework({
345
- devConfig,
346
- project: command.project,
347
- workingDir: command.workingDir,
348
- workspacePackage: command.workspacePackage,
349
- })
401
+ settings = await handleForcedFramework({ devConfig, projectDir })
350
402
  }
351
403
 
352
404
  validateConfiguredPort({ devConfig, detectedPort: settings.frameworkPort })
@@ -357,7 +409,7 @@ const detectServerSettings = async (devConfig, flags, command) => {
357
409
  errorMessage: `Could not acquire required ${formatProperty('port')}`,
358
410
  })
359
411
  const functionsDir = devConfig.functions || settings.functions
360
- const internalFunctionsDir = await getInternalFunctionsDir({ base: command.workingDir })
412
+ const internalFunctionsDir = await getInternalFunctionsDir({ base: projectDir })
361
413
  const shouldStartFunctionsServer = Boolean(functionsDir || internalFunctionsDir)
362
414
 
363
415
  return {
@@ -382,16 +434,15 @@ const filterSettings = function (scriptInquirerOptions, input) {
382
434
  return scriptInquirerOptions.filter((t) => filteredSettingNames.has(t.name))
383
435
  }
384
436
 
385
- /**
386
- * @param {import('@netlify/build-info').Settings[]} settings
387
- * @returns
388
- */
389
- const formatSettingsArrForInquirer = function (settings) {
390
- return settings.map((setting) => ({
391
- name: `[${chalk.yellow(setting.framework.name)}] '${setting.devCommand}'`,
392
- value: { ...setting, commands: [setting.devCommand] },
393
- short: `${setting.name}-${setting.devCommand}`,
394
- }))
437
+ const formatSettingsArrForInquirer = function (frameworks) {
438
+ const formattedArr = frameworks.map((framework) =>
439
+ framework.dev.commands.map((command) => ({
440
+ name: `[${chalk.yellow(framework.name)}] '${command}'`,
441
+ value: { ...framework, commands: [command] },
442
+ short: `${framework.name}-${command}`,
443
+ })),
444
+ )
445
+ return formattedArr.flat()
395
446
  }
396
447
 
397
448
  /**
@@ -18,7 +18,7 @@ const FRAMEWORK_PORT_TIMEOUT = 6e5
18
18
  /**
19
19
  * Start a static server if the `useStaticServer` is provided or a framework specific server
20
20
  * @param {object} config
21
- * @param {import('./types.js').ServerSettings} config.settings
21
+ * @param {Partial<import('./types').ServerSettings>} config.settings
22
22
  * @returns {Promise<StartReturnObject>}
23
23
  */
24
24
  export const startFrameworkServer = async function ({ settings }) {
@@ -46,7 +46,7 @@ export const startFrameworkServer = async function ({ settings }) {
46
46
  host: 'localhost',
47
47
  output: 'silent',
48
48
  timeout: FRAMEWORK_PORT_TIMEOUT,
49
- ...(settings.pollingStrategies?.includes('HTTP') && { protocol: 'http' }),
49
+ ...(settings.pollingStrategies.includes('HTTP') && { protocol: 'http' }),
50
50
  })
51
51
 
52
52
  if (!port.open) {
@@ -37,13 +37,6 @@ export const getFunctionsDistPath = async ({ base }) => {
37
37
  return isDirectory ? path : null
38
38
  }
39
39
 
40
- /**
41
- * Retrieves the internal functions directory and creates it if ensureExists is provided
42
- * @param {object} config
43
- * @param {string} config.base
44
- * @param {boolean=} config.ensureExists
45
- * @returns
46
- */
47
40
  export const getInternalFunctionsDir = async ({ base, ensureExists }) => {
48
41
  const path = resolve(base, getPathInProject([INTERNAL_FUNCTIONS_FOLDER]))
49
42
 
@@ -1,5 +1,6 @@
1
1
  // @ts-check
2
2
  import { dirname } from 'path'
3
+ import process from 'process'
3
4
  import util from 'util'
4
5
 
5
6
  import { findUp } from 'find-up'
@@ -13,14 +14,14 @@ import { log } from './command-helpers.mjs'
13
14
  *
14
15
  * @param {object} config
15
16
  * @param {string} [config.remoteName]
16
- * @param {string} config.workingDir
17
17
  * @returns
18
18
  */
19
- const getRepoData = async function ({ remoteName, workingDir }) {
19
+ const getRepoData = async function ({ remoteName } = {}) {
20
20
  try {
21
+ const cwd = process.cwd()
21
22
  const [gitConfig, gitDirectory] = await Promise.all([
22
- util.promisify(gitconfiglocal)(workingDir),
23
- findUp('.git', { cwd: workingDir, type: 'directory' }),
23
+ util.promisify(gitconfiglocal)(cwd),
24
+ findUp('.git', { cwd, type: 'directory' }),
24
25
  ])
25
26
 
26
27
  if (!gitDirectory || !gitConfig || !gitConfig.remote || Object.keys(gitConfig.remote).length === 0) {
@@ -29,7 +30,7 @@ const getRepoData = async function ({ remoteName, workingDir }) {
29
30
 
30
31
  const baseGitPath = dirname(gitDirectory)
31
32
 
32
- if (workingDir !== baseGitPath) {
33
+ if (cwd !== baseGitPath) {
33
34
  log(`Git directory located in ${baseGitPath}`)
34
35
  }
35
36
 
@@ -207,7 +207,7 @@ export const configGithub = async ({ command, repoName, repoOwner, siteId }) =>
207
207
  const { netlify } = command
208
208
  const {
209
209
  api,
210
- cachedConfig: { configPath },
210
+ cachedConfig: { configPath, env },
211
211
  config,
212
212
  globalConfig,
213
213
  repositoryRoot,
@@ -220,7 +220,7 @@ export const configGithub = async ({ command, repoName, repoOwner, siteId }) =>
220
220
  repositoryRoot,
221
221
  siteRoot,
222
222
  config,
223
- command,
223
+ env,
224
224
  })
225
225
  await saveNetlifyToml({ repositoryRoot, config, configPath, baseDir, buildCmd, buildDir, functionsDir })
226
226