Skip to content

Commit

Permalink
refactor(transformer): change Targets to EngineTargets (#7142)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Nov 5, 2024
1 parent 55e6989 commit 481f7e6
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 130 deletions.
4 changes: 2 additions & 2 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ pub use crate::{
es2015::{ArrowFunctionsOptions, ES2015Options},
jsx::{JsxOptions, JsxRuntime, ReactRefreshOptions},
options::{
babel::{BabelEnvOptions, BabelOptions, Targets},
ESTarget, EnvOptions, TransformOptions,
babel::{BabelEnvOptions, BabelOptions},
ESTarget, EngineTargets, EnvOptions, TransformOptions,
},
plugins::*,
typescript::{RewriteExtensionsMode, TypeScriptOptions},
Expand Down
14 changes: 7 additions & 7 deletions crates/oxc_transformer/src/options/babel/env/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use std::sync::OnceLock;

use rustc_hash::FxHashMap;

use super::Targets;
use super::EngineTargets;

/// Reference: <https://github.com/swc-project/swc/blob/ea14fc8e5996dcd736b8deb4cc99262d07dfff44/crates/swc_ecma_preset_env/src/transform_data.rs#L194-L218>
pub fn features() -> &'static FxHashMap<String, Targets> {
static FEATURES: OnceLock<FxHashMap<String, Targets>> = OnceLock::new();
pub fn features() -> &'static FxHashMap<String, EngineTargets> {
static FEATURES: OnceLock<FxHashMap<String, EngineTargets>> = 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")).unwrap();
Expand All @@ -25,17 +25,17 @@ pub fn features() -> &'static FxHashMap<String, Targets> {
versions.remove("safari");
}
let versions = versions.into_iter().collect::<Vec<_>>();
(feature, Targets::parse_versions(versions))
(feature, EngineTargets::parse_versions(versions))
})
.collect()
})
}

/// Reference: <https://github.com/swc-project/swc/blob/ea14fc8e5996dcd736b8deb4cc99262d07dfff44/crates/swc_ecma_preset_env/src/transform_data.rs#L220-L237>
pub fn bugfix_features() -> &'static FxHashMap<String, Targets> {
static BUGFIX_FEATURES: OnceLock<FxHashMap<String, Targets>> = OnceLock::new();
pub fn bugfix_features() -> &'static FxHashMap<String, EngineTargets> {
static BUGFIX_FEATURES: OnceLock<FxHashMap<String, EngineTargets>> = OnceLock::new();
BUGFIX_FEATURES.get_or_init(|| {
let map = serde_json::from_str::<FxHashMap<String, Targets>>(include_str!(
let map = serde_json::from_str::<FxHashMap<String, EngineTargets>>(include_str!(
"./@babel/compat_data/data/plugin_bugfixes.json"
))
.unwrap();
Expand Down
13 changes: 9 additions & 4 deletions crates/oxc_transformer/src/options/babel/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ mod data;
mod query;
mod targets;

pub use data::{bugfix_features, features};
pub use targets::Targets;

use serde::Deserialize;

pub use self::{
data::{bugfix_features, features},
query::Query,
targets::BabelTargets,
};

use crate::options::EngineTargets;

fn default_as_true() -> bool {
true
}
Expand All @@ -15,7 +20,7 @@ fn default_as_true() -> bool {
#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
pub struct BabelEnvOptions {
#[serde(default)]
pub targets: Targets,
pub targets: EngineTargets,

#[serde(default = "default_as_true")]
pub bugfixes: bool,
Expand Down
10 changes: 5 additions & 5 deletions crates/oxc_transformer/src/options/babel/env/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::Deserialize;

use oxc_diagnostics::{Error, OxcDiagnostic};

use super::Targets;
use super::EngineTargets;

#[derive(Debug, Clone, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[serde(untagged)]
Expand All @@ -14,13 +14,13 @@ pub enum Query {
Multiple(Vec<String>),
}

fn cache() -> &'static DashMap<Query, Targets> {
static CACHE: OnceLock<DashMap<Query, Targets>> = OnceLock::new();
fn cache() -> &'static DashMap<Query, EngineTargets> {
static CACHE: OnceLock<DashMap<Query, EngineTargets>> = OnceLock::new();
CACHE.get_or_init(DashMap::new)
}

impl Query {
pub fn exec(&self) -> Result<Targets, Error> {
pub fn exec(&self) -> Result<EngineTargets, Error> {
if let Some(v) = cache().get(self) {
return Ok(v.clone());
}
Expand Down Expand Up @@ -48,7 +48,7 @@ impl Query {
.into_iter()
.map(|d| (d.name().to_string(), d.version().to_string()))
.collect::<Vec<_>>();
Targets::parse_versions(versions)
EngineTargets::parse_versions(versions)
}
Err(err) => {
return Err(OxcDiagnostic::error(format!("failed to resolve query: {err}")).into())
Expand Down
111 changes: 4 additions & 107 deletions crates/oxc_transformer/src/options/babel/env/targets.rs
Original file line number Diff line number Diff line change
@@ -1,115 +1,12 @@
use oxc_diagnostics::Error;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use oxc_diagnostics::Error;

pub use browserslist::Version;

use super::query::Query;

/// 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, Default, Clone, Eq, PartialEq, Deserialize)]
#[serde(try_from = "BabelTargets")]
pub struct Targets {
chrome: Option<Version>,
deno: Option<Version>,
edge: Option<Version>,
firefox: Option<Version>,
hermes: Option<Version>,
ie: Option<Version>,
ios: Option<Version>,
node: Option<Version>,
opera: Option<Version>,
rhino: Option<Version>,
safari: Option<Version>,
}

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

/// Returns true if all fields are [None].
pub fn is_any_target(&self) -> bool {
*self == Self::default()
}

pub fn should_enable(&self, targets: &Targets) -> bool {
if let (Some(v1), Some(v2)) = (&self.chrome, &targets.chrome) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.deno, &targets.deno) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.edge, &targets.edge) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.firefox, &targets.firefox) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.hermes, &targets.hermes) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.ie, &targets.ie) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.ios, &targets.ios) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.node, &targets.node) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.opera, &targets.opera) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.rhino, &targets.rhino) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.safari, &targets.safari) {
return v1 < v2;
}
false
}

/// Parses the value returned from `browserslist`.
pub fn parse_versions(versions: Vec<(String, String)>) -> Self {
let mut targets = Self::default();
for (name, version) in versions {
let Ok(browser) = targets.get_version_mut(&name) else {
continue;
};
let Ok(version) = version.parse::<Version>() else {
continue;
};
if browser.is_none() || browser.is_some_and(|v| version < v) {
browser.replace(version);
}
}
targets
}

fn get_version_mut(&mut self, key: &str) -> Result<&mut Option<Version>, ()> {
match key {
"chrome" | "and_chr" => Ok(&mut self.chrome),
"deno" => Ok(&mut self.deno),
"edge" => Ok(&mut self.edge),
"firefox" | "and_ff" => Ok(&mut self.firefox),
"hermes" => Ok(&mut self.hermes),
"ie" | "ie_mob" => Ok(&mut self.ie),
"ios" | "ios_saf" => Ok(&mut self.ios),
"node" => Ok(&mut self.node),
"opera" | "op_mob" => Ok(&mut self.opera),
"rhino" => Ok(&mut self.rhino),
"safari" => Ok(&mut self.safari),
_ => Err(()),
}
}
}
use crate::options::EngineTargets;

/// <https://babel.dev/docs/babel-preset-env#targets>
#[derive(Debug, Deserialize)]
Expand All @@ -136,7 +33,7 @@ pub enum BabelTargetsValue {
Float(f64),
}

impl TryFrom<BabelTargets> for Targets {
impl TryFrom<BabelTargets> for EngineTargets {
type Error = Error;
fn try_from(value: BabelTargets) -> Result<Self, Self::Error> {
match value {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_transformer/src/options/babel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::{de::DeserializeOwned, Deserialize};

use crate::CompilerAssumptions;

pub use self::env::{BabelEnvOptions, Targets};
pub use self::env::{BabelEnvOptions, BabelTargets, Query};
use self::{plugins::BabelPlugins, presets::BabelPresets};

/// Babel options
Expand Down
108 changes: 108 additions & 0 deletions crates/oxc_transformer/src/options/engine_targets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use browserslist::Version;
use serde::Deserialize;

use oxc_diagnostics::Error;

use super::babel::{BabelTargets, Query};

/// A map of engine names to minimum supported versions.
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize)]
#[serde(try_from = "BabelTargets")]
pub struct EngineTargets {
chrome: Option<Version>,
deno: Option<Version>,
edge: Option<Version>,
firefox: Option<Version>,
hermes: Option<Version>,
ie: Option<Version>,
ios: Option<Version>,
node: Option<Version>,
opera: Option<Version>,
rhino: Option<Version>,
safari: Option<Version>,
}

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

/// Returns true if all fields are [None].
pub fn is_any_target(&self) -> bool {
*self == Self::default()
}

pub fn should_enable(&self, targets: &EngineTargets) -> bool {
if let (Some(v1), Some(v2)) = (&self.chrome, &targets.chrome) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.deno, &targets.deno) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.edge, &targets.edge) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.firefox, &targets.firefox) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.hermes, &targets.hermes) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.ie, &targets.ie) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.ios, &targets.ios) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.node, &targets.node) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.opera, &targets.opera) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.rhino, &targets.rhino) {
return v1 < v2;
}
if let (Some(v1), Some(v2)) = (&self.safari, &targets.safari) {
return v1 < v2;
}
false
}

/// Parses the value returned from `browserslist`.
pub fn parse_versions(versions: Vec<(String, String)>) -> Self {
let mut targets = Self::default();
for (name, version) in versions {
let Ok(browser) = targets.get_version_mut(&name) else {
continue;
};
let Ok(version) = version.parse::<Version>() else {
continue;
};
if browser.is_none() || browser.is_some_and(|v| version < v) {
browser.replace(version);
}
}
targets
}

pub(crate) fn get_version_mut(&mut self, key: &str) -> Result<&mut Option<Version>, ()> {
match key {
"chrome" | "and_chr" => Ok(&mut self.chrome),
"deno" => Ok(&mut self.deno),
"edge" => Ok(&mut self.edge),
"firefox" | "and_ff" => Ok(&mut self.firefox),
"hermes" => Ok(&mut self.hermes),
"ie" | "ie_mob" => Ok(&mut self.ie),
"ios" | "ios_saf" => Ok(&mut self.ios),
"node" => Ok(&mut self.node),
"opera" | "op_mob" => Ok(&mut self.opera),
"rhino" => Ok(&mut self.rhino),
"safari" => Ok(&mut self.safari),
_ => Err(()),
}
}
}
4 changes: 2 additions & 2 deletions crates/oxc_transformer/src/options/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
es2021::ES2021Options,
es2022::{ClassPropertiesOptions, ES2022Options},
regexp::RegExpOptions,
Targets,
EngineTargets,
};

use super::babel::BabelEnvOptions;
Expand Down Expand Up @@ -139,7 +139,7 @@ impl EnvOptions {
/// * When the query failed to parse.
pub fn from_browserslist_query(query: &str) -> Result<Self, Error> {
Self::try_from(BabelEnvOptions {
targets: Targets::try_from_query(query)?,
targets: EngineTargets::try_from_query(query)?,
// This option will be enabled by default in Babel 8.
// <https://babel.dev/docs/babel-preset-env#bugfixes>
bugfixes: true,
Expand Down
8 changes: 6 additions & 2 deletions crates/oxc_transformer/src/options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod babel;
mod engine_targets;
mod env;

use std::path::PathBuf;
Expand All @@ -22,9 +23,12 @@ use crate::{
ReactRefreshOptions,
};

pub use env::{ESTarget, EnvOptions};
pub use self::{
engine_targets::EngineTargets,
env::{ESTarget, EnvOptions},
};

use babel::BabelOptions;
use self::babel::BabelOptions;

/// <https://babel.dev/docs/options>
#[derive(Debug, Default, Clone)]
Expand Down

0 comments on commit 481f7e6

Please sign in to comment.