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
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+ import boxen from 'boxen'
3
+
4
+ import { chalk, log, NETLIFYDEVLOG } from './command-helpers.mjs'
5
+
6
+ export const printBanner = ({ url }) => {
7
+ const banner = chalk.bold(`${NETLIFYDEVLOG} Server now ready on ${url}`)
8
+
9
+ log(
10
+ boxen(banner, {
11
+ padding: 1,
12
+ margin: 1,
13
+ align: 'center',
14
+ borderColor: '#00c7b7',
15
+ }),
16
+ )
17
+ }
@@ -12,7 +12,7 @@ import WSL from 'is-wsl'
12
12
  import debounce from 'lodash/debounce.js'
13
13
  import terminalLink from 'terminal-link'
14
14
 
15
- import { clearSpinner, startSpinner } from '../lib/spinner.cjs'
15
+ import { clearSpinner, startSpinner } from '../lib/spinner.mjs'
16
16
 
17
17
  import getGlobalConfig from './get-global-config.mjs'
18
18
  import getPackageJson from './get-package-json.mjs'
@@ -11,7 +11,7 @@ import isPlainObject from 'is-plain-obj'
11
11
 
12
12
  import { NETLIFYDEVWARN, chalk, log } from './command-helpers.mjs'
13
13
  import { acquirePort } from './dev.mjs'
14
- import { getInternalFunctionsDir } from './functions/index.mjs'
14
+ import { getInternalFunctionsDir } from './functions/functions.mjs'
15
15
 
16
16
  const formatProperty = (str) => chalk.magenta(`'${str}'`)
17
17
  const formatValue = (str) => chalk.green(`'${str}'`)
@@ -364,4 +364,38 @@ const formatSettingsArrForInquirer = function (frameworks) {
364
364
  return formattedArr.flat()
365
365
  }
366
366
 
367
+ /**
368
+ * Returns a copy of the provided config with any plugins provided by the
369
+ * server settings
370
+ * @param {*} config
371
+ * @param {Partial<import('./types').ServerSettings>} settings
372
+ * @returns {*} Modified config
373
+ */
374
+ export const getConfigWithPlugins = (config, settings) => {
375
+ if (!settings.plugins) {
376
+ return config
377
+ }
378
+
379
+ // If there are plugins that we should be running for this site, add them
380
+ // to the config as if they were declared in netlify.toml. We must check
381
+ // whether the plugin has already been added by another source (like the
382
+ // TOML file or the UI), as we don't want to run the same plugin twice.
383
+ const { plugins: existingPlugins = [] } = config
384
+ const existingPluginNames = new Set(existingPlugins.map((plugin) => plugin.package))
385
+ const newPlugins = settings.plugins
386
+ .map((pluginName) => {
387
+ if (existingPluginNames.has(pluginName)) {
388
+ return
389
+ }
390
+
391
+ return { package: pluginName, origin: 'config', inputs: {} }
392
+ })
393
+ .filter(Boolean)
394
+
395
+ return {
396
+ ...config,
397
+ plugins: [...newPlugins, ...config.plugins],
398
+ }
399
+ }
400
+
367
401
  export default detectServerSettings
package/src/utils/dev.mjs CHANGED
@@ -148,6 +148,10 @@ export const injectEnvVariables = async ({ devConfig, env, site }) => {
148
148
  const newSourceName = `${file} file`
149
149
  const sources = environment.has(key) ? [newSourceName, ...environment.get(key).sources] : [newSourceName]
150
150
 
151
+ if (sources.includes('internal')) {
152
+ return
153
+ }
154
+
151
155
  environment.set(key, {
152
156
  sources,
153
157
  value: fileEnv[key],
@@ -160,6 +164,7 @@ export const injectEnvVariables = async ({ devConfig, env, site }) => {
160
164
  const existsInProcess = process.env[key] !== undefined
161
165
  const [usedSource, ...overriddenSources] = existsInProcess ? ['process', ...variable.sources] : variable.sources
162
166
  const usedSourceName = getEnvSourceName(usedSource)
167
+ const isInternal = variable.sources.includes('internal')
163
168
 
164
169
  overriddenSources.forEach((source) => {
165
170
  const sourceName = getEnvSourceName(source)
@@ -173,17 +178,15 @@ export const injectEnvVariables = async ({ devConfig, env, site }) => {
173
178
  )
174
179
  })
175
180
 
176
- if (!existsInProcess) {
177
- // Omitting `general` env vars to reduce noise in the logs.
178
- if (usedSource !== 'general') {
181
+ if (!existsInProcess || isInternal) {
182
+ // Omitting `general` and `internal` env vars to reduce noise in the logs.
183
+ if (usedSource !== 'general' && !isInternal) {
179
184
  log(`${NETLIFYDEVLOG} Injected ${usedSourceName} env var: ${chalk.yellow(key)}`)
180
185
  }
181
186
 
182
187
  process.env[key] = variable.value
183
188
  }
184
189
  }
185
-
186
- process.env.NETLIFY_DEV = 'true'
187
190
  }
188
191
 
189
192
  export const acquirePort = async ({ configuredPort, defaultPort, errorMessage }) => {
@@ -4,7 +4,7 @@ import path from 'path'
4
4
 
5
5
  import dotenv from 'dotenv'
6
6
 
7
- import { isFileAsync } from '../lib/fs.cjs'
7
+ import { isFileAsync } from '../lib/fs.mjs'
8
8
 
9
9
  import { warn } from './command-helpers.mjs'
10
10
 
@@ -0,0 +1,66 @@
1
+ // @ts-check
2
+ import waitPort from 'wait-port'
3
+
4
+ import { startSpinner, stopSpinner } from '../lib/spinner.mjs'
5
+
6
+ import { error, exit, log, NETLIFYDEVERR, NETLIFYDEVLOG } from './command-helpers.mjs'
7
+ import { runCommand } from './shell.mjs'
8
+ import { startStaticServer } from './static-server.mjs'
9
+
10
+ // 10 minutes
11
+ const FRAMEWORK_PORT_TIMEOUT = 6e5
12
+
13
+ /**
14
+ * @typedef StartReturnObject
15
+ * @property {4 | 6 | undefined=} ipVersion The version the open port was found on
16
+ */
17
+
18
+ /**
19
+ * Start a static server if the `useStaticServer` is provided or a framework specific server
20
+ * @param {object} config
21
+ * @param {Partial<import('./types').ServerSettings>} config.settings
22
+ * @returns {Promise<StartReturnObject>}
23
+ */
24
+ export const startFrameworkServer = async function ({ settings }) {
25
+ if (settings.useStaticServer) {
26
+ if (settings.command) {
27
+ runCommand(settings.command, settings.env)
28
+ }
29
+ await startStaticServer({ settings })
30
+
31
+ return {}
32
+ }
33
+
34
+ log(`${NETLIFYDEVLOG} Starting Netlify Dev with ${settings.framework || 'custom config'}`)
35
+
36
+ const spinner = startSpinner({
37
+ text: `Waiting for framework port ${settings.frameworkPort}. This can be configured using the 'targetPort' property in the netlify.toml`,
38
+ })
39
+
40
+ runCommand(settings.command, settings.env, spinner)
41
+
42
+ let port
43
+ try {
44
+ port = await waitPort({
45
+ port: settings.frameworkPort,
46
+ host: 'localhost',
47
+ output: 'silent',
48
+ timeout: FRAMEWORK_PORT_TIMEOUT,
49
+ ...(settings.pollingStrategies.includes('HTTP') && { protocol: 'http' }),
50
+ })
51
+
52
+ if (!port.open) {
53
+ throw new Error(`Timed out waiting for port '${settings.frameworkPort}' to be open`)
54
+ }
55
+
56
+ stopSpinner({ error: false, spinner })
57
+ } catch (error_) {
58
+ stopSpinner({ error: true, spinner })
59
+ log(NETLIFYDEVERR, `Netlify Dev could not start or connect to localhost:${settings.frameworkPort}.`)
60
+ log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`)
61
+ error(error_)
62
+ exit(1)
63
+ }
64
+
65
+ return { ipVersion: port?.ipVersion }
66
+ }
@@ -1,8 +1,12 @@
1
1
  // @ts-check
2
+ import { promises as fs } from 'fs'
2
3
  import { resolve } from 'path'
3
4
 
4
- import { isDirectoryAsync, isFileAsync } from '../../lib/fs.cjs'
5
- import { getPathInProject } from '../../lib/settings.cjs'
5
+ import { isDirectoryAsync, isFileAsync } from '../../lib/fs.mjs'
6
+ import { getPathInProject } from '../../lib/settings.mjs'
7
+
8
+ export const INTERNAL_FUNCTIONS_FOLDER = 'functions-internal'
9
+ export const SERVE_FUNCTIONS_FOLDER = 'functions-serve'
6
10
 
7
11
  /**
8
12
  * retrieves the function directory out of the flags or config
@@ -26,8 +30,20 @@ export const getFunctionsManifestPath = async ({ base }) => {
26
30
  return isFile ? path : null
27
31
  }
28
32
 
29
- export const getInternalFunctionsDir = async ({ base }) => {
30
- const path = resolve(base, getPathInProject(['functions-internal']))
33
+ export const getFunctionsDistPath = async ({ base }) => {
34
+ const path = resolve(base, getPathInProject(['functions']))
35
+ const isDirectory = await isDirectoryAsync(path)
36
+
37
+ return isDirectory ? path : null
38
+ }
39
+
40
+ export const getInternalFunctionsDir = async ({ base, ensureExists }) => {
41
+ const path = resolve(base, getPathInProject([INTERNAL_FUNCTIONS_FOLDER]))
42
+
43
+ if (ensureExists) {
44
+ await fs.mkdir(path, { recursive: true })
45
+ }
46
+
31
47
  const isDirectory = await isDirectoryAsync(path)
32
48
 
33
49
  return isDirectory ? path : null
@@ -1,5 +1,5 @@
1
1
  // @ts-check
2
- import { fileExistsAsync } from '../../lib/fs.cjs'
2
+ import { fileExistsAsync } from '../../lib/fs.mjs'
3
3
 
4
4
  const getUrlPath = (functionName) => `/.netlify/functions/${functionName}`
5
5
 
@@ -3,7 +3,7 @@ import { readFile } from 'fs/promises'
3
3
  import Configstore from 'configstore'
4
4
  import { v4 as uuidv4 } from 'uuid'
5
5
 
6
- import { getLegacyPathInHome, getPathInHome } from '../lib/settings.cjs'
6
+ import { getLegacyPathInHome, getPathInHome } from '../lib/settings.mjs'
7
7
 
8
8
  const globalConfigDefaults = {
9
9
  /* disable stats from being sent to Netlify */
@@ -4,7 +4,7 @@ import path from 'path'
4
4
 
5
5
  import parseIgnore from 'parse-gitignore'
6
6
 
7
- import { fileExistsAsync } from '../lib/fs.cjs'
7
+ import { fileExistsAsync } from '../lib/fs.mjs'
8
8
 
9
9
  import { log } from './command-helpers.mjs'
10
10
 
@@ -0,0 +1,170 @@
1
+ // @ts-check
2
+ import events from 'events'
3
+ import process from 'process'
4
+
5
+ import {
6
+ OneGraphCliClient,
7
+ loadCLISession,
8
+ markCliSessionInactive,
9
+ persistNewOperationsDocForSession,
10
+ startOneGraphCLISession,
11
+ } from '../lib/one-graph/cli-client.mjs'
12
+ import {
13
+ defaultExampleOperationsDoc,
14
+ getGraphEditUrlBySiteId,
15
+ getNetlifyGraphConfig,
16
+ readGraphQLOperationsSourceFile,
17
+ } from '../lib/one-graph/cli-netlify-graph.mjs'
18
+
19
+ import { chalk, error, getToken, log, normalizeConfig, warn, watchDebounced } from './command-helpers.mjs'
20
+ import { generateNetlifyGraphJWT, processOnExit } from './dev.mjs'
21
+ import { addCleanupJob } from './shell.mjs'
22
+
23
+ export const startPollingForAPIAuthentication = async function (options) {
24
+ const { api, command, config, site, siteInfo } = options
25
+ const frequency = 5000
26
+
27
+ const helper = async (maybeSiteData) => {
28
+ const siteData = await (maybeSiteData || api.getSite({ siteId: site.id }))
29
+ const authlifyTokenId = siteData && siteData.authlify_token_id
30
+
31
+ const existingAuthlifyTokenId = config && config.netlifyGraphConfig && config.netlifyGraphConfig.authlifyTokenId
32
+ if (authlifyTokenId && authlifyTokenId !== existingAuthlifyTokenId) {
33
+ const netlifyToken = await command.authenticate()
34
+ // Only inject the authlify config if a token ID exists. This prevents
35
+ // calling command.authenticate() (which opens a browser window) if the
36
+ // user hasn't enabled API Authentication
37
+ const netlifyGraphConfig = {
38
+ netlifyToken,
39
+ authlifyTokenId: siteData.authlify_token_id,
40
+ siteId: site.id,
41
+ }
42
+ config.netlifyGraphConfig = netlifyGraphConfig
43
+
44
+ const netlifyGraphJWT = generateNetlifyGraphJWT(netlifyGraphConfig)
45
+
46
+ if (netlifyGraphJWT != null) {
47
+ // XXX(anmonteiro): this name is deprecated. Delete after 3/31/2022
48
+ process.env.ONEGRAPH_AUTHLIFY_TOKEN = netlifyGraphJWT
49
+ process.env.NETLIFY_GRAPH_TOKEN = netlifyGraphJWT
50
+ }
51
+ } else if (!authlifyTokenId) {
52
+ // If there's no `authlifyTokenId`, it's because the user disabled API
53
+ // Auth. Delete the config in this case.
54
+ delete config.netlifyGraphConfig
55
+ }
56
+
57
+ setTimeout(helper, frequency)
58
+ }
59
+
60
+ await helper(siteInfo)
61
+ }
62
+
63
+ export const startNetlifyGraph = async ({
64
+ command,
65
+ config,
66
+ options,
67
+ settings,
68
+ site,
69
+ startNetlifyGraphWatcher,
70
+ state,
71
+ }) => {
72
+ if (startNetlifyGraphWatcher && options.offline) {
73
+ warn(`Unable to start Netlify Graph in offline mode`)
74
+ } else if (startNetlifyGraphWatcher && !site.id) {
75
+ error(
76
+ `No siteId defined, unable to start Netlify Graph. To enable, run ${chalk.yellow(
77
+ 'netlify init',
78
+ )} or ${chalk.yellow('netlify link')}.`,
79
+ )
80
+ } else if (startNetlifyGraphWatcher) {
81
+ const netlifyToken = await command.authenticate()
82
+ await OneGraphCliClient.ensureAppForSite(netlifyToken, site.id)
83
+
84
+ let stopWatchingCLISessions
85
+
86
+ let liveConfig = { ...config }
87
+ let isRestartingSession = false
88
+
89
+ const createOrResumeSession = async function () {
90
+ const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options, settings })
91
+
92
+ let graphqlDocument = readGraphQLOperationsSourceFile(netlifyGraphConfig)
93
+
94
+ if (!graphqlDocument || graphqlDocument.trim().length === 0) {
95
+ graphqlDocument = defaultExampleOperationsDoc
96
+ }
97
+
98
+ stopWatchingCLISessions = await startOneGraphCLISession({
99
+ config: liveConfig,
100
+ netlifyGraphConfig,
101
+ netlifyToken,
102
+ site,
103
+ state,
104
+ oneGraphSessionId: options.sessionId,
105
+ })
106
+
107
+ // Should be created by startOneGraphCLISession
108
+ const oneGraphSessionId = loadCLISession(state)
109
+
110
+ await persistNewOperationsDocForSession({
111
+ config: liveConfig,
112
+ netlifyGraphConfig,
113
+ netlifyToken,
114
+ oneGraphSessionId,
115
+ operationsDoc: graphqlDocument,
116
+ siteId: site.id,
117
+ siteRoot: site.root,
118
+ })
119
+
120
+ return oneGraphSessionId
121
+ }
122
+
123
+ const configWatcher = new events.EventEmitter()
124
+
125
+ // Only set up a watcher if we know the config path.
126
+ const { configPath } = command.netlify.site
127
+ if (configPath) {
128
+ // chokidar handle
129
+ command.configWatcherHandle = await watchDebounced(configPath, {
130
+ depth: 1,
131
+ onChange: async () => {
132
+ const cwd = options.cwd || process.cwd()
133
+ const [token] = await getToken(options.auth)
134
+ const { config: newConfig } = await command.getConfig({ cwd, state, token, ...command.netlify.apiUrlOpts })
135
+
136
+ const normalizedNewConfig = normalizeConfig(newConfig)
137
+ configWatcher.emit('change', normalizedNewConfig)
138
+ },
139
+ })
140
+
141
+ processOnExit(async () => {
142
+ await command.configWatcherHandle.close()
143
+ })
144
+ }
145
+
146
+ // Set up a handler for config changes.
147
+ configWatcher.on('change', async (newConfig) => {
148
+ command.netlify.config = newConfig
149
+ liveConfig = newConfig
150
+ if (isRestartingSession) {
151
+ return
152
+ }
153
+ stopWatchingCLISessions && stopWatchingCLISessions()
154
+ isRestartingSession = true
155
+ await createOrResumeSession()
156
+ isRestartingSession = false
157
+ })
158
+
159
+ const oneGraphSessionId = await createOrResumeSession()
160
+ const cleanupSession = () => markCliSessionInactive({ netlifyToken, sessionId: oneGraphSessionId, siteId: site.id })
161
+
162
+ addCleanupJob(cleanupSession)
163
+
164
+ const graphEditUrl = getGraphEditUrlBySiteId({ siteId: site.id, oneGraphSessionId })
165
+
166
+ log(
167
+ `Starting Netlify Graph session, to edit your library visit ${graphEditUrl} or run \`netlify graph:edit\` in another tab`,
168
+ )
169
+ }
170
+ }
@@ -6,7 +6,7 @@ import process from 'process'
6
6
  import cleanDeep from 'clean-deep'
7
7
  import inquirer from 'inquirer'
8
8
 
9
- import { fileExistsAsync } from '../../lib/fs.cjs'
9
+ import { fileExistsAsync } from '../../lib/fs.mjs'
10
10
  import { normalizeBackslash } from '../../lib/path.mjs'
11
11
  import { chalk, error as failAndExit, log, warn } from '../command-helpers.mjs'
12
12
 
@@ -5,7 +5,7 @@ import fetch from 'node-fetch'
5
5
  import pWaitFor from 'p-wait-for'
6
6
 
7
7
  import { fetchLatestVersion, shouldFetchLatestVersion } from '../lib/exec-fetcher.mjs'
8
- import { getPathInHome } from '../lib/settings.cjs'
8
+ import { getPathInHome } from '../lib/settings.mjs'
9
9
 
10
10
  import { NETLIFYDEVERR, NETLIFYDEVLOG, chalk, log } from './command-helpers.mjs'
11
11
  import execa from './execa.mjs'
@@ -11,9 +11,9 @@ import Listr from 'listr'
11
11
  import pathKey from 'path-key'
12
12
 
13
13
  import { fetchLatestVersion, shouldFetchLatestVersion } from '../../lib/exec-fetcher.mjs'
14
- import { fileExistsAsync } from '../../lib/fs.cjs'
14
+ import { fileExistsAsync } from '../../lib/fs.mjs'
15
15
  import { normalizeBackslash } from '../../lib/path.mjs'
16
- import { getLegacyPathInHome, getPathInHome } from '../../lib/settings.cjs'
16
+ import { getLegacyPathInHome, getPathInHome } from '../../lib/settings.mjs'
17
17
  import { chalk } from '../command-helpers.mjs'
18
18
 
19
19
  import { checkGitLFSVersionStep, checkGitVersionStep, checkLFSFiltersStep } from './steps.mjs'
@@ -0,0 +1,90 @@
1
+ // @ts-check
2
+ import { exit, log, NETLIFYDEVERR } from './command-helpers.mjs'
3
+ import { startProxy } from './proxy.mjs'
4
+
5
+ /**
6
+ * @typedef {Object} InspectSettings
7
+ * @property {boolean} enabled - Inspect enabled
8
+ * @property {boolean} pause - Pause on breakpoints
9
+ * @property {string|undefined} address - Host/port override (optional)
10
+ */
11
+
12
+ /**
13
+ * @param {boolean|string} edgeInspect
14
+ * @param {boolean|string} edgeInspectBrk
15
+ * @returns {InspectSettings}
16
+ */
17
+ export const generateInspectSettings = (edgeInspect, edgeInspectBrk) => {
18
+ const enabled = Boolean(edgeInspect) || Boolean(edgeInspectBrk)
19
+ const pause = Boolean(edgeInspectBrk)
20
+ const getAddress = () => {
21
+ if (edgeInspect) {
22
+ return typeof edgeInspect === 'string' ? edgeInspect : undefined
23
+ }
24
+ if (edgeInspectBrk) {
25
+ return typeof edgeInspectBrk === 'string' ? edgeInspectBrk : undefined
26
+ }
27
+ }
28
+
29
+ return {
30
+ enabled,
31
+ pause,
32
+ address: getAddress(),
33
+ }
34
+ }
35
+
36
+ /**
37
+ *
38
+ * @param {object} params
39
+ * @param {*} params.addonsUrls
40
+ * @param {import('../commands/base-command.mjs').NetlifyOptions["config"]} params.config
41
+ * @param {string} [params.configPath] An override for the Netlify config path
42
+ * @param {import('../commands/base-command.mjs').NetlifyOptions["cachedConfig"]['env']} params.env
43
+ * @param {InspectSettings} params.inspectSettings
44
+ * @param {() => Promise<object>} params.getUpdatedConfig
45
+ * @param {string} params.geolocationMode
46
+ * @param {string} params.geoCountry
47
+ * @param {*} params.settings
48
+ * @param {boolean} params.offline
49
+ * @param {*} params.site
50
+ * @param {*} params.siteInfo
51
+ * @param {import('./state-config.mjs').default} params.state
52
+ * @returns
53
+ */
54
+ export const startProxyServer = async ({
55
+ addonsUrls,
56
+ config,
57
+ configPath,
58
+ env,
59
+ geoCountry,
60
+ geolocationMode,
61
+ getUpdatedConfig,
62
+ inspectSettings,
63
+ offline,
64
+ settings,
65
+ site,
66
+ siteInfo,
67
+ state,
68
+ }) => {
69
+ const url = await startProxy({
70
+ addonsUrls,
71
+ config,
72
+ configPath: configPath || site.configPath,
73
+ env,
74
+ geolocationMode,
75
+ geoCountry,
76
+ getUpdatedConfig,
77
+ inspectSettings,
78
+ offline,
79
+ projectDir: site.root,
80
+ settings,
81
+ state,
82
+ siteInfo,
83
+ })
84
+ if (!url) {
85
+ log(NETLIFYDEVERR, `Unable to start proxy server on port '${settings.port}'`)
86
+ exit(1)
87
+ }
88
+
89
+ return url
90
+ }
@@ -25,7 +25,7 @@ import {
25
25
  initializeProxy as initializeEdgeFunctionsProxy,
26
26
  isEdgeFunctionsRequest,
27
27
  } from '../lib/edge-functions/proxy.mjs'
28
- import { fileExistsAsync, isFileAsync } from '../lib/fs.cjs'
28
+ import { fileExistsAsync, isFileAsync } from '../lib/fs.mjs'
29
29
  import renderErrorTemplate from '../lib/render-error-template.mjs'
30
30
 
31
31
  import { NETLIFYDEVLOG, NETLIFYDEVWARN } from './command-helpers.mjs'
@@ -6,7 +6,7 @@ import cookie from 'cookie'
6
6
  import redirector from 'netlify-redirector'
7
7
  import pFilter from 'p-filter'
8
8
 
9
- import { fileExistsAsync } from '../lib/fs.cjs'
9
+ import { fileExistsAsync } from '../lib/fs.mjs'
10
10
 
11
11
  import { NETLIFYDEVLOG } from './command-helpers.mjs'
12
12
  import { parseRedirects } from './redirects.mjs'