netlify-cli 8.0.13 → 8.1.0-rc.2
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 +12 -13
- package/bin/run +38 -2
- package/npm-shrinkwrap.json +260 -6244
- package/package.json +13 -35
- package/src/commands/addons/addons-auth.js +50 -0
- package/src/commands/addons/addons-config.js +180 -0
- package/src/commands/addons/addons-create.js +131 -0
- package/src/commands/addons/addons-delete.js +60 -0
- package/src/commands/addons/addons-list.js +62 -0
- package/src/commands/addons/addons.js +44 -0
- package/src/commands/addons/index.js +3 -24
- package/src/commands/api/api.js +75 -0
- package/src/commands/api/index.js +5 -0
- package/src/commands/base-command.js +509 -0
- package/src/commands/build/build.js +58 -0
- package/src/commands/build/index.js +3 -61
- package/src/commands/completion/completion.js +36 -0
- package/src/commands/completion/index.js +5 -0
- package/src/commands/{deploy.js → deploy/deploy.js} +295 -275
- package/src/commands/deploy/index.js +5 -0
- package/src/commands/dev/dev-exec.js +35 -0
- package/src/commands/dev/dev-trace.js +47 -0
- package/src/commands/dev/dev.js +340 -0
- package/src/commands/dev/index.js +3 -335
- package/src/commands/env/env-get.js +51 -0
- package/src/commands/env/env-import.js +93 -0
- package/src/commands/env/env-list.js +63 -0
- package/src/commands/env/env-set.js +67 -0
- package/src/commands/env/env-unset.js +66 -0
- package/src/commands/env/env.js +42 -0
- package/src/commands/env/index.js +3 -23
- package/src/commands/functions/functions-build.js +59 -0
- package/src/commands/functions/{create.js → functions-create.js} +130 -94
- package/src/commands/functions/functions-invoke.js +273 -0
- package/src/commands/functions/functions-list.js +106 -0
- package/src/commands/functions/functions-serve.js +63 -0
- package/src/commands/functions/functions.js +47 -0
- package/src/commands/functions/index.js +3 -45
- package/src/commands/index.js +7 -0
- package/src/commands/init/index.js +6 -0
- package/src/commands/{init.js → init/init.js} +79 -68
- package/src/commands/link/index.js +6 -0
- package/src/{utils/link/link-by-prompt.js → commands/link/link.js} +141 -14
- package/src/commands/lm/index.js +3 -19
- package/src/commands/lm/lm-info.js +42 -0
- package/src/commands/lm/lm-install.js +33 -0
- package/src/commands/lm/lm-setup.js +106 -0
- package/src/commands/lm/lm-uninstall.js +25 -0
- package/src/commands/lm/lm.js +34 -0
- package/src/commands/login/index.js +6 -0
- package/src/commands/login/login.js +55 -0
- package/src/commands/logout/index.js +5 -0
- package/src/commands/logout/logout.js +43 -0
- package/src/commands/main.js +188 -0
- package/src/commands/open/index.js +3 -39
- package/src/commands/open/open-admin.js +60 -0
- package/src/commands/open/open-site.js +53 -0
- package/src/commands/open/open.js +38 -0
- package/src/commands/sites/index.js +5 -20
- package/src/commands/sites/sites-create.js +187 -0
- package/src/commands/sites/sites-delete.js +104 -0
- package/src/commands/sites/sites-list.js +89 -0
- package/src/commands/sites/sites.js +32 -0
- package/src/commands/status/index.js +3 -118
- package/src/commands/status/status-hooks.js +69 -0
- package/src/commands/status/status.js +124 -0
- package/src/commands/switch/index.js +5 -0
- package/src/commands/switch/switch.js +50 -0
- package/src/commands/unlink/index.js +5 -0
- package/src/commands/unlink/unlink.js +48 -0
- package/src/commands/watch/index.js +5 -0
- package/src/commands/watch/watch.js +121 -0
- package/src/lib/build.js +21 -7
- package/src/lib/exec-fetcher.js +5 -3
- package/src/lib/fs.js +54 -36
- package/src/lib/functions/background.js +1 -1
- package/src/lib/functions/form-submissions-handler.js +2 -1
- package/src/lib/functions/local-proxy.js +2 -1
- package/src/lib/functions/netlify-function.js +4 -1
- package/src/lib/functions/registry.js +4 -6
- package/src/lib/functions/runtimes/go/index.js +2 -1
- package/src/lib/functions/runtimes/js/builders/netlify-lambda.js +6 -4
- package/src/lib/functions/runtimes/js/builders/zisi.js +3 -3
- package/src/lib/functions/runtimes/rust/index.js +4 -3
- package/src/lib/functions/server.js +2 -3
- package/src/lib/functions/synchronous.js +2 -1
- package/src/lib/functions/utils.js +2 -3
- package/src/lib/functions/watcher.js +1 -0
- package/src/lib/http-agent.js +5 -5
- package/src/lib/log.js +2 -1
- package/src/lib/spinner.js +22 -0
- package/src/utils/addons/diffs/index.js +1 -0
- package/src/utils/addons/diffs/options.js +3 -1
- package/src/utils/addons/prepare.js +13 -6
- package/src/utils/addons/prompts.js +2 -1
- package/src/utils/addons/render.js +3 -1
- package/src/utils/command-helpers.js +116 -43
- package/src/utils/create-stream-promise.js +5 -5
- package/src/utils/deferred.js +1 -0
- package/src/utils/deploy/deploy-site.js +1 -1
- package/src/utils/deploy/index.js +4 -0
- package/src/utils/detect-server-settings.js +10 -12
- package/src/utils/dev.js +18 -10
- package/src/utils/dot-env.js +4 -2
- package/src/utils/{edge-handlers.js → functions/edge-handlers.js} +8 -7
- package/src/utils/functions/functions.js +36 -0
- package/src/utils/{get-functions.js → functions/get-functions.js} +2 -1
- package/src/utils/functions/index.js +8 -26
- package/src/utils/get-global-config.js +3 -2
- package/src/utils/get-repo-data.js +1 -0
- package/src/utils/gh-auth.js +1 -0
- package/src/utils/gitignore.js +7 -5
- package/src/utils/headers.js +1 -2
- package/src/utils/index.js +42 -0
- package/src/utils/init/config-github.js +12 -5
- package/src/utils/init/config-manual.js +9 -2
- package/src/utils/init/config.js +13 -7
- package/src/utils/init/frameworks.js +1 -0
- package/src/utils/init/node-version.js +4 -2
- package/src/utils/init/plugins.js +2 -4
- package/src/utils/init/utils.js +10 -6
- package/src/utils/live-tunnel.js +3 -4
- package/src/utils/lm/install.js +10 -15
- package/src/utils/lm/requirements.js +3 -1
- package/src/utils/lm/steps.js +1 -1
- package/src/utils/lm/ui.js +7 -3
- package/src/utils/open-browser.js +8 -2
- package/src/utils/parse-raw-flags.js +4 -4
- package/src/utils/proxy.js +6 -5
- package/src/utils/read-repo-url.js +1 -0
- package/src/utils/redirects.js +3 -5
- package/src/utils/rules-proxy.js +2 -1
- package/src/utils/state-config.js +1 -1
- package/src/utils/telemetry/index.js +2 -113
- package/src/utils/telemetry/request.js +3 -1
- package/src/utils/telemetry/telemetry.js +117 -0
- package/src/utils/telemetry/validation.js +13 -12
- package/src/utils/traffic-mesh.js +3 -3
- package/oclif.manifest.json +0 -1
- package/src/commands/addons/auth.js +0 -42
- package/src/commands/addons/config.js +0 -177
- package/src/commands/addons/create.js +0 -127
- package/src/commands/addons/delete.js +0 -69
- package/src/commands/addons/list.js +0 -54
- package/src/commands/api.js +0 -84
- package/src/commands/dev/exec.js +0 -32
- package/src/commands/dev/trace.js +0 -61
- package/src/commands/env/get.js +0 -44
- package/src/commands/env/import.js +0 -90
- package/src/commands/env/list.js +0 -49
- package/src/commands/env/set.js +0 -64
- package/src/commands/env/unset.js +0 -58
- package/src/commands/functions/build.js +0 -60
- package/src/commands/functions/invoke.js +0 -277
- package/src/commands/functions/list.js +0 -102
- package/src/commands/functions/serve.js +0 -70
- package/src/commands/link.js +0 -133
- package/src/commands/lm/info.js +0 -36
- package/src/commands/lm/install.js +0 -30
- package/src/commands/lm/setup.js +0 -107
- package/src/commands/lm/uninstall.js +0 -17
- package/src/commands/login.js +0 -54
- package/src/commands/logout.js +0 -37
- package/src/commands/open/admin.js +0 -51
- package/src/commands/open/site.js +0 -43
- package/src/commands/sites/create.js +0 -191
- package/src/commands/sites/delete.js +0 -116
- package/src/commands/sites/list.js +0 -84
- package/src/commands/status/hooks.js +0 -60
- package/src/commands/switch.js +0 -44
- package/src/commands/unlink.js +0 -38
- package/src/commands/watch.js +0 -115
- package/src/hooks/init.js +0 -46
- package/src/index.js +0 -25
- package/src/lib/help.js +0 -26
- package/src/utils/chalk.js +0 -16
- package/src/utils/check-command-inputs.js +0 -21
- package/src/utils/command.js +0 -261
- package/src/utils/detect-functions-builder.js +0 -25
- package/src/utils/difference.js +0 -4
- package/src/utils/header.js +0 -18
- package/src/utils/logo.js +0 -11
- package/src/utils/show-help.js +0 -5
- package/src/utils/telemetry/tracked-command.js +0 -51
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const process = require('process')
|
|
5
|
+
|
|
6
|
+
const inquirer = require('inquirer')
|
|
7
|
+
const fetch = require('node-fetch')
|
|
8
|
+
|
|
9
|
+
const { BACKGROUND, NETLIFYDEVWARN, chalk, error, exit, getFunctions } = require('../../utils')
|
|
10
|
+
|
|
11
|
+
// https://www.netlify.com/docs/functions/#event-triggered-functions
|
|
12
|
+
const events = [
|
|
13
|
+
'deploy-building',
|
|
14
|
+
'deploy-succeeded',
|
|
15
|
+
'deploy-failed',
|
|
16
|
+
'deploy-locked',
|
|
17
|
+
'deploy-unlocked',
|
|
18
|
+
'split-test-activated',
|
|
19
|
+
'split-test-deactivated',
|
|
20
|
+
'split-test-modified',
|
|
21
|
+
'submission-created',
|
|
22
|
+
'identity-validate',
|
|
23
|
+
'identity-signup',
|
|
24
|
+
'identity-login',
|
|
25
|
+
]
|
|
26
|
+
const eventTriggeredFunctions = new Set([...events, ...events.map((name) => `${name}${BACKGROUND}`)])
|
|
27
|
+
|
|
28
|
+
const DEFAULT_PORT = 8888
|
|
29
|
+
|
|
30
|
+
// https://stackoverflow.com/questions/3710204/how-to-check-if-a-string-is-a-valid-json-string-in-javascript-without-using-try
|
|
31
|
+
const tryParseJSON = function (jsonString) {
|
|
32
|
+
try {
|
|
33
|
+
const parsedValue = JSON.parse(jsonString)
|
|
34
|
+
|
|
35
|
+
// Handle non-exception-throwing cases:
|
|
36
|
+
// Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
|
|
37
|
+
// but... JSON.parse(null) returns null, and typeof null === "object",
|
|
38
|
+
// so we must check for that, too. Thankfully, null is falsey, so this suffices:
|
|
39
|
+
if (parsedValue && typeof parsedValue === 'object') {
|
|
40
|
+
return parsedValue
|
|
41
|
+
}
|
|
42
|
+
} catch {}
|
|
43
|
+
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const formatQstring = function (querystring) {
|
|
48
|
+
if (querystring) {
|
|
49
|
+
return `?${querystring}`
|
|
50
|
+
}
|
|
51
|
+
return ''
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** process payloads from flag */
|
|
55
|
+
const processPayloadFromFlag = function (payloadString) {
|
|
56
|
+
if (payloadString) {
|
|
57
|
+
// case 1: jsonstring
|
|
58
|
+
let payload = tryParseJSON(payloadString)
|
|
59
|
+
if (payload) return payload
|
|
60
|
+
// case 2: jsonpath
|
|
61
|
+
const payloadpath = path.join(process.cwd(), payloadString)
|
|
62
|
+
const pathexists = fs.existsSync(payloadpath)
|
|
63
|
+
if (pathexists) {
|
|
64
|
+
try {
|
|
65
|
+
// there is code execution potential here
|
|
66
|
+
// eslint-disable-next-line node/global-require, import/no-dynamic-require
|
|
67
|
+
payload = require(payloadpath)
|
|
68
|
+
return payload
|
|
69
|
+
} catch (error_) {
|
|
70
|
+
console.error(error_)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// case 3: invalid string, invalid path
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* prompt for a name if name not supplied
|
|
80
|
+
* also used in functions:create
|
|
81
|
+
* @param {*} functions
|
|
82
|
+
* @param {import('commander').OptionValues} options
|
|
83
|
+
* @param {string} [argumentName] The name that might be provided as argument (optional argument)
|
|
84
|
+
* @returns {Promise<string>}
|
|
85
|
+
*/
|
|
86
|
+
const getNameFromArgs = async function (functions, options, argumentName) {
|
|
87
|
+
const functionToTrigger = getFunctionToTrigger(options, argumentName)
|
|
88
|
+
const functionNames = functions.map(({ name }) => name)
|
|
89
|
+
|
|
90
|
+
if (functionToTrigger) {
|
|
91
|
+
if (functionNames.includes(functionToTrigger)) {
|
|
92
|
+
return functionToTrigger
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.warn(
|
|
96
|
+
`Function name ${chalk.yellow(
|
|
97
|
+
functionToTrigger,
|
|
98
|
+
)} supplied but no matching function found in your functions folder, forcing you to pick a valid one...`,
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const { trigger } = await inquirer.prompt([
|
|
103
|
+
{
|
|
104
|
+
type: 'list',
|
|
105
|
+
message: 'Pick a function to trigger',
|
|
106
|
+
name: 'trigger',
|
|
107
|
+
choices: functionNames,
|
|
108
|
+
},
|
|
109
|
+
])
|
|
110
|
+
return trigger
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* get the function name out of the argument or options
|
|
115
|
+
* @param {import('commander').OptionValues} options
|
|
116
|
+
* @param {string} [argumentName] The name that might be provided as argument (optional argument)
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
const getFunctionToTrigger = function (options, argumentName) {
|
|
120
|
+
if (options.name) {
|
|
121
|
+
if (argumentName) {
|
|
122
|
+
console.error('function name specified in both flag and arg format, pick one')
|
|
123
|
+
exit(1)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return options.name
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return argumentName
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The functions:invoke command
|
|
134
|
+
* @param {string} nameArgument
|
|
135
|
+
* @param {import('commander').OptionValues} options
|
|
136
|
+
* @param {import('../base-command').BaseCommand} command
|
|
137
|
+
*/
|
|
138
|
+
const functionsInvoke = async (nameArgument, options, command) => {
|
|
139
|
+
const { config } = command.netlify
|
|
140
|
+
|
|
141
|
+
const functionsDir = options.functions || (config.dev && config.dev.functions) || config.functionsDirectory
|
|
142
|
+
if (typeof functionsDir === 'undefined') {
|
|
143
|
+
error('functions directory is undefined, did you forget to set it in netlify.toml?')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!options.port)
|
|
147
|
+
console.warn(`${NETLIFYDEVWARN} "port" flag was not specified. Attempting to connect to localhost:8888 by default`)
|
|
148
|
+
const port = options.port || DEFAULT_PORT
|
|
149
|
+
|
|
150
|
+
const functions = await getFunctions(functionsDir)
|
|
151
|
+
const functionToTrigger = await getNameFromArgs(functions, options, nameArgument)
|
|
152
|
+
|
|
153
|
+
let headers = {}
|
|
154
|
+
let body = {}
|
|
155
|
+
|
|
156
|
+
if (eventTriggeredFunctions.has(functionToTrigger)) {
|
|
157
|
+
/** handle event triggered fns */
|
|
158
|
+
// https://www.netlify.com/docs/functions/#event-triggered-functions
|
|
159
|
+
const [name, event] = functionToTrigger.split('-')
|
|
160
|
+
if (name === 'identity') {
|
|
161
|
+
// https://www.netlify.com/docs/functions/#identity-event-functions
|
|
162
|
+
body.event = event
|
|
163
|
+
body.user = {
|
|
164
|
+
id: '1111a1a1-a11a-1111-aa11-aaa11111a11a',
|
|
165
|
+
aud: '',
|
|
166
|
+
role: '',
|
|
167
|
+
email: 'foo@trust-this-company.com',
|
|
168
|
+
app_metadata: {
|
|
169
|
+
provider: 'email',
|
|
170
|
+
},
|
|
171
|
+
user_metadata: {
|
|
172
|
+
full_name: 'Test Person',
|
|
173
|
+
},
|
|
174
|
+
created_at: new Date(Date.now()).toISOString(),
|
|
175
|
+
update_at: new Date(Date.now()).toISOString(),
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
// non identity functions seem to have a different shape
|
|
179
|
+
// https://www.netlify.com/docs/functions/#event-function-payloads
|
|
180
|
+
body.payload = {
|
|
181
|
+
TODO: 'mock up payload data better',
|
|
182
|
+
}
|
|
183
|
+
body.site = {
|
|
184
|
+
TODO: 'mock up site data better',
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
// NOT an event triggered function, but may still want to simulate authentication locally
|
|
189
|
+
let isAuthenticated = false
|
|
190
|
+
if (typeof options.identity === 'undefined') {
|
|
191
|
+
const { isAuthed } = await inquirer.prompt([
|
|
192
|
+
{
|
|
193
|
+
type: 'confirm',
|
|
194
|
+
name: 'isAuthed',
|
|
195
|
+
message: `Invoke with emulated Netlify Identity authentication headers? (pass --identity/--no-identity to override)`,
|
|
196
|
+
default: true,
|
|
197
|
+
},
|
|
198
|
+
])
|
|
199
|
+
isAuthenticated = isAuthed
|
|
200
|
+
} else {
|
|
201
|
+
isAuthenticated = options.identity
|
|
202
|
+
}
|
|
203
|
+
if (isAuthenticated) {
|
|
204
|
+
headers = {
|
|
205
|
+
authorization:
|
|
206
|
+
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb3VyY2UiOiJuZXRsaWZ5IGZ1bmN0aW9uczp0cmlnZ2VyIiwidGVzdERhdGEiOiJORVRMSUZZX0RFVl9MT0NBTExZX0VNVUxBVEVEX0pXVCJ9.Xb6vOFrfLUZmyUkXBbCvU4bM7q8tPilF0F03Wupap_c',
|
|
207
|
+
}
|
|
208
|
+
// you can decode this https://jwt.io/
|
|
209
|
+
// {
|
|
210
|
+
// "source": "netlify functions:trigger",
|
|
211
|
+
// "testData": "NETLIFY_DEV_LOCALLY_EMULATED_JWT"
|
|
212
|
+
// }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const payload = processPayloadFromFlag(options.payload)
|
|
216
|
+
body = { ...body, ...payload }
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetch(
|
|
220
|
+
`http://localhost:${port}/.netlify/functions/${functionToTrigger}${formatQstring(options.querystring)}`,
|
|
221
|
+
{
|
|
222
|
+
method: 'post',
|
|
223
|
+
headers,
|
|
224
|
+
body: JSON.stringify(body),
|
|
225
|
+
},
|
|
226
|
+
)
|
|
227
|
+
const data = await response.text()
|
|
228
|
+
console.log(data)
|
|
229
|
+
} catch (error_) {
|
|
230
|
+
error(`Ran into an error invoking your function: ${error_.message}`)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Creates the `netlify functions:invoke` command
|
|
236
|
+
* @param {import('../base-command').BaseCommand} program
|
|
237
|
+
* @returns
|
|
238
|
+
*/
|
|
239
|
+
const createFunctionsInvokeCommand = (program) =>
|
|
240
|
+
program
|
|
241
|
+
.command('functions:invoke')
|
|
242
|
+
.alias('function:trigger')
|
|
243
|
+
.argument('[name]', 'function name to invoke')
|
|
244
|
+
.description(
|
|
245
|
+
`Trigger a function while in netlify dev with simulated data, good for testing function calls including Netlify's Event Triggered Functions`,
|
|
246
|
+
)
|
|
247
|
+
.option('-n, --name <name>', 'function name to invoke')
|
|
248
|
+
.option('-f, --functions <dir>', 'Specify a functions folder to parse, overriding netlify.toml')
|
|
249
|
+
.option('-q, --querystring <query>', 'Querystring to add to your function invocation')
|
|
250
|
+
.option('-p, --payload <data>', 'Supply POST payload in stringified json, or a path to a json file')
|
|
251
|
+
// TODO: refactor to not need the `undefined` state by removing the --identity flag (value `identity` will be then always defined to true or false)
|
|
252
|
+
.option(
|
|
253
|
+
'--identity',
|
|
254
|
+
'simulate Netlify Identity authentication JWT. pass --identity to affirm unauthenticated request',
|
|
255
|
+
)
|
|
256
|
+
.option(
|
|
257
|
+
'--no-identity',
|
|
258
|
+
'simulate Netlify Identity authentication JWT. pass --no-identity to affirm unauthenticated request',
|
|
259
|
+
)
|
|
260
|
+
.option('--port <port>', 'Port where netlify dev is accessible. e.g. 8888', (value) => Number.parseInt(value))
|
|
261
|
+
.addExamples([
|
|
262
|
+
'netlify functions:invoke',
|
|
263
|
+
'netlify functions:invoke myfunction',
|
|
264
|
+
'netlify functions:invoke --name myfunction',
|
|
265
|
+
'netlify functions:invoke --name myfunction --identity',
|
|
266
|
+
'netlify functions:invoke --name myfunction --no-identity',
|
|
267
|
+
`netlify functions:invoke myfunction --payload '{"foo": 1}'`,
|
|
268
|
+
'netlify functions:invoke myfunction --querystring "foo=1',
|
|
269
|
+
'netlify functions:invoke myfunction --payload "./pathTo.json"',
|
|
270
|
+
])
|
|
271
|
+
.action(functionsInvoke)
|
|
272
|
+
|
|
273
|
+
module.exports = { createFunctionsInvokeCommand }
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
const AsciiTable = require('ascii-table')
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
error,
|
|
7
|
+
exit,
|
|
8
|
+
|
|
9
|
+
getFunctions,
|
|
10
|
+
getFunctionsDir,
|
|
11
|
+
log,
|
|
12
|
+
logJson,
|
|
13
|
+
warn,
|
|
14
|
+
} = require('../../utils')
|
|
15
|
+
|
|
16
|
+
const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) {
|
|
17
|
+
const isDeployed = deployedFunctions.some((deployedFunction) => deployedFunction.n === name)
|
|
18
|
+
return { name, url, isDeployed }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The functions:list command
|
|
23
|
+
* @param {import('commander').OptionValues} options
|
|
24
|
+
* @param {import('../base-command').BaseCommand} command
|
|
25
|
+
*/
|
|
26
|
+
const functionsList = async (options, command) => {
|
|
27
|
+
const { api, config, site } = command.netlify
|
|
28
|
+
|
|
29
|
+
// get deployed site details
|
|
30
|
+
// copied from `netlify status`
|
|
31
|
+
const siteId = site.id
|
|
32
|
+
if (!siteId) {
|
|
33
|
+
warn('Did you run `netlify link` yet?')
|
|
34
|
+
error(`You don't appear to be in a folder that is linked to a site`)
|
|
35
|
+
}
|
|
36
|
+
let siteData
|
|
37
|
+
try {
|
|
38
|
+
siteData = await api.getSite({ siteId })
|
|
39
|
+
} catch (error_) {
|
|
40
|
+
// unauthorized
|
|
41
|
+
if (error_.status === 401) {
|
|
42
|
+
warn(`Log in with a different account or re-link to a site you have permission for`)
|
|
43
|
+
error(`Not authorized to view the currently linked site (${siteId})`)
|
|
44
|
+
}
|
|
45
|
+
// missing
|
|
46
|
+
if (error_.status === 404) {
|
|
47
|
+
error(`The site this folder is linked to can't be found`)
|
|
48
|
+
}
|
|
49
|
+
error(error_)
|
|
50
|
+
}
|
|
51
|
+
const deploy = siteData.published_deploy || {}
|
|
52
|
+
const deployedFunctions = deploy.available_functions || []
|
|
53
|
+
|
|
54
|
+
const functionsDir = getFunctionsDir({ options, config })
|
|
55
|
+
|
|
56
|
+
if (typeof functionsDir === 'undefined') {
|
|
57
|
+
log('Functions directory is undefined')
|
|
58
|
+
log('Please verify functions.directory is set in your Netlify configuration file (netlify.toml/yml/json)')
|
|
59
|
+
log('See https://docs.netlify.com/configure-builds/file-based-configuration/ for more information')
|
|
60
|
+
exit(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const functions = await getFunctions(functionsDir)
|
|
64
|
+
const normalizedFunctions = functions.map(normalizeFunction.bind(null, deployedFunctions))
|
|
65
|
+
|
|
66
|
+
if (normalizedFunctions.length === 0) {
|
|
67
|
+
log(`No functions found in ${functionsDir}`)
|
|
68
|
+
exit()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (options.json) {
|
|
72
|
+
logJson(normalizedFunctions)
|
|
73
|
+
exit()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Make table
|
|
77
|
+
log(`Based on local functions folder ${functionsDir}, these are the functions detected`)
|
|
78
|
+
const table = new AsciiTable(`Netlify Functions (in local functions folder)`)
|
|
79
|
+
table.setHeading('Name', 'URL', 'deployed')
|
|
80
|
+
normalizedFunctions.forEach(({ isDeployed, name, url }) => {
|
|
81
|
+
table.addRow(name, url, isDeployed ? 'yes' : 'no')
|
|
82
|
+
})
|
|
83
|
+
log(table.toString())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates the `netlify functions:list` command
|
|
88
|
+
* @param {import('../base-command').BaseCommand} program
|
|
89
|
+
* @returns
|
|
90
|
+
*/
|
|
91
|
+
const createFunctionsListCommand = (program) =>
|
|
92
|
+
program
|
|
93
|
+
.command('functions:list')
|
|
94
|
+
.alias('function:list')
|
|
95
|
+
.description(
|
|
96
|
+
`List functions that exist locally
|
|
97
|
+
Helpful for making sure that you have formatted your functions correctly
|
|
98
|
+
|
|
99
|
+
NOT the same as listing the functions that have been deployed. For that info you need to go to your Netlify deploy log.`,
|
|
100
|
+
)
|
|
101
|
+
.option('-n, --name <name>', 'name to print')
|
|
102
|
+
.option('-f, --functions <dir>', 'Specify a functions directory to list')
|
|
103
|
+
.option('--json', 'Output function data as JSON')
|
|
104
|
+
.action(functionsList)
|
|
105
|
+
|
|
106
|
+
module.exports = { createFunctionsListCommand }
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
const { join } = require('path')
|
|
4
|
+
|
|
5
|
+
const { startFunctionsServer } = require('../../lib/functions/server')
|
|
6
|
+
const { acquirePort, getFunctionsDir, getSiteInformation, injectEnvVariables } = require('../../utils')
|
|
7
|
+
|
|
8
|
+
const DEFAULT_PORT = 9999
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The functions:serve command
|
|
12
|
+
* @param {import('commander').OptionValues} options
|
|
13
|
+
* @param {import('../base-command').BaseCommand} command
|
|
14
|
+
*/
|
|
15
|
+
const functionsServe = async (options, command) => {
|
|
16
|
+
const { api, config, site, siteInfo } = command.netlify
|
|
17
|
+
|
|
18
|
+
const functionsDir = getFunctionsDir({ options, config }, join('netlify', 'functions'))
|
|
19
|
+
|
|
20
|
+
await injectEnvVariables({ env: command.netlify.cachedConfig.env, site })
|
|
21
|
+
|
|
22
|
+
const { capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
23
|
+
offline: options.offline,
|
|
24
|
+
api,
|
|
25
|
+
site,
|
|
26
|
+
siteInfo,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const functionsPort = await acquirePort({
|
|
30
|
+
configuredPort: options.port || (config.dev && config.dev.functionsPort),
|
|
31
|
+
defaultPort: DEFAULT_PORT,
|
|
32
|
+
errorMessage: 'Could not acquire configured functions port',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
await startFunctionsServer({
|
|
36
|
+
config,
|
|
37
|
+
settings: { functions: functionsDir, functionsPort },
|
|
38
|
+
site,
|
|
39
|
+
siteUrl,
|
|
40
|
+
capabilities,
|
|
41
|
+
timeouts,
|
|
42
|
+
functionsPrefix: '/.netlify/functions/',
|
|
43
|
+
buildersPrefix: '/.netlify/builders/',
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates the `netlify functions:serve` command
|
|
49
|
+
* @param {import('../base-command').BaseCommand} program
|
|
50
|
+
* @returns
|
|
51
|
+
*/
|
|
52
|
+
const createFunctionsServeCommand = (program) =>
|
|
53
|
+
program
|
|
54
|
+
.command('functions:serve')
|
|
55
|
+
.alias('function:serve')
|
|
56
|
+
.description('(Beta) Serve functions locally')
|
|
57
|
+
.option('-f, --functions <dir>', 'Specify a functions directory to serve')
|
|
58
|
+
.option('-p, --port <port>', 'Specify a port for the functions server', (value) => Number.parseInt(value))
|
|
59
|
+
.option('-o, --offline', 'disables any features that require network access')
|
|
60
|
+
.addHelpText('after', 'Helpful for debugging functions.')
|
|
61
|
+
.action(functionsServe)
|
|
62
|
+
|
|
63
|
+
module.exports = { createFunctionsServeCommand }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const { chalk } = require('../../utils')
|
|
3
|
+
|
|
4
|
+
const { createFunctionsBuildCommand } = require('./functions-build')
|
|
5
|
+
const { createFunctionsCreateCommand } = require('./functions-create')
|
|
6
|
+
const { createFunctionsInvokeCommand } = require('./functions-invoke')
|
|
7
|
+
const { createFunctionsListCommand } = require('./functions-list')
|
|
8
|
+
const { createFunctionsServeCommand } = require('./functions-serve')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The functions command
|
|
12
|
+
* @param {import('commander').OptionValues} options
|
|
13
|
+
* @param {import('../base-command').BaseCommand} command
|
|
14
|
+
*/
|
|
15
|
+
const functions = (options, command) => {
|
|
16
|
+
command.help()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates the `netlify functions` command
|
|
21
|
+
* @param {import('../base-command').BaseCommand} program
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
const createFunctionsCommand = (program) => {
|
|
25
|
+
createFunctionsBuildCommand(program)
|
|
26
|
+
createFunctionsCreateCommand(program)
|
|
27
|
+
createFunctionsInvokeCommand(program)
|
|
28
|
+
createFunctionsListCommand(program)
|
|
29
|
+
createFunctionsServeCommand(program)
|
|
30
|
+
|
|
31
|
+
const name = chalk.greenBright('`functions`')
|
|
32
|
+
|
|
33
|
+
return program
|
|
34
|
+
.command('functions')
|
|
35
|
+
.alias('function')
|
|
36
|
+
.description(
|
|
37
|
+
`Manage netlify functions
|
|
38
|
+
The ${name} command will help you manage the functions in this site`,
|
|
39
|
+
)
|
|
40
|
+
.addExamples([
|
|
41
|
+
'netlify functions:create --name function-xyz',
|
|
42
|
+
'netlify functions:build --name function-abc --timeout 30s',
|
|
43
|
+
])
|
|
44
|
+
.action(functions)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { createFunctionsCommand }
|
|
@@ -1,47 +1,5 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { createFunctionsCommand } = require('./functions')
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { TrackedCommand } = require('../../utils/telemetry/tracked-command')
|
|
6
|
-
|
|
7
|
-
const showHelp = function (command) {
|
|
8
|
-
execSync(`netlify ${command} --help`, { stdio: [0, 1, 2] })
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const isEmptyCommand = function (flags, args) {
|
|
12
|
-
if (!hasFlags(flags) && !hasArgs(args)) {
|
|
13
|
-
return true
|
|
14
|
-
}
|
|
15
|
-
return false
|
|
3
|
+
module.exports = {
|
|
4
|
+
createFunctionsCommand,
|
|
16
5
|
}
|
|
17
|
-
|
|
18
|
-
const hasFlags = function (flags) {
|
|
19
|
-
return Object.keys(flags).length
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const hasArgs = function (args) {
|
|
23
|
-
return Object.keys(args).length
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
class FunctionsCommand extends TrackedCommand {
|
|
27
|
-
run() {
|
|
28
|
-
const { args, flags } = this.parse(FunctionsCommand)
|
|
29
|
-
|
|
30
|
-
// run help command if no args passed
|
|
31
|
-
if (isEmptyCommand(flags, args)) {
|
|
32
|
-
showHelp(this.id)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const name = chalk.greenBright('`functions`')
|
|
38
|
-
FunctionsCommand.aliases = ['function']
|
|
39
|
-
FunctionsCommand.description = `Manage netlify functions
|
|
40
|
-
The ${name} command will help you manage the functions in this site
|
|
41
|
-
`
|
|
42
|
-
FunctionsCommand.examples = [
|
|
43
|
-
'netlify functions:create --name function-xyz',
|
|
44
|
-
'netlify functions:build --name function-abc --timeout 30s',
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
module.exports = FunctionsCommand
|