@netlify/edge-bundler 14.5.5 → 14.6.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,5 +1,4 @@
1
1
  import { DenoBridge } from './bridge.js';
2
- import { EdgeFunction } from './edge_function.js';
3
2
  import { ImportMap } from './import_map.js';
4
3
  import { Logger } from './logger.js';
5
4
  import { RateLimit } from './rate_limit.js';
@@ -31,10 +30,10 @@ interface FunctionConfigWithPattern extends BaseFunctionConfig {
31
30
  }
32
31
  export type FunctionConfig = FunctionConfigWithPath | FunctionConfigWithPattern;
33
32
  export type FunctionConfigWithAllPossibleFields = BaseFunctionConfig & Partial<FunctionConfigWithPath & FunctionConfigWithPattern>;
34
- export declare const getFunctionConfig: ({ func, importMap, deno, log, }: {
35
- func: EdgeFunction;
36
- importMap: ImportMap;
33
+ export declare const getFunctionConfig: ({ deno, functionPath, importMap, log, }: {
37
34
  deno: DenoBridge;
35
+ functionPath: string;
36
+ importMap: ImportMap;
38
37
  log: Logger;
39
38
  }) => Promise<FunctionConfig>;
40
39
  export {};
@@ -27,7 +27,7 @@ const getConfigExtractor = () => {
27
27
  const configExtractorPath = join(packagePath, 'deno', 'config.ts');
28
28
  return configExtractorPath;
29
29
  };
30
- export const getFunctionConfig = async ({ func, importMap, deno, log, }) => {
30
+ export const getFunctionConfig = async ({ deno, functionPath, importMap, log, }) => {
31
31
  // The extractor is a Deno script that will import the function and run its
32
32
  // `config` export, if one exists.
33
33
  const extractorPath = getConfigExtractor();
@@ -54,15 +54,12 @@ export const getFunctionConfig = async ({ func, importMap, deno, log, }) => {
54
54
  '--node-modules-dir=false',
55
55
  '--quiet',
56
56
  extractorPath,
57
- pathToFileURL(func.path).href,
57
+ pathToFileURL(functionPath).href,
58
58
  pathToFileURL(collector.path).href,
59
59
  JSON.stringify(ConfigExitCode),
60
60
  ].filter(Boolean), { rejectOnExitCode: false });
61
- if (stderr.includes('Import assertions are deprecated')) {
62
- log.system(`Edge function uses import assertions: ${func.path}`);
63
- }
64
61
  if (exitCode !== ConfigExitCode.Success) {
65
- handleConfigError(func, exitCode, stderr, log);
62
+ handleConfigError(functionPath, exitCode, stderr, log);
66
63
  return {};
67
64
  }
68
65
  if (stdout !== '') {
@@ -74,35 +71,38 @@ export const getFunctionConfig = async ({ func, importMap, deno, log, }) => {
74
71
  collectorData = JSON.parse(collectorDataJSON);
75
72
  }
76
73
  catch {
77
- handleConfigError(func, ConfigExitCode.UnhandledError, stderr, log);
74
+ handleConfigError(functionPath, ConfigExitCode.UnhandledError, stderr, log);
78
75
  }
79
76
  finally {
80
77
  await collector.cleanup();
81
78
  }
82
79
  if (!isValidOnError(collectorData.onError)) {
83
- throw new BundleError(new Error(`The 'onError' configuration property in edge function at '${func.path}' must be one of 'fail', 'bypass', or a path starting with '/'. Got '${collectorData.onError}'. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
80
+ throw new BundleError(new Error(`The 'onError' configuration property in edge function at '${functionPath}' must be one of 'fail', 'bypass', or a path starting with '/'. Got '${collectorData.onError}'. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
84
81
  }
85
82
  return collectorData;
86
83
  };
87
- const handleConfigError = (func, exitCode, stderr, log) => {
84
+ const handleConfigError = (functionPath, exitCode, stderr, log) => {
85
+ let cause;
86
+ if (stderr.includes('Import assertions are deprecated')) {
87
+ log.system(`Edge function uses import assertions: ${functionPath}`);
88
+ cause = 'IMPORT_ASSERT';
89
+ }
88
90
  switch (exitCode) {
89
91
  case ConfigExitCode.ImportError:
90
92
  log.user(stderr);
91
- throw new BundleError(new Error(`Could not load edge function at '${func.path}'. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
92
- break;
93
+ throw new BundleError(new Error(`Could not load edge function at '${functionPath}'. More on the Edge Functions API at https://ntl.fyi/edge-api.`), { cause });
93
94
  case ConfigExitCode.NoConfig:
94
- log.system(`No in-source config found for edge function at '${func.path}'`);
95
+ log.system(`No in-source config found for edge function at '${functionPath}'`);
95
96
  break;
96
97
  case ConfigExitCode.InvalidExport:
97
- throw new BundleError(new Error(`The 'config' export in edge function at '${func.path}' must be an object. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
98
- break;
98
+ throw new BundleError(new Error(`The 'config' export in edge function at '${functionPath}' must be an object. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
99
99
  case ConfigExitCode.SerializationError:
100
- throw new BundleError(new Error(`The 'config' object in the edge function at '${func.path}' must contain primitive values only. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
100
+ throw new BundleError(new Error(`The 'config' object in the edge function at '${functionPath}' must contain primitive values only. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
101
101
  break;
102
102
  case ConfigExitCode.InvalidDefaultExport:
103
- throw new BundleError(new Error(`Default export in '${func.path}' must be a function. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
103
+ throw new BundleError(new Error(`Default export in '${functionPath}' must be a function. More on the Edge Functions API at https://ntl.fyi/edge-api.`));
104
104
  default:
105
- log.user(`Could not load configuration for edge function at '${func.path}'`);
105
+ log.user(`Could not load configuration for edge function at '${functionPath}'`);
106
106
  log.user(stderr);
107
107
  }
108
108
  };
@@ -172,10 +172,7 @@ describe('`getFunctionConfig` extracts configuration properties from function fi
172
172
  const path = join(tmpDir, `${func.name}.js`);
173
173
  await fs.writeFile(path, func.source);
174
174
  const funcCall = () => getFunctionConfig({
175
- func: {
176
- name: func.name,
177
- path,
178
- },
175
+ functionPath: path,
179
176
  importMap: new ImportMap([importMapFile]),
180
177
  deno,
181
178
  log: logger,
@@ -349,10 +346,7 @@ test('Passes validation if default export exists and is a function', async () =>
349
346
  const path = join(tmpDir, `${func.name}.ts`);
350
347
  await fs.writeFile(path, func.source);
351
348
  await expect(getFunctionConfig({
352
- func: {
353
- name: func.name,
354
- path,
355
- },
349
+ functionPath: path,
356
350
  importMap: new ImportMap([importMapFile]),
357
351
  deno,
358
352
  log: logger,
@@ -378,10 +372,7 @@ test('Fails validation if default export is not function', async () => {
378
372
  const path = join(tmpDir, `${func.name}.ts`);
379
373
  await fs.writeFile(path, func.source);
380
374
  const config = getFunctionConfig({
381
- func: {
382
- name: func.name,
383
- path,
384
- },
375
+ functionPath: path,
385
376
  importMap: new ImportMap([importMapFile]),
386
377
  deno,
387
378
  log: logger,
@@ -407,10 +398,7 @@ test('Fails validation if default export is not present', async () => {
407
398
  const path = join(tmpDir, `${func.name}.ts`);
408
399
  await fs.writeFile(path, func.source);
409
400
  const config = getFunctionConfig({
410
- func: {
411
- name: func.name,
412
- path,
413
- },
401
+ functionPath: path,
414
402
  importMap: new ImportMap([importMapFile]),
415
403
  deno,
416
404
  log: logger,
@@ -16,7 +16,6 @@ export const mergeDeclarations = (tomlDeclarations, userFunctionsConfig, interna
16
16
  return declarations;
17
17
  };
18
18
  const getDeclarationsFromInput = (inputDeclarations, functionConfigs, functionsVisited) => {
19
- var _a, _b;
20
19
  const declarations = [];
21
20
  // For any declaration for which we also have a function configuration object,
22
21
  // we replace the path because that object takes precedence.
@@ -26,7 +25,7 @@ const getDeclarationsFromInput = (inputDeclarations, functionConfigs, functionsV
26
25
  // If no config is found, add the declaration as is.
27
26
  declarations.push(declaration);
28
27
  }
29
- else if ('pattern' in config && ((_a = config.pattern) === null || _a === void 0 ? void 0 : _a.length)) {
28
+ else if ('pattern' in config && config.pattern?.length) {
30
29
  // If we have a pattern specified as either a string or non-empty array,
31
30
  // create a declaration for each pattern.
32
31
  const patterns = Array.isArray(config.pattern) ? config.pattern : [config.pattern];
@@ -34,7 +33,7 @@ const getDeclarationsFromInput = (inputDeclarations, functionConfigs, functionsV
34
33
  declarations.push({ ...declaration, cache: config.cache, pattern });
35
34
  });
36
35
  }
37
- else if ('path' in config && ((_b = config.path) === null || _b === void 0 ? void 0 : _b.length)) {
36
+ else if ('path' in config && config.path?.length) {
38
37
  // If we have a path specified as either a string or non-empty array,
39
38
  // create a declaration for each path.
40
39
  const paths = Array.isArray(config.path) ? config.path : [config.path];
@@ -24,13 +24,12 @@ export const load = async (path, logger) => {
24
24
  };
25
25
  };
26
26
  const parse = (data, path) => {
27
- var _a, _b;
28
27
  if (data.version !== 1) {
29
28
  throw new Error(`Unsupported file version: ${data.version}`);
30
29
  }
31
30
  const config = {
32
- declarations: (_a = data.functions) !== null && _a !== void 0 ? _a : [],
33
- layers: (_b = data.layers) !== null && _b !== void 0 ? _b : [],
31
+ declarations: data.functions ?? [],
32
+ layers: data.layers ?? [],
34
33
  };
35
34
  if (data.import_map) {
36
35
  const importMapPath = resolve(dirname(path), data.import_map);
@@ -71,8 +71,7 @@ const getLatestVersion = async () => {
71
71
  }
72
72
  };
73
73
  const getLatestVersionForRange = async (range) => {
74
- var _a;
75
- const minimumVersion = (_a = semver.minVersion(range)) === null || _a === void 0 ? void 0 : _a.version;
74
+ const minimumVersion = semver.minVersion(range)?.version;
76
75
  // We should never get here, because it means that `DENO_VERSION_RANGE` is
77
76
  // a malformed semver range. If this does happen, let's throw an error so
78
77
  // that the tests catch it.
@@ -3,6 +3,7 @@ import { Bundle } from '../bundle.js';
3
3
  import { EdgeFunction } from '../edge_function.js';
4
4
  import { FeatureFlags } from '../feature_flags.js';
5
5
  import { ImportMap } from '../import_map.js';
6
+ export declare const extension = ".eszip";
6
7
  interface BundleESZIPOptions {
7
8
  basePath: string;
8
9
  buildID: string;
@@ -15,5 +16,9 @@ interface BundleESZIPOptions {
15
16
  importMap: ImportMap;
16
17
  vendorDirectory?: string;
17
18
  }
18
- declare const bundleESZIP: ({ basePath, buildID, debug, deno, distDirectory, externals, functions, importMap, vendorDirectory, }: BundleESZIPOptions) => Promise<Bundle>;
19
- export { bundleESZIP as bundle };
19
+ export declare const bundle: ({ basePath, buildID, debug, deno, distDirectory, externals, functions, importMap, vendorDirectory, }: BundleESZIPOptions) => Promise<Bundle>;
20
+ export declare const extract: (deno: DenoBridge, functionPath: string) => Promise<{
21
+ cleanup: () => Promise<void>;
22
+ path: string;
23
+ }>;
24
+ export {};
@@ -1,13 +1,14 @@
1
1
  import { join } from 'path';
2
2
  import { pathToFileURL } from 'url';
3
+ import tmp from 'tmp-promise';
3
4
  import { virtualRoot, virtualVendorRoot } from '../../shared/consts.js';
4
5
  import { BundleFormat } from '../bundle.js';
5
6
  import { wrapBundleError } from '../bundle_error.js';
6
7
  import { wrapNpmImportError } from '../npm_import_error.js';
7
8
  import { getPackagePath } from '../package_json.js';
8
9
  import { getFileHash } from '../utils/sha256.js';
9
- const bundleESZIP = async ({ basePath, buildID, debug, deno, distDirectory, externals, functions, importMap, vendorDirectory, }) => {
10
- const extension = '.eszip';
10
+ export const extension = '.eszip';
11
+ export const bundle = async ({ basePath, buildID, debug, deno, distDirectory, externals, functions, importMap, vendorDirectory, }) => {
11
12
  const destPath = join(distDirectory, `${buildID}${extension}`);
12
13
  const importMapPrefixes = {
13
14
  [`${pathToFileURL(basePath)}/`]: virtualRoot,
@@ -44,7 +45,17 @@ const getESZIPPaths = () => {
44
45
  const denoPath = join(getPackagePath(), 'deno');
45
46
  return {
46
47
  bundler: join(denoPath, 'bundle.ts'),
48
+ extractor: join(denoPath, 'extract.ts'),
47
49
  importMap: join(denoPath, 'vendor', 'import_map.json'),
48
50
  };
49
51
  };
50
- export { bundleESZIP as bundle };
52
+ export const extract = async (deno, functionPath) => {
53
+ const tmpDir = await tmp.dir({ unsafeCleanup: true });
54
+ const { extractor, importMap } = getESZIPPaths();
55
+ const flags = ['--allow-all', '--no-config', '--no-lock', `--import-map=${importMap}`, '--quiet'];
56
+ await deno.run(['run', ...flags, extractor, functionPath, tmpDir.path], { pipeOutput: true });
57
+ return {
58
+ cleanup: tmpDir.cleanup,
59
+ path: join(tmpDir.path, 'source', 'root'),
60
+ };
61
+ };
@@ -37,7 +37,7 @@ const getLocalEntryPoint = (functions, { bootstrapURL, formatExportTypeError = d
37
37
  }
38
38
  `;
39
39
  });
40
- const bootCall = `boot(functions, metadata);`;
40
+ const bootCall = `boot(() => Promise.resolve(functions));`;
41
41
  return [bootImport, declaration, ...imports, bootCall].join('\n\n');
42
42
  };
43
43
  export { generateStage2, getLocalEntryPoint };
@@ -43,6 +43,7 @@ export const bundle = async ({ basePath, buildID, deno, distDirectory, functions
43
43
  importMap.withNodeBuiltins().toDataURL(),
44
44
  '--quiet',
45
45
  '--code-splitting',
46
+ '--allow-import',
46
47
  '--outdir',
47
48
  bundleDir.path,
48
49
  ...functions.map((func) => func.path),
@@ -12,6 +12,11 @@ const INTERNAL_IMPORTS = {
12
12
  // ImportMap can take several import map files and merge them into a final
13
13
  // import map object, also adding the internal imports in the right order.
14
14
  export class ImportMap {
15
+ // The root path which import maps can reference. If an import map attempts
16
+ // to reference a file outside this directory, an error will be thrown.
17
+ rootPath;
18
+ // The different import map files that make up the wider import map.
19
+ sources;
15
20
  constructor(sources = [], rootPath = null) {
16
21
  this.rootPath = rootPath;
17
22
  this.sources = [];
@@ -5,8 +5,8 @@ const getLogger = (systemLogger, userLogger, debug = false) => {
5
5
  // If there is a system logger configured, we'll use that. If there isn't,
6
6
  // we'll pipe system logs to stdout if `debug` is enabled and swallow them
7
7
  // otherwise.
8
- const system = systemLogger !== null && systemLogger !== void 0 ? systemLogger : (debug ? console.log : noopLogger);
9
- const user = userLogger !== null && userLogger !== void 0 ? userLogger : console.log;
8
+ const system = systemLogger ?? (debug ? console.log : noopLogger);
9
+ const user = userLogger ?? console.log;
10
10
  return {
11
11
  system,
12
12
  user,
@@ -12,8 +12,7 @@ import { getPlatformTarget } from './platform.js';
12
12
  const require = createRequire(import.meta.url);
13
13
  const archiver = require('archiver');
14
14
  test('Downloads the Deno CLI on demand and caches it for subsequent calls', async () => {
15
- var _a, _b, _c, _d;
16
- const latestVersion = (_b = (_a = semver.minVersion(DENO_VERSION_RANGE)) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : '';
15
+ const latestVersion = semver.minVersion(DENO_VERSION_RANGE)?.version ?? '';
17
16
  const mockBinaryOutput = `#!/usr/bin/env sh\n\necho "deno ${latestVersion}"`;
18
17
  const data = new PassThrough();
19
18
  const archive = archiver('zip', { zlib: { level: 9 } });
@@ -39,8 +38,8 @@ test('Downloads the Deno CLI on demand and caches it for subsequent calls', asyn
39
38
  const expectedOutput = /^deno [\d.]+/;
40
39
  expect(latestReleaseMock.isDone()).toBe(true);
41
40
  expect(downloadMock.isDone()).toBe(true);
42
- expect((_c = output1 === null || output1 === void 0 ? void 0 : output1.stdout) !== null && _c !== void 0 ? _c : '').toMatch(expectedOutput);
43
- expect((_d = output2 === null || output2 === void 0 ? void 0 : output2.stdout) !== null && _d !== void 0 ? _d : '').toMatch(expectedOutput);
41
+ expect(output1?.stdout ?? '').toMatch(expectedOutput);
42
+ expect(output2?.stdout ?? '').toMatch(expectedOutput);
44
43
  expect(beforeDownload).toHaveBeenCalledTimes(1);
45
44
  expect(afterDownload).toHaveBeenCalledTimes(1);
46
45
  await rm(tmpDir.path, { force: true, recursive: true, maxRetries: 10 });
@@ -5,7 +5,7 @@ import { getHeaderMatchers, normalizePattern } from './declaration.js';
5
5
  import { getPackageVersion } from './package_json.js';
6
6
  import { RateLimitAction, RateLimitAlgorithm, RateLimitAggregator } from './rate_limit.js';
7
7
  import { nonNullable } from './utils/non_nullable.js';
8
- import { ExtendedURLPattern } from './utils/urlpattern.js';
8
+ import { getRegexpFromURLPatternPath } from './utils/urlpattern.js';
9
9
  const removeEmptyConfigValues = (functionConfig) => Object.entries(functionConfig).reduce((acc, [key, value]) => {
10
10
  if (value && !(Array.isArray(value) && value.length === 0)) {
11
11
  return { ...acc, [key]: value };
@@ -176,10 +176,9 @@ const pathToRegularExpression = (path) => {
176
176
  return null;
177
177
  }
178
178
  try {
179
- const pattern = new ExtendedURLPattern({ pathname: path });
180
179
  // Removing the `^` and `$` delimiters because we'll need to modify what's
181
180
  // between them.
182
- const source = pattern.regexp.pathname.source.slice(1, -1);
181
+ const source = getRegexpFromURLPatternPath(path).slice(1, -1);
183
182
  // Wrapping the expression source with `^` and `$`. Also, adding an optional
184
183
  // trailing slash, so that a declaration of `path: "/foo"` matches requests
185
184
  // for both `/foo` and `/foo/`.
@@ -70,7 +70,6 @@ test('Generates a manifest with a generator field', () => {
70
70
  expect(manifest.function_config).toEqual(expectedFunctionConfig);
71
71
  });
72
72
  test('Generates a manifest with excluded paths and patterns', () => {
73
- var _a, _b;
74
73
  const functions = [
75
74
  { name: 'func-1', path: '/path/to/func-1.ts' },
76
75
  { name: 'func-2', path: '/path/to/func-2.ts' },
@@ -104,8 +103,8 @@ test('Generates a manifest with excluded paths and patterns', () => {
104
103
  expect(manifest.function_config).toEqual({});
105
104
  expect(manifest.bundler_version).toBe(version);
106
105
  const matcher = getRouteMatcher(manifest);
107
- expect((_a = matcher('/f1/hello')) === null || _a === void 0 ? void 0 : _a.function).toBe('func-1');
108
- expect((_b = matcher('/grandparent/parent/child/grandchild.html')) === null || _b === void 0 ? void 0 : _b.function).toBeUndefined();
106
+ expect(matcher('/f1/hello')?.function).toBe('func-1');
107
+ expect(matcher('/grandparent/parent/child/grandchild.html')?.function).toBeUndefined();
109
108
  });
110
109
  test('TOML-defined paths can be combined with ISC-defined excluded paths', () => {
111
110
  const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }];
@@ -36,7 +36,7 @@ const getTypePathFromTypesPackage = async (packageName, packageJsonPath) => {
36
36
  return undefined;
37
37
  }
38
38
  const { types, typings } = JSON.parse(await fs.readFile(typesPackagePath, 'utf8'));
39
- const declarationPath = types !== null && types !== void 0 ? types : typings;
39
+ const declarationPath = types ?? typings;
40
40
  if (typeof declarationPath === 'string') {
41
41
  return path.join(typesPackagePath, '..', declarationPath);
42
42
  }
@@ -51,7 +51,7 @@ const getTypesPath = async (packageJsonPath) => {
51
51
  const { name, types, typings } = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
52
52
  // this only looks at `.types` and `.typings` fields. there might also be data in `exports -> . -> types -> import/default`.
53
53
  // we're ignoring that for now.
54
- const declarationPath = types !== null && types !== void 0 ? types : typings;
54
+ const declarationPath = types ?? typings;
55
55
  if (typeof declarationPath === 'string') {
56
56
  return path.join(packageJsonPath, '..', declarationPath);
57
57
  }
@@ -117,7 +117,6 @@ async function parseImportsForFile(file, rootPath) {
117
117
  * to npm modules.
118
118
  */
119
119
  const getNPMSpecifiers = async ({ basePath, functions, importMap, environment, rootPath }, alreadySeenPaths = new Set()) => {
120
- var _a;
121
120
  const baseURL = pathToFileURL(basePath);
122
121
  const npmSpecifiers = [];
123
122
  for (const func of functions) {
@@ -152,7 +151,7 @@ const getNPMSpecifiers = async ({ basePath, functions, importMap, environment, r
152
151
  }
153
152
  const { matched, resolvedImport } = resolve(specifier, importMap, baseURL);
154
153
  if (matched) {
155
- if ((resolvedImport === null || resolvedImport === void 0 ? void 0 : resolvedImport.protocol) === 'file:') {
154
+ if (resolvedImport?.protocol === 'file:') {
156
155
  const newSpecifier = fileURLToPath(resolvedImport).replace(/\\/g, '/');
157
156
  if (alreadySeenPaths.has(newSpecifier)) {
158
157
  break;
@@ -161,7 +160,7 @@ const getNPMSpecifiers = async ({ basePath, functions, importMap, environment, r
161
160
  npmSpecifiers.push(...(await getNPMSpecifiers({ basePath, functions: [newSpecifier], importMap, environment, rootPath }, alreadySeenPaths)));
162
161
  }
163
162
  }
164
- else if (!((_a = resolvedImport === null || resolvedImport === void 0 ? void 0 : resolvedImport.protocol) === null || _a === void 0 ? void 0 : _a.startsWith('http'))) {
163
+ else if (!resolvedImport?.protocol?.startsWith('http')) {
165
164
  const t = await safelyDetectTypes(specifier, basePath);
166
165
  npmSpecifiers.push({
167
166
  specifier: specifier,
@@ -235,8 +234,7 @@ export const vendorNPMSpecifiers = async ({ basePath, directory, functions, impo
235
234
  });
236
235
  outputFiles.push(...outputFilesFromEsBuild.map((file) => file.path));
237
236
  await Promise.all(outputFilesFromEsBuild.map(async (file) => {
238
- var _a;
239
- const types = (_a = ops.find((op) => path.basename(file.path) === path.basename(op.filePath))) === null || _a === void 0 ? void 0 : _a.types;
237
+ const types = ops.find((op) => path.basename(file.path) === path.basename(op.filePath))?.types;
240
238
  let content = file.text;
241
239
  if (types) {
242
240
  content = `/// <reference types="${path.relative(path.dirname(file.path), types)}" />\n${content}`;
@@ -22,8 +22,7 @@ const cleanDirectory = async (directory, except) => {
22
22
  const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImportMapPath, flags: denoFlags, formatExportTypeError, formatImportError, logger, port, rootPath, stderr, stdout, }) => {
23
23
  const processRef = {};
24
24
  const startServer = async (functions, env = {}, options = {}) => {
25
- var _a;
26
- if ((processRef === null || processRef === void 0 ? void 0 : processRef.ps) !== undefined) {
25
+ if (processRef?.ps !== undefined) {
27
26
  await killProcess(processRef.ps);
28
27
  }
29
28
  let graph = {
@@ -41,7 +40,7 @@ const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImport
41
40
  });
42
41
  const features = {};
43
42
  const importMap = new ImportMap();
44
- await importMap.addFiles((_a = options.importMapPaths) !== null && _a !== void 0 ? _a : [], logger);
43
+ await importMap.addFiles(options.importMapPaths ?? [], logger);
45
44
  // we keep track of the files that are relevant to the user's code, so we can clean up leftovers from past executions later
46
45
  const relevantFiles = [stage2Path];
47
46
  const vendor = await vendorNPMSpecifiers({
@@ -84,7 +83,7 @@ const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImport
84
83
  });
85
84
  let functionsConfig = [];
86
85
  if (options.getFunctionsConfig) {
87
- functionsConfig = await Promise.all(functions.map((func) => getFunctionConfig({ func, importMap, deno, log: logger })));
86
+ functionsConfig = await Promise.all(functions.map((func) => getFunctionConfig({ functionPath: func.path, importMap, deno, log: logger })));
88
87
  }
89
88
  if (distImportMapPath) {
90
89
  await importMap.writeToFile(distImportMapPath);
@@ -52,9 +52,9 @@ test('Starts a server and serves requests for edge functions', async () => {
52
52
  expect(features).toEqual({ npmModules: true });
53
53
  expect(success).toBe(true);
54
54
  expect(functionsConfig).toEqual([{ path: '/my-function' }, {}, { path: '/global-netlify' }]);
55
- const modules = graph === null || graph === void 0 ? void 0 : graph.modules.filter(({ kind, mediaType }) => kind === 'esm' && mediaType === 'TypeScript');
55
+ const modules = graph?.modules.filter(({ kind, mediaType }) => kind === 'esm' && mediaType === 'TypeScript');
56
56
  for (const key in functions) {
57
- const graphEntry = modules === null || modules === void 0 ? void 0 : modules.some(({ local }) => local === functions[key].path);
57
+ const graphEntry = modules?.some(({ local }) => local === functions[key].path);
58
58
  expect(graphEntry).toBe(true);
59
59
  }
60
60
  const response1 = await fetch(`http://0.0.0.0:${port}/foo`, {
@@ -127,7 +127,7 @@ test('Serves edge functions in a monorepo setup', async () => {
127
127
  expect(success).toBe(true);
128
128
  expect(functionsConfig).toEqual([{ path: '/func1' }]);
129
129
  for (const key in functions) {
130
- const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
130
+ const graphEntry = graph?.modules.some(({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
131
131
  expect(graphEntry).toBe(true);
132
132
  }
133
133
  const response1 = await fetch(`http://0.0.0.0:${port}/func1`, {
@@ -10,7 +10,7 @@ const SERVER_POLL_TIMEOUT = 1e4;
10
10
  const isServerReady = async (port, successRef, ps) => {
11
11
  // If the process has been killed or if it exited with an error, we return
12
12
  // early with `success: false`.
13
- if ((ps === null || ps === void 0 ? void 0 : ps.killed) || ((ps === null || ps === void 0 ? void 0 : ps.exitCode) && ps.exitCode > 0)) {
13
+ if (ps?.killed || (ps?.exitCode && ps.exitCode > 0)) {
14
14
  return true;
15
15
  }
16
16
  try {
@@ -24,7 +24,7 @@ const isServerReady = async (port, successRef, ps) => {
24
24
  };
25
25
  const killProcess = (ps) => {
26
26
  // If the process is no longer running, there's nothing left to do.
27
- if ((ps === null || ps === void 0 ? void 0 : ps.exitCode) !== null) {
27
+ if (ps?.exitCode !== null) {
28
28
  return;
29
29
  }
30
30
  return new Promise((resolve, reject) => {
@@ -10,7 +10,15 @@ test('`getLocalEntryPoint` returns a valid stage 2 file for local development',
10
10
  // This is a fake bootstrap that we'll create just for the purpose of logging
11
11
  // the functions and the metadata that are sent to the `boot` function.
12
12
  const printer = `
13
- export const boot = async (functions, metadata) => {
13
+ export const boot = async (functionsLoader) => {
14
+ const functions = await functionsLoader()
15
+ const metadata = { functions: {} }
16
+
17
+ // Generate metadata for each function (simulating what the real bootstrap would have)
18
+ for (const name in functions) {
19
+ metadata.functions[name] = { url: new URL('./' + name + '.mjs', import.meta.url).href }
20
+ }
21
+
14
22
  const responses = {}
15
23
 
16
24
  for (const name in functions) {
@@ -2,7 +2,7 @@ import { promises as fs } from 'fs';
2
2
  import { join } from 'path';
3
3
  const TYPES_URL = 'https://edge.netlify.com';
4
4
  const ensureLatestTypes = async (deno, logger, customTypesURL) => {
5
- const typesURL = customTypesURL !== null && customTypesURL !== void 0 ? customTypesURL : TYPES_URL;
5
+ const typesURL = customTypesURL ?? TYPES_URL;
6
6
  // eslint-disable-next-line prefer-const
7
7
  let [localVersion, remoteVersion] = [await getLocalVersion(deno), ''];
8
8
  try {
@@ -1,4 +1 @@
1
- import { URLPattern } from 'urlpattern-polyfill';
2
- export declare class ExtendedURLPattern extends URLPattern {
3
- regexp: Record<string, RegExp>;
4
- }
1
+ export declare const getRegexpFromURLPatternPath: (path: string) => string;
@@ -1,3 +1,5 @@
1
1
  import { URLPattern } from 'urlpattern-polyfill';
2
- export class ExtendedURLPattern extends URLPattern {
3
- }
2
+ export const getRegexpFromURLPatternPath = (path) => {
3
+ const pattern = new URLPattern({ pathname: path });
4
+ return pattern.regexp.pathname.source;
5
+ };
@@ -1,9 +1,9 @@
1
1
  export default class ManifestValidationError extends Error {
2
+ customErrorInfo = {
3
+ type: 'functionsBundling',
4
+ };
2
5
  constructor(message) {
3
6
  super(`Validation of Edge Functions manifest failed\n${message}`);
4
- this.customErrorInfo = {
5
- type: 'functionsBundling',
6
- };
7
7
  this.name = 'ManifestValidationError';
8
8
  // https://github.com/microsoft/TypeScript-wiki/blob/0fecbda7263f130c57394d779b8ca13f0a2e9123/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
9
9
  Object.setPrototypeOf(this, ManifestValidationError.prototype);
package/dist/test/util.js CHANGED
@@ -67,7 +67,6 @@ for (const functionName in manifest.functions) {
67
67
  console.log(JSON.stringify(responses));
68
68
  `;
69
69
  export const getRouteMatcher = (manifest) => (candidate) => manifest.routes.find((route) => {
70
- var _a, _b;
71
70
  const regex = new RegExp(route.pattern);
72
71
  if (!regex.test(candidate)) {
73
72
  return false;
@@ -75,12 +74,11 @@ export const getRouteMatcher = (manifest) => (candidate) => manifest.routes.find
75
74
  if (route.excluded_patterns.some((pattern) => new RegExp(pattern).test(candidate))) {
76
75
  return false;
77
76
  }
78
- const excludedPatterns = (_b = (_a = manifest.function_config[route.function]) === null || _a === void 0 ? void 0 : _a.excluded_patterns) !== null && _b !== void 0 ? _b : [];
77
+ const excludedPatterns = manifest.function_config[route.function]?.excluded_patterns ?? [];
79
78
  const isExcluded = excludedPatterns.some((pattern) => new RegExp(pattern).test(candidate));
80
79
  return !isExcluded;
81
80
  });
82
81
  export const runESZIP = async (eszipPath, vendorDirectory) => {
83
- var _a, _b, _c;
84
82
  const tmpDir = await tmp.dir({ unsafeCleanup: true });
85
83
  // Extract ESZIP into temporary directory.
86
84
  const extractCommand = execa('deno', [
@@ -92,8 +90,8 @@ export const runESZIP = async (eszipPath, vendorDirectory) => {
92
90
  eszipPath,
93
91
  tmpDir.path,
94
92
  ]);
95
- (_a = extractCommand.stderr) === null || _a === void 0 ? void 0 : _a.pipe(stderr);
96
- (_b = extractCommand.stdout) === null || _b === void 0 ? void 0 : _b.pipe(stdout);
93
+ extractCommand.stderr?.pipe(stderr);
94
+ extractCommand.stdout?.pipe(stdout);
97
95
  await extractCommand;
98
96
  const virtualRootPath = join(tmpDir.path, 'source', 'root');
99
97
  const stage2Path = join(virtualRootPath, '..', 'bootstrap-stage2');
@@ -109,13 +107,12 @@ export const runESZIP = async (eszipPath, vendorDirectory) => {
109
107
  await fs.rename(stage2Path, `${stage2Path}.js`);
110
108
  // Run function that imports the extracted stage 2 and invokes each function.
111
109
  const evalCommand = execa('deno', ['eval', '--import-map', importMapPath, inspectESZIPFunction(stage2Path)]);
112
- (_c = evalCommand.stderr) === null || _c === void 0 ? void 0 : _c.pipe(stderr);
110
+ evalCommand.stderr?.pipe(stderr);
113
111
  const result = await evalCommand;
114
112
  await tmpDir.cleanup();
115
113
  return JSON.parse(result.stdout);
116
114
  };
117
115
  export const runTarball = async (tarballPath) => {
118
- var _a;
119
116
  const tmpDir = await tmp.dir({ unsafeCleanup: true });
120
117
  await tar.extract({
121
118
  cwd: tmpDir.path,
@@ -124,7 +121,7 @@ export const runTarball = async (tarballPath) => {
124
121
  const evalCommand = execa('deno', ['eval', inspectTarballFunction()], {
125
122
  cwd: tmpDir.path,
126
123
  });
127
- (_a = evalCommand.stderr) === null || _a === void 0 ? void 0 : _a.pipe(stderr);
124
+ evalCommand.stderr?.pipe(stderr);
128
125
  const result = await evalCommand;
129
126
  await tmpDir.cleanup();
130
127
  return JSON.parse(result.stdout);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "14.5.5",
3
+ "version": "14.6.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
  "test": "test/node"
43
43
  },
44
44
  "devDependencies": {
45
- "@netlify/edge-functions-bootstrap": "^2.14.0",
45
+ "@netlify/edge-functions-bootstrap": "^2.17.1",
46
46
  "@types/node": "^18.19.111",
47
47
  "@types/semver": "^7.3.9",
48
48
  "@types/uuid": "^10.0.0",
@@ -80,5 +80,5 @@
80
80
  "urlpattern-polyfill": "8.0.2",
81
81
  "uuid": "^11.0.0"
82
82
  },
83
- "gitHead": "f01bed379ebde07ecabca6591701fd1c3ec517d2"
83
+ "gitHead": "3dfa7bd62ac0c8fe4b9b61128b8c2315d1272b4f"
84
84
  }