@netlify/edge-bundler 14.8.2 → 14.8.4
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/dist/node/bridge.d.ts +1 -1
- package/dist/node/bridge.js +3 -3
- package/dist/node/bundler.js +41 -46
- package/dist/node/config.js +6 -1
- package/dist/node/feature_flags.js +1 -1
- package/dist/node/formats/eszip.d.ts +0 -4
- package/dist/node/formats/eszip.js +0 -11
- package/package.json +4 -3
- package/deno/extract.ts +0 -7
- package/dist/node/bridge.test.js +0 -141
- package/dist/node/bundler.test.js +0 -717
- package/dist/node/config.test.js +0 -408
- package/dist/node/declaration.test.js +0 -131
- package/dist/node/deploy_config.test.js +0 -48
- package/dist/node/downloader.test.js +0 -124
- package/dist/node/finder.test.js +0 -17
- package/dist/node/import_map.test.js +0 -212
- package/dist/node/logger.test.js +0 -51
- package/dist/node/main.test.js +0 -46
- package/dist/node/manifest.test.js +0 -597
- package/dist/node/package_json.test.js +0 -7
- package/dist/node/server/server.test.js +0 -144
- package/dist/node/stage_2.test.js +0 -53
- package/dist/node/types.test.js +0 -63
- package/dist/node/validation/manifest/index.test.js +0 -237
|
@@ -1,717 +0,0 @@
|
|
|
1
|
-
import { Buffer } from 'buffer';
|
|
2
|
-
import { access, readdir, readFile, rm, writeFile } from 'fs/promises';
|
|
3
|
-
import { join, resolve } from 'path';
|
|
4
|
-
import process from 'process';
|
|
5
|
-
import { pathToFileURL } from 'url';
|
|
6
|
-
import { lt } from 'semver';
|
|
7
|
-
import * as tar from 'tar';
|
|
8
|
-
import tmp from 'tmp-promise';
|
|
9
|
-
import { test, expect, vi, describe } from 'vitest';
|
|
10
|
-
import { importMapSpecifier } from '../shared/consts.js';
|
|
11
|
-
import { denoVersion, runESZIP, runTarball, useFixture } from '../test/util.js';
|
|
12
|
-
import { BundleError } from './bundle_error.js';
|
|
13
|
-
import { bundle } from './bundler.js';
|
|
14
|
-
import { isFileNotFoundError } from './utils/error.js';
|
|
15
|
-
import { validateManifest } from './validation/manifest/index.js';
|
|
16
|
-
test('Produces an ESZIP bundle', async () => {
|
|
17
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
18
|
-
const declarations = [
|
|
19
|
-
{
|
|
20
|
-
function: 'func1',
|
|
21
|
-
path: '/func1',
|
|
22
|
-
},
|
|
23
|
-
];
|
|
24
|
-
const userDirectory = join(basePath, 'user-functions');
|
|
25
|
-
const internalDirectory = join(basePath, 'functions');
|
|
26
|
-
const result = await bundle([userDirectory, internalDirectory], distPath, declarations, {
|
|
27
|
-
basePath,
|
|
28
|
-
configPath: join(internalDirectory, 'config.json'),
|
|
29
|
-
importMapPaths: [join(userDirectory, 'import_map.json')],
|
|
30
|
-
});
|
|
31
|
-
const generatedFiles = await readdir(distPath);
|
|
32
|
-
expect(result.functions.length).toBe(3);
|
|
33
|
-
expect(generatedFiles.length).toBe(2);
|
|
34
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
35
|
-
const manifest = JSON.parse(manifestFile);
|
|
36
|
-
expect(() => validateManifest(manifest)).not.toThrowError();
|
|
37
|
-
const { bundles, import_map: importMapURL } = manifest;
|
|
38
|
-
expect(bundles.length).toBe(1);
|
|
39
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
40
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
41
|
-
expect(importMapURL).toBe(importMapSpecifier);
|
|
42
|
-
const bundlePath = join(distPath, bundles[0].asset);
|
|
43
|
-
const { func1, func2, func3 } = await runESZIP(bundlePath);
|
|
44
|
-
expect(func1).toBe('HELLO, JANE DOE!');
|
|
45
|
-
expect(func2).toBe('Jane Doe');
|
|
46
|
-
expect(func3).toBe('hello, netlify!');
|
|
47
|
-
await cleanup();
|
|
48
|
-
});
|
|
49
|
-
test('Uses the vendored eszip module instead of fetching it from deno.land', async () => {
|
|
50
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
51
|
-
const declarations = [
|
|
52
|
-
{
|
|
53
|
-
function: 'func1',
|
|
54
|
-
path: '/func1',
|
|
55
|
-
},
|
|
56
|
-
];
|
|
57
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
58
|
-
const result = await bundle([sourceDirectory], distPath, declarations, {
|
|
59
|
-
basePath,
|
|
60
|
-
configPath: join(sourceDirectory, 'config.json'),
|
|
61
|
-
});
|
|
62
|
-
const generatedFiles = await readdir(distPath);
|
|
63
|
-
expect(result.functions.length).toBe(1);
|
|
64
|
-
expect(generatedFiles.length).toBe(2);
|
|
65
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
66
|
-
const manifest = JSON.parse(manifestFile);
|
|
67
|
-
const { bundles } = manifest;
|
|
68
|
-
expect(bundles.length).toBe(1);
|
|
69
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
70
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
71
|
-
await cleanup();
|
|
72
|
-
});
|
|
73
|
-
test('Adds a custom error property to user errors during bundling', async () => {
|
|
74
|
-
process.env.NO_COLOR = 'true';
|
|
75
|
-
expect.assertions(3);
|
|
76
|
-
const { basePath, cleanup, distPath } = await useFixture('invalid_functions');
|
|
77
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
78
|
-
const declarations = [
|
|
79
|
-
{
|
|
80
|
-
function: 'func1',
|
|
81
|
-
path: '/func1',
|
|
82
|
-
},
|
|
83
|
-
];
|
|
84
|
-
try {
|
|
85
|
-
await bundle([sourceDirectory], distPath, declarations, { basePath });
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
expect(error).toBeInstanceOf(BundleError);
|
|
89
|
-
const messageBeforeStack = error.message;
|
|
90
|
-
expect(messageBeforeStack
|
|
91
|
-
.replace(/file:\/\/\/(.*?\/)(build\/packages\/edge-bundler\/deno\/vendor\/deno\.land\/x\/eszip.*)/, 'file://$2')
|
|
92
|
-
// eslint-disable-next-line no-control-regex
|
|
93
|
-
.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '')).toMatchSnapshot();
|
|
94
|
-
expect(error.customErrorInfo).toEqual({
|
|
95
|
-
location: {
|
|
96
|
-
format: 'eszip',
|
|
97
|
-
runtime: 'deno',
|
|
98
|
-
},
|
|
99
|
-
type: 'functionsBundling',
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
await cleanup();
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
test('Prints a nice error message when user tries importing an npm module', async () => {
|
|
107
|
-
expect.assertions(2);
|
|
108
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_npm_module_scheme');
|
|
109
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
110
|
-
const declarations = [
|
|
111
|
-
{
|
|
112
|
-
function: 'func1',
|
|
113
|
-
path: '/func1',
|
|
114
|
-
},
|
|
115
|
-
];
|
|
116
|
-
try {
|
|
117
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
118
|
-
basePath,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
expect(error).toBeInstanceOf(BundleError);
|
|
123
|
-
expect(error.message).toEqual(`There was an error when loading the 'p-retry' npm module. Support for npm modules in edge functions is an experimental feature. Refer to https://ntl.fyi/edge-functions-npm for more information.`);
|
|
124
|
-
}
|
|
125
|
-
finally {
|
|
126
|
-
await cleanup();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
test('Does not add a custom error property to system errors during bundling', async () => {
|
|
130
|
-
expect.assertions(1);
|
|
131
|
-
try {
|
|
132
|
-
// @ts-expect-error Sending bad input to `bundle` to force a system error.
|
|
133
|
-
await bundle([123, 321], '/some/directory', declarations);
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
expect(error).not.toBeInstanceOf(BundleError);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
test('Uses the cache directory as the `DENO_DIR` value', async () => {
|
|
140
|
-
expect.assertions(3);
|
|
141
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
142
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
143
|
-
const cacheDir = await tmp.dir();
|
|
144
|
-
const declarations = [
|
|
145
|
-
{
|
|
146
|
-
function: 'func1',
|
|
147
|
-
path: '/func1',
|
|
148
|
-
},
|
|
149
|
-
];
|
|
150
|
-
const options = {
|
|
151
|
-
basePath,
|
|
152
|
-
cacheDirectory: cacheDir.path,
|
|
153
|
-
configPath: join(sourceDirectory, 'config.json'),
|
|
154
|
-
};
|
|
155
|
-
const result = await bundle([sourceDirectory], distPath, declarations, options);
|
|
156
|
-
const outFiles = await readdir(distPath);
|
|
157
|
-
expect(result.functions.length).toBe(1);
|
|
158
|
-
expect(outFiles.length).toBe(2);
|
|
159
|
-
const denoDir = await readdir(join(cacheDir.path, 'deno_dir'));
|
|
160
|
-
expect(denoDir.includes('gen')).toBe(true);
|
|
161
|
-
await cleanup();
|
|
162
|
-
});
|
|
163
|
-
test('Supports import maps with relative paths', async () => {
|
|
164
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
165
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
166
|
-
const declarations = [
|
|
167
|
-
{
|
|
168
|
-
function: 'func1',
|
|
169
|
-
path: '/func1',
|
|
170
|
-
},
|
|
171
|
-
];
|
|
172
|
-
const result = await bundle([sourceDirectory], distPath, declarations, {
|
|
173
|
-
basePath,
|
|
174
|
-
configPath: join(sourceDirectory, 'config.json'),
|
|
175
|
-
});
|
|
176
|
-
const generatedFiles = await readdir(distPath);
|
|
177
|
-
expect(result.functions.length).toBe(1);
|
|
178
|
-
expect(generatedFiles.length).toBe(2);
|
|
179
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
180
|
-
const manifest = JSON.parse(manifestFile);
|
|
181
|
-
const { bundles } = manifest;
|
|
182
|
-
expect(bundles.length).toBe(1);
|
|
183
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
184
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
185
|
-
await cleanup();
|
|
186
|
-
});
|
|
187
|
-
test('Ignores any user-defined `deno.json` files', async () => {
|
|
188
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
189
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
190
|
-
const declarations = [
|
|
191
|
-
{
|
|
192
|
-
function: 'func1',
|
|
193
|
-
path: '/func1',
|
|
194
|
-
},
|
|
195
|
-
];
|
|
196
|
-
// Creating an import map file that rewires the URL of the Deno registry to
|
|
197
|
-
// an invalid location.
|
|
198
|
-
const importMapFile = await tmp.file();
|
|
199
|
-
const importMap = {
|
|
200
|
-
imports: {
|
|
201
|
-
'https://deno.land/': 'https://black.hole/',
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
await writeFile(importMapFile.path, JSON.stringify(importMap));
|
|
205
|
-
// Deno configuration files need to be in the current working directory.
|
|
206
|
-
// There's not a great way for us to set the working directory of the `deno`
|
|
207
|
-
// process that we'll run, so our best bet is to write the file to whatever
|
|
208
|
-
// is the current working directory now and then clean it up.
|
|
209
|
-
const denoConfigPath = join(process.cwd(), 'deno.json');
|
|
210
|
-
const denoConfig = {
|
|
211
|
-
importMap: importMapFile.path,
|
|
212
|
-
};
|
|
213
|
-
try {
|
|
214
|
-
await access(denoConfigPath);
|
|
215
|
-
throw new Error(`The file at '${denoConfigPath} would be overwritten by this test. Please move the file to a different location and try again.'`);
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
if (!isFileNotFoundError(error)) {
|
|
219
|
-
throw error;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
await writeFile(denoConfigPath, JSON.stringify(denoConfig));
|
|
223
|
-
expect(() => bundle([sourceDirectory], distPath, declarations, {
|
|
224
|
-
basePath,
|
|
225
|
-
configPath: join(sourceDirectory, 'config.json'),
|
|
226
|
-
})).not.toThrow();
|
|
227
|
-
await cleanup();
|
|
228
|
-
await rm(denoConfigPath, { force: true, recursive: true, maxRetries: 10 });
|
|
229
|
-
await rm(importMapFile.path, { force: true, recursive: true, maxRetries: 10 });
|
|
230
|
-
});
|
|
231
|
-
test('Processes a function that imports a custom layer', async () => {
|
|
232
|
-
const { basePath, cleanup, distPath } = await useFixture('with_layers');
|
|
233
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
234
|
-
const declarations = [
|
|
235
|
-
{
|
|
236
|
-
function: 'func1',
|
|
237
|
-
path: '/func1',
|
|
238
|
-
},
|
|
239
|
-
];
|
|
240
|
-
const layer = { name: 'https://edge-function-layer-template.netlify.app/mod.ts', flag: 'edge-functions-layer-test' };
|
|
241
|
-
const result = await bundle([sourceDirectory], distPath, declarations, {
|
|
242
|
-
basePath,
|
|
243
|
-
configPath: join(sourceDirectory, 'config.json'),
|
|
244
|
-
});
|
|
245
|
-
const generatedFiles = await readdir(distPath);
|
|
246
|
-
expect(result.functions.length).toBe(1);
|
|
247
|
-
expect(generatedFiles.length).toBe(2);
|
|
248
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
249
|
-
const manifest = JSON.parse(manifestFile);
|
|
250
|
-
const { bundles, layers } = manifest;
|
|
251
|
-
expect(bundles.length).toBe(1);
|
|
252
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
253
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
254
|
-
expect(layers).toEqual([layer]);
|
|
255
|
-
await cleanup();
|
|
256
|
-
});
|
|
257
|
-
test('Loads declarations and import maps from the deploy configuration and in-source config', async () => {
|
|
258
|
-
const { basePath, cleanup, distPath } = await useFixture('with_deploy_config');
|
|
259
|
-
const declarations = [
|
|
260
|
-
{
|
|
261
|
-
function: 'func1',
|
|
262
|
-
path: '/func1',
|
|
263
|
-
},
|
|
264
|
-
];
|
|
265
|
-
const directories = [join(basePath, 'netlify', 'edge-functions'), join(basePath, '.netlify', 'edge-functions')];
|
|
266
|
-
const result = await bundle(directories, distPath, declarations, {
|
|
267
|
-
basePath,
|
|
268
|
-
configPath: join(basePath, '.netlify', 'edge-functions', 'manifest.json'),
|
|
269
|
-
internalSrcFolder: directories[1],
|
|
270
|
-
});
|
|
271
|
-
const generatedFiles = await readdir(distPath);
|
|
272
|
-
expect(result.functions.length).toBe(3);
|
|
273
|
-
expect(generatedFiles.length).toBe(2);
|
|
274
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
275
|
-
const manifest = JSON.parse(manifestFile);
|
|
276
|
-
const { bundles, function_config: functionConfig, routes } = manifest;
|
|
277
|
-
expect(bundles.length).toBe(1);
|
|
278
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
279
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
280
|
-
// respects excludedPath from deploy config
|
|
281
|
-
expect(routes[0].excluded_patterns).toEqual(['^/func2/skip/?$']);
|
|
282
|
-
expect(functionConfig.func2).toEqual({
|
|
283
|
-
name: 'Function two',
|
|
284
|
-
generator: '@netlify/fake-plugin@1.0.0',
|
|
285
|
-
});
|
|
286
|
-
// respects in-source config
|
|
287
|
-
expect(functionConfig.func3).toEqual({
|
|
288
|
-
name: 'in-config-function',
|
|
289
|
-
on_error: 'bypass',
|
|
290
|
-
generator: 'internalFunc',
|
|
291
|
-
});
|
|
292
|
-
await cleanup();
|
|
293
|
-
});
|
|
294
|
-
test("Ignores entries in `importMapPaths` that don't point to an existing import map file", async () => {
|
|
295
|
-
const systemLogger = vi.fn();
|
|
296
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
297
|
-
const sourceDirectory = join(basePath, 'user-functions');
|
|
298
|
-
// Creating import map file
|
|
299
|
-
const importMap = await tmp.file();
|
|
300
|
-
const importMapContents = {
|
|
301
|
-
imports: {
|
|
302
|
-
helper: pathToFileURL(join(basePath, 'helper.ts')).toString(),
|
|
303
|
-
},
|
|
304
|
-
scopes: {
|
|
305
|
-
[pathToFileURL(join(sourceDirectory, 'func3/func3.ts')).toString()]: {
|
|
306
|
-
helper: pathToFileURL(join(basePath, 'helper2.ts')).toString(),
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
await writeFile(importMap.path, JSON.stringify(importMapContents));
|
|
311
|
-
const nonExistingImportMapPath = join(distPath, 'some-file-that-does-not-exist.json');
|
|
312
|
-
const result = await bundle([sourceDirectory], distPath, [
|
|
313
|
-
{
|
|
314
|
-
function: 'func1',
|
|
315
|
-
path: '/func1',
|
|
316
|
-
},
|
|
317
|
-
], {
|
|
318
|
-
basePath,
|
|
319
|
-
importMapPaths: [nonExistingImportMapPath, importMap.path],
|
|
320
|
-
systemLogger,
|
|
321
|
-
});
|
|
322
|
-
const generatedFiles = await readdir(distPath);
|
|
323
|
-
expect(result.functions.length).toBe(2);
|
|
324
|
-
expect(generatedFiles.length).toBe(2);
|
|
325
|
-
expect(systemLogger).toHaveBeenCalledWith(`Did not find an import map file at '${nonExistingImportMapPath}'.`);
|
|
326
|
-
await cleanup();
|
|
327
|
-
await importMap.cleanup();
|
|
328
|
-
});
|
|
329
|
-
test('Handles imports with the `node:` prefix', async () => {
|
|
330
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_node_specifier');
|
|
331
|
-
const userDirectory = join(basePath, 'netlify', 'edge-functions');
|
|
332
|
-
const result = await bundle([userDirectory], distPath, [], {
|
|
333
|
-
basePath,
|
|
334
|
-
importMapPaths: [join(userDirectory, 'import_map.json')],
|
|
335
|
-
});
|
|
336
|
-
const generatedFiles = await readdir(distPath);
|
|
337
|
-
expect(result.functions.length).toBe(1);
|
|
338
|
-
expect(generatedFiles.length).toBe(2);
|
|
339
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
340
|
-
const manifest = JSON.parse(manifestFile);
|
|
341
|
-
expect(() => validateManifest(manifest)).not.toThrowError();
|
|
342
|
-
const { bundles, import_map: importMapURL, routes } = manifest;
|
|
343
|
-
expect(bundles.length).toBe(1);
|
|
344
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
345
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
346
|
-
expect(importMapURL).toBe(importMapSpecifier);
|
|
347
|
-
expect(routes.length).toBe(1);
|
|
348
|
-
expect(routes[0].function).toBe('func1');
|
|
349
|
-
expect(routes[0].pattern).toBe('^/func1/?$');
|
|
350
|
-
const bundlePath = join(distPath, bundles[0].asset);
|
|
351
|
-
const { func1 } = await runESZIP(bundlePath);
|
|
352
|
-
expect(func1).toBe('ok');
|
|
353
|
-
await cleanup();
|
|
354
|
-
});
|
|
355
|
-
test('Handles Node builtin imports without the `node:` prefix', async () => {
|
|
356
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin');
|
|
357
|
-
const userDirectory = join(basePath, 'netlify', 'edge-functions');
|
|
358
|
-
const result = await bundle([userDirectory], distPath, [], {
|
|
359
|
-
basePath,
|
|
360
|
-
importMapPaths: [join(userDirectory, 'import_map.json')],
|
|
361
|
-
});
|
|
362
|
-
const generatedFiles = await readdir(distPath);
|
|
363
|
-
expect(result.functions.length).toBe(1);
|
|
364
|
-
expect(generatedFiles.length).toBe(2);
|
|
365
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
366
|
-
const manifest = JSON.parse(manifestFile);
|
|
367
|
-
expect(() => validateManifest(manifest)).not.toThrowError();
|
|
368
|
-
const { bundles, import_map: importMapURL, routes } = manifest;
|
|
369
|
-
expect(bundles.length).toBe(1);
|
|
370
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
371
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
372
|
-
expect(importMapURL).toBe(importMapSpecifier);
|
|
373
|
-
expect(routes.length).toBe(1);
|
|
374
|
-
expect(routes[0].function).toBe('func1');
|
|
375
|
-
expect(routes[0].pattern).toBe('^/func1/?$');
|
|
376
|
-
const bundlePath = join(distPath, bundles[0].asset);
|
|
377
|
-
const { func1 } = await runESZIP(bundlePath);
|
|
378
|
-
expect(func1).toBe('ok');
|
|
379
|
-
await cleanup();
|
|
380
|
-
});
|
|
381
|
-
test('Loads npm modules from bare specifiers', async () => {
|
|
382
|
-
const systemLogger = vi.fn();
|
|
383
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_npm_module');
|
|
384
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
385
|
-
const declarations = [
|
|
386
|
-
{
|
|
387
|
-
function: 'func1',
|
|
388
|
-
path: '/func1',
|
|
389
|
-
},
|
|
390
|
-
];
|
|
391
|
-
const vendorDirectory = await tmp.dir();
|
|
392
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
393
|
-
basePath,
|
|
394
|
-
importMapPaths: [join(basePath, 'import_map.json')],
|
|
395
|
-
vendorDirectory: vendorDirectory.path,
|
|
396
|
-
systemLogger,
|
|
397
|
-
});
|
|
398
|
-
expect(systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:')).toBeUndefined();
|
|
399
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
400
|
-
const manifest = JSON.parse(manifestFile);
|
|
401
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
402
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
403
|
-
expect(func1).toBe(`<parent-1><child-1>JavaScript</child-1></parent-1>, <parent-2><child-2><grandchild-1>APIs<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-2>, <parent-3><child-2><grandchild-1>Markup<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-3>, TmV0bGlmeQ==`);
|
|
404
|
-
await cleanup();
|
|
405
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
406
|
-
});
|
|
407
|
-
test('Loads npm modules which use package.json.exports', async () => {
|
|
408
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_npm_module_exports');
|
|
409
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
410
|
-
const declarations = [
|
|
411
|
-
{
|
|
412
|
-
function: 'func1',
|
|
413
|
-
path: '/func1',
|
|
414
|
-
},
|
|
415
|
-
];
|
|
416
|
-
const vendorDirectory = await tmp.dir();
|
|
417
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
418
|
-
basePath,
|
|
419
|
-
vendorDirectory: vendorDirectory.path,
|
|
420
|
-
});
|
|
421
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
422
|
-
const manifest = JSON.parse(manifestFile);
|
|
423
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
424
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
425
|
-
expect(func1).toBe('hello');
|
|
426
|
-
await cleanup();
|
|
427
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
428
|
-
});
|
|
429
|
-
test('Loads modules which contain cycles', async () => {
|
|
430
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_cycle');
|
|
431
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
432
|
-
const declarations = [
|
|
433
|
-
{
|
|
434
|
-
function: 'func1',
|
|
435
|
-
path: '/func1',
|
|
436
|
-
},
|
|
437
|
-
];
|
|
438
|
-
const vendorDirectory = await tmp.dir();
|
|
439
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
440
|
-
basePath,
|
|
441
|
-
vendorDirectory: vendorDirectory.path,
|
|
442
|
-
});
|
|
443
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
444
|
-
const manifest = JSON.parse(manifestFile);
|
|
445
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
446
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
447
|
-
expect(func1).toBe('magix');
|
|
448
|
-
await cleanup();
|
|
449
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
450
|
-
});
|
|
451
|
-
test('Loads npm modules in a monorepo setup', async () => {
|
|
452
|
-
const systemLogger = vi.fn();
|
|
453
|
-
const { basePath: rootPath, cleanup, distPath } = await useFixture('monorepo_npm_module');
|
|
454
|
-
const basePath = join(rootPath, 'packages', 'frontend');
|
|
455
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
456
|
-
const declarations = [
|
|
457
|
-
{
|
|
458
|
-
function: 'func1',
|
|
459
|
-
path: '/func1',
|
|
460
|
-
},
|
|
461
|
-
];
|
|
462
|
-
const vendorDirectory = await tmp.dir();
|
|
463
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
464
|
-
basePath,
|
|
465
|
-
importMapPaths: [join(basePath, 'import_map.json')],
|
|
466
|
-
rootPath,
|
|
467
|
-
vendorDirectory: vendorDirectory.path,
|
|
468
|
-
systemLogger,
|
|
469
|
-
});
|
|
470
|
-
expect(systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:')).toBeUndefined();
|
|
471
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
472
|
-
const manifest = JSON.parse(manifestFile);
|
|
473
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
474
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
475
|
-
expect(func1).toBe(`<parent-1><child-1>JavaScript</child-1></parent-1>, <parent-2><child-2><grandchild-1>APIs<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-2>, <parent-3><child-2><grandchild-1>Markup<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-3>`);
|
|
476
|
-
await cleanup();
|
|
477
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
478
|
-
});
|
|
479
|
-
test('Loads JSON modules with `with` attribute', async () => {
|
|
480
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_json');
|
|
481
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
482
|
-
const declarations = [
|
|
483
|
-
{
|
|
484
|
-
function: 'func1',
|
|
485
|
-
path: '/func1',
|
|
486
|
-
},
|
|
487
|
-
];
|
|
488
|
-
const vendorDirectory = await tmp.dir();
|
|
489
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
490
|
-
basePath,
|
|
491
|
-
vendorDirectory: vendorDirectory.path,
|
|
492
|
-
});
|
|
493
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
494
|
-
const manifest = JSON.parse(manifestFile);
|
|
495
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
496
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
497
|
-
expect(func1).toBe(`{"foo":"bar"}`);
|
|
498
|
-
await cleanup();
|
|
499
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
500
|
-
});
|
|
501
|
-
test('Emits a system log when import assertions are used', async () => {
|
|
502
|
-
const { basePath, cleanup, distPath } = await useFixture('with_import_assert');
|
|
503
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
504
|
-
const vendorDirectory = await tmp.dir();
|
|
505
|
-
const systemLogger = vi.fn();
|
|
506
|
-
await bundle([sourceDirectory], distPath, [], {
|
|
507
|
-
basePath,
|
|
508
|
-
featureFlags: {
|
|
509
|
-
edge_bundler_deno_v2: true,
|
|
510
|
-
},
|
|
511
|
-
systemLogger,
|
|
512
|
-
vendorDirectory: vendorDirectory.path,
|
|
513
|
-
});
|
|
514
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
515
|
-
const manifest = JSON.parse(manifestFile);
|
|
516
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
517
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
518
|
-
expect(func1).toBe(`{"foo":"bar"}`);
|
|
519
|
-
expect(systemLogger).toHaveBeenCalledWith(`Edge function uses import assertions: ${join(sourceDirectory, 'func1.ts')}`);
|
|
520
|
-
expect(manifest.routes[0]).toEqual({
|
|
521
|
-
function: 'func1',
|
|
522
|
-
pattern: '^/with-import-assert/?$',
|
|
523
|
-
excluded_patterns: [],
|
|
524
|
-
path: '/with-import-assert',
|
|
525
|
-
});
|
|
526
|
-
await cleanup();
|
|
527
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
528
|
-
});
|
|
529
|
-
test('Supports TSX and process.env', async () => {
|
|
530
|
-
const { basePath, cleanup, distPath } = await useFixture('tsx');
|
|
531
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
532
|
-
const declarations = [
|
|
533
|
-
{
|
|
534
|
-
function: 'func1',
|
|
535
|
-
path: '/func1',
|
|
536
|
-
},
|
|
537
|
-
];
|
|
538
|
-
const vendorDirectory = await tmp.dir();
|
|
539
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
540
|
-
basePath,
|
|
541
|
-
vendorDirectory: vendorDirectory.path,
|
|
542
|
-
});
|
|
543
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
544
|
-
const manifest = JSON.parse(manifestFile);
|
|
545
|
-
const bundlePath = join(distPath, manifest.bundles[0].asset);
|
|
546
|
-
process.env.FOO = 'bar';
|
|
547
|
-
const { func1 } = await runESZIP(bundlePath, vendorDirectory.path);
|
|
548
|
-
expect(Buffer.from(func1, 'base64').toString()).toBe(`hippedy hoppedy, createElement is now a production property. Here, take this env var: FOO=bar`);
|
|
549
|
-
await cleanup();
|
|
550
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
551
|
-
delete process.env.FOO;
|
|
552
|
-
});
|
|
553
|
-
test('Loads edge functions from the Frameworks API', async () => {
|
|
554
|
-
const { basePath, cleanup, distPath } = await useFixture('with_frameworks_api');
|
|
555
|
-
const directories = [resolve(basePath, 'netlify/edge-functions'), resolve(basePath, '.netlify/v1/edge-functions')];
|
|
556
|
-
const result = await bundle(directories, distPath, [], {
|
|
557
|
-
basePath,
|
|
558
|
-
internalSrcFolder: directories[1],
|
|
559
|
-
importMapPaths: [resolve(basePath, '.netlify/v1/edge-functions/import_map.json')],
|
|
560
|
-
});
|
|
561
|
-
const generatedFiles = await readdir(distPath);
|
|
562
|
-
expect(result.functions.length).toBe(3);
|
|
563
|
-
expect(generatedFiles.length).toBe(2);
|
|
564
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
565
|
-
const manifest = JSON.parse(manifestFile);
|
|
566
|
-
const { bundles, function_config: functionConfig, routes } = manifest;
|
|
567
|
-
expect(bundles.length).toBe(1);
|
|
568
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
569
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
570
|
-
expect(routes[0].excluded_patterns).toEqual(['^/func2/skip/?$']);
|
|
571
|
-
expect(functionConfig.func2).toEqual({
|
|
572
|
-
excluded_patterns: ['^/func2/skip/?$'],
|
|
573
|
-
name: 'Function two',
|
|
574
|
-
generator: '@netlify/fake-plugin@1.0.0',
|
|
575
|
-
});
|
|
576
|
-
expect(functionConfig.func3).toEqual({
|
|
577
|
-
name: 'in-config-function',
|
|
578
|
-
on_error: 'bypass',
|
|
579
|
-
generator: 'internalFunc',
|
|
580
|
-
});
|
|
581
|
-
await cleanup();
|
|
582
|
-
});
|
|
583
|
-
describe.skipIf(lt(denoVersion, '2.4.3'))('Produces a tarball bundle', () => {
|
|
584
|
-
test('With only local imports', async () => {
|
|
585
|
-
const systemLogger = vi.fn();
|
|
586
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin', { copyDirectory: true });
|
|
587
|
-
const declarations = [
|
|
588
|
-
{
|
|
589
|
-
function: 'func1',
|
|
590
|
-
path: '/func1',
|
|
591
|
-
},
|
|
592
|
-
];
|
|
593
|
-
const vendorDirectory = await tmp.dir();
|
|
594
|
-
await bundle([join(basePath, 'netlify/edge-functions')], distPath, declarations, {
|
|
595
|
-
basePath,
|
|
596
|
-
configPath: join(basePath, '.netlify/edge-functions/config.json'),
|
|
597
|
-
featureFlags: {
|
|
598
|
-
edge_bundler_generate_tarball: true,
|
|
599
|
-
},
|
|
600
|
-
systemLogger,
|
|
601
|
-
});
|
|
602
|
-
expect(systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:')).toBeUndefined();
|
|
603
|
-
const expectedOutput = {
|
|
604
|
-
func1: 'ok',
|
|
605
|
-
};
|
|
606
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
607
|
-
const manifest = JSON.parse(manifestFile);
|
|
608
|
-
const tarballPath = join(distPath, manifest.bundles[0].asset);
|
|
609
|
-
const tarballResult = await runTarball(tarballPath);
|
|
610
|
-
expect(tarballResult).toStrictEqual(expectedOutput);
|
|
611
|
-
const entries = [];
|
|
612
|
-
await tar.list({
|
|
613
|
-
file: tarballPath,
|
|
614
|
-
onReadEntry: (entry) => {
|
|
615
|
-
entries.push(entry.path);
|
|
616
|
-
},
|
|
617
|
-
});
|
|
618
|
-
expect(entries).toStrictEqual(['___netlify-edge-functions.json', 'deno.json', 'func1.js']);
|
|
619
|
-
const eszipPath = join(distPath, manifest.bundles[1].asset);
|
|
620
|
-
const eszipResult = await runESZIP(eszipPath);
|
|
621
|
-
expect(eszipResult).toStrictEqual(expectedOutput);
|
|
622
|
-
await cleanup();
|
|
623
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
624
|
-
});
|
|
625
|
-
test('Using npm and remote modules', async () => {
|
|
626
|
-
const systemLogger = vi.fn();
|
|
627
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_npm_module', { copyDirectory: true });
|
|
628
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
629
|
-
const declarations = [
|
|
630
|
-
{
|
|
631
|
-
function: 'func1',
|
|
632
|
-
path: '/func1',
|
|
633
|
-
},
|
|
634
|
-
];
|
|
635
|
-
const vendorDirectory = await tmp.dir();
|
|
636
|
-
await bundle([sourceDirectory], distPath, declarations, {
|
|
637
|
-
basePath,
|
|
638
|
-
featureFlags: {
|
|
639
|
-
edge_bundler_generate_tarball: true,
|
|
640
|
-
},
|
|
641
|
-
importMapPaths: [join(basePath, 'import_map.json')],
|
|
642
|
-
vendorDirectory: vendorDirectory.path,
|
|
643
|
-
systemLogger,
|
|
644
|
-
});
|
|
645
|
-
expect(systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:')).toBeUndefined();
|
|
646
|
-
const expectedOutput = `<parent-1><child-1>JavaScript</child-1></parent-1>, <parent-2><child-2><grandchild-1>APIs<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-2>, <parent-3><child-2><grandchild-1>Markup<cwd>${process.cwd()}</cwd></grandchild-1></child-2></parent-3>, TmV0bGlmeQ==`;
|
|
647
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
648
|
-
const manifest = JSON.parse(manifestFile);
|
|
649
|
-
const tarballPath = join(distPath, manifest.bundles[0].asset);
|
|
650
|
-
const tarballResult = await runTarball(tarballPath);
|
|
651
|
-
expect(tarballResult.func1).toBe(expectedOutput);
|
|
652
|
-
const eszipPath = join(distPath, manifest.bundles[1].asset);
|
|
653
|
-
const eszipResult = await runESZIP(eszipPath, vendorDirectory.path);
|
|
654
|
-
expect(eszipResult.func1).toBe(expectedOutput);
|
|
655
|
-
await cleanup();
|
|
656
|
-
await rm(vendorDirectory.path, { force: true, recursive: true });
|
|
657
|
-
});
|
|
658
|
-
describe('Dry-run tarball generation flag enabled', () => {
|
|
659
|
-
test('Logs success message when tarball generation succeeded', async () => {
|
|
660
|
-
const systemLogger = vi.fn();
|
|
661
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin', { copyDirectory: true });
|
|
662
|
-
const declarations = [
|
|
663
|
-
{
|
|
664
|
-
function: 'func1',
|
|
665
|
-
path: '/func1',
|
|
666
|
-
},
|
|
667
|
-
];
|
|
668
|
-
await bundle([join(basePath, 'netlify/edge-functions')], distPath, declarations, {
|
|
669
|
-
basePath,
|
|
670
|
-
configPath: join(basePath, '.netlify/edge-functions/config.json'),
|
|
671
|
-
featureFlags: {
|
|
672
|
-
edge_bundler_dry_run_generate_tarball: true,
|
|
673
|
-
edge_bundler_generate_tarball: false,
|
|
674
|
-
},
|
|
675
|
-
systemLogger,
|
|
676
|
-
});
|
|
677
|
-
expect(systemLogger).toHaveBeenCalledWith('Dry run: Tarball bundle generated successfully.');
|
|
678
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
679
|
-
const manifest = JSON.parse(manifestFile);
|
|
680
|
-
expect(manifest.bundles.length).toBe(1);
|
|
681
|
-
expect(manifest.bundles[0].format).toBe('eszip2');
|
|
682
|
-
await cleanup();
|
|
683
|
-
});
|
|
684
|
-
test('Logs error message when tarball generation failed and does not fail the overall build', async () => {
|
|
685
|
-
const systemLogger = vi.fn();
|
|
686
|
-
vi.resetModules();
|
|
687
|
-
vi.doMock('./formats/tarball.js', () => ({
|
|
688
|
-
bundle: vi.fn().mockRejectedValue(new Error('Simulated tarball bundling failure')),
|
|
689
|
-
}));
|
|
690
|
-
const { bundle: bundleUnderTest } = await import('./bundler.js');
|
|
691
|
-
const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin', { copyDirectory: true });
|
|
692
|
-
const sourceDirectory = join(basePath, 'functions');
|
|
693
|
-
const declarations = [
|
|
694
|
-
{
|
|
695
|
-
function: 'func1',
|
|
696
|
-
path: '/func1',
|
|
697
|
-
},
|
|
698
|
-
];
|
|
699
|
-
await expect(bundleUnderTest([sourceDirectory], distPath, declarations, {
|
|
700
|
-
basePath,
|
|
701
|
-
configPath: join(sourceDirectory, 'config.json'),
|
|
702
|
-
featureFlags: {
|
|
703
|
-
edge_bundler_dry_run_generate_tarball: true,
|
|
704
|
-
edge_bundler_generate_tarball: false,
|
|
705
|
-
},
|
|
706
|
-
systemLogger,
|
|
707
|
-
})).resolves.toBeDefined();
|
|
708
|
-
expect(systemLogger).toHaveBeenCalledWith(`Dry run: Tarball bundle generation failed: Simulated tarball bundling failure`);
|
|
709
|
-
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
710
|
-
const manifest = JSON.parse(manifestFile);
|
|
711
|
-
expect(manifest.bundles.length).toBe(1);
|
|
712
|
-
expect(manifest.bundles[0].format).toBe('eszip2');
|
|
713
|
-
await cleanup();
|
|
714
|
-
vi.resetModules();
|
|
715
|
-
});
|
|
716
|
-
});
|
|
717
|
-
}, 10_000);
|