import { resolve } from 'path' ; import _glob , { IOptions as GlobOptions } from 'glob' ; import fs from 'fs-extra' ; import { getVercelIgnore } from '@vercel/client' ; import uniqueStrings from './unique-strings' ; import { Output } from './output/create-output' ; type NullableString = string | null ; function flatten ( arr , res = [ ] ) { for ( const cur of arr ) { if ( Array . isArray ( cur ) ) { flatten ( cur , res ) ; } else { res . push ( cur ) ; } } return res ; } async function glob ( pattern , options ) { return new Promise ( ( resolve , reject ) => { _glob ( pattern , options , ( err , files ) => { err ? reject ( err ) : resolve ( files ) ; } ) ; } ) ; } param { String } maybe param { String } parent const asAbsolute = function ( path , parent ) { if ( path [ 0 ] === '/' ) { return path ; } return resolve ( parent , path ) ; } ; interface StaticFilesOptions { output : Output ; src ? : string ; } param { String } full param { Object } options return { Array } export async function staticFiles ( path , { output , src } ) { const { debug , time } = output ; let files = [ ] ; const source = src || '.' ; const search = await glob ( source , { cwd : path , absolute : true , dot : true } ) ; const { ig } = await getVercelIgnore ( path ) ; const filter = ig . createFilter ( ) ; const prefixLength = path . length + 1 ; const accepts = ( file ) => { const relativePath = file . substr ( prefixLength ) ; if ( relativePath === '' ) { return true ; } const accepted = filter ( relativePath ) ; if ( ! accepted ) { debug ( `Ignoring ${ file }` ) ; } return accepted ; } ; files = await time ( `Locating files ${ path }` , explode ( search , { accepts , output , } ) ) ; return uniqueStrings ( files ) ; } interface ExplodeOptions { accepts : ( file : string ) => boolean ; output : Output ; } param { Array } of param { Array } of param { Object } options return { Array } async function explode ( paths , { accepts , output } ) { const { debug } = output ; const list = async ( file ) : Promise < string | null > => { let path = file ; let s ; if ( ! accepts ( file ) ) { return null ; } try { s = await fs . stat ( path ) ; } catch ( e ) { path = `${ file }.js` ; try { s = await fs . stat ( path ) ; } catch ( e2 ) { debug ( `Ignoring invalid file ${ file }` ) ; return null ; } } if ( s . isDirectory ( ) ) { const all = await fs . readdir ( file ) ; const recursive = many ( all . map ( subdir => asAbsolute ( subdir , file ) ) ) ; return ( recursive as any ) as Promise < string | null > ; } if ( ! s . isFile ( ) ) { debug ( `Ignoring special file ${ file }` ) ; return null ; } return path ; } ; const many = ( all ) => Promise . all ( all . map ( file => list ( file ) ) ) ; const arrayOfArrays = await many ( paths ) ; return flatten ( arrayOfArrays ) . filter ( notNull ) ; } function notNull < T > ( value ) { return value !== null ; }