import execa from 'execa' ; import * as esbuild from 'esbuild' ; import { flatten } from '../blaze/flatten' ; import { pipe } from '../blaze/pipe' ; import { map } from '../blaze/map' ; import { transduce } from '../blaze/transduce' ; import glob from 'globby' ; import path from 'path' ; import { watch as createWatcher } from 'chokidar' ; import { tscPlugin } from './plugins/tscPlugin' ; import { onErrorPlugin } from './plugins/onErrorPlugin' ; import { fixImportsPlugin } from './plugins/fixImportsPlugin' ; import { handle } from '../blaze/handle' ; import { replaceWithPlugin } from './plugins/replaceWithPlugin' ; import { depCheckPlugin } from './plugins/depCheckPlugin' ; export type BuildResult = esbuild . BuildResult ; export type BuildOptions = esbuild . BuildOptions & { outbase ? : never ; } ; const DEFAULT_BUILD_OPTIONS = { platform : 'node' , keepNames : true , logLevel : 'error' , tsconfig : 'tsconfig.build.json' , incremental : process . env . WATCH === 'true' , metafile : true , } as const ; param options const applyEsmDefaults = ( options ) : BuildOptions => ( { ... DEFAULT_BUILD_OPTIONS , format : 'esm' , target : 'esnext' , outExtension : { '.js' : '.mjs' } , resolveExtensions : [ '.ts' , '.js' , '.mjs' , '.node' ] , entryPoints : glob . sync ( './src/**/*.{j,t}s' , { ignore : [ './src/__tests__/**/*' ] , } ) , mainFields : [ 'module' , 'main' ] , ... options , outfile : options . outfile ? getEsmOutFile ( options ) : undefined , outdir : options . outfile ? undefined : getEsmOutDir ( options ) , plugins : [ ... ( options . plugins ?? [ ] ) , fixImportsPlugin , onErrorPlugin ] , } ) ; param options const applyCjsDefaults = ( options ) : BuildOptions => ( { ... DEFAULT_BUILD_OPTIONS , format : 'cjs' , target : 'es2018' , outExtension : { '.js' : '.js' } , resolveExtensions : [ '.mjs' ] , mainFields : [ 'module' ] , ... options , entryPoints : options . outfile ? glob . sync ( `./${ getEsmOutFile ( options ) }.mjs` ) : glob . sync ( `./${ getEsmOutDir ( options ) }/**/*.mjs` ) , outdir : options . outfile ? undefined : getOutDir ( options ) , plugins : [ replacePlugin , ... ( options . plugins ?? [ ] ) , tscPlugin , onErrorPlugin ] , } ) ; const replacePlugin = replaceWithPlugin ( [ [ /var __require =.*?(?=var)/gs , '' ] , [ /__require(?!\w)/gs , 'require' ] , ] ) ; param options returns function createBuildOptions ( options ) { return flatten ( map ( options , ( options ) => [ ( ) => applyEsmDefaults ( options ) , ( ) => applyCjsDefaults ( options ) , ] ) ) ; } /**
 * We only want to trigger the glob search once we are ready, and that is when
 * the previous build has finished. We get the build options from the deferred.
 */ function computeOptions ( options ) { return options ( ) ; } /**
 * Extensions are not automatically by esbuild set for `options.outfile`. We
 * look at the set `options.outExtension` and we add that to `options.outfile`.
 */ function addExtensionFormat ( options ) { if ( options . outfile && options . outExtension ) { const ext = options . outExtension [ '.js' ] ; options . outfile = `${ options . outfile }${ ext }` ; } return options ; } /**
 * If we don't have `options.outfile`, we default `options.outdir`
 */ function addDefaultOutDir ( options ) { if ( options . outfile === undefined ) { options . outdir = getOutDir ( options ) ; } return options ; } /**
 * Execute esbuild with all the configurations we pass
 */ async function executeEsBuild ( options ) { return esbuild . build ( options ) ; } /**
 * A blank esbuild run to do an analysis of our deps
 */ async function dependencyCheck ( options ) { if ( process . env . DEV === 'true' ) return options ; const buildPromise = esbuild . build ( { entryPoints : glob . sync ( '**/*.{j,t}s' , { ignore : [ './src/__tests__/**/*' ] , gitignore : true , } ) , logLevel : 'silent' , bundle : true , write : false , outdir : 'out' , plugins : [ depCheckPlugin ( options . bundle ) ] , } ) ; await buildPromise . catch ( ( ) => { } ) ; return options ; } param options export async function build ( options ) { await transduce . async ( options , dependencyCheck ) ; return transduce . async ( createBuildOptions ( options ) , pipe . async ( computeOptions , addExtensionFormat , addDefaultOutDir , executeEsBuild , watch ) ) ; } param builds function watch ( build ) { if ( process . env . WATCH !== 'true' ) return build ; const watched = getWatchedFiles ( build ) ; const watcher = createWatcher ( watched , { ignoreInitial : true , useFsEvents : true , } ) ; watcher . once ( 'all' , async ( ) => { const timeBefore = Date . now ( ) ; const result = await handle . async ( ( ) => { return build ?. rebuild ?. ( ) ; } ) ; if ( result instanceof Error ) { console . error ( result . message ) ; watch ( build ) ; } else { watch ( result ) ; } const timeAfter = Date . now ( ) ; console . log ( `${ timeAfter - timeBefore }ms` ) ; } ) ; return undefined ; } function getOutDir ( options ) { if ( options . outfile !== undefined ) { return path . dirname ( options . outfile ) ; } return options . outdir ?? 'dist' ; } function getEsmOutDir ( options ) { return `${ getOutDir ( options ) }/esm` ; } function getEsmOutFile ( options ) { if ( options . outfile !== undefined ) { const dirname = getOutDir ( options ) ; const filename = path . basename ( options . outfile ) ; return `${ dirname }/esm/${ filename }` ; } return undefined ; } export function run ( command ) { return execa . command ( command , { preferLocal : true , shell : true , stdio : 'inherit' , } ) ; } function getWatchedFiles ( build ) { return Object . keys ( build ?. metafile ?. inputs ?? { } ) ; }