Skip to content
Merged
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
2 changes: 1 addition & 1 deletion crates/oxc_transformer/examples/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn main() {

let transform_options = if let Some(targets) = &targets {
TransformOptions::try_from(&EnvOptions {
targets: Targets::from_query(targets),
targets: Targets::try_from_query(targets).unwrap(),
..EnvOptions::default()
})
.unwrap()
Expand Down
26 changes: 13 additions & 13 deletions crates/oxc_transformer/src/env/data/babel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ use std::sync::OnceLock;

use rustc_hash::FxHashMap;

use crate::env::{targets::version::Version, Versions};
use crate::env::targets::{version::Version, Targets};

/// Reference: <https://github.com/swc-project/swc/blob/ea14fc8e5996dcd736b8deb4cc99262d07dfff44/crates/swc_ecma_preset_env/src/transform_data.rs#L194-L218>
fn features() -> &'static FxHashMap<String, Versions> {
static FEATURES: OnceLock<FxHashMap<String, Versions>> = OnceLock::new();
fn features() -> &'static FxHashMap<String, Targets> {
static FEATURES: OnceLock<FxHashMap<String, Targets>> = OnceLock::new();
FEATURES.get_or_init(|| {
let mut map: FxHashMap<String, FxHashMap<String, String>> =
serde_json::from_str(include_str!("./@babel/compat_data/data/plugins.json"))
.expect("failed to parse json");
serde_json::from_str(include_str!("./@babel/compat_data/data/plugins.json")).unwrap();

map.extend(
serde_json::from_str::<FxHashMap<String, FxHashMap<String, String>>>(include_str!(
"./esbuild/features.json"
))
.expect("failed to parse json"),
.unwrap(),
);

map.into_iter()
Expand All @@ -27,7 +26,7 @@ fn features() -> &'static FxHashMap<String, Versions> {
versions.remove("safari");
}

Versions(
Targets::new(
versions
.into_iter()
.map(|(k, v)| (k, v.parse::<Version>().unwrap()))
Expand All @@ -40,17 +39,18 @@ fn features() -> &'static FxHashMap<String, Versions> {
}

/// Reference: <https://github.com/swc-project/swc/blob/ea14fc8e5996dcd736b8deb4cc99262d07dfff44/crates/swc_ecma_preset_env/src/transform_data.rs#L220-L237>
fn bugfix_features() -> &'static FxHashMap<String, Versions> {
static BUGFIX_FEATURES: OnceLock<FxHashMap<String, Versions>> = OnceLock::new();
fn bugfix_features() -> &'static FxHashMap<String, Targets> {
static BUGFIX_FEATURES: OnceLock<FxHashMap<String, Targets>> = OnceLock::new();
BUGFIX_FEATURES.get_or_init(|| {
let map: FxHashMap<String, Versions> =
serde_json::from_str(include_str!("./@babel/compat_data/data/plugin_bugfixes.json"))
.expect("failed to parse json");
let map = serde_json::from_str::<FxHashMap<String, Targets>>(include_str!(
"./@babel/compat_data/data/plugin_bugfixes.json"
))
.unwrap();
features().clone().into_iter().chain(map).collect()
})
}

pub fn can_enable_plugin(name: &str, targets: Option<&Versions>, bugfixes: bool) -> bool {
pub fn can_enable_plugin(name: &str, targets: Option<&Targets>, bugfixes: bool) -> bool {
let versions = if bugfixes {
bugfix_features().get(name).unwrap_or_else(|| &features()[name])
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_transformer/src/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ mod targets;

pub use data::can_enable_plugin;
pub use options::EnvOptions;
pub use targets::{Targets, Versions};
pub use targets::Targets;
2 changes: 1 addition & 1 deletion crates/oxc_transformer/src/env/options.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::Deserialize;
use serde_json::Value;

use super::targets::query::Targets;
use crate::env::Targets;

fn default_as_true() -> bool {
true
Expand Down
112 changes: 91 additions & 21 deletions crates/oxc_transformer/src/env/targets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,52 @@
//!
//! This file is copied from <https://github.com/swc-project/swc/blob/ea14fc8e5996dcd736b8deb4cc99262d07dfff44/crates/preset_env_base/src/lib.rs>

use std::ops::{Deref, DerefMut};
use std::{ops::Deref, str::FromStr};

use oxc_diagnostics::Error;
use rustc_hash::FxHashMap;
use serde::Deserialize;

pub mod query;
pub mod version;
pub use query::Targets;
use version::Version;

pub use query::Query;
pub use version::Version;

/// A map of browser names to data for feature support in browser.
///
/// This type mainly stores `minimum version for each browsers with support for
/// a feature`.
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Versions(pub FxHashMap<String, Version>);
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(try_from = "BabelTargets")] // https://github.com/serde-rs/serde/issues/642#issuecomment-683276351
pub struct Targets(FxHashMap<String, Version>);

impl Deref for Versions {
impl Deref for Targets {
type Target = FxHashMap<String, Version>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for Versions {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
impl Targets {
pub fn new(map: FxHashMap<String, Version>) -> Self {
Self(map)
}

/// # Errors
///
/// * Query is invalid.
pub fn try_from_query(query: &str) -> Result<Self, oxc_diagnostics::Error> {
Query::Single(query.to_string()).exec().map(|v| v.0).map(Self)
}
}

impl Versions {
/// Returns true if all fields are [None].
pub fn is_any_target(&self) -> bool {
self.0.is_empty()
}

/// Parses the value returned from `browserslist` as [Versions].
/// Parses the value returned from `browserslist`.
pub fn parse_versions(distribs: Vec<browserslist::Distrib>) -> Self {
fn remap(key: &str) -> &str {
match key {
Expand All @@ -55,7 +63,7 @@ impl Versions {
}
}

let mut data: Versions = Versions::default();
let mut data = FxHashMap::default();
for dist in distribs {
let browser = dist.name();
let browser = remap(browser);
Expand All @@ -78,11 +86,11 @@ impl Versions {
}
}

data
Self(data)
}

pub fn should_enable(&self, feature: &Versions) -> bool {
self.iter().any(|(target_name, target_version)| {
pub fn should_enable(&self, feature: &Targets) -> bool {
self.0.iter().any(|(target_name, target_version)| {
feature
.get(target_name)
.or_else(|| match target_name.as_str() {
Expand All @@ -97,16 +105,78 @@ impl Versions {
}
}

/// <https://babel.dev/docs/babel-preset-env#targets>
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum BabelTargets {
String(String),
Array(Vec<String>),
/// For Deserializing
/// * `esmodules`: `boolean`
/// * `node`: `string | "current" | true`
/// * `safari`: `string | "tp"`
/// * `browsers`: `string | Array<string>.`
/// * `deno`: `string`
Map(FxHashMap<String, BabelTargetsValue>),
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum BabelTargetsValue {
String(String),
Array(Vec<String>),
Bool(bool),
Int(u32),
Float(f64),
}

impl TryFrom<BabelTargets> for Targets {
type Error = Error;
fn try_from(value: BabelTargets) -> Result<Self, Self::Error> {
match value {
BabelTargets::String(s) => Query::Single(s).exec().map(|v| v.0).map(Self),
BabelTargets::Array(v) => Query::Multiple(v).exec().map(|v| v.0).map(Self),
BabelTargets::Map(map) => {
let mut new_map = FxHashMap::default();
for (k, v) in map {
// TODO: Implement these targets.
if matches!(k.as_str(), "esmodules" | "node" | "safari" | "browsers" | "deno") {
continue;
}
// TODO: Implement `Version::from_number`
if matches!(v, BabelTargetsValue::Int(_) | BabelTargetsValue::Float(_)) {
continue;
};
let BabelTargetsValue::String(v) = v else {
return Err(Error::msg(format!("{v:?} is not a string for {k}.")));
};
match Version::from_str(&v) {
Ok(v) => {
new_map.insert(k, v);
}
Err(()) => {
return Err(oxc_diagnostics::Error::msg(format!(
"Failed to parse `{v}` for `{k}`"
)))
}
}
}
Ok(Self(new_map))
}
}
}
}

#[cfg(test)]
mod tests {
use crate::env::{targets::version::Version, Versions};
use crate::env::{targets::version::Version, Targets};

#[test]
fn should_enable_android_falls_back_to_chrome() {
let mut targets = Versions::default();
targets.insert("android".to_string(), "51.0.0".parse::<Version>().unwrap());
let mut feature = Versions::default();
feature.insert("chrome".to_string(), "51.0.0".parse::<Version>().unwrap());
let mut targets = Targets::default();
targets.0.insert("android".to_string(), "51.0.0".parse::<Version>().unwrap());
let mut feature = Targets::default();
feature.0.insert("chrome".to_string(), "51.0.0".parse::<Version>().unwrap());
assert!(!targets.should_enable(&feature));
}
}
Loading