relay-knowledge 1.1.9

Graph-database-based knowledge graph project.
Documentation
use tree_sitter::Node;

use crate::code::parser::{
    imports::quoted_specifier,
    nodes::{SyntaxRange, compact_whitespace, node_text, syntax_range},
};

pub(in crate::code::parser) fn dynamic_import(
    language_id: &str,
    content: &str,
    node: Node<'_>,
) -> Option<(String, SyntaxRange)> {
    if !matches!(language_id, "javascript" | "jsx" | "typescript" | "tsx")
        || node.kind() != "call_expression"
    {
        return None;
    }
    let function = node.child_by_field_name("function")?;
    if function.kind() != "import" {
        return None;
    }
    first_direct_string_argument(content, node.child_by_field_name("arguments")?)?;
    let source = dynamic_import_source(content, node);

    Some(source)
}

fn dynamic_import_source(content: &str, node: Node<'_>) -> (String, SyntaxRange) {
    let source_node = node
        .parent()
        .filter(|parent| parent.kind() == "await_expression")
        .unwrap_or(node);

    (
        compact_whitespace(&node_text(content, source_node)),
        syntax_range(source_node),
    )
}

fn first_direct_string_argument(content: &str, arguments: Node<'_>) -> Option<String> {
    for index in 0..arguments.named_child_count() {
        let Ok(index) = u32::try_from(index) else {
            continue;
        };
        let child = arguments.named_child(index)?;
        if matches!(child.kind(), "string" | "string_literal") {
            return Some(node_text(content, child));
        }
        return None;
    }

    None
}

pub(in crate::code::parser) fn re_export(
    language_id: &str,
    content: &str,
    node: Node<'_>,
) -> Option<(String, SyntaxRange)> {
    if !matches!(language_id, "javascript" | "jsx" | "typescript" | "tsx")
        || node.kind() != "export_statement"
    {
        return None;
    }
    let statement = compact_whitespace(&node_text(content, node));
    if !export_has_module_specifier(&statement) {
        return None;
    }

    Some((statement, syntax_range(node)))
}

fn export_has_module_specifier(statement: &str) -> bool {
    let Some(body) = statement
        .trim()
        .trim_end_matches(';')
        .trim()
        .strip_prefix("export ")
    else {
        return false;
    };
    body.rsplit_once(" from ")
        .is_some_and(|(_, module)| quoted_specifier(module).is_some())
}