@netlify/edge-bundler 5.3.3 → 5.5.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.
package/deno/bundle.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { writeStage2 } from './lib/stage2.ts'
2
2
 
3
3
  const [payload] = Deno.args
4
- const { basePath, destPath, externals, functions, importMapURL } = JSON.parse(payload)
4
+ const { basePath, destPath, externals, functions, importMapData } = JSON.parse(payload)
5
5
 
6
- await writeStage2({ basePath, destPath, externals, functions, importMapURL })
6
+ await writeStage2({ basePath, destPath, externals, functions, importMapData })
@@ -3,7 +3,7 @@ import { build, LoadResponse } from 'https://deno.land/x/eszip@v0.28.0/mod.ts'
3
3
  import * as path from 'https://deno.land/std@0.127.0/path/mod.ts'
4
4
 
5
5
  import type { InputFunction, WriteStage2Options } from '../../shared/stage2.ts'
6
- import { virtualRoot } from '../../shared/consts.ts'
6
+ import { importMapSpecifier, virtualRoot } from '../../shared/consts.ts'
7
7
  import { PUBLIC_SPECIFIER, STAGE2_SPECIFIER } from './consts.ts'
8
8
  import { inlineModule, loadFromVirtualRoot, loadWithRetry } from './common.ts'
9
9
 
@@ -63,7 +63,7 @@ const getVirtualPath = (basePath: string, filePath: string) => {
63
63
  return url
64
64
  }
65
65
 
66
- const stage2Loader = (basePath: string, functions: InputFunction[], externals: Set<string>) => {
66
+ const stage2Loader = (basePath: string, functions: InputFunction[], externals: Set<string>, importMapData?: string) => {
67
67
  return async (specifier: string): Promise<LoadResponse | undefined> => {
68
68
  if (specifier === STAGE2_SPECIFIER) {
69
69
  const stage2Entry = getStage2Entry(basePath, functions)
@@ -71,6 +71,10 @@ const stage2Loader = (basePath: string, functions: InputFunction[], externals: S
71
71
  return inlineModule(specifier, stage2Entry)
72
72
  }
73
73
 
74
+ if (specifier === importMapSpecifier && importMapData !== undefined) {
75
+ return inlineModule(specifier, importMapData)
76
+ }
77
+
74
78
  if (specifier === PUBLIC_SPECIFIER || externals.has(specifier)) {
75
79
  return {
76
80
  kind: 'external',
@@ -86,8 +90,9 @@ const stage2Loader = (basePath: string, functions: InputFunction[], externals: S
86
90
  }
87
91
  }
88
92
 
89
- const writeStage2 = async ({ basePath, destPath, externals, functions, importMapURL }: WriteStage2Options) => {
90
- const loader = stage2Loader(basePath, functions, new Set(externals))
93
+ const writeStage2 = async ({ basePath, destPath, externals, functions, importMapData }: WriteStage2Options) => {
94
+ const importMapURL = importMapData ? importMapSpecifier : undefined
95
+ const loader = stage2Loader(basePath, functions, new Set(externals), importMapData)
91
96
  const bytes = await build([STAGE2_SPECIFIER], loader, importMapURL)
92
97
  const directory = path.dirname(destPath)
93
98
 
@@ -6,5 +6,4 @@ export interface Bundle {
6
6
  extension: string;
7
7
  format: BundleFormat;
8
8
  hash: string;
9
- importMapURL?: string;
10
9
  }
@@ -2,6 +2,7 @@ import { promises as fs } from 'fs';
2
2
  import { join } from 'path';
3
3
  import commonPathPrefix from 'common-path-prefix';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
+ import { importMapSpecifier } from '../shared/consts.js';
5
6
  import { DenoBridge } from './bridge.js';
6
7
  import { getFunctionConfig } from './config.js';
7
8
  import { getDeclarationsFromConfig } from './declaration.js';
@@ -76,7 +77,7 @@ const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], {
76
77
  declarations,
77
78
  distDirectory,
78
79
  functions,
79
- importMapURL: functionBundle.importMapURL,
80
+ importMap: importMapSpecifier,
80
81
  layers: deployConfig.layers,
81
82
  });
82
83
  if (distImportMapPath) {
@@ -1,10 +1,10 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import { join, resolve } from 'path';
3
3
  import process from 'process';
4
- import { pathToFileURL } from 'url';
5
4
  import { deleteAsync } from 'del';
6
5
  import tmp from 'tmp-promise';
7
6
  import { test, expect } from 'vitest';
7
+ import { importMapSpecifier } from '../shared/consts.js';
8
8
  import { useFixture } from '../test/util.js';
9
9
  import { BundleError } from './bundle_error.js';
10
10
  import { bundle } from './bundler.js';
@@ -25,8 +25,7 @@ test('Produces an ESZIP bundle', async () => {
25
25
  });
26
26
  const generatedFiles = await fs.readdir(distPath);
27
27
  expect(result.functions.length).toBe(1);
28
- // ESZIP, manifest and import map.
29
- expect(generatedFiles.length).toBe(3);
28
+ expect(generatedFiles.length).toBe(2);
30
29
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
31
30
  const manifest = JSON.parse(manifestFile);
32
31
  expect(() => validateManifest(manifest)).not.toThrowError();
@@ -34,7 +33,7 @@ test('Produces an ESZIP bundle', async () => {
34
33
  expect(bundles.length).toBe(1);
35
34
  expect(bundles[0].format).toBe('eszip2');
36
35
  expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
37
- expect(importMapURL).toBe('file:///root/.netlify/edge-functions-dist/import_map.json');
36
+ expect(importMapURL).toBe(importMapSpecifier);
38
37
  await cleanup();
39
38
  });
40
39
  test('Uses the vendored eszip module instead of fetching it from deno.land', async () => {
@@ -52,8 +51,7 @@ test('Uses the vendored eszip module instead of fetching it from deno.land', asy
52
51
  });
53
52
  const generatedFiles = await fs.readdir(distPath);
54
53
  expect(result.functions.length).toBe(1);
55
- // ESZIP, manifest and import map.
56
- expect(generatedFiles.length).toBe(3);
54
+ expect(generatedFiles.length).toBe(2);
57
55
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
58
56
  const manifest = JSON.parse(manifestFile);
59
57
  const { bundles } = manifest;
@@ -140,8 +138,7 @@ test('Uses the cache directory as the `DENO_DIR` value if the `edge_functions_ca
140
138
  const result1 = await bundle([sourceDirectory], distPath, declarations, options);
141
139
  const outFiles1 = await fs.readdir(distPath);
142
140
  expect(result1.functions.length).toBe(1);
143
- // ESZIP, manifest and import map.
144
- expect(outFiles1.length).toBe(3);
141
+ expect(outFiles1.length).toBe(2);
145
142
  try {
146
143
  await fs.readdir(join(cacheDir.path, 'deno_dir'));
147
144
  }
@@ -157,8 +154,7 @@ test('Uses the cache directory as the `DENO_DIR` value if the `edge_functions_ca
157
154
  });
158
155
  const outFiles2 = await fs.readdir(distPath);
159
156
  expect(result2.functions.length).toBe(1);
160
- // ESZIP, manifest and import map.
161
- expect(outFiles2.length).toBe(3);
157
+ expect(outFiles2.length).toBe(2);
162
158
  const denoDir2 = await fs.readdir(join(cacheDir.path, 'deno_dir'));
163
159
  expect(denoDir2.includes('gen')).toBe(true);
164
160
  await cleanup();
@@ -178,8 +174,7 @@ test('Supports import maps with relative paths', async () => {
178
174
  });
179
175
  const generatedFiles = await fs.readdir(distPath);
180
176
  expect(result.functions.length).toBe(1);
181
- // ESZIP, manifest and import map.
182
- expect(generatedFiles.length).toBe(3);
177
+ expect(generatedFiles.length).toBe(2);
183
178
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
184
179
  const manifest = JSON.parse(manifestFile);
185
180
  const { bundles } = manifest;
@@ -247,8 +242,7 @@ test('Processes a function that imports a custom layer', async () => {
247
242
  });
248
243
  const generatedFiles = await fs.readdir(distPath);
249
244
  expect(result.functions.length).toBe(1);
250
- // ESZIP, manifest and import map.
251
- expect(generatedFiles.length).toBe(3);
245
+ expect(generatedFiles.length).toBe(2);
252
246
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
253
247
  const manifest = JSON.parse(manifestFile);
254
248
  const { bundles, layers } = manifest;
@@ -273,8 +267,7 @@ test('Loads declarations and import maps from the deploy configuration', async (
273
267
  });
274
268
  const generatedFiles = await fs.readdir(distPath);
275
269
  expect(result.functions.length).toBe(2);
276
- // ESZIP, manifest and import map.
277
- expect(generatedFiles.length).toBe(3);
270
+ expect(generatedFiles.length).toBe(2);
278
271
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
279
272
  const manifest = JSON.parse(manifestFile);
280
273
  const { bundles } = manifest;
@@ -283,33 +276,3 @@ test('Loads declarations and import maps from the deploy configuration', async (
283
276
  expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
284
277
  await cleanup();
285
278
  });
286
- test('Uses an absolute URL for the import map when the dist directory is not a child of the base path', async () => {
287
- const { basePath, cleanup } = await useFixture('with_import_maps');
288
- const { path: distPath } = await tmp.dir();
289
- const declarations = [
290
- {
291
- function: 'func1',
292
- path: '/func1',
293
- },
294
- ];
295
- const sourceDirectory = join(basePath, 'functions');
296
- const result = await bundle([sourceDirectory], distPath, declarations, {
297
- basePath,
298
- configPath: join(sourceDirectory, 'config.json'),
299
- });
300
- const generatedFiles = await fs.readdir(distPath);
301
- expect(result.functions.length).toBe(1);
302
- // ESZIP, manifest and import map.
303
- expect(generatedFiles.length).toBe(3);
304
- const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
305
- const manifest = JSON.parse(manifestFile);
306
- expect(() => validateManifest(manifest)).not.toThrowError();
307
- const { bundles, import_map: importMapURL } = manifest;
308
- expect(bundles.length).toBe(1);
309
- expect(bundles[0].format).toBe('eszip2');
310
- expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
311
- const importMapPath = join(distPath, 'import_map.json');
312
- expect(importMapURL).toBe(pathToFileURL(importMapPath).toString());
313
- await cleanup();
314
- await fs.rm(distPath, { recursive: true });
315
- });
@@ -131,8 +131,7 @@ test('Ignores function paths from the in-source `config` function if the feature
131
131
  });
132
132
  const generatedFiles = await fs.readdir(distPath);
133
133
  expect(result.functions.length).toBe(6);
134
- // ESZIP, manifest and import map.
135
- expect(generatedFiles.length).toBe(3);
134
+ expect(generatedFiles.length).toBe(2);
136
135
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
137
136
  const manifest = JSON.parse(manifestFile);
138
137
  const { bundles, routes } = manifest;
@@ -165,8 +164,7 @@ test('Loads function paths from the in-source `config` function', async () => {
165
164
  });
166
165
  const generatedFiles = await fs.readdir(distPath);
167
166
  expect(result.functions.length).toBe(6);
168
- // ESZIP, manifest and import map.
169
- expect(generatedFiles.length).toBe(3);
167
+ expect(generatedFiles.length).toBe(2);
170
168
  const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
171
169
  const manifest = JSON.parse(manifestFile);
172
170
  const { bundles, routes, post_cache_routes: postCacheRoutes } = manifest;
@@ -1,5 +1,4 @@
1
- import { join, relative } from 'path';
2
- import { pathToFileURL } from 'url';
1
+ import { join } from 'path';
3
2
  import { virtualRoot } from '../../shared/consts.js';
4
3
  import { BundleFormat } from '../bundle.js';
5
4
  import { wrapBundleError } from '../bundle_error.js';
@@ -10,13 +9,13 @@ const bundleESZIP = async ({ basePath, buildID, debug, deno, distDirectory, exte
10
9
  const extension = '.eszip';
11
10
  const destPath = join(distDirectory, `${buildID}${extension}`);
12
11
  const { bundler, importMap: bundlerImportMap } = getESZIPPaths();
13
- const importMapURL = await createUserImportMap(importMap, basePath, distDirectory);
12
+ const importMapData = JSON.stringify(importMap.getContents(basePath, virtualRoot));
14
13
  const payload = {
15
14
  basePath,
16
15
  destPath,
17
16
  externals,
18
17
  functions,
19
- importMapURL,
18
+ importMapData,
20
19
  };
21
20
  const flags = ['--allow-all', '--no-config', `--import-map=${bundlerImportMap}`];
22
21
  if (!debug) {
@@ -29,24 +28,7 @@ const bundleESZIP = async ({ basePath, buildID, debug, deno, distDirectory, exte
29
28
  throw wrapBundleError(wrapNpmImportError(error), { format: 'eszip' });
30
29
  }
31
30
  const hash = await getFileHash(destPath);
32
- return { extension, format: BundleFormat.ESZIP2, hash, importMapURL };
33
- };
34
- // Takes an import map, writes it to a file on disk, and gets its URL relative
35
- // to the ESZIP root (i.e. using the virtual root prefix).
36
- const createUserImportMap = async (importMap, basePath, distDirectory) => {
37
- const destPath = join(distDirectory, 'import_map.json');
38
- await importMap.writeToFile(destPath);
39
- const virtualPath = relative(basePath, destPath);
40
- // If the dist directory is not a child of the base path, we can't represent
41
- // the relative path as a file URL (because something like 'file://../foo' is
42
- // not valid). This should never happen, but it's best to leave the absolute
43
- // path untransformed to avoid getting a build error due to a missing import
44
- // map.
45
- if (virtualPath.startsWith('..')) {
46
- return pathToFileURL(destPath).toString();
47
- }
48
- const importMapURL = new URL(virtualPath, virtualRoot);
49
- return importMapURL.toString();
31
+ return { extension, format: BundleFormat.ESZIP2, hash };
50
32
  };
51
33
  const getESZIPPaths = () => {
52
34
  const denoPath = join(getPackagePath(), 'deno');
@@ -3,7 +3,7 @@ import { join } from 'path';
3
3
  import { env } from 'process';
4
4
  import { pathToFileURL } from 'url';
5
5
  import { deleteAsync } from 'del';
6
- const BOOTSTRAP_LATEST = 'https://638f0e00dc20510008d6a1f4--edge.netlify.com/bootstrap/index-combined.ts';
6
+ const BOOTSTRAP_LATEST = 'https://639708f6d7f813000870695c--edge.netlify.com/bootstrap/index-combined.ts';
7
7
  const defaultFormatExportTypeError = (name) => `The Edge Function "${name}" has failed to load. Does it have a function as the default export?`;
8
8
  const defaultFormatImpoortError = (name) => `There was an error with Edge Function "${name}".`;
9
9
  const generateStage2 = async ({ distDirectory, fileName, formatExportTypeError, formatImportError, functions, }) => {
@@ -6,12 +6,14 @@ interface ImportMapFile {
6
6
  declare class ImportMap {
7
7
  files: ImportMapFile[];
8
8
  constructor(files?: ImportMapFile[]);
9
- static resolve(importMapFile: ImportMapFile, rootPath?: string): {
9
+ static resolve(importMapFile: ImportMapFile, basePath?: string, prefix?: string): {
10
10
  imports: Record<string, string>;
11
11
  scopes?: import("@import-maps/resolve/types/src/types").ParsedScopesMap | undefined;
12
12
  };
13
13
  add(file: ImportMapFile): void;
14
- getContents(rootPath?: string): string;
14
+ getContents(basePath?: string, prefix?: string): {
15
+ imports: Record<string, string>;
16
+ };
15
17
  toDataURL(): string;
16
18
  writeToFile(path: string): Promise<void>;
17
19
  }
@@ -1,6 +1,6 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import { promises as fs } from 'fs';
3
- import { dirname, isAbsolute, posix, relative, sep } from 'path';
3
+ import { dirname, posix, relative, sep } from 'path';
4
4
  import { fileURLToPath, pathToFileURL } from 'url';
5
5
  import { parse } from '@import-maps/resolve';
6
6
  const INTERNAL_IMPORTS = {
@@ -17,7 +17,7 @@ class ImportMap {
17
17
  }
18
18
  // Transforms an import map by making any relative paths use a different path
19
19
  // as a base.
20
- static resolve(importMapFile, rootPath) {
20
+ static resolve(importMapFile, basePath, prefix = 'file://') {
21
21
  const { baseURL, ...importMap } = importMapFile;
22
22
  const parsedImportMap = parse(importMap, baseURL);
23
23
  const { imports = {} } = parsedImportMap;
@@ -29,13 +29,23 @@ class ImportMap {
29
29
  return;
30
30
  }
31
31
  // If this is a file URL, we might want to transform it to use another
32
- // root path, as long as that root path is defined.
33
- if (url.protocol === 'file:' && rootPath !== undefined) {
32
+ // base path, as long as one is provided.
33
+ if (url.protocol === 'file:' && basePath !== undefined) {
34
+ const path = fileURLToPath(url);
35
+ const relativePath = relative(basePath, path);
36
+ if (relativePath.startsWith('..')) {
37
+ throw new Error(`Import map cannot reference '${path}' as it's outside of the base directory '${basePath}'`);
38
+ }
34
39
  // We want to use POSIX paths for the import map regardless of the OS
35
40
  // we're building in.
36
- const path = relative(rootPath, fileURLToPath(url)).split(sep).join(posix.sep);
37
- const value = isAbsolute(path) ? path : `.${posix.sep}${path}`;
38
- newImports[specifier] = value;
41
+ let normalizedPath = relativePath.split(sep).join(posix.sep);
42
+ // If the original URL had a trailing slash, ensure the normalized path
43
+ // has one too.
44
+ if (normalizedPath !== '' && url.pathname.endsWith(posix.sep) && !normalizedPath.endsWith(posix.sep)) {
45
+ normalizedPath += posix.sep;
46
+ }
47
+ const newURL = new URL(normalizedPath, prefix);
48
+ newImports[specifier] = newURL.toString();
39
49
  return;
40
50
  }
41
51
  newImports[specifier] = url.toString();
@@ -45,10 +55,10 @@ class ImportMap {
45
55
  add(file) {
46
56
  this.files.push(file);
47
57
  }
48
- getContents(rootPath) {
58
+ getContents(basePath, prefix) {
49
59
  let imports = {};
50
60
  this.files.forEach((file) => {
51
- const importMap = ImportMap.resolve(file, rootPath);
61
+ const importMap = ImportMap.resolve(file, basePath, prefix);
52
62
  imports = { ...imports, ...importMap.imports };
53
63
  });
54
64
  // Internal imports must come last, because we need to guarantee that
@@ -57,20 +67,20 @@ class ImportMap {
57
67
  const [specifier, url] = internalImport;
58
68
  imports[specifier] = url;
59
69
  });
60
- const contents = {
70
+ return {
61
71
  imports,
62
72
  };
63
- return JSON.stringify(contents);
64
73
  }
65
74
  toDataURL() {
66
- const encodedImportMap = Buffer.from(this.getContents()).toString('base64');
75
+ const data = JSON.stringify(this.getContents());
76
+ const encodedImportMap = Buffer.from(data).toString('base64');
67
77
  return `data:application/json;base64,${encodedImportMap}`;
68
78
  }
69
79
  async writeToFile(path) {
70
80
  const distDirectory = dirname(path);
71
81
  await fs.mkdir(distDirectory, { recursive: true });
72
- const contents = this.getContents(distDirectory);
73
- await fs.writeFile(path, contents);
82
+ const contents = this.getContents();
83
+ await fs.writeFile(path, JSON.stringify(contents));
74
84
  }
75
85
  }
76
86
  const readFile = async (path) => {
@@ -1,6 +1,8 @@
1
+ import { promises as fs } from 'fs';
1
2
  import { join } from 'path';
2
3
  import { cwd } from 'process';
3
4
  import { pathToFileURL } from 'url';
5
+ import tmp from 'tmp-promise';
4
6
  import { test, expect } from 'vitest';
5
7
  import { ImportMap } from './import_map.js';
6
8
  test('Handles import maps with full URLs without specifying a base URL', () => {
@@ -18,12 +20,12 @@ test('Handles import maps with full URLs without specifying a base URL', () => {
18
20
  },
19
21
  };
20
22
  const map = new ImportMap([inputFile1, inputFile2]);
21
- const { imports } = JSON.parse(map.getContents());
23
+ const { imports } = map.getContents();
22
24
  expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts');
23
25
  expect(imports['alias:jamstack']).toBe('https://jamstack.org/');
24
26
  expect(imports['alias:pets']).toBe('https://petsofnetlify.com/');
25
27
  });
26
- test('Resolves relative paths to absolute paths if a root path is not provided', () => {
28
+ test('Resolves relative paths to absolute paths if a base path is not provided', () => {
27
29
  const basePath = join(cwd(), 'my-cool-site', 'import-map.json');
28
30
  const inputFile1 = {
29
31
  baseURL: pathToFileURL(basePath),
@@ -32,12 +34,12 @@ test('Resolves relative paths to absolute paths if a root path is not provided',
32
34
  },
33
35
  };
34
36
  const map = new ImportMap([inputFile1]);
35
- const { imports } = JSON.parse(map.getContents());
37
+ const { imports } = map.getContents();
36
38
  const expectedPath = join(cwd(), 'my-cool-site', 'heart', 'pets');
37
39
  expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts');
38
40
  expect(imports['alias:pets']).toBe(`${pathToFileURL(expectedPath).toString()}/`);
39
41
  });
40
- test('Transforms relative paths so that they use the root path as a base', () => {
42
+ test('Transforms relative paths so that they become relative to the base path', () => {
41
43
  const basePath = join(cwd(), 'my-cool-site', 'import-map.json');
42
44
  const inputFile1 = {
43
45
  baseURL: pathToFileURL(basePath),
@@ -45,8 +47,43 @@ test('Transforms relative paths so that they use the root path as a base', () =>
45
47
  'alias:pets': './heart/pets/',
46
48
  },
47
49
  };
50
+ // Without a prefix.
51
+ const map1 = new ImportMap([inputFile1]);
52
+ const { imports: imports1 } = map1.getContents(cwd());
53
+ expect(imports1['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts');
54
+ expect(imports1['alias:pets']).toBe('file:///my-cool-site/heart/pets/');
55
+ // With a prefix.
56
+ const map2 = new ImportMap([inputFile1]);
57
+ const { imports: imports2 } = map2.getContents(cwd(), 'file:///root/');
58
+ expect(imports2['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts');
59
+ expect(imports2['alias:pets']).toBe('file:///root/my-cool-site/heart/pets/');
60
+ });
61
+ test('Throws when an import map uses a relative path to reference a file outside of the base path', () => {
62
+ const basePath = join(cwd(), 'my-cool-site');
63
+ const inputFile1 = {
64
+ baseURL: pathToFileURL(join(basePath, 'import_map.json')),
65
+ imports: {
66
+ 'alias:file': '../file.js',
67
+ },
68
+ };
69
+ const map = new ImportMap([inputFile1]);
70
+ expect(() => map.getContents(basePath)).toThrowError(`Import map cannot reference '${join(cwd(), 'file.js')}' as it's outside of the base directory '${basePath}'`);
71
+ });
72
+ test('Writes import map file to disk', async () => {
73
+ const file = await tmp.file();
74
+ const basePath = join(cwd(), 'my-cool-site', 'import-map.json');
75
+ const inputFile1 = {
76
+ baseURL: pathToFileURL(basePath),
77
+ imports: {
78
+ 'alias:pets': './heart/pets/file.ts',
79
+ },
80
+ };
48
81
  const map = new ImportMap([inputFile1]);
49
- const { imports } = JSON.parse(map.getContents(cwd()));
82
+ await map.writeToFile(file.path);
83
+ const createdFile = await fs.readFile(file.path, 'utf8');
84
+ const { imports } = JSON.parse(createdFile);
85
+ const expectedPath = join(cwd(), 'my-cool-site', 'heart', 'pets', 'file.ts');
86
+ await file.cleanup();
50
87
  expect(imports['netlify:edge']).toBe('https://edge.netlify.com/v1/index.ts');
51
- expect(imports['alias:pets']).toBe('./my-cool-site/heart/pets');
88
+ expect(imports['alias:pets']).toBe(pathToFileURL(expectedPath).toString());
52
89
  });
@@ -6,7 +6,7 @@ interface GenerateManifestOptions {
6
6
  bundles?: Bundle[];
7
7
  declarations?: Declaration[];
8
8
  functions: EdgeFunction[];
9
- importMapURL?: string;
9
+ importMap?: string;
10
10
  layers?: Layer[];
11
11
  }
12
12
  interface Manifest {
@@ -31,14 +31,14 @@ interface Manifest {
31
31
  pattern: string;
32
32
  }[];
33
33
  }
34
- declare const generateManifest: ({ bundles, declarations, functions, importMapURL, layers, }: GenerateManifestOptions) => Manifest;
34
+ declare const generateManifest: ({ bundles, declarations, functions, importMap, layers, }: GenerateManifestOptions) => Manifest;
35
35
  interface WriteManifestOptions {
36
36
  bundles: Bundle[];
37
37
  declarations: Declaration[];
38
38
  distDirectory: string;
39
39
  functions: EdgeFunction[];
40
- importMapURL?: string;
40
+ importMap?: string;
41
41
  layers?: Layer[];
42
42
  }
43
- declare const writeManifest: ({ bundles, declarations, distDirectory, functions, importMapURL, layers, }: WriteManifestOptions) => Promise<Manifest>;
43
+ declare const writeManifest: ({ bundles, declarations, distDirectory, functions, importMap, layers, }: WriteManifestOptions) => Promise<Manifest>;
44
44
  export { generateManifest, Manifest, writeManifest };
@@ -3,7 +3,7 @@ import { join } from 'path';
3
3
  import globToRegExp from 'glob-to-regexp';
4
4
  import { getPackageVersion } from './package_json.js';
5
5
  import { nonNullable } from './utils/non_nullable.js';
6
- const generateManifest = ({ bundles = [], declarations = [], functions, importMapURL, layers = [], }) => {
6
+ const generateManifest = ({ bundles = [], declarations = [], functions, importMap, layers = [], }) => {
7
7
  const preCacheRoutes = [];
8
8
  const postCacheRoutes = [];
9
9
  declarations.forEach((declaration) => {
@@ -35,7 +35,7 @@ const generateManifest = ({ bundles = [], declarations = [], functions, importMa
35
35
  post_cache_routes: postCacheRoutes.filter(nonNullable),
36
36
  bundler_version: getPackageVersion(),
37
37
  layers,
38
- import_map: importMapURL,
38
+ import_map: importMap,
39
39
  };
40
40
  return manifest;
41
41
  };
@@ -52,8 +52,8 @@ const getRegularExpression = (declaration) => {
52
52
  const normalizedSource = `^${regularExpression.source}\\/?$`;
53
53
  return new RegExp(normalizedSource);
54
54
  };
55
- const writeManifest = async ({ bundles, declarations = [], distDirectory, functions, importMapURL, layers, }) => {
56
- const manifest = generateManifest({ bundles, declarations, functions, importMapURL, layers });
55
+ const writeManifest = async ({ bundles, declarations = [], distDirectory, functions, importMap, layers, }) => {
56
+ const manifest = generateManifest({ bundles, declarations, functions, importMap, layers });
57
57
  const manifestPath = join(distDirectory, 'manifest.json');
58
58
  await fs.writeFile(manifestPath, JSON.stringify(manifest));
59
59
  return manifest;
@@ -70,7 +70,7 @@ const serve = async ({ certificatePath, debug, distImportMapPath, inspectSetting
70
70
  await ensureLatestTypes(deno, logger);
71
71
  // Creating an ImportMap instance with any import maps supplied by the user,
72
72
  // if any.
73
- const importMap = new ImportMap(importMaps !== null && importMaps !== void 0 ? importMaps : []);
73
+ const importMap = new ImportMap(importMaps);
74
74
  const flags = ['--allow-all', '--unstable', `--import-map=${importMap.toDataURL()}`, '--no-config'];
75
75
  if (certificatePath) {
76
76
  flags.push(`--cert=${certificatePath}`);
@@ -1 +1,2 @@
1
+ export declare const importMapSpecifier = "netlify:import-map";
1
2
  export declare const virtualRoot = "file:///root/";
@@ -1 +1,2 @@
1
+ export const importMapSpecifier = 'netlify:import-map';
1
2
  export const virtualRoot = 'file:///root/';
@@ -7,5 +7,5 @@ export interface WriteStage2Options {
7
7
  destPath: string;
8
8
  externals: string[];
9
9
  functions: InputFunction[];
10
- importMapURL?: string;
10
+ importMapData?: string;
11
11
  }
package/dist/test/util.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import { join, resolve } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import cpy from 'cpy';
5
4
  import tmp from 'tmp-promise';
6
5
  import { getLogger } from '../node/logger.js';
7
- // eslint-disable-next-line @typescript-eslint/no-empty-function
8
- const testLogger = getLogger(() => { });
6
+ const testLogger = getLogger(() => {
7
+ // no-op
8
+ });
9
9
  const url = new URL(import.meta.url);
10
10
  const dirname = fileURLToPath(url);
11
11
  const fixturesDir = resolve(dirname, '..', 'fixtures');
@@ -13,10 +13,9 @@ const useFixture = async (fixtureName) => {
13
13
  const tmpDir = await tmp.dir();
14
14
  const cleanup = () => fs.rmdir(tmpDir.path, { recursive: true });
15
15
  const fixtureDir = resolve(fixturesDir, fixtureName);
16
- await cpy(`${fixtureDir}/**`, tmpDir.path);
17
16
  const distPath = join(tmpDir.path, '.netlify', 'edge-functions-dist');
18
17
  return {
19
- basePath: tmpDir.path,
18
+ basePath: fixtureDir,
20
19
  cleanup,
21
20
  distPath,
22
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "5.3.3",
3
+ "version": "5.5.0",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/node/index.js",
@@ -66,7 +66,7 @@
66
66
  "nock": "^13.2.4",
67
67
  "tar": "^6.1.11",
68
68
  "typescript": "^4.5.4",
69
- "vite": "^3.1.3",
69
+ "vite": "^4.0.0",
70
70
  "vitest": "^0.25.0"
71
71
  },
72
72
  "engines": {
package/shared/consts.ts CHANGED
@@ -1 +1,2 @@
1
+ export const importMapSpecifier = 'netlify:import-map'
1
2
  export const virtualRoot = 'file:///root/'
package/shared/stage2.ts CHANGED
@@ -8,5 +8,5 @@ export interface WriteStage2Options {
8
8
  destPath: string
9
9
  externals: string[]
10
10
  functions: InputFunction[]
11
- importMapURL?: string
11
+ importMapData?: string
12
12
  }