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
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 1 addition & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,6 @@ members = [
"packages/depinfo",
"packages/server",

# Playwright tests
"packages/playwright-tests/liveview",
"packages/playwright-tests/web",
"packages/playwright-tests/fullstack",
"packages/playwright-tests/suspense-carousel",
"packages/playwright-tests/nested-suspense",

# manganis
"packages/manganis/manganis",
Expand Down Expand Up @@ -128,6 +122,7 @@ members = [
"packages/playwright-tests/suspense-carousel",
"packages/playwright-tests/nested-suspense",
"packages/playwright-tests/cli-optimization",
"packages/playwright-tests/external-wasm-bindgen-link",
"packages/playwright-tests/wasm-split-harness",
"packages/playwright-tests/default-features-disabled",
]
Expand Down
21 changes: 18 additions & 3 deletions packages/cli-opt/src/css.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use std::{hash::Hasher, path::Path};

use anyhow::{anyhow, Context};
use anyhow::{anyhow, bail, Context};
use codemap::SpanLoc;
use grass::OutputStyle;
use lightningcss::{
Expand All @@ -9,11 +7,13 @@ use lightningcss::{
targets::{Browsers, Targets},
};
use manganis_core::{CssAssetOptions, CssModuleAssetOptions};
use std::{hash::Hasher, path::Path};

pub(crate) fn process_css(
css_options: &CssAssetOptions,
source: &Path,
output_path: &Path,
allow_fallback: bool,
) -> anyhow::Result<()> {
let css = std::fs::read_to_string(source)?;

Expand All @@ -22,10 +22,15 @@ pub(crate) fn process_css(
match minify_css(&css) {
Ok(minified) => minified,
Err(err) => {
if !allow_fallback {
bail!("Failed to minify css from {}: {}", source.display(), err);
}

tracing::error!(
"Failed to minify css; Falling back to unminified css. Error: {}",
err
);

css
}
}
Expand All @@ -48,6 +53,7 @@ pub(crate) fn process_css_module(
source: &Path,
final_path: &Path,
output_path: &Path,
allow_fallback: bool,
) -> anyhow::Result<()> {
let mut css = std::fs::read_to_string(source)?;

Expand Down Expand Up @@ -90,10 +96,19 @@ pub(crate) fn process_css_module(
match minify_css(&css) {
Ok(minified) => minified,
Err(err) => {
if !allow_fallback {
bail!(
"Failed to minify css module from {}: {}",
source.display(),
err
);
}

tracing::error!(
"Failed to minify css module; Falling back to unminified css. Error: {}",
err
);

css
}
}
Expand Down
16 changes: 9 additions & 7 deletions packages/cli-opt/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ pub fn process_file_to(
options: &AssetOptions,
source: &Path,
output_path: &Path,
allow_fallback: bool,
) -> anyhow::Result<()> {
process_file_to_with_options(options, source, output_path, false)
process_file_to_with_options(options, source, output_path, false, allow_fallback)
}

/// Process a specific file asset with additional options
Expand All @@ -25,6 +26,7 @@ pub(crate) fn process_file_to_with_options(
source: &Path,
output_path: &Path,
in_folder: bool,
allow_fallback: bool,
) -> anyhow::Result<()> {
// If the file already exists and this is a hashed asset, then we must have a file
// with the same hash already. The hash has the file contents and options, so if we
Expand Down Expand Up @@ -52,25 +54,25 @@ pub(crate) fn process_file_to_with_options(

match &resolved_options {
ResolvedAssetType::Css(options) => {
process_css(options, source, &temp_path)?;
process_css(options, source, &temp_path, allow_fallback)?;
}
ResolvedAssetType::CssModule(options) => {
process_css_module(options, source, output_path, &temp_path)?;
process_css_module(options, source, output_path, &temp_path, allow_fallback)?;
}
ResolvedAssetType::Scss(options) => {
process_scss(options, source, &temp_path)?;
}
ResolvedAssetType::Js(options) => {
process_js(options, source, &temp_path, !in_folder)?;
process_js(options, source, &temp_path, !in_folder, allow_fallback)?;
}
ResolvedAssetType::Image(options) => {
process_image(options, source, &temp_path)?;
process_image(options, source, &temp_path, allow_fallback)?;
}
ResolvedAssetType::Json => {
process_json(source, &temp_path)?;
process_json(source, &temp_path, allow_fallback)?;
}
ResolvedAssetType::Folder(_) => {
process_folder(source, &temp_path)?;
process_folder(source, &temp_path, allow_fallback)?;
}
ResolvedAssetType::File => {
let source_file = std::fs::File::open(source)?;
Expand Down
17 changes: 13 additions & 4 deletions packages/cli-opt/src/folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use crate::file::process_file_to_with_options;

/// Process a folder, optimizing and copying all assets into the output folder
pub fn process_folder(source: &Path, output_folder: &Path) -> anyhow::Result<()> {
pub fn process_folder(
source: &Path,
output_folder: &Path,
allow_fallback: bool,
) -> anyhow::Result<()> {
// Create the folder
std::fs::create_dir_all(output_folder)?;

Expand All @@ -21,22 +25,27 @@ pub fn process_folder(source: &Path, output_folder: &Path) -> anyhow::Result<()>
let metadata = file.metadata()?;
let output_path = output_folder.join(file.strip_prefix(source)?);
if metadata.is_dir() {
process_folder(&file, &output_path)
process_folder(&file, &output_path, allow_fallback)
} else {
process_file_minimal(&file, &output_path)
process_file_minimal(&file, &output_path, allow_fallback)
}
})?;

Ok(())
}

/// Optimize a file without changing any of its contents significantly (e.g. by changing the extension)
fn process_file_minimal(input_path: &Path, output_path: &Path) -> anyhow::Result<()> {
fn process_file_minimal(
input_path: &Path,
output_path: &Path,
allow_fallback: bool,
) -> anyhow::Result<()> {
process_file_to_with_options(
&manganis_core::AssetOptions::builder().into_asset_options(),
input_path,
output_path,
true,
allow_fallback,
)?;
Ok(())
}
29 changes: 17 additions & 12 deletions packages/cli-opt/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
file::{resolve_asset_options, ResolvedAssetType},
js::hash_js,
};
use anyhow::bail;
use manganis::{AssetOptions, BundledAsset};

/// The opaque hash type manganis uses to identify assets. Each time an asset or asset options change, this hash will
Expand All @@ -37,19 +38,24 @@ impl AssetHash {
pub fn hash_file_contents(
options: &AssetOptions,
file_path: impl AsRef<Path>,
allow_fallback: bool,
) -> anyhow::Result<AssetHash> {
hash_file(options, file_path.as_ref())
hash_file(options, file_path.as_ref(), allow_fallback)
}
}

/// Process a specific file asset with the given options reading from the source and writing to the output path
fn hash_file(options: &AssetOptions, source: &Path) -> anyhow::Result<AssetHash> {
fn hash_file(
options: &AssetOptions,
source: &Path,
allow_fallback: bool,
) -> anyhow::Result<AssetHash> {
// Create a hasher
let mut hash = std::collections::hash_map::DefaultHasher::new();
options.hash(&mut hash);
// Hash the version of CLI opt
hash.write(crate::build_info::version().as_bytes());
hash_file_with_options(options, source, &mut hash, false)?;
hash_file_with_options(options, source, &mut hash, false, allow_fallback)?;

let hash = hash.finish();
Ok(AssetHash::new(hash))
Expand All @@ -61,6 +67,7 @@ pub(crate) fn hash_file_with_options(
source: &Path,
hasher: &mut impl Hasher,
in_folder: bool,
allow_fallback: bool,
) -> anyhow::Result<()> {
let resolved_options = resolve_asset_options(source, options.variant());

Expand All @@ -71,7 +78,7 @@ pub(crate) fn hash_file_with_options(
hash_scss(options, source, hasher)?;
}
ResolvedAssetType::Js(options) => {
hash_js(options, source, hasher, !in_folder)?;
hash_js(options, source, hasher, !in_folder, allow_fallback)?;
}

// Otherwise, we can just hash the file contents
Expand All @@ -87,7 +94,7 @@ pub(crate) fn hash_file_with_options(
let files = std::fs::read_dir(source)?;
for file in files.flatten() {
let path = file.path();
hash_file_with_options(options, &path, hasher, true)?;
hash_file_with_options(options, &path, hasher, true, allow_fallback)?;
}
}
}
Expand All @@ -114,17 +121,16 @@ pub(crate) fn hash_file_contents(source: &Path, hasher: &mut impl Hasher) -> any
}

/// Add a hash to the asset, or log an error if it fails
pub fn add_hash_to_asset(asset: &mut BundledAsset) {
pub fn add_hash_to_asset(asset: &mut BundledAsset, allow_fallback: bool) -> anyhow::Result<()> {
let source = asset.absolute_source_path();
match AssetHash::hash_file_contents(asset.options(), source) {
match AssetHash::hash_file_contents(asset.options(), source, allow_fallback) {
Ok(hash) => {
let options = *asset.options();

// Set the bundled path to the source path with the hash appended before the extension
let source_path = PathBuf::from(source);
let Some(file_name) = source_path.file_name() else {
tracing::error!("Failed to get file name from path: {source}");
return;
bail!("Failed to get file name from path {}", source);
};
// The output extension path is the extension set by the options
// or the extension of the source file if we don't recognize the file
Expand Down Expand Up @@ -161,9 +167,8 @@ pub fn add_hash_to_asset(asset: &mut BundledAsset) {
let bundled_path = bundled_path.to_string_lossy().to_string();

*asset = BundledAsset::new(source, &bundled_path, options);
Ok(())
}
Err(err) => {
tracing::error!("Failed to hash asset: {err}");
}
Err(err) => Err(err),
}
}
9 changes: 6 additions & 3 deletions packages/cli-opt/src/image/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::path::Path;

use anyhow::Context;
use anyhow::{bail, Context};
use jpg::compress_jpg;
use manganis_core::{ImageAssetOptions, ImageFormat, ImageSize};
use png::compress_png;
use std::path::Path;

mod jpg;
mod png;
Expand All @@ -12,6 +11,7 @@ pub(crate) fn process_image(
image_options: &ImageAssetOptions,
source: &Path,
output_path: &Path,
allow_fallback: bool,
) -> anyhow::Result<()> {
let mut image = image::ImageReader::new(std::io::Cursor::new(&*std::fs::read(source)?))
.with_guessed_format()
Expand Down Expand Up @@ -50,6 +50,9 @@ pub(crate) fn process_image(
)
})?;
}
(Err(err), _) if !allow_fallback => {
bail!("Failed to decode image from {}: {}", source.display(), err);
}
// If we can't decode the image or it is of an unknown type, we just copy the file
_ => {
let source_file = std::fs::File::open(source).context("Failed to open source file")?;
Expand Down
24 changes: 22 additions & 2 deletions packages/cli-opt/src/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,20 @@ pub(crate) fn process_js(
source: &Path,
output_path: &Path,
bundle: bool,
allow_fallback: bool,
) -> anyhow::Result<()> {
let mut writer = std::io::BufWriter::new(std::fs::File::create(output_path)?);
if js_options.minified() {
if let Err(err) = bundle_js_to_writer(source.to_path_buf(), bundle, true, &mut writer) {
tracing::error!("Failed to minify js. Falling back to non-minified: {err}");
if allow_fallback {
tracing::error!("Failed to minify js. Falling back to non-minified: {err:?}");
} else {
return Err(anyhow::anyhow!(
"Failed to minify js from {}: {}",
source.display(),
err
));
}
} else {
return Ok(());
}
Expand Down Expand Up @@ -281,10 +290,21 @@ pub(crate) fn hash_js(
source: &Path,
hasher: &mut impl Hasher,
bundle: bool,
allow_fallback: bool,
) -> anyhow::Result<()> {
if js_options.minified() {
if let Err(err) = hash_js_module(source.to_path_buf(), hasher, bundle) {
tracing::error!("Failed to minify js. Falling back to non-minified: {err}");
if allow_fallback {
tracing::error!(
"Failed to hash js. Falling back to just hashing the individual file: {err:?}"
);
} else {
return Err(anyhow::anyhow!(
"Failed to hash js from {}: {}",
source.display(),
err
));
}
hash_file_contents(source, hasher)?;
}
} else {
Expand Down
Loading
Loading