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.
- package/README.md +5 -0
- package/npm-shrinkwrap.json +1075 -634
- package/package.json +7 -160
- package/src/commands/base-command.mjs +16 -5
- package/src/commands/deploy/deploy.mjs +1 -1
- package/src/commands/dev/dev.mjs +27 -511
- package/src/commands/env/env-list.mjs +1 -0
- package/src/commands/functions/functions-create.mjs +35 -25
- package/src/commands/functions/functions-serve.mjs +5 -1
- package/src/commands/main.mjs +2 -0
- package/src/commands/serve/serve.mjs +189 -0
- package/src/commands/sites/sites-list.mjs +1 -1
- package/src/commands/watch/watch.mjs +1 -1
- package/src/functions-templates/go/hello-world/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/apollo-graphql/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/apollo-graphql-rest/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/auth-fetch/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/create-user/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/fauna-crud/{.netlify-function-template.cjs → .netlify-function-template.mjs} +3 -2
- package/src/functions-templates/javascript/fauna-graphql/{.netlify-function-template.cjs → .netlify-function-template.mjs} +3 -2
- package/src/functions-templates/javascript/google-analytics/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/graphql-gateway/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/hasura-event-triggered/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/hello/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/{typescript/hello-world/.netlify-function-template.cjs → javascript/hello-world/.netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/identity-signup/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/image-external/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/localized-content/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/node-fetch/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/oauth-passport/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/oauth-passport/package.json +1 -1
- package/src/functions-templates/javascript/protected-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/sanity-create/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/sanity-groq/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/scheduled-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/scheduled-function/package.json +1 -1
- package/src/functions-templates/javascript/send-email/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/serverless-ssr/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/set-cookie/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/set-cookies/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/{typescript/set-req-header/.netlify-function-template.cjs → javascript/set-req-header/.netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/set-res-header/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/slack-rate-limit/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/stripe-charge/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
- package/src/functions-templates/javascript/stripe-subscription/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
- package/src/functions-templates/javascript/submission-created/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/token-hider/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
- package/src/functions-templates/{typescript/transform-response/.netlify-function-template.cjs → javascript/transform-response/.netlify-function-template.mjs} +1 -1
- package/src/functions-templates/javascript/url-shortener/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
- package/src/functions-templates/javascript/using-middleware/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/rust/hello-world/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/abtest/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/geolocation/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/geolocation/{{name}}.ts +1 -1
- package/src/functions-templates/{javascript/hello-world/.netlify-function-template.cjs → typescript/hello-world/.netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/hello-world/package-lock.json +7 -7
- package/src/functions-templates/typescript/hello-world/package.json +1 -1
- package/src/functions-templates/typescript/json/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/json/{{name}}.ts +1 -1
- package/src/functions-templates/typescript/log/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/log/{{name}}.ts +2 -2
- package/src/functions-templates/typescript/scheduled-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/scheduled-function/package.json +1 -1
- package/src/functions-templates/typescript/set-cookies/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/{javascript/set-req-header/.netlify-function-template.cjs → typescript/set-req-header/.netlify-function-template.mjs} +1 -1
- package/src/functions-templates/typescript/set-res-header/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
- package/src/functions-templates/{javascript/transform-response/.netlify-function-template.cjs → typescript/transform-response/.netlify-function-template.mjs} +1 -1
- package/src/lib/completion/constants.mjs +1 -1
- package/src/lib/edge-functions/deploy.mjs +1 -1
- package/src/lib/edge-functions/internal.mjs +1 -1
- package/src/lib/edge-functions/proxy.mjs +2 -2
- package/src/lib/edge-functions/registry.mjs +2 -5
- package/src/lib/{fs.cjs → fs.mjs} +5 -13
- package/src/lib/functions/registry.mjs +55 -33
- package/src/lib/functions/runtimes/js/builders/netlify-lambda.mjs +1 -1
- package/src/lib/functions/runtimes/js/builders/zisi.mjs +3 -2
- package/src/lib/functions/runtimes/rust/index.mjs +3 -2
- package/src/lib/functions/server.mjs +35 -18
- package/src/lib/{settings.cjs → settings.mjs} +6 -8
- package/src/lib/{spinner.cjs → spinner.mjs} +5 -7
- package/src/utils/banner.mjs +17 -0
- package/src/utils/command-helpers.mjs +1 -1
- package/src/utils/detect-server-settings.mjs +35 -1
- package/src/utils/dev.mjs +8 -5
- package/src/utils/dot-env.mjs +1 -1
- package/src/utils/framework-server.mjs +66 -0
- package/src/utils/functions/functions.mjs +20 -4
- package/src/utils/functions/get-functions.mjs +1 -1
- package/src/utils/get-global-config.mjs +1 -1
- package/src/utils/gitignore.mjs +1 -1
- package/src/utils/graph.mjs +170 -0
- package/src/utils/init/utils.mjs +1 -1
- package/src/utils/live-tunnel.mjs +1 -1
- package/src/utils/lm/install.mjs +2 -2
- package/src/utils/proxy-server.mjs +90 -0
- package/src/utils/proxy.mjs +1 -1
- package/src/utils/rules-proxy.mjs +1 -1
- package/src/utils/run-build.mjs +129 -0
- package/src/utils/shell.mjs +120 -0
- package/src/utils/state-config.mjs +1 -1
- package/src/utils/static-server.mjs +34 -0
- package/src/utils/validation.mjs +15 -0
package/src/commands/dev/dev.mjs
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
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(
|
|
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')
|