mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
feat(transformer): add EnvOptions::from_browerslist_query API (#7098)
This commit is contained in:
parent
21b8e4988f
commit
bfdbcf1c6a
8 changed files with 95 additions and 45 deletions
|
|
@ -6,7 +6,7 @@ use oxc_codegen::CodeGenerator;
|
|||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{ESTarget, TransformOptions, Transformer};
|
||||
use oxc_transformer::{ESTarget, EnvOptions, TransformOptions, Transformer};
|
||||
use pico_args::Arguments;
|
||||
|
||||
// Instruction:
|
||||
|
|
@ -55,14 +55,11 @@ fn main() {
|
|||
|
||||
let (symbols, scopes) = ret.semantic.into_symbol_table_and_scope_tree();
|
||||
|
||||
let transform_options = if let Some(_targets) = &targets {
|
||||
// FIXME
|
||||
TransformOptions::enable_all()
|
||||
// TransformOptions::try_from(&BabelEnvOptions {
|
||||
// targets: Targets::try_from_query(targets).unwrap(),
|
||||
// ..BabelEnvOptions::default()
|
||||
// })
|
||||
// .unwrap()
|
||||
let transform_options = if let Some(query) = &targets {
|
||||
TransformOptions {
|
||||
env: EnvOptions::from_browerslist_query(query).unwrap(),
|
||||
..TransformOptions::default()
|
||||
}
|
||||
} else if let Some(target) = &target {
|
||||
TransformOptions::from(ESTarget::from_str(target).unwrap())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl Targets {
|
|||
/// # Errors
|
||||
///
|
||||
/// * Query is invalid.
|
||||
pub fn try_from_query(query: &str) -> Result<Self, oxc_diagnostics::Error> {
|
||||
pub fn try_from_query(query: &str) -> Result<Self, Error> {
|
||||
Query::Single(query.to_string()).exec().map(|v| v.0).map(Self)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use cow_utils::CowUtils;
|
||||
use oxc_diagnostics::Error;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -13,6 +14,7 @@ use crate::{
|
|||
es2021::ES2021Options,
|
||||
es2022::{ClassPropertiesOptions, ES2022Options},
|
||||
regexp::RegExpOptions,
|
||||
Targets,
|
||||
};
|
||||
|
||||
use super::babel::BabelEnvOptions;
|
||||
|
|
@ -131,6 +133,20 @@ impl EnvOptions {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
///
|
||||
/// * When the query failed to parse.
|
||||
pub fn from_browerslist_query(query: &str) -> Result<Self, Error> {
|
||||
Self::try_from(BabelEnvOptions {
|
||||
targets: Targets::try_from_query(query)?,
|
||||
// This option will be enabled by default in Babel 8.
|
||||
// <https://babel.dev/docs/babel-preset-env#bugfixes>
|
||||
bugfixes: true,
|
||||
..BabelEnvOptions::default()
|
||||
})
|
||||
.map_err(|err| Error::msg(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ESTarget> for EnvOptions {
|
||||
|
|
|
|||
|
|
@ -1,35 +1,11 @@
|
|||
use std::{path::Path, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_codegen::{CodeGenerator, CodegenOptions};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use crate::{codegen, test};
|
||||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{ESTarget, TransformOptions, Transformer};
|
||||
|
||||
use crate::run;
|
||||
|
||||
pub(crate) fn test(source_text: &str, target: &str) -> String {
|
||||
let source_type = SourceType::default();
|
||||
let allocator = Allocator::default();
|
||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
||||
let mut program = ret.program;
|
||||
let (symbols, scopes) =
|
||||
SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree();
|
||||
let options = TransformOptions::from(ESTarget::from_str(target).unwrap());
|
||||
Transformer::new(&allocator, Path::new(""), options).build_with_symbols_and_scopes(
|
||||
symbols,
|
||||
scopes,
|
||||
&mut program,
|
||||
);
|
||||
CodeGenerator::new()
|
||||
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
|
||||
.build(&program)
|
||||
.code
|
||||
}
|
||||
use oxc_transformer::{ESTarget, TransformOptions};
|
||||
|
||||
#[test]
|
||||
fn es2015() {
|
||||
fn es_target() {
|
||||
use std::fmt::Write;
|
||||
|
||||
let cases = [
|
||||
|
|
@ -45,11 +21,13 @@ fn es2015() {
|
|||
|
||||
// Test no transformation for esnext.
|
||||
for (_, case) in cases {
|
||||
assert_eq!(run(case, SourceType::mjs()), test(case, "esnext"));
|
||||
let options = TransformOptions::from(ESTarget::from_str("esnext").unwrap());
|
||||
assert_eq!(codegen(case, SourceType::mjs()), test(case, options));
|
||||
}
|
||||
|
||||
let snapshot = cases.iter().enumerate().fold(String::new(), |mut w, (i, (target, case))| {
|
||||
let result = test(case, target);
|
||||
let options = TransformOptions::from(ESTarget::from_str(target).unwrap());
|
||||
let result = test(case, options);
|
||||
write!(w, "########## {i} {target}\n{case}\n----------\n{result}\n").unwrap();
|
||||
w
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
mod es_target;
|
||||
mod plugins;
|
||||
mod targets;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_codegen::{CodeGenerator, CodegenOptions};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{TransformOptions, Transformer};
|
||||
|
||||
pub fn run(source_text: &str, source_type: SourceType) -> String {
|
||||
pub fn codegen(source_text: &str, source_type: SourceType) -> String {
|
||||
let allocator = Allocator::default();
|
||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
||||
CodeGenerator::new()
|
||||
|
|
@ -14,3 +19,21 @@ pub fn run(source_text: &str, source_type: SourceType) -> String {
|
|||
.build(&ret.program)
|
||||
.code
|
||||
}
|
||||
|
||||
pub(crate) fn test(source_text: &str, options: TransformOptions) -> String {
|
||||
let source_type = SourceType::default();
|
||||
let allocator = Allocator::default();
|
||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
||||
let mut program = ret.program;
|
||||
let (symbols, scopes) =
|
||||
SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree();
|
||||
Transformer::new(&allocator, Path::new(""), options).build_with_symbols_and_scopes(
|
||||
symbols,
|
||||
scopes,
|
||||
&mut program,
|
||||
);
|
||||
CodeGenerator::new()
|
||||
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
|
||||
.build(&program)
|
||||
.code
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use oxc_semantic::SemanticBuilder;
|
|||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{InjectGlobalVariables, InjectGlobalVariablesConfig, InjectImport};
|
||||
|
||||
use crate::run;
|
||||
use crate::codegen;
|
||||
|
||||
pub(crate) fn test(source_text: &str, expected: &str, config: InjectGlobalVariablesConfig) {
|
||||
let source_type = SourceType::default();
|
||||
|
|
@ -23,7 +23,7 @@ pub(crate) fn test(source_text: &str, expected: &str, config: InjectGlobalVariab
|
|||
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
|
||||
.build(&program)
|
||||
.code;
|
||||
let expected = run(expected, source_type);
|
||||
let expected = codegen(expected, source_type);
|
||||
assert_eq!(result, expected, "for source {source_text}");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use oxc_semantic::SemanticBuilder;
|
|||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{ReplaceGlobalDefines, ReplaceGlobalDefinesConfig};
|
||||
|
||||
use crate::run;
|
||||
use crate::codegen;
|
||||
|
||||
pub(crate) fn test(source_text: &str, expected: &str, config: ReplaceGlobalDefinesConfig) {
|
||||
let source_type = SourceType::default();
|
||||
|
|
@ -19,7 +19,7 @@ pub(crate) fn test(source_text: &str, expected: &str, config: ReplaceGlobalDefin
|
|||
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
|
||||
.build(&program)
|
||||
.code;
|
||||
let expected = run(expected, source_type);
|
||||
let expected = codegen(expected, source_type);
|
||||
assert_eq!(result, expected, "for source {source_text}");
|
||||
}
|
||||
|
||||
|
|
|
|||
36
crates/oxc_transformer/tests/targets/mod.rs
Normal file
36
crates/oxc_transformer/tests/targets/mod.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use crate::{codegen, test};
|
||||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{ESTarget, EnvOptions, TransformOptions};
|
||||
|
||||
#[test]
|
||||
fn targets() {
|
||||
let cases = [
|
||||
("() => {}"),
|
||||
("a ** b"),
|
||||
// ("async function foo() {}"),
|
||||
("({ ...x })"),
|
||||
("try {} catch {}"),
|
||||
("a ?? b"),
|
||||
("a ||= b"),
|
||||
// ("class foo { static {} }"),
|
||||
];
|
||||
|
||||
// Test no transformation for default targets.
|
||||
for case in cases {
|
||||
let options = TransformOptions {
|
||||
env: EnvOptions::from_browerslist_query("defaults").unwrap(),
|
||||
..TransformOptions::default()
|
||||
};
|
||||
assert_eq!(codegen(case, SourceType::mjs()), test(case, options));
|
||||
}
|
||||
|
||||
// Test transformation for very low targets.
|
||||
for case in cases {
|
||||
let options = TransformOptions::from(ESTarget::ES5);
|
||||
let options_node = TransformOptions {
|
||||
env: EnvOptions::from_browerslist_query("node 0.10").unwrap(),
|
||||
..TransformOptions::default()
|
||||
};
|
||||
assert_eq!(test(case, options), test(case, options_node));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue