Skip to content

Commit

Permalink
Merge 7db9535 into 75c5705
Browse files Browse the repository at this point in the history
  • Loading branch information
sokra authored Apr 18, 2023
2 parents 75c5705 + 7db9535 commit bcc1a7d
Show file tree
Hide file tree
Showing 18 changed files with 1,009 additions and 40 deletions.
735 changes: 700 additions & 35 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ members = [
"crates/turbopack-dev-server",
"crates/turbopack-ecmascript",
"crates/turbopack-env",
"crates/turbopack-image",
"crates/turbopack-json",
"crates/turbopack-mdx",
"crates/turbopack-node",
Expand Down Expand Up @@ -131,6 +132,7 @@ turbopack-dev = { path = "crates/turbopack-dev" }
turbopack-dev-server = { path = "crates/turbopack-dev-server" }
turbopack-ecmascript = { path = "crates/turbopack-ecmascript" }
turbopack-env = { path = "crates/turbopack-env" }
turbopack-image = { path = "crates/turbopack-image" }
turbopack-json = { path = "crates/turbopack-json" }
turbopack-mdx = { path = "crates/turbopack-mdx" }
turbopack-node = { path = "crates/turbopack-node" }
Expand Down Expand Up @@ -184,6 +186,7 @@ dunce = "1.0.3"
futures = "0.3.26"
futures-retry = "0.6.0"
httpmock = { version = "0.6.7", default-features = false }
image = { version = "0.24.6", default-features = false }
indexmap = "1.9.2"
indicatif = "0.17.3"
indoc = "2.0.0"
Expand Down
2 changes: 2 additions & 0 deletions crates/turbo-binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ __turbopack_dev_dynamic_embed_contents = [
__turbopack_dev_server = ["__turbopack", "turbopack-dev-server"]
__turbopack_ecmascript = ["__turbopack", "turbopack-ecmascript"]
__turbopack_env = ["__turbopack", "turbopack-env"]
__turbopack_image = ["__turbopack", "turbopack-image"]
__turbopack_json = ["__turbopack", "turbopack-json"]
__turbopack_mdx = ["__turbopack", "turbopack-mdx"]
__turbopack_node = ["__turbopack", "turbopack-node"]
Expand Down Expand Up @@ -183,6 +184,7 @@ turbopack-dev = { optional = true, workspace = true }
turbopack-dev-server = { optional = true, workspace = true }
turbopack-ecmascript = { optional = true, workspace = true }
turbopack-env = { optional = true, workspace = true }
turbopack-image = { optional = true, workspace = true }
turbopack-json = { optional = true, workspace = true }
turbopack-mdx = { optional = true, workspace = true }
turbopack-node = { optional = true, workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions crates/turbo-binding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub mod turbopack {
pub use turbopack_ecmascript as ecmascript;
#[cfg(feature = "__turbopack_env")]
pub use turbopack_env as env;
#[cfg(feature = "__turbopack_image")]
pub use turbopack_image as image;
#[cfg(feature = "__turbopack_json")]
pub use turbopack_json as json;
#[cfg(feature = "__turbopack_mdx")]
Expand Down
1 change: 1 addition & 0 deletions crates/turbopack-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod error;
pub mod ident;
pub mod introspect;
pub mod issue;
pub mod plugin;
pub mod reference;
pub mod reference_type;
pub mod resolve;
Expand Down
11 changes: 11 additions & 0 deletions crates/turbopack-core/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::{asset::AssetVc, context::AssetContextVc, resolve::ModulePartVc};

#[turbo_tasks::value_trait]
pub trait CustomModuleType {
fn create_module(
&self,
source: AssetVc,
context: AssetContextVc,
part: Option<ModulePartVc>,
) -> AssetVc;
}
5 changes: 5 additions & 0 deletions crates/turbopack-ecmascript/src/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ pub struct EcmascriptInputTransforms(Vec<EcmascriptInputTransform>);

#[turbo_tasks::value_impl]
impl EcmascriptInputTransformsVc {
#[turbo_tasks::function]
pub fn empty() -> Self {
EcmascriptInputTransformsVc::cell(Vec::new())
}

#[turbo_tasks::function]
pub async fn extend(self, other: EcmascriptInputTransformsVc) -> Result<Self> {
let mut transforms = self.await?.clone_value();
Expand Down
33 changes: 33 additions & 0 deletions crates/turbopack-image/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "turbopack-image"
version = "0.1.0"
description = "TBD"
license = "MPL-2.0"
edition = "2021"
autobenches = false

[lib]
bench = false

[features]
avif = ["image/avif-decoder", "image/avif-encoder"]

[dependencies]
anyhow = { workspace = true }
base64 = "0.21.0"
image = { workspace = true, default-features = false, features = [
"webp",
"png",
"jpeg",
"webp-encoder",
] }
indexmap = { workspace = true }
serde = { workspace = true }
turbo-tasks = { workspace = true }
turbo-tasks-fs = { workspace = true }
turbopack-core = { workspace = true }
turbopack-ecmascript = { workspace = true }
turbopack-static = { workspace = true }

[build-dependencies]
turbo-tasks-build = { workspace = true }
5 changes: 5 additions & 0 deletions crates/turbopack-image/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use turbo_tasks_build::generate_register;

fn main() {
generate_register();
}
12 changes: 12 additions & 0 deletions crates/turbopack-image/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pub mod module_type;
mod process;
mod source;

pub fn register() {
turbo_tasks::register();
turbo_tasks_fs::register();
turbopack_core::register();
turbopack_ecmascript::register();
turbopack_static::register();
include!(concat!(env!("OUT_DIR"), "/register.rs"));
}
54 changes: 54 additions & 0 deletions crates/turbopack-image/src/module_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use anyhow::Result;
use indexmap::indexmap;
use turbo_tasks::Value;
use turbopack_core::{
asset::AssetVc,
context::{AssetContext, AssetContextVc},
plugin::{CustomModuleType, CustomModuleTypeVc},
resolve::ModulePartVc,
};
use turbopack_ecmascript::{
EcmascriptInputTransformsVc, EcmascriptModuleAssetType, EcmascriptModuleAssetVc,
EcmascriptOptions, InnerAssetsVc,
};
use turbopack_static::StaticModuleAssetVc;

use crate::source::StructuredImageSourceAsset;

#[turbo_tasks::value]
pub struct StructuredImageModuleType {}

#[turbo_tasks::value_impl]
impl StructuredImageModuleTypeVc {
#[turbo_tasks::function]
pub fn new() -> Self {
StructuredImageModuleTypeVc::cell(StructuredImageModuleType {})
}
}

#[turbo_tasks::value_impl]
impl CustomModuleType for StructuredImageModuleType {
#[turbo_tasks::function]
async fn create_module(
&self,
source: AssetVc,
context: AssetContextVc,
_part: Option<ModulePartVc>,
) -> Result<AssetVc> {
let static_asset = StaticModuleAssetVc::new(source, context);
Ok(EcmascriptModuleAssetVc::new_with_inner_assets(
StructuredImageSourceAsset { image: source }.cell().into(),
context,
Value::new(EcmascriptModuleAssetType::Ecmascript),
EcmascriptInputTransformsVc::empty(),
Value::new(EcmascriptOptions {
..Default::default()
}),
context.compile_time_info(),
InnerAssetsVc::cell(indexmap!(
"IMAGE".to_string() => static_asset.into()
)),
)
.into())
}
}
126 changes: 126 additions & 0 deletions crates/turbopack-image/src/process.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::io::Cursor;

use anyhow::{bail, Result};
use base64::{display::Base64Display, engine::general_purpose::STANDARD};
use image::{
codecs::{
jpeg::JpegEncoder,
png::{CompressionType, PngEncoder},
webp::{WebPEncoder, WebPQuality},
},
imageops::FilterType,
GenericImageView, ImageEncoder, ImageFormat,
};
use turbo_tasks_fs::{FileContent, FileContentVc};

#[turbo_tasks::value]
#[serde(rename_all = "camelCase")]
pub struct ImageMetaData {
width: u32,
height: u32,
#[serde(rename = "blurDataURL")]
blur_data_url: Option<String>,
blur_width: u32,
blur_height: u32,
}

const BLUR_IMG_SIZE: u32 = 8;
const BLUR_QUALITY: u8 = 70;

#[turbo_tasks::function]
pub async fn get_meta_data_and_blur_placeholder(content: FileContentVc) -> Result<ImageMetaDataVc> {
let FileContent::Content(content) = &*content.await? else {
bail!("Input image not found");
};
let bytes = content.content().to_bytes()?;
let reader = image::io::Reader::new(Cursor::new(&bytes));
let reader = reader.with_guessed_format()?;
let format = reader.format();
let image = reader.decode()?;
let (width, height) = image.dimensions();
let (blur_data_url, blur_width, blur_height) = if matches!(
format,
// list should match next/client/image.tsx
Some(ImageFormat::Png)
| Some(ImageFormat::Jpeg)
| Some(ImageFormat::WebP)
| Some(ImageFormat::Avif)
) {
let small_image = image.resize(BLUR_IMG_SIZE, BLUR_IMG_SIZE, FilterType::Triangle);
let mut buf = Vec::new();
let blur_width = small_image.width();
let blur_height = small_image.height();
let url = match format {
Some(ImageFormat::Png) => {
PngEncoder::new_with_quality(
&mut buf,
CompressionType::Best,
image::codecs::png::FilterType::NoFilter,
)
.write_image(
small_image.as_bytes(),
blur_width,
blur_height,
small_image.color(),
)?;
format!(
"data:image/png;base64,{}",
Base64Display::new(&buf, &STANDARD)
)
}
Some(ImageFormat::Jpeg) => {
JpegEncoder::new_with_quality(&mut buf, BLUR_QUALITY).write_image(
small_image.as_bytes(),
blur_width,
blur_height,
small_image.color(),
)?;
format!(
"data:image/jpeg;base64,{}",
Base64Display::new(&buf, &STANDARD)
)
}
Some(ImageFormat::WebP) => {
WebPEncoder::new_with_quality(&mut buf, WebPQuality::lossy(BLUR_QUALITY))
.write_image(
small_image.as_bytes(),
blur_width,
blur_height,
small_image.color(),
)?;
format!(
"data:image/webp;base64,{}",
Base64Display::new(&buf, &STANDARD)
)
}
#[cfg(feature = "avif")]
Some(ImageFormat::Avif) => {
use image::codecs::avif::AvifEncoder;
AvifEncoder::new_with_speed_quality(&mut buf, 6, BLUR_QUALITY).write_image(
small_image.as_bytes(),
blur_width,
blur_height,
small_image.color(),
)?;
format!(
"data:image/avif;base64,{}",
Base64Display::new(&buf, &STANDARD)
)
}
_ => unreachable!(),
};

(Some(url), blur_width, blur_height)
} else {
(None, 0, 0)
};

Ok(ImageMetaData {
width,
height,
blur_data_url,
blur_width,
blur_height,
}
.cell())
}
47 changes: 47 additions & 0 deletions crates/turbopack-image/src/source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::io::Write;

use anyhow::{bail, Result};
use turbo_tasks::primitives::StringVc;
use turbo_tasks_fs::{rope::RopeBuilder, FileContent};
use turbopack_core::{
asset::{Asset, AssetContent, AssetContentVc, AssetVc},
ident::AssetIdentVc,
};
use turbopack_ecmascript::utils::StringifyJs;

use crate::process::get_meta_data_and_blur_placeholder;

fn modifier() -> StringVc {
StringVc::cell("structured image object".to_string())
}

#[turbo_tasks::value(shared)]
pub struct StructuredImageSourceAsset {
pub image: AssetVc,
}

#[turbo_tasks::value_impl]
impl Asset for StructuredImageSourceAsset {
#[turbo_tasks::function]
fn ident(&self) -> AssetIdentVc {
self.image.ident().with_modifier(modifier())
}

#[turbo_tasks::function]
async fn content(&self) -> Result<AssetContentVc> {
let content = self.image.content().await?;
let AssetContent::File(content) = *content else {
bail!("Input source is not a file and can't be transformed into image information");
};
let mut result = RopeBuilder::from("");
let info = get_meta_data_and_blur_placeholder(content);
let info = info.await?;
writeln!(result, "import src from \"IMAGE\";",)?;
writeln!(
result,
"export default {{ src, ...{} }}",
StringifyJs(&*info)
)?;
Ok(AssetContent::File(FileContent::Content(result.build().into()).cell()).cell())
}
}
1 change: 1 addition & 0 deletions crates/turbopack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ turbopack-core = { workspace = true }
turbopack-css = { workspace = true }
turbopack-ecmascript = { workspace = true }
turbopack-env = { workspace = true }
turbopack-image = { workspace = true }
turbopack-json = { workspace = true }
turbopack-mdx = { workspace = true }
turbopack-node = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/turbopack/src/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use async_recursion::async_recursion;
use futures::{stream, StreamExt};
use serde::{Deserialize, Serialize};
use turbo_tasks::trace::TraceRawVcs;
use turbo_tasks_fs::{FileSystem, FileSystemPath, FileSystemPathVc};
use turbo_tasks_fs::{FileSystemPath, FileSystemPathVc};

#[derive(Debug, Clone, Serialize, Deserialize, TraceRawVcs, PartialEq, Eq)]
pub enum ContextCondition {
Expand Down
3 changes: 2 additions & 1 deletion crates/turbopack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use turbopack_core::{
context::{AssetContext, AssetContextVc},
ident::AssetIdentVc,
issue::{unsupported_module::UnsupportedModuleIssue, Issue, IssueVc},
plugin::CustomModuleType,
reference::all_referenced_assets,
reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType},
resolve::{
Expand Down Expand Up @@ -177,7 +178,7 @@ async fn apply_module_type(
transforms,
options,
} => MdxModuleAssetVc::new(source, context.into(), *transforms, *options).into(),
ModuleType::Custom(_) => todo!(),
ModuleType::Custom(custom) => custom.create_module(source, context.into(), part),
})
}

Expand Down
Loading

0 comments on commit bcc1a7d

Please sign in to comment.