Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions packages/cli/src/build/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use crate::{
BuildArtifacts, BuildRequest, BuildStage, BuilderUpdate, BundleFormat, ProgressRx, ProgressTx,
Result, StructuredOutput, serve::WebServer, verbosity_or_default,
};
use crate::{BuildPhaseProfile, opt::process_file_to};
use crate::{
BuildPhaseProfile,
opt::{copy_file_verbatim, process_file_to},
};
use anyhow::{Context, Error, bail};
use futures_util::{FutureExt, future::OptionFuture, pin_mut};
use itertools::Itertools;
Expand Down Expand Up @@ -833,8 +836,13 @@ impl AppBuilder {
let to = asset_dir.join(bundled.bundled_path());

tracing::debug!("Copying asset from patch: {}", from.display());
let esbuild = crate::esbuild::Esbuild::path_if_installed();
if let Err(e) = process_file_to(bundled.options(), &from, &to, esbuild.as_deref()) {
let res = if self.build.path_is_in_public_dir(&from) {
copy_file_verbatim(&from, &to)
} else {
let esbuild = crate::esbuild::Esbuild::path_if_installed();
process_file_to(bundled.options(), &from, &to, esbuild.as_deref())
};
if let Err(e) = res {
tracing::error!("Failed to copy asset: {e}");
continue;
}
Expand Down Expand Up @@ -939,9 +947,17 @@ impl AppBuilder {
// And then process the asset with the options into the **old** asset location. If we recompiled,
// the asset would be in a new location because the contents and hash have changed. Since we are
// hotreloading, we need to use the old asset location it was originally written to.
let options = *resource.options();
let esbuild = crate::esbuild::Esbuild::path_if_installed();
let res = process_file_to(&options, &changed_file, &output_path, esbuild.as_deref());
let res = if self.build.path_is_in_public_dir(&changed_file) {
copy_file_verbatim(&changed_file, &output_path)
} else {
let esbuild = crate::esbuild::Esbuild::path_if_installed();
process_file_to(
resource.options(),
&changed_file,
&output_path,
esbuild.as_deref(),
)
};
let bundled_name = PathBuf::from(resource.bundled_path());
if let Err(e) = res {
tracing::debug!("Failed to hotreload asset {e}");
Expand Down
13 changes: 9 additions & 4 deletions packages/cli/src/build/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ use crate::{
};
use crate::{
WorkspaceRustcArgs,
opt::{AppManifest, process_file_to},
opt::{AppManifest, copy_file_verbatim, process_file_to},
};
use anyhow::{Context, bail};
use cargo_metadata::diagnostic::Diagnostic;
Expand Down Expand Up @@ -1528,7 +1528,8 @@ impl BuildRequest {
.unwrap_or(to.as_path());

tracing::debug!("Copying asset {from_:?} to {to_:?}");
assets_to_transfer.push((from, to, *bundled.options()));
let verbatim = self.path_is_in_public_dir(&from);
assets_to_transfer.push((from, to, *bundled.options(), verbatim));
}

let asset_count = assets_to_transfer.len();
Expand All @@ -1544,14 +1545,18 @@ impl BuildRequest {
tokio::task::spawn_blocking(move || {
assets_to_transfer
.par_iter()
.try_for_each(|(from, to, options)| {
.try_for_each(|(from, to, options, verbatim)| {
let processing = started_processing.fetch_add(1, Ordering::SeqCst);
let from_ = from.strip_prefix(&ws_dir).unwrap_or(from);
tracing::trace!(
"Starting asset copy {processing}/{asset_count} from {from_:?}"
);

let res = process_file_to(options, from, to, esbuild_path.as_deref());
let res = if *verbatim {
copy_file_verbatim(from, to)
} else {
process_file_to(options, from, to, esbuild_path.as_deref())
};
if let Err(err) = res.as_ref() {
tracing::error!("Failed to copy asset {from:?}: {err}");
}
Expand Down
41 changes: 41 additions & 0 deletions packages/cli/src/opt/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ use super::{
json::process_json,
};

/// Copy a `public/` file verbatim so it is served as-is, bypassing the asset optimizer.
///
/// Optimizing a worker entry would inline its imports and shift `import.meta.url`, breaking the
/// runtime module re-import that `wasm-bindgen-rayon` uses to spawn its pool.
pub(crate) fn copy_file_verbatim(source: &Path, output_path: &Path) -> anyhow::Result<()> {
if let Some(parent) = output_path.parent() {
std::fs::create_dir_all(parent).context("Failed to create directory")?;
}
std::fs::copy(source, output_path).with_context(|| {
format!(
"Failed to copy {} to {}",
source.display(),
output_path.display()
)
})?;
Ok(())
}

/// Process a specific file asset with the given options reading from the source and writing to the output path
pub(crate) fn process_file_to(
options: &AssetOptions,
Expand Down Expand Up @@ -144,3 +162,26 @@ fn resolve_unknown_asset_options(source: &Path) -> ResolvedAssetType {
_ => ResolvedAssetType::File,
}
}

#[cfg(test)]
mod tests {
use super::copy_file_verbatim;

/// A `public/` module reaches the output untouched, including its imports and ES exports, and
/// nested output directories are created.
#[test]
fn public_js_module_is_copied_byte_for_byte() {
let dir = tempfile::tempdir().unwrap();
let source = dir.path().join("loader.js");
let output = dir.path().join("nested/out/loader.js");
let contents = r#"import init, { initThreadPool } from "./worker.js";
export * from "./worker.js";
export { default } from "./worker.js";
"#;
std::fs::write(&source, contents).unwrap();

copy_file_verbatim(&source, &output).unwrap();

assert_eq!(std::fs::read_to_string(&output).unwrap(), contents);
}
}
2 changes: 1 addition & 1 deletion packages/cli/src/opt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod image;
mod js;
mod json;

pub(crate) use file::process_file_to;
pub(crate) use file::{copy_file_verbatim, process_file_to};
pub(crate) use hash::add_hash_to_asset;
pub(crate) use js::js_is_module;

Expand Down
Loading