mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer): implement some of needs_explicit_esm for typescript (#1047)
Co-authored-by: magic-akari <akari.ccino@gmail.com>
This commit is contained in:
parent
a442fad3b7
commit
af1a76bafa
15 changed files with 287 additions and 189 deletions
|
|
@ -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<Vec<'a, ImportDeclarationSpecifier>>,
|
||||
pub source: StringLiteral,
|
||||
pub assertions: Option<Vec<'a, ImportAttribute>>, // 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"
|
||||
|
|
|
|||
|
|
@ -975,7 +975,7 @@ impl<'a> AstBuilder<'a> {
|
|||
pub fn import_declaration(
|
||||
&self,
|
||||
span: Span,
|
||||
specifiers: Vec<'a, ImportDeclarationSpecifier>,
|
||||
specifiers: Option<Vec<'a, ImportDeclarationSpecifier>>,
|
||||
source: StringLiteral,
|
||||
assertions: Option<Vec<'a, ImportAttribute>>,
|
||||
import_kind: ImportOrExportKind,
|
||||
|
|
|
|||
|
|
@ -142,11 +142,21 @@ impl<'a> BoundNames for ModuleDeclaration<'a> {
|
|||
|
||||
impl<'a> BoundNames for ImportDeclaration<'a> {
|
||||
fn bound_names<F: FnMut(&BindingIdentifier)>(&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -642,72 +642,74 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for FormalParameters<'a> {
|
|||
impl<'a, const MINIFY: bool> Gen<MINIFY> 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<MINIFY> 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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 :
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<false>::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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
|||
/// * <https://babeljs.io/docs/babel-plugin-transform-typescript>
|
||||
/// * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-typescript>
|
||||
pub struct TypeScript<'a> {
|
||||
_ast: Rc<AstBuilder<'a>>,
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> TypeScript<'a> {
|
||||
pub fn new(_ast: Rc<AstBuilder<'a>>) -> Self {
|
||||
Self { _ast }
|
||||
pub fn new(ast: Rc<AstBuilder<'a>>) -> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue