netlify-cli 12.5.0 → 12.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +5 -0
  2. package/npm-shrinkwrap.json +1075 -634
  3. package/package.json +7 -160
  4. package/src/commands/base-command.mjs +16 -5
  5. package/src/commands/deploy/deploy.mjs +1 -1
  6. package/src/commands/dev/dev.mjs +27 -511
  7. package/src/commands/env/env-list.mjs +1 -0
  8. package/src/commands/functions/functions-create.mjs +35 -25
  9. package/src/commands/functions/functions-serve.mjs +5 -1
  10. package/src/commands/main.mjs +2 -0
  11. package/src/commands/serve/serve.mjs +189 -0
  12. package/src/commands/sites/sites-list.mjs +1 -1
  13. package/src/commands/watch/watch.mjs +1 -1
  14. package/src/functions-templates/go/hello-world/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  15. package/src/functions-templates/javascript/apollo-graphql/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  16. package/src/functions-templates/javascript/apollo-graphql-rest/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  17. package/src/functions-templates/javascript/auth-fetch/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  18. package/src/functions-templates/javascript/create-user/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  19. package/src/functions-templates/javascript/fauna-crud/{.netlify-function-template.cjs → .netlify-function-template.mjs} +3 -2
  20. package/src/functions-templates/javascript/fauna-graphql/{.netlify-function-template.cjs → .netlify-function-template.mjs} +3 -2
  21. package/src/functions-templates/javascript/google-analytics/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  22. package/src/functions-templates/javascript/graphql-gateway/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  23. package/src/functions-templates/javascript/hasura-event-triggered/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  24. package/src/functions-templates/javascript/hello/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  25. package/src/functions-templates/{typescript/hello-world/.netlify-function-template.cjs → javascript/hello-world/.netlify-function-template.mjs} +1 -1
  26. package/src/functions-templates/javascript/identity-signup/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  27. package/src/functions-templates/javascript/image-external/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  28. package/src/functions-templates/javascript/localized-content/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  29. package/src/functions-templates/javascript/node-fetch/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  30. package/src/functions-templates/javascript/oauth-passport/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  31. package/src/functions-templates/javascript/oauth-passport/package.json +1 -1
  32. package/src/functions-templates/javascript/protected-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  33. package/src/functions-templates/javascript/sanity-create/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  34. package/src/functions-templates/javascript/sanity-groq/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  35. package/src/functions-templates/javascript/scheduled-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  36. package/src/functions-templates/javascript/scheduled-function/package.json +1 -1
  37. package/src/functions-templates/javascript/send-email/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  38. package/src/functions-templates/javascript/serverless-ssr/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  39. package/src/functions-templates/javascript/set-cookie/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  40. package/src/functions-templates/javascript/set-cookies/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  41. package/src/functions-templates/{typescript/set-req-header/.netlify-function-template.cjs → javascript/set-req-header/.netlify-function-template.mjs} +1 -1
  42. package/src/functions-templates/javascript/set-res-header/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  43. package/src/functions-templates/javascript/slack-rate-limit/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  44. package/src/functions-templates/javascript/stripe-charge/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  45. package/src/functions-templates/javascript/stripe-subscription/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  46. package/src/functions-templates/javascript/submission-created/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  47. package/src/functions-templates/javascript/token-hider/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  48. package/src/functions-templates/{typescript/transform-response/.netlify-function-template.cjs → javascript/transform-response/.netlify-function-template.mjs} +1 -1
  49. package/src/functions-templates/javascript/url-shortener/{.netlify-function-template.cjs → .netlify-function-template.mjs} +2 -2
  50. package/src/functions-templates/javascript/using-middleware/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  51. package/src/functions-templates/rust/hello-world/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  52. package/src/functions-templates/typescript/abtest/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  53. package/src/functions-templates/typescript/geolocation/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  54. package/src/functions-templates/typescript/geolocation/{{name}}.ts +1 -1
  55. package/src/functions-templates/{javascript/hello-world/.netlify-function-template.cjs → typescript/hello-world/.netlify-function-template.mjs} +1 -1
  56. package/src/functions-templates/typescript/hello-world/package-lock.json +7 -7
  57. package/src/functions-templates/typescript/hello-world/package.json +1 -1
  58. package/src/functions-templates/typescript/json/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  59. package/src/functions-templates/typescript/json/{{name}}.ts +1 -1
  60. package/src/functions-templates/typescript/log/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  61. package/src/functions-templates/typescript/log/{{name}}.ts +2 -2
  62. package/src/functions-templates/typescript/scheduled-function/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  63. package/src/functions-templates/typescript/scheduled-function/package.json +1 -1
  64. package/src/functions-templates/typescript/set-cookies/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  65. package/src/functions-templates/{javascript/set-req-header/.netlify-function-template.cjs → typescript/set-req-header/.netlify-function-template.mjs} +1 -1
  66. package/src/functions-templates/typescript/set-res-header/{.netlify-function-template.cjs → .netlify-function-template.mjs} +1 -1
  67. package/src/functions-templates/{javascript/transform-response/.netlify-function-template.cjs → typescript/transform-response/.netlify-function-template.mjs} +1 -1
  68. package/src/lib/completion/constants.mjs +1 -1
  69. package/src/lib/edge-functions/deploy.mjs +1 -1
  70. package/src/lib/edge-functions/internal.mjs +1 -1
  71. package/src/lib/edge-functions/proxy.mjs +2 -2
  72. package/src/lib/edge-functions/registry.mjs +2 -5
  73. package/src/lib/{fs.cjs → fs.mjs} +5 -13
  74. package/src/lib/functions/registry.mjs +55 -33
  75. package/src/lib/functions/runtimes/js/builders/netlify-lambda.mjs +1 -1
  76. package/src/lib/functions/runtimes/js/builders/zisi.mjs +3 -2
  77. package/src/lib/functions/runtimes/rust/index.mjs +3 -2
  78. package/src/lib/functions/server.mjs +35 -18
  79. package/src/lib/{settings.cjs → settings.mjs} +6 -8
  80. package/src/lib/{spinner.cjs → spinner.mjs} +5 -7
  81. package/src/utils/banner.mjs +17 -0
  82. package/src/utils/command-helpers.mjs +1 -1
  83. package/src/utils/detect-server-settings.mjs +35 -1
  84. package/src/utils/dev.mjs +8 -5
  85. package/src/utils/dot-env.mjs +1 -1
  86. package/src/utils/framework-server.mjs +66 -0
  87. package/src/utils/functions/functions.mjs +20 -4
  88. package/src/utils/functions/get-functions.mjs +1 -1
  89. package/src/utils/get-global-config.mjs +1 -1
  90. package/src/utils/gitignore.mjs +1 -1
  91. package/src/utils/graph.mjs +170 -0
  92. package/src/utils/init/utils.mjs +1 -1
  93. package/src/utils/live-tunnel.mjs +1 -1
  94. package/src/utils/lm/install.mjs +2 -2
  95. package/src/utils/proxy-server.mjs +90 -0
  96. package/src/utils/proxy.mjs +1 -1
  97. package/src/utils/rules-proxy.mjs +1 -1
  98. package/src/utils/run-build.mjs +129 -0
  99. package/src/utils/shell.mjs +120 -0
  100. package/src/utils/state-config.mjs +1 -1
  101. package/src/utils/static-server.mjs +34 -0
  102. package/src/utils/validation.mjs +15 -0
@@ -1,6 +1,6 @@
1
- const chalk = require('chalk')
1
+ import chalk from 'chalk'
2
2
 
3
- module.exports = {
3
+ export default {
4
4
  name: 'token-hider',
5
5
  description: 'Token Hider: access APIs without exposing your API keys',
6
6
  functionType: 'serverless',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'transform-response',
3
3
  description: 'Transform the content of an HTTP response',
4
4
  functionType: 'edge',
@@ -1,6 +1,6 @@
1
- const chalk = require('chalk')
1
+ import chalk from 'chalk'
2
2
 
3
- module.exports = {
3
+ export default {
4
4
  name: 'url-shortener',
5
5
  description: 'URL Shortener: simple URL shortener with Netlify Forms!',
6
6
  functionType: 'serverless',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'using-middleware',
3
3
  description: 'Using Middleware with middy',
4
4
  functionType: 'serverless',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'hello-world',
3
3
  priority: 1,
4
4
  description: 'Basic function that shows how to create a handler and return a response',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'abtest',
3
3
  description: "Function that randomly assigns users to group 'a' or 'b' and sets this value as a cookie.",
4
4
  functionType: 'edge',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'geolocation',
3
3
  description: "Returns info about user's geolocation, which can be used to serve location-specific content.",
4
4
  functionType: 'edge',
@@ -17,7 +17,7 @@ export default async (request: Request, context: Context) => {
17
17
  // }
18
18
  // }
19
19
 
20
- return context.json({
20
+ return Response.json({
21
21
  geo: context.geo,
22
22
  header: request.headers.get("x-nf-geo"),
23
23
  });
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'hello-world',
3
3
  priority: 1,
4
4
  description: 'Basic function that shows async/await usage, and response formatting',
@@ -9,15 +9,15 @@
9
9
  "version": "1.0.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@netlify/functions": "^1.3.0",
12
+ "@netlify/functions": "^1.4.0",
13
13
  "@types/node": "^14.0.0",
14
14
  "typescript": "^4.0.0"
15
15
  }
16
16
  },
17
17
  "node_modules/@netlify/functions": {
18
- "version": "1.3.0",
19
- "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-1.3.0.tgz",
20
- "integrity": "sha512-hN/Fgpz8XIOBfsBPLYUMxVKBlCopgeqGB0popayicnmkFLnvKByTTMYgF01wcF9DBtBQdV0H2h1kPFpMl34I8w==",
18
+ "version": "1.4.0",
19
+ "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-1.4.0.tgz",
20
+ "integrity": "sha512-gy7ULTIRroc2/jyFVGx1djCmmBMVisIwrvkqggq5B6iDcInRSy2Tpkm+V5C63hKJVkNRskKWtLQKm9ecCaQTjA==",
21
21
  "dependencies": {
22
22
  "is-promise": "^4.0.0"
23
23
  },
@@ -50,9 +50,9 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@netlify/functions": {
53
- "version": "1.3.0",
54
- "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-1.3.0.tgz",
55
- "integrity": "sha512-hN/Fgpz8XIOBfsBPLYUMxVKBlCopgeqGB0popayicnmkFLnvKByTTMYgF01wcF9DBtBQdV0H2h1kPFpMl34I8w==",
53
+ "version": "1.4.0",
54
+ "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-1.4.0.tgz",
55
+ "integrity": "sha512-gy7ULTIRroc2/jyFVGx1djCmmBMVisIwrvkqggq5B6iDcInRSy2Tpkm+V5C63hKJVkNRskKWtLQKm9ecCaQTjA==",
56
56
  "requires": {
57
57
  "is-promise": "^4.0.0"
58
58
  }
@@ -14,7 +14,7 @@
14
14
  "author": "Netlify",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@netlify/functions": "^1.3.0",
17
+ "@netlify/functions": "^1.4.0",
18
18
  "@types/node": "^14.0.0",
19
19
  "typescript": "^4.0.0"
20
20
  }
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'json',
3
3
  description: 'Function that returns a simple json response',
4
4
  functionType: 'edge',
@@ -1,5 +1,5 @@
1
1
  import type { Context } from "https://edge.netlify.com";
2
2
 
3
3
  export default async (request: Request, context: Context) => {
4
- return context.json({ hello: "world" });
4
+ return Response.json({ hello: "world", location: context.geo.city });
5
5
  };
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'log',
3
3
  description: 'Basic function logging context of request',
4
4
  functionType: 'edge',
@@ -1,9 +1,9 @@
1
1
  import type { Context } from "https://edge.netlify.com";
2
2
 
3
3
  export default async (request: Request, context: Context) => {
4
- context.log("Hello from the logging service");
4
+ console.log(`There was a request from ${context.geo.city} to ${request.url}`);
5
5
 
6
6
  return new Response("The request to this URL was logged", {
7
7
  headers: { "content-type": "text/html" },
8
8
  });
9
- };
9
+ };
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'scheduled-function',
3
3
  priority: 1,
4
4
  description: 'Basic implementation of a scheduled function in TypeScript.',
@@ -15,7 +15,7 @@
15
15
  "author": "Netlify",
16
16
  "license": "MIT",
17
17
  "dependencies": {
18
- "@netlify/functions": "^1.3.0",
18
+ "@netlify/functions": "^1.4.0",
19
19
  "@types/node": "^14.18.9",
20
20
  "typescript": "^4.5.5"
21
21
  }
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'set-cookies',
3
3
  description: 'Create and manage HTTP cookies',
4
4
  functionType: 'edge',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'set-req-header',
3
3
  description: 'Adds a custom HTTP header to HTTP request.',
4
4
  functionType: 'edge',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'set-res-header',
3
3
  description: 'Adds a custom HTTP header to HTTP response.',
4
4
  functionType: 'edge',
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  name: 'transform-response',
3
3
  description: 'Transform the content of an HTTP response',
4
4
  functionType: 'edge',
@@ -1,4 +1,4 @@
1
1
  // @ts-check
2
- import { getPathInHome } from '../settings.cjs'
2
+ import { getPathInHome } from '../settings.mjs'
3
3
 
4
4
  export const AUTOCOMPLETION_FILE = getPathInHome(['autocompletion.json'])
@@ -2,7 +2,7 @@
2
2
  import { stat } from 'fs/promises'
3
3
  import { join } from 'path'
4
4
 
5
- import { getPathInProject } from '../settings.cjs'
5
+ import { getPathInProject } from '../settings.mjs'
6
6
 
7
7
  import { EDGE_FUNCTIONS_FOLDER, PUBLIC_URL_PATH } from './consts.mjs'
8
8
 
@@ -3,7 +3,7 @@ import { readFile, stat } from 'fs/promises'
3
3
  import { dirname, join, resolve } from 'path'
4
4
  import { cwd } from 'process'
5
5
 
6
- import { getPathInProject } from '../settings.cjs'
6
+ import { getPathInProject } from '../settings.mjs'
7
7
 
8
8
  import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from './consts.mjs'
9
9
 
@@ -8,8 +8,8 @@ import { v4 as generateUUID } from 'uuid'
8
8
 
9
9
  import { NETLIFYDEVERR, NETLIFYDEVWARN, chalk, error as printError, log } from '../../utils/command-helpers.mjs'
10
10
  import { getGeoLocation } from '../geo-location.mjs'
11
- import { getPathInProject } from '../settings.cjs'
12
- import { startSpinner, stopSpinner } from '../spinner.cjs'
11
+ import { getPathInProject } from '../settings.mjs'
12
+ import { startSpinner, stopSpinner } from '../spinner.mjs'
13
13
 
14
14
  import { DIST_IMPORT_MAP_PATH } from './consts.mjs'
15
15
  import headers from './headers.mjs'
@@ -213,17 +213,14 @@ export class EdgeFunctionsRegistry {
213
213
  if (
214
214
  variable.sources.includes('ui') ||
215
215
  variable.sources.includes('account') ||
216
- variable.sources.includes('addons')
216
+ variable.sources.includes('addons') ||
217
+ variable.sources.includes('internal')
217
218
  ) {
218
219
  env[key] = variable.value
219
220
  }
220
221
  })
221
222
 
222
223
  env.DENO_REGION = 'local'
223
- env.NETLIFY_DEV = 'true'
224
- // We use it in the bootstrap layer to detect whether we're running in production or not
225
- // (see https://github.com/netlify/edge-functions-bootstrap/blob/main/src/bootstrap/environment.ts#L2)
226
- // env.DENO_DEPLOYMENT_ID = 'xxx='
227
224
 
228
225
  return env
229
226
  }
@@ -1,10 +1,8 @@
1
1
  // @ts-check
2
- const {
3
- constants,
4
- promises: { access, stat },
5
- } = require('fs')
2
+ import { constants } from 'fs'
3
+ import { access, stat } from 'fs/promises'
6
4
 
7
- const fileExistsAsync = async (filePath) => {
5
+ export const fileExistsAsync = async (filePath) => {
8
6
  try {
9
7
  await access(filePath, constants.F_OK)
10
8
  return true
@@ -36,16 +34,10 @@ const isType = async (filePath, type) => {
36
34
  * Checks if the provided filePath is a file
37
35
  * @param {string} filePath
38
36
  */
39
- const isFileAsync = (filePath) => isType(filePath, 'isFile')
37
+ export const isFileAsync = (filePath) => isType(filePath, 'isFile')
40
38
 
41
39
  /**
42
40
  * Checks if the provided filePath is a directory
43
41
  * @param {string} filePath
44
42
  */
45
- const isDirectoryAsync = (filePath) => isType(filePath, 'isDirectory')
46
-
47
- module.exports = {
48
- fileExistsAsync,
49
- isDirectoryAsync,
50
- isFileAsync,
51
- }
43
+ export const isDirectoryAsync = (filePath) => isType(filePath, 'isDirectory')
@@ -1,19 +1,22 @@
1
1
  // @ts-check
2
2
  import { mkdir } from 'fs/promises'
3
- import { extname, isAbsolute, join } from 'path'
3
+ import { extname, isAbsolute, join, resolve } from 'path'
4
4
  import { env } from 'process'
5
5
 
6
+ import extractZip from 'extract-zip'
7
+
6
8
  import {
7
9
  chalk,
8
10
  getTerminalLink,
9
11
  log,
10
12
  NETLIFYDEVERR,
11
13
  NETLIFYDEVLOG,
12
- NETLIFYDEVWARN,
13
14
  warn,
14
15
  watchDebounced,
15
16
  } from '../../utils/command-helpers.mjs'
17
+ import { SERVE_FUNCTIONS_FOLDER } from '../../utils/functions/functions.mjs'
16
18
  import { getLogMessage } from '../log.mjs'
19
+ import { getPathInProject } from '../settings.mjs'
17
20
 
18
21
  import NetlifyFunction from './netlify-function.mjs'
19
22
  import runtimes from './runtimes/index.mjs'
@@ -21,9 +24,10 @@ import runtimes from './runtimes/index.mjs'
21
24
  const ZIP_EXTENSION = '.zip'
22
25
 
23
26
  export class FunctionsRegistry {
24
- constructor({ capabilities, config, isConnected = false, projectRoot, settings, timeouts }) {
27
+ constructor({ capabilities, config, debug = false, isConnected = false, projectRoot, settings, timeouts }) {
25
28
  this.capabilities = capabilities
26
29
  this.config = config
30
+ this.debug = debug
27
31
  this.isConnected = isConnected
28
32
  this.projectRoot = projectRoot
29
33
  this.timeouts = timeouts
@@ -123,7 +127,7 @@ export class FunctionsRegistry {
123
127
  return this.functions.get(name)
124
128
  }
125
129
 
126
- registerFunction(name, funcBeforeHook) {
130
+ async registerFunction(name, funcBeforeHook) {
127
131
  const { runtime } = funcBeforeHook
128
132
 
129
133
  // The `onRegister` hook allows runtimes to modify the function before it's
@@ -145,11 +149,16 @@ export class FunctionsRegistry {
145
149
  )
146
150
  }
147
151
 
148
- // This fixes the bug described here https://github.com/netlify/zip-it-and-ship-it/issues/637
149
- // If the current function's file is a zip bundle, we ignore it and log a helpful message.
152
+ // If the function file is a ZIP, we extract it and rewire its main file to
153
+ // the new location.
150
154
  if (extname(func.mainFile) === ZIP_EXTENSION) {
151
- log(`${NETLIFYDEVWARN} Skipped bundled function ${chalk.yellow(name)}. Unzip the archive to load it from source.`)
152
- return
155
+ const unzippedDirectory = await this.unzipFunction(func)
156
+
157
+ if (this.debug) {
158
+ log(`${NETLIFYDEVLOG} ${chalk.green('Extracted')} function ${chalk.yellow(name)} from ${func.mainFile}.`)
159
+ }
160
+
161
+ func.mainFile = join(unzippedDirectory, `${func.name}.js`)
153
162
  }
154
163
 
155
164
  this.functions.set(name, func)
@@ -197,34 +206,36 @@ export class FunctionsRegistry {
197
206
 
198
207
  await Promise.all(deletedFunctions.map((func) => this.unregisterFunction(func.name)))
199
208
 
200
- functions.forEach(({ mainFile, name, runtime: runtimeName }) => {
201
- const runtime = runtimes[runtimeName]
202
-
203
- // If there is no matching runtime, it means this function is not yet
204
- // supported in Netlify Dev.
205
- if (runtime === undefined) {
206
- return
207
- }
209
+ await Promise.all(
210
+ functions.map(async ({ mainFile, name, runtime: runtimeName }) => {
211
+ const runtime = runtimes[runtimeName]
208
212
 
209
- // If this function has already been registered, we skip it.
210
- if (this.functions.has(name)) {
211
- return
212
- }
213
+ // If there is no matching runtime, it means this function is not yet
214
+ // supported in Netlify Dev.
215
+ if (runtime === undefined) {
216
+ return
217
+ }
213
218
 
214
- const func = new NetlifyFunction({
215
- config: this.config,
216
- directory: directories.find((directory) => mainFile.startsWith(directory)),
217
- mainFile,
218
- name,
219
- projectRoot: this.projectRoot,
220
- runtime,
221
- timeoutBackground: this.timeouts.backgroundFunctions,
222
- timeoutSynchronous: this.timeouts.syncFunctions,
223
- settings: this.settings,
224
- })
219
+ // If this function has already been registered, we skip it.
220
+ if (this.functions.has(name)) {
221
+ return
222
+ }
225
223
 
226
- this.registerFunction(name, func)
227
- })
224
+ const func = new NetlifyFunction({
225
+ config: this.config,
226
+ directory: directories.find((directory) => mainFile.startsWith(directory)),
227
+ mainFile,
228
+ name,
229
+ projectRoot: this.projectRoot,
230
+ runtime,
231
+ timeoutBackground: this.timeouts.backgroundFunctions,
232
+ timeoutSynchronous: this.timeouts.syncFunctions,
233
+ settings: this.settings,
234
+ })
235
+
236
+ await this.registerFunction(name, func)
237
+ }),
238
+ )
228
239
 
229
240
  await Promise.all(directories.map((path) => this.setupDirectoryWatcher(path)))
230
241
  }
@@ -261,4 +272,15 @@ export class FunctionsRegistry {
261
272
  await watcher.close()
262
273
  }
263
274
  }
275
+
276
+ async unzipFunction(func) {
277
+ const targetDirectory = resolve(
278
+ this.projectRoot,
279
+ getPathInProject([SERVE_FUNCTIONS_FOLDER, '.unzipped', func.name]),
280
+ )
281
+
282
+ await extractZip(func.mainFile, { dir: targetDirectory })
283
+
284
+ return targetDirectory
285
+ }
264
286
  }
@@ -5,7 +5,7 @@ import { resolve } from 'path'
5
5
  import minimist from 'minimist'
6
6
 
7
7
  import execa from '../../../../../utils/execa.mjs'
8
- import { fileExistsAsync } from '../../../../fs.cjs'
8
+ import { fileExistsAsync } from '../../../../fs.mjs'
9
9
  import { memoizedBuild } from '../../../memoized-build.mjs'
10
10
 
11
11
  export const detectNetlifyLambda = async function ({ packageJson } = {}) {
@@ -7,7 +7,8 @@ import readPkgUp from 'read-pkg-up'
7
7
  import sourceMapSupport from 'source-map-support'
8
8
 
9
9
  import { NETLIFYDEVERR } from '../../../../../utils/command-helpers.mjs'
10
- import { getPathInProject } from '../../../../settings.cjs'
10
+ import { SERVE_FUNCTIONS_FOLDER } from '../../../../../utils/functions/functions.mjs'
11
+ import { getPathInProject } from '../../../../settings.mjs'
11
12
  import { normalizeFunctionsConfig } from '../../../config.mjs'
12
13
  import { memoizedBuild } from '../../../memoized-build.mjs'
13
14
 
@@ -94,7 +95,7 @@ const clearFunctionsCache = (functionsPath) => {
94
95
  }
95
96
 
96
97
  const getTargetDirectory = async ({ errorExit }) => {
97
- const targetDirectory = path.resolve(getPathInProject(['functions-serve']))
98
+ const targetDirectory = path.resolve(getPathInProject([SERVE_FUNCTIONS_FOLDER]))
98
99
 
99
100
  try {
100
101
  await mkdir(targetDirectory, { recursive: true })
@@ -7,7 +7,8 @@ import findUp from 'find-up'
7
7
  import toml from 'toml'
8
8
 
9
9
  import execa from '../../../../utils/execa.mjs'
10
- import { getPathInProject } from '../../../settings.cjs'
10
+ import { SERVE_FUNCTIONS_FOLDER } from '../../../../utils/functions/functions.mjs'
11
+ import { getPathInProject } from '../../../settings.mjs'
11
12
  import { runFunctionsProxy } from '../../local-proxy.mjs'
12
13
 
13
14
  const isWindows = platform === 'win32'
@@ -16,7 +17,7 @@ export const name = 'rs'
16
17
 
17
18
  const build = async ({ func }) => {
18
19
  const functionDirectory = dirname(func.mainFile)
19
- const cacheDirectory = resolve(getPathInProject(['functions-serve']))
20
+ const cacheDirectory = resolve(getPathInProject([SERVE_FUNCTIONS_FOLDER]))
20
21
  const targetDirectory = join(cacheDirectory, func.name)
21
22
  const crateName = await getCrateName(functionDirectory)
22
23
  const binaryName = `${crateName}${isWindows ? '.exe' : ''}`
@@ -4,7 +4,7 @@ import jwtDecode from 'jwt-decode'
4
4
 
5
5
  import { NETLIFYDEVERR, NETLIFYDEVLOG, error as errorExit, log } from '../../utils/command-helpers.mjs'
6
6
  import { generateNetlifyGraphJWT } from '../../utils/dev.mjs'
7
- import { CLOCKWORK_USERAGENT, getInternalFunctionsDir } from '../../utils/functions/index.mjs'
7
+ import { CLOCKWORK_USERAGENT, getFunctionsDistPath, getInternalFunctionsDir } from '../../utils/functions/index.mjs'
8
8
 
9
9
  import { handleBackgroundFunction, handleBackgroundFunctionResult } from './background.mjs'
10
10
  import { createFormSubmissionHandler } from './form-submissions-handler.mjs'
@@ -208,29 +208,46 @@ const getFunctionsServer = async function (options) {
208
208
  }
209
209
 
210
210
  export const startFunctionsServer = async (options) => {
211
- const { capabilities, config, settings, site, siteUrl, timeouts } = options
211
+ const { capabilities, config, debug, loadDistFunctions, settings, site, siteUrl, timeouts } = options
212
212
  const internalFunctionsDir = await getInternalFunctionsDir({ base: site.root })
213
+ const functionsDirectories = []
213
214
 
214
- // The order of the function directories matters. Leftmost directories take
215
- // precedence.
216
- const functionsDirectories = [settings.functions, internalFunctionsDir].filter(Boolean)
217
-
218
- if (functionsDirectories.length !== 0) {
219
- const functionsRegistry = new FunctionsRegistry({
220
- capabilities,
221
- config,
222
- isConnected: Boolean(siteUrl),
223
- projectRoot: site.root,
224
- settings,
225
- timeouts,
226
- })
215
+ // If the `loadDistFunctions` parameter is sent, the functions server will
216
+ // use the built functions created by zip-it-and-ship-it rather than building
217
+ // them from source.
218
+ if (loadDistFunctions) {
219
+ const distPath = await getFunctionsDistPath({ base: site.root })
227
220
 
228
- await functionsRegistry.scan(functionsDirectories)
221
+ if (distPath) {
222
+ functionsDirectories.push(distPath)
223
+ }
224
+ } else {
225
+ // The order of the function directories matters. Leftmost directories take
226
+ // precedence.
227
+ const sourceDirectories = [settings.functions, internalFunctionsDir].filter(Boolean)
229
228
 
230
- const server = await getFunctionsServer(Object.assign(options, { functionsRegistry }))
229
+ functionsDirectories.push(...sourceDirectories)
230
+ }
231
231
 
232
- await startWebServer({ server, settings })
232
+ if (functionsDirectories.length === 0) {
233
+ return
233
234
  }
235
+
236
+ const functionsRegistry = new FunctionsRegistry({
237
+ capabilities,
238
+ config,
239
+ debug,
240
+ isConnected: Boolean(siteUrl),
241
+ projectRoot: site.root,
242
+ settings,
243
+ timeouts,
244
+ })
245
+
246
+ await functionsRegistry.scan(functionsDirectories)
247
+
248
+ const server = await getFunctionsServer(Object.assign(options, { functionsRegistry }))
249
+
250
+ await startWebServer({ server, settings })
234
251
  }
235
252
 
236
253
  const startWebServer = async ({ server, settings }) => {
@@ -1,7 +1,7 @@
1
- const os = require('os')
2
- const path = require('path')
1
+ import os from 'os'
2
+ import path from 'path'
3
3
 
4
- const envPaths = require('env-paths')
4
+ import envPaths from 'env-paths'
5
5
 
6
6
  const OSBasedPaths = envPaths('netlify', { suffix: '' })
7
7
  const NETLIFY_HOME = '.netlify'
@@ -12,7 +12,7 @@ const NETLIFY_HOME = '.netlify'
12
12
  * @param {string[]} paths
13
13
  * @returns {string}
14
14
  */
15
- const getLegacyPathInHome = (paths) => {
15
+ export const getLegacyPathInHome = (paths) => {
16
16
  const pathInHome = path.join(os.homedir(), NETLIFY_HOME, ...paths)
17
17
  return pathInHome
18
18
  }
@@ -22,7 +22,7 @@ const getLegacyPathInHome = (paths) => {
22
22
  * @param {string[]} paths
23
23
  * @returns {string}
24
24
  */
25
- const getPathInHome = (paths) => {
25
+ export const getPathInHome = (paths) => {
26
26
  const pathInHome = path.join(OSBasedPaths.config, ...paths)
27
27
  return pathInHome
28
28
  }
@@ -32,9 +32,7 @@ const getPathInHome = (paths) => {
32
32
  * @param {string[]} paths
33
33
  * @returns {string}
34
34
  */
35
- const getPathInProject = (paths) => {
35
+ export const getPathInProject = (paths) => {
36
36
  const pathInProject = path.join(NETLIFY_HOME, ...paths)
37
37
  return pathInProject
38
38
  }
39
-
40
- module.exports = { getLegacyPathInHome, getPathInHome, getPathInProject }
@@ -1,6 +1,6 @@
1
1
  // @ts-check
2
- const logSymbols = require('log-symbols')
3
- const ora = require('ora')
2
+ import logSymbols from 'log-symbols'
3
+ import ora from 'ora'
4
4
 
5
5
  /**
6
6
  * Creates a spinner with the following text
@@ -8,7 +8,7 @@ const ora = require('ora')
8
8
  * @param {string} config.text
9
9
  * @returns {ora.Ora}
10
10
  */
11
- const startSpinner = ({ text }) =>
11
+ export const startSpinner = ({ text }) =>
12
12
  ora({
13
13
  text,
14
14
  }).start()
@@ -21,7 +21,7 @@ const startSpinner = ({ text }) =>
21
21
  * @param {string} [config.text]
22
22
  * @returns {void}
23
23
  */
24
- const stopSpinner = ({ error, spinner, text }) => {
24
+ export const stopSpinner = ({ error, spinner, text }) => {
25
25
  if (!spinner) {
26
26
  return
27
27
  }
@@ -39,10 +39,8 @@ const stopSpinner = ({ error, spinner, text }) => {
39
39
  * @param {ora.Ora} config.spinner
40
40
  * @returns {void}
41
41
  */
42
- const clearSpinner = ({ spinner }) => {
42
+ export const clearSpinner = ({ spinner }) => {
43
43
  if (spinner) {
44
44
  spinner.stop()
45
45
  }
46
46
  }
47
-
48
- module.exports = { clearSpinner, startSpinner, stopSpinner }