netlify-cli 15.10.0 → 16.0.0-alpha.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 +6 -5
- package/npm-shrinkwrap.json +628 -42
- package/package.json +4 -5
- package/src/commands/base-command.mjs +295 -118
- package/src/commands/build/build.mjs +9 -1
- package/src/commands/deploy/deploy.mjs +42 -18
- package/src/commands/dev/dev.mjs +22 -17
- package/src/commands/functions/functions-create.mjs +118 -89
- package/src/commands/functions/functions-invoke.mjs +10 -7
- package/src/commands/functions/functions-list.mjs +3 -3
- package/src/commands/functions/functions-serve.mjs +1 -0
- package/src/commands/init/init.mjs +1 -1
- package/src/commands/link/link.mjs +5 -5
- package/src/commands/serve/serve.mjs +10 -6
- package/src/commands/sites/sites-create-template.mjs +1 -1
- package/src/commands/sites/sites-create.mjs +1 -1
- package/src/functions-templates/javascript/google-analytics/package.json +1 -1
- package/src/functions-templates/typescript/scheduled-function/package.json +1 -1
- package/src/lib/edge-functions/deploy.mjs +11 -4
- package/src/lib/edge-functions/internal.mjs +5 -3
- package/src/lib/edge-functions/proxy.mjs +29 -5
- package/src/lib/functions/netlify-function.mjs +26 -1
- package/src/lib/functions/registry.mjs +14 -26
- package/src/lib/functions/runtimes/js/builders/zisi.mjs +20 -3
- package/src/lib/functions/runtimes/js/worker.mjs +1 -1
- package/src/lib/functions/server.mjs +3 -2
- package/src/lib/spinner.mjs +1 -1
- package/src/recipes/vscode/index.mjs +24 -6
- package/src/utils/build-info.mjs +100 -0
- package/src/utils/command-helpers.mjs +16 -7
- package/src/utils/deploy/deploy-site.mjs +4 -4
- package/src/utils/deploy/hash-fns.mjs +2 -2
- package/src/utils/detect-server-settings.mjs +133 -245
- package/src/utils/framework-server.mjs +6 -5
- package/src/utils/functions/functions.mjs +8 -5
- 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 +68 -68
- package/src/utils/proxy-server.mjs +7 -4
- package/src/utils/proxy.mjs +4 -3
- package/src/utils/read-repo-url.mjs +4 -0
- package/src/utils/run-build.mjs +58 -32
- package/src/utils/shell.mjs +24 -7
- package/src/utils/state-config.mjs +5 -1
- package/src/utils/static-server.mjs +4 -0
- package/src/utils/init/frameworks.mjs +0 -23
|
@@ -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 {
|
|
@@ -141,11 +144,11 @@ const getFunctionToTrigger = function (options, argumentName) {
|
|
|
141
144
|
* @param {import('../base-command.mjs').default} command
|
|
142
145
|
*/
|
|
143
146
|
const functionsInvoke = async (nameArgument, options, command) => {
|
|
144
|
-
const { config } = command.netlify
|
|
147
|
+
const { config, relConfigFilePath } = command.netlify
|
|
145
148
|
|
|
146
149
|
const functionsDir = options.functions || (config.dev && config.dev.functions) || config.functionsDirectory
|
|
147
150
|
if (typeof functionsDir === 'undefined') {
|
|
148
|
-
error(
|
|
151
|
+
error(`Functions directory is undefined, did you forget to set it in ${relConfigFilePath}?`)
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
if (!options.port)
|
|
@@ -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 {
|
|
@@ -16,7 +16,7 @@ const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) {
|
|
|
16
16
|
* @param {import('../base-command.mjs').default} command
|
|
17
17
|
*/
|
|
18
18
|
const functionsList = async (options, command) => {
|
|
19
|
-
const { config, siteInfo } = command.netlify
|
|
19
|
+
const { config, relConfigFilePath, siteInfo } = command.netlify
|
|
20
20
|
|
|
21
21
|
const deploy = siteInfo.published_deploy || {}
|
|
22
22
|
const deployedFunctions = deploy.available_functions || []
|
|
@@ -25,8 +25,8 @@ const functionsList = async (options, command) => {
|
|
|
25
25
|
|
|
26
26
|
if (typeof functionsDir === 'undefined') {
|
|
27
27
|
log('Functions directory is undefined')
|
|
28
|
-
log(
|
|
29
|
-
log('
|
|
28
|
+
log(`Please verify that 'functions.directory' is set in your Netlify configuration file ${relConfigFilePath}`)
|
|
29
|
+
log('Refer to https://docs.netlify.com/configure-builds/file-based-configuration/ for more information')
|
|
30
30
|
exit(1)
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -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
|
}
|
|
@@ -38,6 +38,7 @@ const serve = async (options, command) => {
|
|
|
38
38
|
const devConfig = {
|
|
39
39
|
...(config.functionsDirectory && { functions: config.functionsDirectory }),
|
|
40
40
|
...(config.build.publish && { publish: config.build.publish }),
|
|
41
|
+
|
|
41
42
|
...config.dev,
|
|
42
43
|
...options,
|
|
43
44
|
// Override the `framework` value so that we start a static server and not
|
|
@@ -69,10 +70,9 @@ const serve = async (options, command) => {
|
|
|
69
70
|
// Netlify Build are loaded.
|
|
70
71
|
await getInternalFunctionsDir({ base: site.root, ensureExists: true })
|
|
71
72
|
|
|
72
|
-
/** @type {
|
|
73
|
-
let settings = {}
|
|
73
|
+
let settings = /** @type {import('../../utils/types.js').ServerSettings} */ ({})
|
|
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,11 @@ 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
|
+
command,
|
|
92
|
+
settings,
|
|
93
|
+
options,
|
|
94
|
+
})
|
|
91
95
|
|
|
92
96
|
await startFunctionsServer({
|
|
93
97
|
api,
|
|
@@ -117,8 +121,7 @@ const serve = async (options, command) => {
|
|
|
117
121
|
|
|
118
122
|
// TODO: We should consolidate this with the existing config watcher.
|
|
119
123
|
const getUpdatedConfig = async () => {
|
|
120
|
-
const
|
|
121
|
-
const { config: newConfig } = await command.getConfig({ cwd, offline: true, state })
|
|
124
|
+
const { config: newConfig } = await command.getConfig({ cwd: command.workingDir, offline: true, state })
|
|
122
125
|
const normalizedNewConfig = normalizeConfig(newConfig)
|
|
123
126
|
|
|
124
127
|
return normalizedNewConfig
|
|
@@ -135,6 +138,7 @@ const serve = async (options, command) => {
|
|
|
135
138
|
getUpdatedConfig,
|
|
136
139
|
inspectSettings,
|
|
137
140
|
offline: options.offline,
|
|
141
|
+
projectDir: command.workingDir,
|
|
138
142
|
settings,
|
|
139
143
|
site,
|
|
140
144
|
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
|
|
|
@@ -8,8 +8,12 @@ import { EDGE_FUNCTIONS_FOLDER, PUBLIC_URL_PATH } from './consts.mjs'
|
|
|
8
8
|
|
|
9
9
|
const distPath = getPathInProject([EDGE_FUNCTIONS_FOLDER])
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} workingDir
|
|
13
|
+
* @param {*} file
|
|
14
|
+
*/
|
|
15
|
+
export const deployFileNormalizer = (workingDir, file) => {
|
|
16
|
+
const absoluteDistPath = join(workingDir, distPath)
|
|
13
17
|
const isEdgeFunction = file.root === absoluteDistPath
|
|
14
18
|
const normalizedPath = isEdgeFunction ? `${PUBLIC_URL_PATH}/${file.normalizedPath}` : file.normalizedPath
|
|
15
19
|
|
|
@@ -19,9 +23,12 @@ export const deployFileNormalizer = (rootDir, file) => {
|
|
|
19
23
|
}
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} workingDir
|
|
28
|
+
*/
|
|
29
|
+
export const getDistPathIfExists = async (workingDir) => {
|
|
23
30
|
try {
|
|
24
|
-
const absoluteDistPath = join(
|
|
31
|
+
const absoluteDistPath = join(workingDir, distPath)
|
|
25
32
|
const stats = await stat(absoluteDistPath)
|
|
26
33
|
|
|
27
34
|
if (!stats.isDirectory()) {
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { Buffer } from 'buffer'
|
|
3
|
-
import { relative } from 'path'
|
|
4
|
-
import {
|
|
3
|
+
import { join, relative } from 'path'
|
|
4
|
+
import { env } from 'process'
|
|
5
5
|
|
|
6
6
|
// eslint-disable-next-line import/no-namespace
|
|
7
7
|
import * as bundler from '@netlify/edge-bundler'
|
|
@@ -62,6 +62,26 @@ export const createAccountInfoHeader = (accountInfo = {}) => {
|
|
|
62
62
|
return Buffer.from(accountString).toString('base64')
|
|
63
63
|
}
|
|
64
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
|
+
*/
|
|
65
85
|
export const initializeProxy = async ({
|
|
66
86
|
accountId,
|
|
67
87
|
config,
|
|
@@ -79,7 +99,11 @@ export const initializeProxy = async ({
|
|
|
79
99
|
siteInfo,
|
|
80
100
|
state,
|
|
81
101
|
}) => {
|
|
82
|
-
const {
|
|
102
|
+
const {
|
|
103
|
+
functions: internalFunctions,
|
|
104
|
+
importMap,
|
|
105
|
+
path: internalFunctionsPath,
|
|
106
|
+
} = await getInternalFunctions(projectDir)
|
|
83
107
|
const userFunctionsPath = config.build.edge_functions
|
|
84
108
|
const isolatePort = await getAvailablePort()
|
|
85
109
|
|
|
@@ -133,7 +157,7 @@ export const initializeProxy = async ({
|
|
|
133
157
|
)} matches declaration for edge function ${chalk.yellow(
|
|
134
158
|
functionName,
|
|
135
159
|
)}, but there's no matching function file in ${chalk.yellow(
|
|
136
|
-
relative(
|
|
160
|
+
relative(projectDir, userFunctionsPath),
|
|
137
161
|
)}. Please visit ${chalk.blue('https://ntl.fyi/edge-create')} for more information.`,
|
|
138
162
|
)
|
|
139
163
|
})
|
|
@@ -193,7 +217,7 @@ const prepareServer = async ({
|
|
|
193
217
|
...getDownloadUpdateFunctions(),
|
|
194
218
|
bootstrapURL: getBootstrapURL(),
|
|
195
219
|
debug: env.NETLIFY_DENO_DEBUG === 'true',
|
|
196
|
-
distImportMapPath,
|
|
220
|
+
distImportMapPath: join(projectDir, distImportMapPath),
|
|
197
221
|
formatExportTypeError: (name) =>
|
|
198
222
|
`${NETLIFYDEVERR} ${chalk.red('Failed')} to load Edge Function ${chalk.yellow(
|
|
199
223
|
name,
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
import { version as nodeVersion } from 'process'
|
|
3
|
+
|
|
2
4
|
import CronParser from 'cron-parser'
|
|
5
|
+
import semver from 'semver'
|
|
3
6
|
|
|
4
7
|
import { error as errorExit } from '../../utils/command-helpers.mjs'
|
|
5
8
|
import { BACKGROUND } from '../../utils/functions/get-functions.mjs'
|
|
6
9
|
|
|
10
|
+
const V2_MIN_NODE_VERSION = '18.0.0'
|
|
11
|
+
|
|
7
12
|
// Returns a new set with all elements of `setA` that don't exist in `setB`.
|
|
8
13
|
const difference = (setA, setB) => new Set([...setA].filter((item) => !setB.has(item)))
|
|
9
14
|
|
|
@@ -27,12 +32,13 @@ export default class NetlifyFunction {
|
|
|
27
32
|
timeoutBackground,
|
|
28
33
|
timeoutSynchronous,
|
|
29
34
|
}) {
|
|
35
|
+
this.buildError = null
|
|
30
36
|
this.config = config
|
|
31
37
|
this.directory = directory
|
|
32
38
|
this.errorExit = errorExit
|
|
33
39
|
this.mainFile = mainFile
|
|
34
40
|
this.name = name
|
|
35
|
-
this.displayName = displayName
|
|
41
|
+
this.displayName = displayName ?? name
|
|
36
42
|
this.projectRoot = projectRoot
|
|
37
43
|
this.runtime = runtime
|
|
38
44
|
this.timeoutBackground = timeoutBackground
|
|
@@ -63,6 +69,10 @@ export default class NetlifyFunction {
|
|
|
63
69
|
return Boolean(this.schedule)
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
isSupported() {
|
|
73
|
+
return !(this.buildData?.runtimeAPIVersion === 2 && semver.lt(nodeVersion, V2_MIN_NODE_VERSION))
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
async getNextRun() {
|
|
67
77
|
if (!(await this.isScheduled())) {
|
|
68
78
|
return null
|
|
@@ -93,11 +103,22 @@ export default class NetlifyFunction {
|
|
|
93
103
|
const srcFilesDiff = this.getSrcFilesDiff(srcFilesSet)
|
|
94
104
|
|
|
95
105
|
this.buildData = buildData
|
|
106
|
+
this.buildError = null
|
|
96
107
|
this.srcFiles = srcFilesSet
|
|
97
108
|
this.schedule = schedule || this.schedule
|
|
98
109
|
|
|
110
|
+
if (!this.isSupported()) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Function requires Node.js version ${V2_MIN_NODE_VERSION} or above, but ${nodeVersion.slice(
|
|
113
|
+
1,
|
|
114
|
+
)} is installed. Refer to https://ntl.fyi/functions-node18 for information on how to update.`,
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
return { includedFiles, srcFilesDiff }
|
|
100
119
|
} catch (error) {
|
|
120
|
+
this.buildError = error
|
|
121
|
+
|
|
101
122
|
return { error }
|
|
102
123
|
}
|
|
103
124
|
}
|
|
@@ -118,6 +139,10 @@ export default class NetlifyFunction {
|
|
|
118
139
|
async invoke(event, context) {
|
|
119
140
|
await this.buildQueue
|
|
120
141
|
|
|
142
|
+
if (this.buildError) {
|
|
143
|
+
return { result: null, error: { errorMessage: this.buildError.message } }
|
|
144
|
+
}
|
|
145
|
+
|
|
121
146
|
const timeout = this.isBackground ? this.timeoutBackground : this.timeoutSynchronous
|
|
122
147
|
|
|
123
148
|
try {
|
|
@@ -6,15 +6,7 @@ import { env } from 'process'
|
|
|
6
6
|
import { listFunctions } from '@netlify/zip-it-and-ship-it'
|
|
7
7
|
import extractZip from 'extract-zip'
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
chalk,
|
|
11
|
-
getTerminalLink,
|
|
12
|
-
log,
|
|
13
|
-
NETLIFYDEVERR,
|
|
14
|
-
NETLIFYDEVLOG,
|
|
15
|
-
warn,
|
|
16
|
-
watchDebounced,
|
|
17
|
-
} from '../../utils/command-helpers.mjs'
|
|
9
|
+
import { chalk, log, NETLIFYDEVERR, NETLIFYDEVLOG, warn, watchDebounced } from '../../utils/command-helpers.mjs'
|
|
18
10
|
import { INTERNAL_FUNCTIONS_FOLDER, SERVE_FUNCTIONS_FOLDER } from '../../utils/functions/functions.mjs'
|
|
19
11
|
import { BACKGROUND_FUNCTIONS_WARNING } from '../log.mjs'
|
|
20
12
|
import { getPathInProject } from '../settings.mjs'
|
|
@@ -70,21 +62,23 @@ export class FunctionsRegistry {
|
|
|
70
62
|
)
|
|
71
63
|
}
|
|
72
64
|
|
|
73
|
-
async buildFunctionAndWatchFiles(func,
|
|
74
|
-
if (
|
|
75
|
-
log(`${NETLIFYDEVLOG} ${chalk.magenta('Reloading')} function ${chalk.yellow(func.
|
|
65
|
+
async buildFunctionAndWatchFiles(func, firstLoad = false) {
|
|
66
|
+
if (!firstLoad) {
|
|
67
|
+
log(`${NETLIFYDEVLOG} ${chalk.magenta('Reloading')} function ${chalk.yellow(func.displayName)}...`)
|
|
76
68
|
}
|
|
77
69
|
|
|
78
|
-
const {
|
|
70
|
+
const { error: buildError, includedFiles, srcFilesDiff } = await func.build({ cache: this.buildCache })
|
|
79
71
|
|
|
80
|
-
if (
|
|
72
|
+
if (buildError) {
|
|
81
73
|
log(
|
|
82
|
-
`${NETLIFYDEVERR} ${chalk.red('Failed')}
|
|
83
|
-
|
|
74
|
+
`${NETLIFYDEVERR} ${chalk.red('Failed to load')} function ${chalk.yellow(func.displayName)}: ${
|
|
75
|
+
buildError.message
|
|
84
76
|
}`,
|
|
85
77
|
)
|
|
86
|
-
} else
|
|
87
|
-
|
|
78
|
+
} else {
|
|
79
|
+
const verb = firstLoad ? 'Loaded' : 'Reloaded'
|
|
80
|
+
|
|
81
|
+
log(`${NETLIFYDEVLOG} ${chalk.green(verb)} function ${chalk.yellow(func.displayName)}`)
|
|
88
82
|
}
|
|
89
83
|
|
|
90
84
|
// If the build hasn't resulted in any files being added or removed, there
|
|
@@ -116,7 +110,7 @@ export class FunctionsRegistry {
|
|
|
116
110
|
|
|
117
111
|
const newWatcher = await watchDebounced(filesToWatch, {
|
|
118
112
|
onChange: () => {
|
|
119
|
-
this.buildFunctionAndWatchFiles(func,
|
|
113
|
+
this.buildFunctionAndWatchFiles(func, false)
|
|
120
114
|
},
|
|
121
115
|
})
|
|
122
116
|
|
|
@@ -163,14 +157,8 @@ export class FunctionsRegistry {
|
|
|
163
157
|
}
|
|
164
158
|
|
|
165
159
|
this.functions.set(name, func)
|
|
166
|
-
this.buildFunctionAndWatchFiles(func)
|
|
167
160
|
|
|
168
|
-
|
|
169
|
-
`${NETLIFYDEVLOG} ${chalk.green('Loaded')} function ${getTerminalLink(
|
|
170
|
-
chalk.yellow(func.displayName || name),
|
|
171
|
-
func.url,
|
|
172
|
-
)}.`,
|
|
173
|
-
)
|
|
161
|
+
this.buildFunctionAndWatchFiles(func, true)
|
|
174
162
|
}
|
|
175
163
|
|
|
176
164
|
// This function is here so we can mock it in tests
|
|
@@ -105,8 +105,15 @@ const clearFunctionsCache = (functionsPath) => {
|
|
|
105
105
|
.forEach(decache)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
/**
|
|
109
|
+
*
|
|
110
|
+
* @param {object} config
|
|
111
|
+
* @param {string} config.projectRoot
|
|
112
|
+
* @param {(msg: string) => void} config.errorExit
|
|
113
|
+
* @returns
|
|
114
|
+
*/
|
|
115
|
+
const getTargetDirectory = async ({ errorExit, projectRoot }) => {
|
|
116
|
+
const targetDirectory = path.resolve(projectRoot, getPathInProject([SERVE_FUNCTIONS_FOLDER]))
|
|
110
117
|
|
|
111
118
|
try {
|
|
112
119
|
await mkdir(targetDirectory, { recursive: true })
|
|
@@ -120,6 +127,16 @@ const getTargetDirectory = async ({ errorExit }) => {
|
|
|
120
127
|
const netlifyConfigToZisiConfig = ({ config, projectRoot }) =>
|
|
121
128
|
addFunctionsConfigDefaults(normalizeFunctionsConfig({ functionsConfig: config.functions, projectRoot }))
|
|
122
129
|
|
|
130
|
+
/**
|
|
131
|
+
*
|
|
132
|
+
* @param {object} param0
|
|
133
|
+
* @param {*} param0.config
|
|
134
|
+
* @param {*} param0.directory
|
|
135
|
+
* @param {*} param0.errorExit
|
|
136
|
+
* @param {*} param0.func
|
|
137
|
+
* @param {*} param0.metadata
|
|
138
|
+
* @param {string} param0.projectRoot
|
|
139
|
+
*/
|
|
123
140
|
export default async function handler({ config, directory, errorExit, func, metadata, projectRoot }) {
|
|
124
141
|
const functionsConfig = netlifyConfigToZisiConfig({ config, projectRoot })
|
|
125
142
|
|
|
@@ -153,7 +170,7 @@ export default async function handler({ config, directory, errorExit, func, meta
|
|
|
153
170
|
// Enable source map support.
|
|
154
171
|
sourceMapSupport.install()
|
|
155
172
|
|
|
156
|
-
const targetDirectory = await getTargetDirectory({ errorExit })
|
|
173
|
+
const targetDirectory = await getTargetDirectory({ projectRoot, errorExit })
|
|
157
174
|
|
|
158
175
|
return {
|
|
159
176
|
build: ({ cache = {} }) =>
|
|
@@ -220,7 +220,7 @@ const getFunctionsServer = (options) => {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
export const startFunctionsServer = async (options) => {
|
|
223
|
-
const { capabilities, config, debug, loadDistFunctions, settings, site, siteUrl, timeouts } = options
|
|
223
|
+
const { capabilities, command, config, debug, loadDistFunctions, settings, site, siteUrl, timeouts } = options
|
|
224
224
|
const internalFunctionsDir = await getInternalFunctionsDir({ base: site.root })
|
|
225
225
|
const functionsDirectories = []
|
|
226
226
|
|
|
@@ -250,7 +250,8 @@ export const startFunctionsServer = async (options) => {
|
|
|
250
250
|
config,
|
|
251
251
|
debug,
|
|
252
252
|
isConnected: Boolean(siteUrl),
|
|
253
|
-
|
|
253
|
+
// functions always need to be inside the packagePath if set inside a monorepo
|
|
254
|
+
projectRoot: command.workingDir,
|
|
254
255
|
settings,
|
|
255
256
|
timeouts,
|
|
256
257
|
})
|
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,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
import { join } from 'path'
|
|
2
3
|
|
|
3
4
|
import { DenoBridge } from '@netlify/edge-bundler'
|
|
@@ -27,15 +28,24 @@ const getPrompt = ({ fileExists, path }) => {
|
|
|
27
28
|
const getEdgeFunctionsPath = ({ config, repositoryRoot }) =>
|
|
28
29
|
config.build.edge_functions || join(repositoryRoot, 'netlify', 'edge-functions')
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} repositoryRoot
|
|
33
|
+
*/
|
|
30
34
|
const getSettingsPath = (repositoryRoot) => join(repositoryRoot, '.vscode', 'settings.json')
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} repositoryRoot
|
|
38
|
+
*/
|
|
39
|
+
const hasDenoVSCodeExt = async (repositoryRoot) => {
|
|
40
|
+
const { stdout: extensions } = await execa('code', ['--list-extensions'], { stderr: 'inherit', cwd: repositoryRoot })
|
|
34
41
|
return extensions.split('\n').includes('denoland.vscode-deno')
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} repositoryRoot
|
|
46
|
+
*/
|
|
47
|
+
const getDenoVSCodeExt = async (repositoryRoot) => {
|
|
48
|
+
await execa('code', ['--install-extension', 'denoland.vscode-deno'], { stdio: 'inherit', cwd: repositoryRoot })
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
const getDenoExtPrompt = () => {
|
|
@@ -49,6 +59,12 @@ const getDenoExtPrompt = () => {
|
|
|
49
59
|
})
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @param {object} params
|
|
64
|
+
* @param {*} params.config
|
|
65
|
+
* @param {string} params.repositoryRoot
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
52
68
|
export const run = async ({ config, repositoryRoot }) => {
|
|
53
69
|
const deno = new DenoBridge({
|
|
54
70
|
onBeforeDownload: () =>
|
|
@@ -66,9 +82,11 @@ export const run = async ({ config, repositoryRoot }) => {
|
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
try {
|
|
69
|
-
if (!(await hasDenoVSCodeExt())) {
|
|
85
|
+
if (!(await hasDenoVSCodeExt(repositoryRoot))) {
|
|
70
86
|
const { confirm: denoExtConfirm } = await getDenoExtPrompt()
|
|
71
|
-
if (denoExtConfirm)
|
|
87
|
+
if (denoExtConfirm) {
|
|
88
|
+
getDenoVSCodeExt(repositoryRoot)
|
|
89
|
+
}
|
|
72
90
|
}
|
|
73
91
|
} catch {
|
|
74
92
|
log(
|