[−][src]Attribute Macro wasm_run::prelude::main
#[main]
Makes an entrypoint to your binary (not WASM).
It requires to be used with structopt
on an enum
. Please consult the documentation of
structopt
if you don't know how to make an enum
with it.
By default it provides a command Build
and a command Serve
which you can override simply by
providing them manually. Otherwise it uses the defaults (DefaultBuildArgs
and
DefaultServeArgs
).
There are a number of named arguments you can provide to the macro:
other_cli_commands
: a function that is called if you have added new commands to theenum
;pre_build
: a function that is called when the build has not yet started (you can tweak the command-line arguments of the build command);post_build
: a function that is called when the build is finished (after the optimization withwasm-opt
);watch
: a function that is called when the watcher is being initialized (allowing you to add extra things to watch for example);serve
: (only if built with theserve
feature): a function that is called when the HTTP serve is getting configured;run_server
: (only if built without theserve
feature): a function that is called to run the HTTP server;default_build_path
: a function that is called that provides the default directory path when the user didn't provide it through the command-line arguments (the default isworkspace root/build
).
You can also change the package that is built by providing its name in the first positional argument:
#[wasm_run::main("my-frontend-crate")]
Example
Please note that all the code showed here is mostly the actual code for the default hooks. This means that if you don't provide your own hook, this code will be executed. But if you do provide a hook, the code will be replaced.
use anyhow::{Context, Result}; // anyhow is provided by `wasm_run::prelude::*` use fs_extra::dir; // fs_extra is provided by `wasm_run::prelude::*` use std::fs; use std::path::PathBuf; use structopt::StructOpt; // due to limitation, this does *not* come from the prelude use wasm_run::prelude::*; const DEFAULT_INDEX: &str = r#"<!DOCTYPE html><html><head><meta charset="utf-8"/><script type="module">import init from "/app.js";init();</script></head><body></body></html>"#; /// Makes an entrypoint to your binary. #[wasm_run::main( "basic", build_args = BuildCommand, serve_args = ServeCommand, other_cli_commands = run_other_cli_commands, pre_build = pre_build, post_build = post_build, serve = serve, watch = watch, default_build_path = default_build_path, )] #[derive(StructOpt, Debug)] enum Cli { Hello, } /// Define a custom `build` command. #[derive(StructOpt, Debug)] struct BuildCommand { #[structopt(skip)] i: i32, } impl BuildArgs for BuildCommand { fn build_path(&self) -> &PathBuf { self.default_build_path() } fn profiling(&self) -> bool { false } } /// Define a custom `serve` command. #[derive(StructOpt, Debug)] struct ServeCommand { #[structopt(flatten)] build_args: BuildCommand, #[structopt(skip)] j: i32, } impl ServeArgs for ServeCommand { fn build_args(&self) -> &dyn BuildArgs { &self.build_args } fn log(&self) -> bool { false } fn ip(&self) -> &str { "127.0.0.1" } fn port(&self) -> u16 { 3000 } } /// This function is called if you have added new commands to the enum. fn run_other_cli_commands(cli: Cli, _metadata: &Metadata, _package: &Package) -> Result<()> { match cli { Cli::Hello => println!("Hello World!"), } Ok(()) } /// This function is called after the build. fn pre_build( args: &BuildCommand, profile: BuildProfile, command: &mut std::process::Command, ) -> Result<()> { let _i = args.i; // NOTE: this is an example, this hook has no default code. command .arg("--no-default-features") .env("RUSTFLAGS", "-Zmacro-backtrace"); Ok(()) } /// This function is called after the build. fn post_build( args: &BuildCommand, _profile: BuildProfile, wasm_js: String, wasm_bin: Vec<u8>, ) -> Result<()> { let build_path = args.build_path(); let wasm_js_path = build_path.join("app.js"); let wasm_bin_path = build_path.join("app_bg.wasm"); fs::write(&wasm_js_path, wasm_js).with_context(|| { format!("could not write JS file to `{}`", wasm_js_path.display()) })?; fs::write(&wasm_bin_path, wasm_bin).with_context(|| { format!("could not write WASM file to `{}`", wasm_bin_path.display()) })?; let index_path = build_path.join("index.html"); let static_dir = args .package() .manifest_path .parent() .unwrap() .join("static"); if index_path.exists() { fs::copy("index.html", &index_path).context(format!( "could not copy index.html to `{}`", index_path.display() ))?; } else if static_dir.exists() { dir::copy( &static_dir, &build_path, &dir::CopyOptions { content_only: true, ..dir::CopyOptions::new() }, ) .with_context(|| { format!( "could not copy content of directory static: `{}` to `{}`", static_dir.display(), build_path.display() ) })?; } else { fs::write(&index_path, DEFAULT_INDEX).with_context(|| { format!( "could not write default index.html to `{}`", index_path.display() ) })?; } #[cfg(feature = "sass")] { let options = args.sass_options(profile); for style_path in args.sass_lookup_directories() { args.build_sass_from_dir(&style_path, options.clone())?; } } Ok(()) } /// This function is called before serving files. fn serve(args: &ServeCommand, server: &mut Server<()>) -> Result<()> { let _j = args.j; use tide::{Body, Response}; let build_path = args.build_args().build_path(); let index_path = build_path.join("index.html"); server.at("/").serve_dir(args.build_args().build_path())?; server.at("/").get(move |_| { let index_path = index_path.clone(); async move { Ok(Response::from(Body::from_file(index_path).await?)) } }); Ok(()) } /// This function is called when the watcher is being initialized. fn watch(args: &ServeCommand, watcher: &mut RecommendedWatcher) -> Result<()> { let _j = args.j; use notify::{RecursiveMode, Watcher}; use std::collections::HashSet; use std::iter::FromIterator; let metadata = args.build_args().metadata(); let _ = watcher.watch("index.html", RecursiveMode::Recursive); let members: HashSet<_> = HashSet::from_iter(&metadata.workspace_members); for package in metadata.packages.iter().filter(|x| members.contains(&x.id)) { let _ = watcher.watch(&package.manifest_path, RecursiveMode::Recursive); let _ = watcher.watch( package.manifest_path.parent().unwrap().join("src"), RecursiveMode::Recursive, ); } Ok(()) } /// Define another build path if not provided by the user in the command-line arguments. fn default_build_path(metadata: &Metadata, _package: &Package) -> PathBuf { metadata.workspace_root.join("build") }