import fs from 'fs-extra' ; import { join , parse , relative , dirname , basename , extname } from 'path' ; import glob from './fs/glob' ; import { normalizePath } from './fs/normalize-path' ; import { FILES_SYMBOL , Lambda } from './lambda' ; import type { BuildOptions , Files } from './types' ; import { debug , getIgnoreFilter } from '.' ; const ignoredPaths = [ '.output' , '.vercel' , 'vercel.json' , 'now.json' ] ; const shouldIgnorePath = ( file , ignoreFilter , ignoreFile ) => { const isNative = ignoredPaths . some ( item => { return file . startsWith ( item ) ; } ) ; if ( ! ignoreFile ) { return isNative ; } return isNative || ignoreFilter ( file ) ; } ; const getSourceFiles = async ( workPath , ignoreFilter ) => { const list = await glob ( '**' , { cwd : workPath , } ) ; for ( const file in list ) { if ( shouldIgnorePath ( file , ignoreFilter , true ) ) { delete list [ file ] ; } } return list ; } ; param buildRuntime param packageName param ext export function _experimental_convertRuntimeToPlugin ( buildRuntime , packageName , ext ) { return async function build ( { workPath } ) { const ignoreFilter = await getIgnoreFilter ( workPath ) ; const sourceFilesPreBuild = await getSourceFiles ( workPath , ignoreFilter ) ; const entrypoints = Object . assign ( { } , sourceFilesPreBuild ) ; const entrypointMatch = new RegExp ( `^api/.*${ ext }$` ) ; for ( const file in entrypoints ) { if ( ! entrypointMatch . test ( file ) ) { delete entrypoints [ file ] ; } } const pages = { } ; const pluginName = packageName . replace ( 'vercel-plugin-' , '' ) ; const outputPath = join ( workPath , '.output' ) ; const traceDir = join ( outputPath , `inputs` , `api-routes-${ pluginName }` ) ; await fs . ensureDir ( traceDir ) ; const entryRoot = join ( outputPath , 'server' , 'pages' ) ; for ( const entrypoint of Object . keys ( entrypoints ) ) { const { output } = await buildRuntime ( { files : sourceFilesPreBuild , entrypoint , workPath , config : { zeroConfig : true , } , meta : { avoidTopLevelInstall : true , skipDownload : true , } , } ) ; const lambdaFiles = output [ FILES_SYMBOL ] ; for ( const file in lambdaFiles ) { if ( shouldIgnorePath ( file , ignoreFilter , false ) ) { delete lambdaFiles [ file ] ; } } let handlerFileBase = output . handler ; let handlerFile = lambdaFiles [ handlerFileBase ] ; let handlerHasImport = false ; const { handler } = output ; const handlerMethod = handler . split ( '.' ) . pop ( ) ; const handlerFileName = handler . replace ( `.${ handlerMethod }` , '' ) ; if ( ! handlerFile ) { handlerFileBase = handlerFileName + ext ; handlerFile = lambdaFiles [ handlerFileBase ] ; handlerHasImport = true ; } if ( ! handlerFile || ! handlerFile . fsPath ) { throw new Error ( `Could not find a handler file. Please ensure that \`files\` for the returned \`Lambda\` contains an \`FileFsRef\` named "${ handlerFileBase }" with a valid \`fsPath\`.` ) ; } const handlerExtName = extname ( handlerFile . fsPath ) ; const entryBase = basename ( entrypoint ) . replace ( ext , handlerExtName ) ; const entryPath = join ( dirname ( entrypoint ) , entryBase ) ; const entry = join ( entryRoot , entryPath ) ; await fs . ensureDir ( dirname ( entry ) ) ; if ( handlerHasImport ) { const { fsPath } = handlerFile ; const encoding = 'utf-8' ; const locationPrefix = relative ( entry , outputPath ) ; let handlerContent = await fs . readFile ( fsPath , encoding ) ; const importPaths = [ entrypoint . slice ( 0 , - ext . length ) , ] ; const patterns = importPaths . map ( path => { return new RegExp ( `('|")(${ path . replace ( /\./g , '\\.' ) })('|")` , 'g' ) ; } ) ; let replacedMatch = null ; for ( const pattern of patterns ) { const newContent = handlerContent . replace ( pattern , ( _ , p1 , p2 , p3 ) => { return `${ p1 }${ join ( locationPrefix , p2 ) }${ p3 }` ; } ) ; if ( newContent !== handlerContent ) { debug ( `Replaced "${ pattern }" inside "${ entry }" to ensure correct import of user-provided request handler` ) ; handlerContent = newContent ; replacedMatch = true ; } } if ( ! replacedMatch ) { new Error ( `No replacable matches for "${ importPaths [ 0 ] }" or "${ importPaths [ 1 ] }" found in "${ fsPath }"` ) ; } await fs . writeFile ( entry , handlerContent , encoding ) ; } else { await fs . copy ( handlerFile . fsPath , entry ) ; } await fs . remove ( handlerFile . fsPath ) ; debug ( `Removed temporary file "${ handlerFile . fsPath }"` ) ; const nft = `${ entry }.nft.json` ; const json = JSON . stringify ( { version : 2 , files : Object . keys ( lambdaFiles ) . map ( file => { const { fsPath } = lambdaFiles [ file ] ; if ( ! fsPath ) { throw new Error ( `File "${ file }" is missing valid \`fsPath\` property` ) ; } if ( file === handlerFileBase ) { return ; } return normalizePath ( relative ( dirname ( nft ) , fsPath ) ) ; } ) . filter ( Boolean ) , } ) ; await fs . writeFile ( nft , json ) ; pages [ normalizePath ( entryPath ) ] = { handler : handler . replace ( handlerFileName , parse ( entry ) . name ) , runtime : output . runtime , memory : output . memory , maxDuration : output . maxDuration , environment : output . environment , allowQuery : output . allowQuery , } ; } await _experimental_updateFunctionsManifest ( { workPath , pages } ) ; } ; } async function readJson ( filePath ) { try { const str = await fs . readFile ( filePath , 'utf8' ) ; return JSON . parse ( str ) ; } catch ( err ) { if ( err . code === 'ENOENT' ) { return { } ; } throw err ; } } /**
 * If `.output/functions-manifest.json` exists, append to the pages
 * property. Otherwise write a new file.
 */ export async function _experimental_updateFunctionsManifest ( { workPath , pages , } ) { const functionsManifestPath = join ( workPath , '.output' , 'functions-manifest.json' ) ; const functionsManifest = await readJson ( functionsManifestPath ) ; if ( ! functionsManifest . version ) functionsManifest . version = 2 ; if ( ! functionsManifest . pages ) functionsManifest . pages = { } ; for ( const [ pageKey , pageConfig ] of Object . entries ( pages ) ) { functionsManifest . pages [ pageKey ] = { ... pageConfig } ; } await fs . writeFile ( functionsManifestPath , JSON . stringify ( functionsManifest ) ) ; } /**
 * Append routes to the `routes-manifest.json` file.
 * If the file does not exist, it will be created.
 */ export async function _experimental_updateRoutesManifest ( { workPath , redirects , rewrites , headers , dynamicRoutes , staticRoutes , } ) { const routesManifestPath = join ( workPath , '.output' , 'routes-manifest.json' ) ; const routesManifest = await readJson ( routesManifestPath ) ; if ( ! routesManifest . version ) routesManifest . version = 3 ; if ( routesManifest . pages404 === undefined ) routesManifest . pages404 = true ; if ( redirects ) { if ( ! routesManifest . redirects ) routesManifest . redirects = [ ] ; routesManifest . redirects . push ( ... redirects ) ; } if ( rewrites ) { if ( ! routesManifest . rewrites ) routesManifest . rewrites = [ ] ; routesManifest . rewrites . push ( ... rewrites ) ; } if ( headers ) { if ( ! routesManifest . headers ) routesManifest . headers = [ ] ; routesManifest . headers . push ( ... headers ) ; } if ( dynamicRoutes ) { if ( ! routesManifest . dynamicRoutes ) routesManifest . dynamicRoutes = [ ] ; routesManifest . dynamicRoutes . push ( ... dynamicRoutes ) ; } if ( staticRoutes ) { if ( ! routesManifest . staticRoutes ) routesManifest . staticRoutes = [ ] ; routesManifest . staticRoutes . push ( ... staticRoutes ) ; } await fs . writeFile ( routesManifestPath , JSON . stringify ( routesManifest ) ) ; }