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
4 changes: 4 additions & 0 deletions crates/oxc_semantic/src/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ impl Reference {
self.symbol_id = Some(symbol_id);
}

pub fn flag_mut(&mut self) -> &mut ReferenceFlag {
&mut self.flag
}

/// Returns `true` if the identifier value was read. This is not mutually
/// exclusive with [`#is_write`]
pub fn is_read(&self) -> bool {
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ impl SymbolTable {
&self.references[reference_id]
}

pub fn get_reference_mut(&mut self, reference_id: ReferenceId) -> &mut Reference {
&mut self.references[reference_id]
}

pub fn has_binding(&self, reference_id: ReferenceId) -> bool {
self.references[reference_id].symbol_id().is_some()
}
Expand Down
17 changes: 15 additions & 2 deletions crates/oxc_transformer/examples/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use oxc_allocator::Allocator;
use oxc_codegen::{Codegen, CodegenOptions};
use oxc_parser::Parser;
use oxc_span::SourceType;
use oxc_transformer::{TransformOptions, Transformer};
use oxc_transformer::{
ArrowFunctionsOptions, ES2015Options, ReactOptions, TransformOptions, Transformer,
TypeScriptOptions,
};

// Instruction:
// create a `test.js`,
Expand Down Expand Up @@ -32,7 +35,17 @@ fn main() {
println!("{source_text}\n");

let mut program = ret.program;
let transform_options = TransformOptions::default();
let transform_options = TransformOptions {
typescript: TypeScriptOptions::default(),
es2015: ES2015Options { arrow_function: Some(ArrowFunctionsOptions::default()) },
react: ReactOptions {
jsx_plugin: true,
jsx_self_plugin: true,
jsx_source_plugin: true,
..Default::default()
},
..Default::default()
};
Transformer::new(&allocator, path, source_type, &source_text, &ret.trivias, transform_options)
.build(&mut program)
.unwrap();
Expand Down
16 changes: 4 additions & 12 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x0_typescript.transform_program(program, ctx);
}

fn exit_program(&mut self, program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_react.transform_program_on_exit(program);
self.x0_typescript.transform_program_on_exit(program);
self.x0_typescript.transform_program_on_exit(program, ctx);
}

// ALPHASORT
Expand Down Expand Up @@ -232,20 +232,12 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x0_typescript.transform_tagged_template_expression(expr);
}

fn enter_identifier_reference(
&mut self,
ident: &mut IdentifierReference<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.x0_typescript.transform_identifier_reference(ident, ctx);
}

fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_statement(stmt, ctx);
}

fn enter_declaration(&mut self, decl: &mut Declaration<'a>, _ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_declaration(decl);
fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_declaration(decl, ctx);
self.x3_es2015.transform_declaration(decl);
}

Expand Down
64 changes: 48 additions & 16 deletions crates/oxc_transformer/src/typescript/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_span::{Atom, SPAN};
use oxc_syntax::operator::AssignmentOperator;
use oxc_traverse::TraverseCtx;
use rustc_hash::FxHashSet;

use super::collector::TypeScriptReferenceCollector;

pub struct TypeScriptAnnotations<'a> {
#[allow(dead_code)]
options: Rc<TypeScriptOptions>,
Expand All @@ -25,6 +24,7 @@ pub struct TypeScriptAnnotations<'a> {
has_jsx_fragment: bool,
jsx_element_import_name: String,
jsx_fragment_import_name: String,
type_identifier_names: FxHashSet<Atom<'a>>,
}

impl<'a> TypeScriptAnnotations<'a> {
Expand All @@ -50,6 +50,7 @@ impl<'a> TypeScriptAnnotations<'a> {
has_jsx_fragment: false,
jsx_element_import_name,
jsx_fragment_import_name,
type_identifier_names: FxHashSet::default(),
}
}

Expand Down Expand Up @@ -82,19 +83,20 @@ impl<'a> TypeScriptAnnotations<'a> {

// Remove type only imports/exports
pub fn transform_program_on_exit(
&self,
&mut self,
program: &mut Program<'a>,
references: &TypeScriptReferenceCollector,
ctx: &mut TraverseCtx<'a>,
) {
let mut type_names = FxHashSet::default();
let mut module_count = 0;
let mut removed_count = 0;

// let mut type_identifier_names = self.type_identifier_names.clone();

program.body.retain_mut(|stmt| {
// fix namespace/export-type-only/input.ts
// The namespace is type only. So if its name appear in the ExportNamedDeclaration, we should remove it.
if let Statement::TSModuleDeclaration(decl) = stmt {
type_names.insert(decl.id.name().clone());
self.type_identifier_names.insert(decl.id.name().clone());
return false;
}

Expand All @@ -106,7 +108,7 @@ impl<'a> TypeScriptAnnotations<'a> {
ModuleDeclaration::ExportNamedDeclaration(decl) => {
decl.specifiers.retain(|specifier| {
!(specifier.export_kind.is_type()
|| type_names.contains(specifier.exported.name()))
|| self.type_identifier_names.contains(specifier.exported.name()))
});

decl.export_kind.is_type()
Expand All @@ -117,6 +119,9 @@ impl<'a> TypeScriptAnnotations<'a> {
.is_some_and(Declaration::is_typescript_syntax))
&& decl.specifiers.is_empty())
}
ModuleDeclaration::ExportAllDeclaration(decl) => {
return !decl.export_kind.is_type()
}
ModuleDeclaration::ImportDeclaration(decl) => {
let is_type = decl.import_kind.is_type();

Expand All @@ -127,40 +132,39 @@ impl<'a> TypeScriptAnnotations<'a> {
specifiers.retain(|specifier| match specifier {
ImportDeclarationSpecifier::ImportSpecifier(s) => {
if is_type || s.import_kind.is_type() {
type_names.insert(s.local.name.clone());
self.type_identifier_names.insert(s.local.name.clone());
return false;
}

if self.options.only_remove_type_imports {
return true;
}

references.has_reference(&s.local.name)
|| self.is_jsx_imports(&s.local.name)
self.has_value_reference(&s.local.name, ctx)
}
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => {
if is_type {
type_names.insert(s.local.name.clone());
self.type_identifier_names.insert(s.local.name.clone());
return false;
}

if self.options.only_remove_type_imports {
return true;
}
references.has_reference(&s.local.name)
|| self.is_jsx_imports(&s.local.name)

self.has_value_reference(&s.local.name, ctx)
}
ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => {
if is_type {
type_names.insert(s.local.name.clone());
self.type_identifier_names.insert(s.local.name.clone());
return false;
}

if self.options.only_remove_type_imports {
return true;
}

references.has_reference(&s.local.name)
|| self.is_jsx_imports(&s.local.name)
self.has_value_reference(&s.local.name, ctx)
}
});
}
Expand Down Expand Up @@ -428,4 +432,32 @@ impl<'a> TypeScriptAnnotations<'a> {
pub fn transform_jsx_fragment(&mut self, _elem: &mut JSXFragment<'a>) {
self.has_jsx_fragment = true;
}

pub fn transform_export_named_declaration(&mut self, decl: &mut ExportNamedDeclaration<'a>) {
let is_type = decl.export_kind.is_type();
for specifier in &decl.specifiers {
if is_type || specifier.export_kind.is_type() {
self.type_identifier_names.insert(specifier.local.name().clone());
}
}
}

pub fn has_value_reference(&self, name: &Atom<'a>, ctx: &TraverseCtx<'a>) -> bool {
if let Some(symbol_id) = ctx.scopes().get_root_binding(name) {
if ctx.symbols().get_flag(symbol_id).is_export()
&& !self.type_identifier_names.contains(name)
{
return true;
}
if ctx
.symbols()
.get_resolved_references(symbol_id)
.any(|reference| !reference.is_type())
{
return true;
}
}

self.is_jsx_imports(name)
}
}
36 changes: 0 additions & 36 deletions crates/oxc_transformer/src/typescript/collector.rs

This file was deleted.

32 changes: 10 additions & 22 deletions crates/oxc_transformer/src/typescript/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
mod annotations;
mod collector;
mod diagnostics;
mod r#enum;
mod module;
Expand All @@ -14,10 +13,7 @@ use oxc_traverse::TraverseCtx;

use crate::context::Ctx;

use self::{
annotations::TypeScriptAnnotations, collector::TypeScriptReferenceCollector,
r#enum::TypeScriptEnum,
};
use self::{annotations::TypeScriptAnnotations, r#enum::TypeScriptEnum};

pub use self::options::TypeScriptOptions;

Expand Down Expand Up @@ -49,7 +45,6 @@ pub struct TypeScript<'a> {

annotations: TypeScriptAnnotations<'a>,
r#enum: TypeScriptEnum<'a>,
reference_collector: TypeScriptReferenceCollector<'a>,
}

impl<'a> TypeScript<'a> {
Expand All @@ -59,7 +54,6 @@ impl<'a> TypeScript<'a> {
Self {
annotations: TypeScriptAnnotations::new(&options, ctx),
r#enum: TypeScriptEnum::new(ctx),
reference_collector: TypeScriptReferenceCollector::new(),
options,
ctx: Rc::clone(ctx),
}
Expand All @@ -79,8 +73,12 @@ impl<'a> TypeScript<'a> {
}
}

pub fn transform_program_on_exit(&self, program: &mut Program<'a>) {
self.annotations.transform_program_on_exit(program, &self.reference_collector);
pub fn transform_program_on_exit(
&mut self,
program: &mut Program<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.transform_program_on_exit(program, ctx);
}

pub fn transform_arrow_expression(&mut self, expr: &mut ArrowFunctionExpression<'a>) {
Expand All @@ -104,7 +102,7 @@ impl<'a> TypeScript<'a> {
}

pub fn transform_export_named_declaration(&mut self, decl: &mut ExportNamedDeclaration<'a>) {
self.reference_collector.visit_transform_export_named_declaration(decl);
self.annotations.transform_export_named_declaration(decl);
}

pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
Expand Down Expand Up @@ -196,22 +194,12 @@ impl<'a> TypeScript<'a> {
self.annotations.transform_tagged_template_expression(expr);
}

pub fn transform_identifier_reference(
&mut self,
ident: &mut IdentifierReference<'a>,
ctx: &TraverseCtx<'a>,
) {
if !ctx.parent().is_ts_interface_heritage() && !ctx.parent().is_ts_type_reference() {
self.reference_collector.visit_identifier_reference(ident);
}
}

pub fn transform_declaration(&mut self, decl: &mut Declaration<'a>) {
pub fn transform_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
match decl {
Declaration::TSImportEqualsDeclaration(ts_import_equals)
if ts_import_equals.import_kind.is_value() =>
{
*decl = self.transform_ts_import_equals(ts_import_equals);
*decl = self.transform_ts_import_equals(ts_import_equals, ctx);
}
_ => {}
}
Expand Down
Loading