use crate::errors::*;
use crate::export;
use crate::install::Tools;
use crate::parse::Opts;
use crate::parse::{DeployOpts, ExportOpts, ServeOpts};
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;
pub fn deploy(
dir: PathBuf,
opts: &DeployOpts,
tools: &Tools,
global_opts: &Opts,
) -> Result<i32, Error> {
let exit_code = if opts.export_static {
deploy_export(dir, opts.output.to_string(), opts, tools, global_opts)?
} else {
deploy_full(dir, opts.output.to_string(), opts, tools, global_opts)?
};
Ok(exit_code)
}
fn deploy_full(
dir: PathBuf,
output: String,
opts: &DeployOpts,
tools: &Tools,
global_opts: &Opts,
) -> Result<i32, Error> {
let (serve_exit_code, server_path) = serve(
dir.clone(),
&ServeOpts {
no_run: true,
no_build: false,
release: true,
standalone: true,
watch: false,
custom_watch: Vec::new(),
host: "127.0.0.1".to_string(),
port: 8080,
},
tools,
global_opts,
&MultiProgress::new(),
true,
)?;
if serve_exit_code != 0 {
return Ok(serve_exit_code);
}
if let Some(server_path) = server_path {
let output_path = PathBuf::from(&output);
if output_path.exists() {
if let Err(err) = fs::remove_dir_all(&output_path) {
return Err(DeployError::ReplaceOutputDirFailed {
path: output,
source: err,
}
.into());
}
}
if let Err(err) = fs::create_dir(&output_path) {
return Err(DeployError::ReplaceOutputDirFailed {
path: output,
source: err,
}
.into());
}
#[cfg(target_os = "windows")]
let to = output_path.join("server.exe");
#[cfg(not(target_os = "windows"))]
let to = output_path.join("server");
if let Err(err) = fs::copy(&server_path, &to) {
return Err(DeployError::MoveAssetFailed {
to: to.to_str().map(|s| s.to_string()).unwrap(),
from: server_path,
source: err,
}
.into());
}
let from = dir.join("static");
if from.exists() {
if let Err(err) = copy_dir(&from, &output, &CopyOptions::new()) {
return Err(DeployError::MoveDirFailed {
to: output,
from: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.into());
}
}
let from = dir.join("translations");
if from.exists() {
if let Err(err) = copy_dir(&from, &output, &CopyOptions::new()) {
return Err(DeployError::MoveDirFailed {
to: output,
from: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.into());
}
}
if let Err(err) = fs::create_dir(output_path.join("dist")) {
return Err(DeployError::CreateDistDirFailed { source: err }.into());
}
let from = dir.join("dist/static");
if let Err(err) = copy_dir(&from, output_path.join("dist"), &CopyOptions::new()) {
return Err(DeployError::MoveDirFailed {
to: output,
from: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.into());
}
let from = dir.join("dist/pkg"); if let Err(err) = copy_dir(&from, output_path.join("dist"), &CopyOptions::new()) {
return Err(DeployError::MoveDirFailed {
to: output,
from: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.into());
}
let from = dir.join("dist/render_conf.json");
if let Err(err) = fs::copy(&from, output_path.join("dist/render_conf.json")) {
return Err(DeployError::MoveAssetFailed {
to: output,
from: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.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());
Ok(0)
} else {
Err(ExecutionError::GetServerExecutableFailedSimple.into())
}
}
fn deploy_export(
dir: PathBuf,
output: String,
opts: &DeployOpts,
tools: &Tools,
global_opts: &Opts,
) -> Result<i32, Error> {
let export_exit_code = export(
dir.clone(),
&ExportOpts {
release: true,
serve: false,
host: String::new(),
port: 0,
watch: false,
custom_watch: Vec::new(),
},
tools,
global_opts,
)?;
if export_exit_code != 0 {
return Ok(export_exit_code);
}
let from = dir.join("dist/exported");
let output_path = PathBuf::from(&output);
if output_path.exists() {
if let Err(err) = fs::remove_dir_all(&output_path) {
return Err(DeployError::ReplaceOutputDirFailed {
path: output,
source: err,
}
.into());
}
}
if let Err(err) = fs::create_dir(&output_path) {
return Err(DeployError::ReplaceOutputDirFailed {
path: output,
source: err,
}
.into());
}
let items = fs::read_dir(&from);
let items: Vec<PathBuf> = match items {
Ok(items) => {
let mut ok_items = Vec::new();
for item in items {
match item {
Ok(item) => ok_items.push(item.path()),
Err(err) => {
return Err(DeployError::ReadExportDirFailed {
path: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.into())
}
}
}
ok_items
}
Err(err) => {
return Err(DeployError::ReadExportDirFailed {
path: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.into())
}
};
if let Err(err) = copy_items(&items, &output, &CopyOptions::new()) {
return Err(DeployError::MoveExportDirFailed {
to: output,
from: from.to_str().map(|s| s.to_string()).unwrap(),
source: err,
}
.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)
}
fn minify_js(from: &Path, to: &Path) -> Result<(), DeployError> {
let js_bundle = fs::read_to_string(from)
.map_err(|err| DeployError::ReadUnminifiedJsFailed { source: err })?;
let js_bundle = js_bundle.replace("export { initSync }", "// export { initSync }");
let mut minified = Vec::new();
minify(
TopLevelMode::Global,
js_bundle.as_bytes().to_vec(),
&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(())
}