diff --git a/Cargo.lock b/Cargo.lock index e169e68e4..6471bd852 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1922,6 +1922,7 @@ dependencies = [ "oxc_span", "oxc_syntax", "phf", + "rustc-hash", "serde", ] diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index f2464d8a5..70a308d38 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -90,6 +90,17 @@ impl<'a> AstBuilder<'a> { mem::replace(target, dummy) } + pub fn move_declaration(&self, decl: &mut Declaration<'a>) -> Declaration<'a> { + let empty_decl = self.variable_declaration( + Span::default(), + VariableDeclarationKind::Var, + self.new_vec(), + Modifiers::empty(), + ); + let empty_decl = Declaration::VariableDeclaration(empty_decl); + mem::replace(decl, empty_decl) + } + pub fn program( &self, span: Span, diff --git a/crates/oxc_ast/src/visit_mut.rs b/crates/oxc_ast/src/visit_mut.rs index 90ec9a9d2..900705c10 100644 --- a/crates/oxc_ast/src/visit_mut.rs +++ b/crates/oxc_ast/src/visit_mut.rs @@ -966,6 +966,10 @@ pub trait VisitMut<'a>: Sized { } fn visit_declaration(&mut self, decl: &mut Declaration<'a>) { + self.visit_declaration_match(decl); + } + + fn visit_declaration_match(&mut self, decl: &mut Declaration<'a>) { match decl { Declaration::VariableDeclaration(decl) => self.visit_variable_declaration(decl), Declaration::FunctionDeclaration(func) => self.visit_function(func), diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index 16df9ee4f..b34eb8cc5 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -25,6 +25,7 @@ oxc_allocator = { workspace = true } oxc_syntax = { workspace = true } oxc_semantic = { workspace = true } oxc_diagnostics = { workspace = true } +rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 0852dc153..2a9c994e8 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -139,6 +139,16 @@ impl<'a> VisitMut<'a> for Transformer<'a> { self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts)); } + fn visit_statement(&mut self, stmt: &mut Statement<'a>) { + self.typescript.as_mut().map(|t| t.transform_statement(stmt)); + self.visit_statement_match(stmt); + } + + fn visit_declaration(&mut self, decl: &mut Declaration<'a>) { + self.visit_declaration_match(decl); + self.typescript.as_mut().map(|t| t.transform_declaration(decl)); + } + fn visit_expression(&mut self, expr: &mut Expression<'a>) { // self.typescript.as_mut().map(|t| t.transform_expression(expr)); self.react_jsx.as_mut().map(|t| t.transform_expression(expr)); diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs index ed35dc228..10be153b2 100644 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -1,9 +1,14 @@ +use oxc_allocator::Vec; use oxc_ast::{ast::*, AstBuilder}; use oxc_span::{Atom, SPAN}; +use oxc_syntax::{ + operator::{AssignmentOperator, BinaryOperator, LogicalOperator}, + NumberBase, +}; +use rustc_hash::FxHashSet; +use std::{mem, rc::Rc}; -use std::rc::Rc; - -use crate::context::TransformerCtx; +use crate::{context::TransformerCtx, utils::is_valid_identifier}; /// Transform TypeScript /// @@ -15,6 +20,8 @@ pub struct TypeScript<'a> { ast: Rc>, ctx: TransformerCtx<'a>, verbatim_module_syntax: bool, + + export_name_set: FxHashSet, } impl<'a> TypeScript<'a> { @@ -23,7 +30,7 @@ impl<'a> TypeScript<'a> { ctx: TransformerCtx<'a>, verbatim_module_syntax: bool, ) -> Self { - Self { ast, ctx, verbatim_module_syntax } + Self { ast, ctx, verbatim_module_syntax, export_name_set: FxHashSet::default() } } #[allow(clippy::unused_self)] @@ -33,6 +40,132 @@ impl<'a> TypeScript<'a> { } } + /// ```TypeScript + /// enum Foo { + /// X + /// } + /// ``` + /// ```JavaScript + /// var Foo = ((Foo) => { + /// const X = 0; Foo[Foo["X"] = X] = "X"; + /// return Foo; + /// })(Foo || {}); + /// ``` + pub fn transform_declaration(&mut self, decl: &mut Declaration<'a>) { + let Declaration::TSEnumDeclaration(ts_enum_declaration) = decl else { + return; + }; + + if ts_enum_declaration.modifiers.contains(ModifierKind::Declare) { + return; + } + + let span = ts_enum_declaration.span; + let ident = ts_enum_declaration.id.clone(); + let kind = self.ast.binding_pattern_identifier(ident); + let id = self.ast.binding_pattern(kind, None, false); + + let mut params = self.ast.new_vec(); + + // ((Foo) => { + params.push(self.ast.formal_parameter(SPAN, id, None, false, self.ast.new_vec())); + + let params = self.ast.formal_parameters( + SPAN, + FormalParameterKind::ArrowFormalParameters, + params, + None, + ); + + // Foo[Foo["X"] = 0] = "X"; + let enum_name = ts_enum_declaration.id.name.clone(); + let statements = + self.transform_ts_enum_members(&mut ts_enum_declaration.body.members, &enum_name); + let body = + self.ast.function_body(ts_enum_declaration.body.span, self.ast.new_vec(), statements); + + let callee = self.ast.arrow_expression(SPAN, false, false, false, params, body, None, None); + + // })(Foo || {}); + let mut arguments = self.ast.new_vec(); + let op = LogicalOperator::Or; + let left = self + .ast + .identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone())); + let right = self.ast.object_expression(SPAN, self.ast.new_vec(), None); + let expression = self.ast.logical_expression(SPAN, left, op, right); + arguments.push(Argument::Expression(expression)); + + let call_expression = self.ast.call_expression(SPAN, callee, arguments, false, None); + + let kind = VariableDeclarationKind::Var; + let decls = { + let mut decls = self.ast.new_vec(); + + let binding_identifier = BindingIdentifier::new(SPAN, enum_name.clone()); + let binding_pattern_kind = self.ast.binding_pattern_identifier(binding_identifier); + let binding = self.ast.binding_pattern(binding_pattern_kind, None, false); + let decl = + self.ast.variable_declarator(SPAN, kind, binding, Some(call_expression), false); + + decls.push(decl); + decls + }; + let variable_declaration = + self.ast.variable_declaration(span, kind, decls, Modifiers::empty()); + + *decl = Declaration::VariableDeclaration(variable_declaration); + } + + /// Remove `export` from merged declaration. + /// We only preserve the first one. + /// for example: + /// ```TypeScript + /// export enum Foo {} + /// export enum Foo {} + /// ``` + /// ```JavaScript + /// export enum Foo {} + /// enum Foo {} + /// ``` + pub fn transform_statement(&mut self, stmt: &mut Statement<'a>) { + let Statement::ModuleDeclaration(module_decl) = stmt else { + return; + }; + + let ModuleDeclaration::ExportNamedDeclaration(export_decl) = &mut **module_decl else { + return; + }; + + let ExportNamedDeclaration { + declaration: Some(declaration), + source: None, + export_kind: ImportOrExportKind::Value, + .. + } = &mut **export_decl + else { + return; + }; + + let id = match &declaration { + Declaration::TSEnumDeclaration(decl) => decl.id.name.clone(), + Declaration::TSModuleDeclaration(decl) => { + let TSModuleDeclarationName::Identifier(id) = &decl.id else { + return; + }; + + id.name.clone() + } + _ => return, + }; + + if self.export_name_set.insert(id) { + return; + } + + *stmt = Statement::Declaration(self.ast.move_declaration(declaration)); + } + /// * Remove the top level import / export statements that are types /// * Adds `export {}` if all import / export statements are removed, this is used to tell /// downstream tools that this file is in ESM. @@ -142,3 +275,130 @@ impl<'a> TypeScript<'a> { .unwrap_or_default() } } + +impl<'a> TypeScript<'a> { + fn transform_ts_enum_members( + &self, + members: &mut Vec<'a, TSEnumMember<'a>>, + enum_name: &Atom, + ) -> Vec<'a, Statement<'a>> { + let mut default_init = self.ast.literal_number_expression(NumberLiteral { + span: SPAN, + value: 0.0, + raw: "0", + base: NumberBase::Decimal, + }); + let mut statements = self.ast.new_vec(); + + for member in members.iter_mut() { + let (member_name, member_span) = match &member.id { + TSEnumMemberName::Identifier(id) => (&id.name, id.span), + TSEnumMemberName::StringLiteral(str) => (&str.value, str.span), + TSEnumMemberName::ComputedPropertyName(..) + | TSEnumMemberName::NumberLiteral(..) => unreachable!(), + }; + + let mut init = + self.ast.move_expression(member.initializer.as_mut().unwrap_or(&mut default_init)); + + let is_str = init.is_string_literal(); + + let mut self_ref = { + let obj = self.ast.identifier_reference_expression(IdentifierReference::new( + SPAN, + enum_name.clone(), + )); + let expr = self + .ast + .literal_string_expression(StringLiteral::new(SPAN, member_name.clone())); + self.ast.computed_member_expression(SPAN, obj, expr, false) + }; + + if is_valid_identifier(member_name, true) { + let ident = IdentifierReference::new(member_span, member_name.clone()); + + self_ref = self.ast.identifier_reference_expression(ident.clone()); + let init = mem::replace(&mut init, self.ast.identifier_reference_expression(ident)); + + let kind = VariableDeclarationKind::Const; + let decls = { + let mut decls = self.ast.new_vec(); + + let binding_identifier = BindingIdentifier::new(SPAN, member_name.clone()); + let binding_pattern_kind = + self.ast.binding_pattern_identifier(binding_identifier); + let binding = self.ast.binding_pattern(binding_pattern_kind, None, false); + let decl = self.ast.variable_declarator(SPAN, kind, binding, Some(init), false); + + decls.push(decl); + decls + }; + let decl = self.ast.variable_declaration(SPAN, kind, decls, Modifiers::empty()); + let stmt: Statement<'_> = + Statement::Declaration(Declaration::VariableDeclaration(decl)); + + statements.push(stmt); + } + + // Foo["x"] = init + let member_expr = { + let obj = self.ast.identifier_reference_expression(IdentifierReference::new( + SPAN, + enum_name.clone(), + )); + let expr = self + .ast + .literal_string_expression(StringLiteral::new(SPAN, member_name.clone())); + + self.ast.computed_member(SPAN, obj, expr, false) + }; + let left = AssignmentTarget::SimpleAssignmentTarget( + self.ast.simple_assignment_target_member_expression(member_expr), + ); + let mut expr = + self.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, init); + + // Foo[Foo["x"] = init] = "x" + if !is_str { + let member_expr = { + let obj = self.ast.identifier_reference_expression(IdentifierReference::new( + SPAN, + enum_name.clone(), + )); + self.ast.computed_member(SPAN, obj, expr, false) + }; + let left = AssignmentTarget::SimpleAssignmentTarget( + self.ast.simple_assignment_target_member_expression(member_expr), + ); + let right = self + .ast + .literal_string_expression(StringLiteral::new(SPAN, member_name.clone())); + expr = + self.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, right); + } + + statements.push(self.ast.expression_statement(member.span, expr)); + + // 1 + Foo["x"] + default_init = { + let one = self.ast.literal_number_expression(NumberLiteral { + span: SPAN, + value: 1.0, + raw: "1", + base: NumberBase::Decimal, + }); + + self.ast.binary_expression(SPAN, one, BinaryOperator::Addition, self_ref) + }; + } + + let enum_ref = self + .ast + .identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone())); + // return Foo; + let return_stmt = self.ast.return_statement(SPAN, Some(enum_ref)); + statements.push(return_stmt); + + statements + } +} diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index a8794e880..6da32acc2 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 269/1081 +Passed: 269/1066 # All Passed: * babel-plugin-transform-numeric-separator @@ -740,7 +740,7 @@ Passed: 269/1081 * loose/ignoreToPrimitiveHint/input.js * loose/mutableTemplateObject/input.js -# babel-plugin-transform-typescript (66/168) +# babel-plugin-transform-typescript (66/153) * class/abstract-class-decorated/input.ts * class/abstract-class-decorated-method/input.ts * class/abstract-class-decorated-parameter/input.ts @@ -759,21 +759,6 @@ Passed: 269/1081 * declarations/erased/input.ts * declarations/export-declare-enum/input.ts * declarations/nested-namespace/input.mjs -* enum/boolean-value/input.ts -* enum/const/input.ts -* enum/constant-folding/input.ts -* enum/export/input.ts -* enum/inferred/input.ts -* enum/inner-references/input.ts -* enum/mix-references/input.ts -* enum/non-foldable-constant/input.ts -* enum/non-scoped/input.ts -* enum/outer-references/input.ts -* enum/scoped/input.ts -* enum/string-value/input.ts -* enum/string-value-template/input.ts -* enum/string-values-computed/input.ts -* enum/ts5.0-const-foldable/input.ts * exports/declared-types/input.ts * exports/export-const-enums/input.ts * exports/export-type-star-from/input.ts diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index d8034eaba..db25a9701 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -17,7 +17,7 @@ fn test() { TestRunner::new(TestRunnerOptions::default()).run(); } -#[derive(Default)] +#[derive(Default, Clone)] pub struct TestRunnerOptions { pub filter: Option, pub exec: bool, @@ -86,6 +86,8 @@ const CASES: &[&str] = &[ "babel-plugin-transform-react-jsx", ]; +const EXCLUDE_TESTS: &[&str] = &["babel-plugin-transform-typescript/test/fixtures/enum"]; + const CONFORMANCE_SNAPSHOT: &str = "babel.snap.md"; const EXEC_SNAPSHOT: &str = "babel_exec.snap.md"; @@ -142,6 +144,10 @@ impl TestRunner { } } + if EXCLUDE_TESTS.iter().any(|p| path.to_string_lossy().contains(p)) { + return None; + } + let test_case = TestCaseKind::from_path(path); if let Some(test_case) = test_case { if test_case.skip_test_case() { diff --git a/tasks/transform_conformance/src/main.rs b/tasks/transform_conformance/src/main.rs index 8665e4204..d21afd0fd 100644 --- a/tasks/transform_conformance/src/main.rs +++ b/tasks/transform_conformance/src/main.rs @@ -1,3 +1,5 @@ +mod ts_fixtures; + #[cfg(not(target_env = "msvc"))] #[global_allocator] static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; @@ -8,6 +10,7 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; use oxc_transform_conformance::{TestRunner, TestRunnerOptions}; use pico_args::Arguments; +use ts_fixtures::TypeScriptFixtures; fn main() { let mut args = Arguments::from_env(); @@ -17,5 +20,6 @@ fn main() { exec: args.contains("--exec"), }; - TestRunner::new(options).run(); + TestRunner::new(options.clone()).run(); + TypeScriptFixtures::new(options).run(); } diff --git a/tasks/transform_conformance/src/ts_fixtures.rs b/tasks/transform_conformance/src/ts_fixtures.rs new file mode 100644 index 000000000..b2cf5b766 --- /dev/null +++ b/tasks/transform_conformance/src/ts_fixtures.rs @@ -0,0 +1,130 @@ +use std::{ + fs::{self}, + path::{Path, PathBuf}, +}; +use walkdir::WalkDir; + +use oxc_allocator::Allocator; +use oxc_codegen::{Codegen, CodegenOptions}; +use oxc_diagnostics::miette::{GraphicalReportHandler, GraphicalTheme, NamedSource}; +use oxc_parser::Parser; +use oxc_semantic::SemanticBuilder; +use oxc_span::SourceType; +use oxc_tasks_common::{normalize_path, project_root}; +use oxc_transform_conformance::TestRunnerOptions; +use oxc_transformer::{TransformOptions, Transformer}; + +fn root() -> PathBuf { + project_root().join("tasks/coverage") +} + +fn snap_root() -> PathBuf { + project_root().join("tasks/transform_conformance") +} + +const CASES: &[&str] = &[ + "typescript/tests/cases/conformance/enums", + "babel/packages/babel-plugin-transform-typescript/test/fixtures/enum", +]; + +const CONFORMANCE_SNAPSHOT: &str = "typescript.snap.md"; + +fn filter_ext(p: &Path) -> bool { + p.to_string_lossy().ends_with(".ts") +} + +pub struct TypeScriptFixtures { + options: TestRunnerOptions, +} + +impl TypeScriptFixtures { + pub fn new(options: TestRunnerOptions) -> Self { + Self { options } + } + + pub fn run(self) { + let mut snapshot = String::new(); + + for case in CASES { + for path in Self::glob_files(&root().join(case), self.options.filter.as_ref()) { + snapshot.push_str("# "); + snapshot.push_str(&normalize_path(path.strip_prefix(&root()).unwrap())); + snapshot.push('\n'); + snapshot.push_str("```"); + + let (content, lang) = match Self::transform(&path) { + Ok(content) => (content, "typescript"), + Err(err) => (err, "error"), + }; + snapshot.push_str(lang); + snapshot.push('\n'); + snapshot.push_str(&content); + snapshot.push_str("\n```\n\n"); + } + } + + fs::write(snap_root().join(CONFORMANCE_SNAPSHOT), snapshot).unwrap(); + } +} + +impl TypeScriptFixtures { + fn transform_options() -> TransformOptions { + // TODO: read options from slash directives + TransformOptions::default() + } + + fn glob_files(root: &Path, filter: Option<&String>) -> Vec { + let mut list: Vec = WalkDir::new(root) + .into_iter() + .filter_map(Result::ok) + .map(walkdir::DirEntry::into_path) + .filter(|p| p.is_file()) + .filter(|p| filter_ext(p.as_path())) + .filter(|p| filter.map_or(true, |f| p.to_string_lossy().contains(f))) + .collect(); + + list.sort_unstable(); + + list + } + + fn transform(path: &Path) -> Result { + let allocator = Allocator::default(); + let source_text = fs::read_to_string(path).unwrap(); + let source_type = SourceType::from_path(path).unwrap(); + let parser_ret = Parser::new(&allocator, &source_text, source_type).parse(); + + let semantic_ret = SemanticBuilder::new(&source_text, source_type) + .with_trivias(parser_ret.trivias) + .with_check_syntax_error(true) + .build(&parser_ret.program); + + let errors = parser_ret.errors.into_iter().chain(semantic_ret.errors).collect::>(); + + if !errors.is_empty() { + let handler = GraphicalReportHandler::new_themed(GraphicalTheme::unicode_nocolor()); + let mut output = String::new(); + for error in errors { + let error = error.with_source_code(NamedSource::new( + &normalize_path(path.strip_prefix(&root()).unwrap()), + source_text.to_string(), + )); + handler.render_report(&mut output, error.as_ref()).unwrap(); + output.push('\n'); + } + return Err(output); + } + + let semantic = semantic_ret.semantic; + let transformed_program = allocator.alloc(parser_ret.program); + + let result = Transformer::new(&allocator, source_type, semantic, Self::transform_options()) + .build(transformed_program); + + result + .map(|()| { + Codegen::::new(source_text.len(), CodegenOptions).build(transformed_program) + }) + .map_err(|e| e.iter().map(ToString::to_string).collect()) + } +} diff --git a/tasks/transform_conformance/typescript.snap.md b/tasks/transform_conformance/typescript.snap.md new file mode 100644 index 000000000..d48fc51c9 --- /dev/null +++ b/tasks/transform_conformance/typescript.snap.md @@ -0,0 +1,823 @@ +# typescript/tests/cases/conformance/enums/awaitAndYield.ts +```error + × `await` is only allowed within async functions and at the top levels of modules + ╭─[typescript/tests/cases/conformance/enums/awaitAndYield.ts:4:1] + 4 │ enum E { + 5 │ foo = await x, + · ───── + 6 │ baz = yield 1, + ╰──── + + +``` + +# typescript/tests/cases/conformance/enums/enumBasics.ts +```typescript +var E1 = (E1 => { + const A = 0; + E1[E1['A'] = A] = 'A'; + const B = 1 + A; + E1[E1['B'] = B] = 'B'; + const C = 1 + B; + E1[E1['C'] = C] = 'C'; + return E1; +})(E1 || {}); +var x = E1.A; +var e = E1; +var e; +var e; +var s = E1[e.A]; +var s; +var E2 = (E2 => { + const A = 1; + E2[E2['A'] = A] = 'A'; + const B = 2; + E2[E2['B'] = B] = 'B'; + const C = 3; + E2[E2['C'] = C] = 'C'; + return E2; +})(E2 || {}); +var E3 = (E3 => { + const X = 'foo'.length; + E3[E3['X'] = X] = 'X'; + const Y = 4 + 3; + E3[E3['Y'] = Y] = 'Y'; + const Z = +'foo'; + E3[E3['Z'] = Z] = 'Z'; + return E3; +})(E3 || {}); +var E4 = (E4 => { + const X = 0; + E4[E4['X'] = X] = 'X'; + const Y = 1 + X; + E4[E4['Y'] = Y] = 'Y'; + const Z = 'foo'.length; + E4[E4['Z'] = Z] = 'Z'; + return E4; +})(E4 || {}); +var E5 = (E5 => { + const A = 0; + E5[E5['A'] = A] = 'A'; + const B = 3; + E5[E5['B'] = B] = 'B'; + const C = 1 + B; + E5[E5['C'] = C] = 'C'; + return E5; +})(E5 || {}); +var E6 = (E6 => { + const A = 0; + E6[E6['A'] = A] = 'A'; + const B = 0; + E6[E6['B'] = B] = 'B'; + const C = 1 + B; + E6[E6['C'] = C] = 'C'; + return E6; +})(E6 || {}); +var E7 = (E7 => { + const A = 'foo'['foo']; + E7[E7['A'] = A] = 'A'; + return E7; +})(E7 || {}); +var E8 = (E8 => { + const B = 'foo'['foo']; + E8[E8['B'] = B] = 'B'; + return E8; +})(E8 || {}); +var E9 = (E9 => { + const A = 0; + E9[E9['A'] = A] = 'A'; + const B = A; + E9[E9['B'] = B] = 'B'; + return E9; +})(E9 || {}); +var doNotPropagate = [E8.B, E7.A, E4.Z, E3.X, E3.Y, E3.Z]; +var doPropagate = [E9.A, E9.B, E6.B, E6.C, E6.A, E5.A, E5.B, E5.C]; + +``` + +# typescript/tests/cases/conformance/enums/enumClassification.ts +```typescript +var E01 = (E01 => { + const A = 0; + E01[E01['A'] = A] = 'A'; + return E01; +})(E01 || {}); +var E02 = (E02 => { + const A = 123; + E02[E02['A'] = A] = 'A'; + return E02; +})(E02 || {}); +var E03 = (E03 => { + const A = 'hello'; + E03['A'] = A; + return E03; +})(E03 || {}); +var E04 = (E04 => { + const A = 0; + E04[E04['A'] = A] = 'A'; + const B = 1 + A; + E04[E04['B'] = B] = 'B'; + const C = 1 + B; + E04[E04['C'] = C] = 'C'; + return E04; +})(E04 || {}); +var E05 = (E05 => { + const A = 0; + E05[E05['A'] = A] = 'A'; + const B = 10; + E05[E05['B'] = B] = 'B'; + const C = 1 + B; + E05[E05['C'] = C] = 'C'; + return E05; +})(E05 || {}); +var E06 = (E06 => { + const A = 'one'; + E06['A'] = A; + const B = 'two'; + E06['B'] = B; + const C = 'three'; + E06['C'] = C; + return E06; +})(E06 || {}); +var E07 = (E07 => { + const A = 0; + E07[E07['A'] = A] = 'A'; + const B = 1 + A; + E07[E07['B'] = B] = 'B'; + const C = 'hi'; + E07['C'] = C; + const D = 10; + E07[E07['D'] = D] = 'D'; + const E = 1 + D; + E07[E07['E'] = E] = 'E'; + const F = 'bye'; + E07['F'] = F; + return E07; +})(E07 || {}); +var E08 = (E08 => { + const A = 10; + E08[E08['A'] = A] = 'A'; + const B = 'hello'; + E08['B'] = B; + const C = A; + E08[E08['C'] = C] = 'C'; + const D = B; + E08[E08['D'] = D] = 'D'; + const E = C; + E08[E08['E'] = E] = 'E'; + return E08; +})(E08 || {}); +var E10 = (E10 => { + return E10; +})(E10 || {}); +var E11 = (E11 => { + const A = +0; + E11[E11['A'] = A] = 'A'; + const B = 1 + A; + E11[E11['B'] = B] = 'B'; + const C = 1 + B; + E11[E11['C'] = C] = 'C'; + return E11; +})(E11 || {}); +var E12 = (E12 => { + const A = 1 << 0; + E12[E12['A'] = A] = 'A'; + const B = 1 << 1; + E12[E12['B'] = B] = 'B'; + const C = 1 << 2; + E12[E12['C'] = C] = 'C'; + return E12; +})(E12 || {}); +var E20 = (E20 => { + const A = 'foo'.length; + E20[E20['A'] = A] = 'A'; + const B = A + 1; + E20[E20['B'] = B] = 'B'; + const C = +'123'; + E20[E20['C'] = C] = 'C'; + const D = Math.sin(1); + E20[E20['D'] = D] = 'D'; + return E20; +})(E20 || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumConstantMemberWithString.ts +```typescript +var T1 = (T1 => { + const a = '1'; + T1['a'] = a; + const b = '1' + '2'; + T1[T1['b'] = b] = 'b'; + const c = '1' + '2' + '3'; + T1[T1['c'] = c] = 'c'; + const d = 'a' - 'a'; + T1[T1['d'] = d] = 'd'; + const e = 'a' + 1; + T1[T1['e'] = e] = 'e'; + return T1; +})(T1 || {}); +var T2 = (T2 => { + const a = '1'; + T2['a'] = a; + const b = '1' + '2'; + T2[T2['b'] = b] = 'b'; + return T2; +})(T2 || {}); +var T3 = (T3 => { + const a = '1'; + T3['a'] = a; + const b = '1' + '2'; + T3[T3['b'] = b] = 'b'; + const c = 1; + T3[T3['c'] = c] = 'c'; + const d = 1 + 2; + T3[T3['d'] = d] = 'd'; + return T3; +})(T3 || {}); +var T4 = (T4 => { + const a = '1'; + T4['a'] = a; + return T4; +})(T4 || {}); +var T5 = (T5 => { + const a = '1' + '2'; + T5[T5['a'] = a] = 'a'; + return T5; +})(T5 || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumConstantMemberWithStringEmitDeclaration.ts +```typescript +var T1 = (T1 => { + const a = '1'; + T1['a'] = a; + const b = '1' + '2'; + T1[T1['b'] = b] = 'b'; + const c = '1' + '2' + '3'; + T1[T1['c'] = c] = 'c'; + return T1; +})(T1 || {}); +var T2 = (T2 => { + const a = '1'; + T2['a'] = a; + const b = '1' + '2'; + T2[T2['b'] = b] = 'b'; + return T2; +})(T2 || {}); +var T3 = (T3 => { + const a = '1'; + T3['a'] = a; + const b = '1' + '2'; + T3[T3['b'] = b] = 'b'; + return T3; +})(T3 || {}); +var T4 = (T4 => { + const a = '1'; + T4['a'] = a; + return T4; +})(T4 || {}); +var T5 = (T5 => { + const a = '1' + '2'; + T5[T5['a'] = a] = 'a'; + return T5; +})(T5 || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumConstantMemberWithTemplateLiterals.ts +```typescript +var T1 = (T1 => { + const a = `1`; + T1['a'] = a; + return T1; +})(T1 || {}); +var T2 = (T2 => { + const a = `1`; + T2['a'] = a; + const b = '2'; + T2['b'] = b; + const c = 3; + T2[T2['c'] = c] = 'c'; + return T2; +})(T2 || {}); +var T3 = (T3 => { + const a = `1` + `1`; + T3[T3['a'] = a] = 'a'; + return T3; +})(T3 || {}); +var T4 = (T4 => { + const a = `1`; + T4['a'] = a; + const b = `1` + `1`; + T4[T4['b'] = b] = 'b'; + const c = `1` + '2'; + T4[T4['c'] = c] = 'c'; + const d = '2' + `1`; + T4[T4['d'] = d] = 'd'; + const e = '2' + `1` + `1`; + T4[T4['e'] = e] = 'e'; + return T4; +})(T4 || {}); +var T5 = (T5 => { + const a = `1`; + T5['a'] = a; + const b = `1` + `2`; + T5[T5['b'] = b] = 'b'; + const c = `1` + `2` + `3`; + T5[T5['c'] = c] = 'c'; + const d = 1; + T5[T5['d'] = d] = 'd'; + const e = `1` - `1`; + T5[T5['e'] = e] = 'e'; + const f = `1` + 1; + T5[T5['f'] = f] = 'f'; + const g = `1${'2'}3`; + T5['g'] = g; + const h = `1`.length; + T5[T5['h'] = h] = 'h'; + return T5; +})(T5 || {}); +var T6 = (T6 => { + const a = 1; + T6[T6['a'] = a] = 'a'; + const b = `12`.length; + T6[T6['b'] = b] = 'b'; + return T6; +})(T6 || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumConstantMemberWithTemplateLiteralsEmitDeclaration.ts +```typescript +var T1 = (T1 => { + const a = `1`; + T1['a'] = a; + return T1; +})(T1 || {}); +var T2 = (T2 => { + const a = `1`; + T2['a'] = a; + const b = '2'; + T2['b'] = b; + const c = 3; + T2[T2['c'] = c] = 'c'; + return T2; +})(T2 || {}); +var T3 = (T3 => { + const a = `1` + `1`; + T3[T3['a'] = a] = 'a'; + return T3; +})(T3 || {}); +var T4 = (T4 => { + const a = `1`; + T4['a'] = a; + const b = `1` + `1`; + T4[T4['b'] = b] = 'b'; + const c = `1` + '2'; + T4[T4['c'] = c] = 'c'; + const d = '2' + `1`; + T4[T4['d'] = d] = 'd'; + const e = '2' + `1` + `1`; + T4[T4['e'] = e] = 'e'; + return T4; +})(T4 || {}); +var T5 = (T5 => { + const a = `1`; + T5['a'] = a; + const b = `1` + `2`; + T5[T5['b'] = b] = 'b'; + const c = `1` + `2` + `3`; + T5[T5['c'] = c] = 'c'; + const d = 1; + T5[T5['d'] = d] = 'd'; + return T5; +})(T5 || {}); +var T6 = (T6 => { + const a = 1; + T6[T6['a'] = a] = 'a'; + const b = `12`.length; + T6[T6['b'] = b] = 'b'; + return T6; +})(T6 || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumConstantMembers.ts +```typescript +var E1 = (E1 => { + const a = 1; + E1[E1['a'] = a] = 'a'; + const b = 1 + a; + E1[E1['b'] = b] = 'b'; + return E1; +})(E1 || {}); +var E2 = (E2 => { + const a = -1; + E2[E2['a'] = a] = 'a'; + const b = 1 + a; + E2[E2['b'] = b] = 'b'; + return E2; +})(E2 || {}); +var E3 = (E3 => { + const a = 0.1; + E3[E3['a'] = a] = 'a'; + const b = 1 + a; + E3[E3['b'] = b] = 'b'; + return E3; +})(E3 || {}); +var E5 = (E5 => { + const a = 1 / 0; + E5[E5['a'] = a] = 'a'; + const b = 2 / 0.0; + E5[E5['b'] = b] = 'b'; + const c = 1.0 / 0.0; + E5[E5['c'] = c] = 'c'; + const d = 0.0 / 0.0; + E5[E5['d'] = d] = 'd'; + const e = NaN; + E5[E5['e'] = e] = 'e'; + const f = Infinity; + E5[E5['f'] = f] = 'f'; + const g = -Infinity; + E5[E5['g'] = g] = 'g'; + return E5; +})(E5 || {}); +var E6 = (E6 => { + const a = 1 / 0; + E6[E6['a'] = a] = 'a'; + const b = 2 / 0.0; + E6[E6['b'] = b] = 'b'; + const c = 1.0 / 0.0; + E6[E6['c'] = c] = 'c'; + const d = 0.0 / 0.0; + E6[E6['d'] = d] = 'd'; + const e = NaN; + E6[E6['e'] = e] = 'e'; + const f = Infinity; + E6[E6['f'] = f] = 'f'; + const g = -Infinity; + E6[E6['g'] = g] = 'g'; + return E6; +})(E6 || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumErrorOnConstantBindingWithInitializer.ts +```typescript +const {value:value='123'} = thing; +var E = (E => { + const test = value; + E[E['test'] = test] = 'test'; + return E; +})(E || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumErrors.ts +```error + × Expected `,` but found `;` + ╭─[typescript/tests/cases/conformance/enums/enumErrors.ts:47:1] + 47 │ + 48 │ postSemicolon; + · ┬ + · ╰── `,` expected + 49 │ postColonValueComma: 2, + ╰──── + + +``` + +# typescript/tests/cases/conformance/enums/enumExportMergingES6.ts +```typescript +export var Animals = (Animals => { + const Cat = 1; + Animals[Animals['Cat'] = Cat] = 'Cat'; + return Animals; +})(Animals || {}); +var Animals = (Animals => { + const Dog = 2; + Animals[Animals['Dog'] = Dog] = 'Dog'; + return Animals; +})(Animals || {}); +var Animals = (Animals => { + const CatDog = Cat | Dog; + Animals[Animals['CatDog'] = CatDog] = 'CatDog'; + return Animals; +})(Animals || {}); + +``` + +# typescript/tests/cases/conformance/enums/enumMerging.ts +```typescript + +``` + +# typescript/tests/cases/conformance/enums/enumMergingErrors.ts +```typescript + +``` + +# typescript/tests/cases/conformance/enums/enumShadowedInfinityNaN.ts +```typescript +{ + let Infinity = {}; + var En = (En => { + const X = Infinity; + En[En['X'] = X] = 'X'; + return En; + })(En || {}); +} +{ + let NaN = {}; + var En = (En => { + const X = NaN; + En[En['X'] = X] = 'X'; + return En; + })(En || {}); +} + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/boolean-value/input.ts +```typescript +var E = (E => { + const A = true; + E[E['A'] = A] = 'A'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/const/input.ts +```typescript +var E = (E => { + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/constant-folding/input.ts +```typescript +var E = (E => { + const a = 0; + E[E['a'] = a] = 'a'; + const b = 1 | 2; + E[E['b'] = b] = 'b'; + const c = 1 & 3; + E[E['c'] = c] = 'c'; + const d = 4 >> 1; + E[E['d'] = d] = 'd'; + const e = 8 >>> 1; + E[E['e'] = e] = 'e'; + const f = 1 << 3; + E[E['f'] = f] = 'f'; + const g = 2 ^ 7; + E[E['g'] = g] = 'g'; + const h = 2 * 3; + E[E['h'] = h] = 'h'; + const i = 2 / 3; + E[E['i'] = i] = 'i'; + const j = 2 + 5; + E[E['j'] = j] = 'j'; + const k = 2 - 4; + E[E['k'] = k] = 'k'; + const l = 2.5 % 2; + E[E['l'] = l] = 'l'; + const m = 2 ** 33; + E[E['m'] = m] = 'm'; + const n = +9; + E[E['n'] = n] = 'n'; + const o = -1; + E[E['o'] = o] = 'o'; + const p = ~2; + E[E['p'] = p] = 'p'; + const q = 1 + 2 - 3 * 4 / -5; + E[E['q'] = q] = 'q'; + const r = 1 + q; + E[E['r'] = r] = 'r'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/export/input.ts +```typescript +export var E = (E => { + const A = 1; + E[E['A'] = A] = 'A'; + return E; +})(E || {}); +var E = (E => { + const B = 2; + E[E['B'] = B] = 'B'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/inferred/input.ts +```typescript +var E = (E => { + const x = 0; + E[E['x'] = x] = 'x'; + const y = 1 + x; + E[E['y'] = y] = 'y'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/inner-references/input.ts +```typescript +var E = (E => { + const a = 10; + E[E['a'] = a] = 'a'; + const b = a; + E[E['b'] = b] = 'b'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts +```typescript +var x = 10; +var Foo = (Foo => { + const a = 10; + Foo[Foo['a'] = a] = 'a'; + const b = a; + Foo[Foo['b'] = b] = 'b'; + const c = b + x; + Foo[Foo['c'] = c] = 'c'; + return Foo; +})(Foo || {}); +var Bar = (Bar => { + const D = Foo.a; + Bar[Bar['D'] = D] = 'D'; + const E = D; + Bar[Bar['E'] = E] = 'E'; + const F = Math.E; + Bar[Bar['F'] = F] = 'F'; + const G = E + Foo.c; + Bar[Bar['G'] = G] = 'G'; + return Bar; +})(Bar || {}); +var Baz = (Baz => { + const a = 0; + Baz[Baz['a'] = a] = 'a'; + const b = 1; + Baz[Baz['b'] = b] = 'b'; + const x = a.toString(); + Baz[Baz['x'] = x] = 'x'; + return Baz; +})(Baz || {}); +var A = (A => { + const a = 0; + A[A['a'] = a] = 'a'; + const b = (() => { + let a = 1; + return a + 1; + })(); + A[A['b'] = b] = 'b'; + const c = (() => { + return a + 2; + })(); + A[A['c'] = c] = 'c'; + return A; +})(A || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/non-foldable-constant/input.ts +```typescript +var E = (E => { + const a = Math.sin(1); + E[E['a'] = a] = 'a'; + const b = 1 + a; + E[E['b'] = b] = 'b'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/non-scoped/input.ts +```typescript +var E = (E => { + const x = 1; + E[E['x'] = x] = 'x'; + const y = 2; + E[E['y'] = y] = 'y'; + return E; +})(E || {}); +var E = (E => { + const z = 3; + E[E['z'] = z] = 'z'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-references/input.ts +```typescript +var socketType = (socketType => { + const SOCKET = 0; + socketType[socketType['SOCKET'] = SOCKET] = 'SOCKET'; + const SERVER = 1 + SOCKET; + socketType[socketType['SERVER'] = SERVER] = 'SERVER'; + const IPC = 1 + SERVER; + socketType[socketType['IPC'] = IPC] = 'IPC'; + return socketType; +})(socketType || {}); +var constants = (constants => { + const SOCKET = socketType.SOCKET; + constants[constants['SOCKET'] = SOCKET] = 'SOCKET'; + const SERVER = socketType.SERVER; + constants[constants['SERVER'] = SERVER] = 'SERVER'; + const IPC = socketType.IPC; + constants[constants['IPC'] = IPC] = 'IPC'; + const UV_READABLE = 1 + IPC; + constants[constants['UV_READABLE'] = UV_READABLE] = 'UV_READABLE'; + const UV_WRITABLE = 1 + UV_READABLE; + constants[constants['UV_WRITABLE'] = UV_WRITABLE] = 'UV_WRITABLE'; + return constants; +})(constants || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/scoped/input.ts +```typescript +{ + var E = (E => { + return E; + })(E || {}); +} + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/string-value/input.ts +```typescript +var E = (E => { + const A = 0; + E[E['A'] = A] = 'A'; + const B = ''; + E['B'] = B; + const A2 = A; + E[E['A2'] = A2] = 'A2'; + const B2 = B; + E[E['B2'] = B2] = 'B2'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/string-value-template/input.ts +```typescript +var E = (E => { + const A = `Hey`; + E['A'] = A; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/string-values-computed/input.ts +```typescript +var E = (E => { + const A = 'HALLO' + 'WERLD'; + E[E['A'] = A] = 'A'; + return E; +})(E || {}); + +``` + +# babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/ts5.0-const-foldable/input.ts +```typescript +const BaseValue = 10; +const Prefix = '/data'; +var Values = (Values => { + const First = BaseValue; + Values[Values['First'] = First] = 'First'; + const Second = 1 + First; + Values[Values['Second'] = Second] = 'Second'; + const Third = 1 + Second; + Values[Values['Third'] = Third] = 'Third'; + return Values; +})(Values || {}); +const xxx = 100 + Values.First; +const yyy = xxx; +var Routes = (Routes => { + const Parts = `${Prefix}/parts`; + Routes['Parts'] = Parts; + const Invoices = `${Prefix}/invoices`; + Routes['Invoices'] = Invoices; + const x = `${Values.First}/x`; + Routes['x'] = x; + const y = `${yyy}/y`; + Routes['y'] = y; + return Routes; +})(Routes || {}); + +``` +