@netlify/edge-bundler 10.0.0 → 10.1.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/README.md +29 -19
- package/deno/vendor/deno.land/x/deno_graph@0.59.2/media_type.ts +20 -0
- package/deno/vendor/deno.land/x/deno_graph@0.59.2/types.d.ts +182 -0
- package/dist/deno/vendor/deno.land/x/deno_graph@0.59.2/media_type.d.ts +18 -0
- package/dist/deno/vendor/deno.land/x/deno_graph@0.59.2/media_type.js +20 -0
- package/dist/node/bundler.js +1 -1
- package/dist/node/bundler.test.js +25 -0
- package/dist/node/index.d.ts +1 -1
- package/dist/node/npm_dependencies.d.ts +2 -2
- package/dist/node/npm_dependencies.js +14 -5
- package/dist/node/server/server.d.ts +8 -1
- package/dist/node/server/server.js +6 -2
- package/dist/node/server/server.test.js +2 -6
- package/package.json +3 -2
- package/deno/lib/stage2.test.ts +0 -58
- package/dist/node/bootstrap.test.d.ts +0 -1
- package/dist/node/bootstrap.test.js +0 -26
- package/dist/node/deno_config.d.ts +0 -5
- package/dist/node/deno_config.js +0 -40
- package/dist/node/deno_config.test.d.ts +0 -1
- package/dist/node/deno_config.test.js +0 -37
- package/dist/node/serving.test.d.ts +0 -1
- package/dist/node/serving.test.js +0 -31
package/README.md
CHANGED
|
@@ -9,32 +9,42 @@ Intelligently prepare Netlify Edge Functions for deployment.
|
|
|
9
9
|
|
|
10
10
|
1. Install this module as a dependency in your project
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
```
|
|
13
|
+
npm install @netlify/edge-bundler --save
|
|
14
|
+
```
|
|
15
15
|
|
|
16
16
|
2. Import it and create a bundle from a directory of Edge Functions and a list of declarations.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
```js
|
|
19
|
+
import { bundle } from '@netlify/edge-bundler'
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"/repo/netlify/edge-functions",
|
|
24
|
-
"/repo/.netlify/edge-functions"
|
|
25
|
-
]
|
|
21
|
+
// List of directories to search for Edge Functions.
|
|
22
|
+
const sourceDirectories = ['/repo/netlify/edge-functions', '/repo/.netlify/edge-functions']
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
// Directory where bundle should be placed.
|
|
25
|
+
const distDirectory = '/repo/.netlify/edge-functions-dist'
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
// List of Edge Functions declarations.
|
|
28
|
+
const declarations = [
|
|
29
|
+
{ function: 'user-1', path: '/blog/*' },
|
|
30
|
+
{ function: 'internal-2', path: '/' },
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
await bundle(sourceDirectories, distDirectory, declarations)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Vendored modules
|
|
37
|
+
|
|
38
|
+
To avoid pulling in additional dependencies at runtime, this package vendors some Deno modules in the `deno/vendor`
|
|
39
|
+
directory.
|
|
40
|
+
|
|
41
|
+
You can recreate this directory by running `npm run vendor`.
|
|
42
|
+
|
|
43
|
+
> [!WARNING]
|
|
44
|
+
> At the time of writing, the underlying Deno CLI command doesn't correctly pull the WASM binary required by the ESZIP
|
|
45
|
+
> module. If you run the command to update the list of vendores modules, please ensure you're not deleting
|
|
46
|
+
> `eszip_wasm_bg.wasm`.
|
|
35
47
|
|
|
36
|
-
await bundle(sourceDirectories, distDirectory, declarations)
|
|
37
|
-
```
|
|
38
48
|
## Contributors
|
|
39
49
|
|
|
40
50
|
Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to set up and work on this repository. Thanks
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
2
|
+
|
|
3
|
+
export enum MediaType {
|
|
4
|
+
JavaScript = "JavaScript",
|
|
5
|
+
Mjs = "Mjs",
|
|
6
|
+
Cjs = "Cjs",
|
|
7
|
+
Jsx = "Jsx",
|
|
8
|
+
TypeScript = "TypeScript",
|
|
9
|
+
Mts = "Mts",
|
|
10
|
+
Cts = "Cts",
|
|
11
|
+
Dts = "Dts",
|
|
12
|
+
Dmts = "Dmts",
|
|
13
|
+
Dcts = "Dcts",
|
|
14
|
+
Tsx = "Tsx",
|
|
15
|
+
Json = "Json",
|
|
16
|
+
Wasm = "Wasm",
|
|
17
|
+
TsBuildInfo = "TsBuildInfo",
|
|
18
|
+
SourceMap = "SourceMap",
|
|
19
|
+
Unknown = "Unknown",
|
|
20
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
2
|
+
|
|
3
|
+
import type { MediaType } from "./media_type.ts";
|
|
4
|
+
|
|
5
|
+
/** Additional meta data that is used to enrich the output of the module
|
|
6
|
+
* graph. */
|
|
7
|
+
export interface CacheInfo {
|
|
8
|
+
/** The string path to where the local version of the content is located. For
|
|
9
|
+
* non `file:` URLs, this is the location of the cached content, otherwise it
|
|
10
|
+
* is the absolute path to the local file. */
|
|
11
|
+
local?: string;
|
|
12
|
+
/** The string path to where a transpiled version of the source content is
|
|
13
|
+
* located, if any. */
|
|
14
|
+
emit?: string;
|
|
15
|
+
/** The string path to where an external source map of the transpiled source
|
|
16
|
+
* content is located, if any. */
|
|
17
|
+
map?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TypesDependency {
|
|
21
|
+
/** The string URL to the type information for the module. */
|
|
22
|
+
types: string;
|
|
23
|
+
/** An optional range which indicates the source of the dependency. */
|
|
24
|
+
source?: Range;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface LoadResponseModule {
|
|
28
|
+
/** A module with code has been loaded. */
|
|
29
|
+
kind: "module";
|
|
30
|
+
/** The string URL of the resource. If there were redirects, the final
|
|
31
|
+
* specifier should be set here, otherwise the requested specifier. */
|
|
32
|
+
specifier: string;
|
|
33
|
+
/** For remote resources, a record of headers should be set, where the key's
|
|
34
|
+
* have been normalized to be lower case values. */
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
/** The string value of the loaded resources. */
|
|
37
|
+
content: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface LoadResponseExternal {
|
|
41
|
+
/** The loaded module is either _external_ or _built-in_ to the runtime. */
|
|
42
|
+
kind: "external";
|
|
43
|
+
/** The strung URL of the resource. If there were redirects, the final
|
|
44
|
+
* specifier should be set here, otherwise the requested specifier. */
|
|
45
|
+
specifier: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type LoadResponse = LoadResponseModule | LoadResponseExternal;
|
|
49
|
+
|
|
50
|
+
export interface PositionJson {
|
|
51
|
+
/** The line number of a position within a source file. The number is a zero
|
|
52
|
+
* based index. */
|
|
53
|
+
line: number;
|
|
54
|
+
/** The character number of a position within a source file. The number is a
|
|
55
|
+
* zero based index. */
|
|
56
|
+
character: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Range {
|
|
60
|
+
/** A string URL representing a source of a dependency. */
|
|
61
|
+
specifier: string;
|
|
62
|
+
/** The start location of a range of text in a source file. */
|
|
63
|
+
start?: PositionJson;
|
|
64
|
+
/** The end location of a range of text in a source file. */
|
|
65
|
+
end?: PositionJson;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface RangeJson {
|
|
69
|
+
/** The start location of a range of text in a source file. */
|
|
70
|
+
start: PositionJson;
|
|
71
|
+
/** The end location of a range of text in a source file. */
|
|
72
|
+
end: PositionJson;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ResolvedDependency {
|
|
76
|
+
/** The fully resolved string URL of the dependency, which should be
|
|
77
|
+
* resolvable in the module graph. If there was an error, `error` will be set
|
|
78
|
+
* and this will be undefined. */
|
|
79
|
+
specifier?: string;
|
|
80
|
+
/** Any error encountered when trying to resolved the specifier. If this is
|
|
81
|
+
* defined, `specifier` will be undefined. */
|
|
82
|
+
error?: string;
|
|
83
|
+
/** The range within the source code where the specifier was identified. */
|
|
84
|
+
span: RangeJson;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface TypesDependencyJson {
|
|
88
|
+
/** The string specifier that was used for the dependency. */
|
|
89
|
+
specifier: string;
|
|
90
|
+
/** An object pointing to the resolved dependency. */
|
|
91
|
+
dependency: ResolvedDependency;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The kind of module.
|
|
95
|
+
*
|
|
96
|
+
* For asserted modules, the value of the `asserted` property is set to the
|
|
97
|
+
* `type` value of the import attribute.
|
|
98
|
+
*
|
|
99
|
+
* Dependency analysis is not performed for asserted or Script modules
|
|
100
|
+
* currently. Synthetic modules were injected into the graph with their own
|
|
101
|
+
* dependencies provided. */
|
|
102
|
+
export type ModuleKind =
|
|
103
|
+
| "asserted"
|
|
104
|
+
| "esm"
|
|
105
|
+
| "npm"
|
|
106
|
+
| "external";
|
|
107
|
+
|
|
108
|
+
export interface DependencyJson {
|
|
109
|
+
/** The string specifier that was used for the dependency. */
|
|
110
|
+
specifier: string;
|
|
111
|
+
/** An object pointing to the resolved _code_ dependency. */
|
|
112
|
+
code?: ResolvedDependency;
|
|
113
|
+
/** An object pointing to the resolved _type_ dependency of a module. This is
|
|
114
|
+
* populated when the `@deno-types` directive was used to supply a type
|
|
115
|
+
* definition file for a dependency. */
|
|
116
|
+
type?: ResolvedDependency;
|
|
117
|
+
/** A flag indicating if the dependency was dynamic. (e.g.
|
|
118
|
+
* `await import("mod.ts")`) */
|
|
119
|
+
isDynamic?: true;
|
|
120
|
+
assertionType?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// todo(dsherret): split this up into separate types based on the "kind"
|
|
124
|
+
|
|
125
|
+
export interface ModuleJson extends CacheInfo {
|
|
126
|
+
/** The string URL of the module. */
|
|
127
|
+
specifier: string;
|
|
128
|
+
/** Any error encountered when attempting to load the module. */
|
|
129
|
+
error?: string;
|
|
130
|
+
/** The module kind that was determined when the module was resolved. This is
|
|
131
|
+
* used by loaders to indicate how a module needs to be loaded at runtime. */
|
|
132
|
+
kind?: ModuleKind;
|
|
133
|
+
/** An array of dependencies that were identified in the module. */
|
|
134
|
+
dependencies?: DependencyJson[];
|
|
135
|
+
/** If the module had a types dependency, the information about that
|
|
136
|
+
* dependency. */
|
|
137
|
+
typesDependency?: TypesDependencyJson;
|
|
138
|
+
/** The resolved media type of the module, which determines how Deno will
|
|
139
|
+
* handle the module. */
|
|
140
|
+
mediaType?: MediaType;
|
|
141
|
+
/** The size of the source content of the module in bytes. */
|
|
142
|
+
size?: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface GraphImportJson {
|
|
146
|
+
/** The referrer (URL string) that was used as a base when resolving the
|
|
147
|
+
* imports added to the graph. */
|
|
148
|
+
referrer: string;
|
|
149
|
+
/** An array of resolved dependencies which were imported using the
|
|
150
|
+
* referrer. */
|
|
151
|
+
dependencies?: DependencyJson[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The plain-object representation of a module graph that is suitable for
|
|
155
|
+
* serialization to JSON. */
|
|
156
|
+
export interface ModuleGraphJson {
|
|
157
|
+
/** The module specifiers (URL string) of the _roots_ of the module graph of
|
|
158
|
+
* which the module graph was built for. */
|
|
159
|
+
roots: string[];
|
|
160
|
+
/** An array of modules that are part of the module graph. */
|
|
161
|
+
modules: ModuleJson[];
|
|
162
|
+
/** External imports that were added to the graph when it was being built.
|
|
163
|
+
* The key is the referrer which was used as a base to resolve the
|
|
164
|
+
* dependency. The value is the resolved dependency. */
|
|
165
|
+
imports?: GraphImportJson[];
|
|
166
|
+
/** A record/map of any redirects encountered when resolving modules. The
|
|
167
|
+
* key was the requested module specifier and the value is the redirected
|
|
168
|
+
* module specifier. */
|
|
169
|
+
redirects: Record<string, string>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface Dependency {
|
|
173
|
+
/** An object pointing to the resolved _code_ dependency. */
|
|
174
|
+
code?: ResolvedDependency;
|
|
175
|
+
/** An object pointing to the resolved _type_ dependency of a module. This is
|
|
176
|
+
* populated when the `@deno-types` directive was used to supply a type
|
|
177
|
+
* definition file for a dependency. */
|
|
178
|
+
type?: ResolvedDependency;
|
|
179
|
+
/** A flag indicating if the dependency was dynamic. (e.g.
|
|
180
|
+
* `await import("mod.ts")`) */
|
|
181
|
+
isDynamic?: true;
|
|
182
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare enum MediaType {
|
|
2
|
+
JavaScript = "JavaScript",
|
|
3
|
+
Mjs = "Mjs",
|
|
4
|
+
Cjs = "Cjs",
|
|
5
|
+
Jsx = "Jsx",
|
|
6
|
+
TypeScript = "TypeScript",
|
|
7
|
+
Mts = "Mts",
|
|
8
|
+
Cts = "Cts",
|
|
9
|
+
Dts = "Dts",
|
|
10
|
+
Dmts = "Dmts",
|
|
11
|
+
Dcts = "Dcts",
|
|
12
|
+
Tsx = "Tsx",
|
|
13
|
+
Json = "Json",
|
|
14
|
+
Wasm = "Wasm",
|
|
15
|
+
TsBuildInfo = "TsBuildInfo",
|
|
16
|
+
SourceMap = "SourceMap",
|
|
17
|
+
Unknown = "Unknown"
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
2
|
+
export var MediaType;
|
|
3
|
+
(function (MediaType) {
|
|
4
|
+
MediaType["JavaScript"] = "JavaScript";
|
|
5
|
+
MediaType["Mjs"] = "Mjs";
|
|
6
|
+
MediaType["Cjs"] = "Cjs";
|
|
7
|
+
MediaType["Jsx"] = "Jsx";
|
|
8
|
+
MediaType["TypeScript"] = "TypeScript";
|
|
9
|
+
MediaType["Mts"] = "Mts";
|
|
10
|
+
MediaType["Cts"] = "Cts";
|
|
11
|
+
MediaType["Dts"] = "Dts";
|
|
12
|
+
MediaType["Dmts"] = "Dmts";
|
|
13
|
+
MediaType["Dcts"] = "Dcts";
|
|
14
|
+
MediaType["Tsx"] = "Tsx";
|
|
15
|
+
MediaType["Json"] = "Json";
|
|
16
|
+
MediaType["Wasm"] = "Wasm";
|
|
17
|
+
MediaType["TsBuildInfo"] = "TsBuildInfo";
|
|
18
|
+
MediaType["SourceMap"] = "SourceMap";
|
|
19
|
+
MediaType["Unknown"] = "Unknown";
|
|
20
|
+
})(MediaType || (MediaType = {}));
|
package/dist/node/bundler.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
1
2
|
import { access, readdir, readFile, rm, writeFile } from 'fs/promises';
|
|
2
3
|
import { join, resolve } from 'path';
|
|
3
4
|
import process from 'process';
|
|
@@ -430,3 +431,27 @@ test('Loads JSON modules', async () => {
|
|
|
430
431
|
await cleanup();
|
|
431
432
|
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
432
433
|
});
|
|
434
|
+
test('Supports TSX and process.env', async () => {
|
|
435
|
+
const { basePath, cleanup, distPath } = await useFixture('tsx');
|
|
436
|
+
const sourceDirectory = join(basePath, 'functions');
|
|
437
|
+
const declarations = [
|
|
438
|
+
{
|
|
439
|
+
function: 'func1',
|
|
440
|
+
path: '/func1',
|
|
441
|
+
},
|
|
442
|
+
];
|
|
443
|
+
const vendorDirectory = await tmp.dir();
|
|
444
|
+
await bundle([sourceDirectory], distPath, declarations, {
|
|
445
|
+
basePath,
|
|
446
|
+
vendorDirectory: vendorDirectory.path,
|
|
447
|
+
});
|
|
448
|
+
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
449
|
+
const manifest = JSON.parse(manifestFile);
|
|
450
|
+
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
451
|
+
process.env.FOO = 'bar';
|
|
452
|
+
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
453
|
+
expect(Buffer.from(func1, 'base64').toString()).toBe(`hippedy hoppedy, createElement is now a production property. Here, take this env var: FOO=bar`);
|
|
454
|
+
await cleanup();
|
|
455
|
+
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
456
|
+
delete process.env.FOO;
|
|
457
|
+
});
|
package/dist/node/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export type { EdgeFunction } from './edge_function.js';
|
|
|
6
6
|
export { findFunctions as find } from './finder.js';
|
|
7
7
|
export { generateManifest } from './manifest.js';
|
|
8
8
|
export type { EdgeFunctionConfig, Manifest } from './manifest.js';
|
|
9
|
-
export { serve } from './server/server.js';
|
|
9
|
+
export { ModuleGraph, serve } from './server/server.js';
|
|
10
10
|
export { validateManifest, ManifestValidationError } from './validation/manifest/index.js';
|
|
@@ -7,10 +7,10 @@ interface VendorNPMSpecifiersOptions {
|
|
|
7
7
|
functions: string[];
|
|
8
8
|
importMap: ImportMap;
|
|
9
9
|
logger: Logger;
|
|
10
|
-
|
|
10
|
+
environment: 'production' | 'development';
|
|
11
11
|
rootPath?: string;
|
|
12
12
|
}
|
|
13
|
-
export declare const vendorNPMSpecifiers: ({ basePath, directory, functions, importMap,
|
|
13
|
+
export declare const vendorNPMSpecifiers: ({ basePath, directory, functions, importMap, environment, rootPath, }: VendorNPMSpecifiersOptions) => Promise<{
|
|
14
14
|
cleanup: () => Promise<void>;
|
|
15
15
|
directory: string;
|
|
16
16
|
importMap: {
|
|
@@ -9,7 +9,7 @@ import { findUp } from 'find-up';
|
|
|
9
9
|
import getPackageName from 'get-package-name';
|
|
10
10
|
import tmp from 'tmp-promise';
|
|
11
11
|
import { pathsBetween } from './utils/fs.js';
|
|
12
|
-
const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.cts', '.mts']);
|
|
12
|
+
const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.tsx', '.cts', '.ctsx', '.mts', '.mtsx']);
|
|
13
13
|
const slugifyPackageName = (specifier) => {
|
|
14
14
|
if (!specifier.startsWith('@'))
|
|
15
15
|
return specifier;
|
|
@@ -66,6 +66,10 @@ const safelyDetectTypes = async (packageJsonPath) => {
|
|
|
66
66
|
// Workaround for https://github.com/evanw/esbuild/issues/1921.
|
|
67
67
|
const banner = {
|
|
68
68
|
js: `
|
|
69
|
+
import process from "node:process";
|
|
70
|
+
import {setImmediate, clearImmediate} from "node:timers";
|
|
71
|
+
import {Buffer} from "node:buffer";
|
|
72
|
+
|
|
69
73
|
import {createRequire as ___nfyCreateRequire} from "node:module";
|
|
70
74
|
import {fileURLToPath as ___nfyFileURLToPath} from "node:url";
|
|
71
75
|
import {dirname as ___nfyPathDirname} from "node:path";
|
|
@@ -78,7 +82,7 @@ const banner = {
|
|
|
78
82
|
* Parses a set of functions and returns a list of specifiers that correspond
|
|
79
83
|
* to npm modules.
|
|
80
84
|
*/
|
|
81
|
-
const getNPMSpecifiers = async ({ basePath, functions, importMap,
|
|
85
|
+
const getNPMSpecifiers = async ({ basePath, functions, importMap, environment, rootPath }) => {
|
|
82
86
|
const baseURL = pathToFileURL(basePath);
|
|
83
87
|
const { reasons } = await nodeFileTrace(functions, {
|
|
84
88
|
base: rootPath,
|
|
@@ -153,7 +157,7 @@ const getNPMSpecifiers = async ({ basePath, functions, importMap, referenceTypes
|
|
|
153
157
|
if (isDirectDependency) {
|
|
154
158
|
npmSpecifiers.push({
|
|
155
159
|
specifier: packageName,
|
|
156
|
-
types:
|
|
160
|
+
types: environment === 'development' ? await safelyDetectTypes(path.join(basePath, filePath)) : undefined,
|
|
157
161
|
});
|
|
158
162
|
}
|
|
159
163
|
}
|
|
@@ -162,7 +166,7 @@ const getNPMSpecifiers = async ({ basePath, functions, importMap, referenceTypes
|
|
|
162
166
|
npmSpecifiersWithExtraneousFiles: [...npmSpecifiersWithExtraneousFiles],
|
|
163
167
|
};
|
|
164
168
|
};
|
|
165
|
-
export const vendorNPMSpecifiers = async ({ basePath, directory, functions, importMap,
|
|
169
|
+
export const vendorNPMSpecifiers = async ({ basePath, directory, functions, importMap, environment, rootPath = basePath, }) => {
|
|
166
170
|
// The directories that esbuild will use when resolving Node modules. We must
|
|
167
171
|
// set these manually because esbuild will be operating from a temporary
|
|
168
172
|
// directory that will not live inside the project root, so the normal
|
|
@@ -176,7 +180,7 @@ export const vendorNPMSpecifiers = async ({ basePath, directory, functions, impo
|
|
|
176
180
|
basePath,
|
|
177
181
|
functions,
|
|
178
182
|
importMap: importMap.getContentsWithURLObjects(),
|
|
179
|
-
|
|
183
|
+
environment,
|
|
180
184
|
rootPath,
|
|
181
185
|
});
|
|
182
186
|
// If we found no specifiers, there's nothing left to do here.
|
|
@@ -210,6 +214,11 @@ export const vendorNPMSpecifiers = async ({ basePath, directory, functions, impo
|
|
|
210
214
|
splitting: true,
|
|
211
215
|
target: 'es2020',
|
|
212
216
|
write: false,
|
|
217
|
+
define: environment === 'production'
|
|
218
|
+
? {
|
|
219
|
+
'process.env.NODE_ENV': '"production"',
|
|
220
|
+
}
|
|
221
|
+
: undefined,
|
|
213
222
|
});
|
|
214
223
|
await Promise.all(outputFiles.map(async (file) => {
|
|
215
224
|
var _a;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
/// <reference types="node" />
|
|
6
|
+
import type { ModuleGraphJson } from '../../deno/vendor/deno.land/x/deno_graph@0.59.2/types.d.js';
|
|
1
7
|
import { OnAfterDownloadHook, OnBeforeDownloadHook } from '../bridge.js';
|
|
2
8
|
import { FunctionConfig } from '../config.js';
|
|
3
9
|
import type { EdgeFunction } from '../edge_function.js';
|
|
4
10
|
import type { FeatureFlags } from '../feature_flags.js';
|
|
5
11
|
import { LogFunction } from '../logger.js';
|
|
6
12
|
export type FormatFunction = (name: string) => string;
|
|
13
|
+
export type ModuleGraph = ModuleGraphJson;
|
|
7
14
|
interface StartServerOptions {
|
|
8
15
|
getFunctionsConfig?: boolean;
|
|
9
16
|
}
|
|
@@ -34,7 +41,7 @@ interface ServeOptions {
|
|
|
34
41
|
export declare const serve: ({ basePath, bootstrapURL, certificatePath, debug, distImportMapPath, inspectSettings, featureFlags, formatExportTypeError, formatImportError, importMapPaths, onAfterDownload, onBeforeDownload, port, rootPath, servePath, userLogger, systemLogger, }: ServeOptions) => Promise<(functions: EdgeFunction[], env?: NodeJS.ProcessEnv, options?: StartServerOptions) => Promise<{
|
|
35
42
|
features: Record<string, boolean>;
|
|
36
43
|
functionsConfig: FunctionConfig[];
|
|
37
|
-
graph:
|
|
44
|
+
graph: ModuleGraphJson;
|
|
38
45
|
npmSpecifiersWithExtraneousFiles: string[];
|
|
39
46
|
success: boolean;
|
|
40
47
|
}>>;
|
|
@@ -24,7 +24,11 @@ const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImport
|
|
|
24
24
|
if ((processRef === null || processRef === void 0 ? void 0 : processRef.ps) !== undefined) {
|
|
25
25
|
await killProcess(processRef.ps);
|
|
26
26
|
}
|
|
27
|
-
let graph
|
|
27
|
+
let graph = {
|
|
28
|
+
roots: [],
|
|
29
|
+
modules: [],
|
|
30
|
+
redirects: {},
|
|
31
|
+
};
|
|
28
32
|
const stage2Path = await generateStage2({
|
|
29
33
|
bootstrapURL,
|
|
30
34
|
distDirectory,
|
|
@@ -44,7 +48,7 @@ const prepareServer = ({ basePath, bootstrapURL, deno, distDirectory, distImport
|
|
|
44
48
|
functions: functions.map(({ path }) => path),
|
|
45
49
|
importMap,
|
|
46
50
|
logger,
|
|
47
|
-
|
|
51
|
+
environment: 'development',
|
|
48
52
|
rootPath,
|
|
49
53
|
});
|
|
50
54
|
if (vendor) {
|
|
@@ -48,9 +48,7 @@ test('Starts a server and serves requests for edge functions', async () => {
|
|
|
48
48
|
expect(functionsConfig).toEqual([{ path: '/my-function' }, {}, { path: '/global-netlify' }]);
|
|
49
49
|
expect(npmSpecifiersWithExtraneousFiles).toEqual(['dictionary']);
|
|
50
50
|
for (const key in functions) {
|
|
51
|
-
const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(
|
|
52
|
-
// @ts-expect-error TODO: Module graph is currently not typed
|
|
53
|
-
({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
|
|
51
|
+
const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
|
|
54
52
|
expect(graphEntry).toBe(true);
|
|
55
53
|
}
|
|
56
54
|
const response1 = await fetch(`http://0.0.0.0:${port}/foo`, {
|
|
@@ -121,9 +119,7 @@ test('Serves edge functions in a monorepo setup', async () => {
|
|
|
121
119
|
expect(functionsConfig).toEqual([{ path: '/func1' }]);
|
|
122
120
|
expect(npmSpecifiersWithExtraneousFiles).toEqual(['child-1']);
|
|
123
121
|
for (const key in functions) {
|
|
124
|
-
const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(
|
|
125
|
-
// @ts-expect-error TODO: Module graph is currently not typed
|
|
126
|
-
({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
|
|
122
|
+
const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
|
|
127
123
|
expect(graphEntry).toBe(true);
|
|
128
124
|
}
|
|
129
125
|
const response1 = await fetch(`http://0.0.0.0:${port}/func1`, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/edge-bundler",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.1.1",
|
|
4
4
|
"description": "Intelligently prepare Netlify Edge Functions for deployment",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/node/index.js",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"test:dev:deno": "deno test --allow-all deno",
|
|
35
35
|
"test:ci:vitest": "vitest run --coverage",
|
|
36
36
|
"test:ci:deno": "deno test --allow-all deno",
|
|
37
|
-
"test:integration": "node --experimental-modules test/integration/test.js"
|
|
37
|
+
"test:integration": "node --experimental-modules test/integration/test.js",
|
|
38
|
+
"vendor": "deno vendor --force --output deno/vendor https://deno.land/x/deno_graph@0.59.2/types.d.ts https://deno.land/x/eszip@v0.55.2/mod.ts https://deno.land/x/retry@v2.0.0/mod.ts https://deno.land/x/std@0.177.0/path/mod.ts"
|
|
38
39
|
},
|
|
39
40
|
"config": {
|
|
40
41
|
"eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{node,scripts,.github}/**/*.{js,ts,md,html}\" \"*.{js,ts,md,html}\"",
|
package/deno/lib/stage2.test.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { assertEquals, assertStringIncludes } from 'https://deno.land/std@0.177.0/testing/asserts.ts'
|
|
2
|
-
|
|
3
|
-
import { join } from 'https://deno.land/std@0.177.0/path/mod.ts'
|
|
4
|
-
import { pathToFileURL } from 'https://deno.land/std@0.177.0/node/url.ts'
|
|
5
|
-
|
|
6
|
-
import { getStage2Entry } from './stage2.ts'
|
|
7
|
-
import { virtualRoot } from './consts.ts'
|
|
8
|
-
|
|
9
|
-
Deno.test('`getStage2Entry` returns a valid stage 2 file', async () => {
|
|
10
|
-
const directory = await Deno.makeTempDir()
|
|
11
|
-
const functions = [
|
|
12
|
-
{
|
|
13
|
-
name: 'func1',
|
|
14
|
-
path: join(directory, 'func1.ts'),
|
|
15
|
-
response: 'Hello from function 1',
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
name: 'func2',
|
|
19
|
-
path: join(directory, 'func2.ts'),
|
|
20
|
-
response: 'Hello from function 2',
|
|
21
|
-
},
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
for (const func of functions) {
|
|
25
|
-
const contents = `export default async () => new Response(${JSON.stringify(func.response)})`
|
|
26
|
-
|
|
27
|
-
await Deno.writeTextFile(func.path, contents)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const baseURL = pathToFileURL(directory)
|
|
31
|
-
const stage2 = getStage2Entry(
|
|
32
|
-
directory,
|
|
33
|
-
functions.map(({ name, path }) => ({ name, path })),
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
// Ensuring that the stage 2 paths have the virtual root before we strip it.
|
|
37
|
-
assertStringIncludes(stage2, virtualRoot)
|
|
38
|
-
|
|
39
|
-
// Replacing the virtual root with the URL of the temporary directory so that
|
|
40
|
-
// we can actually import the module.
|
|
41
|
-
const normalizedStage2 = stage2.replaceAll(virtualRoot, `${baseURL.href}/`)
|
|
42
|
-
|
|
43
|
-
const stage2Path = join(directory, 'stage2.ts')
|
|
44
|
-
const stage2URL = pathToFileURL(stage2Path)
|
|
45
|
-
|
|
46
|
-
await Deno.writeTextFile(stage2Path, normalizedStage2)
|
|
47
|
-
|
|
48
|
-
const mod = await import(stage2URL.href)
|
|
49
|
-
|
|
50
|
-
await Deno.remove(directory, { recursive: true })
|
|
51
|
-
|
|
52
|
-
for (const func of functions) {
|
|
53
|
-
const result = await mod.functions[func.name]()
|
|
54
|
-
|
|
55
|
-
assertEquals(await result.text(), func.response)
|
|
56
|
-
assertEquals(mod.metadata.functions[func.name].url, pathToFileURL(func.path).toString())
|
|
57
|
-
}
|
|
58
|
-
})
|
|
@@ -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
|
-
});
|
package/dist/node/deno_config.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { join, resolve } from 'path';
|
|
3
|
-
import { parse as parseJSONC } from 'jsonc-parser';
|
|
4
|
-
import { isNodeError } from './utils/error.js';
|
|
5
|
-
const filenames = ['deno.json', 'deno.jsonc'];
|
|
6
|
-
export const getConfig = async (basePath) => {
|
|
7
|
-
if (basePath === undefined) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
for (const filename of filenames) {
|
|
11
|
-
const candidatePath = join(basePath, filename);
|
|
12
|
-
const config = await getConfigFromFile(candidatePath);
|
|
13
|
-
if (config !== undefined) {
|
|
14
|
-
return normalizeConfig(config, basePath);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const getConfigFromFile = async (filePath) => {
|
|
19
|
-
try {
|
|
20
|
-
const data = await fs.readFile(filePath, 'utf8');
|
|
21
|
-
const config = parseJSONC(data);
|
|
22
|
-
return config;
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const normalizeConfig = (rawConfig, basePath) => {
|
|
32
|
-
const config = {};
|
|
33
|
-
if (rawConfig.importMap) {
|
|
34
|
-
if (typeof rawConfig.importMap !== 'string') {
|
|
35
|
-
throw new TypeError(`'importMap' property in Deno config must be a string`);
|
|
36
|
-
}
|
|
37
|
-
config.importMap = resolve(basePath, rawConfig.importMap);
|
|
38
|
-
}
|
|
39
|
-
return config;
|
|
40
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import tmp from 'tmp-promise';
|
|
4
|
-
import { expect, test } from 'vitest';
|
|
5
|
-
import { getConfig } from './deno_config.js';
|
|
6
|
-
test('Returns `undefined` if no config file is found', async () => {
|
|
7
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
8
|
-
const config = await getConfig(path);
|
|
9
|
-
expect(config).toBeUndefined();
|
|
10
|
-
await cleanup();
|
|
11
|
-
});
|
|
12
|
-
test('Returns an empty object if the config file cannot be parsed', async () => {
|
|
13
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
14
|
-
const configPath = join(path, 'deno.json');
|
|
15
|
-
await fs.writeFile(configPath, '{');
|
|
16
|
-
const config = await getConfig(path);
|
|
17
|
-
expect(config).toEqual({});
|
|
18
|
-
await cleanup();
|
|
19
|
-
});
|
|
20
|
-
test('Resolves `importMap` into an absolute path', async () => {
|
|
21
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
22
|
-
const configPath = join(path, 'deno.json');
|
|
23
|
-
const data = JSON.stringify({ importMap: 'import_map.json' });
|
|
24
|
-
await fs.writeFile(configPath, data);
|
|
25
|
-
const config = await getConfig(path);
|
|
26
|
-
expect(config).toEqual({ importMap: join(path, 'import_map.json') });
|
|
27
|
-
await cleanup();
|
|
28
|
-
});
|
|
29
|
-
test('Supports JSONC', async () => {
|
|
30
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
31
|
-
const configPath = join(path, 'deno.jsonc');
|
|
32
|
-
const data = JSON.stringify({ importMap: 'import_map.json' });
|
|
33
|
-
await fs.writeFile(configPath, `// This is a comment\n${data}`);
|
|
34
|
-
const config = await getConfig(path);
|
|
35
|
-
expect(config).toEqual({ importMap: join(path, 'import_map.json') });
|
|
36
|
-
await cleanup();
|
|
37
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import getPort from 'get-port';
|
|
3
|
-
import fetch from 'node-fetch';
|
|
4
|
-
import { test, expect } from 'vitest';
|
|
5
|
-
import { fixturesDir } from '../test/util.js';
|
|
6
|
-
import { serve } from './index.js';
|
|
7
|
-
test('bundler serving functionality', async () => {
|
|
8
|
-
const port = await getPort();
|
|
9
|
-
const server = await serve({
|
|
10
|
-
port,
|
|
11
|
-
});
|
|
12
|
-
const { success } = await server([
|
|
13
|
-
{
|
|
14
|
-
name: 'echo_env',
|
|
15
|
-
path: join(fixturesDir, 'serve_test', 'echo_env.ts'),
|
|
16
|
-
},
|
|
17
|
-
], {
|
|
18
|
-
very_secret_secret: 'i love netlify',
|
|
19
|
-
});
|
|
20
|
-
expect(success).toBe(true);
|
|
21
|
-
const response = await fetch(`http://0.0.0.0:${port}/foo`, {
|
|
22
|
-
headers: {
|
|
23
|
-
'x-deno-functions': 'echo_env',
|
|
24
|
-
'x-deno-pass': 'passthrough',
|
|
25
|
-
'X-NF-Request-ID': 'foo',
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
expect(response.status).toBe(200);
|
|
29
|
-
const body = (await response.json());
|
|
30
|
-
expect(body.very_secret_secret).toBe('i love netlify');
|
|
31
|
-
});
|