@netlify/edge-bundler 1.14.0 → 2.0.1

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, functions, imports } = JSON.parse(payload)
4
+ const { basePath, destPath, functions, importMapURL } = JSON.parse(payload)
5
5
 
6
- await writeStage2({ basePath, destPath, functions, imports })
6
+ await writeStage2({ basePath, destPath, functions, importMapURL })
@@ -1,22 +1,11 @@
1
- import { build, LoadResponse } from 'https://deno.land/x/eszip@v0.18.0/mod.ts'
1
+ import { build, LoadResponse } from 'https://deno.land/x/eszip@v0.28.0/mod.ts'
2
2
 
3
3
  import * as path from 'https://deno.land/std@0.127.0/path/mod.ts'
4
4
 
5
+ import type { InputFunction, WriteStage2Options } from '../../src/stage2.ts'
5
6
  import { PUBLIC_SPECIFIER, STAGE2_SPECIFIER, virtualRoot } from './consts.ts'
6
7
  import { inlineModule, loadFromVirtualRoot, loadWithRetry } from './common.ts'
7
8
 
8
- interface InputFunction {
9
- name: string
10
- path: string
11
- }
12
-
13
- interface WriteStage2Options {
14
- basePath: string
15
- destPath: string
16
- functions: InputFunction[]
17
- imports?: Record<string, string>
18
- }
19
-
20
9
  const getFunctionReference = (basePath: string, func: InputFunction, index: number) => {
21
10
  const importName = `func${index}`
22
11
  const exportLine = `"${func.name}": ${importName}`
@@ -44,7 +33,7 @@ const getVirtualPath = (basePath: string, filePath: string) => {
44
33
  return url
45
34
  }
46
35
 
47
- const stage2Loader = (basePath: string, functions: InputFunction[], imports: Record<string, string> = {}) => {
36
+ const stage2Loader = (basePath: string, functions: InputFunction[]) => {
48
37
  return async (specifier: string): Promise<LoadResponse | undefined> => {
49
38
  if (specifier === STAGE2_SPECIFIER) {
50
39
  const stage2Entry = getStage2Entry(basePath, functions)
@@ -59,10 +48,6 @@ const stage2Loader = (basePath: string, functions: InputFunction[], imports: Rec
59
48
  }
60
49
  }
61
50
 
62
- if (imports[specifier] !== undefined) {
63
- return await loadWithRetry(imports[specifier])
64
- }
65
-
66
51
  if (specifier.startsWith(virtualRoot)) {
67
52
  return loadFromVirtualRoot(specifier, virtualRoot, basePath)
68
53
  }
@@ -71,9 +56,9 @@ const stage2Loader = (basePath: string, functions: InputFunction[], imports: Rec
71
56
  }
72
57
  }
73
58
 
74
- const writeStage2 = async ({ basePath, destPath, functions, imports }: WriteStage2Options) => {
75
- const loader = stage2Loader(basePath, functions, imports)
76
- const bytes = await build([STAGE2_SPECIFIER], loader)
59
+ const writeStage2 = async ({ basePath, destPath, functions, importMapURL }: WriteStage2Options) => {
60
+ const loader = stage2Loader(basePath, functions)
61
+ const bytes = await build([STAGE2_SPECIFIER], loader, importMapURL)
77
62
 
78
63
  return await Deno.writeFile(destPath, bytes)
79
64
  }
package/dist/bundler.d.ts CHANGED
@@ -15,7 +15,7 @@ interface BundleOptions {
15
15
  onBeforeDownload?: OnBeforeDownloadHook;
16
16
  systemLogger?: LogFunction;
17
17
  }
18
- declare const bundle: (sourceDirectories: string[], distDirectory: string, declarations?: Declaration[], { basePath: inputBasePath, cacheDirectory, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMaps, onAfterDownload, onBeforeDownload, systemLogger, }?: BundleOptions) => Promise<{
18
+ declare const bundle: (sourceDirectories: string[], distDirectory: string, declarations?: Declaration[], { basePath, cacheDirectory, debug, distImportMapPath, featureFlags, importMaps, onAfterDownload, onBeforeDownload, systemLogger, }?: BundleOptions) => Promise<{
19
19
  functions: EdgeFunction[];
20
20
  manifest: import("./manifest.js").Manifest;
21
21
  }>;
@@ -1,3 +1,3 @@
1
1
  import { Logger } from './logger.js';
2
- declare const download: (targetDirectory: string, versionRange: string, logger: Logger) => Promise<string>;
3
- export { download };
2
+ declare const downloadWithRetry: (targetDirectory: string, versionRange: string, logger: Logger) => Promise<string>;
3
+ export { downloadWithRetry as download };
@@ -1,51 +1,59 @@
1
- import fs from 'fs';
1
+ import { createWriteStream, promises as fs } from 'fs';
2
2
  import path from 'path';
3
+ import { promisify } from 'util';
3
4
  import fetch from 'node-fetch';
4
5
  import StreamZip from 'node-stream-zip';
5
6
  import pRetry from 'p-retry';
6
7
  import semver from 'semver';
7
8
  import { getBinaryExtension, getPlatformTarget } from './platform.js';
8
- const download = async (targetDirectory, versionRange, logger) => {
9
+ const downloadWithRetry = async (targetDirectory, versionRange, logger) => await pRetry(async () => await download(targetDirectory, versionRange), {
10
+ retries: 3,
11
+ onFailedAttempt: (error) => {
12
+ logger.system('Deno download with retry failed', error);
13
+ },
14
+ });
15
+ const download = async (targetDirectory, versionRange) => {
9
16
  const zipPath = path.join(targetDirectory, 'deno-cli-latest.zip');
10
- const data = await downloadVersionWithRetry(versionRange, logger);
17
+ const data = await downloadVersion(versionRange);
11
18
  const binaryName = `deno${getBinaryExtension()}`;
12
19
  const binaryPath = path.join(targetDirectory, binaryName);
13
- const file = fs.createWriteStream(zipPath);
14
- await new Promise((resolve, reject) => {
15
- data.pipe(file);
16
- data.on('error', reject);
17
- file.on('finish', resolve);
18
- });
19
- await extractBinaryFromZip(zipPath, binaryPath, binaryName);
20
+ const file = createWriteStream(zipPath);
20
21
  try {
21
- await fs.promises.unlink(zipPath);
22
+ await new Promise((resolve, reject) => {
23
+ data.pipe(file);
24
+ data.on('error', reject);
25
+ file.on('finish', resolve);
26
+ });
27
+ await extractBinaryFromZip(zipPath, binaryPath, binaryName);
28
+ return binaryPath;
22
29
  }
23
- catch {
24
- // no-op
30
+ finally {
31
+ // Try closing and deleting the zip file in any case, error or not
32
+ await promisify(file.close.bind(file))();
33
+ try {
34
+ await fs.unlink(zipPath);
35
+ }
36
+ catch {
37
+ // no-op
38
+ }
25
39
  }
26
- return binaryPath;
27
40
  };
28
41
  const downloadVersion = async (versionRange) => {
29
42
  const version = await getLatestVersionForRange(versionRange);
30
43
  const url = getReleaseURL(version);
31
44
  const res = await fetch(url);
32
- if (res.body === null) {
33
- throw new Error('Could not download Deno');
45
+ // eslint-disable-next-line no-magic-numbers
46
+ if (res.body === null || res.status < 200 || res.status > 299) {
47
+ throw new Error(`Download failed with status code ${res.status}`);
34
48
  }
35
49
  return res.body;
36
50
  };
37
- const downloadVersionWithRetry = async (versionRange, logger) => await pRetry(async () => await downloadVersion(versionRange), {
38
- retries: 3,
39
- onFailedAttempt: (error) => {
40
- logger.system('Deno CLI download retry attempt error', error);
41
- },
42
- });
43
51
  const extractBinaryFromZip = async (zipPath, binaryPath, binaryName) => {
44
52
  const { async: StreamZipAsync } = StreamZip;
45
53
  const zip = new StreamZipAsync({ file: zipPath });
46
54
  await zip.extract(binaryName, binaryPath);
47
55
  await zip.close();
48
- await fs.promises.chmod(binaryPath, '755');
56
+ await fs.chmod(binaryPath, '755');
49
57
  };
50
58
  const getLatestVersion = async () => {
51
59
  try {
@@ -82,4 +90,4 @@ const getReleaseURL = (version) => {
82
90
  const target = getPlatformTarget();
83
91
  return `https://dl.deno.land/release/v${version}/deno-${target}.zip`;
84
92
  };
85
- export { download };
93
+ export { downloadWithRetry as download };
@@ -10,7 +10,7 @@ const bundleESZIP = async ({ basePath, buildID, debug, deno, distDirectory, func
10
10
  basePath,
11
11
  destPath,
12
12
  functions,
13
- imports: importMap.imports,
13
+ importMapURL: importMap.toDataURL(),
14
14
  };
15
15
  const flags = ['--allow-all'];
16
16
  if (!debug) {
@@ -1,10 +1,12 @@
1
1
  interface ImportMapFile {
2
+ baseURL: URL;
2
3
  imports: Record<string, string>;
3
- scopes?: Record<string, string>;
4
+ scopes?: Record<string, Record<string, string>>;
4
5
  }
5
6
  declare class ImportMap {
6
- imports: Record<string, string>;
7
- constructor(input?: ImportMapFile[]);
7
+ imports: Record<string, URL | null>;
8
+ constructor(files?: ImportMapFile[]);
9
+ static resolve(importMapFile: ImportMapFile): import("@import-maps/resolve/types/src/types").ParsedImportMap;
8
10
  getContents(): string;
9
11
  toDataURL(): string;
10
12
  writeToFile(path: string): Promise<void>;
@@ -1,15 +1,29 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import { promises as fs } from 'fs';
3
3
  import { dirname } from 'path';
4
+ import { parse } from '@import-maps/resolve';
4
5
  const INTERNAL_IMPORTS = {
5
- 'netlify:edge': 'https://edge.netlify.com/v1/index.ts',
6
+ 'netlify:edge': new URL('https://edge.netlify.com/v1/index.ts'),
6
7
  };
7
8
  class ImportMap {
8
- constructor(input = []) {
9
- const inputImports = input.reduce((acc, { imports }) => ({ ...acc, ...imports }), {});
10
- // `INTERNAL_IMPORTS` must come last,
11
- // because we need to guarantee `netlify:edge` isn't user-defined.
12
- this.imports = { ...inputImports, ...INTERNAL_IMPORTS };
9
+ constructor(files = []) {
10
+ let imports = {};
11
+ files.forEach((file) => {
12
+ const importMap = ImportMap.resolve(file);
13
+ imports = { ...imports, ...importMap.imports };
14
+ });
15
+ // Internal imports must come last, because we need to guarantee that
16
+ // `netlify:edge` isn't user-defined.
17
+ Object.entries(INTERNAL_IMPORTS).forEach((internalImport) => {
18
+ const [specifier, url] = internalImport;
19
+ imports[specifier] = url;
20
+ });
21
+ this.imports = imports;
22
+ }
23
+ static resolve(importMapFile) {
24
+ const { baseURL, ...importMap } = importMapFile;
25
+ const parsedImportMap = parse(importMap, baseURL);
26
+ return parsedImportMap;
13
27
  }
14
28
  getContents() {
15
29
  const contents = {
@@ -0,0 +1,10 @@
1
+ export interface InputFunction {
2
+ name: string;
3
+ path: string;
4
+ }
5
+ export interface WriteStage2Options {
6
+ basePath: string;
7
+ destPath: string;
8
+ functions: InputFunction[];
9
+ importMapURL?: string;
10
+ }
package/dist/stage2.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/edge-bundler",
3
- "version": "1.14.0",
3
+ "version": "2.0.1",
4
4
  "description": "Intelligently prepare Netlify Edge Functions for deployment",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -79,6 +79,7 @@
79
79
  "node": "^12.20.0 || ^14.14.0 || >=16.0.0"
80
80
  },
81
81
  "dependencies": {
82
+ "@import-maps/resolve": "^1.0.1",
82
83
  "common-path-prefix": "^3.0.0",
83
84
  "del": "^6.0.0",
84
85
  "env-paths": "^3.0.0",