Skip to content

Commit

Permalink
feat(cli): added automatic js minification on deployment
Browse files Browse the repository at this point in the history
This is implemented a little shoddily at the moment, but since the
`wasm-bindgen` format is reasonably predictable, it should be fine, and
the errors are clear about how to disable it if there are problems.

Also removed an unnecessary "not running" message on serverful deployment.
  • Loading branch information
arctic-hen7 committed Jan 14, 2023
1 parent 2a9bfde commit d516f0a
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/perseus-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ flate2 = "1"
directories = "4"
cargo_metadata = "0.15"
cargo-lock = "8"
minify-js = "0.4"

[dev-dependencies]
assert_cmd = "2"
Expand Down
51 changes: 47 additions & 4 deletions packages/perseus-cli/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use crate::serve;
use fs_extra::copy_items;
use fs_extra::dir::{copy as copy_dir, CopyOptions};
use indicatif::MultiProgress;
use minify_js::{minify, TopLevelMode};
use std::fs;
use std::path::Path;
use std::path::PathBuf;

/// Deploys the user's app to the `pkg/` directory (can be changed with
Expand All @@ -26,9 +28,9 @@ pub fn deploy(
) -> Result<i32, Error> {
// Fork at whether we're using static exporting or not
let exit_code = if opts.export_static {
deploy_export(dir, opts.output.to_string(), tools, global_opts)?
deploy_export(dir, opts.output.to_string(), opts, tools, global_opts)?
} else {
deploy_full(dir, opts.output.to_string(), tools, global_opts)?
deploy_full(dir, opts.output.to_string(), opts, tools, global_opts)?
};

Ok(exit_code)
Expand All @@ -40,6 +42,7 @@ pub fn deploy(
fn deploy_full(
dir: PathBuf,
output: String,
opts: &DeployOpts,
tools: &Tools,
global_opts: &Opts,
) -> Result<i32, Error> {
Expand All @@ -61,8 +64,8 @@ fn deploy_full(
tools,
global_opts,
&MultiProgress::new(),
// We're not testing
false,
// Don't emit the "not running" message
true,
)?;
if serve_exit_code != 0 {
return Ok(serve_exit_code);
Expand Down Expand Up @@ -158,6 +161,13 @@ fn deploy_full(
.into());
}

if !opts.no_minify_js {
minify_js(
&dir.join("dist/pkg/perseus_engine.js"),
&output_path.join("dist/pkg/perseus_engine.js"),
)?
}

println!();
println!("Deployment complete 🚀! Your app is now available for serving in the standalone folder '{}'! You can run it by executing the `server` binary in that folder.", &output_path.to_str().map(|s| s.to_string()).unwrap());

Expand All @@ -173,6 +183,7 @@ fn deploy_full(
fn deploy_export(
dir: PathBuf,
output: String,
opts: &DeployOpts,
tools: &Tools,
global_opts: &Opts,
) -> Result<i32, Error> {
Expand Down Expand Up @@ -253,8 +264,40 @@ fn deploy_export(
.into());
}

if !opts.no_minify_js {
minify_js(
&dir.join("dist/exported/.perseus/bundle.js"),
&output_path.join(".perseus/bundle.js"),
)?
}

println!();
println!("Deployment complete 🚀! Your app is now available for serving in the standalone folder '{}'! You can run it by serving the contents of that folder however you'd like.", &output_path.to_str().map(|s| s.to_string()).unwrap());

Ok(0)
}

/// Minifies the given JS code.
fn minify_js(from: &Path, to: &Path) -> Result<(), DeployError> {
let js_bundle = fs::read_to_string(from)
.map_err(|err| DeployError::ReadUnminifiedJsFailed { source: err })?;

// TODO Remove this pending wilsonzlin/minify-js#7
// `minify-js` has a hard time with non-default exports right now, which is
// actually fine, because we don't need `initSync` whatsoever
let js_bundle = js_bundle.replace("export { initSync }", "// export { initSync }");

let mut minified = Vec::new();
minify(
TopLevelMode::Global,
js_bundle.as_bytes().to_vec(),
// Guaranteed to be UTF-8 output
&mut minified,
)
.map_err(|err| DeployError::MinifyError { source: err })?;
let minified =
String::from_utf8(minified).map_err(|err| DeployError::MinifyNotUtf8 { source: err })?;
fs::write(to, &minified).map_err(|err| DeployError::WriteMinifiedJsFailed { source: err })?;

Ok(())
}
20 changes: 20 additions & 0 deletions packages/perseus-cli/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,26 @@ pub enum DeployError {
#[source]
source: std::io::Error,
},
#[error("failed to minify javascript bundle (this is probably an upstream bug, re-try with `--no-minify-js`)")]
MinifyError {
#[source]
source: minify_js::MinifyError,
},
#[error("minified js was not utf-8 (this is a bug, re-try with `--no-minify-js` for now)")]
MinifyNotUtf8 {
#[source]
source: std::string::FromUtf8Error,
},
#[error("failed to read unminified js (if this persists, try `perseus clean`)")]
ReadUnminifiedJsFailed {
#[source]
source: std::io::Error,
},
#[error("failed to write minified js (if this persists, try `perseus clean`)")]
WriteMinifiedJsFailed {
#[source]
source: std::io::Error,
},
}

#[derive(Error, Debug)]
Expand Down
5 changes: 4 additions & 1 deletion packages/perseus-cli/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,12 @@ pub struct DeployOpts {
/// Change the output from `pkg/` to somewhere else
#[clap(short, long, default_value = "pkg")]
pub output: String,
/// Export you app to purely static files (see `export`)
/// Export your app to purely static files (see `export`)
#[clap(short, long)]
pub export_static: bool,
/// Don't minify JavaScript (this will decrease performance)
#[clap(long)]
pub no_minify_js: bool,
}
/// Runs the `tinker` action of plugins, which lets them modify the Perseus
/// engine
Expand Down
8 changes: 4 additions & 4 deletions packages/perseus-cli/src/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ pub fn serve(
tools: &Tools,
global_opts: &Opts,
spinners: &MultiProgress,
testing: bool,
silent_no_run: bool,
) -> Result<(i32, Option<String>), ExecutionError> {
// Set the environment variables for the host and port
// NOTE Another part of this code depends on setting these in this way
Expand Down Expand Up @@ -278,9 +278,9 @@ pub fn serve(
// The user doesn't want to run the server, so we'll give them the executable
// path instead
let exec_str = (*exec.lock().unwrap()).to_string();
// Only tell the user about this if we're not testing (which is a whole separate
// workflow)
if !testing {
// Only tell the user about this if they've told us not to run (since deployment
// and testing both implicitly do this)
if !silent_no_run {
println!("Not running server because `--no-run` was provided. You can run it manually by running the following executable from the root of the project.\n{}", &exec_str);
}
Ok((0, Some(exec_str)))
Expand Down

0 comments on commit d516f0a

Please sign in to comment.