From af1a76bafac2c3cbdb7f669db2f709e27e21d620 Mon Sep 17 00:00:00 2001 From: Boshen Date: Tue, 24 Oct 2023 17:30:01 +0800 Subject: [PATCH] feat(transformer): implement some of needs_explicit_esm for typescript (#1047) Co-authored-by: magic-akari --- crates/oxc_ast/src/ast/js.rs | 6 +- crates/oxc_ast/src/ast_builder.rs | 2 +- .../syntax_directed_operations/bound_names.rs | 20 ++- crates/oxc_ast/src/visit.rs | 6 +- crates/oxc_ast/src/visit_mut.rs | 6 +- crates/oxc_codegen/src/gen.rs | 122 ++++++++-------- crates/oxc_formatter/src/gen.rs | 134 +++++++++--------- crates/oxc_linter/src/utils/jest.rs | 13 +- crates/oxc_parser/src/js/module.rs | 6 +- crates/oxc_query/src/edges.rs | 2 + .../oxc_semantic/src/module_record/builder.rs | 48 ++++--- .../oxc_transformer/examples/transformer.rs | 4 +- crates/oxc_transformer/src/lib.rs | 9 ++ crates/oxc_transformer/src/typescript/mod.rs | 79 ++++++++++- tasks/transform_conformance/babel.snap.md | 19 +-- 15 files changed, 287 insertions(+), 189 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 321f0aca5..c0d12ce29 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1864,7 +1864,8 @@ pub struct ImportExpression<'a> { pub struct ImportDeclaration<'a> { #[cfg_attr(feature = "serde", serde(flatten))] pub span: Span, - pub specifiers: Vec<'a, ImportDeclarationSpecifier>, + /// `None` for `import 'foo'`, `Some([])` for `import {} from 'foo'` + pub specifiers: Option>, pub source: StringLiteral, pub assertions: Option>, // Some(vec![]) for empty assertion pub import_kind: ImportOrExportKind, // `import type { foo } from 'bar'` @@ -1885,12 +1886,13 @@ pub enum ImportDeclarationSpecifier { // import {imported} from "source" // import {imported as local} from "source" #[derive(Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] +#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))] pub struct ImportSpecifier { #[cfg_attr(feature = "serde", serde(flatten))] pub span: Span, pub imported: ModuleExportName, pub local: BindingIdentifier, + pub import_kind: ImportOrExportKind, } // import local from "source" diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index 23c126351..1b10a1d50 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -975,7 +975,7 @@ impl<'a> AstBuilder<'a> { pub fn import_declaration( &self, span: Span, - specifiers: Vec<'a, ImportDeclarationSpecifier>, + specifiers: Option>, source: StringLiteral, assertions: Option>, import_kind: ImportOrExportKind, diff --git a/crates/oxc_ast/src/syntax_directed_operations/bound_names.rs b/crates/oxc_ast/src/syntax_directed_operations/bound_names.rs index 26a04717b..504918d43 100644 --- a/crates/oxc_ast/src/syntax_directed_operations/bound_names.rs +++ b/crates/oxc_ast/src/syntax_directed_operations/bound_names.rs @@ -142,11 +142,21 @@ impl<'a> BoundNames for ModuleDeclaration<'a> { impl<'a> BoundNames for ImportDeclaration<'a> { fn bound_names(&self, f: &mut F) { - self.specifiers.iter().for_each(|specifier| match specifier { - ImportDeclarationSpecifier::ImportSpecifier(specifier) => f(&specifier.local), - ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => f(&specifier.local), - ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => f(&specifier.local), - }); + if let Some(specifiers) = &self.specifiers { + for specifier in specifiers { + match specifier { + ImportDeclarationSpecifier::ImportSpecifier(specifier) => { + f(&specifier.local); + } + ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => { + f(&specifier.local); + } + ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => { + f(&specifier.local); + } + } + } + } } } diff --git a/crates/oxc_ast/src/visit.rs b/crates/oxc_ast/src/visit.rs index af82e9e68..41792c88e 100644 --- a/crates/oxc_ast/src/visit.rs +++ b/crates/oxc_ast/src/visit.rs @@ -1279,8 +1279,10 @@ pub trait Visit<'a>: Sized { } fn visit_import_declaration(&mut self, decl: &ImportDeclaration<'a>) { - for specifier in &decl.specifiers { - self.visit_import_declaration_specifier(specifier); + if let Some(specifiers) = &decl.specifiers { + for specifier in specifiers { + self.visit_import_declaration_specifier(specifier); + } } // TODO: source // TODO: assertions diff --git a/crates/oxc_ast/src/visit_mut.rs b/crates/oxc_ast/src/visit_mut.rs index 921a571c5..90ec9a9d2 100644 --- a/crates/oxc_ast/src/visit_mut.rs +++ b/crates/oxc_ast/src/visit_mut.rs @@ -897,8 +897,10 @@ pub trait VisitMut<'a>: Sized { } fn visit_import_declaration(&mut self, decl: &mut ImportDeclaration<'a>) { - for specifier in decl.specifiers.iter_mut() { - self.visit_import_declaration_specifier(specifier); + if let Some(specifiers) = &mut decl.specifiers { + for specifier in specifiers.iter_mut() { + self.visit_import_declaration_specifier(specifier); + } } // TODO: source // TODO: assertions diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index b7948ae9c..565f91180 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -642,72 +642,74 @@ impl<'a, const MINIFY: bool> Gen for FormalParameters<'a> { impl<'a, const MINIFY: bool> Gen for ImportDeclaration<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(b"import "); - if self.specifiers.is_empty() { - p.print(b'\''); - p.print_str(self.source.value.as_bytes()); - p.print(b'\''); - self.assertions.gen(p, ctx); - p.print_semicolon_after_statement(); - return; - } + if let Some(specifiers) = &self.specifiers { + if specifiers.is_empty() { + p.print(b'\''); + p.print_str(self.source.value.as_bytes()); + p.print(b'\''); + self.assertions.gen(p, ctx); + p.print_semicolon_after_statement(); + return; + } - let mut in_block = false; - for (index, specifier) in self.specifiers.iter().enumerate() { - match specifier { - ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { - if in_block { - p.print_str(b"},"); - in_block = false; - } else if index != 0 { - p.print_comma(); - } - spec.local.gen(p, ctx); - } - ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { - if in_block { - p.print_str(b"},"); - in_block = false; - } else if index != 0 { - p.print_comma(); - } - p.print_str(b"* as "); - spec.local.gen(p, ctx); - } - ImportDeclarationSpecifier::ImportSpecifier(spec) => { - if in_block { - p.print_comma(); - } else { - if index != 0 { + let mut in_block = false; + for (index, specifier) in specifiers.iter().enumerate() { + match specifier { + ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { + if in_block { + p.print_str(b"},"); + in_block = false; + } else if index != 0 { p.print_comma(); } - in_block = true; - p.print(b'{'); - } - - let imported_name = match &spec.imported { - ModuleExportName::Identifier(identifier) => { - identifier.gen(p, ctx); - identifier.name.as_bytes() - } - ModuleExportName::StringLiteral(literal) => { - literal.gen(p, ctx); - literal.value.as_bytes() - } - }; - - let local_name = spec.local.name.as_bytes(); - - if imported_name != local_name { - p.print_str(b" as "); spec.local.gen(p, ctx); } + ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { + if in_block { + p.print_str(b"},"); + in_block = false; + } else if index != 0 { + p.print_comma(); + } + p.print_str(b"* as "); + spec.local.gen(p, ctx); + } + ImportDeclarationSpecifier::ImportSpecifier(spec) => { + if in_block { + p.print_comma(); + } else { + if index != 0 { + p.print_comma(); + } + in_block = true; + p.print(b'{'); + } + + let imported_name = match &spec.imported { + ModuleExportName::Identifier(identifier) => { + identifier.gen(p, ctx); + identifier.name.as_bytes() + } + ModuleExportName::StringLiteral(literal) => { + literal.gen(p, ctx); + literal.value.as_bytes() + } + }; + + let local_name = spec.local.name.as_bytes(); + + if imported_name != local_name { + p.print_str(b" as "); + spec.local.gen(p, ctx); + } + } } } + if in_block { + p.print(b'}'); + } + p.print_str(b" from "); } - if in_block { - p.print(b'}'); - } - p.print_str(b" from "); self.source.gen(p, ctx); self.assertions.gen(p, ctx); p.print_semicolon_after_statement(); @@ -747,11 +749,15 @@ impl<'a, const MINIFY: bool> Gen for ExportNamedDeclaration<'a> { None => { p.print(b'{'); if !self.specifiers.is_empty() { + p.print_soft_space(); p.print_list(&self.specifiers, ctx); + p.print_soft_space(); } p.print(b'}'); if let Some(source) = &self.source { + p.print_soft_space(); p.print_str(b"from"); + p.print_soft_space(); source.gen(p, ctx); } p.needs_semicolon = true; diff --git a/crates/oxc_formatter/src/gen.rs b/crates/oxc_formatter/src/gen.rs index 6ad7e2c06..ecd542cf6 100644 --- a/crates/oxc_formatter/src/gen.rs +++ b/crates/oxc_formatter/src/gen.rs @@ -566,81 +566,83 @@ impl<'a> Gen for FormalParameters<'a> { impl<'a> Gen for ImportDeclaration<'a> { fn gen(&self, p: &mut Formatter) { p.print_str(b"import "); - if self.specifiers.is_empty() { - p.print(b'\''); - p.print_str(self.source.value.as_bytes()); - p.print(b'\''); - self.assertions.gen(p); - p.print_semicolon_after_statement(); - return; - } + if let Some(specifiers) = &self.specifiers { + if specifiers.is_empty() { + p.print(b'\''); + p.print_str(self.source.value.as_bytes()); + p.print(b'\''); + self.assertions.gen(p); + p.print_semicolon_after_statement(); + return; + } - let mut in_block = false; - for (index, specifier) in self.specifiers.iter().enumerate() { - match specifier { - ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { - if in_block { - p.print_space(); - p.print_str(b"},"); - p.print_space(); - in_block = false; - } else if index != 0 { - p.print_comma(); - p.print_space(); - } - spec.local.gen(p); - } - ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { - if in_block { - p.print_space(); - p.print_str(b"},"); - p.print_space(); - in_block = false; - } else if index != 0 { - p.print_comma(); - p.print_space(); - } - p.print_str(b"* as "); - spec.local.gen(p); - } - ImportDeclarationSpecifier::ImportSpecifier(spec) => { - if in_block { - p.print_comma(); - } else { - if index != 0 { + let mut in_block = false; + for (index, specifier) in specifiers.iter().enumerate() { + match specifier { + ImportDeclarationSpecifier::ImportDefaultSpecifier(spec) => { + if in_block { + p.print_space(); + p.print_str(b"},"); + p.print_space(); + in_block = false; + } else if index != 0 { p.print_comma(); p.print_space(); } - in_block = true; - p.print(b'{'); - } - p.print_space(); - - let imported_name = match &spec.imported { - ModuleExportName::Identifier(identifier) => { - identifier.gen(p); - identifier.name.as_bytes() - } - ModuleExportName::StringLiteral(literal) => { - literal.gen(p); - literal.value.as_bytes() - } - }; - - let local_name = spec.local.name.as_bytes(); - - if imported_name != local_name { - p.print_str(b" as "); spec.local.gen(p); } + ImportDeclarationSpecifier::ImportNamespaceSpecifier(spec) => { + if in_block { + p.print_space(); + p.print_str(b"},"); + p.print_space(); + in_block = false; + } else if index != 0 { + p.print_comma(); + p.print_space(); + } + p.print_str(b"* as "); + spec.local.gen(p); + } + ImportDeclarationSpecifier::ImportSpecifier(spec) => { + if in_block { + p.print_comma(); + } else { + if index != 0 { + p.print_comma(); + p.print_space(); + } + in_block = true; + p.print(b'{'); + } + p.print_space(); + + let imported_name = match &spec.imported { + ModuleExportName::Identifier(identifier) => { + identifier.gen(p); + identifier.name.as_bytes() + } + ModuleExportName::StringLiteral(literal) => { + literal.gen(p); + literal.value.as_bytes() + } + }; + + let local_name = spec.local.name.as_bytes(); + + if imported_name != local_name { + p.print_str(b" as "); + spec.local.gen(p); + } + } } } + if in_block { + p.print_space(); + p.print(b'}'); + } + p.print_str(b" from "); } - if in_block { - p.print_space(); - p.print(b'}'); - } - p.print_str(b" from "); self.source.gen(p); self.assertions.gen(p); p.print_semicolon_after_statement(); diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index 881bea0f2..c072f7d31 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -369,12 +369,13 @@ fn resolve_to_jest_fn<'a>( }; if import_decl.source.value == "@jest/globals" { - let original = import_decl.specifiers.iter().find_map(|specifier| match specifier { - ImportDeclarationSpecifier::ImportSpecifier(import_specifier) => { - Some(import_specifier.imported.name()) - } - _ => None, - }); + let original = + import_decl.specifiers.iter().flatten().find_map(|specifier| match specifier { + ImportDeclarationSpecifier::ImportSpecifier(import_specifier) => { + Some(import_specifier.imported.name()) + } + _ => None, + }); return Some(ResolvedJestFn { local: &ident.name, kind: JestFnFrom::Import, original }); } diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 13f962c0a..6c4b95bbf 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -52,9 +52,9 @@ impl<'a> Parser<'a> { let specifiers = if self.at(Kind::Str) { // import "source" - self.ast.new_vec() + None } else { - self.parse_import_declaration_specifiers()? + Some(self.parse_import_declaration_specifiers()?) }; let source = self.parse_literal_string()?; @@ -388,7 +388,7 @@ impl<'a> Parser<'a> { let imported = IdentifierName { span: local.span, name: local.name.clone() }; (ModuleExportName::Identifier(imported), local) }; - Ok(ImportSpecifier { span: self.end_span(specifier_span), imported, local }) + Ok(ImportSpecifier { span: self.end_span(specifier_span), imported, local, import_kind }) } // ModuleExportName : diff --git a/crates/oxc_query/src/edges.rs b/crates/oxc_query/src/edges.rs index b9db75cf7..0e58ee631 100644 --- a/crates/oxc_query/src/edges.rs +++ b/crates/oxc_query/src/edges.rs @@ -1643,6 +1643,7 @@ mod import { .import .specifiers .iter() + .flatten() .filter_map(|the_specifier| { if let ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) = the_specifier @@ -1685,6 +1686,7 @@ mod import { .import .specifiers .iter() + .flatten() .filter_map(|the_specifier| { if let ImportDeclarationSpecifier::ImportSpecifier(specifier) = the_specifier diff --git a/crates/oxc_semantic/src/module_record/builder.rs b/crates/oxc_semantic/src/module_record/builder.rs index a4c31b749..6a85f4f39 100644 --- a/crates/oxc_semantic/src/module_record/builder.rs +++ b/crates/oxc_semantic/src/module_record/builder.rs @@ -214,29 +214,31 @@ impl ModuleRecordBuilder { return; } let module_request = NameSpan::new(decl.source.value.clone(), decl.source.span); - for specifier in &decl.specifiers { - let (import_name, local_name) = match specifier { - ImportDeclarationSpecifier::ImportSpecifier(specifier) => ( - ImportImportName::Name(NameSpan::new( - specifier.imported.name().clone(), - specifier.imported.span(), - )), - NameSpan::new(specifier.local.name.clone(), specifier.local.span), - ), - ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => ( - ImportImportName::NamespaceObject, - NameSpan::new(specifier.local.name.clone(), specifier.local.span), - ), - ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => ( - ImportImportName::Default(specifier.span), - NameSpan::new(specifier.local.name.clone(), specifier.local.span), - ), - }; - self.add_import_entry(ImportEntry { - module_request: module_request.clone(), - import_name, - local_name, - }); + if let Some(specifiers) = &decl.specifiers { + for specifier in specifiers { + let (import_name, local_name) = match specifier { + ImportDeclarationSpecifier::ImportSpecifier(specifier) => ( + ImportImportName::Name(NameSpan::new( + specifier.imported.name().clone(), + specifier.imported.span(), + )), + NameSpan::new(specifier.local.name.clone(), specifier.local.span), + ), + ImportDeclarationSpecifier::ImportNamespaceSpecifier(specifier) => ( + ImportImportName::NamespaceObject, + NameSpan::new(specifier.local.name.clone(), specifier.local.span), + ), + ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => ( + ImportImportName::Default(specifier.span), + NameSpan::new(specifier.local.name.clone(), specifier.local.span), + ), + }; + self.add_import_entry(ImportEntry { + module_request: module_request.clone(), + import_name, + local_name, + }); + } } self.add_module_request(&module_request); } diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index 73b6c1d2a..b71d59d8c 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -13,7 +13,7 @@ use oxc_transformer::{TransformOptions, TransformTarget, Transformer}; // or `just watch "run -p oxc_transformer --example transformer"` fn main() { - let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string()); + let name = env::args().nth(1).unwrap_or_else(|| "test.tsx".to_string()); let path = Path::new(&name); let source_text = std::fs::read_to_string(path).expect("{name} not found"); let allocator = Allocator::default(); @@ -31,7 +31,7 @@ fn main() { let codegen_options = CodegenOptions; let printed = Codegen::::new(source_text.len(), codegen_options).build(&ret.program); println!("Original:\n"); - println!("{printed}"); + println!("{printed}\n"); let semantic = SemanticBuilder::new(&source_text, source_type).build(&ret.program).semantic; let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree(); diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index ddd2885f9..7cfa4506d 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -96,6 +96,15 @@ impl<'a> Transformer<'a> { } impl<'a> VisitMut<'a> for Transformer<'a> { + fn visit_program(&mut self, program: &mut Program<'a>) { + for directive in program.directives.iter_mut() { + self.visit_directive(directive); + } + + self.typescript.as_mut().map(|t| t.transform_program(program)); + self.visit_statements(&mut program.body); + } + fn visit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { for stmt in stmts.iter_mut() { self.visit_statement(stmt); diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs index 2d9beafd2..bac896805 100644 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -1,5 +1,5 @@ -use oxc_ast::ast::*; -use oxc_ast::AstBuilder; +use oxc_ast::{ast::*, AstBuilder}; +use oxc_span::Span; use std::rc::Rc; @@ -9,12 +9,12 @@ use std::rc::Rc; /// * /// * pub struct TypeScript<'a> { - _ast: Rc>, + ast: Rc>, } impl<'a> TypeScript<'a> { - pub fn new(_ast: Rc>) -> Self { - Self { _ast } + pub fn new(ast: Rc>) -> Self { + Self { ast } } #[allow(clippy::unused_self)] @@ -23,4 +23,73 @@ impl<'a> TypeScript<'a> { params.items.remove(0); } } + + /// * 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. + pub fn transform_program(&self, program: &mut Program<'a>) { + let mut needs_explicit_esm = false; + + for stmt in program.body.iter_mut() { + if let Statement::ModuleDeclaration(module_decl) = stmt { + needs_explicit_esm = true; + match &mut **module_decl { + ModuleDeclaration::ExportNamedDeclaration(decl) => { + decl.specifiers.retain(|specifier| specifier.export_kind.is_value()); + } + ModuleDeclaration::ImportDeclaration(decl) => { + if let Some(specifiers) = &mut decl.specifiers { + specifiers.retain(|specifier| match specifier { + ImportDeclarationSpecifier::ImportSpecifier(s) => { + s.import_kind.is_value() + } + _ => false, + }); + } + } + _ => {} + } + } + } + + program.body.retain(|stmt| match stmt { + Statement::ModuleDeclaration(module_decl) => match &**module_decl { + ModuleDeclaration::ImportDeclaration(decl) => { + if decl.import_kind.is_type() { + return false; + } + if decl.specifiers.as_ref().is_some_and(|specifiers| specifiers.is_empty()) { + // TODO: verbatim_module_syntax + return false; + } + true + } + ModuleDeclaration::ExportNamedDeclaration(decl) => { + if decl.export_kind.is_type() { + return false; + } + if decl.declaration.is_none() && decl.specifiers.is_empty() { + return false; + } + true + } + _ => true, + }, + _ => true, + }); + + if needs_explicit_esm + && !program.body.iter().any(|s| matches!(s, Statement::ModuleDeclaration(_))) + { + let empty_export = self.ast.export_named_declaration( + Span::default(), + None, + self.ast.new_vec(), + None, + ImportOrExportKind::Value, + ); + let export_decl = ModuleDeclaration::ExportNamedDeclaration(empty_export); + program.body.push(self.ast.module_declaration(export_decl)); + } + } } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index f460fbfaf..2d66dddb9 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 164/1083 +Passed: 173/1083 # All Passed: * babel-plugin-transform-numeric-separator @@ -705,7 +705,7 @@ Passed: 164/1083 * unicode-regex/negated-set/input.js * unicode-regex/slash/input.js -# babel-plugin-transform-typescript (73/181) +# babel-plugin-transform-typescript (82/181) * class/abstract-class-decorated/input.ts * class/abstract-class-decorated-method/input.ts * class/abstract-class-decorated-parameter/input.ts @@ -742,33 +742,26 @@ Passed: 164/1083 * enum/ts5.0-const-foldable/input.ts * exports/declared-types/input.ts * exports/export-const-enums/input.ts -* exports/export-type/input.ts -* exports/export-type-from/input.ts * exports/export-type-star-from/input.ts * exports/export=-to-cjs/input.ts * exports/imported-types/input.ts * exports/imported-types-only-remove-type-imports/input.ts * exports/issue-9916-3/input.ts -* exports/type-only-export-specifier-1/input.ts -* exports/type-only-export-specifier-2/input.ts -* exports/type-only-export-specifier-3/input.ts * function/overloads-exports/input.mjs * imports/elide-injected/input.ts +* imports/elide-no-import-specifiers/input.ts * imports/elide-preact/input.ts -* imports/elide-react/input.ts * imports/elide-type-referenced-in-imports-equal-no/input.ts * imports/elide-typeof/input.ts * imports/elision/input.ts * imports/elision-export-type/input.ts * imports/elision-locations/input.ts -* imports/elision-qualifiedname/input.ts * imports/elision-rename/input.ts * imports/enum-id/input.ts * imports/enum-value/input.ts +* imports/import-named-type/input.ts +* imports/import-named-type-default-and-named/input.ts * imports/import-removed-exceptions/input.ts -* imports/import-type/input.ts -* imports/import-type-func-with-duplicate-name/input.ts -* imports/import-type-not-removed/input.ts * imports/import=-declaration/input.ts * imports/import=-module-to-cjs/input.ts * imports/only-remove-type-imports/input.ts @@ -776,8 +769,6 @@ Passed: 164/1083 * imports/property-signature/input.ts * imports/type-only-export-specifier-1/input.ts * imports/type-only-export-specifier-2/input.ts -* imports/type-only-import-specifier-1/input.ts -* imports/type-only-import-specifier-2/input.ts * imports/type-only-import-specifier-3/input.ts * imports/type-only-import-specifier-4/input.ts * namespace/alias/input.ts