import assert from "assert" ; import childProcess from "child_process" ; import path from "path" ; import { Awaitable , BeforeSetupResult , MiniflareError , Option , OptionType , Plugin , PluginContext , WranglerConfig , } from "@miniflare/shared" ; export class BuildError extends MiniflareError < number > { } export interface BuildOptions { buildCommand ? : string ; buildBasePath ? : string ; buildWatchPaths ? : string [ ] ; } export class BuildPlugin extends Plugin < BuildOptions > implements BuildOptions { @ Option ( { type : OptionType . STRING , alias : "B" , description : "Command to build project" , fromWrangler : ( { build } ) => build ?. command , } ) buildCommand ? : string ; @ Option ( { type : OptionType . STRING , description : "Working directory for build command" , fromWrangler : ( { build } ) => build ?. cwd , } ) buildBasePath ? : string ; @ Option ( { type : OptionType . ARRAY , description : "Directory to watch for rebuilding on changes" , fromWrangler : ( { build , miniflare } ) => { const watchPaths = miniflare ?. build_watch_dirs ?? [ ] ; if ( build ?. watch_dir ) watchPaths . push ( build . watch_dir ) ; if ( watchPaths . length ) return watchPaths ; if ( build ?. command ) return [ "src" ] ; } , } ) buildWatchPaths ? : string [ ] ; constructor ( ctx , options ? ) { super ( ctx ) ; this . assignOptions ( options ) ; } beforeSetup ( ) { const buildCommand = this . buildCommand ; if ( ! buildCommand ) return { } ; return new Promise ( ( resolve , reject ) => { const build = childProcess . spawn ( buildCommand , { cwd : this . buildBasePath ? path . resolve ( this . ctx . rootPath , this . buildBasePath ) : this . ctx . rootPath , shell : true , stdio : "inherit" , env : { ... process . env , MINIFLARE : "1" } , } ) ; build . on ( "exit" , ( exitCode ) => { if ( exitCode !== 0 ) { const error = new BuildError ( exitCode ?? 0 , `Build failed with exit code ${ exitCode }` ) ; return reject ( error ) ; } this . ctx . log . info ( "Build succeeded" ) ; const watch = this . buildWatchPaths ?. map ( ( watchPath ) => path . resolve ( this . ctx . rootPath , watchPath ) ) ; resolve ( { watch } ) ; } ) ; } ) ; } } internal export function _populateBuildConfig ( config , configDir , configEnv ? ) { if ( config . build || ( config . type !== "webpack" && config . type !== "rust" ) ) { return ; } config . build = { cwd : configDir , upload : { dir : "" } } ; assert ( config . build . upload ) ; const env = configEnv ? ` --env ${ configEnv }` : "" ; if ( config . type === "webpack" ) { let packageDir = "" ; if ( config . site ) { packageDir = config . site [ "entry-point" ] ?? "workers-site" ; } config . build . command = `wrangler build${ env }` ; config . build . upload . main = path . join ( packageDir , "worker" , "script.js" ) ; } else if ( config . type === "rust" ) { const rustScript = path . join ( __dirname , "plugins" , "rust.js" ) ; config . build . command = `wrangler build${ env } && ${ process . execPath } ${ rustScript }` ; config . build . upload . main = path . join ( "worker" , "generated" , "script.js" ) ; config . wasm_modules ??= { } ; config . wasm_modules . wasm = path . join ( configDir , "worker" , "generated" , "script.wasm" ) ; config . miniflare ??= { } ; } }