@netlify/edge-bundler 8.13.2 → 8.14.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.
@@ -76,9 +76,8 @@ const createDeclarationsFromFunctionConfigs = (functionConfigs, functionsVisited
76
76
  // Validates and normalizes a pattern so that it's a valid regular expression
77
77
  // in Go, which is the engine used by our edge nodes.
78
78
  export const parsePattern = (pattern) => {
79
- // Escaping forward slashes with back slashes.
80
- const normalizedPattern = pattern.replace(/\//g, '\\/');
81
- const regex = regexpAST.transform(`/${normalizedPattern}/`, {
79
+ const regexp = new RegExp(pattern);
80
+ const newRegexp = regexpAST.transform(regexp, {
82
81
  Assertion(path) {
83
82
  // Lookaheads are not supported. If we find one, throw an error.
84
83
  if (path.node.kind === 'Lookahead') {
@@ -99,5 +98,5 @@ export const parsePattern = (pattern) => {
99
98
  },
100
99
  });
101
100
  // Strip leading and forward slashes.
102
- return regex.toString().slice(1, -1);
101
+ return newRegexp.toString().slice(1, -1);
103
102
  };
@@ -1,5 +1,5 @@
1
1
  import { test, expect } from 'vitest';
2
- import { mergeDeclarations } from './declaration.js';
2
+ import { mergeDeclarations, parsePattern } from './declaration.js';
3
3
  const deployConfigDeclarations = [];
4
4
  test('Ensure the order of edge functions with FF', () => {
5
5
  const deployConfigDeclarations = [
@@ -137,3 +137,15 @@ test('netlify.toml-defined excludedPath are respected', () => {
137
137
  const declarations = mergeDeclarations(tomlConfig, funcConfig, {}, deployConfigDeclarations);
138
138
  expect(declarations).toEqual(expectedDeclarations);
139
139
  });
140
+ test('Does not escape front slashes in a regex pattern if they are already escaped', () => {
141
+ const regexPattern = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[\\/#\\?]?$';
142
+ const expected = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[\\/#\\?]?$';
143
+ const actual = parsePattern(regexPattern);
144
+ expect(actual).toEqual(expected);
145
+ });
146
+ test('Escapes front slashes in a regex pattern', () => {
147
+ const regexPattern = '^(?:/(_next/data/[^/]{1,}))?(?:/([^/.]{1,}))/shows(?:/(.*))(.json)?[/#\\?]?$';
148
+ const expected = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[/#\\?]?$';
149
+ const actual = parsePattern(regexPattern);
150
+ expect(actual).toEqual(expected);
151
+ });
@@ -44,4 +44,4 @@ interface WriteManifestOptions extends GenerateManifestOptions {
44
44
  distDirectory: string;
45
45
  }
46
46
  declare const writeManifest: ({ distDirectory, ...rest }: WriteManifestOptions) => Promise<Manifest>;
47
- export { generateManifest, Manifest, writeManifest };
47
+ export { generateManifest, Manifest, Route, writeManifest };
@@ -1,5 +1,6 @@
1
1
  import { env } from 'process';
2
2
  import { test, expect, vi } from 'vitest';
3
+ import { getRouteMatcher } from '../test/util.js';
3
4
  import { BundleFormat } from './bundle.js';
4
5
  import { generateManifest } from './manifest.js';
5
6
  test('Generates a manifest with different bundles', () => {
@@ -56,23 +57,45 @@ test('Generates a manifest with a generator field', () => {
56
57
  expect(manifest.function_config).toEqual(expectedFunctionConfig);
57
58
  });
58
59
  test('Generates a manifest with excluded paths and patterns', () => {
60
+ var _a, _b;
59
61
  const functions = [
60
62
  { name: 'func-1', path: '/path/to/func-1.ts' },
61
63
  { name: 'func-2', path: '/path/to/func-2.ts' },
64
+ { name: 'func-3', path: '/path/to/func-3.ts' },
62
65
  ];
63
66
  const declarations = [
64
67
  { function: 'func-1', path: '/f1/*', excludedPath: '/f1/exclude' },
65
68
  { function: 'func-2', pattern: '^/f2/.*/?$', excludedPattern: '^/f2/exclude$' },
69
+ { function: 'func-3', path: '/*', excludedPath: '/**/*.html' },
66
70
  ];
67
71
  const manifest = generateManifest({ bundles: [], declarations, functions });
68
72
  const expectedRoutes = [
69
73
  { function: 'func-1', pattern: '^/f1/.*/?$' },
70
74
  { function: 'func-2', pattern: '^/f2/.*/?$' },
75
+ { function: 'func-3', pattern: '^/.*/?$' },
71
76
  ];
72
77
  expect(manifest.routes).toEqual(expectedRoutes);
73
78
  expect(manifest.function_config).toEqual({
74
79
  'func-1': { excluded_patterns: ['^/f1/exclude/?$'] },
75
80
  'func-2': { excluded_patterns: ['^/f2/exclude$'] },
81
+ 'func-3': { excluded_patterns: ['^/.*/.*\\.html/?$'] },
82
+ });
83
+ expect(manifest.bundler_version).toBe(env.npm_package_version);
84
+ const matcher = getRouteMatcher(manifest);
85
+ expect((_a = matcher('/f1/hello')) === null || _a === void 0 ? void 0 : _a.function).toBe('func-1');
86
+ expect((_b = matcher('/grandparent/parent/child/grandchild.html')) === null || _b === void 0 ? void 0 : _b.function).toBeUndefined();
87
+ });
88
+ test('TOML-defined paths can be combined with ISC-defined excluded paths', () => {
89
+ const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
90
+ const declarations = [{ function: 'func-1', path: '/f1/*' }];
91
+ const userFunctionConfig = {
92
+ 'func-1': { excludedPath: '/f1/exclude' },
93
+ };
94
+ const manifest = generateManifest({ bundles: [], declarations, functions, userFunctionConfig });
95
+ const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$' }];
96
+ expect(manifest.routes).toEqual(expectedRoutes);
97
+ expect(manifest.function_config).toEqual({
98
+ 'func-1': { excluded_patterns: ['^/f1/exclude/?$'] },
76
99
  });
77
100
  expect(manifest.bundler_version).toBe(env.npm_package_version);
78
101
  });
@@ -1,3 +1,4 @@
1
+ import type { Manifest } from '../node/manifest.js';
1
2
  declare const testLogger: import("../node/logger.js").Logger;
2
3
  declare const fixturesDir: string;
3
4
  declare const useFixture: (fixtureName: string) => Promise<{
@@ -5,5 +6,6 @@ declare const useFixture: (fixtureName: string) => Promise<{
5
6
  cleanup: () => Promise<void>;
6
7
  distPath: string;
7
8
  }>;
9
+ declare const getRouteMatcher: (manifest: Manifest) => (candidate: string) => import("../node/manifest.js").Route | undefined;
8
10
  declare const runESZIP: (eszipPath: string) => Promise<any>;
9
- export { fixturesDir, testLogger, runESZIP, useFixture };
11
+ export { fixturesDir, getRouteMatcher, testLogger, runESZIP, useFixture };
package/dist/test/util.js CHANGED
@@ -35,6 +35,15 @@ const inspectFunction = (path) => `
35
35
 
36
36
  console.log(JSON.stringify(responses));
37
37
  `;
38
+ const getRouteMatcher = (manifest) => (candidate) => manifest.routes.find((route) => {
39
+ const regex = new RegExp(route.pattern);
40
+ if (!regex.test(candidate)) {
41
+ return false;
42
+ }
43
+ const excludedPattern = manifest.function_config[route.function].excluded_patterns;
44
+ const isExcluded = excludedPattern.some((pattern) => new RegExp(pattern).test(candidate));
45
+ return !isExcluded;
46
+ });
38
47
  const runESZIP = async (eszipPath) => {
39
48
  var _a, _b, _c;
40
49
  const tmpDir = await tmp.dir({ unsafeCleanup: true });
@@ -66,4 +75,4 @@ const runESZIP = async (eszipPath) => {
66
75
  await tmpDir.cleanup();
67
76
  return JSON.parse(result.stdout);
68
77
  };
69
- export { fixturesDir, testLogger, runESZIP, useFixture };
78
+ export { fixturesDir, getRouteMatcher, testLogger, runESZIP, useFixture };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "8.13.2",
3
+ "version": "8.14.0",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "keywords": [],
44
44
  "license": "MIT",
45
- "repository": "netlify/edge-bundler",
45
+ "repository": "https://github.com/netlify/edge-bundler",
46
46
  "bugs": {
47
47
  "url": "https://github.com/netlify/edge-bundler/issues"
48
48
  },