ast_grep_napi/
lib.rs

1#![cfg(not(feature = "napi-noop-in-unit-test"))]
2
3mod doc;
4mod find_files;
5mod napi_lang;
6mod sg_node;
7
8use ast_grep_core::{AstGrep, Language};
9use ast_grep_language::SupportLang;
10use napi::bindgen_prelude::*;
11use napi_derive::napi;
12use napi_lang::register_dynamic_language as register_dynamic_language_impl;
13
14use doc::{JsDoc, NapiConfig};
15use find_files::{find_in_files_impl, FindConfig, FindInFiles, ParseAsync};
16use napi_lang::NapiLang;
17use sg_node::SgRoot;
18
19pub use find_files::parse_files;
20
21macro_rules! impl_lang_mod {
22  ($name: ident, $lang: ident) => {
23    #[napi]
24    pub mod $name {
25      use super::*;
26
27      #[napi]
28      pub fn parse(src: String) -> SgRoot {
29        parse_with_lang(SupportLang::$lang.to_string(), src).expect("parse failed")
30      }
31
32      #[napi]
33      pub fn parse_async(src: String) -> Result<AsyncTask<ParseAsync>> {
34        parse_async_with_lang(SupportLang::$lang.to_string(), src)
35      }
36      #[napi]
37      pub fn kind(kind_name: String) -> Result<u16> {
38        kind_with_lang(SupportLang::$lang.to_string(), kind_name)
39      }
40      #[napi]
41      pub fn pattern(pattern: String) -> NapiConfig {
42        pattern_with_lang(SupportLang::$lang.to_string(), pattern)
43      }
44      #[napi]
45      pub fn find_in_files(
46        config: FindConfig,
47        callback: JsFunction,
48      ) -> Result<AsyncTask<FindInFiles>> {
49        find_in_files_impl(SupportLang::$lang.into(), config, callback)
50      }
51    }
52  };
53}
54
55// for name conflict in mod
56use kind as kind_with_lang;
57use parse as parse_with_lang;
58use parse_async as parse_async_with_lang;
59use pattern as pattern_with_lang;
60impl_lang_mod!(html, Html);
61impl_lang_mod!(js, JavaScript);
62impl_lang_mod!(jsx, JavaScript);
63impl_lang_mod!(ts, TypeScript);
64impl_lang_mod!(tsx, Tsx);
65impl_lang_mod!(css, Css);
66
67/// Parse a string to an ast-grep instance
68#[napi]
69pub fn parse(lang: String, src: String) -> Result<SgRoot> {
70  let doc = JsDoc::new(src, lang.parse()?);
71  Ok(SgRoot(AstGrep::doc(doc), "anonymous".into()))
72}
73
74/// Parse a string to an ast-grep instance asynchronously in threads.
75/// It utilize multiple CPU cores when **concurrent processing sources**.
76/// However, spawning excessive many threads may backfire.
77/// Please refer to libuv doc, nodejs' underlying runtime
78/// for its default behavior and performance tuning tricks.
79#[napi]
80pub fn parse_async(lang: String, src: String) -> Result<AsyncTask<ParseAsync>> {
81  let lang = lang.parse()?;
82  Ok(AsyncTask::new(ParseAsync { src, lang }))
83}
84
85/// Get the `kind` number from its string name.
86#[napi]
87pub fn kind(lang: String, kind_name: String) -> Result<u16> {
88  let lang: NapiLang = lang.parse()?;
89  let kind = lang
90    .get_ts_language()
91    .id_for_node_kind(&kind_name, /* named */ true);
92  Ok(kind)
93}
94
95/// Compile a string to ast-grep Pattern.
96#[napi]
97pub fn pattern(lang: String, pattern: String) -> NapiConfig {
98  NapiConfig {
99    rule: serde_json::json!({
100      "pattern": pattern,
101    }),
102    constraints: None,
103    language: Some(lang),
104    utils: None,
105    transform: None,
106  }
107}
108
109/// Discover and parse multiple files in Rust.
110/// `lang` specifies the language.
111/// `config` specifies the file path and matcher.
112/// `callback` will receive matching nodes found in a file.
113#[napi]
114pub fn find_in_files(
115  lang: String,
116  config: FindConfig,
117  callback: JsFunction,
118) -> Result<AsyncTask<FindInFiles>> {
119  let lang: NapiLang = lang.parse()?;
120  find_in_files_impl(lang, config, callback)
121}
122
123/// Register a dynamic language to ast-grep.
124/// `langs` is a Map of language name to its CustomLanguage registration.
125#[napi]
126pub fn register_dynamic_language(langs: serde_json::Value) -> Result<()> {
127  let langs = serde_json::from_value(langs)?;
128  register_dynamic_language_impl(langs)
129}