Skip to content

Commit d516f0a

Browse files
committed
feat(cli): added automatic js minification on deployment
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.
1 parent 2a9bfde commit d516f0a

File tree

5 files changed

+76
-9
lines changed

5 files changed

+76
-9
lines changed

packages/perseus-cli/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ flate2 = "1"
4343
directories = "4"
4444
cargo_metadata = "0.15"
4545
cargo-lock = "8"
46+
minify-js = "0.4"
4647

4748
[dev-dependencies]
4849
assert_cmd = "2"

packages/perseus-cli/src/deploy.rs

+47-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use crate::serve;
77
use fs_extra::copy_items;
88
use fs_extra::dir::{copy as copy_dir, CopyOptions};
99
use indicatif::MultiProgress;
10+
use minify_js::{minify, TopLevelMode};
1011
use std::fs;
12+
use std::path::Path;
1113
use std::path::PathBuf;
1214

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

3436
Ok(exit_code)
@@ -40,6 +42,7 @@ pub fn deploy(
4042
fn deploy_full(
4143
dir: PathBuf,
4244
output: String,
45+
opts: &DeployOpts,
4346
tools: &Tools,
4447
global_opts: &Opts,
4548
) -> Result<i32, Error> {
@@ -61,8 +64,8 @@ fn deploy_full(
6164
tools,
6265
global_opts,
6366
&MultiProgress::new(),
64-
// We're not testing
65-
false,
67+
// Don't emit the "not running" message
68+
true,
6669
)?;
6770
if serve_exit_code != 0 {
6871
return Ok(serve_exit_code);
@@ -158,6 +161,13 @@ fn deploy_full(
158161
.into());
159162
}
160163

164+
if !opts.no_minify_js {
165+
minify_js(
166+
&dir.join("dist/pkg/perseus_engine.js"),
167+
&output_path.join("dist/pkg/perseus_engine.js"),
168+
)?
169+
}
170+
161171
println!();
162172
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());
163173

@@ -173,6 +183,7 @@ fn deploy_full(
173183
fn deploy_export(
174184
dir: PathBuf,
175185
output: String,
186+
opts: &DeployOpts,
176187
tools: &Tools,
177188
global_opts: &Opts,
178189
) -> Result<i32, Error> {
@@ -253,8 +264,40 @@ fn deploy_export(
253264
.into());
254265
}
255266

267+
if !opts.no_minify_js {
268+
minify_js(
269+
&dir.join("dist/exported/.perseus/bundle.js"),
270+
&output_path.join(".perseus/bundle.js"),
271+
)?
272+
}
273+
256274
println!();
257275
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());
258276

259277
Ok(0)
260278
}
279+
280+
/// Minifies the given JS code.
281+
fn minify_js(from: &Path, to: &Path) -> Result<(), DeployError> {
282+
let js_bundle = fs::read_to_string(from)
283+
.map_err(|err| DeployError::ReadUnminifiedJsFailed { source: err })?;
284+
285+
// TODO Remove this pending wilsonzlin/minify-js#7
286+
// `minify-js` has a hard time with non-default exports right now, which is
287+
// actually fine, because we don't need `initSync` whatsoever
288+
let js_bundle = js_bundle.replace("export { initSync }", "// export { initSync }");
289+
290+
let mut minified = Vec::new();
291+
minify(
292+
TopLevelMode::Global,
293+
js_bundle.as_bytes().to_vec(),
294+
// Guaranteed to be UTF-8 output
295+
&mut minified,
296+
)
297+
.map_err(|err| DeployError::MinifyError { source: err })?;
298+
let minified =
299+
String::from_utf8(minified).map_err(|err| DeployError::MinifyNotUtf8 { source: err })?;
300+
fs::write(to, &minified).map_err(|err| DeployError::WriteMinifiedJsFailed { source: err })?;
301+
302+
Ok(())
303+
}

packages/perseus-cli/src/errors.rs

+20
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,26 @@ pub enum DeployError {
153153
#[source]
154154
source: std::io::Error,
155155
},
156+
#[error("failed to minify javascript bundle (this is probably an upstream bug, re-try with `--no-minify-js`)")]
157+
MinifyError {
158+
#[source]
159+
source: minify_js::MinifyError,
160+
},
161+
#[error("minified js was not utf-8 (this is a bug, re-try with `--no-minify-js` for now)")]
162+
MinifyNotUtf8 {
163+
#[source]
164+
source: std::string::FromUtf8Error,
165+
},
166+
#[error("failed to read unminified js (if this persists, try `perseus clean`)")]
167+
ReadUnminifiedJsFailed {
168+
#[source]
169+
source: std::io::Error,
170+
},
171+
#[error("failed to write minified js (if this persists, try `perseus clean`)")]
172+
WriteMinifiedJsFailed {
173+
#[source]
174+
source: std::io::Error,
175+
},
156176
}
157177

158178
#[derive(Error, Debug)]

packages/perseus-cli/src/parse.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,12 @@ pub struct DeployOpts {
217217
/// Change the output from `pkg/` to somewhere else
218218
#[clap(short, long, default_value = "pkg")]
219219
pub output: String,
220-
/// Export you app to purely static files (see `export`)
220+
/// Export your app to purely static files (see `export`)
221221
#[clap(short, long)]
222222
pub export_static: bool,
223+
/// Don't minify JavaScript (this will decrease performance)
224+
#[clap(long)]
225+
pub no_minify_js: bool,
223226
}
224227
/// Runs the `tinker` action of plugins, which lets them modify the Perseus
225228
/// engine

packages/perseus-cli/src/serve.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ pub fn serve(
208208
tools: &Tools,
209209
global_opts: &Opts,
210210
spinners: &MultiProgress,
211-
testing: bool,
211+
silent_no_run: bool,
212212
) -> Result<(i32, Option<String>), ExecutionError> {
213213
// Set the environment variables for the host and port
214214
// NOTE Another part of this code depends on setting these in this way
@@ -278,9 +278,9 @@ pub fn serve(
278278
// The user doesn't want to run the server, so we'll give them the executable
279279
// path instead
280280
let exec_str = (*exec.lock().unwrap()).to_string();
281-
// Only tell the user about this if we're not testing (which is a whole separate
282-
// workflow)
283-
if !testing {
281+
// Only tell the user about this if they've told us not to run (since deployment
282+
// and testing both implicitly do this)
283+
if !silent_no_run {
284284
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);
285285
}
286286
Ok((0, Some(exec_str)))

0 commit comments

Comments
 (0)