import * as fs from 'fs-extra' ; import * as globby from 'globby' ; import * as path from 'path' ; import { intersection , isEmpty , lensProp , map , over , pipe , reject , replace , test , without , } from 'ramda' ; import * as semver from 'semver' ; import { EsbuildServerlessPlugin } from '.' ; import { ONLY_PREFIX , SERVERLESS_FOLDER } from './constants' ; import { doSharePath , flatDep , getDepsFromBundle } from './helper' ; import * as Packagers from './packagers' ; import { IFiles } from './types' ; import { humanSize , zip , trimExtension } from './utils' ; function setFunctionArtifactPath ( this , func , artifactPath ) { const version = this . serverless . getVersion ( ) ; if ( semver . lt ( version , '1.18.0' ) ) { func . artifact = artifactPath ; func . package = Object . assign ( { } , func . package , { disable : true } ) ; this . serverless . cli . log ( `${ func . name } is packaged by the esbuild plugin. Ignore messages from SLS.` ) ; } else { func . package = { artifact : artifactPath , } ; } } const excludedFilesDefault = [ 'package-lock.json' , 'pnpm-lock.yaml' , 'yarn.lock' , 'package.json' ] ; export const filterFilesForZipPackage = ( { files , functionAlias , includedFiles , excludedFiles , hasExternals , isGoogleProvider , depWhiteList , } ) => { return files . filter ( ( { localPath } ) => { if ( includedFiles . find ( ( file ) => file === localPath ) ) { return true ; } if ( excludedFiles . find ( ( p ) => localPath . startsWith ( `${ p }.` ) ) ) return false ; if ( localPath . startsWith ( ONLY_PREFIX ) && ! localPath . startsWith ( `${ ONLY_PREFIX }${ functionAlias }/` ) ) return false ; if ( localPath . startsWith ( 'node_modules' ) ) { if ( ! hasExternals || isGoogleProvider ) return false ; if ( ! depWhiteList . find ( ( dep ) => doSharePath ( localPath , 'node_modules/' + dep ) ) ) return false ; } return true ; } ) ; } ; export async function pack ( this ) { const isGoogleProvider = this . serverless ?. service ?. provider ?. name === 'google' ; const excludedFiles = isGoogleProvider ? [ ] : excludedFilesDefault ; if ( isGoogleProvider && this . serverless ?. service ?. package ?. individually ) throw new Error ( 'Packaging failed: cannot package function individually when using Google provider' ) ; const files = globby . sync ( '**' , { cwd : this . buildDirPath , dot : true , onlyFiles : true , } ) . filter ( ( p ) => ! excludedFiles . includes ( p ) ) . map ( ( localPath ) => ( { localPath , rootPath : path . join ( this . buildDirPath , localPath ) } ) ) ; if ( isEmpty ( files ) ) { console . log ( 'Packaging: No files found. Skipping esbuild.' ) ; return ; } if ( ! this . serverless ?. service ?. package ?. individually ) { const zipName = `${ this . serverless . service . service }.zip` ; const artifactPath = path . join ( this . workDirPath , SERVERLESS_FOLDER , zipName ) ; const filesPathList = pipe < IFiles , IFiles , IFiles > ( reject ( test ( /^__only_[^/]+$/ ) ) as ( x : IFiles ) => IFiles , map ( over ( lensProp ( 'localPath' ) , replace ( /^__only_[^/]+\// , '' ) ) ) ) ( files ) ; const startZip = Date . now ( ) ; await zip ( artifactPath , filesPathList , this . buildOptions . nativeZip ) ; const { size } = fs . statSync ( artifactPath ) ; this . serverless . cli . log ( `Zip service ${ this . serverless . service . service } - ${ humanSize ( size ) } [${ Date . now ( ) - startZip } ms]` ) ; this . serverless . service . package . artifact = artifactPath ; return ; } const packager = await Packagers . get ( this . buildOptions . packager ) ; const buildResults = this . buildResults ; const bundlePathList = buildResults . map ( ( b ) => b . bundlePath ) ; let externals = [ ] ; if ( this . buildOptions . exclude !== '*' && ! this . buildOptions . exclude . includes ( '*' ) ) { externals = without < string > ( this . buildOptions . exclude , this . buildOptions . external ) ; } const hasExternals = ! ! externals ?. length ; const packagerDependenciesList = hasExternals ? await packager . getProdDependencies ( this . buildDirPath ) : { } ; const packageFiles = await globby ( this . serverless . service . package . patterns ) ; await Promise . all ( buildResults . map ( async ( { func , functionAlias , bundlePath } ) => { const excludedFiles = bundlePathList . filter ( ( p ) => ! bundlePath . startsWith ( p ) ) . map ( trimExtension ) ; const functionFiles = await globby ( func . package . patterns ) ; const includedFiles = [ ... packageFiles , ... functionFiles ] ; let depWhiteList = [ ] ; if ( hasExternals ) { const bundleDeps = getDepsFromBundle ( path . join ( this . buildDirPath , bundlePath ) ) ; const bundleExternals = intersection ( bundleDeps , externals ) ; depWhiteList = flatDep ( packagerDependenciesList . dependencies , bundleExternals ) ; } const zipName = `${ functionAlias }.zip` ; const artifactPath = path . join ( this . workDirPath , SERVERLESS_FOLDER , zipName ) ; const filesPathList = filterFilesForZipPackage ( { files , functionAlias , includedFiles , excludedFiles , hasExternals , isGoogleProvider , depWhiteList , } ) . map ( ( { localPath , ... rest } ) => ( { localPath : localPath . replace ( `${ ONLY_PREFIX }${ functionAlias }/` , '' ) , ... rest , } ) ) ; const startZip = Date . now ( ) ; await zip ( artifactPath , filesPathList , this . buildOptions . nativeZip ) ; const { size } = fs . statSync ( artifactPath ) ; this . serverless . cli . log ( `Zip function: ${ functionAlias } - ${ humanSize ( size ) } [${ Date . now ( ) - startZip } ms]` ) ; setFunctionArtifactPath . call ( this , func , path . relative ( this . serviceDirPath , artifactPath ) ) ; } ) ) ; }