@netlify/edge-bundler 6.1.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/bundler.d.ts +2 -1
- package/dist/node/bundler.js +3 -16
- package/dist/node/bundler.test.js +47 -8
- package/dist/node/deploy_config.d.ts +1 -2
- package/dist/node/deploy_config.js +4 -9
- package/dist/node/deploy_config.test.js +3 -6
- package/dist/node/feature_flags.js +0 -1
- package/dist/node/import_map.d.ts +18 -11
- package/dist/node/import_map.js +75 -32
- package/dist/node/server/server.d.ts +2 -4
- package/dist/node/server/server.js +4 -14
- package/dist/node/server/server.test.js +9 -16
- package/dist/node/utils/error.d.ts +1 -0
- package/dist/node/utils/error.js +2 -0
- package/dist/test/util.d.ts +2 -1
- package/dist/test/util.js +51 -5
- package/package.json +1 -1
- package/dist/node/deno_config.d.ts +0 -6
- package/dist/node/deno_config.js +0 -43
- package/dist/node/deno_config.test.d.ts +0 -1
- package/dist/node/deno_config.test.js +0 -92
package/dist/node/bundler.d.ts
CHANGED
|
@@ -9,11 +9,12 @@ interface BundleOptions {
|
|
|
9
9
|
debug?: boolean;
|
|
10
10
|
distImportMapPath?: string;
|
|
11
11
|
featureFlags?: FeatureFlags;
|
|
12
|
+
importMapPaths?: (string | undefined)[];
|
|
12
13
|
onAfterDownload?: OnAfterDownloadHook;
|
|
13
14
|
onBeforeDownload?: OnBeforeDownloadHook;
|
|
14
15
|
systemLogger?: LogFunction;
|
|
15
16
|
}
|
|
16
|
-
declare const bundle: (sourceDirectories: string[], distDirectory: string, tomlDeclarations?: Declaration[], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, onAfterDownload, onBeforeDownload, systemLogger, }?: BundleOptions) => Promise<{
|
|
17
|
+
declare const bundle: (sourceDirectories: string[], distDirectory: string, tomlDeclarations?: Declaration[], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMapPaths, onAfterDownload, onBeforeDownload, systemLogger, }?: BundleOptions) => Promise<{
|
|
17
18
|
functions: import("./edge_function.js").EdgeFunction[];
|
|
18
19
|
manifest: import("./manifest.js").Manifest;
|
|
19
20
|
}>;
|
package/dist/node/bundler.js
CHANGED
|
@@ -6,16 +6,15 @@ import { importMapSpecifier } from '../shared/consts.js';
|
|
|
6
6
|
import { DenoBridge } from './bridge.js';
|
|
7
7
|
import { getFunctionConfig } from './config.js';
|
|
8
8
|
import { getDeclarationsFromConfig } from './declaration.js';
|
|
9
|
-
import { getConfig as getDenoConfig } from './deno_config.js';
|
|
10
9
|
import { load as loadDeployConfig } from './deploy_config.js';
|
|
11
10
|
import { getFlags } from './feature_flags.js';
|
|
12
11
|
import { findFunctions } from './finder.js';
|
|
13
12
|
import { bundle as bundleESZIP } from './formats/eszip.js';
|
|
14
|
-
import { ImportMap
|
|
13
|
+
import { ImportMap } from './import_map.js';
|
|
15
14
|
import { getLogger } from './logger.js';
|
|
16
15
|
import { writeManifest } from './manifest.js';
|
|
17
16
|
import { ensureLatestTypes } from './types.js';
|
|
18
|
-
const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, onAfterDownload, onBeforeDownload, systemLogger, } = {}) => {
|
|
17
|
+
const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], { basePath: inputBasePath, cacheDirectory, configPath, debug, distImportMapPath, featureFlags: inputFeatureFlags, importMapPaths = [], onAfterDownload, onBeforeDownload, systemLogger, } = {}) => {
|
|
19
18
|
const logger = getLogger(systemLogger, debug);
|
|
20
19
|
const featureFlags = getFlags(inputFeatureFlags);
|
|
21
20
|
const options = {
|
|
@@ -42,19 +41,7 @@ const bundle = async (sourceDirectories, distDirectory, tomlDeclarations = [], {
|
|
|
42
41
|
// not actually included in the bundle.
|
|
43
42
|
const externals = deployConfig.layers.map((layer) => layer.name);
|
|
44
43
|
const importMap = new ImportMap();
|
|
45
|
-
|
|
46
|
-
importMap.add(deployConfig.importMap);
|
|
47
|
-
}
|
|
48
|
-
if (featureFlags.edge_functions_read_deno_config) {
|
|
49
|
-
// Look for a Deno config file and read it if one exists.
|
|
50
|
-
const denoConfig = await getDenoConfig(logger, basePath);
|
|
51
|
-
// If the Deno config file defines an import map, read the file and add the
|
|
52
|
-
// imports to the global import map.
|
|
53
|
-
if (denoConfig === null || denoConfig === void 0 ? void 0 : denoConfig.importMap) {
|
|
54
|
-
const importMapFile = await readImportMapFile(denoConfig.importMap);
|
|
55
|
-
importMap.add(importMapFile);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
44
|
+
await importMap.addFiles([deployConfig === null || deployConfig === void 0 ? void 0 : deployConfig.importMap, ...importMapPaths], logger);
|
|
58
45
|
const functions = await findFunctions(sourceDirectories);
|
|
59
46
|
const functionBundle = await bundleESZIP({
|
|
60
47
|
basePath,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { join, resolve } from 'path';
|
|
3
3
|
import process from 'process';
|
|
4
|
+
import { pathToFileURL } from 'url';
|
|
4
5
|
import { deleteAsync } from 'del';
|
|
5
6
|
import tmp from 'tmp-promise';
|
|
6
|
-
import { test, expect } from 'vitest';
|
|
7
|
+
import { test, expect, vi } from 'vitest';
|
|
7
8
|
import { importMapSpecifier } from '../shared/consts.js';
|
|
8
|
-
import { useFixture } from '../test/util.js';
|
|
9
|
+
import { runESZIP, useFixture } from '../test/util.js';
|
|
9
10
|
import { BundleError } from './bundle_error.js';
|
|
10
11
|
import { bundle } from './bundler.js';
|
|
11
|
-
import {
|
|
12
|
+
import { isFileNotFoundError } from './utils/error.js';
|
|
12
13
|
import { validateManifest } from './validation/manifest/index.js';
|
|
13
14
|
test('Produces an ESZIP bundle', async () => {
|
|
14
15
|
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
@@ -23,12 +24,10 @@ test('Produces an ESZIP bundle', async () => {
|
|
|
23
24
|
const result = await bundle([userDirectory, internalDirectory], distPath, declarations, {
|
|
24
25
|
basePath,
|
|
25
26
|
configPath: join(internalDirectory, 'config.json'),
|
|
26
|
-
|
|
27
|
-
edge_functions_read_deno_config: true,
|
|
28
|
-
},
|
|
27
|
+
importMapPaths: [join(userDirectory, 'import_map.json')],
|
|
29
28
|
});
|
|
30
29
|
const generatedFiles = await fs.readdir(distPath);
|
|
31
|
-
expect(result.functions.length).toBe(
|
|
30
|
+
expect(result.functions.length).toBe(3);
|
|
32
31
|
expect(generatedFiles.length).toBe(2);
|
|
33
32
|
const manifestFile = await fs.readFile(resolve(distPath, 'manifest.json'), 'utf8');
|
|
34
33
|
const manifest = JSON.parse(manifestFile);
|
|
@@ -38,6 +37,11 @@ test('Produces an ESZIP bundle', async () => {
|
|
|
38
37
|
expect(bundles[0].format).toBe('eszip2');
|
|
39
38
|
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
40
39
|
expect(importMapURL).toBe(importMapSpecifier);
|
|
40
|
+
const bundlePath = join(distPath, bundles[0].asset);
|
|
41
|
+
const { func1, func2, func3 } = await runESZIP(bundlePath);
|
|
42
|
+
expect(func1).toBe('HELLO, JANE DOE!');
|
|
43
|
+
expect(func2).toBe('Jane Doe');
|
|
44
|
+
expect(func3).toBe('hello, netlify!');
|
|
41
45
|
await cleanup();
|
|
42
46
|
});
|
|
43
47
|
test('Uses the vendored eszip module instead of fetching it from deno.land', async () => {
|
|
@@ -218,7 +222,7 @@ test('Ignores any user-defined `deno.json` files', async () => {
|
|
|
218
222
|
throw new Error(`The file at '${denoConfigPath} would be overwritten by this test. Please move the file to a different location and try again.'`);
|
|
219
223
|
}
|
|
220
224
|
catch (error) {
|
|
221
|
-
if (
|
|
225
|
+
if (!isFileNotFoundError(error)) {
|
|
222
226
|
throw error;
|
|
223
227
|
}
|
|
224
228
|
}
|
|
@@ -280,3 +284,38 @@ test('Loads declarations and import maps from the deploy configuration', async (
|
|
|
280
284
|
expect(generatedFiles.includes(bundles[0].asset)).toBe(true);
|
|
281
285
|
await cleanup();
|
|
282
286
|
});
|
|
287
|
+
test("Ignores entries in `importMapPaths` that don't point to an existing import map file", async () => {
|
|
288
|
+
const systemLogger = vi.fn();
|
|
289
|
+
const { basePath, cleanup, distPath } = await useFixture('with_import_maps');
|
|
290
|
+
const sourceDirectory = join(basePath, 'user-functions');
|
|
291
|
+
// Creating import map file
|
|
292
|
+
const importMap = await tmp.file();
|
|
293
|
+
const importMapContents = {
|
|
294
|
+
imports: {
|
|
295
|
+
helper: pathToFileURL(join(basePath, 'helper.ts')).toString(),
|
|
296
|
+
},
|
|
297
|
+
scopes: {
|
|
298
|
+
[pathToFileURL(join(sourceDirectory, 'func3')).toString()]: {
|
|
299
|
+
helper: pathToFileURL(join(basePath, 'helper2.ts')).toString(),
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
await fs.writeFile(importMap.path, JSON.stringify(importMapContents));
|
|
304
|
+
const nonExistingImportMapPath = join(distPath, 'some-file-that-does-not-exist.json');
|
|
305
|
+
const result = await bundle([sourceDirectory], distPath, [
|
|
306
|
+
{
|
|
307
|
+
function: 'func1',
|
|
308
|
+
path: '/func1',
|
|
309
|
+
},
|
|
310
|
+
], {
|
|
311
|
+
basePath,
|
|
312
|
+
importMapPaths: [nonExistingImportMapPath, importMap.path],
|
|
313
|
+
systemLogger,
|
|
314
|
+
});
|
|
315
|
+
const generatedFiles = await fs.readdir(distPath);
|
|
316
|
+
expect(result.functions.length).toBe(2);
|
|
317
|
+
expect(generatedFiles.length).toBe(2);
|
|
318
|
+
expect(systemLogger).toHaveBeenCalledWith(`Did not find an import map file at '${nonExistingImportMapPath}'.`);
|
|
319
|
+
await cleanup();
|
|
320
|
+
await importMap.cleanup();
|
|
321
|
+
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { Declaration } from './declaration.js';
|
|
2
|
-
import { ImportMapFile } from './import_map.js';
|
|
3
2
|
import type { Layer } from './layer.js';
|
|
4
3
|
import type { Logger } from './logger.js';
|
|
5
4
|
export interface DeployConfig {
|
|
6
5
|
declarations: Declaration[];
|
|
7
|
-
importMap?:
|
|
6
|
+
importMap?: string;
|
|
8
7
|
layers: Layer[];
|
|
9
8
|
}
|
|
10
9
|
export declare const load: (path: string | undefined, logger: Logger) => Promise<DeployConfig>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { dirname, resolve } from 'path';
|
|
3
|
-
import {
|
|
4
|
-
import { isNodeError } from './utils/error.js';
|
|
3
|
+
import { isFileNotFoundError } from './utils/error.js';
|
|
5
4
|
export const load = async (path, logger) => {
|
|
6
5
|
if (path === undefined) {
|
|
7
6
|
return {
|
|
@@ -15,7 +14,7 @@ export const load = async (path, logger) => {
|
|
|
15
14
|
return parse(config, path);
|
|
16
15
|
}
|
|
17
16
|
catch (error) {
|
|
18
|
-
if (
|
|
17
|
+
if (!isFileNotFoundError(error)) {
|
|
19
18
|
logger.system('Error while parsing internal edge functions manifest:', error);
|
|
20
19
|
}
|
|
21
20
|
}
|
|
@@ -24,7 +23,7 @@ export const load = async (path, logger) => {
|
|
|
24
23
|
layers: [],
|
|
25
24
|
};
|
|
26
25
|
};
|
|
27
|
-
const parse =
|
|
26
|
+
const parse = (data, path) => {
|
|
28
27
|
var _a, _b;
|
|
29
28
|
if (data.version !== 1) {
|
|
30
29
|
throw new Error(`Unsupported file version: ${data.version}`);
|
|
@@ -35,11 +34,7 @@ const parse = async (data, path) => {
|
|
|
35
34
|
};
|
|
36
35
|
if (data.import_map) {
|
|
37
36
|
const importMapPath = resolve(dirname(path), data.import_map);
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
...config,
|
|
41
|
-
importMap,
|
|
42
|
-
};
|
|
37
|
+
config.importMap = importMapPath;
|
|
43
38
|
}
|
|
44
39
|
return config;
|
|
45
40
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { cwd } from 'process';
|
|
4
|
-
import { pathToFileURL } from 'url';
|
|
5
4
|
import tmp from 'tmp-promise';
|
|
6
5
|
import { test, expect } from 'vitest';
|
|
7
6
|
import { load } from './deploy_config.js';
|
|
@@ -14,8 +13,7 @@ test('Returns an empty config object if there is no file at the given path', asy
|
|
|
14
13
|
expect(config.layers).toEqual([]);
|
|
15
14
|
});
|
|
16
15
|
test('Returns a config object with declarations, layers, and import map', async () => {
|
|
17
|
-
|
|
18
|
-
const importMapFile = await tmp.file();
|
|
16
|
+
const importMapFile = await tmp.file({ postfix: '.json' });
|
|
19
17
|
const importMap = {
|
|
20
18
|
imports: {
|
|
21
19
|
'https://deno.land/': 'https://black.hole/',
|
|
@@ -42,9 +40,8 @@ test('Returns a config object with declarations, layers, and import map', async
|
|
|
42
40
|
};
|
|
43
41
|
await fs.writeFile(configFile.path, JSON.stringify(config));
|
|
44
42
|
const parsedConfig = await load(configFile.path, logger);
|
|
43
|
+
await importMapFile.cleanup();
|
|
45
44
|
expect(parsedConfig.declarations).toEqual(config.functions);
|
|
46
45
|
expect(parsedConfig.layers).toEqual(config.layers);
|
|
47
|
-
expect(parsedConfig.importMap).
|
|
48
|
-
expect((_a = parsedConfig.importMap) === null || _a === void 0 ? void 0 : _a.baseURL).toEqual(pathToFileURL(importMapFile.path));
|
|
49
|
-
expect((_b = parsedConfig.importMap) === null || _b === void 0 ? void 0 : _b.imports).toEqual(importMap.imports);
|
|
46
|
+
expect(parsedConfig.importMap).toBe(importMapFile.path);
|
|
50
47
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const defaultFlags = {
|
|
2
2
|
edge_functions_cache_deno_dir: false,
|
|
3
3
|
edge_functions_config_export: false,
|
|
4
|
-
edge_functions_read_deno_config: false,
|
|
5
4
|
};
|
|
6
5
|
const getFlags = (input = {}, flags = defaultFlags) => Object.entries(flags).reduce((result, [key, defaultValue]) => ({
|
|
7
6
|
...result,
|
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { Logger } from './logger.js';
|
|
2
|
+
type Imports = Record<string, string>;
|
|
3
|
+
interface ImportMapSource {
|
|
2
4
|
baseURL: URL;
|
|
3
|
-
imports:
|
|
4
|
-
scopes?: Record<string,
|
|
5
|
+
imports: Imports;
|
|
6
|
+
scopes?: Record<string, Imports>;
|
|
5
7
|
}
|
|
6
8
|
declare class ImportMap {
|
|
7
|
-
|
|
8
|
-
constructor(
|
|
9
|
-
static resolve(
|
|
9
|
+
sources: ImportMapSource[];
|
|
10
|
+
constructor(sources?: ImportMapSource[]);
|
|
11
|
+
static resolve(source: ImportMapSource, basePath?: string, prefix?: string): {
|
|
10
12
|
imports: Record<string, string>;
|
|
11
|
-
scopes
|
|
13
|
+
scopes: Record<string, Imports>;
|
|
12
14
|
};
|
|
13
|
-
|
|
15
|
+
static resolveImports(imports: Record<string, URL | null>, basePath?: string, prefix?: string): Record<string, string>;
|
|
16
|
+
static resolvePath(url: URL, basePath?: string, prefix?: string): string;
|
|
17
|
+
add(source: ImportMapSource): void;
|
|
18
|
+
addFile(path: string, logger: Logger): Promise<void>;
|
|
19
|
+
addFiles(paths: (string | undefined)[], logger: Logger): Promise<void>;
|
|
14
20
|
getContents(basePath?: string, prefix?: string): {
|
|
15
|
-
imports:
|
|
21
|
+
imports: Imports;
|
|
22
|
+
scopes: Record<string, Imports>;
|
|
16
23
|
};
|
|
17
24
|
toDataURL(): string;
|
|
18
25
|
writeToFile(path: string): Promise<void>;
|
|
19
26
|
}
|
|
20
|
-
declare const readFile: (path: string) => Promise<
|
|
27
|
+
declare const readFile: (path: string, logger: Logger) => Promise<ImportMapSource>;
|
|
21
28
|
export { ImportMap, readFile };
|
|
22
|
-
export type { ImportMapFile };
|
|
29
|
+
export type { ImportMapSource as ImportMapFile };
|
package/dist/node/import_map.js
CHANGED
|
@@ -3,25 +3,37 @@ import { promises as fs } from 'fs';
|
|
|
3
3
|
import { dirname, posix, relative, sep } from 'path';
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
5
|
import { parse } from '@import-maps/resolve';
|
|
6
|
+
import { isFileNotFoundError } from './utils/error.js';
|
|
6
7
|
const INTERNAL_IMPORTS = {
|
|
7
8
|
'netlify:edge': 'https://edge.netlify.com/v1/index.ts',
|
|
8
9
|
};
|
|
9
10
|
// ImportMap can take several import map files and merge them into a final
|
|
10
11
|
// import map object, also adding the internal imports in the right order.
|
|
11
12
|
class ImportMap {
|
|
12
|
-
constructor(
|
|
13
|
-
this.
|
|
14
|
-
|
|
13
|
+
constructor(sources = []) {
|
|
14
|
+
this.sources = [];
|
|
15
|
+
sources.forEach((file) => {
|
|
15
16
|
this.add(file);
|
|
16
17
|
});
|
|
17
18
|
}
|
|
18
19
|
// Transforms an import map by making any relative paths use a different path
|
|
19
20
|
// as a base.
|
|
20
|
-
static resolve(
|
|
21
|
-
const { baseURL, ...importMap } =
|
|
21
|
+
static resolve(source, basePath, prefix = 'file://') {
|
|
22
|
+
const { baseURL, ...importMap } = source;
|
|
22
23
|
const parsedImportMap = parse(importMap, baseURL);
|
|
23
|
-
const { imports = {} } = parsedImportMap;
|
|
24
|
-
const
|
|
24
|
+
const { imports = {}, scopes = {} } = parsedImportMap;
|
|
25
|
+
const resolvedImports = ImportMap.resolveImports(imports, basePath, prefix);
|
|
26
|
+
const resolvedScopes = {};
|
|
27
|
+
Object.keys(scopes).forEach((path) => {
|
|
28
|
+
const resolvedPath = ImportMap.resolvePath(new URL(path), basePath, prefix);
|
|
29
|
+
resolvedScopes[resolvedPath] = ImportMap.resolveImports(scopes[path], basePath, prefix);
|
|
30
|
+
});
|
|
31
|
+
return { ...parsedImportMap, imports: resolvedImports, scopes: resolvedScopes };
|
|
32
|
+
}
|
|
33
|
+
// Takes an imports object and resolves relative specifiers with a given base
|
|
34
|
+
// path and URL prefix.
|
|
35
|
+
static resolveImports(imports, basePath, prefix) {
|
|
36
|
+
const resolvedImports = {};
|
|
25
37
|
Object.keys(imports).forEach((specifier) => {
|
|
26
38
|
const url = imports[specifier];
|
|
27
39
|
// If there's no URL, don't even add the specifier to the final imports.
|
|
@@ -29,37 +41,62 @@ class ImportMap {
|
|
|
29
41
|
return;
|
|
30
42
|
}
|
|
31
43
|
// If this is a file URL, we might want to transform it to use another
|
|
32
|
-
// base path
|
|
33
|
-
if (url.protocol === 'file:'
|
|
34
|
-
|
|
35
|
-
const relativePath = relative(basePath, path);
|
|
36
|
-
if (relativePath.startsWith('..')) {
|
|
37
|
-
throw new Error(`Import map cannot reference '${path}' as it's outside of the base directory '${basePath}'`);
|
|
38
|
-
}
|
|
39
|
-
// We want to use POSIX paths for the import map regardless of the OS
|
|
40
|
-
// we're building in.
|
|
41
|
-
let normalizedPath = relativePath.split(sep).join(posix.sep);
|
|
42
|
-
// If the original URL had a trailing slash, ensure the normalized path
|
|
43
|
-
// has one too.
|
|
44
|
-
if (normalizedPath !== '' && url.pathname.endsWith(posix.sep) && !normalizedPath.endsWith(posix.sep)) {
|
|
45
|
-
normalizedPath += posix.sep;
|
|
46
|
-
}
|
|
47
|
-
const newURL = new URL(normalizedPath, prefix);
|
|
48
|
-
newImports[specifier] = newURL.toString();
|
|
44
|
+
// base path.
|
|
45
|
+
if (url.protocol === 'file:') {
|
|
46
|
+
resolvedImports[specifier] = ImportMap.resolvePath(url, basePath, prefix);
|
|
49
47
|
return;
|
|
50
48
|
}
|
|
51
|
-
|
|
49
|
+
resolvedImports[specifier] = url.toString();
|
|
52
50
|
});
|
|
53
|
-
return
|
|
51
|
+
return resolvedImports;
|
|
52
|
+
}
|
|
53
|
+
// Takes a URL, turns it into a path relative to the given base, and prepends
|
|
54
|
+
// a prefix (such as the virtual root prefix).
|
|
55
|
+
static resolvePath(url, basePath, prefix) {
|
|
56
|
+
if (basePath === undefined) {
|
|
57
|
+
return url.toString();
|
|
58
|
+
}
|
|
59
|
+
const path = fileURLToPath(url);
|
|
60
|
+
const relativePath = relative(basePath, path);
|
|
61
|
+
if (relativePath.startsWith('..')) {
|
|
62
|
+
throw new Error(`Import map cannot reference '${path}' as it's outside of the base directory '${basePath}'`);
|
|
63
|
+
}
|
|
64
|
+
// We want to use POSIX paths for the import map regardless of the OS
|
|
65
|
+
// we're building in.
|
|
66
|
+
let normalizedPath = relativePath.split(sep).join(posix.sep);
|
|
67
|
+
// If the original URL had a trailing slash, ensure the normalized path
|
|
68
|
+
// has one too.
|
|
69
|
+
if (normalizedPath !== '' && url.pathname.endsWith(posix.sep) && !normalizedPath.endsWith(posix.sep)) {
|
|
70
|
+
normalizedPath += posix.sep;
|
|
71
|
+
}
|
|
72
|
+
const newURL = new URL(normalizedPath, prefix);
|
|
73
|
+
return newURL.toString();
|
|
54
74
|
}
|
|
55
|
-
add(
|
|
56
|
-
this.
|
|
75
|
+
add(source) {
|
|
76
|
+
this.sources.push(source);
|
|
77
|
+
}
|
|
78
|
+
async addFile(path, logger) {
|
|
79
|
+
const source = await readFile(path, logger);
|
|
80
|
+
if (Object.keys(source.imports).length === 0) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
return this.add(source);
|
|
84
|
+
}
|
|
85
|
+
async addFiles(paths, logger) {
|
|
86
|
+
for (const path of paths) {
|
|
87
|
+
if (path === undefined) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
await this.addFile(path, logger);
|
|
91
|
+
}
|
|
57
92
|
}
|
|
58
93
|
getContents(basePath, prefix) {
|
|
59
94
|
let imports = {};
|
|
60
|
-
|
|
95
|
+
let scopes = {};
|
|
96
|
+
this.sources.forEach((file) => {
|
|
61
97
|
const importMap = ImportMap.resolve(file, basePath, prefix);
|
|
62
98
|
imports = { ...imports, ...importMap.imports };
|
|
99
|
+
scopes = { ...scopes, ...importMap.scopes };
|
|
63
100
|
});
|
|
64
101
|
// Internal imports must come last, because we need to guarantee that
|
|
65
102
|
// `netlify:edge` isn't user-defined.
|
|
@@ -69,6 +106,7 @@ class ImportMap {
|
|
|
69
106
|
});
|
|
70
107
|
return {
|
|
71
108
|
imports,
|
|
109
|
+
scopes,
|
|
72
110
|
};
|
|
73
111
|
}
|
|
74
112
|
toDataURL() {
|
|
@@ -83,7 +121,7 @@ class ImportMap {
|
|
|
83
121
|
await fs.writeFile(path, JSON.stringify(contents));
|
|
84
122
|
}
|
|
85
123
|
}
|
|
86
|
-
const readFile = async (path) => {
|
|
124
|
+
const readFile = async (path, logger) => {
|
|
87
125
|
const baseURL = pathToFileURL(path);
|
|
88
126
|
try {
|
|
89
127
|
const data = await fs.readFile(path, 'utf8');
|
|
@@ -93,8 +131,13 @@ const readFile = async (path) => {
|
|
|
93
131
|
baseURL,
|
|
94
132
|
};
|
|
95
133
|
}
|
|
96
|
-
catch {
|
|
97
|
-
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (isFileNotFoundError(error)) {
|
|
136
|
+
logger.system(`Did not find an import map file at '${path}'.`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
logger.user(`Error while loading import map at '${path}':`, error);
|
|
140
|
+
}
|
|
98
141
|
}
|
|
99
142
|
return {
|
|
100
143
|
baseURL,
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { OnAfterDownloadHook, OnBeforeDownloadHook } from '../bridge.js';
|
|
7
7
|
import { FunctionConfig } from '../config.js';
|
|
8
8
|
import type { EdgeFunction } from '../edge_function.js';
|
|
9
|
-
import { ImportMapFile } from '../import_map.js';
|
|
10
9
|
import { LogFunction } from '../logger.js';
|
|
11
10
|
type FormatFunction = (name: string) => string;
|
|
12
11
|
interface StartServerOptions {
|
|
@@ -18,12 +17,11 @@ interface InspectSettings {
|
|
|
18
17
|
address?: string;
|
|
19
18
|
}
|
|
20
19
|
interface ServeOptions {
|
|
21
|
-
basePath: string;
|
|
22
20
|
certificatePath?: string;
|
|
23
21
|
debug?: boolean;
|
|
24
22
|
distImportMapPath?: string;
|
|
25
23
|
inspectSettings?: InspectSettings;
|
|
26
|
-
|
|
24
|
+
importMapPaths?: string[];
|
|
27
25
|
onAfterDownload?: OnAfterDownloadHook;
|
|
28
26
|
onBeforeDownload?: OnBeforeDownloadHook;
|
|
29
27
|
formatExportTypeError?: FormatFunction;
|
|
@@ -31,7 +29,7 @@ interface ServeOptions {
|
|
|
31
29
|
port: number;
|
|
32
30
|
systemLogger?: LogFunction;
|
|
33
31
|
}
|
|
34
|
-
declare const serve: ({
|
|
32
|
+
declare const serve: ({ certificatePath, debug, distImportMapPath, inspectSettings, formatExportTypeError, formatImportError, importMapPaths, onAfterDownload, onBeforeDownload, port, systemLogger, }: ServeOptions) => Promise<(functions: EdgeFunction[], env?: NodeJS.ProcessEnv, options?: StartServerOptions) => Promise<{
|
|
35
33
|
functionsConfig: FunctionConfig[];
|
|
36
34
|
graph: any;
|
|
37
35
|
success: boolean;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { tmpName } from 'tmp-promise';
|
|
2
2
|
import { DenoBridge } from '../bridge.js';
|
|
3
3
|
import { getFunctionConfig } from '../config.js';
|
|
4
|
-
import { getConfig as getDenoConfig } from '../deno_config.js';
|
|
5
4
|
import { generateStage2 } from '../formats/javascript.js';
|
|
6
|
-
import { ImportMap
|
|
5
|
+
import { ImportMap } from '../import_map.js';
|
|
7
6
|
import { getLogger } from '../logger.js';
|
|
8
7
|
import { ensureLatestTypes } from '../types.js';
|
|
9
8
|
import { killProcess, waitForServer } from './util.js';
|
|
@@ -54,7 +53,7 @@ const prepareServer = ({ deno, distDirectory, flags: denoFlags, formatExportType
|
|
|
54
53
|
};
|
|
55
54
|
return startServer;
|
|
56
55
|
};
|
|
57
|
-
const serve = async ({
|
|
56
|
+
const serve = async ({ certificatePath, debug, distImportMapPath, inspectSettings, formatExportTypeError, formatImportError, importMapPaths = [], onAfterDownload, onBeforeDownload, port, systemLogger, }) => {
|
|
58
57
|
const logger = getLogger(systemLogger, debug);
|
|
59
58
|
const deno = new DenoBridge({
|
|
60
59
|
debug,
|
|
@@ -69,17 +68,8 @@ const serve = async ({ basePath, certificatePath, debug, distImportMapPath, insp
|
|
|
69
68
|
await deno.getBinaryPath();
|
|
70
69
|
// Downloading latest types if needed.
|
|
71
70
|
await ensureLatestTypes(deno, logger);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const importMap = new ImportMap(importMaps);
|
|
75
|
-
// Look for a Deno config file and read it if one exists.
|
|
76
|
-
const denoConfig = await getDenoConfig(logger, basePath);
|
|
77
|
-
// If the Deno config file defines an import map, read the file and add the
|
|
78
|
-
// imports to the global import map.
|
|
79
|
-
if (denoConfig === null || denoConfig === void 0 ? void 0 : denoConfig.importMap) {
|
|
80
|
-
const importMapFile = await readImportMapFile(denoConfig.importMap);
|
|
81
|
-
importMap.add(importMapFile);
|
|
82
|
-
}
|
|
71
|
+
const importMap = new ImportMap();
|
|
72
|
+
await importMap.addFiles(importMapPaths, logger);
|
|
83
73
|
const flags = ['--allow-all', '--unstable', `--import-map=${importMap.toDataURL()}`, '--no-config'];
|
|
84
74
|
if (certificatePath) {
|
|
85
75
|
flags.push(`--cert=${certificatePath}`);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
|
-
import { pathToFileURL } from 'url';
|
|
3
2
|
import getPort from 'get-port';
|
|
4
3
|
import fetch from 'node-fetch';
|
|
5
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
@@ -8,30 +7,24 @@ import { fixturesDir } from '../../test/util.js';
|
|
|
8
7
|
import { serve } from '../index.js';
|
|
9
8
|
test('Starts a server and serves requests for edge functions', async () => {
|
|
10
9
|
const basePath = join(fixturesDir, 'serve_test');
|
|
11
|
-
const
|
|
12
|
-
internal: join(basePath, '.netlify', 'edge-functions'
|
|
13
|
-
user: join(basePath, 'netlify', 'edge-functions'
|
|
10
|
+
const paths = {
|
|
11
|
+
internal: join(basePath, '.netlify', 'edge-functions'),
|
|
12
|
+
user: join(basePath, 'netlify', 'edge-functions'),
|
|
14
13
|
};
|
|
15
14
|
const port = await getPort();
|
|
16
|
-
const
|
|
17
|
-
baseURL: pathToFileURL(functionPaths.internal),
|
|
18
|
-
imports: {
|
|
19
|
-
'internal-helper': '../../helper.ts',
|
|
20
|
-
},
|
|
21
|
-
};
|
|
15
|
+
const importMapPaths = [join(paths.internal, 'import_map.json'), join(paths.user, 'import-map.json')];
|
|
22
16
|
const server = await serve({
|
|
23
|
-
|
|
24
|
-
importMaps: [internalImportMap],
|
|
17
|
+
importMapPaths,
|
|
25
18
|
port,
|
|
26
19
|
});
|
|
27
20
|
const functions = [
|
|
28
21
|
{
|
|
29
22
|
name: 'echo_env',
|
|
30
|
-
path:
|
|
23
|
+
path: join(paths.user, 'echo_env.ts'),
|
|
31
24
|
},
|
|
32
25
|
{
|
|
33
26
|
name: 'greet',
|
|
34
|
-
path:
|
|
27
|
+
path: join(paths.internal, 'greet.ts'),
|
|
35
28
|
},
|
|
36
29
|
];
|
|
37
30
|
const options = {
|
|
@@ -42,10 +35,10 @@ test('Starts a server and serves requests for edge functions', async () => {
|
|
|
42
35
|
}, options);
|
|
43
36
|
expect(success).toBe(true);
|
|
44
37
|
expect(functionsConfig).toEqual([{ path: '/my-function' }, {}]);
|
|
45
|
-
for (const key in
|
|
38
|
+
for (const key in functions) {
|
|
46
39
|
const graphEntry = graph === null || graph === void 0 ? void 0 : graph.modules.some(
|
|
47
40
|
// @ts-expect-error TODO: Module graph is currently not typed
|
|
48
|
-
({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local ===
|
|
41
|
+
({ kind, mediaType, local }) => kind === 'esm' && mediaType === 'TypeScript' && local === functions[key].path);
|
|
49
42
|
expect(graphEntry).toBe(true);
|
|
50
43
|
}
|
|
51
44
|
const response1 = await fetch(`http://0.0.0.0:${port}/foo`, {
|
package/dist/node/utils/error.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
2
|
export const isNodeError = (error) => error instanceof Error;
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
export const isFileNotFoundError = (error) => isNodeError(error) && error.code === 'ENOENT';
|
package/dist/test/util.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ declare const useFixture: (fixtureName: string) => Promise<{
|
|
|
5
5
|
cleanup: () => Promise<void>;
|
|
6
6
|
distPath: string;
|
|
7
7
|
}>;
|
|
8
|
-
|
|
8
|
+
declare const runESZIP: (eszipPath: string) => Promise<any>;
|
|
9
|
+
export { fixturesDir, testLogger, runESZIP, useFixture };
|
package/dist/test/util.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { join, resolve } from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { stderr, stdout } from 'process';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
5
|
+
import { execa } from 'execa';
|
|
4
6
|
import tmp from 'tmp-promise';
|
|
5
7
|
import { getLogger } from '../node/logger.js';
|
|
6
8
|
const testLogger = getLogger(() => {
|
|
@@ -10,14 +12,58 @@ const url = new URL(import.meta.url);
|
|
|
10
12
|
const dirname = fileURLToPath(url);
|
|
11
13
|
const fixturesDir = resolve(dirname, '..', 'fixtures');
|
|
12
14
|
const useFixture = async (fixtureName) => {
|
|
13
|
-
const tmpDir = await tmp.dir();
|
|
14
|
-
const cleanup = () => fs.rmdir(tmpDir.path, { recursive: true });
|
|
15
|
+
const tmpDir = await tmp.dir({ unsafeCleanup: true });
|
|
15
16
|
const fixtureDir = resolve(fixturesDir, fixtureName);
|
|
16
17
|
const distPath = join(tmpDir.path, '.netlify', 'edge-functions-dist');
|
|
17
18
|
return {
|
|
18
19
|
basePath: fixtureDir,
|
|
19
|
-
cleanup,
|
|
20
|
+
cleanup: tmpDir.cleanup,
|
|
20
21
|
distPath,
|
|
21
22
|
};
|
|
22
23
|
};
|
|
23
|
-
|
|
24
|
+
const inspectFunction = (path) => `
|
|
25
|
+
import { functions } from "${pathToFileURL(path)}.js";
|
|
26
|
+
|
|
27
|
+
const responses = {};
|
|
28
|
+
|
|
29
|
+
for (const functionName in functions) {
|
|
30
|
+
const req = new Request("https://test.netlify");
|
|
31
|
+
const res = await functions[functionName](req);
|
|
32
|
+
|
|
33
|
+
responses[functionName] = await res.text();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(JSON.stringify(responses));
|
|
37
|
+
`;
|
|
38
|
+
const runESZIP = async (eszipPath) => {
|
|
39
|
+
var _a, _b, _c;
|
|
40
|
+
const tmpDir = await tmp.dir({ unsafeCleanup: true });
|
|
41
|
+
// Extract ESZIP into temporary directory.
|
|
42
|
+
const extractCommand = execa('deno', [
|
|
43
|
+
'run',
|
|
44
|
+
'--allow-all',
|
|
45
|
+
'https://deno.land/x/eszip@v0.28.0/eszip.ts',
|
|
46
|
+
'x',
|
|
47
|
+
eszipPath,
|
|
48
|
+
tmpDir.path,
|
|
49
|
+
]);
|
|
50
|
+
(_a = extractCommand.stderr) === null || _a === void 0 ? void 0 : _a.pipe(stderr);
|
|
51
|
+
(_b = extractCommand.stdout) === null || _b === void 0 ? void 0 : _b.pipe(stdout);
|
|
52
|
+
await extractCommand;
|
|
53
|
+
const virtualRootPath = join(tmpDir.path, 'source', 'root');
|
|
54
|
+
const stage2Path = join(virtualRootPath, '..', 'bootstrap-stage2');
|
|
55
|
+
const importMapPath = join(virtualRootPath, '..', 'import-map');
|
|
56
|
+
for (const path of [importMapPath, stage2Path]) {
|
|
57
|
+
const file = await fs.readFile(path, 'utf8');
|
|
58
|
+
const normalizedFile = file.replace(/file:\/\/\/root/g, pathToFileURL(virtualRootPath).toString());
|
|
59
|
+
await fs.writeFile(path, normalizedFile);
|
|
60
|
+
}
|
|
61
|
+
await fs.rename(stage2Path, `${stage2Path}.js`);
|
|
62
|
+
// Run function that imports the extracted stage 2 and invokes each function.
|
|
63
|
+
const evalCommand = execa('deno', ['eval', '--no-check', '--import-map', importMapPath, inspectFunction(stage2Path)]);
|
|
64
|
+
(_c = evalCommand.stderr) === null || _c === void 0 ? void 0 : _c.pipe(stderr);
|
|
65
|
+
const result = await evalCommand;
|
|
66
|
+
await tmpDir.cleanup();
|
|
67
|
+
return JSON.parse(result.stdout);
|
|
68
|
+
};
|
|
69
|
+
export { fixturesDir, testLogger, runESZIP, useFixture };
|
package/package.json
CHANGED
package/dist/node/deno_config.js
DELETED
|
@@ -1,43 +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 (logger, basePath) => {
|
|
7
|
-
if (basePath === undefined) {
|
|
8
|
-
logger.system('No base path specified, will not attempt to read Deno config');
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
for (const filename of filenames) {
|
|
12
|
-
const candidatePath = join(basePath, filename);
|
|
13
|
-
const config = await getConfigFromFile(candidatePath);
|
|
14
|
-
if (config !== undefined) {
|
|
15
|
-
logger.system('Loaded Deno config file from path', candidatePath);
|
|
16
|
-
return normalizeConfig(config, basePath);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
logger.system('No Deno config file found at base path', basePath);
|
|
20
|
-
};
|
|
21
|
-
const getConfigFromFile = async (filePath) => {
|
|
22
|
-
try {
|
|
23
|
-
const data = await fs.readFile(filePath, 'utf8');
|
|
24
|
-
const config = parseJSONC(data);
|
|
25
|
-
return config;
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
return {};
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
const normalizeConfig = (rawConfig, basePath) => {
|
|
35
|
-
const config = {};
|
|
36
|
-
if (rawConfig.importMap) {
|
|
37
|
-
if (typeof rawConfig.importMap !== 'string') {
|
|
38
|
-
throw new TypeError(`'importMap' property in Deno config must be a string`);
|
|
39
|
-
}
|
|
40
|
-
config.importMap = resolve(basePath, rawConfig.importMap);
|
|
41
|
-
}
|
|
42
|
-
return config;
|
|
43
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,92 +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 { testLogger } from '../test/util.js';
|
|
6
|
-
import { getConfig } from './deno_config.js';
|
|
7
|
-
test('Returns `undefined` if no config file is found', async () => {
|
|
8
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
9
|
-
const config = await getConfig(testLogger, path);
|
|
10
|
-
expect(config).toBeUndefined();
|
|
11
|
-
await cleanup();
|
|
12
|
-
});
|
|
13
|
-
test('Returns an empty object if the config file cannot be parsed', async () => {
|
|
14
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
15
|
-
const configPath = join(path, 'deno.json');
|
|
16
|
-
await fs.writeFile(configPath, '{');
|
|
17
|
-
const config = await getConfig(testLogger, path);
|
|
18
|
-
expect(config).toEqual({});
|
|
19
|
-
await cleanup();
|
|
20
|
-
});
|
|
21
|
-
test('Throws a type error if the `importMap` contains anything other than a string', async () => {
|
|
22
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
23
|
-
const configPath = join(path, 'deno.json');
|
|
24
|
-
const data = JSON.stringify({ importMap: { imports: { foo: './bar/' } } });
|
|
25
|
-
await fs.writeFile(configPath, data);
|
|
26
|
-
await expect(getConfig(testLogger, path)).rejects.toThrowError(TypeError);
|
|
27
|
-
await cleanup();
|
|
28
|
-
});
|
|
29
|
-
test('Excludes unsupported properties', async () => {
|
|
30
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
31
|
-
const configPath = join(path, 'deno.json');
|
|
32
|
-
const data = JSON.stringify({
|
|
33
|
-
compilerOptions: {
|
|
34
|
-
allowJs: true,
|
|
35
|
-
lib: ['deno.window'],
|
|
36
|
-
strict: true,
|
|
37
|
-
},
|
|
38
|
-
importMap: 'import_map.json',
|
|
39
|
-
lint: {
|
|
40
|
-
files: {
|
|
41
|
-
include: ['src/'],
|
|
42
|
-
exclude: ['src/testdata/'],
|
|
43
|
-
},
|
|
44
|
-
rules: {
|
|
45
|
-
tags: ['recommended'],
|
|
46
|
-
include: ['ban-untagged-todo'],
|
|
47
|
-
exclude: ['no-unused-vars'],
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
fmt: {
|
|
51
|
-
files: {
|
|
52
|
-
include: ['src/'],
|
|
53
|
-
exclude: ['src/testdata/'],
|
|
54
|
-
},
|
|
55
|
-
options: {
|
|
56
|
-
useTabs: true,
|
|
57
|
-
lineWidth: 80,
|
|
58
|
-
indentWidth: 4,
|
|
59
|
-
singleQuote: true,
|
|
60
|
-
proseWrap: 'preserve',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
test: {
|
|
64
|
-
files: {
|
|
65
|
-
include: ['src/'],
|
|
66
|
-
exclude: ['src/testdata/'],
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
await fs.writeFile(configPath, data);
|
|
71
|
-
const config = await getConfig(testLogger, path);
|
|
72
|
-
expect(Object.keys(config !== null && config !== void 0 ? config : {})).toEqual(['importMap']);
|
|
73
|
-
await cleanup();
|
|
74
|
-
});
|
|
75
|
-
test('Resolves `importMap` into an absolute path', async () => {
|
|
76
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
77
|
-
const configPath = join(path, 'deno.json');
|
|
78
|
-
const data = JSON.stringify({ importMap: 'import_map.json' });
|
|
79
|
-
await fs.writeFile(configPath, data);
|
|
80
|
-
const config = await getConfig(testLogger, path);
|
|
81
|
-
expect(config).toEqual({ importMap: join(path, 'import_map.json') });
|
|
82
|
-
await cleanup();
|
|
83
|
-
});
|
|
84
|
-
test('Supports JSONC', async () => {
|
|
85
|
-
const { cleanup, path } = await tmp.dir({ unsafeCleanup: true });
|
|
86
|
-
const configPath = join(path, 'deno.jsonc');
|
|
87
|
-
const data = JSON.stringify({ importMap: 'import_map.json' });
|
|
88
|
-
await fs.writeFile(configPath, `// This is a comment\n${data}`);
|
|
89
|
-
const config = await getConfig(testLogger, path);
|
|
90
|
-
expect(config).toEqual({ importMap: join(path, 'import_map.json') });
|
|
91
|
-
await cleanup();
|
|
92
|
-
});
|