netlify-cli 8.14.1 → 8.15.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.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
- "version": "8.14.1",
3
+ "version": "8.15.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "netlify-cli",
9
- "version": "8.14.1",
9
+ "version": "8.15.0",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
@@ -22,6 +22,7 @@
22
22
  "@sindresorhus/slugify": "^1.1.0",
23
23
  "ansi-escapes": "^5.0.0",
24
24
  "ansi-styles": "^5.0.0",
25
+ "ansi2html": "^0.0.1",
25
26
  "ascii-table": "0.0.9",
26
27
  "backoff": "^2.5.0",
27
28
  "better-opn": "^3.0.0",
@@ -36,6 +37,7 @@
36
37
  "content-type": "^1.0.4",
37
38
  "cookie": "^0.4.0",
38
39
  "copy-template-dir": "^1.4.0",
40
+ "cron-parser": "^4.2.1",
39
41
  "debug": "^4.1.1",
40
42
  "decache": "^4.6.0",
41
43
  "del": "^6.0.0",
@@ -134,7 +136,6 @@
134
136
  "ini": "^2.0.0",
135
137
  "jsonwebtoken": "^8.5.1",
136
138
  "mock-fs": "^5.1.2",
137
- "mock-require": "^3.0.3",
138
139
  "p-timeout": "^4.0.0",
139
140
  "proxyquire": "^2.1.3",
140
141
  "seedrandom": "^3.0.5",
@@ -4426,6 +4427,17 @@
4426
4427
  "url": "https://github.com/chalk/ansi-styles?sponsor=1"
4427
4428
  }
4428
4429
  },
4430
+ "node_modules/ansi2html": {
4431
+ "version": "0.0.1",
4432
+ "resolved": "https://registry.npmjs.org/ansi2html/-/ansi2html-0.0.1.tgz",
4433
+ "integrity": "sha1-u4gARhtECvALkb89c2al4LhHO6g=",
4434
+ "bin": {
4435
+ "ansi2html": "bin/ansi2html"
4436
+ },
4437
+ "engines": {
4438
+ "node": ">0.4"
4439
+ }
4440
+ },
4429
4441
  "node_modules/any-observable": {
4430
4442
  "version": "0.3.0",
4431
4443
  "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
@@ -10887,12 +10899,6 @@
10887
10899
  "node": ">=6.0"
10888
10900
  }
10889
10901
  },
10890
- "node_modules/get-caller-file": {
10891
- "version": "1.0.3",
10892
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
10893
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
10894
- "dev": true
10895
- },
10896
10902
  "node_modules/get-intrinsic": {
10897
10903
  "version": "1.1.1",
10898
10904
  "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
@@ -14770,31 +14776,6 @@
14770
14776
  "node": ">=12.0.0"
14771
14777
  }
14772
14778
  },
14773
- "node_modules/mock-require": {
14774
- "version": "3.0.3",
14775
- "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
14776
- "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
14777
- "dev": true,
14778
- "dependencies": {
14779
- "get-caller-file": "^1.0.2",
14780
- "normalize-path": "^2.1.1"
14781
- },
14782
- "engines": {
14783
- "node": ">=4.3.0"
14784
- }
14785
- },
14786
- "node_modules/mock-require/node_modules/normalize-path": {
14787
- "version": "2.1.1",
14788
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
14789
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
14790
- "dev": true,
14791
- "dependencies": {
14792
- "remove-trailing-separator": "^1.0.1"
14793
- },
14794
- "engines": {
14795
- "node": ">=0.10.0"
14796
- }
14797
- },
14798
14779
  "node_modules/module-definition": {
14799
14780
  "version": "3.3.1",
14800
14781
  "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-3.3.1.tgz",
@@ -24813,6 +24794,11 @@
24813
24794
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
24814
24795
  "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="
24815
24796
  },
24797
+ "ansi2html": {
24798
+ "version": "0.0.1",
24799
+ "resolved": "https://registry.npmjs.org/ansi2html/-/ansi2html-0.0.1.tgz",
24800
+ "integrity": "sha1-u4gARhtECvALkb89c2al4LhHO6g="
24801
+ },
24816
24802
  "any-observable": {
24817
24803
  "version": "0.3.0",
24818
24804
  "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
@@ -29735,12 +29721,6 @@
29735
29721
  "node-source-walk": "^4.0.0"
29736
29722
  }
29737
29723
  },
29738
- "get-caller-file": {
29739
- "version": "1.0.3",
29740
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
29741
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
29742
- "dev": true
29743
- },
29744
29724
  "get-intrinsic": {
29745
29725
  "version": "1.1.1",
29746
29726
  "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
@@ -32621,27 +32601,6 @@
32621
32601
  "integrity": "sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A==",
32622
32602
  "dev": true
32623
32603
  },
32624
- "mock-require": {
32625
- "version": "3.0.3",
32626
- "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz",
32627
- "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==",
32628
- "dev": true,
32629
- "requires": {
32630
- "get-caller-file": "^1.0.2",
32631
- "normalize-path": "^2.1.1"
32632
- },
32633
- "dependencies": {
32634
- "normalize-path": {
32635
- "version": "2.1.1",
32636
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
32637
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
32638
- "dev": true,
32639
- "requires": {
32640
- "remove-trailing-separator": "^1.0.1"
32641
- }
32642
- }
32643
- }
32644
- },
32645
32604
  "module-definition": {
32646
32605
  "version": "3.3.1",
32647
32606
  "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-3.3.1.tgz",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
3
  "description": "Netlify command line tool",
4
- "version": "8.14.1",
4
+ "version": "8.15.0",
5
5
  "author": "Netlify Inc.",
6
6
  "contributors": [
7
7
  "Mathias Biilmann <matt@netlify.com> (https://twitter.com/biilmann)",
@@ -64,7 +64,7 @@
64
64
  "test:dev:ava": "ava --verbose",
65
65
  "test:ci:ava": "c8 -r json ava",
66
66
  "test:affected": "node ./tools/affected-test.js",
67
- "e2e": "node ./tools/e2e/run.js",
67
+ "e2e": "node ./tools/e2e/run.mjs",
68
68
  "docs": "node ./site/scripts/docs.js",
69
69
  "watch": "c8 --reporter=lcov ava --watch",
70
70
  "site:build": "run-s site:build:*",
@@ -73,8 +73,8 @@
73
73
  "postinstall": "node ./scripts/postinstall.js"
74
74
  },
75
75
  "config": {
76
- "eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{src,tools,scripts,site,tests,.github}/**/*.{js,md,html}\" \"*.{js,md,html}\" \".*.{js,md,html}\"",
77
- "prettier": "--ignore-path .gitignore --loglevel=warn \"{src,tools,scripts,site,tests,.github}/**/*.{js,md,yml,json,html}\" \"*.{js,yml,json,html}\" \".*.{js,yml,json,html}\" \"!CHANGELOG.md\" \"!npm-shrinkwrap.json\" \"!.github/**/*.md\""
76
+ "eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{src,tools,scripts,site,tests,.github}/**/*.{mjs,cjs,js,md,html}\" \"*.{mjs,cjs,js,md,html}\" \".*.{mjs,cjs,js,md,html}\"",
77
+ "prettier": "--ignore-path .gitignore --loglevel=warn \"{src,tools,scripts,site,tests,.github}/**/*.{mjs,cjs,js,md,yml,json,html}\" \"*.{mjs,cjs,js,yml,json,html}\" \".*.{mjs,cjs,js,yml,json,html}\" \"!CHANGELOG.md\" \"!npm-shrinkwrap.json\" \"!.github/**/*.md\""
78
78
  },
79
79
  "dependencies": {
80
80
  "@netlify/build": "^26.2.0",
@@ -89,6 +89,7 @@
89
89
  "@sindresorhus/slugify": "^1.1.0",
90
90
  "ansi-escapes": "^5.0.0",
91
91
  "ansi-styles": "^5.0.0",
92
+ "ansi2html": "^0.0.1",
92
93
  "ascii-table": "0.0.9",
93
94
  "backoff": "^2.5.0",
94
95
  "better-opn": "^3.0.0",
@@ -103,6 +104,7 @@
103
104
  "content-type": "^1.0.4",
104
105
  "cookie": "^0.4.0",
105
106
  "copy-template-dir": "^1.4.0",
107
+ "cron-parser": "^4.2.1",
106
108
  "debug": "^4.1.1",
107
109
  "decache": "^4.6.0",
108
110
  "del": "^6.0.0",
@@ -197,7 +199,6 @@
197
199
  "ini": "^2.0.0",
198
200
  "jsonwebtoken": "^8.5.1",
199
201
  "mock-fs": "^5.1.2",
200
- "mock-require": "^3.0.3",
201
202
  "p-timeout": "^4.0.0",
202
203
  "proxyquire": "^2.1.3",
203
204
  "seedrandom": "^3.0.5",
@@ -3,10 +3,11 @@ const fs = require('fs')
3
3
  const path = require('path')
4
4
  const process = require('process')
5
5
 
6
+ const CronParser = require('cron-parser')
6
7
  const inquirer = require('inquirer')
7
8
  const fetch = require('node-fetch')
8
9
 
9
- const { BACKGROUND, NETLIFYDEVWARN, chalk, error, exit, getFunctions } = require('../../utils')
10
+ const { BACKGROUND, CLOCKWORK_USERAGENT, NETLIFYDEVWARN, chalk, error, exit, getFunctions } = require('../../utils')
10
11
 
11
12
  // https://www.netlify.com/docs/functions/#event-triggered-functions
12
13
  const events = [
@@ -130,6 +131,13 @@ const getFunctionToTrigger = function (options, argumentName) {
130
131
  return argumentName
131
132
  }
132
133
 
134
+ const getNextRun = function (schedule) {
135
+ const cron = CronParser.parseExpression(schedule, {
136
+ tz: 'Etc/UTC',
137
+ })
138
+ return cron.next().toDate()
139
+ }
140
+
133
141
  /**
134
142
  * The functions:invoke command
135
143
  * @param {string} nameArgument
@@ -150,11 +158,18 @@ const functionsInvoke = async (nameArgument, options, command) => {
150
158
 
151
159
  const functions = await getFunctions(functionsDir)
152
160
  const functionToTrigger = await getNameFromArgs(functions, options, nameArgument)
161
+ const functionObj = functions.find((func) => func.name === functionToTrigger)
153
162
 
154
163
  let headers = {}
155
164
  let body = {}
156
165
 
157
- if (eventTriggeredFunctions.has(functionToTrigger)) {
166
+ if (functionObj.schedule) {
167
+ body.next_run = getNextRun(functionObj.schedule)
168
+ headers = {
169
+ 'user-agent': CLOCKWORK_USERAGENT,
170
+ 'X-NF-Event': 'schedule',
171
+ }
172
+ } else if (eventTriggeredFunctions.has(functionToTrigger)) {
158
173
  /** handle event triggered fns */
159
174
  // https://www.netlify.com/docs/functions/#event-triggered-functions
160
175
  const [name, event] = functionToTrigger.split('-')
@@ -32,6 +32,7 @@ class NetlifyFunction {
32
32
  // Determines whether this is a background function based on the function
33
33
  // name.
34
34
  this.isBackground = name.endsWith(BACKGROUND_SUFFIX)
35
+ this.schedule = null
35
36
 
36
37
  // List of the function's source files. This starts out as an empty set
37
38
  // and will get populated on every build.
@@ -44,6 +45,12 @@ class NetlifyFunction {
44
45
  return /^[A-Za-z0-9_-]+$/.test(this.name)
45
46
  }
46
47
 
48
+ async isScheduled() {
49
+ await this.buildQueue
50
+
51
+ return Boolean(this.schedule)
52
+ }
53
+
47
54
  // The `build` method transforms source files into invocable functions. Its
48
55
  // return value is an object with:
49
56
  //
@@ -61,12 +68,13 @@ class NetlifyFunction {
61
68
  this.buildQueue = buildFunction({ cache })
62
69
 
63
70
  try {
64
- const { srcFiles, ...buildData } = await this.buildQueue
71
+ const { schedule, srcFiles, ...buildData } = await this.buildQueue
65
72
  const srcFilesSet = new Set(srcFiles)
66
73
  const srcFilesDiff = this.getSrcFilesDiff(srcFilesSet)
67
74
 
68
75
  this.buildData = buildData
69
76
  this.srcFiles = srcFilesSet
77
+ this.schedule = schedule
70
78
 
71
79
  return { srcFilesDiff }
72
80
  } catch (error) {
@@ -1,7 +1,7 @@
1
1
  const { mkdir, writeFile } = require('fs').promises
2
2
  const path = require('path')
3
3
 
4
- const { zipFunction } = require('@netlify/zip-it-and-ship-it')
4
+ const { listFunction, zipFunction } = require('@netlify/zip-it-and-ship-it')
5
5
  const decache = require('decache')
6
6
  const readPkgUp = require('read-pkg-up')
7
7
  const sourceMapSupport = require('source-map-support')
@@ -35,7 +35,11 @@ const buildFunction = async ({ cache, config, directory, func, hasTypeModule, pr
35
35
  // root of the functions directory (e.g. `functions/my-func.js`). In
36
36
  // this case, we use `mainFile` as the function path of `zipFunction`.
37
37
  const entryPath = functionDirectory === directory ? func.mainFile : functionDirectory
38
- const { inputs, path: functionPath } = await memoizedBuild({
38
+ const {
39
+ inputs,
40
+ path: functionPath,
41
+ schedule,
42
+ } = await memoizedBuild({
39
43
  cache,
40
44
  cacheKey: `zisi-${entryPath}`,
41
45
  command: () => zipFunction(entryPath, targetDirectory, zipOptions),
@@ -56,7 +60,22 @@ const buildFunction = async ({ cache, config, directory, func, hasTypeModule, pr
56
60
 
57
61
  clearFunctionsCache(targetDirectory)
58
62
 
59
- return { buildPath, srcFiles }
63
+ return { buildPath, srcFiles, schedule }
64
+ }
65
+
66
+ /**
67
+ * @param {object} params
68
+ * @param {unknown} params.config
69
+ * @param {string} params.mainFile
70
+ * @param {string} params.projectRoot
71
+ */
72
+ const parseForSchedule = async ({ config, mainFile, projectRoot }) => {
73
+ const listedFunction = await listFunction(mainFile, {
74
+ config: netlifyConfigToZisiConfig({ config, projectRoot }),
75
+ parseISC: true,
76
+ })
77
+
78
+ return listedFunction && listedFunction.schedule
60
79
  }
61
80
 
62
81
  // Clears the cache for any files inside the directory from which functions are
@@ -79,10 +98,11 @@ const getTargetDirectory = async ({ errorExit }) => {
79
98
  return targetDirectory
80
99
  }
81
100
 
101
+ const netlifyConfigToZisiConfig = ({ config, projectRoot }) =>
102
+ addFunctionsConfigDefaults(normalizeFunctionsConfig({ functionsConfig: config.functions, projectRoot }))
103
+
82
104
  module.exports = async ({ config, directory, errorExit, func, projectRoot }) => {
83
- const functionsConfig = addFunctionsConfigDefaults(
84
- normalizeFunctionsConfig({ functionsConfig: config.functions, projectRoot }),
85
- )
105
+ const functionsConfig = netlifyConfigToZisiConfig({ config, projectRoot })
86
106
 
87
107
  const packageJson = await readPkgUp(func.mainFile)
88
108
  const hasTypeModule = packageJson && packageJson.packageJson.type === 'module'
@@ -115,3 +135,5 @@ module.exports = async ({ config, directory, errorExit, func, projectRoot }) =>
115
135
  target: targetDirectory,
116
136
  }
117
137
  }
138
+
139
+ module.exports.parseForSchedule = parseForSchedule
@@ -46,8 +46,9 @@ const getBuildFunction = async ({ config, directory, errorExit, func, projectRoo
46
46
  // main file otherwise.
47
47
  const functionDirectory = dirname(func.mainFile)
48
48
  const srcFiles = functionDirectory === directory ? [func.mainFile] : [functionDirectory]
49
+ const schedule = await detectZisiBuilder.parseForSchedule({ mainFile: func.mainFile, config, projectRoot })
49
50
 
50
- return () => ({ srcFiles })
51
+ return () => ({ schedule, srcFiles })
51
52
  }
52
53
 
53
54
  const invokeFunction = async ({ context, event, func, timeout }) => {
@@ -0,0 +1,98 @@
1
+ const ansi2html = require('ansi2html')
2
+
3
+ const { CLOCKWORK_USERAGENT } = require('../../utils')
4
+
5
+ const { formatLambdaError } = require('./utils')
6
+
7
+ const buildHelpResponse = ({ error, headers, path, result }) => {
8
+ const acceptsHtml = headers.accept && headers.accept.includes('text/html')
9
+
10
+ const paragraph = (text) => {
11
+ text = text.trim()
12
+
13
+ if (acceptsHtml) {
14
+ return ansi2html(`<p>${text}</p>`)
15
+ }
16
+
17
+ text = text
18
+ .replace(/<pre><code>/gm, '```\n')
19
+ .replace(/<\/code><\/pre>/gm, '\n```')
20
+ .replace(/<code>/gm, '`')
21
+ .replace(/<\/code>/gm, '`')
22
+
23
+ return `${text}\n\n`
24
+ }
25
+
26
+ const isSimulatedRequest = headers['user-agent'] === CLOCKWORK_USERAGENT
27
+
28
+ let message = ''
29
+
30
+ if (!isSimulatedRequest) {
31
+ message += paragraph(`
32
+ You performed an HTTP request to <code>${path}</code>, which is a scheduled function.
33
+ You can do this to test your functions locally, but it won't work in production.
34
+ `)
35
+ }
36
+
37
+ if (error) {
38
+ message += paragraph(`
39
+ There was an error during execution of your scheduled function:
40
+
41
+ <pre><code>${formatLambdaError(error)}</code></pre>`)
42
+ }
43
+
44
+ if (result) {
45
+ // lambda emulator adds level field, which isn't user-provided
46
+ const returnValue = { ...result }
47
+ delete returnValue.level
48
+
49
+ const { statusCode } = returnValue
50
+ if (statusCode >= 500) {
51
+ message += paragraph(`
52
+ Your function returned a status code of <code>${statusCode}</code>.
53
+ At the moment, Netlify does nothing about that. In the future, there might be a retry mechanism based on this.
54
+ `)
55
+ }
56
+
57
+ const allowedKeys = new Set(['statusCode'])
58
+ const returnedKeys = Object.keys(returnValue)
59
+ const ignoredKeys = returnedKeys.filter((key) => !allowedKeys.has(key))
60
+
61
+ if (ignoredKeys.length !== 0) {
62
+ message += paragraph(
63
+ `Your function returned ${ignoredKeys
64
+ .map((key) => `<code>${key}</code>`)
65
+ .join(', ')}. Is this an accident? It won't be interpreted by Netlify.`,
66
+ )
67
+ }
68
+ }
69
+
70
+ const statusCode = error ? 500 : 200
71
+ return acceptsHtml
72
+ ? {
73
+ statusCode,
74
+ contentType: 'text/html',
75
+ message: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">\n
76
+ ${message}`,
77
+ }
78
+ : {
79
+ statusCode,
80
+ contentType: 'text/plain',
81
+ message,
82
+ }
83
+ }
84
+
85
+ const handleScheduledFunction = ({ error, request, response, result }) => {
86
+ const { contentType, message, statusCode } = buildHelpResponse({
87
+ error,
88
+ headers: request.headers,
89
+ path: request.path,
90
+ result,
91
+ })
92
+
93
+ response.status(statusCode)
94
+ response.set('Content-Type', contentType)
95
+ response.send(message)
96
+ }
97
+
98
+ module.exports = { handleScheduledFunction, buildHelpResponse }
@@ -6,6 +6,7 @@ const { NETLIFYDEVERR, NETLIFYDEVLOG, error: errorExit, getInternalFunctionsDir,
6
6
  const { handleBackgroundFunction, handleBackgroundFunctionResult } = require('./background')
7
7
  const { createFormSubmissionHandler } = require('./form-submissions-handler')
8
8
  const { FunctionsRegistry } = require('./registry')
9
+ const { handleScheduledFunction } = require('./scheduled')
9
10
  const { handleSynchronousFunction } = require('./synchronous')
10
11
  const { shouldBase64Encode } = require('./utils')
11
12
 
@@ -105,6 +106,15 @@ const createHandler = function ({ functionsRegistry }) {
105
106
  const { error } = await func.invoke(event, clientContext)
106
107
 
107
108
  handleBackgroundFunctionResult(functionName, error)
109
+ } else if (await func.isScheduled()) {
110
+ const { error, result } = await func.invoke(event, clientContext)
111
+
112
+ handleScheduledFunction({
113
+ error,
114
+ result,
115
+ request,
116
+ response,
117
+ })
108
118
  } else {
109
119
  const { error, result } = await func.invoke(event, clientContext)
110
120
 
@@ -0,0 +1,5 @@
1
+ const CLOCKWORK_USERAGENT = 'Netlify Clockwork'
2
+
3
+ module.exports = {
4
+ CLOCKWORK_USERAGENT,
5
+ }
@@ -5,10 +5,10 @@ const getUrlPath = (functionName) => `/.netlify/functions/${functionName}`
5
5
 
6
6
  const BACKGROUND = '-background'
7
7
 
8
- const addFunctionProps = ({ mainFile, name, runtime }) => {
8
+ const addFunctionProps = ({ mainFile, name, runtime, schedule }) => {
9
9
  const urlPath = getUrlPath(name)
10
10
  const isBackground = name.endsWith(BACKGROUND)
11
- return { mainFile, name, runtime, urlPath, isBackground }
11
+ return { mainFile, name, runtime, urlPath, isBackground, schedule }
12
12
  }
13
13
 
14
14
  const JS = 'js'
@@ -21,7 +21,9 @@ const getFunctions = async (functionsSrcDir) => {
21
21
  // performance optimization, load '@netlify/zip-it-and-ship-it' on demand
22
22
  // eslint-disable-next-line node/global-require
23
23
  const { listFunctions } = require('@netlify/zip-it-and-ship-it')
24
- const functions = await listFunctions(functionsSrcDir)
24
+ const functions = await listFunctions(functionsSrcDir, {
25
+ parseISC: true,
26
+ })
25
27
  const functionsWithProps = functions.filter(({ runtime }) => runtime === JS).map((func) => addFunctionProps(func))
26
28
  return functionsWithProps
27
29
  }
@@ -1,8 +1,10 @@
1
+ const constants = require('./constants')
1
2
  const edgeHandlers = require('./edge-handlers')
2
3
  const functions = require('./functions')
3
4
  const getFunctions = require('./get-functions')
4
5
 
5
6
  module.exports = {
7
+ ...constants,
6
8
  ...functions,
7
9
  ...edgeHandlers,
8
10
  ...getFunctions,