@@ -8,7 +8,7 @@ import { get_pathname, pattern_to_src } from './utils.js';
88import  {  VERSION  }  from  '@sveltejs/kit' ; 
99
1010const  name  =  '@sveltejs/adapter-vercel' ; 
11- const  DEFAULT_FUNCTION_NAME  =  'fn'  ; 
11+ const  INTERNAL  =  '![-]'  ;   // this name is guaranteed not to conflict with user routes 
1212
1313const  get_default_runtime  =  ( )  =>  { 
1414	const  major  =  Number ( process . version . slice ( 1 ) . split ( '.' ) [ 0 ] ) ; 
@@ -319,7 +319,7 @@ const plugin = function (defaults = {}) {
319319					group . config . runtime  ===  'edge'  ? generate_edge_function  : generate_serverless_function ; 
320320
321321				// generate one function for the group 
322- 				const  name  =  singular  ? DEFAULT_FUNCTION_NAME  : `fn- ${ group . i }  ` ; 
322+ 				const  name  =  singular  ? ` ${ INTERNAL } /catchall`   : `${ INTERNAL } / ${ group . i }  ` ; 
323323
324324				await  generate_function ( 
325325					name , 
@@ -332,12 +332,27 @@ const plugin = function (defaults = {}) {
332332				} 
333333			} 
334334
335+ 			if  ( ! singular )  { 
336+ 				// we need to create a catch-all route so that 404s are handled 
337+ 				// by SvelteKit rather than Vercel 
338+ 
339+ 				const  runtime  =  defaults . runtime  ??  get_default_runtime ( ) ; 
340+ 				const  generate_function  = 
341+ 					runtime  ===  'edge'  ? generate_edge_function  : generate_serverless_function ; 
342+ 
343+ 				await  generate_function ( 
344+ 					`${ INTERNAL }  /catchall` , 
345+ 					/** @type  {any } */  ( {  runtime,  ...defaults  } ) , 
346+ 					[ ] 
347+ 				) ; 
348+ 			} 
349+ 
335350			for  ( const  route  of  builder . routes )  { 
336351				if  ( is_prerendered ( route ) )  continue ; 
337352
338353				const  pattern  =  route . pattern . toString ( ) ; 
339354				const  src  =  pattern_to_src ( pattern ) ; 
340- 				const  name  =  functions . get ( pattern )   ??   'fn-0' ; 
355+ 				const  name  =  functions . get ( pattern ) ; 
341356
342357				const  isr  =  isr_config . get ( route ) ; 
343358				if  ( isr )  { 
@@ -370,24 +385,43 @@ const plugin = function (defaults = {}) {
370385						src : src  +  '/__data.json$' , 
371386						dest : `/${ isr_name }  /__data.json${ q }  ` 
372387					} ) ; 
373- 				}  else  if  ( ! singular )  { 
374- 					static_config . routes . push ( {  src : src  +  '(?:/__data.json)?$' ,  dest : `/${ name }  `  } ) ; 
375- 				} 
376- 			} 
388+ 				}  else  { 
389+ 					// Create a symlink for each route to the main function for better observability 
390+ 					// (without this, every request appears to go through `/![-]`) 
377391
378- 			if  ( ! singular )  { 
379- 				// we need to create a catch-all route so that 404s are handled 
380- 				// by SvelteKit rather than Vercel 
392+ 					// Use 'index' for the root route's filesystem representation 
393+ 					// Use an empty string ('') for the root route's destination name part in Vercel config 
394+ 					const  is_root  =  route . id  ===  '/' ; 
395+ 					const  route_fs_name  =  is_root  ? 'index'  : route . id . slice ( 1 ) ; 
396+ 					const  route_dest_name  =  is_root  ? ''  : route . id . slice ( 1 ) ; 
381397
382- 				const  runtime  =  defaults . runtime  ??  get_default_runtime ( ) ; 
383- 				const  generate_function  = 
384- 					runtime  ===  'edge'  ? generate_edge_function  : generate_serverless_function ; 
398+ 					// Define paths using path.join for safety 
399+ 					const  base_dir  =  path . join ( dirs . functions ,  route_fs_name ) ;  // e.g., .vercel/output/functions/index 
400+ 					// The main symlink should be named based on the route, adjacent to its potential directory 
401+ 					const  main_symlink_path  =  `${ base_dir }  .func` ;  // e.g., .vercel/output/functions/index.func 
402+ 					// The data symlink goes inside the directory 
403+ 					const  data_symlink_path  =  path . join ( base_dir ,  '__data.json.func' ) ;  // e.g., .vercel/output/functions/index/__data.json.func 
385404
386- 				await  generate_function ( 
387- 					DEFAULT_FUNCTION_NAME , 
388- 					/** @type  {any } */  ( {  runtime,  ...defaults  } ) , 
389- 					[ ] 
390- 				) ; 
405+ 					const  target  =  path . join ( dirs . functions ,  `${ name }  .func` ) ;  // The actual function directory e.g., .vercel/output/functions/![-].func 
406+ 
407+ 					// Ensure the directory for the data endpoint symlink exists (e.g., functions/index/) 
408+ 					builder . mkdirp ( base_dir ) ; 
409+ 
410+ 					// Calculate relative paths FROM the directory containing the symlink TO the target 
411+ 					const  relative_for_main  =  path . relative ( path . dirname ( main_symlink_path ) ,  target ) ; 
412+ 					const  relative_for_data  =  path . relative ( path . dirname ( data_symlink_path ) ,  target ) ;  // This is path.relative(base_dir, target) 
413+ 
414+ 					// Create symlinks 
415+ 					fs . symlinkSync ( relative_for_main ,  main_symlink_path ) ;  // Creates functions/index.func -> ![-].func 
416+ 					fs . symlinkSync ( relative_for_data ,  data_symlink_path ) ;  // Creates functions/index/__data.json.func -> ../![-].func 
417+ 
418+ 					// Add route to the config 
419+ 					static_config . routes . push ( { 
420+ 						src : src  +  '(?:/__data.json)?$' ,  // Matches the incoming request path 
421+ 						dest : `/${ route_dest_name }  `  // Maps to the function: '/' for root, '/about' for about, etc. 
422+ 						// Vercel uses this dest to find the corresponding .func dir/symlink 
423+ 					} ) ; 
424+ 				} 
391425			} 
392426
393427			// optional chaining to support older versions that don't have this setting yet 
@@ -412,7 +446,7 @@ const plugin = function (defaults = {}) {
412446
413447			// Catch-all route must come at the end, otherwise it will swallow all other routes, 
414448			// including ISR aliases if there is only one function 
415- 			static_config . routes . push ( {  src : '/.*' ,  dest : `/${ DEFAULT_FUNCTION_NAME }  `  } ) ; 
449+ 			static_config . routes . push ( {  src : '/.*' ,  dest : `/${ INTERNAL } /catchall `  } ) ; 
416450
417451			builder . log . minor ( 'Writing routes...' ) ; 
418452
0 commit comments