netlify-cli 15.10.0-rc.1 → 15.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/run.mjs +5 -6
- package/npm-shrinkwrap.json +3 -6
- package/package.json +2 -1
- package/src/commands/base-command.mjs +116 -295
- package/src/commands/build/build.mjs +1 -9
- package/src/commands/deploy/deploy.mjs +9 -23
- package/src/commands/dev/dev.mjs +17 -22
- package/src/commands/functions/functions-create.mjs +89 -118
- package/src/commands/functions/functions-invoke.mjs +7 -10
- package/src/commands/functions/functions-list.mjs +2 -2
- package/src/commands/init/init.mjs +1 -1
- package/src/commands/link/link.mjs +5 -5
- package/src/commands/serve/serve.mjs +6 -10
- package/src/commands/sites/sites-create-template.mjs +1 -1
- package/src/commands/sites/sites-create.mjs +1 -1
- package/src/lib/edge-functions/internal.mjs +3 -5
- package/src/lib/edge-functions/proxy.mjs +3 -27
- package/src/lib/functions/netlify-function.mjs +26 -1
- package/src/lib/functions/registry.mjs +14 -26
- package/src/lib/functions/runtimes/js/worker.mjs +1 -1
- package/src/lib/spinner.mjs +1 -1
- package/src/recipes/vscode/index.mjs +6 -24
- package/src/utils/command-helpers.mjs +7 -16
- package/src/utils/detect-server-settings.mjs +245 -133
- package/src/utils/framework-server.mjs +5 -6
- package/src/utils/functions/functions.mjs +5 -8
- package/src/utils/get-repo-data.mjs +6 -5
- package/src/utils/init/config-github.mjs +2 -2
- package/src/utils/init/config-manual.mjs +7 -24
- package/src/utils/init/frameworks.mjs +23 -0
- package/src/utils/init/utils.mjs +63 -62
- package/src/utils/proxy-server.mjs +4 -7
- package/src/utils/proxy.mjs +3 -4
- package/src/utils/read-repo-url.mjs +0 -4
- package/src/utils/run-build.mjs +32 -58
- package/src/utils/shell.mjs +7 -24
- package/src/utils/state-config.mjs +1 -5
- package/src/utils/static-server.mjs +0 -4
- package/src/utils/build-info.mjs +0 -100
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import process from 'process'
|
|
3
3
|
|
|
4
4
|
import { getBuildOptions, runBuild } from '../../lib/build.mjs'
|
|
5
|
-
import { detectFrameworkSettings } from '../../utils/build-info.mjs'
|
|
6
5
|
import { error, exit, getToken } from '../../utils/command-helpers.mjs'
|
|
7
6
|
import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.mjs'
|
|
8
7
|
|
|
@@ -34,18 +33,11 @@ const injectEnv = async function (command, { api, buildOptions, context, siteInf
|
|
|
34
33
|
* @param {import('../base-command.mjs').default} command
|
|
35
34
|
*/
|
|
36
35
|
const build = async (options, command) => {
|
|
37
|
-
const { cachedConfig, siteInfo } = command.netlify
|
|
38
36
|
command.setAnalyticsPayload({ dry: options.dry })
|
|
39
37
|
// Retrieve Netlify Build options
|
|
40
38
|
const [token] = await getToken()
|
|
41
|
-
const settings = await detectFrameworkSettings(command, 'build')
|
|
42
|
-
|
|
43
|
-
// override the build command with the detection result if no command is specified through the config
|
|
44
|
-
if (!cachedConfig.config.build.command) {
|
|
45
|
-
cachedConfig.config.build.command = settings?.buildCommand
|
|
46
|
-
cachedConfig.config.build.commandOrigin = 'heuristics'
|
|
47
|
-
}
|
|
48
39
|
|
|
40
|
+
const { cachedConfig, siteInfo } = command.netlify
|
|
49
41
|
const buildOptions = await getBuildOptions({
|
|
50
42
|
cachedConfig,
|
|
51
43
|
token,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { stat } from 'fs/promises'
|
|
3
3
|
import { basename, resolve } from 'path'
|
|
4
|
-
import { env } from 'process'
|
|
4
|
+
import { cwd, env } from 'process'
|
|
5
5
|
|
|
6
6
|
import { runCoreSteps } from '@netlify/build'
|
|
7
7
|
import { restoreConfig, updateConfig } from '@netlify/config'
|
|
@@ -64,18 +64,16 @@ const triggerDeploy = async ({ api, options, siteData, siteId }) => {
|
|
|
64
64
|
/**
|
|
65
65
|
* g
|
|
66
66
|
* @param {object} config
|
|
67
|
-
* @param {string} config.workingDir The process working directory
|
|
68
67
|
* @param {object} config.config
|
|
69
68
|
* @param {import('commander').OptionValues} config.options
|
|
70
69
|
* @param {object} config.site
|
|
71
70
|
* @param {object} config.siteData
|
|
72
71
|
* @returns {Promise<string>}
|
|
73
72
|
*/
|
|
74
|
-
const getDeployFolder = async ({ config, options, site, siteData
|
|
75
|
-
console.log()
|
|
73
|
+
const getDeployFolder = async ({ config, options, site, siteData }) => {
|
|
76
74
|
let deployFolder
|
|
77
75
|
if (options.dir) {
|
|
78
|
-
deployFolder = resolve(
|
|
76
|
+
deployFolder = resolve(cwd(), options.dir)
|
|
79
77
|
} else if (config?.build?.publish) {
|
|
80
78
|
deployFolder = resolve(site.root, config.build.publish)
|
|
81
79
|
} else if (siteData?.build_settings?.dir) {
|
|
@@ -84,13 +82,14 @@ const getDeployFolder = async ({ config, options, site, siteData, workingDir })
|
|
|
84
82
|
|
|
85
83
|
if (!deployFolder) {
|
|
86
84
|
log('Please provide a publish directory (e.g. "public" or "dist" or "."):')
|
|
85
|
+
log(cwd())
|
|
87
86
|
const { promptPath } = await inquirer.prompt([
|
|
88
87
|
{
|
|
89
88
|
type: 'input',
|
|
90
89
|
name: 'promptPath',
|
|
91
90
|
message: 'Publish directory',
|
|
92
91
|
default: '.',
|
|
93
|
-
filter: (input) => resolve(
|
|
92
|
+
filter: (input) => resolve(cwd(), input),
|
|
94
93
|
},
|
|
95
94
|
])
|
|
96
95
|
deployFolder = promptPath
|
|
@@ -129,15 +128,14 @@ const validateDeployFolder = async ({ deployFolder }) => {
|
|
|
129
128
|
* @param {import('commander').OptionValues} config.options
|
|
130
129
|
* @param {object} config.site
|
|
131
130
|
* @param {object} config.siteData
|
|
132
|
-
* @param {string} config.workingDir // The process working directory
|
|
133
131
|
* @returns {string}
|
|
134
132
|
*/
|
|
135
|
-
const getFunctionsFolder = ({ config, options, site, siteData
|
|
133
|
+
const getFunctionsFolder = ({ config, options, site, siteData }) => {
|
|
136
134
|
let functionsFolder
|
|
137
135
|
// Support "functions" and "Functions"
|
|
138
136
|
const funcConfig = config.functionsDirectory
|
|
139
137
|
if (options.functions) {
|
|
140
|
-
functionsFolder = resolve(
|
|
138
|
+
functionsFolder = resolve(cwd(), options.functions)
|
|
141
139
|
} else if (funcConfig) {
|
|
142
140
|
functionsFolder = resolve(site.root, funcConfig)
|
|
143
141
|
} else if (siteData?.build_settings?.functions_dir) {
|
|
@@ -180,21 +178,12 @@ const validateFolders = async ({ deployFolder, functionsFolder }) => {
|
|
|
180
178
|
return { deployFolderStat, functionsFolderStat }
|
|
181
179
|
}
|
|
182
180
|
|
|
183
|
-
/**
|
|
184
|
-
* @param {object} config
|
|
185
|
-
* @param {string} config.deployFolder
|
|
186
|
-
* @param {*} config.site
|
|
187
|
-
* @returns
|
|
188
|
-
*/
|
|
189
181
|
const getDeployFilesFilter = ({ deployFolder, site }) => {
|
|
190
182
|
// site.root === deployFolder can happen when users run `netlify deploy --dir .`
|
|
191
183
|
// in that specific case we don't want to publish the repo node_modules
|
|
192
184
|
// when site.root !== deployFolder the behaviour matches our buildbot
|
|
193
185
|
const skipNodeModules = site.root === deployFolder
|
|
194
186
|
|
|
195
|
-
/**
|
|
196
|
-
* @param {string} filename
|
|
197
|
-
*/
|
|
198
187
|
return (filename) => {
|
|
199
188
|
if (filename == null) {
|
|
200
189
|
return false
|
|
@@ -507,7 +496,6 @@ const printResults = ({ deployToProduction, json, results, runBuildCommand }) =>
|
|
|
507
496
|
* @param {import('../base-command.mjs').default} command
|
|
508
497
|
*/
|
|
509
498
|
const deploy = async (options, command) => {
|
|
510
|
-
const { workingDir } = command
|
|
511
499
|
const { api, site, siteInfo } = command.netlify
|
|
512
500
|
const alias = options.alias || options.branch
|
|
513
501
|
|
|
@@ -572,18 +560,16 @@ const deploy = async (options, command) => {
|
|
|
572
560
|
siteInfo: siteData,
|
|
573
561
|
})
|
|
574
562
|
}
|
|
575
|
-
|
|
576
563
|
const { configMutations = [], newConfig } = await handleBuild({
|
|
577
564
|
cachedConfig: command.netlify.cachedConfig,
|
|
578
565
|
options,
|
|
579
566
|
})
|
|
580
567
|
const config = newConfig || command.netlify.config
|
|
581
568
|
|
|
582
|
-
const deployFolder = await getDeployFolder({
|
|
583
|
-
const functionsFolder = getFunctionsFolder({
|
|
569
|
+
const deployFolder = await getDeployFolder({ options, config, site, siteData })
|
|
570
|
+
const functionsFolder = getFunctionsFolder({ options, config, site, siteData })
|
|
584
571
|
const { configPath } = site
|
|
585
572
|
const edgeFunctionsConfig = command.netlify.config.edge_functions
|
|
586
|
-
console.log({ functionsFolder, edgeFunctionsConfig })
|
|
587
573
|
|
|
588
574
|
// build flag wasn't used and edge functions exist
|
|
589
575
|
if (!options.build && edgeFunctionsConfig && edgeFunctionsConfig.length !== 0) {
|
package/src/commands/dev/dev.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { printBanner } from '../../utils/banner.mjs'
|
|
|
9
9
|
import {
|
|
10
10
|
BANG,
|
|
11
11
|
chalk,
|
|
12
|
+
exit,
|
|
12
13
|
log,
|
|
13
14
|
NETLIFYDEV,
|
|
14
15
|
NETLIFYDEVERR,
|
|
@@ -34,7 +35,7 @@ import { createDevExecCommand } from './dev-exec.mjs'
|
|
|
34
35
|
* @param {object} config
|
|
35
36
|
* @param {*} config.api
|
|
36
37
|
* @param {import('commander').OptionValues} config.options
|
|
37
|
-
* @param {
|
|
38
|
+
* @param {*} config.settings
|
|
38
39
|
* @param {*} config.site
|
|
39
40
|
* @param {*} config.state
|
|
40
41
|
* @returns
|
|
@@ -67,9 +68,6 @@ const handleLiveTunnel = async ({ api, options, settings, site, state }) => {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
/**
|
|
71
|
-
* @param {string} args
|
|
72
|
-
*/
|
|
73
71
|
const validateShortFlagArgs = (args) => {
|
|
74
72
|
if (args.startsWith('=')) {
|
|
75
73
|
throw new Error(
|
|
@@ -96,13 +94,11 @@ const dev = async (options, command) => {
|
|
|
96
94
|
const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify
|
|
97
95
|
config.dev = { ...config.dev }
|
|
98
96
|
config.build = { ...config.build }
|
|
99
|
-
/** @type {import('./types
|
|
97
|
+
/** @type {import('./types').DevConfig} */
|
|
100
98
|
const devConfig = {
|
|
101
99
|
framework: '#auto',
|
|
102
|
-
autoLaunch: Boolean(options.open),
|
|
103
100
|
...(config.functionsDirectory && { functions: config.functionsDirectory }),
|
|
104
101
|
...(config.build.publish && { publish: config.build.publish }),
|
|
105
|
-
...(config.build.base && { base: config.build.base }),
|
|
106
102
|
...config.dev,
|
|
107
103
|
...options,
|
|
108
104
|
}
|
|
@@ -128,17 +124,20 @@ const dev = async (options, command) => {
|
|
|
128
124
|
siteInfo,
|
|
129
125
|
})
|
|
130
126
|
|
|
131
|
-
/** @type {import('../../utils/types
|
|
132
|
-
let settings
|
|
127
|
+
/** @type {Partial<import('../../utils/types').ServerSettings>} */
|
|
128
|
+
let settings = {}
|
|
133
129
|
try {
|
|
134
|
-
settings = await detectServerSettings(devConfig, options,
|
|
130
|
+
settings = await detectServerSettings(devConfig, options, site.root, {
|
|
131
|
+
site: {
|
|
132
|
+
id: site.id,
|
|
133
|
+
url: siteUrl,
|
|
134
|
+
},
|
|
135
|
+
})
|
|
135
136
|
|
|
136
137
|
cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
|
|
137
138
|
} catch (error_) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
process.exit(1)
|
|
139
|
+
log(NETLIFYDEVERR, error_.message)
|
|
140
|
+
exit(1)
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
command.setAnalyticsPayload({ live: options.live })
|
|
@@ -152,9 +151,10 @@ const dev = async (options, command) => {
|
|
|
152
151
|
log(`${NETLIFYDEVWARN} Setting up local development server`)
|
|
153
152
|
|
|
154
153
|
const { configPath: configPathOverride } = await runDevTimeline({
|
|
155
|
-
|
|
154
|
+
cachedConfig,
|
|
156
155
|
options,
|
|
157
156
|
settings,
|
|
157
|
+
site,
|
|
158
158
|
env: {
|
|
159
159
|
URL: url,
|
|
160
160
|
DEPLOY_URL: url,
|
|
@@ -188,11 +188,8 @@ const dev = async (options, command) => {
|
|
|
188
188
|
|
|
189
189
|
// TODO: We should consolidate this with the existing config watcher.
|
|
190
190
|
const getUpdatedConfig = async () => {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
offline: true,
|
|
194
|
-
state,
|
|
195
|
-
})
|
|
191
|
+
const cwd = options.cwd || process.cwd()
|
|
192
|
+
const { config: newConfig } = await command.getConfig({ cwd, offline: true, state })
|
|
196
193
|
const normalizedNewConfig = normalizeConfig(newConfig)
|
|
197
194
|
|
|
198
195
|
return normalizedNewConfig
|
|
@@ -205,7 +202,6 @@ const dev = async (options, command) => {
|
|
|
205
202
|
config,
|
|
206
203
|
configPath: configPathOverride,
|
|
207
204
|
debug: options.debug,
|
|
208
|
-
projectDir: command.workingDir,
|
|
209
205
|
env,
|
|
210
206
|
getUpdatedConfig,
|
|
211
207
|
inspectSettings,
|
|
@@ -252,7 +248,6 @@ export const createDevCommand = (program) => {
|
|
|
252
248
|
.argParser((value) => Number.parseInt(value))
|
|
253
249
|
.hideHelp(true),
|
|
254
250
|
)
|
|
255
|
-
.addOption(new Option('--no-open', 'disables the automatic opening of a browser window'))
|
|
256
251
|
.option('--target-port <port>', 'port of target app server', (value) => Number.parseInt(value))
|
|
257
252
|
.option('--framework <name>', 'framework to use. Defaults to #auto which automatically detects a framework')
|
|
258
253
|
.option('-d ,--dir <path>', 'dir with static files')
|
|
@@ -3,7 +3,7 @@ import cp from 'child_process'
|
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
import { mkdir, readdir, unlink } from 'fs/promises'
|
|
5
5
|
import { createRequire } from 'module'
|
|
6
|
-
import path, { dirname
|
|
6
|
+
import path, { dirname } from 'path'
|
|
7
7
|
import process from 'process'
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'url'
|
|
9
9
|
import { promisify } from 'util'
|
|
@@ -12,6 +12,7 @@ import copyTemplateDirOriginal from 'copy-template-dir'
|
|
|
12
12
|
import { findUp } from 'find-up'
|
|
13
13
|
import fuzzy from 'fuzzy'
|
|
14
14
|
import inquirer from 'inquirer'
|
|
15
|
+
import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt'
|
|
15
16
|
import fetch from 'node-fetch'
|
|
16
17
|
import ora from 'ora'
|
|
17
18
|
|
|
@@ -30,10 +31,8 @@ const templatesDir = path.resolve(dirname(fileURLToPath(import.meta.url)), '../.
|
|
|
30
31
|
|
|
31
32
|
const showRustTemplates = process.env.NETLIFY_EXPERIMENTAL_BUILD_RUST_SOURCE === 'true'
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* each `value` property in this list.
|
|
36
|
-
*/
|
|
34
|
+
// Ensure that there's a sub-directory in `src/functions-templates` named after
|
|
35
|
+
// each `value` property in this list.
|
|
37
36
|
const languages = [
|
|
38
37
|
{ name: 'JavaScript', value: 'javascript' },
|
|
39
38
|
{ name: 'TypeScript', value: 'typescript' },
|
|
@@ -92,28 +91,23 @@ const filterRegistry = function (registry, input) {
|
|
|
92
91
|
})
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
/**
|
|
96
|
-
* @param {string} lang
|
|
97
|
-
* @param {'edge' | 'serverless'} funcType
|
|
98
|
-
*/
|
|
99
94
|
const formatRegistryArrayForInquirer = async function (lang, funcType) {
|
|
100
|
-
const
|
|
95
|
+
const folderNames = await readdir(path.join(templatesDir, lang))
|
|
101
96
|
|
|
102
97
|
const imports = await Promise.all(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// noop if import fails we don't break the whole inquirer
|
|
112
|
-
}
|
|
98
|
+
folderNames
|
|
99
|
+
// filter out markdown files
|
|
100
|
+
.filter((folderName) => !folderName.endsWith('.md'))
|
|
101
|
+
.map(async (folderName) => {
|
|
102
|
+
const templatePath = path.join(templatesDir, lang, folderName, '.netlify-function-template.mjs')
|
|
103
|
+
const template = await import(pathToFileURL(templatePath))
|
|
104
|
+
|
|
105
|
+
return template.default
|
|
113
106
|
}),
|
|
114
107
|
)
|
|
108
|
+
|
|
115
109
|
const registry = imports
|
|
116
|
-
.filter((template) => template
|
|
110
|
+
.filter((template) => template.functionType === funcType)
|
|
117
111
|
.sort((templateA, templateB) => {
|
|
118
112
|
const priorityDiff = (templateA.priority || DEFAULT_PRIORITY) - (templateB.priority || DEFAULT_PRIORITY)
|
|
119
113
|
|
|
@@ -142,7 +136,7 @@ const formatRegistryArrayForInquirer = async function (lang, funcType) {
|
|
|
142
136
|
/**
|
|
143
137
|
* pick template from our existing templates
|
|
144
138
|
* @param {import('commander').OptionValues} config
|
|
145
|
-
*
|
|
139
|
+
*
|
|
146
140
|
*/
|
|
147
141
|
const pickTemplate = async function ({ language: languageFromFlag }, funcType) {
|
|
148
142
|
const specialCommands = [
|
|
@@ -178,6 +172,8 @@ const pickTemplate = async function ({ language: languageFromFlag }, funcType) {
|
|
|
178
172
|
language = languageFromPrompt
|
|
179
173
|
}
|
|
180
174
|
|
|
175
|
+
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)
|
|
176
|
+
|
|
181
177
|
let templatesForLanguage
|
|
182
178
|
|
|
183
179
|
try {
|
|
@@ -211,7 +207,6 @@ const pickTemplate = async function ({ language: languageFromFlag }, funcType) {
|
|
|
211
207
|
|
|
212
208
|
const DEFAULT_PRIORITY = 999
|
|
213
209
|
|
|
214
|
-
/** @returns {Promise<'edge' | 'serverless'>} */
|
|
215
210
|
const selectTypeOfFunc = async () => {
|
|
216
211
|
const functionTypes = [
|
|
217
212
|
{ name: 'Edge function (Deno)', value: 'edge' },
|
|
@@ -229,100 +224,92 @@ const selectTypeOfFunc = async () => {
|
|
|
229
224
|
return functionType
|
|
230
225
|
}
|
|
231
226
|
|
|
232
|
-
/**
|
|
233
|
-
* @param {import('../base-command.mjs').default} command
|
|
234
|
-
*/
|
|
235
227
|
const ensureEdgeFuncDirExists = function (command) {
|
|
236
228
|
const { config, site } = command.netlify
|
|
237
229
|
const siteId = site.id
|
|
230
|
+
let functionsDirHolder = config.build.edge_functions
|
|
238
231
|
|
|
239
232
|
if (!siteId) {
|
|
240
233
|
error(`${NETLIFYDEVERR} No site id found, please run inside a site directory or \`netlify link\``)
|
|
241
234
|
}
|
|
242
235
|
|
|
243
|
-
|
|
244
|
-
|
|
236
|
+
if (!functionsDirHolder) {
|
|
237
|
+
functionsDirHolder = 'netlify/edge-functions'
|
|
238
|
+
}
|
|
245
239
|
|
|
246
|
-
if (!fs.existsSync(
|
|
240
|
+
if (!fs.existsSync(functionsDirHolder)) {
|
|
247
241
|
log(
|
|
248
242
|
`${NETLIFYDEVLOG} Edge Functions directory ${chalk.magenta.inverse(
|
|
249
|
-
|
|
243
|
+
functionsDirHolder,
|
|
250
244
|
)} does not exist yet, creating it...`,
|
|
251
245
|
)
|
|
252
246
|
|
|
253
|
-
fs.mkdirSync(
|
|
247
|
+
fs.mkdirSync(functionsDirHolder, { recursive: true })
|
|
254
248
|
|
|
255
|
-
log(`${NETLIFYDEVLOG} Edge Functions directory ${chalk.magenta.inverse(
|
|
249
|
+
log(`${NETLIFYDEVLOG} Edge Functions directory ${chalk.magenta.inverse(functionsDirHolder)} created.`)
|
|
256
250
|
}
|
|
257
|
-
|
|
258
|
-
return functionsDir
|
|
251
|
+
return functionsDirHolder
|
|
259
252
|
}
|
|
260
253
|
|
|
261
254
|
/**
|
|
262
|
-
*
|
|
255
|
+
* Get functions directory (and make it if necessary)
|
|
263
256
|
* @param {import('../base-command.mjs').default} command
|
|
264
|
-
* @returns {Promise<string>} - functions directory or throws an error
|
|
257
|
+
* @returns {Promise<string|never>} - functions directory or throws an error
|
|
265
258
|
*/
|
|
266
|
-
const
|
|
267
|
-
const { api,
|
|
268
|
-
|
|
259
|
+
const ensureFunctionDirExists = async function (command) {
|
|
260
|
+
const { api, config, site } = command.netlify
|
|
261
|
+
const siteId = site.id
|
|
262
|
+
let functionsDirHolder = config.functionsDirectory
|
|
269
263
|
|
|
270
|
-
if (!
|
|
271
|
-
|
|
272
|
-
}
|
|
264
|
+
if (!functionsDirHolder) {
|
|
265
|
+
log(`${NETLIFYDEVLOG} functions directory not specified in netlify.toml or UI settings`)
|
|
273
266
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
name: 'functionsDir',
|
|
278
|
-
message: 'Enter the path, relative to your site, where your functions should live:',
|
|
279
|
-
default: 'netlify/functions',
|
|
280
|
-
},
|
|
281
|
-
])
|
|
267
|
+
if (!siteId) {
|
|
268
|
+
error(`${NETLIFYDEVERR} No site id found, please run inside a site directory or \`netlify link\``)
|
|
269
|
+
}
|
|
282
270
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
build_settings: {
|
|
291
|
-
functions_dir: functionsDir,
|
|
292
|
-
},
|
|
271
|
+
const { functionsDir } = await inquirer.prompt([
|
|
272
|
+
{
|
|
273
|
+
type: 'input',
|
|
274
|
+
name: 'functionsDir',
|
|
275
|
+
message:
|
|
276
|
+
'Enter the path, relative to your site’s base directory in your repository, where your functions should live:',
|
|
277
|
+
default: 'netlify/functions',
|
|
293
278
|
},
|
|
294
|
-
|
|
279
|
+
])
|
|
295
280
|
|
|
296
|
-
|
|
297
|
-
} catch {
|
|
298
|
-
throw error('Error updating site settings')
|
|
299
|
-
}
|
|
300
|
-
return functionsDir
|
|
301
|
-
}
|
|
281
|
+
functionsDirHolder = functionsDir
|
|
302
282
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
283
|
+
try {
|
|
284
|
+
log(`${NETLIFYDEVLOG} updating site settings with ${chalk.magenta.inverse(functionsDirHolder)}`)
|
|
285
|
+
|
|
286
|
+
// @ts-ignore Typings of API are not correct
|
|
287
|
+
await api.updateSite({
|
|
288
|
+
siteId: site.id,
|
|
289
|
+
body: {
|
|
290
|
+
build_settings: {
|
|
291
|
+
functions_dir: functionsDirHolder,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
})
|
|
313
295
|
|
|
314
|
-
|
|
296
|
+
log(`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(functionsDirHolder)} updated in site settings`)
|
|
297
|
+
} catch {
|
|
298
|
+
throw error('Error updating site settings')
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (!(await fileExistsAsync(functionsDirHolder))) {
|
|
315
303
|
log(
|
|
316
304
|
`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(
|
|
317
|
-
|
|
305
|
+
functionsDirHolder,
|
|
318
306
|
)} does not exist yet, creating it...`,
|
|
319
307
|
)
|
|
320
308
|
|
|
321
309
|
await mkdir(functionsDirHolder, { recursive: true })
|
|
322
310
|
|
|
323
|
-
log(`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(
|
|
311
|
+
log(`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(functionsDirHolder)} created`)
|
|
324
312
|
}
|
|
325
|
-
|
|
326
313
|
return functionsDirHolder
|
|
327
314
|
}
|
|
328
315
|
|
|
@@ -383,24 +370,20 @@ const downloadFromURL = async function (command, options, argumentName, function
|
|
|
383
370
|
}
|
|
384
371
|
}
|
|
385
372
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
* name and version range (e.g. '@netlify/functions@0.1.0').
|
|
391
|
-
*/
|
|
373
|
+
// Takes a list of existing packages and a list of packages required by a
|
|
374
|
+
// function, and returns the packages from the latter that aren't present
|
|
375
|
+
// in the former. The packages are returned as an array of strings with the
|
|
376
|
+
// name and version range (e.g. '@netlify/functions@0.1.0').
|
|
392
377
|
const getNpmInstallPackages = (existingPackages = {}, neededPackages = {}) =>
|
|
393
378
|
Object.entries(neededPackages)
|
|
394
379
|
.filter(([name]) => existingPackages[name] === undefined)
|
|
395
380
|
.map(([name, version]) => `${name}@${version}`)
|
|
396
381
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
* another part of the project, which we don't want to do.
|
|
403
|
-
*/
|
|
382
|
+
// When installing a function's dependencies, we first try to find a site-level
|
|
383
|
+
// `package.json` file. If we do, we look for any dependencies of the function
|
|
384
|
+
// that aren't already listed as dependencies of the site and install them. If
|
|
385
|
+
// we don't do this check, we may be upgrading the version of a module used in
|
|
386
|
+
// another part of the project, which we don't want to do.
|
|
404
387
|
const installDeps = async ({ functionPackageJson, functionPath, functionsDir }) => {
|
|
405
388
|
const { dependencies: functionDependencies, devDependencies: functionDevDependencies } = require(functionPackageJson)
|
|
406
389
|
const sitePackageJson = await findUp('package.json', { cwd: functionsDir })
|
|
@@ -447,8 +430,8 @@ const installDeps = async ({ functionPackageJson, functionPath, functionsDir })
|
|
|
447
430
|
* @param {import('../base-command.mjs').default} command
|
|
448
431
|
* @param {import('commander').OptionValues} options
|
|
449
432
|
* @param {string} argumentName
|
|
450
|
-
* @param {string} functionsDir
|
|
451
|
-
* @param {
|
|
433
|
+
* @param {string} functionsDir
|
|
434
|
+
* @param {string} funcType
|
|
452
435
|
*/
|
|
453
436
|
// eslint-disable-next-line max-params
|
|
454
437
|
const scaffoldFromTemplate = async function (command, options, argumentName, functionsDir, funcType) {
|
|
@@ -460,7 +443,7 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun
|
|
|
460
443
|
name: 'chosenUrl',
|
|
461
444
|
message: 'URL to clone: ',
|
|
462
445
|
type: 'input',
|
|
463
|
-
validate: (
|
|
446
|
+
validate: (val) => Boolean(validateRepoURL(val)),
|
|
464
447
|
// make sure it is not undefined and is a valid filename.
|
|
465
448
|
// this has some nuance i have ignored, eg crossenv and i18n concerns
|
|
466
449
|
},
|
|
@@ -523,7 +506,7 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun
|
|
|
523
506
|
}
|
|
524
507
|
|
|
525
508
|
if (funcType === 'edge') {
|
|
526
|
-
registerEFInToml(name
|
|
509
|
+
registerEFInToml(name)
|
|
527
510
|
}
|
|
528
511
|
|
|
529
512
|
await installAddons(command, addons, path.resolve(functionPath))
|
|
@@ -648,15 +631,9 @@ const installAddons = async function (command, functionAddons, fnPath) {
|
|
|
648
631
|
return Promise.all(arr)
|
|
649
632
|
}
|
|
650
633
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
* @param {import('../types.js').NetlifyOptions} options
|
|
655
|
-
*/
|
|
656
|
-
const registerEFInToml = async (funcName, options) => {
|
|
657
|
-
const { configFilePath, relConfigFilePath } = options
|
|
658
|
-
if (!fs.existsSync(configFilePath)) {
|
|
659
|
-
log(`${NETLIFYDEVLOG} \`${relConfigFilePath}\` file does not exist yet. Creating it...`)
|
|
634
|
+
const registerEFInToml = async (funcName) => {
|
|
635
|
+
if (!fs.existsSync('netlify.toml')) {
|
|
636
|
+
log(`${NETLIFYDEVLOG} \`netlify.toml\` file does not exist yet. Creating it...`)
|
|
660
637
|
}
|
|
661
638
|
|
|
662
639
|
let { funcPath } = await inquirer.prompt([
|
|
@@ -679,22 +656,17 @@ const registerEFInToml = async (funcName, options) => {
|
|
|
679
656
|
const functionRegister = `\n\n[[edge_functions]]\nfunction = "${funcName}"\npath = "${funcPath}"`
|
|
680
657
|
|
|
681
658
|
try {
|
|
682
|
-
fs.promises.appendFile(
|
|
659
|
+
fs.promises.appendFile('netlify.toml', functionRegister)
|
|
683
660
|
log(
|
|
684
|
-
`${NETLIFYDEVLOG} Function '${funcName}' registered for route \`${funcPath}\`. To change, edit your
|
|
661
|
+
`${NETLIFYDEVLOG} Function '${funcName}' registered for route \`${funcPath}\`. To change, edit your \`netlify.toml\` file.`,
|
|
685
662
|
)
|
|
686
663
|
} catch {
|
|
687
|
-
error(`${NETLIFYDEVERR} Unable to register function. Please check your
|
|
664
|
+
error(`${NETLIFYDEVERR} Unable to register function. Please check your \`netlify.toml\` file.`)
|
|
688
665
|
}
|
|
689
666
|
}
|
|
690
667
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
* but have retired that to force every scaffolded function to be a directory
|
|
694
|
-
* @param {string} functionsDir
|
|
695
|
-
* @param {string} name
|
|
696
|
-
* @returns
|
|
697
|
-
*/
|
|
668
|
+
// we used to allow for a --dir command,
|
|
669
|
+
// but have retired that to force every scaffolded function to be a directory
|
|
698
670
|
const ensureFunctionPathIsOk = function (functionsDir, name) {
|
|
699
671
|
const functionPath = path.join(functionsDir, name)
|
|
700
672
|
if (fs.existsSync(functionPath)) {
|
|
@@ -706,7 +678,6 @@ const ensureFunctionPathIsOk = function (functionsDir, name) {
|
|
|
706
678
|
|
|
707
679
|
/**
|
|
708
680
|
* The functions:create command
|
|
709
|
-
* @param {string} name
|
|
710
681
|
* @param {import('commander').OptionValues} options
|
|
711
682
|
* @param {import('../base-command.mjs').default} command
|
|
712
683
|
*/
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from 'fs'
|
|
3
3
|
import { createRequire } from 'module'
|
|
4
4
|
import path from 'path'
|
|
5
|
+
import process from 'process'
|
|
5
6
|
|
|
6
7
|
import inquirer from 'inquirer'
|
|
7
8
|
import fetch from 'node-fetch'
|
|
@@ -55,18 +56,14 @@ const formatQstring = function (querystring) {
|
|
|
55
56
|
return ''
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
* @param {string} payloadString
|
|
61
|
-
* @param {string} workingDir
|
|
62
|
-
*/
|
|
63
|
-
const processPayloadFromFlag = function (payloadString, workingDir) {
|
|
59
|
+
/** process payloads from flag */
|
|
60
|
+
const processPayloadFromFlag = function (payloadString) {
|
|
64
61
|
if (payloadString) {
|
|
65
62
|
// case 1: jsonstring
|
|
66
63
|
let payload = tryParseJSON(payloadString)
|
|
67
64
|
if (payload) return payload
|
|
68
65
|
// case 2: jsonpath
|
|
69
|
-
const payloadpath = path.join(
|
|
66
|
+
const payloadpath = path.join(process.cwd(), payloadString)
|
|
70
67
|
const pathexists = fs.existsSync(payloadpath)
|
|
71
68
|
if (pathexists) {
|
|
72
69
|
try {
|
|
@@ -144,11 +141,11 @@ const getFunctionToTrigger = function (options, argumentName) {
|
|
|
144
141
|
* @param {import('../base-command.mjs').default} command
|
|
145
142
|
*/
|
|
146
143
|
const functionsInvoke = async (nameArgument, options, command) => {
|
|
147
|
-
const { config
|
|
144
|
+
const { config } = command.netlify
|
|
148
145
|
|
|
149
146
|
const functionsDir = options.functions || (config.dev && config.dev.functions) || config.functionsDirectory
|
|
150
147
|
if (typeof functionsDir === 'undefined') {
|
|
151
|
-
error(
|
|
148
|
+
error('functions directory is undefined, did you forget to set it in netlify.toml?')
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
if (!options.port)
|
|
@@ -213,7 +210,7 @@ const functionsInvoke = async (nameArgument, options, command) => {
|
|
|
213
210
|
// }
|
|
214
211
|
}
|
|
215
212
|
}
|
|
216
|
-
const payload = processPayloadFromFlag(options.payload
|
|
213
|
+
const payload = processPayloadFromFlag(options.payload)
|
|
217
214
|
body = { ...body, ...payload }
|
|
218
215
|
|
|
219
216
|
try {
|