@netlify/edge-bundler 8.11.0 → 8.12.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.
@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid';
6
6
  import { importMapSpecifier } from '../shared/consts.js';
7
7
  import { DenoBridge } from './bridge.js';
8
8
  import { getFunctionConfig } from './config.js';
9
- import { getDeclarationsFromConfig } from './declaration.js';
9
+ import { mergeDeclarations } from './declaration.js';
10
10
  import { load as loadDeployConfig } from './deploy_config.js';
11
11
  import { getFlags } from './feature_flags.js';
12
12
  import { findFunctions } from './finder.js';
@@ -66,7 +66,7 @@ const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], {
66
66
  const functionsWithConfig = functions.reduce((acc, func, index) => ({ ...acc, [func.name]: functionsConfig[index] }), {});
67
67
  // Creating a final declarations array by combining the TOML file with the
68
68
  // deploy configuration API and the in-source configuration.
69
- const declarationsFromConfig = getDeclarationsFromConfig(tomlDeclarations, functionsWithConfig, deployConfig);
69
+ const declarationsFromConfig = mergeDeclarations(tomlDeclarations, functionsWithConfig, deployConfig.declarations);
70
70
  // If any declarations are autogenerated and are missing the generator field
71
71
  // add a default string.
72
72
  const declarations = internalFunctionsPath
@@ -1,5 +1,4 @@
1
1
  import { FunctionConfig } from './config.js';
2
- import type { DeployConfig } from './deploy_config.js';
3
2
  interface BaseDeclaration {
4
3
  cache?: string;
5
4
  function: string;
@@ -14,7 +13,7 @@ type DeclarationWithPattern = BaseDeclaration & {
14
13
  pattern: string;
15
14
  excludedPattern?: string;
16
15
  };
17
- type Declaration = DeclarationWithPath | DeclarationWithPattern;
18
- export declare const getDeclarationsFromConfig: (tomlDeclarations: Declaration[], functionsConfig: Record<string, FunctionConfig>, deployConfig: DeployConfig) => Declaration[];
16
+ export type Declaration = DeclarationWithPath | DeclarationWithPattern;
17
+ export declare const mergeDeclarations: (tomlDeclarations: Declaration[], functionsConfig: Record<string, FunctionConfig>, deployConfigDeclarations: Declaration[]) => Declaration[];
19
18
  export declare const parsePattern: (pattern: string) => string;
20
- export { Declaration, DeclarationWithPath, DeclarationWithPattern };
19
+ export {};
@@ -1,5 +1,5 @@
1
1
  import regexpAST from 'regexp-tree';
2
- export const getDeclarationsFromConfig = (tomlDeclarations, functionsConfig, deployConfig) => {
2
+ export const mergeDeclarations = (tomlDeclarations, functionsConfig, deployConfigDeclarations) => {
3
3
  var _a;
4
4
  const declarations = [];
5
5
  const functionsVisited = new Set();
@@ -7,23 +7,22 @@ export const getDeclarationsFromConfig = (tomlDeclarations, functionsConfig, dep
7
7
  // the deploy configuration file. For any declaration for which we also have
8
8
  // a function configuration object, we replace the path because that object
9
9
  // takes precedence.
10
- for (const declaration of [...tomlDeclarations, ...deployConfig.declarations]) {
10
+ for (const declaration of [...tomlDeclarations, ...deployConfigDeclarations]) {
11
11
  const config = functionsConfig[declaration.function];
12
- // If no config is found, add the declaration as is
13
12
  if (!config) {
13
+ // If no config is found, add the declaration as is.
14
14
  declarations.push(declaration);
15
- // If we have a path specified as either a string or non-empty array
16
- // create a declaration for each path
17
15
  }
18
16
  else if ((_a = config.path) === null || _a === void 0 ? void 0 : _a.length) {
17
+ // If we have a path specified as either a string or non-empty array,
18
+ // create a declaration for each path.
19
19
  const paths = Array.isArray(config.path) ? config.path : [config.path];
20
20
  paths.forEach((path) => {
21
21
  declarations.push({ ...declaration, cache: config.cache, path });
22
22
  });
23
- // With an in-source config without a path, add the config to the declaration
24
23
  }
25
24
  else {
26
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ // With an in-source config without a path, add the config to the declaration.
27
26
  const { path, excludedPath, ...rest } = config;
28
27
  declarations.push({ ...declaration, ...rest });
29
28
  }
@@ -33,7 +32,7 @@ export const getDeclarationsFromConfig = (tomlDeclarations, functionsConfig, dep
33
32
  // in the TOML at all.
34
33
  for (const name in functionsConfig) {
35
34
  const { cache, path } = functionsConfig[name];
36
- // If we have path specified create a declaration for each path
35
+ // If we have a path specified, create a declaration for each path.
37
36
  if (!functionsVisited.has(name) && path) {
38
37
  const paths = Array.isArray(path) ? path : [path];
39
38
  paths.forEach((singlePath) => {
@@ -1,10 +1,7 @@
1
1
  import { test, expect } from 'vitest';
2
- import { getDeclarationsFromConfig } from './declaration.js';
2
+ import { mergeDeclarations } from './declaration.js';
3
3
  // TODO: Add tests with the deploy config.
4
- const deployConfig = {
5
- declarations: [],
6
- layers: [],
7
- };
4
+ const deployConfigDeclarations = [];
8
5
  test('In-source config takes precedence over netlify.toml config', () => {
9
6
  const tomlConfig = [
10
7
  { function: 'geolocation', path: '/geo', cache: 'off' },
@@ -19,7 +16,7 @@ test('In-source config takes precedence over netlify.toml config', () => {
19
16
  { function: 'geolocation', path: '/*', cache: 'manual' },
20
17
  { function: 'json', path: '/json', cache: 'off' },
21
18
  ];
22
- const declarations = getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig);
19
+ const declarations = mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations);
23
20
  expect(declarations).toEqual(expectedDeclarations);
24
21
  });
25
22
  test("Declarations don't break if no in-source config is provided", () => {
@@ -35,7 +32,7 @@ test("Declarations don't break if no in-source config is provided", () => {
35
32
  { function: 'geolocation', path: '/geo-isc', cache: 'manual' },
36
33
  { function: 'json', path: '/json', cache: 'manual' },
37
34
  ];
38
- const declarations = getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig);
35
+ const declarations = mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations);
39
36
  expect(declarations).toEqual(expectedDeclarations);
40
37
  });
41
38
  test('In-source config works independent of the netlify.toml file if a path is defined and otherwise if no path is set', () => {
@@ -52,9 +49,9 @@ test('In-source config works independent of the netlify.toml file if a path is d
52
49
  { function: 'json', path: '/json-isc', cache: 'off' },
53
50
  ];
54
51
  const expectedDeclarationsWithoutISCPath = [{ function: 'geolocation', path: '/geo', cache: 'off' }];
55
- const declarationsWithISCPath = getDeclarationsFromConfig(tomlConfig, funcConfigWithPath, deployConfig);
52
+ const declarationsWithISCPath = mergeDeclarations(tomlConfig, funcConfigWithPath, deployConfigDeclarations);
56
53
  expect(declarationsWithISCPath).toEqual(expectedDeclarationsWithISCPath);
57
- const declarationsWithoutISCPath = getDeclarationsFromConfig(tomlConfig, funcConfigWithoutPath, deployConfig);
54
+ const declarationsWithoutISCPath = mergeDeclarations(tomlConfig, funcConfigWithoutPath, deployConfigDeclarations);
58
55
  expect(declarationsWithoutISCPath).toEqual(expectedDeclarationsWithoutISCPath);
59
56
  });
60
57
  test('In-source config works if only the cache config property is set', () => {
@@ -63,7 +60,7 @@ test('In-source config works if only the cache config property is set', () => {
63
60
  geolocation: { cache: 'manual' },
64
61
  };
65
62
  const expectedDeclarations = [{ function: 'geolocation', path: '/geo', cache: 'manual' }];
66
- expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations);
63
+ expect(mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations)).toEqual(expectedDeclarations);
67
64
  });
68
65
  test("In-source config path property works if it's not an array", () => {
69
66
  const tomlConfig = [{ function: 'json', path: '/json-toml', cache: 'off' }];
@@ -71,7 +68,7 @@ test("In-source config path property works if it's not an array", () => {
71
68
  json: { path: '/json', cache: 'manual' },
72
69
  };
73
70
  const expectedDeclarations = [{ function: 'json', path: '/json', cache: 'manual' }];
74
- expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations);
71
+ expect(mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations)).toEqual(expectedDeclarations);
75
72
  });
76
73
  test("In-source config path property works if it's not an array and it's not present in toml or deploy config", () => {
77
74
  const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }];
@@ -82,7 +79,7 @@ test("In-source config path property works if it's not an array and it's not pre
82
79
  { function: 'geolocation', path: '/geo', cache: 'off' },
83
80
  { function: 'json', path: '/json-isc', cache: 'manual' },
84
81
  ];
85
- expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations);
82
+ expect(mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations)).toEqual(expectedDeclarations);
86
83
  });
87
84
  test('In-source config works if path property is an empty array with cache value specified', () => {
88
85
  const tomlConfig = [{ function: 'json', path: '/json-toml', cache: 'off' }];
@@ -90,12 +87,12 @@ test('In-source config works if path property is an empty array with cache value
90
87
  json: { path: [], cache: 'manual' },
91
88
  };
92
89
  const expectedDeclarations = [{ function: 'json', path: '/json-toml', cache: 'manual' }];
93
- expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations);
90
+ expect(mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations)).toEqual(expectedDeclarations);
94
91
  });
95
92
  test('netlify.toml-defined excludedPath are respected', () => {
96
93
  const tomlConfig = [{ function: 'geolocation', path: '/geo/*', excludedPath: '/geo/exclude' }];
97
94
  const funcConfig = {};
98
95
  const expectedDeclarations = [{ function: 'geolocation', path: '/geo/*', excludedPath: '/geo/exclude' }];
99
- const declarations = getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig);
96
+ const declarations = mergeDeclarations(tomlConfig, funcConfig, deployConfigDeclarations);
100
97
  expect(declarations).toEqual(expectedDeclarations);
101
98
  });
@@ -1,17 +1,18 @@
1
1
  import { EdgeFunction } from '../edge_function.js';
2
2
  import type { FormatFunction } from '../server/server.js';
3
3
  interface GenerateStage2Options {
4
+ bootstrapURL: string;
4
5
  distDirectory: string;
5
6
  fileName: string;
6
7
  formatExportTypeError?: FormatFunction;
7
8
  formatImportError?: FormatFunction;
8
9
  functions: EdgeFunction[];
9
10
  }
10
- declare const generateStage2: ({ distDirectory, fileName, formatExportTypeError, formatImportError, functions, }: GenerateStage2Options) => Promise<string>;
11
- declare const getBootstrapURL: () => string;
11
+ declare const generateStage2: ({ bootstrapURL, distDirectory, fileName, formatExportTypeError, formatImportError, functions, }: GenerateStage2Options) => Promise<string>;
12
12
  interface GetLocalEntryPointOptions {
13
+ bootstrapURL: string;
13
14
  formatExportTypeError?: FormatFunction;
14
15
  formatImportError?: FormatFunction;
15
16
  }
16
- declare const getLocalEntryPoint: (functions: EdgeFunction[], { formatExportTypeError, formatImportError, }: GetLocalEntryPointOptions) => string;
17
- export { generateStage2, getBootstrapURL, getLocalEntryPoint };
17
+ declare const getLocalEntryPoint: (functions: EdgeFunction[], { bootstrapURL, formatExportTypeError, formatImportError, }: GetLocalEntryPointOptions) => string;
18
+ export { generateStage2, getLocalEntryPoint };
@@ -1,25 +1,22 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import { join } from 'path';
3
- import { env } from 'process';
4
3
  import { pathToFileURL } from 'url';
5
4
  import { deleteAsync } from 'del';
6
- const BOOTSTRAP_LATEST = 'https://64071f3033a1800007cc20f8--edge.netlify.com/bootstrap/index-combined.ts';
7
5
  const defaultFormatExportTypeError = (name) => `The Edge Function "${name}" has failed to load. Does it have a function as the default export?`;
8
6
  const defaultFormatImpoortError = (name) => `There was an error with Edge Function "${name}".`;
9
- const generateStage2 = async ({ distDirectory, fileName, formatExportTypeError, formatImportError, functions, }) => {
7
+ const generateStage2 = async ({ bootstrapURL, distDirectory, fileName, formatExportTypeError, formatImportError, functions, }) => {
10
8
  await deleteAsync(distDirectory, { force: true });
11
9
  await fs.mkdir(distDirectory, { recursive: true });
12
- const entryPoint = getLocalEntryPoint(functions, { formatExportTypeError, formatImportError });
10
+ const entryPoint = getLocalEntryPoint(functions, { bootstrapURL, formatExportTypeError, formatImportError });
13
11
  const stage2Path = join(distDirectory, fileName);
14
12
  await fs.writeFile(stage2Path, entryPoint);
15
13
  return stage2Path;
16
14
  };
17
- const getBootstrapURL = () => { var _a; return (_a = env.NETLIFY_EDGE_BOOTSTRAP) !== null && _a !== void 0 ? _a : BOOTSTRAP_LATEST; };
18
15
  // For the local development environment, we import the user functions with
19
16
  // dynamic imports to gracefully handle the case where the file doesn't have
20
17
  // a valid default export.
21
- const getLocalEntryPoint = (functions, { formatExportTypeError = defaultFormatExportTypeError, formatImportError = defaultFormatImpoortError, }) => {
22
- const bootImport = `import { boot } from "${getBootstrapURL()}";`;
18
+ const getLocalEntryPoint = (functions, { bootstrapURL, formatExportTypeError = defaultFormatExportTypeError, formatImportError = defaultFormatImpoortError, }) => {
19
+ const bootImport = `import { boot } from "${bootstrapURL}";`;
23
20
  const declaration = `const functions = {}; const metadata = { functions: {} };`;
24
21
  const imports = functions.map((func) => {
25
22
  const url = pathToFileURL(func.path);
@@ -45,4 +42,4 @@ const getLocalEntryPoint = (functions, { formatExportTypeError = defaultFormatEx
45
42
  const bootCall = `boot(functions, metadata);`;
46
43
  return [bootImport, declaration, ...imports, bootCall].join('\n\n');
47
44
  };
48
- export { generateStage2, getBootstrapURL, getLocalEntryPoint };
45
+ export { generateStage2, getLocalEntryPoint };
@@ -1,5 +1,8 @@
1
1
  export { bundle } from './bundler.js';
2
2
  export { DenoBridge } from './bridge.js';
3
+ export type { FunctionConfig } from './config.js';
4
+ export { Declaration, mergeDeclarations } from './declaration.js';
5
+ export type { EdgeFunction } from './edge_function.js';
3
6
  export { findFunctions as find } from './finder.js';
4
7
  export { generateManifest } from './manifest.js';
5
8
  export { serve } from './server/server.js';
@@ -1,5 +1,6 @@
1
1
  export { bundle } from './bundler.js';
2
2
  export { DenoBridge } from './bridge.js';
3
+ export { mergeDeclarations } from './declaration.js';
3
4
  export { findFunctions as find } from './finder.js';
4
5
  export { generateManifest } from './manifest.js';
5
6
  export { serve } from './server/server.js';
@@ -17,6 +17,7 @@ interface InspectSettings {
17
17
  address?: string;
18
18
  }
19
19
  interface ServeOptions {
20
+ bootstrapURL: string;
20
21
  certificatePath?: string;
21
22
  debug?: boolean;
22
23
  distImportMapPath?: string;
@@ -29,7 +30,7 @@ interface ServeOptions {
29
30
  port: number;
30
31
  systemLogger?: LogFunction;
31
32
  }
32
- declare const serve: ({ certificatePath, debug, distImportMapPath, inspectSettings, formatExportTypeError, formatImportError, importMapPaths, onAfterDownload, onBeforeDownload, port, systemLogger, }: ServeOptions) => Promise<(functions: EdgeFunction[], env?: NodeJS.ProcessEnv, options?: StartServerOptions) => Promise<{
33
+ declare const serve: ({ bootstrapURL, certificatePath, debug, distImportMapPath, inspectSettings, formatExportTypeError, formatImportError, importMapPaths, onAfterDownload, onBeforeDownload, port, systemLogger, }: ServeOptions) => Promise<(functions: EdgeFunction[], env?: NodeJS.ProcessEnv, options?: StartServerOptions) => Promise<{
33
34
  functionsConfig: FunctionConfig[];
34
35
  graph: any;
35
36
  success: boolean;
@@ -6,7 +6,7 @@ import { ImportMap } from '../import_map.js';
6
6
  import { getLogger } from '../logger.js';
7
7
  import { ensureLatestTypes } from '../types.js';
8
8
  import { killProcess, waitForServer } from './util.js';
9
- const prepareServer = ({ deno, distDirectory, flags: denoFlags, formatExportTypeError, formatImportError, importMap, logger, port, }) => {
9
+ const prepareServer = ({ bootstrapURL, deno, distDirectory, flags: denoFlags, formatExportTypeError, formatImportError, importMap, logger, port, }) => {
10
10
  const processRef = {};
11
11
  const startServer = async (functions, env = {}, options = {}) => {
12
12
  if ((processRef === null || processRef === void 0 ? void 0 : processRef.ps) !== undefined) {
@@ -14,6 +14,7 @@ const prepareServer = ({ deno, distDirectory, flags: denoFlags, formatExportType
14
14
  }
15
15
  let graph;
16
16
  const stage2Path = await generateStage2({
17
+ bootstrapURL,
17
18
  distDirectory,
18
19
  fileName: 'dev.js',
19
20
  functions,
@@ -53,7 +54,7 @@ const prepareServer = ({ deno, distDirectory, flags: denoFlags, formatExportType
53
54
  };
54
55
  return startServer;
55
56
  };
56
- const serve = async ({ certificatePath, debug, distImportMapPath, inspectSettings, formatExportTypeError, formatImportError, importMapPaths = [], onAfterDownload, onBeforeDownload, port, systemLogger, }) => {
57
+ const serve = async ({ bootstrapURL, certificatePath, debug, distImportMapPath, inspectSettings, formatExportTypeError, formatImportError, importMapPaths = [], onAfterDownload, onBeforeDownload, port, systemLogger, }) => {
57
58
  const logger = getLogger(systemLogger, debug);
58
59
  const deno = new DenoBridge({
59
60
  debug,
@@ -89,6 +90,7 @@ const serve = async ({ certificatePath, debug, distImportMapPath, inspectSetting
89
90
  }
90
91
  }
91
92
  const server = prepareServer({
93
+ bootstrapURL,
92
94
  deno,
93
95
  distDirectory,
94
96
  flags,
@@ -14,6 +14,7 @@ test('Starts a server and serves requests for edge functions', async () => {
14
14
  const port = await getPort();
15
15
  const importMapPaths = [join(paths.internal, 'import_map.json'), join(paths.user, 'import-map.json')];
16
16
  const server = await serve({
17
+ bootstrapURL: 'https://edge.netlify.com/bootstrap/index-combined.ts',
17
18
  importMapPaths,
18
19
  port,
19
20
  });
@@ -1,6 +1,5 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import { join } from 'path';
3
- import process from 'process';
4
3
  import { pathToFileURL } from 'url';
5
4
  import { deleteAsync } from 'del';
6
5
  import { execa } from 'execa';
@@ -23,8 +22,8 @@ test('`getLocalEntryPoint` returns a valid stage 2 file for local development',
23
22
  }
24
23
  `;
25
24
  const printerPath = join(tmpDir, 'printer.mjs');
25
+ const bootstrapURL = pathToFileURL(printerPath).toString();
26
26
  await fs.writeFile(printerPath, printer);
27
- process.env.NETLIFY_EDGE_BOOTSTRAP = pathToFileURL(printerPath).toString();
28
27
  const functions = [
29
28
  { name: 'func1', path: join(tmpDir, 'func1.mjs'), response: 'Hello from function 1' },
30
29
  { name: 'func2', path: join(tmpDir, 'func2.mjs'), response: 'Hello from function 2' },
@@ -33,7 +32,7 @@ test('`getLocalEntryPoint` returns a valid stage 2 file for local development',
33
32
  const contents = `export default () => ${JSON.stringify(func.response)}`;
34
33
  await fs.writeFile(func.path, contents);
35
34
  }
36
- const stage2 = getLocalEntryPoint(functions.map(({ name, path }) => ({ name, path })), {});
35
+ const stage2 = getLocalEntryPoint(functions.map(({ name, path }) => ({ name, path })), { bootstrapURL });
37
36
  const stage2Path = join(tmpDir, 'stage2.mjs');
38
37
  await fs.writeFile(stage2Path, stage2);
39
38
  const { stdout, stderr } = await execa('deno', ['run', '--allow-all', stage2Path]);
@@ -44,5 +43,4 @@ test('`getLocalEntryPoint` returns a valid stage 2 file for local development',
44
43
  expect(metadata.functions[func.name].url).toBe(pathToFileURL(func.path).toString());
45
44
  }
46
45
  await deleteAsync(tmpDir, { force: true });
47
- delete process.env.NETLIFY_EDGE_BOOTSTRAP;
48
46
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "8.11.0",
3
+ "version": "8.12.0",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",
@@ -1 +0,0 @@
1
- export {};
@@ -1,26 +0,0 @@
1
- import { Buffer } from 'buffer';
2
- import { env } from 'process';
3
- import fetch, { Headers } from 'node-fetch';
4
- import { test, expect } from 'vitest';
5
- import { getBootstrapURL } from './formats/javascript.js';
6
- test('Imports the bootstrap layer from a valid URL', async () => {
7
- const importURL = new URL(getBootstrapURL());
8
- const headers = new Headers();
9
- // `node-fetch` doesn't let us send credentials as part of the URL, so we
10
- // have to transform them into an `Authorization` header.
11
- if (importURL.username) {
12
- const auth = Buffer.from(`${importURL.username}:${importURL.password}`);
13
- importURL.username = '';
14
- importURL.password = '';
15
- headers.set('Authorization', `Basic ${auth.toString('base64')}`);
16
- }
17
- const canonicalURL = importURL.toString();
18
- const { status } = await fetch(canonicalURL, { headers });
19
- expect(status).toBe(200);
20
- });
21
- test('Imports the bootstrap layer from the URL present in the `NETLIFY_EDGE_BOOTSTRAP` environment variable, if present', () => {
22
- const mockURL = 'https://example.com/boot.ts';
23
- env.NETLIFY_EDGE_BOOTSTRAP = mockURL;
24
- expect(getBootstrapURL()).toBe(mockURL);
25
- env.NETLIFY_EDGE_BOOTSTRAP = undefined;
26
- });