netlify-cli 15.9.0 → 15.9.1-rc.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/npm-shrinkwrap.json +749 -147
- package/package.json +6 -7
- package/scripts/postinstall.mjs +8 -8
- package/src/commands/base-command.mjs +195 -59
- package/src/commands/deploy/deploy.mjs +21 -9
- package/src/commands/dev/dev.mjs +21 -15
- package/src/commands/functions/functions-build.mjs +2 -2
- package/src/commands/functions/functions-create.mjs +0 -3
- package/src/commands/functions/functions-invoke.mjs +8 -5
- package/src/commands/init/init.mjs +1 -1
- package/src/commands/link/link.mjs +5 -5
- package/src/commands/main.mjs +1 -1
- package/src/commands/serve/serve.mjs +11 -5
- package/src/commands/sites/sites-create-template.mjs +1 -1
- package/src/commands/sites/sites-create.mjs +1 -1
- package/src/lib/completion/generate-autocompletion.mjs +4 -4
- package/src/lib/edge-functions/bootstrap.mjs +1 -1
- package/src/lib/edge-functions/internal.mjs +5 -3
- package/src/lib/edge-functions/proxy.mjs +30 -5
- package/src/lib/functions/registry.mjs +1 -3
- package/src/lib/functions/runtimes/js/builders/zisi.mjs +3 -8
- package/src/lib/functions/server.mjs +6 -6
- package/src/lib/spinner.mjs +1 -1
- package/src/recipes/vscode/index.mjs +1 -1
- package/src/utils/build-info.mjs +19 -0
- package/src/utils/command-helpers.mjs +16 -7
- package/src/utils/deploy/hash-fns.mjs +1 -2
- package/src/utils/detect-server-settings.mjs +148 -200
- package/src/utils/execa.mjs +4 -2
- package/src/utils/framework-server.mjs +2 -2
- package/src/utils/functions/functions.mjs +7 -0
- package/src/utils/functions/get-functions.mjs +2 -2
- package/src/utils/get-repo-data.mjs +5 -6
- package/src/utils/init/config-github.mjs +2 -2
- package/src/utils/init/config-manual.mjs +24 -7
- package/src/utils/init/utils.mjs +62 -63
- package/src/utils/proxy-server.mjs +7 -4
- package/src/utils/proxy.mjs +4 -0
- package/src/utils/run-build.mjs +58 -7
- package/src/utils/shell.mjs +14 -3
- package/src/utils/state-config.mjs +5 -1
- package/src/utils/static-server.mjs +4 -0
- package/src/utils/telemetry/report-error.mjs +8 -4
- package/src/utils/init/frameworks.mjs +0 -23
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { mkdir } from 'fs/promises'
|
|
3
3
|
|
|
4
|
+
import { zipFunctions } from '@netlify/zip-it-and-ship-it'
|
|
5
|
+
|
|
4
6
|
import { NETLIFYDEVERR, NETLIFYDEVLOG, exit, log } from '../../utils/command-helpers.mjs'
|
|
5
7
|
import { getFunctionsDir } from '../../utils/functions/index.mjs'
|
|
6
8
|
|
|
@@ -36,8 +38,6 @@ const functionsBuild = async (options, command) => {
|
|
|
36
38
|
|
|
37
39
|
log(`${NETLIFYDEVLOG} Building functions`)
|
|
38
40
|
|
|
39
|
-
const { zipFunctions } = await import('@netlify/zip-it-and-ship-it')
|
|
40
|
-
|
|
41
41
|
zipFunctions(src, dst, { skipGo: true })
|
|
42
42
|
log(`${NETLIFYDEVLOG} Functions built to `, dst)
|
|
43
43
|
}
|
|
@@ -12,7 +12,6 @@ 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'
|
|
16
15
|
import fetch from 'node-fetch'
|
|
17
16
|
import ora from 'ora'
|
|
18
17
|
|
|
@@ -172,8 +171,6 @@ const pickTemplate = async function ({ language: languageFromFlag }, funcType) {
|
|
|
172
171
|
language = languageFromPrompt
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)
|
|
176
|
-
|
|
177
174
|
let templatesForLanguage
|
|
178
175
|
|
|
179
176
|
try {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import fs from 'fs'
|
|
3
3
|
import { createRequire } from 'module'
|
|
4
4
|
import path from 'path'
|
|
5
|
-
import process from 'process'
|
|
6
5
|
|
|
7
6
|
import inquirer from 'inquirer'
|
|
8
7
|
import fetch from 'node-fetch'
|
|
@@ -56,14 +55,18 @@ const formatQstring = function (querystring) {
|
|
|
56
55
|
return ''
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
/**
|
|
60
|
-
|
|
58
|
+
/**
|
|
59
|
+
* process payloads from flag
|
|
60
|
+
* @param {string} payloadString
|
|
61
|
+
* @param {string} workingDir
|
|
62
|
+
*/
|
|
63
|
+
const processPayloadFromFlag = function (payloadString, workingDir) {
|
|
61
64
|
if (payloadString) {
|
|
62
65
|
// case 1: jsonstring
|
|
63
66
|
let payload = tryParseJSON(payloadString)
|
|
64
67
|
if (payload) return payload
|
|
65
68
|
// case 2: jsonpath
|
|
66
|
-
const payloadpath = path.join(
|
|
69
|
+
const payloadpath = path.join(workingDir, payloadString)
|
|
67
70
|
const pathexists = fs.existsSync(payloadpath)
|
|
68
71
|
if (pathexists) {
|
|
69
72
|
try {
|
|
@@ -210,7 +213,7 @@ const functionsInvoke = async (nameArgument, options, command) => {
|
|
|
210
213
|
// }
|
|
211
214
|
}
|
|
212
215
|
}
|
|
213
|
-
const payload = processPayloadFromFlag(options.payload)
|
|
216
|
+
const payload = processPayloadFromFlag(options.payload, command.workingDir)
|
|
214
217
|
body = { ...body, ...payload }
|
|
215
218
|
|
|
216
219
|
try {
|
|
@@ -196,7 +196,7 @@ export const init = async (options, command) => {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
// Look for local repo
|
|
199
|
-
const repoData = await getRepoData({ remoteName: options.gitRemoteName })
|
|
199
|
+
const repoData = await getRepoData({ workingDir: command.workingDir, remoteName: options.gitRemoteName })
|
|
200
200
|
if (repoData.error) {
|
|
201
201
|
await handleNoGitRemoteAndExit({ command, error: repoData.error, state })
|
|
202
202
|
}
|
|
@@ -11,11 +11,11 @@ import { track } from '../../utils/telemetry/index.mjs'
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
*
|
|
14
|
-
* @param {import('../base-command.mjs').
|
|
14
|
+
* @param {import('../base-command.mjs').default} command
|
|
15
15
|
* @param {import('commander').OptionValues} options
|
|
16
16
|
*/
|
|
17
|
-
const linkPrompt = async (
|
|
18
|
-
const { api, state } = netlify
|
|
17
|
+
const linkPrompt = async (command, options) => {
|
|
18
|
+
const { api, state } = command.netlify
|
|
19
19
|
|
|
20
20
|
const SITE_NAME_PROMPT = 'Search by full or partial site name'
|
|
21
21
|
const SITE_LIST_PROMPT = 'Choose from a list of your recently updated sites'
|
|
@@ -24,7 +24,7 @@ const linkPrompt = async (netlify, options) => {
|
|
|
24
24
|
let GIT_REMOTE_PROMPT = 'Use the current git remote origin URL'
|
|
25
25
|
let site
|
|
26
26
|
// Get git remote data if exists
|
|
27
|
-
const repoData = await getRepoData({ remoteName: options.gitRemoteName })
|
|
27
|
+
const repoData = await getRepoData({ workingDir: command.workingDir, remoteName: options.gitRemoteName })
|
|
28
28
|
|
|
29
29
|
let linkChoices = [SITE_NAME_PROMPT, SITE_LIST_PROMPT, SITE_ID_PROMPT]
|
|
30
30
|
|
|
@@ -326,7 +326,7 @@ export const link = async (options, command) => {
|
|
|
326
326
|
kind: 'byName',
|
|
327
327
|
})
|
|
328
328
|
} else {
|
|
329
|
-
siteData = await linkPrompt(command
|
|
329
|
+
siteData = await linkPrompt(command, options)
|
|
330
330
|
}
|
|
331
331
|
return siteData
|
|
332
332
|
}
|
package/src/commands/main.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import process from 'process'
|
|
3
3
|
|
|
4
4
|
import { Option } from 'commander'
|
|
5
|
+
import envinfo from 'envinfo'
|
|
5
6
|
import { closest } from 'fastest-levenshtein'
|
|
6
7
|
import inquirer from 'inquirer'
|
|
7
8
|
|
|
@@ -39,7 +40,6 @@ const SUGGESTION_TIMEOUT = 1e4
|
|
|
39
40
|
const getVersionPage = async () => {
|
|
40
41
|
// performance optimization - load envinfo on demand
|
|
41
42
|
|
|
42
|
-
const envinfo = await import('envinfo')
|
|
43
43
|
const data = await envinfo.run({
|
|
44
44
|
System: ['OS', 'CPU'],
|
|
45
45
|
Binaries: ['Node', 'Yarn', 'npm'],
|
|
@@ -69,10 +69,10 @@ const serve = async (options, command) => {
|
|
|
69
69
|
// Netlify Build are loaded.
|
|
70
70
|
await getInternalFunctionsDir({ base: site.root, ensureExists: true })
|
|
71
71
|
|
|
72
|
-
/** @type {Partial<import('../../utils/types').ServerSettings>} */
|
|
72
|
+
/** @type {Partial<import('../../utils/types.js').ServerSettings>} */
|
|
73
73
|
let settings = {}
|
|
74
74
|
try {
|
|
75
|
-
settings = await detectServerSettings(devConfig, options,
|
|
75
|
+
settings = await detectServerSettings(devConfig, options, command)
|
|
76
76
|
|
|
77
77
|
cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
|
|
78
78
|
} catch (error_) {
|
|
@@ -87,7 +87,13 @@ const serve = async (options, command) => {
|
|
|
87
87
|
`${NETLIFYDEVWARN} Changes will not be hot-reloaded, so if you need to rebuild your site you must exit and run 'netlify serve' again`,
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
const { configPath: configPathOverride } = await runBuildTimeline({
|
|
90
|
+
const { configPath: configPathOverride } = await runBuildTimeline({
|
|
91
|
+
cachedConfig,
|
|
92
|
+
options,
|
|
93
|
+
settings,
|
|
94
|
+
projectDir: command.workingDir,
|
|
95
|
+
site,
|
|
96
|
+
})
|
|
91
97
|
|
|
92
98
|
await startFunctionsServer({
|
|
93
99
|
api,
|
|
@@ -117,8 +123,7 @@ const serve = async (options, command) => {
|
|
|
117
123
|
|
|
118
124
|
// TODO: We should consolidate this with the existing config watcher.
|
|
119
125
|
const getUpdatedConfig = async () => {
|
|
120
|
-
const
|
|
121
|
-
const { config: newConfig } = await command.getConfig({ cwd, offline: true, state })
|
|
126
|
+
const { config: newConfig } = await command.getConfig({ cwd: command.workingDir, offline: true, state })
|
|
122
127
|
const normalizedNewConfig = normalizeConfig(newConfig)
|
|
123
128
|
|
|
124
129
|
return normalizedNewConfig
|
|
@@ -135,6 +140,7 @@ const serve = async (options, command) => {
|
|
|
135
140
|
getUpdatedConfig,
|
|
136
141
|
inspectSettings,
|
|
137
142
|
offline: options.offline,
|
|
143
|
+
projectDir: command.workingDir,
|
|
138
144
|
settings,
|
|
139
145
|
site,
|
|
140
146
|
siteInfo,
|
|
@@ -197,7 +197,7 @@ const sitesCreateTemplate = async (repository, options, command) => {
|
|
|
197
197
|
|
|
198
198
|
if (options.withCi) {
|
|
199
199
|
log('Configuring CI')
|
|
200
|
-
const repoData = await getRepoData()
|
|
200
|
+
const repoData = await getRepoData({ workingDir: command.workingDir })
|
|
201
201
|
await configureRepo({ command, siteId: site.id, repoData, manual: options.manual })
|
|
202
202
|
}
|
|
203
203
|
|
|
@@ -102,7 +102,7 @@ export const sitesCreate = async (options, command) => {
|
|
|
102
102
|
|
|
103
103
|
if (options.withCi) {
|
|
104
104
|
log('Configuring CI')
|
|
105
|
-
const repoData = await getRepoData()
|
|
105
|
+
const repoData = await getRepoData({ workingDir: command.workingDir })
|
|
106
106
|
await configureRepo({ command, siteId: site.id, repoData, manual: options.manual })
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import
|
|
2
|
+
import fs from 'fs'
|
|
3
3
|
import { dirname } from 'path'
|
|
4
4
|
|
|
5
5
|
import { sortOptions, warn } from '../../utils/command-helpers.mjs'
|
|
@@ -28,10 +28,10 @@ const generateAutocompletion = (program) => {
|
|
|
28
28
|
{},
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
if (!existsSync(dirname(AUTOCOMPLETION_FILE))) {
|
|
32
|
-
mkdirSync(dirname(AUTOCOMPLETION_FILE), { recursive: true })
|
|
31
|
+
if (!fs.existsSync(dirname(AUTOCOMPLETION_FILE))) {
|
|
32
|
+
fs.mkdirSync(dirname(AUTOCOMPLETION_FILE), { recursive: true })
|
|
33
33
|
}
|
|
34
|
-
writeFileSync(AUTOCOMPLETION_FILE, JSON.stringify(autocomplete), 'utf-8')
|
|
34
|
+
fs.writeFileSync(AUTOCOMPLETION_FILE, JSON.stringify(autocomplete), 'utf-8')
|
|
35
35
|
} catch (error_) {
|
|
36
36
|
// Sometimes it can happen that the autocomplete generation in the postinstall script lacks permissions
|
|
37
37
|
// to write files to the home directory of the user. Therefore just warn with the error and don't break install.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { env } from 'process'
|
|
2
2
|
|
|
3
|
-
const latestBootstrapURL = 'https://
|
|
3
|
+
const latestBootstrapURL = 'https://64ae60d920fd0f000865bcfc--edge.netlify.com/bootstrap/index-combined.ts'
|
|
4
4
|
|
|
5
5
|
export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { readFile, stat } from 'fs/promises'
|
|
3
3
|
import { dirname, join, resolve } from 'path'
|
|
4
|
-
import { cwd } from 'process'
|
|
5
4
|
|
|
6
5
|
import { getPathInProject } from '../settings.mjs'
|
|
7
6
|
|
|
8
7
|
import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from './consts.mjs'
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} workingDir
|
|
11
|
+
*/
|
|
12
|
+
export const getInternalFunctions = async (workingDir) => {
|
|
13
|
+
const path = join(workingDir, getPathInProject([INTERNAL_EDGE_FUNCTIONS_FOLDER]))
|
|
12
14
|
|
|
13
15
|
try {
|
|
14
16
|
const stats = await stat(path)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { Buffer } from 'buffer'
|
|
3
3
|
import { relative } from 'path'
|
|
4
|
-
import {
|
|
4
|
+
import { env } from 'process'
|
|
5
5
|
|
|
6
|
+
// eslint-disable-next-line import/no-namespace
|
|
7
|
+
import * as bundler from '@netlify/edge-bundler'
|
|
6
8
|
import getAvailablePort from 'get-port'
|
|
7
9
|
|
|
8
10
|
import { NETLIFYDEVERR, NETLIFYDEVWARN, chalk, error as printError, log } from '../../utils/command-helpers.mjs'
|
|
@@ -60,6 +62,26 @@ export const createAccountInfoHeader = (accountInfo = {}) => {
|
|
|
60
62
|
return Buffer.from(accountString).toString('base64')
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @param {object} config
|
|
68
|
+
* @param {*} config.accountId
|
|
69
|
+
* @param {*} config.config
|
|
70
|
+
* @param {*} config.configPath
|
|
71
|
+
* @param {*} config.debug
|
|
72
|
+
* @param {*} config.env
|
|
73
|
+
* @param {*} config.geoCountry
|
|
74
|
+
* @param {*} config.geolocationMode
|
|
75
|
+
* @param {*} config.getUpdatedConfig
|
|
76
|
+
* @param {*} config.inspectSettings
|
|
77
|
+
* @param {*} config.mainPort
|
|
78
|
+
* @param {boolean=} config.offline
|
|
79
|
+
* @param {*} config.passthroughPort
|
|
80
|
+
* @param {*} config.projectDir
|
|
81
|
+
* @param {*} config.siteInfo
|
|
82
|
+
* @param {*} config.state
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
63
85
|
export const initializeProxy = async ({
|
|
64
86
|
accountId,
|
|
65
87
|
config,
|
|
@@ -77,7 +99,11 @@ export const initializeProxy = async ({
|
|
|
77
99
|
siteInfo,
|
|
78
100
|
state,
|
|
79
101
|
}) => {
|
|
80
|
-
const {
|
|
102
|
+
const {
|
|
103
|
+
functions: internalFunctions,
|
|
104
|
+
importMap,
|
|
105
|
+
path: internalFunctionsPath,
|
|
106
|
+
} = await getInternalFunctions(projectDir)
|
|
81
107
|
const userFunctionsPath = config.build.edge_functions
|
|
82
108
|
const isolatePort = await getAvailablePort()
|
|
83
109
|
|
|
@@ -112,7 +138,7 @@ export const initializeProxy = async ({
|
|
|
112
138
|
if (!registry) return
|
|
113
139
|
|
|
114
140
|
// Setting header with geolocation and site info.
|
|
115
|
-
req.headers[headers.Geo] = JSON.stringify(geoLocation)
|
|
141
|
+
req.headers[headers.Geo] = Buffer.from(JSON.stringify(geoLocation)).toString('base64')
|
|
116
142
|
req.headers[headers.Site] = createSiteInfoHeader(siteInfo)
|
|
117
143
|
req.headers[headers.Account] = createAccountInfoHeader({ id: accountId })
|
|
118
144
|
|
|
@@ -130,7 +156,7 @@ export const initializeProxy = async ({
|
|
|
130
156
|
)} matches declaration for edge function ${chalk.yellow(
|
|
131
157
|
functionName,
|
|
132
158
|
)}, but there's no matching function file in ${chalk.yellow(
|
|
133
|
-
relative(
|
|
159
|
+
relative(projectDir, userFunctionsPath),
|
|
134
160
|
)}. Please visit ${chalk.blue('https://ntl.fyi/edge-create')} for more information.`,
|
|
135
161
|
)
|
|
136
162
|
})
|
|
@@ -185,7 +211,6 @@ const prepareServer = async ({
|
|
|
185
211
|
const importMapPaths = [...importMaps, config.functions['*'].deno_import_map]
|
|
186
212
|
|
|
187
213
|
try {
|
|
188
|
-
const bundler = await import('@netlify/edge-bundler')
|
|
189
214
|
const distImportMapPath = getPathInProject([DIST_IMPORT_MAP_PATH])
|
|
190
215
|
const runIsolate = await bundler.serve({
|
|
191
216
|
...getDownloadUpdateFunctions(),
|
|
@@ -3,6 +3,7 @@ import { mkdir } from 'fs/promises'
|
|
|
3
3
|
import { extname, isAbsolute, join, resolve } from 'path'
|
|
4
4
|
import { env } from 'process'
|
|
5
5
|
|
|
6
|
+
import { listFunctions } from '@netlify/zip-it-and-ship-it'
|
|
6
7
|
import extractZip from 'extract-zip'
|
|
7
8
|
|
|
8
9
|
import {
|
|
@@ -175,9 +176,6 @@ export class FunctionsRegistry {
|
|
|
175
176
|
// This function is here so we can mock it in tests
|
|
176
177
|
// eslint-disable-next-line class-methods-use-this
|
|
177
178
|
async listFunctions(...args) {
|
|
178
|
-
// Performance optimization: load '@netlify/zip-it-and-ship-it' on demand.
|
|
179
|
-
const { listFunctions } = await import('@netlify/zip-it-and-ship-it')
|
|
180
|
-
|
|
181
179
|
return await listFunctions(...args)
|
|
182
180
|
}
|
|
183
181
|
|
|
@@ -2,6 +2,7 @@ import { mkdir, writeFile } from 'fs/promises'
|
|
|
2
2
|
import { createRequire } from 'module'
|
|
3
3
|
import path from 'path'
|
|
4
4
|
|
|
5
|
+
import { zipFunction, listFunction } from '@netlify/zip-it-and-ship-it'
|
|
5
6
|
import decache from 'decache'
|
|
6
7
|
import { readPackageUp } from 'read-pkg-up'
|
|
7
8
|
import sourceMapSupport from 'source-map-support'
|
|
@@ -44,9 +45,6 @@ const buildFunction = async ({
|
|
|
44
45
|
}
|
|
45
46
|
const functionDirectory = path.dirname(func.mainFile)
|
|
46
47
|
|
|
47
|
-
// performance
|
|
48
|
-
const { zipFunction } = await import('@netlify/zip-it-and-ship-it')
|
|
49
|
-
|
|
50
48
|
// If we have a function at `functions/my-func/index.js` and we pass
|
|
51
49
|
// that path to `zipFunction`, it will lack the context of the whole
|
|
52
50
|
// functions directory and will infer the name of the function to be
|
|
@@ -92,15 +90,12 @@ const buildFunction = async ({
|
|
|
92
90
|
* @param {string} params.mainFile
|
|
93
91
|
* @param {string} params.projectRoot
|
|
94
92
|
*/
|
|
95
|
-
export const parseFunctionForMetadata = async ({ config, mainFile, projectRoot }) =>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return await listFunction(mainFile, {
|
|
93
|
+
export const parseFunctionForMetadata = async ({ config, mainFile, projectRoot }) =>
|
|
94
|
+
await listFunction(mainFile, {
|
|
99
95
|
config: netlifyConfigToZisiConfig({ config, projectRoot }),
|
|
100
96
|
featureFlags: { zisi_functions_api_v2: true },
|
|
101
97
|
parseISC: true,
|
|
102
98
|
})
|
|
103
|
-
}
|
|
104
99
|
|
|
105
100
|
// Clears the cache for any files inside the directory from which functions are
|
|
106
101
|
// served.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
import { Buffer } from 'buffer'
|
|
3
|
+
|
|
4
|
+
import express from 'express'
|
|
5
|
+
import expressLogging from 'express-logging'
|
|
2
6
|
import jwtDecode from 'jwt-decode'
|
|
3
7
|
|
|
4
8
|
import { NETLIFYDEVERR, NETLIFYDEVLOG, error as errorExit, log } from '../../utils/command-helpers.mjs'
|
|
@@ -45,7 +49,6 @@ const hasBody = (req) =>
|
|
|
45
49
|
// eslint-disable-next-line unicorn/prefer-number-properties
|
|
46
50
|
(req.header('transfer-encoding') !== undefined || !isNaN(req.header('content-length'))) &&
|
|
47
51
|
// we expect a string or a buffer, because we use the two bodyParsers(text, raw) from express
|
|
48
|
-
// eslint-disable-next-line n/prefer-global/buffer
|
|
49
52
|
(typeof req.body === 'string' || Buffer.isBuffer(req.body))
|
|
50
53
|
|
|
51
54
|
export const createHandler = function (options) {
|
|
@@ -112,7 +115,7 @@ export const createHandler = function (options) {
|
|
|
112
115
|
'client-ip': [remoteAddress],
|
|
113
116
|
'x-nf-client-connection-ip': [remoteAddress],
|
|
114
117
|
'x-nf-account-id': [options.accountId],
|
|
115
|
-
[efHeaders.Geo]: JSON.stringify(geoLocation),
|
|
118
|
+
[efHeaders.Geo]: Buffer.from(JSON.stringify(geoLocation)).toString('base64'),
|
|
116
119
|
}).reduce((prev, [key, value]) => ({ ...prev, [key]: Array.isArray(value) ? value : [value] }), {})
|
|
117
120
|
const rawQuery = new URLSearchParams(requestQuery).toString()
|
|
118
121
|
const protocol = options.config?.dev?.https ? 'https' : 'http'
|
|
@@ -185,11 +188,8 @@ export const createHandler = function (options) {
|
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
190
|
|
|
188
|
-
const getFunctionsServer =
|
|
191
|
+
const getFunctionsServer = (options) => {
|
|
189
192
|
const { buildersPrefix = '', functionsPrefix = '', functionsRegistry, siteUrl } = options
|
|
190
|
-
// performance optimization, load express on demand
|
|
191
|
-
const { default: express } = await import('express')
|
|
192
|
-
const { default: expressLogging } = await import('express-logging')
|
|
193
193
|
const app = express()
|
|
194
194
|
const functionHandler = createHandler(options)
|
|
195
195
|
|
package/src/lib/spinner.mjs
CHANGED
|
@@ -17,7 +17,7 @@ export const startSpinner = ({ text }) =>
|
|
|
17
17
|
* Stops the spinner with the following text
|
|
18
18
|
* @param {object} config
|
|
19
19
|
* @param {ora.Ora} config.spinner
|
|
20
|
-
* @param {
|
|
20
|
+
* @param {boolean} [config.error]
|
|
21
21
|
* @param {string} [config.text]
|
|
22
22
|
* @returns {void}
|
|
23
23
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join } from 'path'
|
|
2
2
|
|
|
3
|
+
import { DenoBridge } from '@netlify/edge-bundler'
|
|
3
4
|
import execa from 'execa'
|
|
4
5
|
import inquirer from 'inquirer'
|
|
5
6
|
|
|
@@ -49,7 +50,6 @@ const getDenoExtPrompt = () => {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
export const run = async ({ config, repositoryRoot }) => {
|
|
52
|
-
const { DenoBridge } = await import('@netlify/edge-bundler')
|
|
53
53
|
const deno = new DenoBridge({
|
|
54
54
|
onBeforeDownload: () =>
|
|
55
55
|
log(`${NETLIFYDEVWARN} Setting up the Edge Functions environment. This may take a couple of minutes.`),
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Detects and filters the build setting for a project and a command
|
|
5
|
+
* @param {import('../commands/base-command.mjs').default} command
|
|
6
|
+
*/
|
|
7
|
+
export const detectBuildSettings = async (command) => {
|
|
8
|
+
const { project, workspacePackage } = command
|
|
9
|
+
const buildSettings = await project.getBuildSettings(project.workspace ? workspacePackage : '')
|
|
10
|
+
return buildSettings
|
|
11
|
+
.filter((setting) => {
|
|
12
|
+
if (project.workspace && project.relativeBaseDirectory && setting.packagePath) {
|
|
13
|
+
return project.relativeBaseDirectory.startsWith(setting.packagePath)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return true
|
|
17
|
+
})
|
|
18
|
+
.filter((setting) => setting.devCommand)
|
|
19
|
+
}
|
|
@@ -24,7 +24,7 @@ const argv = process.argv.slice(2)
|
|
|
24
24
|
* Chalk instance for CLI that can be initialized with no colors mode
|
|
25
25
|
* needed for json outputs where we don't want to have colors
|
|
26
26
|
* @param {boolean} noColors - disable chalk colors
|
|
27
|
-
* @return {
|
|
27
|
+
* @return {import('chalk').ChalkInstance} - default or custom chalk instance
|
|
28
28
|
*/
|
|
29
29
|
const safeChalk = function (noColors) {
|
|
30
30
|
if (noColors) {
|
|
@@ -174,12 +174,18 @@ export const warn = (message = '') => {
|
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* throws an error or log it
|
|
177
|
-
* @param {
|
|
177
|
+
* @param {unknown} message
|
|
178
178
|
* @param {object} [options]
|
|
179
179
|
* @param {boolean} [options.exit]
|
|
180
180
|
*/
|
|
181
181
|
export const error = (message = '', options = {}) => {
|
|
182
|
-
const err =
|
|
182
|
+
const err =
|
|
183
|
+
message instanceof Error
|
|
184
|
+
? message
|
|
185
|
+
: // eslint-disable-next-line unicorn/no-nested-ternary
|
|
186
|
+
typeof message === 'string'
|
|
187
|
+
? new Error(message)
|
|
188
|
+
: /** @type {Error} */ ({ message, stack: undefined, name: 'Error' })
|
|
183
189
|
|
|
184
190
|
if (options.exit === false) {
|
|
185
191
|
const bang = chalk.red(BANG)
|
|
@@ -198,10 +204,13 @@ export const exit = (code = 0) => {
|
|
|
198
204
|
process.exit(code)
|
|
199
205
|
}
|
|
200
206
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
207
|
+
/**
|
|
208
|
+
* When `build.publish` is not set by the user, the CLI behavior differs in
|
|
209
|
+
* several ways. It detects it by checking if `build.publish` is `undefined`.
|
|
210
|
+
* However, `@netlify/config` adds a default value to `build.publish`.
|
|
211
|
+
* This removes 'publish' and 'publishOrigin' in this case.
|
|
212
|
+
* @param {*} config
|
|
213
|
+
*/
|
|
205
214
|
export const normalizeConfig = (config) => {
|
|
206
215
|
// Unused var here is in order to omit 'publish' from build config
|
|
207
216
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -2,6 +2,7 @@ import { readFile } from 'fs/promises'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { promisify } from 'util'
|
|
4
4
|
|
|
5
|
+
import { zipFunctions } from '@netlify/zip-it-and-ship-it'
|
|
5
6
|
import fromArray from 'from2-array'
|
|
6
7
|
import pumpModule from 'pump'
|
|
7
8
|
|
|
@@ -66,8 +67,6 @@ const getFunctionZips = async ({
|
|
|
66
67
|
})
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
const { zipFunctions } = await import('@netlify/zip-it-and-ship-it')
|
|
70
|
-
|
|
71
70
|
return await zipFunctions(directories, tmpDir, {
|
|
72
71
|
basePath: rootDir,
|
|
73
72
|
configFileDirectories: [getPathInProject([INTERNAL_FUNCTIONS_FOLDER])],
|