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