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.
- package/bin/run.mjs +5 -6
- package/npm-shrinkwrap.json +3 -6
- package/package.json +2 -1
- package/src/commands/base-command.mjs +116 -295
- package/src/commands/build/build.mjs +1 -9
- package/src/commands/deploy/deploy.mjs +9 -23
- package/src/commands/dev/dev.mjs +17 -22
- package/src/commands/functions/functions-create.mjs +89 -118
- package/src/commands/functions/functions-invoke.mjs +7 -10
- package/src/commands/functions/functions-list.mjs +2 -2
- package/src/commands/init/init.mjs +1 -1
- package/src/commands/link/link.mjs +5 -5
- package/src/commands/serve/serve.mjs +6 -10
- package/src/commands/sites/sites-create-template.mjs +1 -1
- package/src/commands/sites/sites-create.mjs +1 -1
- package/src/lib/edge-functions/internal.mjs +3 -5
- package/src/lib/edge-functions/proxy.mjs +3 -27
- package/src/lib/functions/netlify-function.mjs +26 -1
- package/src/lib/functions/registry.mjs +14 -26
- package/src/lib/functions/runtimes/js/worker.mjs +1 -1
- package/src/lib/spinner.mjs +1 -1
- package/src/recipes/vscode/index.mjs +6 -24
- package/src/utils/command-helpers.mjs +7 -16
- package/src/utils/detect-server-settings.mjs +245 -133
- package/src/utils/framework-server.mjs +5 -6
- package/src/utils/functions/functions.mjs +5 -8
- package/src/utils/get-repo-data.mjs +6 -5
- package/src/utils/init/config-github.mjs +2 -2
- package/src/utils/init/config-manual.mjs +7 -24
- package/src/utils/init/frameworks.mjs +23 -0
- package/src/utils/init/utils.mjs +63 -62
- package/src/utils/proxy-server.mjs +4 -7
- package/src/utils/proxy.mjs +3 -4
- package/src/utils/read-repo-url.mjs +0 -4
- package/src/utils/run-build.mjs +32 -58
- package/src/utils/shell.mjs +7 -24
- package/src/utils/state-config.mjs +1 -5
- package/src/utils/static-server.mjs +0 -4
- package/src/utils/build-info.mjs +0 -100
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { readFile } from 'fs/promises'
|
|
3
3
|
import { EOL } from 'os'
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import process from 'process'
|
|
6
|
+
|
|
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'
|
|
11
|
+
import fuzzy from 'fuzzy'
|
|
7
12
|
import getPort from 'get-port'
|
|
13
|
+
import inquirer from 'inquirer'
|
|
14
|
+
import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt'
|
|
8
15
|
|
|
9
|
-
import { detectFrameworkSettings } from './build-info.mjs'
|
|
10
16
|
import { NETLIFYDEVWARN, chalk, log } from './command-helpers.mjs'
|
|
11
17
|
import { acquirePort } from './dev.mjs'
|
|
12
18
|
import { getInternalFunctionsDir } from './functions/functions.mjs'
|
|
13
|
-
import {
|
|
19
|
+
import { reportError } from './telemetry/report-error.mjs'
|
|
14
20
|
|
|
15
|
-
/** @param {string} str */
|
|
16
21
|
const formatProperty = (str) => chalk.magenta(`'${str}'`)
|
|
17
|
-
/** @param {string} str */
|
|
18
22
|
const formatValue = (str) => chalk.green(`'${str}'`)
|
|
19
23
|
|
|
20
|
-
/**
|
|
21
|
-
* @param {object} options
|
|
22
|
-
* @param {string} options.keyFile
|
|
23
|
-
* @param {string} options.certFile
|
|
24
|
-
* @returns {Promise<{ key: string, cert: string, keyFilePath: string, certFilePath: string }>}
|
|
25
|
-
*/
|
|
26
24
|
const readHttpsSettings = async (options) => {
|
|
27
25
|
if (typeof options !== 'object' || !options.keyFile || !options.certFile) {
|
|
28
26
|
throw new TypeError(
|
|
@@ -40,43 +38,43 @@ const readHttpsSettings = async (options) => {
|
|
|
40
38
|
throw new TypeError(`Certificate file configuration should be a string`)
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
const [key, cert] = await Promise.allSettled([
|
|
41
|
+
const [{ reason: keyError, value: key }, { reason: certError, value: cert }] = await Promise.allSettled([
|
|
42
|
+
readFile(keyFile, 'utf-8'),
|
|
43
|
+
readFile(certFile, 'utf-8'),
|
|
44
|
+
])
|
|
44
45
|
|
|
45
|
-
if (
|
|
46
|
-
throw new Error(`Error reading private key file: ${
|
|
46
|
+
if (keyError) {
|
|
47
|
+
throw new Error(`Error reading private key file: ${keyError.message}`)
|
|
47
48
|
}
|
|
48
|
-
if (
|
|
49
|
-
throw new Error(`Error reading certificate file: ${
|
|
49
|
+
if (certError) {
|
|
50
|
+
throw new Error(`Error reading certificate file: ${certError.message}`)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
return { key
|
|
53
|
+
return { key, cert, keyFilePath: path.resolve(keyFile), certFilePath: path.resolve(certFile) }
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* @param {import('../commands/dev/types.js').DevConfig} devConfig The devConfig
|
|
58
|
-
* @param {keyof import('../commands/dev/types.js').DevConfig} property The property to validate
|
|
59
|
-
* @param {'string' | 'number'} type The type it should have
|
|
60
|
-
*/
|
|
61
|
-
function validateProperty(devConfig, property, type) {
|
|
62
|
-
// eslint-disable-next-line valid-typeof
|
|
63
|
-
if (devConfig[property] && typeof devConfig[property] !== type) {
|
|
56
|
+
const validateStringProperty = ({ devConfig, property }) => {
|
|
57
|
+
if (devConfig[property] && typeof devConfig[property] !== 'string') {
|
|
64
58
|
const formattedProperty = formatProperty(property)
|
|
65
59
|
throw new TypeError(
|
|
66
|
-
`Invalid ${formattedProperty} option provided in config. The value of ${formattedProperty} option must be
|
|
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`,
|
|
67
70
|
)
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
73
|
-
* @param {object} config
|
|
74
|
-
* @param {import('../commands/dev/types.js').DevConfig} config.devConfig
|
|
75
|
-
*/
|
|
76
74
|
const validateFrameworkConfig = ({ devConfig }) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
validateStringProperty({ devConfig, property: 'command' })
|
|
76
|
+
validateNumberProperty({ devConfig, property: 'port' })
|
|
77
|
+
validateNumberProperty({ devConfig, property: 'targetPort' })
|
|
80
78
|
|
|
81
79
|
if (devConfig.targetPort && devConfig.targetPort === devConfig.port) {
|
|
82
80
|
throw new Error(
|
|
@@ -87,11 +85,6 @@ const validateFrameworkConfig = ({ devConfig }) => {
|
|
|
87
85
|
}
|
|
88
86
|
}
|
|
89
87
|
|
|
90
|
-
/**
|
|
91
|
-
* @param {object} config
|
|
92
|
-
* @param {import('../commands/dev/types.js').DevConfig} config.devConfig
|
|
93
|
-
* @param {number=} config.detectedPort
|
|
94
|
-
*/
|
|
95
88
|
const validateConfiguredPort = ({ detectedPort, devConfig }) => {
|
|
96
89
|
if (devConfig.port && devConfig.port === detectedPort) {
|
|
97
90
|
const formattedPort = formatProperty('port')
|
|
@@ -104,22 +97,13 @@ const validateConfiguredPort = ({ detectedPort, devConfig }) => {
|
|
|
104
97
|
const DEFAULT_PORT = 8888
|
|
105
98
|
const DEFAULT_STATIC_PORT = 3999
|
|
106
99
|
|
|
107
|
-
|
|
108
|
-
* Logs a message that it was unable to determine the dist directory and falls back to the workingDir
|
|
109
|
-
* @param {string} workingDir
|
|
110
|
-
*/
|
|
111
|
-
const getDefaultDist = (workingDir) => {
|
|
100
|
+
const getDefaultDist = () => {
|
|
112
101
|
log(`${NETLIFYDEVWARN} Unable to determine public folder to serve files from. Using current working directory`)
|
|
113
102
|
log(`${NETLIFYDEVWARN} Setup a netlify.toml file with a [dev] section to specify your dev server settings.`)
|
|
114
103
|
log(`${NETLIFYDEVWARN} See docs at: https://cli.netlify.com/netlify-dev#project-detection`)
|
|
115
|
-
return
|
|
104
|
+
return process.cwd()
|
|
116
105
|
}
|
|
117
106
|
|
|
118
|
-
/**
|
|
119
|
-
* @param {object} config
|
|
120
|
-
* @param {import('../commands/dev/types.js').DevConfig} config.devConfig
|
|
121
|
-
* @returns {Promise<number>}
|
|
122
|
-
*/
|
|
123
107
|
const getStaticServerPort = async ({ devConfig }) => {
|
|
124
108
|
const port = await acquirePort({
|
|
125
109
|
configuredPort: devConfig.staticServerPort,
|
|
@@ -132,16 +116,16 @@ const getStaticServerPort = async ({ devConfig }) => {
|
|
|
132
116
|
|
|
133
117
|
/**
|
|
134
118
|
*
|
|
135
|
-
* @param {object}
|
|
136
|
-
* @param {import('../commands/dev/types.js').DevConfig}
|
|
137
|
-
* @param {import('commander').OptionValues}
|
|
138
|
-
* @param {string}
|
|
139
|
-
* @returns {Promise<
|
|
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>}
|
|
140
124
|
*/
|
|
141
|
-
const handleStaticServer = async ({ devConfig,
|
|
142
|
-
|
|
125
|
+
const handleStaticServer = async ({ devConfig, options, projectDir }) => {
|
|
126
|
+
validateNumberProperty({ devConfig, property: 'staticServerPort' })
|
|
143
127
|
|
|
144
|
-
if (
|
|
128
|
+
if (options.dir) {
|
|
145
129
|
log(`${NETLIFYDEVWARN} Using simple static server because ${formatProperty('--dir')} flag was specified`)
|
|
146
130
|
} else if (devConfig.framework === '#static') {
|
|
147
131
|
log(
|
|
@@ -159,8 +143,8 @@ const handleStaticServer = async ({ devConfig, flags, workingDir }) => {
|
|
|
159
143
|
)
|
|
160
144
|
}
|
|
161
145
|
|
|
162
|
-
const dist =
|
|
163
|
-
log(`${NETLIFYDEVWARN} Running static server from "${relative(dirname(
|
|
146
|
+
const dist = options.dir || devConfig.publish || getDefaultDist()
|
|
147
|
+
log(`${NETLIFYDEVWARN} Running static server from "${path.relative(path.dirname(projectDir), dist)}"`)
|
|
164
148
|
|
|
165
149
|
const frameworkPort = await getStaticServerPort({ devConfig })
|
|
166
150
|
return {
|
|
@@ -173,39 +157,144 @@ const handleStaticServer = async ({ devConfig, flags, workingDir }) => {
|
|
|
173
157
|
|
|
174
158
|
/**
|
|
175
159
|
* Retrieves the settings from a framework
|
|
176
|
-
* @param {import('
|
|
177
|
-
* @returns {import('./types.js').BaseServerSettings
|
|
160
|
+
* @param {import('./types.js').FrameworkInfo} framework
|
|
161
|
+
* @returns {import('./types.js').BaseServerSettings}
|
|
178
162
|
*/
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
+
|
|
183
177
|
return {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
plugins: getPluginsToAutoInstall(settings.plugins_from_config_file, settings.plugins_recommended),
|
|
178
|
+
command,
|
|
179
|
+
frameworkPort,
|
|
180
|
+
dist: staticDir || dist,
|
|
181
|
+
framework: frameworkName,
|
|
182
|
+
env,
|
|
183
|
+
pollingStrategies: pollingStrategies.map(({ name }) => name),
|
|
184
|
+
plugins,
|
|
192
185
|
}
|
|
193
186
|
}
|
|
194
187
|
|
|
188
|
+
const hasDevCommand = (framework) => Array.isArray(framework.dev.commands) && framework.dev.commands.length !== 0
|
|
189
|
+
|
|
195
190
|
/**
|
|
196
|
-
*
|
|
191
|
+
* The new build setting detection with build systems and frameworks combined
|
|
192
|
+
* @param {string} projectDir
|
|
197
193
|
*/
|
|
198
|
-
const
|
|
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])
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (frameworks.length > 1) {
|
|
264
|
+
/** multiple matching detectors, make the user choose */
|
|
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`,
|
|
270
|
+
type: 'autocomplete',
|
|
271
|
+
source(_, input) {
|
|
272
|
+
if (!input || input === '') {
|
|
273
|
+
return scriptInquirerOptions
|
|
274
|
+
}
|
|
275
|
+
// only show filtered results
|
|
276
|
+
return filterSettings(scriptInquirerOptions, input)
|
|
277
|
+
},
|
|
278
|
+
})
|
|
279
|
+
log(
|
|
280
|
+
`Add ${formatProperty(
|
|
281
|
+
`framework = "${chosenFramework.id}"`,
|
|
282
|
+
)} to the [dev] section of your netlify.toml to avoid this selection prompt next time`,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return getSettingsFromFramework(chosenFramework)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const hasCommandAndTargetPort = ({ devConfig }) => devConfig.command && devConfig.targetPort
|
|
199
290
|
|
|
200
291
|
/**
|
|
201
292
|
* Creates settings for the custom framework
|
|
202
|
-
* @param {
|
|
203
|
-
* @param {import('../commands/dev/types.js').DevConfig} config.devConfig
|
|
204
|
-
* @param {string} config.workingDir
|
|
293
|
+
* @param {*} param0
|
|
205
294
|
* @returns {import('./types.js').BaseServerSettings}
|
|
206
295
|
*/
|
|
207
|
-
const handleCustomFramework = ({ devConfig
|
|
208
|
-
if (!hasCommandAndTargetPort(devConfig)) {
|
|
296
|
+
const handleCustomFramework = ({ devConfig }) => {
|
|
297
|
+
if (!hasCommandAndTargetPort({ devConfig })) {
|
|
209
298
|
throw new Error(
|
|
210
299
|
`${formatProperty('command')} and ${formatProperty('targetPort')} properties are required when ${formatProperty(
|
|
211
300
|
'framework',
|
|
@@ -215,100 +304,101 @@ const handleCustomFramework = ({ devConfig, workingDir }) => {
|
|
|
215
304
|
return {
|
|
216
305
|
command: devConfig.command,
|
|
217
306
|
frameworkPort: devConfig.targetPort,
|
|
218
|
-
dist: devConfig.publish || getDefaultDist(
|
|
307
|
+
dist: devConfig.publish || getDefaultDist(),
|
|
219
308
|
framework: '#custom',
|
|
220
309
|
pollingStrategies: devConfig.pollingStrategies || [],
|
|
221
310
|
}
|
|
222
311
|
}
|
|
223
312
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
313
|
+
const mergeSettings = async ({ devConfig, frameworkSettings = {} }) => {
|
|
314
|
+
const {
|
|
315
|
+
command: frameworkCommand,
|
|
316
|
+
dist,
|
|
317
|
+
env,
|
|
318
|
+
framework,
|
|
319
|
+
frameworkPort: frameworkDetectedPort,
|
|
320
|
+
pollingStrategies = [],
|
|
321
|
+
} = frameworkSettings
|
|
322
|
+
|
|
323
|
+
const command = devConfig.command || frameworkCommand
|
|
324
|
+
const frameworkPort = devConfig.targetPort || frameworkDetectedPort
|
|
234
325
|
// if the framework doesn't start a server, we use a static one
|
|
235
326
|
const useStaticServer = !(command && frameworkPort)
|
|
236
327
|
return {
|
|
237
|
-
baseDirectory: devConfig.base || frameworkSettings.baseDirectory,
|
|
238
328
|
command,
|
|
239
329
|
frameworkPort: useStaticServer ? await getStaticServerPort({ devConfig }) : frameworkPort,
|
|
240
|
-
dist: devConfig.publish ||
|
|
241
|
-
framework
|
|
242
|
-
env
|
|
243
|
-
pollingStrategies
|
|
330
|
+
dist: devConfig.publish || dist || getDefaultDist(),
|
|
331
|
+
framework,
|
|
332
|
+
env,
|
|
333
|
+
pollingStrategies,
|
|
244
334
|
useStaticServer,
|
|
245
335
|
}
|
|
246
336
|
}
|
|
247
337
|
|
|
248
338
|
/**
|
|
249
339
|
* Handles a forced framework and retrieves the settings for it
|
|
250
|
-
* @param {
|
|
251
|
-
* @param {import('../commands/dev/types.js').DevConfig} config.devConfig
|
|
252
|
-
* @param {import('@netlify/build-info').Project} config.project
|
|
253
|
-
* @param {string} config.workingDir
|
|
254
|
-
* @param {string=} config.workspacePackage
|
|
340
|
+
* @param {*} param0
|
|
255
341
|
* @returns {Promise<import('./types.js').BaseServerSettings>}
|
|
256
342
|
*/
|
|
257
|
-
const handleForcedFramework = async ({ devConfig,
|
|
343
|
+
const handleForcedFramework = async ({ devConfig, projectDir }) => {
|
|
258
344
|
// this throws if `devConfig.framework` is not a supported framework
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
const frameworkSettings = getSettingsFromDetectedSettings(settings)
|
|
262
|
-
return mergeSettings({ devConfig, workingDir, frameworkSettings })
|
|
345
|
+
const frameworkSettings = getSettingsFromFramework(await getFramework(devConfig.framework, { projectDir }))
|
|
346
|
+
return mergeSettings({ devConfig, frameworkSettings })
|
|
263
347
|
}
|
|
264
348
|
|
|
265
349
|
/**
|
|
266
350
|
* Get the server settings based on the flags and the devConfig
|
|
267
351
|
* @param {import('../commands/dev/types.js').DevConfig} devConfig
|
|
268
|
-
* @param {import('commander').OptionValues}
|
|
269
|
-
* @param {
|
|
352
|
+
* @param {import('commander').OptionValues} options
|
|
353
|
+
* @param {string} projectDir
|
|
354
|
+
* @param {Record<string, Record<string, any>>} [metadata]
|
|
270
355
|
* @returns {Promise<import('./types.js').ServerSettings>}
|
|
271
356
|
*/
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
validateProperty(devConfig, 'framework', 'string')
|
|
357
|
+
const detectServerSettings = async (devConfig, options, projectDir, metadata) => {
|
|
358
|
+
validateStringProperty({ devConfig, property: 'framework' })
|
|
275
359
|
|
|
276
360
|
/** @type {Partial<import('./types.js').BaseServerSettings>} */
|
|
277
361
|
let settings = {}
|
|
278
362
|
|
|
279
|
-
if (
|
|
363
|
+
if (options.dir || devConfig.framework === '#static') {
|
|
280
364
|
// serving files statically without a framework server
|
|
281
|
-
settings = await handleStaticServer({
|
|
365
|
+
settings = await handleStaticServer({ options, devConfig, projectDir })
|
|
282
366
|
} else if (devConfig.framework === '#auto') {
|
|
283
367
|
// this is the default CLI behavior
|
|
284
368
|
|
|
285
|
-
const runDetection = !hasCommandAndTargetPort(devConfig)
|
|
286
|
-
const frameworkSettings = runDetection
|
|
287
|
-
|
|
288
|
-
|
|
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
|
+
|
|
289
385
|
if (frameworkSettings === undefined && runDetection) {
|
|
290
386
|
log(`${NETLIFYDEVWARN} No app server detected. Using simple static server`)
|
|
291
|
-
settings = await handleStaticServer({
|
|
387
|
+
settings = await handleStaticServer({ options, devConfig, projectDir })
|
|
292
388
|
} else {
|
|
293
389
|
validateFrameworkConfig({ devConfig })
|
|
294
|
-
|
|
295
|
-
settings = await mergeSettings({ devConfig, frameworkSettings, workingDir: command.workingDir })
|
|
390
|
+
settings = await mergeSettings({ devConfig, frameworkSettings })
|
|
296
391
|
}
|
|
297
392
|
|
|
298
|
-
settings.plugins = frameworkSettings
|
|
393
|
+
settings.plugins = frameworkSettings && frameworkSettings.plugins
|
|
299
394
|
} else if (devConfig.framework === '#custom') {
|
|
300
395
|
validateFrameworkConfig({ devConfig })
|
|
301
396
|
// when the users wants to configure `command` and `targetPort`
|
|
302
|
-
settings = handleCustomFramework({ devConfig
|
|
397
|
+
settings = handleCustomFramework({ devConfig })
|
|
303
398
|
} else if (devConfig.framework) {
|
|
304
399
|
validateFrameworkConfig({ devConfig })
|
|
305
400
|
// this is when the user explicitly configures a framework, e.g. `framework = "gatsby"`
|
|
306
|
-
settings = await handleForcedFramework({
|
|
307
|
-
devConfig,
|
|
308
|
-
project: command.project,
|
|
309
|
-
workingDir: command.workingDir,
|
|
310
|
-
workspacePackage: command.workspacePackage,
|
|
311
|
-
})
|
|
401
|
+
settings = await handleForcedFramework({ devConfig, projectDir })
|
|
312
402
|
}
|
|
313
403
|
|
|
314
404
|
validateConfiguredPort({ devConfig, detectedPort: settings.frameworkPort })
|
|
@@ -319,7 +409,7 @@ const detectServerSettings = async (devConfig, flags, command) => {
|
|
|
319
409
|
errorMessage: `Could not acquire required ${formatProperty('port')}`,
|
|
320
410
|
})
|
|
321
411
|
const functionsDir = devConfig.functions || settings.functions
|
|
322
|
-
const internalFunctionsDir = await getInternalFunctionsDir({ base:
|
|
412
|
+
const internalFunctionsDir = await getInternalFunctionsDir({ base: projectDir })
|
|
323
413
|
const shouldStartFunctionsServer = Boolean(functionsDir || internalFunctionsDir)
|
|
324
414
|
|
|
325
415
|
return {
|
|
@@ -333,6 +423,28 @@ const detectServerSettings = async (devConfig, flags, command) => {
|
|
|
333
423
|
}
|
|
334
424
|
}
|
|
335
425
|
|
|
426
|
+
const filterSettings = function (scriptInquirerOptions, input) {
|
|
427
|
+
const filterOptions = scriptInquirerOptions.map((scriptInquirerOption) => scriptInquirerOption.name)
|
|
428
|
+
// TODO: remove once https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1394 is fixed
|
|
429
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
430
|
+
const filteredSettings = fuzzy.filter(input, filterOptions)
|
|
431
|
+
const filteredSettingNames = new Set(
|
|
432
|
+
filteredSettings.map((filteredSetting) => (input ? filteredSetting.string : filteredSetting)),
|
|
433
|
+
)
|
|
434
|
+
return scriptInquirerOptions.filter((t) => filteredSettingNames.has(t.name))
|
|
435
|
+
}
|
|
436
|
+
|
|
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()
|
|
446
|
+
}
|
|
447
|
+
|
|
336
448
|
/**
|
|
337
449
|
* Returns a copy of the provided config with any plugins provided by the
|
|
338
450
|
* server settings
|
|
@@ -18,14 +18,13 @@ 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
|
|
22
|
-
* @param {string} config.cwd
|
|
21
|
+
* @param {Partial<import('./types').ServerSettings>} config.settings
|
|
23
22
|
* @returns {Promise<StartReturnObject>}
|
|
24
23
|
*/
|
|
25
|
-
export const startFrameworkServer = async function ({
|
|
24
|
+
export const startFrameworkServer = async function ({ settings }) {
|
|
26
25
|
if (settings.useStaticServer) {
|
|
27
26
|
if (settings.command) {
|
|
28
|
-
runCommand(settings.command,
|
|
27
|
+
runCommand(settings.command, settings.env)
|
|
29
28
|
}
|
|
30
29
|
await startStaticServer({ settings })
|
|
31
30
|
|
|
@@ -38,7 +37,7 @@ export const startFrameworkServer = async function ({ cwd, settings }) {
|
|
|
38
37
|
text: `Waiting for framework port ${settings.frameworkPort}. This can be configured using the 'targetPort' property in the netlify.toml`,
|
|
39
38
|
})
|
|
40
39
|
|
|
41
|
-
runCommand(settings.command,
|
|
40
|
+
runCommand(settings.command, settings.env, spinner)
|
|
42
41
|
|
|
43
42
|
let port
|
|
44
43
|
try {
|
|
@@ -47,7 +46,7 @@ export const startFrameworkServer = async function ({ cwd, settings }) {
|
|
|
47
46
|
host: 'localhost',
|
|
48
47
|
output: 'silent',
|
|
49
48
|
timeout: FRAMEWORK_PORT_TIMEOUT,
|
|
50
|
-
...(settings.pollingStrategies
|
|
49
|
+
...(settings.pollingStrategies.includes('HTTP') && { protocol: 'http' }),
|
|
51
50
|
})
|
|
52
51
|
|
|
53
52
|
if (!port.open) {
|
|
@@ -17,7 +17,11 @@ export const SERVE_FUNCTIONS_FOLDER = 'functions-serve'
|
|
|
17
17
|
* @returns {string}
|
|
18
18
|
*/
|
|
19
19
|
export const getFunctionsDir = ({ config, options }, defaultValue) =>
|
|
20
|
-
options.functions ||
|
|
20
|
+
options.functions ||
|
|
21
|
+
(config.dev && config.dev.functions) ||
|
|
22
|
+
config.functionsDirectory ||
|
|
23
|
+
(config.dev && config.dev.Functions) ||
|
|
24
|
+
defaultValue
|
|
21
25
|
|
|
22
26
|
export const getFunctionsManifestPath = async ({ base }) => {
|
|
23
27
|
const path = resolve(base, getPathInProject(['functions', 'manifest.json']))
|
|
@@ -33,13 +37,6 @@ export const getFunctionsDistPath = async ({ base }) => {
|
|
|
33
37
|
return isDirectory ? path : null
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
/**
|
|
37
|
-
* Retrieves the internal functions directory and creates it if ensureExists is provided
|
|
38
|
-
* @param {object} config
|
|
39
|
-
* @param {string} config.base
|
|
40
|
-
* @param {boolean=} config.ensureExists
|
|
41
|
-
* @returns
|
|
42
|
-
*/
|
|
43
40
|
export const getInternalFunctionsDir = async ({ base, ensureExists }) => {
|
|
44
41
|
const path = resolve(base, getPathInProject([INTERNAL_FUNCTIONS_FOLDER]))
|
|
45
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
|
|
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)(
|
|
23
|
-
findUp('.git', { cwd
|
|
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 (
|
|
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
|
-
|
|
223
|
+
env,
|
|
224
224
|
})
|
|
225
225
|
await saveNetlifyToml({ repositoryRoot, config, configPath, baseDir, buildCmd, buildDir, functionsDir })
|
|
226
226
|
|