diff --git a/deno/lib/consts.ts b/deno/lib/consts.ts index bc8fd206..c3058ef2 100644 --- a/deno/lib/consts.ts +++ b/deno/lib/consts.ts @@ -1,4 +1,5 @@ -export const PUBLIC_SPECIFIER = "netlify:edge"; -export const STAGE1_SPECIFIER = "netlify:bootstrap-stage1"; -export const STAGE2_SPECIFIER = "netlify:bootstrap-stage2"; -export const virtualRoot = "file:///root/"; +export const CUSTOM_LAYER_PREFIX = 'layer:' +export const PUBLIC_SPECIFIER = 'netlify:edge' +export const STAGE1_SPECIFIER = 'netlify:bootstrap-stage1' +export const STAGE2_SPECIFIER = 'netlify:bootstrap-stage2' +export const virtualRoot = 'file:///root/' diff --git a/deno/lib/stage2.ts b/deno/lib/stage2.ts index 8a2355fd..045792be 100644 --- a/deno/lib/stage2.ts +++ b/deno/lib/stage2.ts @@ -3,7 +3,7 @@ import { build, LoadResponse } from 'https://deno.land/x/eszip@v0.28.0/mod.ts' import * as path from 'https://deno.land/std@0.127.0/path/mod.ts' import type { InputFunction, WriteStage2Options } from '../../shared/stage2.ts' -import { PUBLIC_SPECIFIER, STAGE2_SPECIFIER, virtualRoot } from './consts.ts' +import { CUSTOM_LAYER_PREFIX, PUBLIC_SPECIFIER, STAGE2_SPECIFIER, virtualRoot } from './consts.ts' import { inlineModule, loadFromVirtualRoot, loadWithRetry } from './common.ts' interface FunctionReference { @@ -70,7 +70,7 @@ const stage2Loader = (basePath: string, functions: InputFunction[]) => { return inlineModule(specifier, stage2Entry) } - if (specifier === PUBLIC_SPECIFIER) { + if (specifier === PUBLIC_SPECIFIER || specifier.startsWith(CUSTOM_LAYER_PREFIX)) { return { kind: 'external', specifier, @@ -88,6 +88,9 @@ const stage2Loader = (basePath: string, functions: InputFunction[]) => { const writeStage2 = async ({ basePath, destPath, functions, importMapURL }: WriteStage2Options) => { const loader = stage2Loader(basePath, functions) const bytes = await build([STAGE2_SPECIFIER], loader, importMapURL) + const directory = path.dirname(destPath) + + await Deno.mkdir(directory, { recursive: true }) return await Deno.writeFile(destPath, bytes) } diff --git a/node/bundler.test.ts b/node/bundler.test.ts index a18f4860..d605ba07 100644 --- a/node/bundler.test.ts +++ b/node/bundler.test.ts @@ -325,3 +325,38 @@ test('Ignores any user-defined `deno.json` files', async () => { await deleteAsync([tmpDir.path, denoConfigPath, importMapFile.path], { force: true }) }) + +test('Processes a function that imports a custom layer', async () => { + const sourceDirectory = resolve(fixturesDir, 'with_layers', 'functions') + const tmpDir = await tmp.dir() + const declarations = [ + { + function: 'func1', + path: '/func1', + }, + ] + const layer = { name: 'test', flag: 'edge-functions-layer-test' } + const result = await bundle([sourceDirectory], tmpDir.path, declarations, { + basePath: fixturesDir, + featureFlags: { + edge_functions_produce_eszip: true, + }, + layers: [layer], + }) + const generatedFiles = await fs.readdir(tmpDir.path) + + expect(result.functions.length).toBe(1) + expect(generatedFiles.length).toBe(2) + + const manifestFile = await fs.readFile(resolve(tmpDir.path, 'manifest.json'), 'utf8') + const manifest = JSON.parse(manifestFile) + const { bundles, layers } = manifest + + expect(bundles.length).toBe(1) + expect(bundles[0].format).toBe('eszip2') + expect(generatedFiles.includes(bundles[0].asset)).toBe(true) + + expect(layers).toEqual([layer]) + + await fs.rmdir(tmpDir.path, { recursive: true }) +}) diff --git a/node/bundler.ts b/node/bundler.ts index f6d72de2..9613ed95 100644 --- a/node/bundler.ts +++ b/node/bundler.ts @@ -87,6 +87,7 @@ const bundle = async ( distImportMapPath, featureFlags: inputFeatureFlags, importMaps, + layers, onAfterDownload, onBeforeDownload, systemLogger, @@ -162,6 +163,7 @@ const bundle = async ( declarations, distDirectory, functions, + layers, }) if (distImportMapPath) { diff --git a/test/fixtures/with_layers/functions/func1.ts b/test/fixtures/with_layers/functions/func1.ts new file mode 100644 index 00000000..14b7d131 --- /dev/null +++ b/test/fixtures/with_layers/functions/func1.ts @@ -0,0 +1,3 @@ +import { handleRequest } from 'layer:test' + +export default (req: Request) => handleRequest(req)