Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: cache edge function dependency tracing #6440

Merged
merged 2 commits into from Mar 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/lib/edge-functions/registry.ts
Expand Up @@ -26,6 +26,8 @@ export interface Config {
edge_functions?: Declaration[]
[key: string]: unknown
}

type DependencyCache = Record<string, string[]>
type EdgeFunctionEvent = 'buildError' | 'loaded' | 'reloaded' | 'reloading' | 'removed'
type Route = Omit<Manifest['routes'][0], 'pattern'> & { pattern: RegExp }
type RunIsolate = Awaited<ReturnType<typeof import('@netlify/edge-bundler').serve>>
Expand All @@ -49,13 +51,21 @@ interface EdgeFunctionsRegistryOptions {
}

/**
* Helper method which, given a edge bundler graph module and an index of modules by path, traverses its dependency tree
* and returns an array of all of ist local dependencies
* Given an Edge Bundler module graph and an index of modules by path,
* traverses its dependency tree and returns an array of all of its
* local dependencies.
*/
function traverseLocalDependencies(
{ dependencies = [] }: ModuleJson,
{ dependencies = [], specifier }: ModuleJson,
modulesByPath: Map<string, ModuleJson>,
cache: DependencyCache,
): string[] {
// If we've already traversed this specifier, return the cached list of
// dependencies.
if (cache[specifier] !== undefined) {
return cache[specifier]
}

return dependencies.flatMap((dependency) => {
// We're interested in tracking local dependencies, so we only look at
// specifiers with the `file:` protocol.
Expand All @@ -66,17 +76,20 @@ function traverseLocalDependencies(
) {
return []
}

const { specifier: dependencyURL } = dependency.code
const dependencyPath = fileURLToPath(dependencyURL)
const dependencyModule = modulesByPath.get(dependencyPath)

// No module indexed for this dependency
// No module indexed for this dependency.
if (dependencyModule === undefined) {
return [dependencyPath]
}

// Keep traversing the child dependencies and return the current dependency path
return [...traverseLocalDependencies(dependencyModule, modulesByPath), dependencyPath]
// Keep traversing the child dependencies and return the current dependency path.
cache[specifier] = [...traverseLocalDependencies(dependencyModule, modulesByPath, cache), dependencyPath]

return cache[specifier]
})
}

Expand Down Expand Up @@ -485,9 +498,11 @@ export class EdgeFunctionsRegistry {
}
})

const dependencyCache: DependencyCache = {}

// We start from our functions and we traverse through their dependency tree
functionModules.forEach(({ functionName, module }) => {
const traversedPaths = traverseLocalDependencies(module, modulesByPath)
const traversedPaths = traverseLocalDependencies(module, modulesByPath, dependencyCache)
traversedPaths.forEach((dependencyPath) => {
this.dependencyPaths.add(dependencyPath, functionName)
})
Expand Down