diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 7dbb13f37fbe13..7e036a48ff0924 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -488,47 +488,56 @@ impl<'a> SemanticBuilder<'a> { // If unresolved, transfer it to parent scope's unresolved references. let bindings = self.scope.get_bindings(self.current_scope_id); if let Some(symbol_id) = bindings.get(name.as_str()).copied() { - let symbol_flags = self.symbols.get_flags(symbol_id); - references.retain(|&reference_id| { - let reference = &mut self.symbols.references[reference_id]; + if self.source_type.is_typescript() { + let symbol_flags = self.symbols.get_flags(symbol_id); + references.retain(|&reference_id| { + let reference = &mut self.symbols.references[reference_id]; + + let flags = reference.flags_mut(); + + // Determine the symbol whether can be referenced by this reference. + let resolved = (flags.is_value() + && symbol_flags.can_be_referenced_by_value()) + || (flags.is_type() && symbol_flags.can_be_referenced_by_type()) + || (flags.is_value_as_type() + && symbol_flags.can_be_referenced_by_value_as_type()); + + if !resolved { + return true; + } - let flags = reference.flags_mut(); + if symbol_flags.is_value() && flags.is_value() { + // The non type-only ExportSpecifier can reference both type/value symbols, + // if the symbol is a value symbol and reference flag is not type-only, + // remove the type flag. For example: `const B = 1; export { B };` + *flags -= ReferenceFlags::Type; + } else { + // 1. ReferenceFlags::ValueAsType -> ReferenceFlags::Type + // `const ident = 0; typeof ident` + // ^^^^^ -> The ident is a value symbols, + // but it used as a type. + // 2. ReferenceFlags::Value | ReferenceFlags::Type -> ReferenceFlags::Type + // `type ident = string; export default ident; + // ^^^^^ We have confirmed the symbol is + // not a value symbol, so we need to + // make sure the reference is a type only. + *flags = ReferenceFlags::Type; + } + reference.set_symbol_id(symbol_id); + self.symbols.add_resolved_reference(symbol_id, reference_id); - // Determine the symbol whether can be referenced by this reference. - let resolved = (flags.is_value() && symbol_flags.can_be_referenced_by_value()) - || (flags.is_type() && symbol_flags.can_be_referenced_by_type()) - || (flags.is_value_as_type() - && symbol_flags.can_be_referenced_by_value_as_type()); + false + }); - if !resolved { - return true; + if references.is_empty() { + continue; } - - if symbol_flags.is_value() && flags.is_value() { - // The non type-only ExportSpecifier can reference both type/value symbols, - // if the symbol is a value symbol and reference flag is not type-only, - // remove the type flag. For example: `const B = 1; export { B };` - *flags -= ReferenceFlags::Type; - } else { - // 1. ReferenceFlags::ValueAsType -> ReferenceFlags::Type - // `const ident = 0; typeof ident` - // ^^^^^ -> The ident is a value symbols, - // but it used as a type. - // 2. ReferenceFlags::Value | ReferenceFlags::Type -> ReferenceFlags::Type - // `type ident = string; export default ident; - // ^^^^^ We have confirmed the symbol is - // not a value symbol, so we need to - // make sure the reference is a type only. - *flags = ReferenceFlags::Type; + } else { + for reference_id in references.drain(..) { + let reference = &mut self.symbols.references[reference_id]; + reference.set_symbol_id(symbol_id); + self.symbols.add_resolved_reference(symbol_id, reference_id); } - reference.set_symbol_id(symbol_id); - self.symbols.add_resolved_reference(symbol_id, reference_id); - - false - }); - - if references.is_empty() { - continue; } } @@ -1843,7 +1852,9 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { ExportDefaultDeclarationKind::Identifier(it) => { // `export default ident` // ^^^^^ -> can reference both type/value symbols - self.current_reference_flags = ReferenceFlags::Read | ReferenceFlags::Type; + if self.source_type.is_typescript() { + self.current_reference_flags = ReferenceFlags::Read | ReferenceFlags::Type; + } self.visit_identifier_reference(it); } match_expression!(ExportDefaultDeclarationKind) => { @@ -1863,6 +1874,8 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { if let Some(source) = &it.source { self.visit_string_literal(source); self.visit_export_specifiers(&it.specifiers); + } else if !self.source_type.is_typescript() { + self.visit_export_specifiers(&it.specifiers); } else { for specifier in &it.specifiers { // `export type { a }` or `export { type a }` -> `a` is a type reference