use crate::{
AnyJsImportClause, AnyJsNamedImportSpecifier, JsArrayBindingPatternElement,
JsArrayBindingPatternRestElement, JsArrowFunctionExpression, JsBogusNamedImportSpecifier,
JsBogusParameter, JsCatchDeclaration, JsClassDeclaration, JsClassExportDefaultDeclaration,
JsClassExpression, JsConstructorClassMember, JsConstructorParameterList,
JsConstructorParameters, JsDefaultImportSpecifier, JsExport, JsFormalParameter,
JsFunctionDeclaration, JsFunctionExportDefaultDeclaration, JsFunctionExpression,
JsIdentifierBinding, JsMethodClassMember, JsMethodObjectMember, JsNamedImportSpecifier,
JsNamespaceImportSpecifier, JsObjectBindingPatternProperty, JsObjectBindingPatternRest,
JsObjectBindingPatternShorthandProperty, JsParameterList, JsParameters, JsRestParameter,
JsSetterClassMember, JsSetterObjectMember, JsShorthandNamedImportSpecifier, JsSyntaxKind,
JsSyntaxNode, JsSyntaxToken, JsVariableDeclarator, TsCallSignatureTypeMember,
TsConstructSignatureTypeMember, TsConstructorSignatureClassMember, TsConstructorType,
TsDeclareFunctionDeclaration, TsDeclareFunctionExportDefaultDeclaration, TsEnumDeclaration,
TsFunctionType, TsIdentifierBinding, TsImportEqualsDeclaration, TsIndexSignatureClassMember,
TsIndexSignatureParameter, TsInferType, TsInterfaceDeclaration, TsMappedType,
TsMethodSignatureClassMember, TsMethodSignatureTypeMember, TsModuleDeclaration,
TsPropertyParameter, TsSetterSignatureClassMember, TsSetterSignatureTypeMember,
TsTypeAliasDeclaration, TsTypeParameter, TsTypeParameterName,
};
use biome_rowan::{declare_node_union, AstNode, SyntaxResult};
declare_node_union! {
pub AnyJsBindingDeclaration =
JsArrayBindingPatternElement
| JsArrayBindingPatternRestElement
| JsObjectBindingPatternProperty
| JsObjectBindingPatternRest
| JsObjectBindingPatternShorthandProperty
| JsVariableDeclarator
| JsArrowFunctionExpression | JsFormalParameter | JsRestParameter | JsBogusParameter
| TsIndexSignatureParameter | TsPropertyParameter
| TsInferType | TsMappedType | TsTypeParameter
| JsFunctionDeclaration | JsFunctionExpression
| TsDeclareFunctionDeclaration
| JsClassDeclaration | JsClassExpression
| TsInterfaceDeclaration | TsTypeAliasDeclaration | TsEnumDeclaration | TsModuleDeclaration
| JsShorthandNamedImportSpecifier
| JsNamedImportSpecifier | JsBogusNamedImportSpecifier | JsDefaultImportSpecifier
| JsNamespaceImportSpecifier
| TsImportEqualsDeclaration
| JsClassExportDefaultDeclaration | JsFunctionExportDefaultDeclaration
| TsDeclareFunctionExportDefaultDeclaration
| JsCatchDeclaration
}
impl AnyJsBindingDeclaration {
pub const fn is_mergeable(&self, other: &AnyJsBindingDeclaration) -> bool {
Self::can_merge(self, other) || Self::can_merge(other, self)
}
const fn can_merge(a: &AnyJsBindingDeclaration, b: &AnyJsBindingDeclaration) -> bool {
match (a, b) {
(
AnyJsBindingDeclaration::TsDeclareFunctionDeclaration(_),
AnyJsBindingDeclaration::JsFunctionDeclaration(_)
| AnyJsBindingDeclaration::TsDeclareFunctionDeclaration(_),
) => true,
(
AnyJsBindingDeclaration::TsDeclareFunctionExportDefaultDeclaration(_),
AnyJsBindingDeclaration::JsFunctionExportDefaultDeclaration(_)
| AnyJsBindingDeclaration::TsDeclareFunctionExportDefaultDeclaration(_),
) => true,
(
AnyJsBindingDeclaration::TsEnumDeclaration(_),
AnyJsBindingDeclaration::TsEnumDeclaration(_),
) => true,
(
AnyJsBindingDeclaration::TsTypeAliasDeclaration(_)
| AnyJsBindingDeclaration::TsInterfaceDeclaration(_)
| AnyJsBindingDeclaration::TsModuleDeclaration(_),
AnyJsBindingDeclaration::JsFunctionDeclaration(_)
| AnyJsBindingDeclaration::JsArrayBindingPatternElement(_)
| AnyJsBindingDeclaration::JsArrayBindingPatternRestElement(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternProperty(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternRest(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternShorthandProperty(_)
| AnyJsBindingDeclaration::JsVariableDeclarator(_)
| AnyJsBindingDeclaration::JsArrowFunctionExpression(_)
| AnyJsBindingDeclaration::JsFormalParameter(_)
| AnyJsBindingDeclaration::JsRestParameter(_)
| AnyJsBindingDeclaration::TsPropertyParameter(_)
| AnyJsBindingDeclaration::JsCatchDeclaration(_)
| AnyJsBindingDeclaration::TsModuleDeclaration(_),
) => true,
(
AnyJsBindingDeclaration::TsInterfaceDeclaration(_),
AnyJsBindingDeclaration::JsClassDeclaration(_)
| AnyJsBindingDeclaration::TsDeclareFunctionDeclaration(_)
| AnyJsBindingDeclaration::TsInterfaceDeclaration(_),
) => true,
(
AnyJsBindingDeclaration::TsModuleDeclaration(_),
AnyJsBindingDeclaration::JsClassDeclaration(_)
| AnyJsBindingDeclaration::TsDeclareFunctionDeclaration(_)
| AnyJsBindingDeclaration::TsEnumDeclaration(_)
| AnyJsBindingDeclaration::TsInterfaceDeclaration(_),
) => true,
(_, _) => false,
}
}
pub fn parent_binding_pattern_declaration(&self) -> Option<AnyJsBindingDeclaration> {
match self {
AnyJsBindingDeclaration::JsArrayBindingPatternElement(_)
| AnyJsBindingDeclaration::JsArrayBindingPatternRestElement(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternProperty(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternRest(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternShorthandProperty(_) => {
parent_binding_pattern_declaration(self.syntax())
}
_ => None,
}
}
pub const fn is_parameter_like(&self) -> bool {
matches!(
self,
AnyJsBindingDeclaration::JsArrowFunctionExpression(_)
| AnyJsBindingDeclaration::JsFormalParameter(_)
| AnyJsBindingDeclaration::JsRestParameter(_)
| AnyJsBindingDeclaration::JsBogusParameter(_)
| AnyJsBindingDeclaration::TsPropertyParameter(_)
)
}
pub const fn is_type_parameter(&self) -> bool {
matches!(self, AnyJsBindingDeclaration::TsTypeParameter(_))
}
pub fn export(&self) -> Option<JsExport> {
let maybe_export = match self {
AnyJsBindingDeclaration::JsArrayBindingPatternElement(_)
| AnyJsBindingDeclaration::JsArrayBindingPatternRestElement(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternProperty(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternRest(_)
| AnyJsBindingDeclaration::JsObjectBindingPatternShorthandProperty(_) => {
return parent_binding_pattern_declaration(self.syntax())
.and_then(|decl| decl.export());
}
Self::JsVariableDeclarator(_) => self.syntax().ancestors().nth(4),
Self::JsFunctionDeclaration(_)
| Self::JsClassDeclaration(_)
| Self::TsTypeAliasDeclaration(_)
| Self::TsEnumDeclaration(_)
| Self::TsModuleDeclaration(_) => self.syntax().parent(),
Self::TsInterfaceDeclaration(_) => {
self.syntax()
.ancestors()
.skip(1)
.find(|x| x.kind() != JsSyntaxKind::JS_EXPORT_DEFAULT_DECLARATION_CLAUSE)
}
Self::JsClassExportDefaultDeclaration(_)
| Self::JsFunctionExportDefaultDeclaration(_)
| Self::TsDeclareFunctionDeclaration(_)
| Self::TsDeclareFunctionExportDefaultDeclaration(_) => self.syntax().grand_parent(),
_ => None,
};
maybe_export.and_then(JsExport::cast)
}
}
declare_node_union! {
pub AnyJsIdentifierBinding = JsIdentifierBinding | TsIdentifierBinding | TsTypeParameterName
}
fn declaration(node: JsSyntaxNode) -> Option<AnyJsBindingDeclaration> {
match AnyJsBindingDeclaration::cast(node)? {
AnyJsBindingDeclaration::JsFormalParameter(parameter) => {
match parameter.parent::<TsPropertyParameter>() {
Some(parameter) => Some(AnyJsBindingDeclaration::TsPropertyParameter(parameter)),
None => Some(AnyJsBindingDeclaration::JsFormalParameter(parameter)),
}
}
declaration => Some(declaration),
}
}
fn parent_binding_pattern_declaration(node: &JsSyntaxNode) -> Option<AnyJsBindingDeclaration> {
let possible_declarator = node.ancestors().skip(1).find(|x| {
!matches!(
x.kind(),
JsSyntaxKind::JS_ARRAY_BINDING_PATTERN_ELEMENT
| JsSyntaxKind::JS_ARRAY_BINDING_PATTERN_ELEMENT_LIST
| JsSyntaxKind::JS_ARRAY_BINDING_PATTERN
| JsSyntaxKind::JS_OBJECT_BINDING_PATTERN
| JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_PROPERTY
| JsSyntaxKind::JS_OBJECT_BINDING_PATTERN_PROPERTY_LIST
)
})?;
declaration(possible_declarator)
}
fn is_under_pattern_binding(node: &JsSyntaxNode) -> Option<bool> {
use JsSyntaxKind::*;
Some(matches!(
node.parent()?.kind(),
JS_ARRAY_BINDING_PATTERN_ELEMENT
| JS_OBJECT_BINDING_PATTERN
| JS_OBJECT_BINDING_PATTERN_REST
| JS_OBJECT_BINDING_PATTERN_PROPERTY
| JS_OBJECT_BINDING_PATTERN_PROPERTY_LIST
| JS_OBJECT_BINDING_PATTERN_SHORTHAND_PROPERTY
| JS_ARRAY_BINDING_PATTERN
| JS_ARRAY_BINDING_PATTERN_ELEMENT_LIST
| JS_ARRAY_BINDING_PATTERN_REST_ELEMENT
))
}
fn is_under_array_pattern_binding(node: &JsSyntaxNode) -> Option<bool> {
use JsSyntaxKind::*;
let parent = node.parent()?;
match parent.kind() {
JS_ARRAY_BINDING_PATTERN
| JS_ARRAY_BINDING_PATTERN_ELEMENT_LIST
| JS_ARRAY_BINDING_PATTERN_REST_ELEMENT => Some(true),
JS_ARRAY_BINDING_PATTERN_ELEMENT => is_under_array_pattern_binding(&parent),
_ => Some(false),
}
}
fn is_under_object_pattern_binding(node: &JsSyntaxNode) -> Option<bool> {
use JsSyntaxKind::*;
let parent = node.parent()?;
match parent.kind() {
JS_OBJECT_BINDING_PATTERN
| JS_OBJECT_BINDING_PATTERN_REST
| JS_OBJECT_BINDING_PATTERN_PROPERTY
| JS_OBJECT_BINDING_PATTERN_PROPERTY_LIST
| JS_OBJECT_BINDING_PATTERN_SHORTHAND_PROPERTY => Some(true),
JS_ARRAY_BINDING_PATTERN_ELEMENT => is_under_object_pattern_binding(&parent),
_ => Some(false),
}
}
impl AnyJsIdentifierBinding {
pub fn name_token(&self) -> SyntaxResult<JsSyntaxToken> {
match self {
AnyJsIdentifierBinding::JsIdentifierBinding(binding) => binding.name_token(),
AnyJsIdentifierBinding::TsIdentifierBinding(binding) => binding.name_token(),
AnyJsIdentifierBinding::TsTypeParameterName(binding) => binding.ident_token(),
}
}
pub fn declaration(&self) -> Option<AnyJsBindingDeclaration> {
declaration(self.syntax().parent()?)
}
pub fn is_under_pattern_binding(&self) -> Option<bool> {
is_under_pattern_binding(self.syntax())
}
pub fn is_under_array_pattern_binding(&self) -> Option<bool> {
is_under_array_pattern_binding(self.syntax())
}
pub fn is_under_object_pattern_binding(&self) -> Option<bool> {
is_under_object_pattern_binding(self.syntax())
}
pub fn is_type_only(&self) -> bool {
match self {
AnyJsIdentifierBinding::JsIdentifierBinding(binding) => {
if let Some(specifier) = binding.parent::<AnyJsNamedImportSpecifier>() {
return specifier.imports_only_types();
}
if let Some(clause) = binding
.syntax()
.grand_parent()
.and_then(AnyJsImportClause::cast)
{
return clause.type_token().is_some();
}
}
AnyJsIdentifierBinding::TsIdentifierBinding(binding) => {
return binding.parent::<TsModuleDeclaration>().is_none();
}
AnyJsIdentifierBinding::TsTypeParameterName(_) => {}
}
false
}
pub fn with_name_token(self, name_token: JsSyntaxToken) -> Self {
match self {
Self::JsIdentifierBinding(binding) => {
Self::JsIdentifierBinding(binding.with_name_token(name_token))
}
Self::TsIdentifierBinding(binding) => {
Self::TsIdentifierBinding(binding.with_name_token(name_token))
}
Self::TsTypeParameterName(binding) => {
Self::TsTypeParameterName(binding.with_ident_token(name_token))
}
}
}
}
impl JsIdentifierBinding {
pub fn declaration(&self) -> Option<AnyJsBindingDeclaration> {
declaration(self.syntax.parent()?)
}
pub fn is_under_pattern_binding(&self) -> Option<bool> {
is_under_pattern_binding(self.syntax())
}
pub fn is_under_array_pattern_binding(&self) -> Option<bool> {
is_under_array_pattern_binding(self.syntax())
}
pub fn is_under_object_pattern_binding(&self) -> Option<bool> {
is_under_object_pattern_binding(self.syntax())
}
}
impl TsIdentifierBinding {
pub fn declaration(&self) -> Option<AnyJsBindingDeclaration> {
declaration(self.syntax.parent()?)
}
pub fn is_under_pattern_binding(&self) -> Option<bool> {
is_under_pattern_binding(self.syntax())
}
pub fn is_under_array_pattern_binding(&self) -> Option<bool> {
is_under_array_pattern_binding(self.syntax())
}
pub fn is_under_object_pattern_binding(&self) -> Option<bool> {
is_under_object_pattern_binding(self.syntax())
}
}
declare_node_union! {
pub JsAnyParameterParentFunction =
JsFunctionDeclaration
| JsFunctionExpression
| JsArrowFunctionExpression
| JsFunctionExportDefaultDeclaration
| JsConstructorClassMember
| JsMethodClassMember
| JsSetterClassMember
| JsMethodObjectMember
| JsSetterObjectMember
| TsFunctionType
| TsConstructorType
| TsDeclareFunctionDeclaration
| TsDeclareFunctionExportDefaultDeclaration
| TsConstructorSignatureClassMember
| TsMethodSignatureClassMember
| TsSetterSignatureClassMember
| TsIndexSignatureClassMember
| TsConstructSignatureTypeMember
| TsMethodSignatureTypeMember
| TsSetterSignatureTypeMember
| TsCallSignatureTypeMember
}
fn parent_function(node: &JsSyntaxNode) -> Option<JsAnyParameterParentFunction> {
let parent = node.parent()?;
match parent.kind() {
JsSyntaxKind::JS_PARAMETER_LIST => {
let parameters = JsParameterList::unwrap_cast(parent).parent::<JsParameters>()?;
let parent = parameters.syntax.parent()?;
JsAnyParameterParentFunction::cast(parent)
}
JsSyntaxKind::JS_CONSTRUCTOR_PARAMETER_LIST => {
let parameters = JsConstructorParameterList::unwrap_cast(parent)
.parent::<JsConstructorParameters>()?;
let parent = parameters.syntax().parent()?;
JsAnyParameterParentFunction::cast(parent)
}
_ => JsAnyParameterParentFunction::cast(parent),
}
}
impl JsFormalParameter {
pub fn parent_function(&self) -> Option<JsAnyParameterParentFunction> {
parent_function(&self.syntax)
}
}
impl JsRestParameter {
pub fn parent_function(&self) -> Option<JsAnyParameterParentFunction> {
parent_function(&self.syntax)
}
}
impl TsPropertyParameter {
pub fn parent_function(&self) -> Option<JsAnyParameterParentFunction> {
parent_function(&self.syntax)
}
}