@netlify/edge-bundler 8.5.0 → 8.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.
@@ -74,6 +74,7 @@ const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], {
74
74
  bundles: [functionBundle],
75
75
  declarations,
76
76
  distDirectory,
77
+ featureFlags,
77
78
  functions,
78
79
  functionConfig: functionsWithConfig,
79
80
  importMap: importMapSpecifier,
@@ -4,6 +4,7 @@ interface BaseDeclaration {
4
4
  cache?: string;
5
5
  function: string;
6
6
  name?: string;
7
+ generator?: string;
7
8
  }
8
9
  type DeclarationWithPath = BaseDeclaration & {
9
10
  path: string;
@@ -1,6 +1,7 @@
1
1
  const defaultFlags = {
2
2
  edge_functions_cache_deno_dir: false,
3
3
  edge_functions_config_export: false,
4
+ edge_functions_fail_unsupported_regex: false,
4
5
  };
5
6
  const getFlags = (input = {}, flags = defaultFlags) => Object.entries(flags).reduce((result, [key, defaultValue]) => ({
6
7
  ...result,
@@ -2,11 +2,13 @@ import type { Bundle } from './bundle.js';
2
2
  import { FunctionConfig } from './config.js';
3
3
  import { Declaration } from './declaration.js';
4
4
  import { EdgeFunction } from './edge_function.js';
5
+ import { FeatureFlags } from './feature_flags.js';
5
6
  import { Layer } from './layer.js';
6
7
  interface Route {
7
8
  function: string;
8
9
  name?: string;
9
10
  pattern: string;
11
+ generator?: string;
10
12
  }
11
13
  interface EdgeFunctionConfig {
12
14
  excluded_patterns: string[];
@@ -29,6 +31,7 @@ interface Manifest {
29
31
  interface GenerateManifestOptions {
30
32
  bundles?: Bundle[];
31
33
  declarations?: Declaration[];
34
+ featureFlags?: FeatureFlags;
32
35
  functions: EdgeFunction[];
33
36
  functionConfig?: Record<string, FunctionConfig>;
34
37
  importMap?: string;
@@ -38,8 +41,9 @@ interface Route {
38
41
  function: string;
39
42
  name?: string;
40
43
  pattern: string;
44
+ generator?: string;
41
45
  }
42
- declare const generateManifest: ({ bundles, declarations, functions, functionConfig, importMap, layers, }: GenerateManifestOptions) => Manifest;
46
+ declare const generateManifest: ({ bundles, declarations, featureFlags, functions, functionConfig, importMap, layers, }: GenerateManifestOptions) => Manifest;
43
47
  interface WriteManifestOptions extends GenerateManifestOptions {
44
48
  distDirectory: string;
45
49
  }
@@ -18,7 +18,7 @@ const sanitizeEdgeFunctionConfig = (config) => {
18
18
  }
19
19
  return newConfig;
20
20
  };
21
- const generateManifest = ({ bundles = [], declarations = [], functions, functionConfig = {}, importMap, layers = [], }) => {
21
+ const generateManifest = ({ bundles = [], declarations = [], featureFlags, functions, functionConfig = {}, importMap, layers = [], }) => {
22
22
  const preCacheRoutes = [];
23
23
  const postCacheRoutes = [];
24
24
  const manifestFunctionConfig = Object.fromEntries(functions.map(({ name }) => [name, { excluded_patterns: [] }]));
@@ -34,13 +34,14 @@ const generateManifest = ({ bundles = [], declarations = [], functions, function
34
34
  if (func === undefined) {
35
35
  return;
36
36
  }
37
- const pattern = getRegularExpression(declaration);
37
+ const pattern = getRegularExpression(declaration, featureFlags === null || featureFlags === void 0 ? void 0 : featureFlags.edge_functions_fail_unsupported_regex);
38
38
  const route = {
39
39
  function: func.name,
40
40
  name: declaration.name,
41
41
  pattern: serializePattern(pattern),
42
+ generator: declaration.generator,
42
43
  };
43
- const excludedPattern = getExcludedRegularExpression(declaration);
44
+ const excludedPattern = getExcludedRegularExpression(declaration, featureFlags === null || featureFlags === void 0 ? void 0 : featureFlags.edge_functions_fail_unsupported_regex);
44
45
  if (excludedPattern) {
45
46
  manifestFunctionConfig[func.name].excluded_patterns.push(serializePattern(excludedPattern));
46
47
  }
@@ -76,24 +77,34 @@ const pathToRegularExpression = (path) => {
76
77
  const normalizedSource = `^${regularExpression.source}\\/?$`;
77
78
  return normalizedSource;
78
79
  };
79
- const getRegularExpression = (declaration) => {
80
+ const getRegularExpression = (declaration, failUnsupportedRegex = false) => {
80
81
  if ('pattern' in declaration) {
81
82
  try {
82
83
  return parsePattern(declaration.pattern);
83
84
  }
84
85
  catch (error) {
85
- throw new Error(`Could not parse path declaration of function '${declaration.function}': ${error.message}`);
86
+ // eslint-disable-next-line max-depth
87
+ if (failUnsupportedRegex) {
88
+ throw new Error(`Could not parse path declaration of function '${declaration.function}': ${error.message}`);
89
+ }
90
+ console.warn(`Function '${declaration.function}' uses an unsupported regular expression and will not be invoked: ${error.message}`);
91
+ return declaration.pattern;
86
92
  }
87
93
  }
88
94
  return pathToRegularExpression(declaration.path);
89
95
  };
90
- const getExcludedRegularExpression = (declaration) => {
96
+ const getExcludedRegularExpression = (declaration, failUnsupportedRegex = false) => {
91
97
  if ('excludedPattern' in declaration && declaration.excludedPattern) {
92
98
  try {
93
99
  return parsePattern(declaration.excludedPattern);
94
100
  }
95
101
  catch (error) {
96
- throw new Error(`Could not parse path declaration of function '${declaration.function}': ${error.message}`);
102
+ // eslint-disable-next-line max-depth
103
+ if (failUnsupportedRegex) {
104
+ throw new Error(`Could not parse path declaration of function '${declaration.function}': ${error.message}`);
105
+ }
106
+ console.warn(`Function '${declaration.function}' uses an unsupported regular expression and will therefore not be invoked: ${error.message}`);
107
+ return declaration.excludedPattern;
97
108
  }
98
109
  }
99
110
  if ('path' in declaration && declaration.excludedPath) {
@@ -1,5 +1,5 @@
1
1
  import { env } from 'process';
2
- import { test, expect } from 'vitest';
2
+ import { test, expect, vi } from 'vitest';
3
3
  import { BundleFormat } from './bundle.js';
4
4
  import { generateManifest } from './manifest.js';
5
5
  test('Generates a manifest with different bundles', () => {
@@ -42,6 +42,27 @@ test('Generates a manifest with display names', () => {
42
42
  expect(manifest.routes).toEqual(expectedRoutes);
43
43
  expect(manifest.bundler_version).toBe(env.npm_package_version);
44
44
  });
45
+ test('Generates a manifest with a generator field', () => {
46
+ const functions = [
47
+ { name: 'func-1', path: '/path/to/func-1.ts' },
48
+ { name: 'func-2', path: '/path/to/func-2.ts' },
49
+ { name: 'func-3', path: '/path/to/func-3.ts' },
50
+ ];
51
+ const declarations = [
52
+ { function: 'func-1', generator: '@netlify/fake-plugin@1.0.0', path: '/f1/*' },
53
+ { function: 'func-2', path: '/f2/*' },
54
+ { function: 'func-3', generator: '@netlify/fake-plugin@1.0.0', cache: 'manual', path: '/f3' },
55
+ ];
56
+ const manifest = generateManifest({ bundles: [], declarations, functions });
57
+ const expectedRoutes = [
58
+ { function: 'func-1', generator: '@netlify/fake-plugin@1.0.0', pattern: '^/f1/.*/?$' },
59
+ { function: 'func-2', pattern: '^/f2/.*/?$' },
60
+ ];
61
+ const expectedPostCacheRoutes = [{ function: 'func-3', generator: '@netlify/fake-plugin@1.0.0', pattern: '^/f3/?$' }];
62
+ expect(manifest.routes).toEqual(expectedRoutes);
63
+ expect(manifest.post_cache_routes).toEqual(expectedPostCacheRoutes);
64
+ expect(manifest.bundler_version).toBe(env.npm_package_version);
65
+ });
45
66
  test('Generates a manifest with excluded paths and patterns', () => {
46
67
  const functions = [
47
68
  { name: 'func-1', path: '/path/to/func-1.ts' },
@@ -164,10 +185,31 @@ test('Generates a manifest with layers', () => {
164
185
  expect(manifest2.routes).toEqual(expectedRoutes);
165
186
  expect(manifest2.layers).toEqual(layers);
166
187
  });
167
- test('Throws an error if the regular expression contains a negative lookahead', () => {
188
+ test('Shows a warning if the regular expression contains a negative lookahead', () => {
189
+ const mockConsoleWarn = vi.fn();
190
+ const consoleWarn = console.warn;
191
+ console.warn = mockConsoleWarn;
192
+ const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
193
+ const declarations = [{ function: 'func-1', pattern: '^/\\w+(?=\\d)$' }];
194
+ const manifest = generateManifest({
195
+ bundles: [],
196
+ declarations,
197
+ functions,
198
+ });
199
+ console.warn = consoleWarn;
200
+ expect(manifest.routes).toEqual([{ function: 'func-1', pattern: '^/\\w+(?=\\d)$' }]);
201
+ expect(mockConsoleWarn).toHaveBeenCalledOnce();
202
+ expect(mockConsoleWarn).toHaveBeenCalledWith("Function 'func-1' uses an unsupported regular expression and will not be invoked: Regular expressions with lookaheads are not supported");
203
+ });
204
+ test('Throws an error if the regular expression contains a negative lookahead and the `edge_functions_fail_unsupported_regex` flag is set', () => {
168
205
  const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
169
206
  const declarations = [{ function: 'func-1', pattern: '^/\\w+(?=\\d)$' }];
170
- expect(() => generateManifest({ bundles: [], declarations, functions })).toThrowError(/^Could not parse path declaration of function 'func-1': Regular expressions with lookaheads are not supported$/);
207
+ expect(() => generateManifest({
208
+ bundles: [],
209
+ declarations,
210
+ featureFlags: { edge_functions_fail_unsupported_regex: true },
211
+ functions,
212
+ })).toThrowError(/^Could not parse path declaration of function 'func-1': Regular expressions with lookaheads are not supported$/);
171
213
  });
172
214
  test('Converts named capture groups to unnamed capture groups in regular expressions', () => {
173
215
  const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
@@ -14,6 +14,7 @@ const getBaseManifest = () => ({
14
14
  name: 'name',
15
15
  function: 'hello',
16
16
  pattern: '^/hello/?$',
17
+ generator: '@netlify/fake-plugin@1.0.0',
17
18
  },
18
19
  ],
19
20
  post_cache_routes: [
@@ -21,6 +22,7 @@ const getBaseManifest = () => ({
21
22
  name: 'name',
22
23
  function: 'hello',
23
24
  pattern: '^/hello/?$',
25
+ generator: '@netlify/fake-plugin@1.0.0',
24
26
  },
25
27
  ],
26
28
  layers: [
@@ -36,6 +36,9 @@ declare const edgeManifestSchema: {
36
36
  format: string;
37
37
  errorMessage: string;
38
38
  };
39
+ generator: {
40
+ type: string;
41
+ };
39
42
  };
40
43
  additionalProperties: boolean;
41
44
  };
@@ -57,6 +60,9 @@ declare const edgeManifestSchema: {
57
60
  format: string;
58
61
  errorMessage: string;
59
62
  };
63
+ generator: {
64
+ type: string;
65
+ };
60
66
  };
61
67
  additionalProperties: boolean;
62
68
  };
@@ -18,6 +18,7 @@ const routesSchema = {
18
18
  format: 'regexPattern',
19
19
  errorMessage: 'pattern needs to be a regex that starts with ^ and ends with $ without any additional slashes before and afterwards',
20
20
  },
21
+ generator: { type: 'string' },
21
22
  },
22
23
  additionalProperties: false,
23
24
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "8.5.0",
3
+ "version": "8.7.0",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",