feat(transformer): add EnvOptions::from_browerslist_query API (#7098)

This commit is contained in:
Boshen 2024-11-03 12:43:25 +00:00
parent 21b8e4988f
commit bfdbcf1c6a
8 changed files with 95 additions and 45 deletions

View file

@ -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 {

View file

@ -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)
}

View file

@ -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 {

View file

@ -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
});

View file

@ -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
}

View file

@ -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}");
}

View file

@ -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}");
}

View 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));
}
}