netlify-cli 12.5.0 → 12.7.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 (102) hide show
  1. package/README.md +5 -0
  2. package/npm-shrinkwrap.json +1075 -634
  3. package/package.json +7 -160
  4. package/src/commands/base-command.mjs +16 -5
  5. package/src/commands/deploy/deploy.mjs +1 -1
  6. package/src/commands/dev/dev.mjs +27 -511
  7. package/src/commands/env/env-list.mjs +1 -0
  8. package/src/commands/functions/functions-create.mjs +35 -25
  9. package/src/commands/functions/functions-serve.mjs +5 -1
  10. package/src/commands/main.mjs +2 -0
  11. package/src/commands/serve/serve.mjs +189 -0
  12. package/src/commands/sites/sites-list.mjs +1 -1
  13. package/src/commands/watch/watch.mjs +1 -1
  14. package/src/functions-templates/go/hello-world/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  15. package/src/functions-templates/javascript/apollo-graphql/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  16. package/src/functions-templates/javascript/apollo-graphql-rest/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  17. package/src/functions-templates/javascript/auth-fetch/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  18. package/src/functions-templates/javascript/create-user/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  19. package/src/functions-templates/javascript/fauna-crud/{.netlify-function-template.cjs → .netlify-function-template.mjs} +3 -2
  20. package/src/functions-templates/javascript/fauna-graphql/{.netlify-function-template.cjs → .netlify-function-template.mjs} +3 -2
  21. package/src/functions-templates/javascript/google-analytics/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  22. package/src/functions-templates/javascript/graphql-gateway/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  23. package/src/functions-templates/javascript/hasura-event-triggered/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  24. package/src/functions-templates/javascript/hello/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  25. package/src/functions-templates/{typescript/hello-world/.netlify-function-template.cjs → javascript/hello-world/.netlify-function-template.mjs} +1 -1
  26. package/src/functions-templates/javascript/identity-signup/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  27. package/src/functions-templates/javascript/image-external/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  28. package/src/functions-templates/javascript/localized-content/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  29. package/src/functions-templates/javascript/node-fetch/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  30. package/src/functions-templates/javascript/oauth-passport/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  31. package/src/functions-templates/javascript/oauth-passport/package.json +1 -1
  32. package/src/functions-templates/javascript/protected-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  33. package/src/functions-templates/javascript/sanity-create/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  34. package/src/functions-templates/javascript/sanity-groq/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  35. package/src/functions-templates/javascript/scheduled-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  36. package/src/functions-templates/javascript/scheduled-function/package.json +1 -1
  37. package/src/functions-templates/javascript/send-email/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  38. package/src/functions-templates/javascript/serverless-ssr/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  39. package/src/functions-templates/javascript/set-cookie/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  40. package/src/functions-templates/javascript/set-cookies/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  41. package/src/functions-templates/{typescript/set-req-header/.netlify-function-template.cjs → javascript/set-req-header/.netlify-function-template.mjs} +1 -1
  42. package/src/functions-templates/javascript/set-res-header/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  43. package/src/functions-templates/javascript/slack-rate-limit/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  44. package/src/functions-templates/javascript/stripe-charge/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  45. package/src/functions-templates/javascript/stripe-subscription/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  46. package/src/functions-templates/javascript/submission-created/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  47. package/src/functions-templates/javascript/token-hider/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  48. package/src/functions-templates/{typescript/transform-response/.netlify-function-template.cjs → javascript/transform-response/.netlify-function-template.mjs} +1 -1
  49. package/src/functions-templates/javascript/url-shortener/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  50. package/src/functions-templates/javascript/using-middleware/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  51. package/src/functions-templates/rust/hello-world/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  52. package/src/functions-templates/typescript/abtest/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  53. package/src/functions-templates/typescript/geolocation/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  54. package/src/functions-templates/typescript/geolocation/{{name}}.ts +1 -1
  55. package/src/functions-templates/{javascript/hello-world/.netlify-function-template.cjs → typescript/hello-world/.netlify-function-template.mjs} +1 -1
  56. package/src/functions-templates/typescript/hello-world/package-lock.json +7 -7
  57. package/src/functions-templates/typescript/hello-world/package.json +1 -1
  58. package/src/functions-templates/typescript/json/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  59. package/src/functions-templates/typescript/json/{{name}}.ts +1 -1
  60. package/src/functions-templates/typescript/log/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  61. package/src/functions-templates/typescript/log/{{name}}.ts +2 -2
  62. package/src/functions-templates/typescript/scheduled-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  63. package/src/functions-templates/typescript/scheduled-function/package.json +1 -1
  64. package/src/functions-templates/typescript/set-cookies/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  65. package/src/functions-templates/{javascript/set-req-header/.netlify-function-template.cjs → typescript/set-req-header/.netlify-function-template.mjs} +1 -1
  66. package/src/functions-templates/typescript/set-res-header/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  67. package/src/functions-templates/{javascript/transform-response/.netlify-function-template.cjs → typescript/transform-response/.netlify-function-template.mjs} +1 -1
  68. package/src/lib/completion/constants.mjs +1 -1
  69. package/src/lib/edge-functions/deploy.mjs +1 -1
  70. package/src/lib/edge-functions/internal.mjs +1 -1
  71. package/src/lib/edge-functions/proxy.mjs +2 -2
  72. package/src/lib/edge-functions/registry.mjs +2 -5
  73. package/src/lib/{fs.cjs → fs.mjs} +5 -13
  74. package/src/lib/functions/registry.mjs +55 -33
  75. package/src/lib/functions/runtimes/js/builders/netlify-lambda.mjs +1 -1
  76. package/src/lib/functions/runtimes/js/builders/zisi.mjs +3 -2
  77. package/src/lib/functions/runtimes/rust/index.mjs +3 -2
  78. package/src/lib/functions/server.mjs +35 -18
  79. package/src/lib/{settings.cjs → settings.mjs} +6 -8
  80. package/src/lib/{spinner.cjs → spinner.mjs} +5 -7
  81. package/src/utils/banner.mjs +17 -0
  82. package/src/utils/command-helpers.mjs +1 -1
  83. package/src/utils/detect-server-settings.mjs +35 -1
  84. package/src/utils/dev.mjs +8 -5
  85. package/src/utils/dot-env.mjs +1 -1
  86. package/src/utils/framework-server.mjs +66 -0
  87. package/src/utils/functions/functions.mjs +20 -4
  88. package/src/utils/functions/get-functions.mjs +1 -1
  89. package/src/utils/get-global-config.mjs +1 -1
  90. package/src/utils/gitignore.mjs +1 -1
  91. package/src/utils/graph.mjs +170 -0
  92. package/src/utils/init/utils.mjs +1 -1
  93. package/src/utils/live-tunnel.mjs +1 -1
  94. package/src/utils/lm/install.mjs +2 -2
  95. package/src/utils/proxy-server.mjs +90 -0
  96. package/src/utils/proxy.mjs +1 -1
  97. package/src/utils/rules-proxy.mjs +1 -1
  98. package/src/utils/run-build.mjs +129 -0
  99. package/src/utils/shell.mjs +120 -0
  100. package/src/utils/state-config.mjs +1 -1
  101. package/src/utils/static-server.mjs +34 -0
  102. package/src/utils/validation.mjs +15 -0
@@ -1,298 +1,35 @@
1
1
  // @ts-check
2
- import events from 'events'
3
- import path from 'path'
4
2
  import process from 'process'
5
- import { promisify } from 'util'
6
3
 
7
- import boxen from 'boxen'
8
4
  import { Option } from 'commander'
9
- import execa from 'execa'
10
- import StaticServer from 'static-server'
11
- import stripAnsiCc from 'strip-ansi-control-characters'
12
- import waitPort from 'wait-port'
13
5
 
14
6
  import { promptEditorHelper } from '../../lib/edge-functions/editor-helper.mjs'
15
7
  import { startFunctionsServer } from '../../lib/functions/server.mjs'
16
- import {
17
- OneGraphCliClient,
18
- loadCLISession,
19
- markCliSessionInactive,
20
- persistNewOperationsDocForSession,
21
- startOneGraphCLISession,
22
- } from '../../lib/one-graph/cli-client.mjs'
23
- import {
24
- defaultExampleOperationsDoc,
25
- getGraphEditUrlBySiteId,
26
- getNetlifyGraphConfig,
27
- readGraphQLOperationsSourceFile,
28
- } from '../../lib/one-graph/cli-netlify-graph.mjs'
29
- import { startSpinner, stopSpinner } from '../../lib/spinner.cjs'
8
+ import { printBanner } from '../../utils/banner.mjs'
30
9
  import {
31
10
  BANG,
32
11
  chalk,
33
- error,
34
12
  exit,
35
- getToken,
36
13
  log,
37
14
  NETLIFYDEV,
38
15
  NETLIFYDEVERR,
39
16
  NETLIFYDEVLOG,
40
17
  NETLIFYDEVWARN,
41
18
  normalizeConfig,
42
- warn,
43
- watchDebounced,
44
19
  } from '../../utils/command-helpers.mjs'
45
- import detectServerSettings from '../../utils/detect-server-settings.mjs'
46
- import { generateNetlifyGraphJWT, getSiteInformation, injectEnvVariables, processOnExit } from '../../utils/dev.mjs'
20
+ import detectServerSettings, { getConfigWithPlugins } from '../../utils/detect-server-settings.mjs'
21
+ import { getSiteInformation, injectEnvVariables } from '../../utils/dev.mjs'
47
22
  import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.mjs'
48
23
  import { ensureNetlifyIgnore } from '../../utils/gitignore.mjs'
24
+ import { startNetlifyGraph, startPollingForAPIAuthentication } from '../../utils/graph.mjs'
49
25
  import { startLiveTunnel } from '../../utils/live-tunnel.mjs'
50
26
  import openBrowser from '../../utils/open-browser.mjs'
51
- import { startProxy } from '../../utils/proxy.mjs'
27
+ import { generateInspectSettings, startProxyServer } from '../../utils/proxy-server.mjs'
28
+ import { runDevTimeline } from '../../utils/run-build.mjs'
29
+ import { getGeoCountryArgParser } from '../../utils/validation.mjs'
52
30
 
53
31
  import { createDevExecCommand } from './dev-exec.mjs'
54
32
 
55
- const netlifyBuildPromise = import('@netlify/build')
56
-
57
- const startStaticServer = async ({ settings }) => {
58
- const server = new StaticServer({
59
- rootPath: settings.dist,
60
- name: 'netlify-dev',
61
- port: settings.frameworkPort,
62
- templates: {
63
- notFound: path.join(settings.dist, '404.html'),
64
- },
65
- })
66
-
67
- await promisify(server.start.bind(server))()
68
- log(`\n${NETLIFYDEVLOG} Static server listening to`, settings.frameworkPort)
69
- }
70
-
71
- const isNonExistingCommandError = ({ command, error: commandError }) => {
72
- // `ENOENT` is only returned for non Windows systems
73
- // See https://github.com/sindresorhus/execa/pull/447
74
- if (commandError.code === 'ENOENT') {
75
- return true
76
- }
77
-
78
- // if the command is a package manager we let it report the error
79
- if (['yarn', 'npm'].includes(command)) {
80
- return false
81
- }
82
-
83
- // this only works on English versions of Windows
84
- return (
85
- typeof commandError.message === 'string' &&
86
- commandError.message.includes('is not recognized as an internal or external command')
87
- )
88
- }
89
-
90
- /**
91
- * @type {(() => Promise<void>)[]} - array of functions to run before the process exits
92
- */
93
- const cleanupWork = []
94
-
95
- let cleanupStarted = false
96
-
97
- /**
98
- * @param {object} input
99
- * @param {number=} input.exitCode The exit code to return when exiting the process after cleanup
100
- */
101
- const cleanupBeforeExit = async ({ exitCode }) => {
102
- // If cleanup has started, then wherever started it will be responsible for exiting
103
- if (!cleanupStarted) {
104
- cleanupStarted = true
105
- try {
106
- // eslint-disable-next-line no-unused-vars
107
- const cleanupFinished = await Promise.all(cleanupWork.map((cleanup) => cleanup()))
108
- } finally {
109
- process.exit(exitCode)
110
- }
111
- }
112
- }
113
-
114
- /**
115
- * Run a command and pipe stdout, stderr and stdin
116
- * @param {string} command
117
- * @param {NodeJS.ProcessEnv} env
118
- * @returns {execa.ExecaChildProcess<string>}
119
- */
120
- const runCommand = (command, env = {}, spinner = null) => {
121
- const commandProcess = execa.command(command, {
122
- preferLocal: true,
123
- // we use reject=false to avoid rejecting synchronously when the command doesn't exist
124
- reject: false,
125
- env,
126
- // windowsHide needs to be false for child process to terminate properly on Windows
127
- windowsHide: false,
128
- })
129
-
130
- // This ensures that an active spinner stays at the bottom of the commandline
131
- // even though the actual framework command might be outputting stuff
132
- const pipeDataWithSpinner = (writeStream, chunk) => {
133
- if (spinner && spinner.isSpinning) {
134
- spinner.clear()
135
- spinner.isSilent = true
136
- }
137
- writeStream.write(chunk, () => {
138
- if (spinner && spinner.isSpinning) {
139
- spinner.isSilent = false
140
- spinner.render()
141
- }
142
- })
143
- }
144
-
145
- commandProcess.stdout.pipe(stripAnsiCc.stream()).on('data', pipeDataWithSpinner.bind(null, process.stdout))
146
- commandProcess.stderr.pipe(stripAnsiCc.stream()).on('data', pipeDataWithSpinner.bind(null, process.stderr))
147
- process.stdin.pipe(commandProcess.stdin)
148
-
149
- // we can't try->await->catch since we don't want to block on the framework server which
150
- // is a long running process
151
- // eslint-disable-next-line promise/catch-or-return
152
- commandProcess
153
- // eslint-disable-next-line promise/prefer-await-to-then
154
- .then(async () => {
155
- const result = await commandProcess
156
- const [commandWithoutArgs] = command.split(' ')
157
- if (result.failed && isNonExistingCommandError({ command: commandWithoutArgs, error: result })) {
158
- log(
159
- NETLIFYDEVERR,
160
- `Failed running command: ${command}. Please verify ${chalk.magenta(`'${commandWithoutArgs}'`)} exists`,
161
- )
162
- } else {
163
- const errorMessage = result.failed
164
- ? `${NETLIFYDEVERR} ${result.shortMessage}`
165
- : `${NETLIFYDEVWARN} "${command}" exited with code ${result.exitCode}`
166
-
167
- log(`${errorMessage}. Shutting down Netlify Dev server`)
168
- }
169
-
170
- return await cleanupBeforeExit({ exitCode: 1 })
171
- })
172
- processOnExit(async () => await cleanupBeforeExit({}))
173
-
174
- return commandProcess
175
- }
176
-
177
- /**
178
- * @typedef StartReturnObject
179
- * @property {4 | 6 | undefined=} ipVersion The version the open port was found on
180
- */
181
-
182
- /**
183
- * Start a static server if the `useStaticServer` is provided or a framework specific server
184
- * @param {object} config
185
- * @param {Partial<import('../../utils/types').ServerSettings>} config.settings
186
- * @returns {Promise<StartReturnObject>}
187
- */
188
- const startFrameworkServer = async function ({ settings }) {
189
- if (settings.useStaticServer) {
190
- if (settings.command) {
191
- runCommand(settings.command, settings.env)
192
- }
193
- await startStaticServer({ settings })
194
-
195
- return {}
196
- }
197
-
198
- log(`${NETLIFYDEVLOG} Starting Netlify Dev with ${settings.framework || 'custom config'}`)
199
-
200
- const spinner = startSpinner({
201
- text: `Waiting for framework port ${settings.frameworkPort}. This can be configured using the 'targetPort' property in the netlify.toml`,
202
- })
203
-
204
- runCommand(settings.command, settings.env, spinner)
205
-
206
- let port
207
- try {
208
- port = await waitPort({
209
- port: settings.frameworkPort,
210
- host: 'localhost',
211
- output: 'silent',
212
- timeout: FRAMEWORK_PORT_TIMEOUT,
213
- ...(settings.pollingStrategies.includes('HTTP') && { protocol: 'http' }),
214
- })
215
-
216
- if (!port.open) {
217
- throw new Error(`Timed out waiting for port '${settings.frameworkPort}' to be open`)
218
- }
219
-
220
- stopSpinner({ error: false, spinner })
221
- } catch (error_) {
222
- stopSpinner({ error: true, spinner })
223
- log(NETLIFYDEVERR, `Netlify Dev could not start or connect to localhost:${settings.frameworkPort}.`)
224
- log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`)
225
- error(error_)
226
- exit(1)
227
- }
228
-
229
- return { ipVersion: port?.ipVersion }
230
- }
231
-
232
- // 10 minutes
233
- const FRAMEWORK_PORT_TIMEOUT = 6e5
234
-
235
- /**
236
- * @typedef {Object} InspectSettings
237
- * @property {boolean} enabled - Inspect enabled
238
- * @property {boolean} pause - Pause on breakpoints
239
- * @property {string|undefined} address - Host/port override (optional)
240
- */
241
-
242
- /**
243
- *
244
- * @param {object} params
245
- * @param {*} params.addonsUrls
246
- * @param {import('../base-command.mjs').NetlifyOptions["config"]} params.config
247
- * @param {import('../base-command.mjs').NetlifyOptions["cachedConfig"]['env']} params.env
248
- * @param {InspectSettings} params.inspectSettings
249
- * @param {() => Promise<object>} params.getUpdatedConfig
250
- * @param {string} params.geolocationMode
251
- * @param {string} params.geoCountry
252
- * @param {*} params.settings
253
- * @param {boolean} params.offline
254
- * @param {*} params.site
255
- * @param {*} params.siteInfo
256
- * @param {import('../../utils/state-config.mjs').default} params.state
257
- * @returns
258
- */
259
- const startProxyServer = async ({
260
- addonsUrls,
261
- config,
262
- env,
263
- geoCountry,
264
- geolocationMode,
265
- getUpdatedConfig,
266
- inspectSettings,
267
- offline,
268
- settings,
269
- site,
270
- siteInfo,
271
- state,
272
- }) => {
273
- const url = await startProxy({
274
- addonsUrls,
275
- config,
276
- configPath: site.configPath,
277
- env,
278
- geolocationMode,
279
- geoCountry,
280
- getUpdatedConfig,
281
- inspectSettings,
282
- offline,
283
- projectDir: site.root,
284
- settings,
285
- state,
286
- siteInfo,
287
- })
288
- if (!url) {
289
- log(NETLIFYDEVERR, `Unable to start proxy server on port '${settings.port}'`)
290
- exit(1)
291
- }
292
-
293
- return url
294
- }
295
-
296
33
  /**
297
34
  *
298
35
  * @param {object} config
@@ -314,83 +51,6 @@ const handleLiveTunnel = async ({ api, options, settings, site }) => {
314
51
  }
315
52
  }
316
53
 
317
- const printBanner = ({ url }) => {
318
- const banner = chalk.bold(`${NETLIFYDEVLOG} Server now ready on ${url}`)
319
-
320
- log(
321
- boxen(banner, {
322
- padding: 1,
323
- margin: 1,
324
- align: 'center',
325
- borderColor: '#00c7b7',
326
- }),
327
- )
328
- }
329
-
330
- const startPollingForAPIAuthentication = async function (options) {
331
- const { api, command, config, site, siteInfo } = options
332
- const frequency = 5000
333
-
334
- const helper = async (maybeSiteData) => {
335
- const siteData = await (maybeSiteData || api.getSite({ siteId: site.id }))
336
- const authlifyTokenId = siteData && siteData.authlify_token_id
337
-
338
- const existingAuthlifyTokenId = config && config.netlifyGraphConfig && config.netlifyGraphConfig.authlifyTokenId
339
- if (authlifyTokenId && authlifyTokenId !== existingAuthlifyTokenId) {
340
- const netlifyToken = await command.authenticate()
341
- // Only inject the authlify config if a token ID exists. This prevents
342
- // calling command.authenticate() (which opens a browser window) if the
343
- // user hasn't enabled API Authentication
344
- const netlifyGraphConfig = {
345
- netlifyToken,
346
- authlifyTokenId: siteData.authlify_token_id,
347
- siteId: site.id,
348
- }
349
- config.netlifyGraphConfig = netlifyGraphConfig
350
-
351
- const netlifyGraphJWT = generateNetlifyGraphJWT(netlifyGraphConfig)
352
-
353
- if (netlifyGraphJWT != null) {
354
- // XXX(anmonteiro): this name is deprecated. Delete after 3/31/2022
355
- process.env.ONEGRAPH_AUTHLIFY_TOKEN = netlifyGraphJWT
356
- process.env.NETLIFY_GRAPH_TOKEN = netlifyGraphJWT
357
- }
358
- } else if (!authlifyTokenId) {
359
- // If there's no `authlifyTokenId`, it's because the user disabled API
360
- // Auth. Delete the config in this case.
361
- delete config.netlifyGraphConfig
362
- }
363
-
364
- setTimeout(helper, frequency)
365
- }
366
-
367
- await helper(siteInfo)
368
- }
369
-
370
- /**
371
- * @param {boolean|string} edgeInspect
372
- * @param {boolean|string} edgeInspectBrk
373
- * @returns {InspectSettings}
374
- */
375
- const generateInspectSettings = (edgeInspect, edgeInspectBrk) => {
376
- const enabled = Boolean(edgeInspect) || Boolean(edgeInspectBrk)
377
- const pause = Boolean(edgeInspectBrk)
378
- const getAddress = () => {
379
- if (edgeInspect) {
380
- return typeof edgeInspect === 'string' ? edgeInspect : undefined
381
- }
382
- if (edgeInspectBrk) {
383
- return typeof edgeInspectBrk === 'string' ? edgeInspectBrk : undefined
384
- }
385
- }
386
-
387
- return {
388
- enabled,
389
- pause,
390
- address: getAddress(),
391
- }
392
- }
393
-
394
54
  const validateShortFlagArgs = (args) => {
395
55
  if (args.startsWith('=')) {
396
56
  throw new Error(
@@ -407,19 +67,6 @@ const validateShortFlagArgs = (args) => {
407
67
  return args
408
68
  }
409
69
 
410
- const validateGeoCountryCode = (arg) => {
411
- // Validate that the arg passed is two letters only for country
412
- // See https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
413
- if (!/^[a-z]{2}$/i.test(arg)) {
414
- throw new Error(
415
- `The geo country code must use a two letter abbreviation.
416
- ${chalk.red(BANG)} Example:
417
- netlify dev --geo=mock --country=FR`,
418
- )
419
- }
420
- return arg.toUpperCase()
421
- }
422
-
423
70
  /**
424
71
  * The dev command
425
72
  * @param {import('commander').OptionValues} options
@@ -428,7 +75,6 @@ const validateGeoCountryCode = (arg) => {
428
75
  const dev = async (options, command) => {
429
76
  log(`${NETLIFYDEV}`)
430
77
  const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify
431
- const netlifyBuild = await netlifyBuildPromise
432
78
  config.dev = { ...config.dev }
433
79
  config.build = { ...config.build }
434
80
  /** @type {import('./types').DevConfig} */
@@ -442,6 +88,8 @@ const dev = async (options, command) => {
442
88
 
443
89
  let { env } = cachedConfig
444
90
 
91
+ env.NETLIFY_DEV = { sources: ['internal'], value: 'true' }
92
+
445
93
  if (!options.offline && siteInfo.use_envelope) {
446
94
  env = await getEnvelopeEnv({ api, context: options.context, env, siteInfo })
447
95
  log(`${NETLIFYDEVLOG} Injecting environment variable values for ${chalk.yellow('all scopes')}`)
@@ -463,25 +111,7 @@ const dev = async (options, command) => {
463
111
  try {
464
112
  settings = await detectServerSettings(devConfig, options, site.root)
465
113
 
466
- // If there are plugins that we should be running for this site, add them
467
- // to the config as if they were declared in netlify.toml. We must check
468
- // whether the plugin has already been added by another source (like the
469
- // TOML file or the UI), as we don't want to run the same plugin twice.
470
- if (settings.plugins) {
471
- const { plugins: existingPlugins = [] } = cachedConfig.config
472
- const existingPluginNames = new Set(existingPlugins.map((plugin) => plugin.package))
473
- const newPlugins = settings.plugins
474
- .map((pluginName) => {
475
- if (existingPluginNames.has(pluginName)) {
476
- return
477
- }
478
-
479
- return { package: pluginName, origin: 'config', inputs: {} }
480
- })
481
- .filter(Boolean)
482
-
483
- cachedConfig.config.plugins = [...newPlugins, ...cachedConfig.config.plugins]
484
- }
114
+ cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
485
115
  } catch (error_) {
486
116
  log(NETLIFYDEVERR, error_.message)
487
117
  exit(1)
@@ -494,10 +124,15 @@ const dev = async (options, command) => {
494
124
  startPollingForAPIAuthentication({ api, command, config, site, siteInfo })
495
125
  }
496
126
 
127
+ log(`${NETLIFYDEVWARN} Setting up local development server`)
128
+
129
+ const { configPath: configPathOverride } = await runDevTimeline({ cachedConfig, options, settings, site })
130
+
497
131
  await startFunctionsServer({
498
132
  api,
499
133
  command,
500
134
  config,
135
+ debug: options.debug,
501
136
  settings,
502
137
  site,
503
138
  siteInfo,
@@ -506,23 +141,6 @@ const dev = async (options, command) => {
506
141
  timeouts,
507
142
  })
508
143
 
509
- log(`${NETLIFYDEVWARN} Setting up local development server`)
510
-
511
- const devCommand = async () => {
512
- const { ipVersion } = await startFrameworkServer({ settings })
513
-
514
- settings.frameworkHost = ipVersion === 6 ? '::1' : '127.0.0.1'
515
- }
516
- const startDevOptions = getBuildOptions({
517
- cachedConfig,
518
- options,
519
- })
520
- const { error: startDevError, success } = await netlifyBuild.startDev(devCommand, startDevOptions)
521
-
522
- if (!success) {
523
- error(`Could not start local development server\n\n${startDevError.message}\n\n${startDevError.stack}`)
524
- }
525
-
526
144
  // Try to add `.netlify` to `.gitignore`.
527
145
  try {
528
146
  await ensureNetlifyIgnore(repositoryRoot)
@@ -544,7 +162,8 @@ const dev = async (options, command) => {
544
162
  let url = await startProxyServer({
545
163
  addonsUrls,
546
164
  config,
547
- env: command.netlify.cachedConfig.env,
165
+ configPath: configPathOverride,
166
+ env,
548
167
  geolocationMode: options.geo,
549
168
  geoCountry: options.country,
550
169
  getUpdatedConfig,
@@ -566,121 +185,19 @@ const dev = async (options, command) => {
566
185
  process.env.URL = url
567
186
  process.env.DEPLOY_URL = url
568
187
 
569
- if (startNetlifyGraphWatcher && options.offline) {
570
- warn(`Unable to start Netlify Graph in offline mode`)
571
- } else if (startNetlifyGraphWatcher && !site.id) {
572
- error(
573
- `No siteId defined, unable to start Netlify Graph. To enable, run ${chalk.yellow(
574
- 'netlify init',
575
- )} or ${chalk.yellow('netlify link')}.`,
576
- )
577
- } else if (startNetlifyGraphWatcher) {
578
- const netlifyToken = await command.authenticate()
579
- await OneGraphCliClient.ensureAppForSite(netlifyToken, site.id)
580
-
581
- let stopWatchingCLISessions
582
-
583
- let liveConfig = { ...config }
584
- let isRestartingSession = false
585
-
586
- const createOrResumeSession = async function () {
587
- const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options, settings })
588
-
589
- let graphqlDocument = readGraphQLOperationsSourceFile(netlifyGraphConfig)
590
-
591
- if (!graphqlDocument || graphqlDocument.trim().length === 0) {
592
- graphqlDocument = defaultExampleOperationsDoc
593
- }
594
-
595
- stopWatchingCLISessions = await startOneGraphCLISession({
596
- config: liveConfig,
597
- netlifyGraphConfig,
598
- netlifyToken,
599
- site,
600
- state,
601
- oneGraphSessionId: options.sessionId,
602
- })
603
-
604
- // Should be created by startOneGraphCLISession
605
- const oneGraphSessionId = loadCLISession(state)
606
-
607
- await persistNewOperationsDocForSession({
608
- config: liveConfig,
609
- netlifyGraphConfig,
610
- netlifyToken,
611
- oneGraphSessionId,
612
- operationsDoc: graphqlDocument,
613
- siteId: site.id,
614
- siteRoot: site.root,
615
- })
616
-
617
- return oneGraphSessionId
618
- }
619
-
620
- const configWatcher = new events.EventEmitter()
621
-
622
- // Only set up a watcher if we know the config path.
623
- const { configPath } = command.netlify.site
624
- if (configPath) {
625
- // chokidar handle
626
- command.configWatcherHandle = await watchDebounced(configPath, {
627
- depth: 1,
628
- onChange: async () => {
629
- const cwd = options.cwd || process.cwd()
630
- const [token] = await getToken(options.auth)
631
- const { config: newConfig } = await command.getConfig({ cwd, state, token, ...command.netlify.apiUrlOpts })
632
-
633
- const normalizedNewConfig = normalizeConfig(newConfig)
634
- configWatcher.emit('change', normalizedNewConfig)
635
- },
636
- })
637
-
638
- processOnExit(async () => {
639
- await command.configWatcherHandle.close()
640
- })
641
- }
642
-
643
- // Set up a handler for config changes.
644
- configWatcher.on('change', async (newConfig) => {
645
- command.netlify.config = newConfig
646
- liveConfig = newConfig
647
- if (isRestartingSession) {
648
- return
649
- }
650
- stopWatchingCLISessions && stopWatchingCLISessions()
651
- isRestartingSession = true
652
- await createOrResumeSession()
653
- isRestartingSession = false
654
- })
655
-
656
- const oneGraphSessionId = await createOrResumeSession()
657
- const cleanupSession = () => markCliSessionInactive({ netlifyToken, sessionId: oneGraphSessionId, siteId: site.id })
658
-
659
- cleanupWork.push(cleanupSession)
660
-
661
- const graphEditUrl = getGraphEditUrlBySiteId({ siteId: site.id, oneGraphSessionId })
662
-
663
- log(
664
- `Starting Netlify Graph session, to edit your library visit ${graphEditUrl} or run \`netlify graph:edit\` in another tab`,
665
- )
666
- }
188
+ await startNetlifyGraph({
189
+ command,
190
+ config,
191
+ options,
192
+ settings,
193
+ site,
194
+ startNetlifyGraphWatcher,
195
+ state,
196
+ })
667
197
 
668
198
  printBanner({ url })
669
199
  }
670
200
 
671
- const getBuildOptions = ({ cachedConfig, options: { context, cwd = process.cwd(), debug, dry, offline }, token }) => ({
672
- cachedConfig,
673
- token,
674
- dry,
675
- debug,
676
- context,
677
- mode: 'cli',
678
- telemetry: false,
679
- buffer: false,
680
- offline,
681
- cwd,
682
- })
683
-
684
201
  /**
685
202
  * Creates the `netlify dev` command
686
203
  * @param {import('../base-command.mjs').default} program
@@ -700,7 +217,6 @@ export const createDevCommand = (program) => {
700
217
  '--context <context>',
701
218
  'Specify a deploy context or branch for environment variables (contexts: "production", "deploy-preview", "branch-deploy", "dev")',
702
219
  normalizeContext,
703
- 'dev',
704
220
  )
705
221
  .option('-p ,--port <port>', 'port of netlify dev', (value) => Number.parseInt(value))
706
222
  .option('--targetPort <port>', 'port of target app server', (value) => Number.parseInt(value))
@@ -722,7 +238,7 @@ export const createDevCommand = (program) => {
722
238
  new Option(
723
239
  '--country <geoCountry>',
724
240
  'Two-letter country code (https://ntl.fyi/country-codes) to use as mock geolocation (enables --geo=mock automatically)',
725
- ).argParser(validateGeoCountryCode),
241
+ ).argParser(getGeoCountryArgParser('netlify dev --geo=mock --country=FR')),
726
242
  )
727
243
  .addOption(
728
244
  new Option('--staticServerPort <port>', 'port of the static app server used when no framework is detected')
@@ -134,6 +134,7 @@ export const createEnvListCommand = (program) =>
134
134
  normalizeContext,
135
135
  'dev',
136
136
  )
137
+ .option('--json', 'Output environment variables as JSON')
137
138
  .addOption(new Option('--plain', 'Output environment variables as plaintext').conflicts('json'))
138
139
  .addOption(
139
140
  new Option('-s, --scope <scope>', 'Specify a scope')