@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
package/dist/node/config.test.js
DELETED
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { rm } from 'fs/promises';
|
|
3
|
-
import { join, resolve } from 'path';
|
|
4
|
-
import { pathToFileURL } from 'url';
|
|
5
|
-
import tmp from 'tmp-promise';
|
|
6
|
-
import { test, expect, vi, describe } from 'vitest';
|
|
7
|
-
import { fixturesDir, useFixture } from '../test/util.js';
|
|
8
|
-
import { DenoBridge } from './bridge.js';
|
|
9
|
-
import { bundle } from './bundler.js';
|
|
10
|
-
import { getFunctionConfig } from './config.js';
|
|
11
|
-
import { ImportMap } from './import_map.js';
|
|
12
|
-
import { RateLimitAction, RateLimitAggregator } from './rate_limit.js';
|
|
13
|
-
const importMapFile = {
|
|
14
|
-
baseURL: new URL('file:///some/path/import-map.json'),
|
|
15
|
-
imports: {
|
|
16
|
-
'alias:helper': pathToFileURL(join(fixturesDir, 'helper.ts')).toString(),
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
const invalidDefaultExportErr = (path) => `Default export in '${path}' must be a function. More on the Edge Functions API at https://ntl.fyi/edge-api.`;
|
|
20
|
-
const functions = [
|
|
21
|
-
{
|
|
22
|
-
testName: 'no config',
|
|
23
|
-
expectedConfig: {},
|
|
24
|
-
name: 'func1',
|
|
25
|
-
source: `export default async () => new Response("Hello from function one")`,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
testName: 'empty config',
|
|
29
|
-
expectedConfig: {},
|
|
30
|
-
name: 'func2',
|
|
31
|
-
source: `
|
|
32
|
-
export default async () => new Response("Hello from function two")
|
|
33
|
-
|
|
34
|
-
export const config = {}
|
|
35
|
-
`,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
testName: 'config with wrong type',
|
|
39
|
-
name: 'func3',
|
|
40
|
-
source: `
|
|
41
|
-
export default async () => new Response("Hello from function two")
|
|
42
|
-
|
|
43
|
-
export const config = () => ({})
|
|
44
|
-
`,
|
|
45
|
-
error: /^The 'config' export in edge function at '(.*)' must be an object\. More on the Edge Functions API at https:\/\/ntl\.fyi\/edge-api\.$/,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
testName: 'config with syntax error',
|
|
49
|
-
name: 'func4',
|
|
50
|
-
source: `
|
|
51
|
-
export default async () => new Response("Hello from function two")
|
|
52
|
-
|
|
53
|
-
export const config
|
|
54
|
-
`,
|
|
55
|
-
error: /^Could not load edge function at '(.*)'\. More on the Edge Functions API at https:\/\/ntl\.fyi\/edge-api\.$/,
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
testName: 'config with correct onError',
|
|
59
|
-
expectedConfig: { onError: 'bypass' },
|
|
60
|
-
name: 'func5',
|
|
61
|
-
source: `
|
|
62
|
-
export default async () => new Response("Hello from function two")
|
|
63
|
-
export const config = { onError: "bypass" }
|
|
64
|
-
`,
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
testName: 'config with wrong onError',
|
|
68
|
-
name: 'func6',
|
|
69
|
-
source: `
|
|
70
|
-
export default async () => new Response("Hello from function two")
|
|
71
|
-
export const config = { onError: "foo" }
|
|
72
|
-
`,
|
|
73
|
-
error: /The 'onError' configuration property in edge function at .*/,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
testName: 'config with `path`',
|
|
77
|
-
expectedConfig: { path: '/home' },
|
|
78
|
-
name: 'func7',
|
|
79
|
-
source: `
|
|
80
|
-
export default async () => new Response("Hello from function three")
|
|
81
|
-
|
|
82
|
-
export const config = { path: "/home" }
|
|
83
|
-
`,
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
testName: 'config with path, generator, name and onError`',
|
|
87
|
-
expectedConfig: {
|
|
88
|
-
path: '/home',
|
|
89
|
-
generator: '@netlify/fake-plugin@1.0.0',
|
|
90
|
-
name: 'a displayName',
|
|
91
|
-
onError: 'bypass',
|
|
92
|
-
},
|
|
93
|
-
name: 'func8',
|
|
94
|
-
source: `
|
|
95
|
-
export default async () => new Response("Hello from function three")
|
|
96
|
-
|
|
97
|
-
export const config = {
|
|
98
|
-
path: "/home",
|
|
99
|
-
generator: '@netlify/fake-plugin@1.0.0',
|
|
100
|
-
name: 'a displayName',
|
|
101
|
-
onError: 'bypass',
|
|
102
|
-
}
|
|
103
|
-
`,
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
testName: 'config with ratelimit',
|
|
107
|
-
expectedConfig: {
|
|
108
|
-
path: '/ratelimit',
|
|
109
|
-
name: 'a limit rate',
|
|
110
|
-
rateLimit: {
|
|
111
|
-
windowSize: 10,
|
|
112
|
-
windowLimit: 100,
|
|
113
|
-
aggregateBy: [RateLimitAggregator.IP, RateLimitAggregator.Domain],
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
name: 'func9',
|
|
117
|
-
source: `
|
|
118
|
-
export default async () => new Response("Rate my limits")
|
|
119
|
-
|
|
120
|
-
export const config = {
|
|
121
|
-
path: "/ratelimit",
|
|
122
|
-
rateLimit: {
|
|
123
|
-
windowSize: 10,
|
|
124
|
-
windowLimit: 100,
|
|
125
|
-
aggregateBy: ["ip", "domain"],
|
|
126
|
-
},
|
|
127
|
-
name: 'a limit rate',
|
|
128
|
-
}
|
|
129
|
-
`,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
testName: 'config with rewrite',
|
|
133
|
-
expectedConfig: {
|
|
134
|
-
path: '/rewrite',
|
|
135
|
-
name: 'a limit rewrite',
|
|
136
|
-
rateLimit: {
|
|
137
|
-
action: RateLimitAction.Rewrite,
|
|
138
|
-
to: '/rewritten',
|
|
139
|
-
windowSize: 20,
|
|
140
|
-
windowLimit: 200,
|
|
141
|
-
aggregateBy: [RateLimitAggregator.IP, RateLimitAggregator.Domain],
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
name: 'func9',
|
|
145
|
-
source: `
|
|
146
|
-
export default async () => new Response("Rate my limits")
|
|
147
|
-
|
|
148
|
-
export const config = {
|
|
149
|
-
path: "/rewrite",
|
|
150
|
-
rateLimit: {
|
|
151
|
-
action: "rewrite",
|
|
152
|
-
to: "/rewritten",
|
|
153
|
-
windowSize: 20,
|
|
154
|
-
windowLimit: 200,
|
|
155
|
-
aggregateBy: ["ip", "domain"],
|
|
156
|
-
},
|
|
157
|
-
name: 'a limit rewrite',
|
|
158
|
-
}
|
|
159
|
-
`,
|
|
160
|
-
},
|
|
161
|
-
];
|
|
162
|
-
describe('`getFunctionConfig` extracts configuration properties from function file', () => {
|
|
163
|
-
test.each(functions)('$testName', async (func) => {
|
|
164
|
-
const { path: tmpDir } = await tmp.dir();
|
|
165
|
-
const deno = new DenoBridge({
|
|
166
|
-
cacheDirectory: tmpDir,
|
|
167
|
-
});
|
|
168
|
-
const logger = {
|
|
169
|
-
user: vi.fn().mockResolvedValue(null),
|
|
170
|
-
system: vi.fn().mockResolvedValue(null),
|
|
171
|
-
};
|
|
172
|
-
const path = join(tmpDir, `${func.name}.js`);
|
|
173
|
-
await fs.writeFile(path, func.source);
|
|
174
|
-
const funcCall = () => getFunctionConfig({
|
|
175
|
-
functionPath: path,
|
|
176
|
-
importMap: new ImportMap([importMapFile]),
|
|
177
|
-
deno,
|
|
178
|
-
log: logger,
|
|
179
|
-
});
|
|
180
|
-
if (func.error) {
|
|
181
|
-
await expect(funcCall()).rejects.toThrowError(func.error);
|
|
182
|
-
}
|
|
183
|
-
else if (func.userLog) {
|
|
184
|
-
await expect(funcCall()).resolves.not.toThrowError();
|
|
185
|
-
expect(logger.user).toHaveBeenCalledWith(expect.stringMatching(func.userLog));
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
const config = await funcCall();
|
|
189
|
-
expect(config).toEqual(func.expectedConfig);
|
|
190
|
-
expect(logger.user).not.toHaveBeenCalled();
|
|
191
|
-
}
|
|
192
|
-
await rm(tmpDir, { force: true, recursive: true, maxRetries: 10 });
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
test('Loads function paths from the in-source `config` function', async () => {
|
|
196
|
-
const { basePath, cleanup, distPath } = await useFixture('with_config');
|
|
197
|
-
const userDirectory = resolve(basePath, 'netlify', 'edge-functions');
|
|
198
|
-
const internalDirectory = resolve(basePath, '.netlify', 'edge-functions');
|
|
199
|
-
const declarations = [
|
|
200
|
-
{
|
|
201
|
-
function: 'framework-func2',
|
|
202
|
-
path: '/framework-func2',
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
function: 'user-func2',
|
|
206
|
-
path: '/user-func2',
|
|
207
|
-
method: ['PATCH'],
|
|
208
|
-
},
|
|
209
|
-
];
|
|
210
|
-
const result = await bundle([internalDirectory, userDirectory], distPath, declarations, {
|
|
211
|
-
basePath,
|
|
212
|
-
configPath: join(internalDirectory, 'config.json'),
|
|
213
|
-
});
|
|
214
|
-
const generatedFiles = await fs.readdir(distPath);
|
|
215
|
-
expect(result.functions.length).toBe(11);
|
|
216
|
-
expect(generatedFiles.length).toBe(2);
|
|
217
|
-
const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
218
|
-
const manifest = JSON.parse(manifestFile);
|
|
219
|
-
const { bundles, routes, post_cache_routes: postCacheRoutes, function_config: functionConfig } = manifest;
|
|
220
|
-
expect(bundles.length).toBe(1);
|
|
221
|
-
expect(bundles[0].format).toBe('eszip2');
|
|
222
|
-
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
223
|
-
expect(routes.length).toBe(12);
|
|
224
|
-
expect(routes[0]).toEqual({
|
|
225
|
-
function: 'framework-func2',
|
|
226
|
-
pattern: '^/framework-func2/?$',
|
|
227
|
-
excluded_patterns: [],
|
|
228
|
-
path: '/framework-func2',
|
|
229
|
-
});
|
|
230
|
-
expect(routes[1]).toEqual({
|
|
231
|
-
function: 'user-func2',
|
|
232
|
-
pattern: '^/user-func2/?$',
|
|
233
|
-
excluded_patterns: [],
|
|
234
|
-
path: '/user-func2',
|
|
235
|
-
methods: ['PATCH'],
|
|
236
|
-
});
|
|
237
|
-
expect(routes[2]).toEqual({
|
|
238
|
-
function: 'framework-func1',
|
|
239
|
-
pattern: '^/framework-func1/?$',
|
|
240
|
-
excluded_patterns: [],
|
|
241
|
-
path: '/framework-func1',
|
|
242
|
-
});
|
|
243
|
-
expect(routes[3]).toEqual({
|
|
244
|
-
function: 'framework-func3',
|
|
245
|
-
pattern: '^/framework-func3(/.*)?$',
|
|
246
|
-
excluded_patterns: ['^/framework-func3/excluded$'],
|
|
247
|
-
});
|
|
248
|
-
expect(routes[4]).toEqual({
|
|
249
|
-
function: 'framework-func4',
|
|
250
|
-
pattern: '^/framework-func4(/.*)?$',
|
|
251
|
-
excluded_patterns: ['^/framework-func4/excluded$', '^/framework-func4-alt/excluded$'],
|
|
252
|
-
});
|
|
253
|
-
expect(routes[5]).toEqual({
|
|
254
|
-
function: 'framework-func4',
|
|
255
|
-
pattern: '^/framework-func4-alt(/.*)?$',
|
|
256
|
-
excluded_patterns: ['^/framework-func4/excluded$', '^/framework-func4-alt/excluded$'],
|
|
257
|
-
});
|
|
258
|
-
expect(routes[6]).toEqual({
|
|
259
|
-
function: 'user-func1',
|
|
260
|
-
pattern: '^/user-func1/?$',
|
|
261
|
-
excluded_patterns: [],
|
|
262
|
-
path: '/user-func1',
|
|
263
|
-
headers: {
|
|
264
|
-
'x-must-be-there': {
|
|
265
|
-
matcher: 'exists',
|
|
266
|
-
},
|
|
267
|
-
'x-must-match': {
|
|
268
|
-
pattern: '^(foo|bar)$',
|
|
269
|
-
matcher: 'regex',
|
|
270
|
-
},
|
|
271
|
-
'x-must-not-be-there': {
|
|
272
|
-
matcher: 'missing',
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
});
|
|
276
|
-
expect(routes[7]).toEqual({
|
|
277
|
-
function: 'user-func3',
|
|
278
|
-
pattern: '^/user-func3/?$',
|
|
279
|
-
excluded_patterns: [],
|
|
280
|
-
path: '/user-func3',
|
|
281
|
-
});
|
|
282
|
-
expect(routes[8]).toEqual({
|
|
283
|
-
function: 'user-func5',
|
|
284
|
-
pattern: '^/user-func5(?:/(.*))/?$',
|
|
285
|
-
excluded_patterns: ['^/user-func5/excluded/?$'],
|
|
286
|
-
path: '/user-func5/*',
|
|
287
|
-
methods: ['GET'],
|
|
288
|
-
});
|
|
289
|
-
expect(routes[9]).toEqual({
|
|
290
|
-
function: 'user-func6',
|
|
291
|
-
pattern: '^/user-func6(/.*)?$',
|
|
292
|
-
excluded_patterns: ['^/user-func6/excluded$'],
|
|
293
|
-
});
|
|
294
|
-
expect(routes[10]).toEqual({
|
|
295
|
-
function: 'user-func7',
|
|
296
|
-
pattern: '^/user-func7(/.*)?$',
|
|
297
|
-
excluded_patterns: ['^/user-func7/excluded$', '^/user-func7-alt/excluded$'],
|
|
298
|
-
});
|
|
299
|
-
expect(routes[11]).toEqual({
|
|
300
|
-
function: 'user-func7',
|
|
301
|
-
pattern: '^/user-func7-alt(/.*)?$',
|
|
302
|
-
excluded_patterns: ['^/user-func7/excluded$', '^/user-func7-alt/excluded$'],
|
|
303
|
-
});
|
|
304
|
-
expect(postCacheRoutes.length).toBe(1);
|
|
305
|
-
expect(postCacheRoutes[0]).toEqual({
|
|
306
|
-
function: 'user-func4',
|
|
307
|
-
pattern: '^/user-func4/?$',
|
|
308
|
-
excluded_patterns: [],
|
|
309
|
-
path: '/user-func4',
|
|
310
|
-
methods: ['POST', 'PUT'],
|
|
311
|
-
});
|
|
312
|
-
expect(Object.keys(functionConfig)).toHaveLength(5);
|
|
313
|
-
expect(functionConfig['user-func5']).toEqual({
|
|
314
|
-
excluded_patterns: ['^/user-func5/excluded/?$'],
|
|
315
|
-
});
|
|
316
|
-
expect(functionConfig['user-func6']).toEqual({
|
|
317
|
-
excluded_patterns: ['^/user-func6/excluded$'],
|
|
318
|
-
});
|
|
319
|
-
expect(functionConfig['user-func7']).toEqual({
|
|
320
|
-
excluded_patterns: ['^/user-func7/excluded$', '^/user-func7-alt/excluded$'],
|
|
321
|
-
});
|
|
322
|
-
expect(functionConfig['framework-func3']).toEqual({
|
|
323
|
-
excluded_patterns: ['^/framework-func3/excluded$'],
|
|
324
|
-
});
|
|
325
|
-
expect(functionConfig['framework-func4']).toEqual({
|
|
326
|
-
excluded_patterns: ['^/framework-func4/excluded$', '^/framework-func4-alt/excluded$'],
|
|
327
|
-
});
|
|
328
|
-
await cleanup();
|
|
329
|
-
});
|
|
330
|
-
test('Passes validation if default export exists and is a function', async () => {
|
|
331
|
-
const { path: tmpDir } = await tmp.dir();
|
|
332
|
-
const deno = new DenoBridge({
|
|
333
|
-
cacheDirectory: tmpDir,
|
|
334
|
-
});
|
|
335
|
-
const func = {
|
|
336
|
-
name: 'func1',
|
|
337
|
-
source: `
|
|
338
|
-
const func = () => new Response("Hello world!")
|
|
339
|
-
export default func
|
|
340
|
-
`,
|
|
341
|
-
};
|
|
342
|
-
const logger = {
|
|
343
|
-
user: vi.fn().mockResolvedValue(null),
|
|
344
|
-
system: vi.fn().mockResolvedValue(null),
|
|
345
|
-
};
|
|
346
|
-
const path = join(tmpDir, `${func.name}.ts`);
|
|
347
|
-
await fs.writeFile(path, func.source);
|
|
348
|
-
await expect(getFunctionConfig({
|
|
349
|
-
functionPath: path,
|
|
350
|
-
importMap: new ImportMap([importMapFile]),
|
|
351
|
-
deno,
|
|
352
|
-
log: logger,
|
|
353
|
-
})).resolves.not.toThrow();
|
|
354
|
-
await rm(tmpDir, { force: true, recursive: true, maxRetries: 10 });
|
|
355
|
-
});
|
|
356
|
-
test('Fails validation if default export is not function', async () => {
|
|
357
|
-
const { path: tmpDir } = await tmp.dir();
|
|
358
|
-
const deno = new DenoBridge({
|
|
359
|
-
cacheDirectory: tmpDir,
|
|
360
|
-
});
|
|
361
|
-
const func = {
|
|
362
|
-
name: 'func2',
|
|
363
|
-
source: `
|
|
364
|
-
const func = new Response("Hello world!")
|
|
365
|
-
export default func
|
|
366
|
-
`,
|
|
367
|
-
};
|
|
368
|
-
const logger = {
|
|
369
|
-
user: vi.fn().mockResolvedValue(null),
|
|
370
|
-
system: vi.fn().mockResolvedValue(null),
|
|
371
|
-
};
|
|
372
|
-
const path = join(tmpDir, `${func.name}.ts`);
|
|
373
|
-
await fs.writeFile(path, func.source);
|
|
374
|
-
const config = getFunctionConfig({
|
|
375
|
-
functionPath: path,
|
|
376
|
-
importMap: new ImportMap([importMapFile]),
|
|
377
|
-
deno,
|
|
378
|
-
log: logger,
|
|
379
|
-
});
|
|
380
|
-
await expect(config).rejects.toThrowError(invalidDefaultExportErr(path));
|
|
381
|
-
await rm(tmpDir, { force: true, recursive: true, maxRetries: 10 });
|
|
382
|
-
});
|
|
383
|
-
test('Fails validation if default export is not present', async () => {
|
|
384
|
-
const { path: tmpDir } = await tmp.dir();
|
|
385
|
-
const deno = new DenoBridge({
|
|
386
|
-
cacheDirectory: tmpDir,
|
|
387
|
-
});
|
|
388
|
-
const func = {
|
|
389
|
-
name: 'func3',
|
|
390
|
-
source: `
|
|
391
|
-
export const func = () => new Response("Hello world!")
|
|
392
|
-
`,
|
|
393
|
-
};
|
|
394
|
-
const logger = {
|
|
395
|
-
user: vi.fn().mockResolvedValue(null),
|
|
396
|
-
system: vi.fn().mockResolvedValue(null),
|
|
397
|
-
};
|
|
398
|
-
const path = join(tmpDir, `${func.name}.ts`);
|
|
399
|
-
await fs.writeFile(path, func.source);
|
|
400
|
-
const config = getFunctionConfig({
|
|
401
|
-
functionPath: path,
|
|
402
|
-
importMap: new ImportMap([importMapFile]),
|
|
403
|
-
deno,
|
|
404
|
-
log: logger,
|
|
405
|
-
});
|
|
406
|
-
await expect(config).rejects.toThrowError(invalidDefaultExportErr(path));
|
|
407
|
-
await rm(tmpDir, { force: true, recursive: true, maxRetries: 10 });
|
|
408
|
-
});
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
2
|
-
import { mergeDeclarations, normalizePattern } from './declaration.js';
|
|
3
|
-
const deployConfigDeclarations = [];
|
|
4
|
-
test('Ensure the order of edge functions with FF', () => {
|
|
5
|
-
const deployConfigDeclarations = [
|
|
6
|
-
{ function: 'framework-manifest-a', path: '/path1' },
|
|
7
|
-
{ function: 'framework-manifest-c', path: '/path3' },
|
|
8
|
-
{ function: 'framework-manifest-b', path: '/path2' },
|
|
9
|
-
];
|
|
10
|
-
const tomlConfig = [
|
|
11
|
-
{ function: 'user-toml-a', path: '/path1' },
|
|
12
|
-
{ function: 'user-toml-c', path: '/path3' },
|
|
13
|
-
{ function: 'user-toml-b', path: '/path2' },
|
|
14
|
-
];
|
|
15
|
-
const userFuncConfig = {
|
|
16
|
-
'user-isc-c': { path: ['/path1', '/path2'] },
|
|
17
|
-
};
|
|
18
|
-
const internalFuncConfig = {
|
|
19
|
-
'framework-isc-c': { path: ['/path1', '/path2'] },
|
|
20
|
-
};
|
|
21
|
-
expect(mergeDeclarations(tomlConfig, userFuncConfig, internalFuncConfig, deployConfigDeclarations)).toMatchSnapshot();
|
|
22
|
-
});
|
|
23
|
-
test('In-source config takes precedence over netlify.toml config', () => {
|
|
24
|
-
const tomlConfig = [
|
|
25
|
-
{ function: 'geolocation', path: '/geo', cache: 'off' },
|
|
26
|
-
{ function: 'json', path: '/json', cache: 'manual' },
|
|
27
|
-
];
|
|
28
|
-
const userFuncConfig = {
|
|
29
|
-
geolocation: { path: ['/geo-isc', '/*'], cache: 'manual' },
|
|
30
|
-
json: { path: '/json', cache: 'off' },
|
|
31
|
-
};
|
|
32
|
-
const expectedDeclarations = [
|
|
33
|
-
{ function: 'geolocation', path: '/geo-isc', cache: 'manual' },
|
|
34
|
-
{ function: 'geolocation', path: '/*', cache: 'manual' },
|
|
35
|
-
{ function: 'json', path: '/json', cache: 'off' },
|
|
36
|
-
];
|
|
37
|
-
const declarations = mergeDeclarations(tomlConfig, userFuncConfig, {}, deployConfigDeclarations);
|
|
38
|
-
expect(declarations).toEqual(expectedDeclarations);
|
|
39
|
-
});
|
|
40
|
-
test("Declarations don't break if no in-source config is provided", () => {
|
|
41
|
-
const tomlConfig = [
|
|
42
|
-
{ function: 'geolocation', path: '/geo', cache: 'off' },
|
|
43
|
-
{ function: 'json', path: '/json', cache: 'manual' },
|
|
44
|
-
];
|
|
45
|
-
const userFuncConfig = {
|
|
46
|
-
geolocation: { path: ['/geo-isc'], cache: 'manual' },
|
|
47
|
-
json: {},
|
|
48
|
-
};
|
|
49
|
-
const expectedDeclarations = [
|
|
50
|
-
{ function: 'geolocation', path: '/geo-isc', cache: 'manual' },
|
|
51
|
-
{ function: 'json', path: '/json', cache: 'manual' },
|
|
52
|
-
];
|
|
53
|
-
const declarations = mergeDeclarations(tomlConfig, userFuncConfig, {}, deployConfigDeclarations);
|
|
54
|
-
expect(declarations).toEqual(expectedDeclarations);
|
|
55
|
-
});
|
|
56
|
-
test('In-source config works independent of the netlify.toml file if a path is defined and otherwise if no path is set', () => {
|
|
57
|
-
const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }];
|
|
58
|
-
const funcConfigWithPath = {
|
|
59
|
-
json: { path: ['/json', '/json-isc'], cache: 'off' },
|
|
60
|
-
};
|
|
61
|
-
const funcConfigWithoutPath = {
|
|
62
|
-
json: { cache: 'off' },
|
|
63
|
-
};
|
|
64
|
-
const expectedDeclarationsWithISCPath = [
|
|
65
|
-
{ function: 'geolocation', path: '/geo', cache: 'off' },
|
|
66
|
-
{ function: 'json', path: '/json', cache: 'off' },
|
|
67
|
-
{ function: 'json', path: '/json-isc', cache: 'off' },
|
|
68
|
-
];
|
|
69
|
-
const expectedDeclarationsWithoutISCPath = [{ function: 'geolocation', path: '/geo', cache: 'off' }];
|
|
70
|
-
const declarationsWithISCPath = mergeDeclarations(tomlConfig, funcConfigWithPath, {}, deployConfigDeclarations);
|
|
71
|
-
expect(declarationsWithISCPath).toEqual(expectedDeclarationsWithISCPath);
|
|
72
|
-
const declarationsWithoutISCPath = mergeDeclarations(tomlConfig, funcConfigWithoutPath, {}, deployConfigDeclarations);
|
|
73
|
-
expect(declarationsWithoutISCPath).toEqual(expectedDeclarationsWithoutISCPath);
|
|
74
|
-
});
|
|
75
|
-
test('In-source config works if only the cache config property is set', () => {
|
|
76
|
-
const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }];
|
|
77
|
-
const funcConfig = {
|
|
78
|
-
geolocation: { cache: 'manual' },
|
|
79
|
-
};
|
|
80
|
-
const expectedDeclarations = [{ function: 'geolocation', path: '/geo', cache: 'manual' }];
|
|
81
|
-
expect(mergeDeclarations(tomlConfig, funcConfig, {}, deployConfigDeclarations)).toEqual(expectedDeclarations);
|
|
82
|
-
});
|
|
83
|
-
test("In-source config path property works if it's not an array", () => {
|
|
84
|
-
const tomlConfig = [{ function: 'json', path: '/json-toml', cache: 'off' }];
|
|
85
|
-
const funcConfig = {
|
|
86
|
-
json: { path: '/json', cache: 'manual' },
|
|
87
|
-
};
|
|
88
|
-
const expectedDeclarations = [{ function: 'json', path: '/json', cache: 'manual' }];
|
|
89
|
-
expect(mergeDeclarations(tomlConfig, funcConfig, {}, deployConfigDeclarations)).toEqual(expectedDeclarations);
|
|
90
|
-
});
|
|
91
|
-
test("In-source config path property works if it's not an array and it's not present in toml or deploy config", () => {
|
|
92
|
-
const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }];
|
|
93
|
-
const funcConfig = {
|
|
94
|
-
json: { path: '/json-isc', cache: 'manual' },
|
|
95
|
-
};
|
|
96
|
-
const expectedDeclarations = [
|
|
97
|
-
{ function: 'geolocation', path: '/geo', cache: 'off' },
|
|
98
|
-
{ function: 'json', path: '/json-isc', cache: 'manual' },
|
|
99
|
-
];
|
|
100
|
-
expect(mergeDeclarations(tomlConfig, funcConfig, {}, deployConfigDeclarations)).toEqual(expectedDeclarations);
|
|
101
|
-
});
|
|
102
|
-
test('In-source config works if path property is an empty array with cache value specified', () => {
|
|
103
|
-
const tomlConfig = [{ function: 'json', path: '/json-toml', cache: 'off' }];
|
|
104
|
-
const funcConfig = {
|
|
105
|
-
json: { path: [], cache: 'manual' },
|
|
106
|
-
};
|
|
107
|
-
const expectedDeclarations = [{ function: 'json', path: '/json-toml', cache: 'manual' }];
|
|
108
|
-
expect(mergeDeclarations(tomlConfig, funcConfig, {}, deployConfigDeclarations)).toEqual(expectedDeclarations);
|
|
109
|
-
});
|
|
110
|
-
test('netlify.toml-defined excludedPath are respected', () => {
|
|
111
|
-
const tomlConfig = [{ function: 'geolocation', path: '/geo/*', excludedPath: '/geo/exclude' }];
|
|
112
|
-
const funcConfig = {};
|
|
113
|
-
const expectedDeclarations = [{ function: 'geolocation', path: '/geo/*', excludedPath: '/geo/exclude' }];
|
|
114
|
-
const declarations = mergeDeclarations(tomlConfig, funcConfig, {}, deployConfigDeclarations);
|
|
115
|
-
expect(declarations).toEqual(expectedDeclarations);
|
|
116
|
-
});
|
|
117
|
-
test('Does not escape front slashes in a regex pattern if they are already escaped', () => {
|
|
118
|
-
const regexPattern = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[\\/#\\?]?$';
|
|
119
|
-
const expected = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[\\/#\\?]?$';
|
|
120
|
-
expect(normalizePattern(regexPattern)).toEqual(expected);
|
|
121
|
-
});
|
|
122
|
-
test('Escapes front slashes in a regex pattern', () => {
|
|
123
|
-
const regexPattern = '^(?:/(_next/data/[^/]{1,}))?(?:/([^/.]{1,}))/shows(?:/(.*))(.json)?[/#\\?]?$';
|
|
124
|
-
const expected = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/(.*))(.json)?[/#\\?]?$';
|
|
125
|
-
expect(normalizePattern(regexPattern)).toEqual(expected);
|
|
126
|
-
});
|
|
127
|
-
test('Ensures pattern match on the whole path', () => {
|
|
128
|
-
const regexPattern = '/foo/.*/bar';
|
|
129
|
-
const expected = '^\\/foo\\/.*\\/bar$';
|
|
130
|
-
expect(normalizePattern(regexPattern)).toEqual(expected);
|
|
131
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { cwd } from 'process';
|
|
4
|
-
import tmp from 'tmp-promise';
|
|
5
|
-
import { test, expect } from 'vitest';
|
|
6
|
-
import { load } from './deploy_config.js';
|
|
7
|
-
import { getLogger } from './logger.js';
|
|
8
|
-
const logger = getLogger(console.log);
|
|
9
|
-
test('Returns an empty config object if there is no file at the given path', async () => {
|
|
10
|
-
const mockPath = join(cwd(), 'some-directory', `a-file-that-does-not-exist-${Date.now()}.json`);
|
|
11
|
-
const config = await load(mockPath, logger);
|
|
12
|
-
expect(config.declarations).toEqual([]);
|
|
13
|
-
expect(config.layers).toEqual([]);
|
|
14
|
-
});
|
|
15
|
-
test('Returns a config object with declarations, layers, and import map', async () => {
|
|
16
|
-
const importMapFile = await tmp.file({ postfix: '.json' });
|
|
17
|
-
const importMap = {
|
|
18
|
-
imports: {
|
|
19
|
-
'https://deno.land/': 'https://black.hole/',
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
await fs.writeFile(importMapFile.path, JSON.stringify(importMap));
|
|
23
|
-
const configFile = await tmp.file();
|
|
24
|
-
const config = {
|
|
25
|
-
functions: [
|
|
26
|
-
{
|
|
27
|
-
function: 'func1',
|
|
28
|
-
path: '/func1',
|
|
29
|
-
generator: 'internalFunc',
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
layers: [
|
|
33
|
-
{
|
|
34
|
-
name: 'layer1',
|
|
35
|
-
flag: 'edge_functions_layer1_url',
|
|
36
|
-
local: 'https://some-url.netlify.app/mod.ts',
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
import_map: importMapFile.path,
|
|
40
|
-
version: 1,
|
|
41
|
-
};
|
|
42
|
-
await fs.writeFile(configFile.path, JSON.stringify(config));
|
|
43
|
-
const parsedConfig = await load(configFile.path, logger);
|
|
44
|
-
await importMapFile.cleanup();
|
|
45
|
-
expect(parsedConfig.declarations).toEqual(config.functions);
|
|
46
|
-
expect(parsedConfig.layers).toEqual(config.layers);
|
|
47
|
-
expect(parsedConfig.importMap).toBe(importMapFile.path);
|
|
48
|
-
});
|