Skip to content

Commit

Permalink
fix: support multiple path values in edge functions config (#5480)
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoboucas authored Feb 16, 2023
1 parent 5011be5 commit 27bea34
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 17 deletions.
61 changes: 45 additions & 16 deletions src/lib/edge-functions/registry.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { fileURLToPath } from 'url'

import { NETLIFYDEVERR, NETLIFYDEVLOG, chalk, log, warn, watchDebounced } from '../../utils/command-helpers.mjs'

// TODO: Import from `@netlify/edge-bundler` once it exports this type.
/**
* @typedef InSourceDeclaration
* @type {object}
* @property {string} [cache]
* @property {string} function
* @property {string | string[]} [path]
* @property {string | string[]} [excludedPath]
*/

/**
* @typedef EdgeFunction
* @type {object}
Expand All @@ -21,10 +31,11 @@ import { NETLIFYDEVERR, NETLIFYDEVLOG, chalk, log, warn, watchDebounced } from '
* @typedef EdgeFunctionDeclarationWithPattern
* @type {object}
* @property {string} function
* @property {RegExp} pattern
* @property {string} pattern
*/

/** @typedef {(EdgeFunctionDeclarationWithPath | EdgeFunctionDeclarationWithPattern) } EdgeFunctionDeclaration */
/** @typedef {(EdgeFunctionDeclarationWithPath | EdgeFunctionDeclarationWithPattern)} EdgeFunctionDeclaration */
/** @typedef {Awaited<ReturnType<typeof import('@netlify/edge-bundler').serve>>} RunIsolate */

export class EdgeFunctionsRegistry {
/**
Expand All @@ -37,7 +48,7 @@ export class EdgeFunctionsRegistry {
* @param {() => Promise<object>} opts.getUpdatedConfig
* @param {EdgeFunction[]} opts.internalFunctions
* @param {string} opts.projectDir
* @param {(functions: EdgeFunction[], env?: NodeJS.ProcessEnv) => Promise<object>} opts.runIsolate
* @param {RunIsolate} opts.runIsolate
*/
constructor({
bundler,
Expand All @@ -50,9 +61,6 @@ export class EdgeFunctionsRegistry {
projectDir,
runIsolate,
}) {
/**
* @type {import('@netlify/edge-bundler')}
*/
this.bundler = bundler

/**
Expand All @@ -76,7 +84,7 @@ export class EdgeFunctionsRegistry {
this.internalFunctions = internalFunctions

/**
* @type {(functions: EdgeFunction[], env?: NodeJS.ProcessEnv) => Promise<object>}
* @type {RunIsolate}
*/
this.runIsolate = runIsolate

Expand All @@ -91,7 +99,7 @@ export class EdgeFunctionsRegistry {
this.declarationsFromConfig = this.getDeclarationsFromConfig(config)

/**
* @type {EdgeFunctionDeclaration[]}
* @type {InSourceDeclaration[]}
*/
this.declarationsFromSource = []

Expand Down Expand Up @@ -287,7 +295,7 @@ export class EdgeFunctionsRegistry {
*/
async matchURLPath(urlPath) {
const declarations = this.mergeDeclarations()
const manifest = await this.bundler.generateManifest({
const manifest = this.bundler.generateManifest({
declarations,
functions: this.functions,
})
Expand All @@ -309,14 +317,14 @@ export class EdgeFunctionsRegistry {
return { functionNames, orphanedDeclarations }
}

async matchURLPathAgainstOrphanedDeclarations(urlPath) {
matchURLPathAgainstOrphanedDeclarations(urlPath) {
// `generateManifest` will only include functions for which there is both a
// function file and a config declaration, but we want to catch cases where
// a config declaration exists without a matching function file. To do that
// we compute a list of functions from the declarations (the `path` doesn't
// really matter).
const functions = this.declarationsFromConfig.map((declaration) => ({ name: declaration.function, path: '' }))
const manifest = await this.bundler.generateManifest({
const manifest = this.bundler.generateManifest({
declarations: this.declarationsFromConfig,
functions,
})
Expand Down Expand Up @@ -346,12 +354,33 @@ export class EdgeFunctionsRegistry {
const declarations = [...this.declarationsFromConfig]

this.declarationsFromSource.forEach((declarationFromSource) => {
const index = declarations.findIndex(({ function: func }) => func === declarationFromSource.function)

if (index === -1) {
declarations.push(declarationFromSource)
const { path: pathOrPaths, ...configProps } = declarationFromSource

// Find any declarations in the config for the function.
const indexes = declarations
.filter(({ function: func }) => func === declarationFromSource.function)
.map((_, index) => index)

// If the in-source declaration doesn't have a path, add its properties
// to any existing declarations for the function.
if (pathOrPaths === undefined) {
indexes.forEach((index) => {
declarations[index] = { ...declarations[index], ...configProps }
})
} else {
declarations[index] = { ...declarations[index], ...declarationFromSource }
// The in-source declaration has a path, and those take precedence.
// Discard any config declarations first.
indexes.forEach((index) => {
declarations.splice(index, 1)
})

// The path may be an array or a string, so let's normalize that first.
const paths = Array.isArray(pathOrPaths) ? pathOrPaths : [pathOrPaths]

// Create a new declaration for each path.
paths.forEach((path) => {
declarations.push({ ...configProps, path })
})
}
})

Expand Down
7 changes: 6 additions & 1 deletion tests/integration/100.command.dev.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ test('should respect in-source configuration from edge functions', async (t) =>

await builder
.withEdgeFunction({
config: { path: '/hello-2' },
config: { path: ['/hello-2', '/hello-3'] },
handler: () => new Response('Hello world'),
name: 'hello',
})
Expand All @@ -723,6 +723,11 @@ test('should respect in-source configuration from edge functions', async (t) =>

t.is(res3.statusCode, 200)
t.is(res3.body, 'Hello world')

const res4 = await got(`http://localhost:${port}/hello-3`, { throwHttpErrors: false })

t.is(res4.statusCode, 200)
t.is(res4.body, 'Hello world')
})
})
})
Expand Down

1 comment on commit 27bea34

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📊 Benchmark results

  • Package size: 263 MB

Please sign in to comment.