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
11 changes: 11 additions & 0 deletions crates/oxc_compat/src/es_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub enum ESFeature {
ES2024UnicodeSetsRegex,
ES2025DuplicateNamedCapturingGroupsRegex,
ES2025RegexpModifiers,
ES2026ExplicitResourceManagement,
}
pub fn features() -> &'static FxHashMap<ESFeature, EngineTargets> {
use ESFeature::*;
Expand Down Expand Up @@ -875,6 +876,16 @@ pub fn features() -> &'static FxHashMap<ESFeature, EngineTargets> {
(Es, Version(2025u16, 0, 0)),
])),
),
(
ES2026ExplicitResourceManagement,
EngineTargets::new(FxHashMap::from_iter([
(Chrome, Version(134u16, 0u16, 0u16)),
(Node, Version(24u16, 0u16, 0u16)),
(Firefox, Version(141u16, 0u16, 0u16)),
(Edge, Version(134u16, 0u16, 0u16)),
(Es, Version(2026u16, 0, 0)),
])),
),
])
})
}
1 change: 1 addition & 0 deletions crates/oxc_compat/src/es_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ impl ESVersion for ESTarget {
Self::ES2023 => Version(2023, 0, 0),
Self::ES2024 => Version(2024, 0, 0),
Self::ES2025 => Version(2025, 0, 0),
Self::ES2026 => Version(2026, 0, 0),
Self::ESNext => Version(9999, 0, 0),
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_syntax/src/es_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum ESTarget {
ES2023,
ES2024,
ES2025,
ES2026,
#[default]
ESNext,
}
Expand All @@ -39,6 +40,7 @@ impl FromStr for ESTarget {
"es2023" => Ok(Self::ES2023),
"es2024" => Ok(Self::ES2024),
"es2025" => Ok(Self::ES2025),
"es2026" => Ok(Self::ES2026),
"esnext" => Ok(Self::ESNext),
_ => Err(format!("Invalid target \"{s}\".")),
}
Expand All @@ -59,6 +61,7 @@ impl fmt::Display for ESTarget {
Self::ES2023 => "es2023",
Self::ES2024 => "es2024",
Self::ES2025 => "es2025",
Self::ES2026 => "es2026",
Self::ESNext => "esnext",
};
f.write_str(s)
Expand Down
70 changes: 70 additions & 0 deletions crates/oxc_transformer/src/es2026/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use oxc_ast::ast::*;
use oxc_traverse::Traverse;

use crate::{
context::{TransformCtx, TraverseCtx},
state::TransformState,
};

mod explicit_resource_management;
mod options;

use explicit_resource_management::ExplicitResourceManagement;
pub use options::ES2026Options;

pub struct ES2026<'a, 'ctx> {
explicit_resource_management: Option<ExplicitResourceManagement<'a, 'ctx>>,
}

impl<'a, 'ctx> ES2026<'a, 'ctx> {
pub fn new(options: ES2026Options, ctx: &'ctx TransformCtx<'a>) -> Self {
let explicit_resource_management = if options.explicit_resource_management {
Some(ExplicitResourceManagement::new(ctx))
} else {
None
};
Self { explicit_resource_management }
}
}

impl<'a> Traverse<'a, TransformState<'a>> for ES2026<'a, '_> {
fn enter_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(explicit_resource_management) = &mut self.explicit_resource_management {
explicit_resource_management.enter_program(program, ctx);
}
}

fn enter_for_of_statement(
&mut self,
for_of_stmt: &mut ForOfStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if let Some(explicit_resource_management) = &mut self.explicit_resource_management {
explicit_resource_management.enter_for_of_statement(for_of_stmt, ctx);
}
}

fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(explicit_resource_management) = &mut self.explicit_resource_management {
explicit_resource_management.exit_static_block(block, ctx);
}
}

fn enter_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(explicit_resource_management) = &mut self.explicit_resource_management {
explicit_resource_management.enter_function_body(body, ctx);
}
}

fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(explicit_resource_management) = &mut self.explicit_resource_management {
explicit_resource_management.enter_statement(stmt, ctx);
}
}

fn enter_try_statement(&mut self, node: &mut TryStatement<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(explicit_resource_management) = &mut self.explicit_resource_management {
explicit_resource_management.enter_try_statement(node, ctx);
}
}
}
8 changes: 8 additions & 0 deletions crates/oxc_transformer/src/es2026/options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use serde::Deserialize;

#[derive(Debug, Default, Clone, Copy, Deserialize)]
#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
pub struct ES2026Options {
#[serde(skip)]
pub explicit_resource_management: bool,
}
36 changes: 12 additions & 24 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod es2019;
mod es2020;
mod es2021;
mod es2022;
mod es2026;
mod jsx;
mod proposals;
mod regexp;
Expand All @@ -50,8 +51,8 @@ use es2019::ES2019;
use es2020::ES2020;
use es2021::ES2021;
use es2022::ES2022;
use es2026::ES2026;
use jsx::Jsx;
use proposals::ExplicitResourceManagement;
use regexp::RegExp;
use rustc_hash::FxHashMap;
use state::TransformState;
Expand All @@ -70,6 +71,7 @@ pub use crate::{
es2020::ES2020Options,
es2021::ES2021Options,
es2022::{ClassPropertiesOptions, ES2022Options},
es2026::ES2026Options,
jsx::{JsxOptions, JsxRuntime, ReactRefreshOptions},
options::{
ESTarget, Engine, EngineTargets, EnvOptions, Module, TransformOptions,
Expand Down Expand Up @@ -98,6 +100,7 @@ pub struct Transformer<'a> {
plugins: PluginsOptions,
jsx: JsxOptions,
env: EnvOptions,
#[expect(dead_code)]
proposals: ProposalOptions,
}

Expand Down Expand Up @@ -140,15 +143,12 @@ impl<'a> Transformer<'a> {
common: Common::new(&self.env, &self.ctx),
decorator: Decorator::new(self.decorator, &self.ctx),
plugins: Plugins::new(self.plugins, &self.ctx),
explicit_resource_management: self
.proposals
.explicit_resource_management
.then(|| ExplicitResourceManagement::new(&self.ctx)),
x0_typescript: program
.source_type
.is_typescript()
.then(|| TypeScript::new(&self.typescript, &self.ctx)),
x1_jsx: Jsx::new(self.jsx, self.env.es2018.object_rest_spread, ast_builder, &self.ctx),
x2_es2026: ES2026::new(self.env.es2026, &self.ctx),
x2_es2022: ES2022::new(
self.env.es2022,
!self.typescript.allow_declare_fields
Expand Down Expand Up @@ -178,8 +178,8 @@ struct TransformerImpl<'a, 'ctx> {
x0_typescript: Option<TypeScript<'a, 'ctx>>,
decorator: Decorator<'a, 'ctx>,
plugins: Plugins<'a, 'ctx>,
explicit_resource_management: Option<ExplicitResourceManagement<'a, 'ctx>>,
x1_jsx: Jsx<'a, 'ctx>,
x2_es2026: ES2026<'a, 'ctx>,
x2_es2022: ES2022<'a, 'ctx>,
x2_es2021: ES2021<'a, 'ctx>,
x2_es2020: ES2020<'a, 'ctx>,
Expand All @@ -200,9 +200,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {
}
self.plugins.enter_program(program, ctx);
self.x1_jsx.enter_program(program, ctx);
if let Some(explicit_resource_management) = self.explicit_resource_management.as_mut() {
explicit_resource_management.enter_program(program, ctx);
}
self.x2_es2026.enter_program(program, ctx);
}

fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down Expand Up @@ -316,9 +314,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {

fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
self.common.exit_static_block(block, ctx);
if let Some(explicit_resource_management) = self.explicit_resource_management.as_mut() {
explicit_resource_management.exit_static_block(block, ctx);
}
self.x2_es2026.exit_static_block(block, ctx);
self.x2_es2022.exit_static_block(block, ctx);
}

Expand Down Expand Up @@ -409,9 +405,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {

fn enter_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
self.common.enter_function_body(body, ctx);
if let Some(explicit_resource_management) = self.explicit_resource_management.as_mut() {
explicit_resource_management.enter_function_body(body, ctx);
}
self.x2_es2026.enter_function_body(body, ctx);
}

fn exit_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down Expand Up @@ -603,9 +597,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {
typescript.enter_statement(stmt, ctx);
}
self.x2_es2018.enter_statement(stmt, ctx);
if let Some(explicit_resource_management) = self.explicit_resource_management.as_mut() {
explicit_resource_management.enter_statement(stmt, ctx);
}
self.x2_es2026.enter_statement(stmt, ctx);
}

fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down Expand Up @@ -646,9 +638,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {
if let Some(typescript) = self.x0_typescript.as_mut() {
typescript.enter_for_of_statement(stmt, ctx);
}
if let Some(explicit_resource_management) = self.explicit_resource_management.as_mut() {
explicit_resource_management.enter_for_of_statement(stmt, ctx);
}
self.x2_es2026.enter_for_of_statement(stmt, ctx);
self.x2_es2018.enter_for_of_statement(stmt, ctx);
}

Expand All @@ -660,9 +650,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {
}

fn enter_try_statement(&mut self, stmt: &mut TryStatement<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(explicit_resource_management) = self.explicit_resource_management.as_mut() {
explicit_resource_management.enter_try_statement(stmt, ctx);
}
self.x2_es2026.enter_try_statement(stmt, ctx);
}

fn enter_catch_clause(&mut self, clause: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_transformer/src/options/babel/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ pub struct BabelPlugins {
// ES2022
pub class_static_block: bool,
pub class_properties: Option<ClassPropertiesOptions>,
// ES2026
pub explicit_resource_management: bool,
// Decorator
pub legacy_decorator: Option<DecoratorOptions>,
// Proposals
pub explicit_resource_management: bool,
// Built-in plugins
pub styled_components: Option<StyledComponentsOptions>,
}
Expand Down
7 changes: 7 additions & 0 deletions crates/oxc_transformer/src/options/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
es2020::ES2020Options,
es2021::ES2021Options,
es2022::{ClassPropertiesOptions, ES2022Options},
es2026::ES2026Options,
regexp::RegExpOptions,
};

Expand Down Expand Up @@ -38,6 +39,8 @@ pub struct EnvOptions {
pub es2021: ES2021Options,

pub es2022: ES2022Options,

pub es2026: ES2026Options,
}

impl EnvOptions {
Expand Down Expand Up @@ -84,6 +87,7 @@ impl EnvOptions {
class_static_block: true,
class_properties: Some(ClassPropertiesOptions::default()),
},
es2026: ES2026Options { explicit_resource_management: true },
}
}

Expand Down Expand Up @@ -161,6 +165,9 @@ impl From<EngineTargets> for EnvOptions {
class_static_block: o.has_feature(ES2022ClassStaticBlock),
class_properties: o.has_feature(ES2022ClassProperties).then(Default::default),
},
es2026: ES2026Options {
explicit_resource_management: o.has_feature(ES2026ExplicitResourceManagement),
},
}
}
}
8 changes: 5 additions & 3 deletions crates/oxc_transformer/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{
es2020::ES2020Options,
es2021::ES2021Options,
es2022::ES2022Options,
es2026::ES2026Options,
jsx::JsxOptions,
plugins::{PluginsOptions, StyledComponentsOptions},
proposals::ProposalOptions,
Expand Down Expand Up @@ -277,10 +278,11 @@ impl TryFrom<&BabelOptions> for TransformOptions {
es2020,
es2021,
es2022,
es2026: ES2026Options {
explicit_resource_management: options.plugins.explicit_resource_management,
},
},
proposals: ProposalOptions {
explicit_resource_management: options.plugins.explicit_resource_management,
},
proposals: ProposalOptions::default(),
helper_loader,
plugins,
})
Expand Down
2 changes: 0 additions & 2 deletions crates/oxc_transformer/src/proposals/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod explicit_resource_management;
mod options;

pub use explicit_resource_management::ExplicitResourceManagement;
pub use options::ProposalOptions;
11 changes: 3 additions & 8 deletions crates/oxc_transformer/src/proposals/options.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
#[derive(Debug, Clone, Copy)]
#[expect(clippy::empty_structs_with_brackets)]
#[derive(Debug, Clone, Copy, Default)]
pub struct ProposalOptions {
pub explicit_resource_management: bool,
}

impl Default for ProposalOptions {
fn default() -> Self {
Self { explicit_resource_management: true }
}
// Currently no proposals are configured here.
}
1 change: 1 addition & 0 deletions napi/transform/test/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ describe('target', () => {
['es2019', 'a?.b;\n'],
['es2019', 'a ?? b;\n'],
['es2021', 'class foo {\n\tstatic {}\n}\n'],
['es2025', 'using handlerSync = openSync();\n'],
];

test.each(data)('transform %s', (target, code) => {
Expand Down
14 changes: 14 additions & 0 deletions tasks/compat_data/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -1034,5 +1034,19 @@
"samsung": "27",
"electron": "31.0"
}
},
{
"name": "ExplicitResourceManagement",
"babel": "transform-explicit-resource-management",
"features": [
"Explicit Resource Management"
],
"es": "ES2026",
"targets": {
"chrome": "134",
"edge": "134",
"firefox": "141",
"node": "24"
}
}
]
Loading
Loading