@@ -23,6 +23,8 @@ type EdgeFunctionEvent = 'buildError' | 'loaded' | 'reloaded' | 'reloading' | 'r
2323type Route = Omit < Manifest [ 'routes' ] [ 0 ] , 'pattern' > & { pattern : RegExp }
2424type RunIsolate = Awaited < ReturnType < typeof import ( '@netlify/edge-bundler' ) . serve > >
2525
26+ type ModuleJson = ModuleGraph [ 'modules' ] [ number ]
27+
2628const featureFlags = { edge_functions_correct_order : true }
2729
2830interface EdgeFunctionsRegistryOptions {
@@ -40,6 +42,40 @@ interface EdgeFunctionsRegistryOptions {
4042 servePath : string
4143}
4244
45+ /**
46+ * Helper method which, given a edge bundler graph module and an index of modules by path, traverses its dependency tree
47+ * and returns an array of all of ist local dependencies
48+ */
49+ function traverseLocalDependencies (
50+ { dependencies = [ ] } : ModuleJson ,
51+ modulesByPath : Map < string , ModuleJson > ,
52+ ) : string [ ] {
53+ return dependencies
54+ . map ( ( dependency ) => {
55+ // We're interested in tracking local dependencies, so we only look at
56+ // specifiers with the `file:` protocol.
57+ if (
58+ dependency . code === undefined ||
59+ typeof dependency . code . specifier !== 'string' ||
60+ ! dependency . code . specifier . startsWith ( 'file://' )
61+ ) {
62+ return [ ]
63+ }
64+ const { specifier : dependencyURL } = dependency . code
65+ const dependencyPath = fileURLToPath ( dependencyURL )
66+ const dependencyModule = modulesByPath . get ( dependencyPath )
67+
68+ // No module indexed for this dependency
69+ if ( dependencyModule === undefined ) {
70+ return [ dependencyPath ]
71+ }
72+
73+ // Keep traversing the child dependencies and return the current dependency path
74+ return [ ...traverseLocalDependencies ( dependencyModule , modulesByPath ) , dependencyPath ]
75+ } )
76+ . reduce ( ( acc , deps ) => [ ...acc , ...deps ] , [ ] )
77+ }
78+
4379export class EdgeFunctionsRegistry {
4480 private buildError : Error | null = null
4581 private bundler : typeof import ( '@netlify/edge-bundler' )
@@ -420,36 +456,44 @@ export class EdgeFunctionsRegistry {
420456 // Mapping file URLs to names of functions that use them as dependencies.
421457 const dependencyPaths = new Map < string , string [ ] > ( )
422458
459+ // Map modules by path, acting as an helper index
460+ const modulesByPath = new Map < string , ModuleJson > ( )
461+
462+ // Map function modules, acting as an helper index
463+ const functionModules = new Map < string , ModuleJson > ( )
464+
423465 const { modules } = graph
424466
425- modules . forEach ( ( { dependencies = [ ] , specifier } ) => {
467+ modules . forEach ( ( module ) => {
468+ // We're interested in tracking local dependencies, so we only look at
469+ // specifiers with the `file:` protocol.
470+ const { specifier } = module
426471 if ( ! specifier . startsWith ( 'file://' ) ) {
427472 return
428473 }
429474
475+ // Populate the module index
430476 const path = fileURLToPath ( specifier )
431- const functionMatch = functionPaths . get ( path )
477+ modulesByPath . set ( path , module )
432478
433- if ( ! functionMatch ) {
434- return
479+ // Populate the function modules index
480+ const functionMatch = functionPaths . get ( path )
481+ if ( functionMatch ) {
482+ functionModules . set ( path , module )
435483 }
484+ } )
436485
437- dependencies . forEach ( ( dependency ) => {
438- // We're interested in tracking local dependencies, so we only look at
439- // specifiers with the `file:` protocol.
440- if (
441- dependency . code === undefined ||
442- typeof dependency . code . specifier !== 'string' ||
443- ! dependency . code . specifier . startsWith ( 'file://' )
444- ) {
445- return
446- }
447-
448- const { specifier : dependencyURL } = dependency . code
449- const dependencyPath = fileURLToPath ( dependencyURL )
486+ // We start from our functions and we traverse through their dependency tree
487+ functionModules . forEach ( ( functionModule ) => {
488+ const { specifier } = functionModule
489+ const traversedPaths = traverseLocalDependencies ( functionModule , modulesByPath )
490+ const path = fileURLToPath ( specifier )
491+ const functionName = functionPaths . get ( path )
492+ if ( ! functionName ) return
493+ // With the paths for the local dependencies we build the dependency path map
494+ traversedPaths . forEach ( ( dependencyPath ) => {
450495 const functions = dependencyPaths . get ( dependencyPath ) || [ ]
451-
452- dependencyPaths . set ( dependencyPath , [ ...functions , functionMatch ] )
496+ dependencyPaths . set ( dependencyPath , [ ...functions , functionName ] )
453497 } )
454498 } )
455499
0 commit comments