Skip to content

Commit

Permalink
fix: implement precedence for user functions
Browse files Browse the repository at this point in the history
before this commit, we were relying on the order of calling `scanDirectories`
to set things up right. now that internal functions can be added
during dev, we need to handle precedence more explicitly!
  • Loading branch information
Skn0tt committed Dec 14, 2023
1 parent a14891e commit 20f389c
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 12 deletions.
25 changes: 22 additions & 3 deletions src/lib/functions/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createRequire } from 'module'
import { basename, extname, isAbsolute, join, resolve } from 'path'
import { env } from 'process'

import { listFunctions } from '@netlify/zip-it-and-ship-it'
import { ListedFunction, listFunctions } from '@netlify/zip-it-and-ship-it'
import extractZip from 'extract-zip'

import {
Expand All @@ -28,6 +28,9 @@ export const DEFAULT_FUNCTION_URL_EXPRESSION = /^\/.netlify\/(functions|builders
const TYPES_PACKAGE = '@netlify/functions'
const ZIP_EXTENSION = '.zip'

const isInternalFunction = (func: ListedFunction | NetlifyFunction) =>
func.mainFile.includes(getPathInProject([INTERNAL_FUNCTIONS_FOLDER]))

/**
* @typedef {"buildError" | "extracted" | "loaded" | "missing-types-package" | "reloaded" | "reloading" | "removed"} FunctionEvent
*/
Expand Down Expand Up @@ -476,13 +479,25 @@ export class FunctionsRegistry {
config: this.config.functions,
})

console.log({ functions })
// user-defined functions take precedence over internal functions,
// so we want to ignore any internal functions where there's a user-defined one with the same name
const ignoredFunctions = new Set(
functions
.filter(
(func) =>
isInternalFunction(func) &&
this.functions.has(func.name) &&
!isInternalFunction(this.functions.get(func.name)!),
)
.map((func) => func.name),
)

// Before registering any functions, we look for any functions that were on
// the previous list but are missing from the new one. We unregister them.
const deletedFunctions = [...this.functions.values()].filter((oldFunc) => {
const isFound = functions.some(
(newFunc) => newFunc.name === oldFunc.name && newFunc.mainFile === oldFunc.mainFile,
(newFunc) =>
ignoredFunctions.has(newFunc.name) || (newFunc.name === oldFunc.name && newFunc.mainFile === oldFunc.mainFile),
)

return !isFound
Expand All @@ -496,6 +511,10 @@ export class FunctionsRegistry {
// where the last ones precede the previous ones. This is why
// we reverse the array so we get the right functions precedence in the CLI.
functions.reverse().map(async ({ displayName, mainFile, name, runtime: runtimeName }) => {
if (ignoredFunctions.has(name)) {
return
}

const runtime = runtimes[runtimeName]

// If there is no matching runtime, it means this function is not yet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,17 +569,20 @@ exports.handler = async () => ({
body: 'User',
}),
})
.withFunction({
path: 'hello.js',
pathPrefix: '.netlify/functions-internal',
handler: async () => ({
statusCode: 200,
body: 'Internal',
}),
})
.buildAsync()
.build()

await withDevServer({ cwd: builder.directory, args }, async ({ outputBuffer, port, waitForLogMatching }) => {
await builder
.withFunction({
path: 'hello.js',
pathPrefix: '.netlify/functions-internal',
handler: async () => ({
statusCode: 200,
body: 'Internal',
}),
})
.build()

await tryAndLogOutput(async () => {
t.expect(await fetch(`http://localhost:${port}/.netlify/functions/hello`).then((res) => res.text())).toEqual(
'User',
Expand Down

0 comments on commit 20f389c

Please sign in to comment.