From 5ce3d92b7db8b5bffbd0c3ce5184a7f0da6dc4af Mon Sep 17 00:00:00 2001 From: ityuany <519495771@qq.com> Date: Tue, 24 Dec 2024 15:31:23 +0800 Subject: [PATCH] Decoupling semantic_handler --- .vscode/settings.json | 1 + Cargo.lock | 1 + crates/binding/src/lib.rs | 8 +- crates/check_browser_supported/src/lib.rs | 114 +++-- crates/module_member_usage/Cargo.toml | 1 + crates/module_member_usage/src/lib.rs | 51 ++- .../src/{handler.rs => process.rs} | 406 ++++++------------ crates/module_member_usage/src/response.rs | 9 +- 8 files changed, 219 insertions(+), 372 deletions(-) rename crates/module_member_usage/src/{handler.rs => process.rs} (64%) diff --git a/.vscode/settings.json b/.vscode/settings.json index dcd13be..0759134 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,6 +19,7 @@ "insta", "instanceof", "isnan", + "itertools", "kosaraju", "msvc", "musleabihf", diff --git a/Cargo.lock b/Cargo.lock index dbc9656..2e0b43b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -967,6 +967,7 @@ dependencies = [ "napi-derive", "oxc_allocator", "oxc_ast", + "oxc_parser", "oxc_semantic", "oxc_span", "parking_lot", diff --git a/crates/binding/src/lib.rs b/crates/binding/src/lib.rs index 07dfc83..ffb86f5 100644 --- a/crates/binding/src/lib.rs +++ b/crates/binding/src/lib.rs @@ -105,7 +105,7 @@ pub async fn check_danger_strings( pub fn check_module_member_usage( npm_name_vec: Vec, args: GlobJsArgs, -) -> Result> { +) -> Result> { module_member_usage::check_module_member_usage(npm_name_vec, args.into()) .map_err(to_napi_error) } @@ -118,7 +118,7 @@ pub async fn check_filename_case( } #[napi] -pub fn check_browser_supported( +pub async fn check_browser_supported( target: check_browser_supported::Target, args: utils::GlobJsArgs, ) -> Result> { @@ -127,13 +127,15 @@ pub fn check_browser_supported( } #[napi] -pub fn check_browser_supported_with_source_code( +pub async fn check_browser_supported_with_source_code( target: check_browser_supported::Target, source_code: String, + file_path: String, ) -> Result> { check_browser_supported::check_browser_supported_with_source_code( target, source_code, + file_path, ) .map_err(to_napi_error) } diff --git a/crates/check_browser_supported/src/lib.rs b/crates/check_browser_supported/src/lib.rs index afc4d19..f970c9a 100644 --- a/crates/check_browser_supported/src/lib.rs +++ b/crates/check_browser_supported/src/lib.rs @@ -6,6 +6,8 @@ mod macros; mod operators; mod statements; +use std::path::Path; + use browserslist::{resolve, Distrib, Opts}; pub use compat::{CompatBox, CompatHandler}; @@ -14,8 +16,11 @@ use log::debug; use napi::Error; use napi_derive::napi; +use oxc_allocator::Allocator; +use oxc_parser::Parser; +use oxc_semantic::SemanticBuilder; use utils::{ - glob_by_semantic, GlobErrorHandler, GlobSuccessHandler, SemanticBuilder, + glob_by_semantic, source_type_from_path, GlobErrorHandler, GlobSuccessHandler, }; fn get_version_list<'a>( @@ -51,6 +56,7 @@ pub struct Target { pub fn check_browser_supported_with_source_code( target: Target, source_code: String, + file_path: String, ) -> Result> { debug!("User-specified browser target: {:?}", target); @@ -132,40 +138,52 @@ pub fn check_browser_supported_with_source_code( } } - let mut used: Vec = Vec::new(); + let allocator = Allocator::default(); - let builder = SemanticBuilder::js(&source_code); + let source_type = source_type_from_path(&Path::new(&file_path)); - let handler = match builder.build_handler() { - Ok(handler) => handler, - Err(e) => { - eprintln!("parse error: {}", e); - return Err(e); - } - }; + let parser = Parser::new(&allocator, &source_code, source_type); - handler.each_node(|handler, node| { - for compat_handler in compat_handlers.iter() { - if compat_handler.handle( - handler.semantic.source_text(), - node, - handler.semantic.nodes(), - ) { - let ast_node = beans::AstNode::with_source_and_ast_node( - handler.semantic.source_text(), - node, - ); - - used.push(CompatBox::new( - ast_node, - compat_handler.get_compat().clone(), - String::new(), - )); - } - } - }); + let parse = parser.parse(); + + let program = allocator.alloc(&parse.program); + + let semantic_return = SemanticBuilder::new() + .with_check_syntax_error(false) + // TODO 很多场景下是不需要开启的,只有 oxlint 下需要开启,这可能对性能会产生一定的影响 + .with_cfg(true) + .build(program); + + let nodes = semantic_return.semantic.nodes(); - Ok(used) + let nodes = nodes + .iter() + .map(|item| { + return compat_handlers + .iter() + .filter_map(|compat_handler| { + if compat_handler.handle( + semantic_return.semantic.source_text(), + item, + nodes, + ) { + let ast_node = + beans::AstNode::with_source_and_ast_node(&source_code, item); + Some(CompatBox::new( + ast_node, + compat_handler.get_compat().clone(), + String::new(), + )) + } else { + None + } + }) + .collect::>(); + }) + .flatten() + .collect::>(); + + Ok(nodes) } pub fn check_browser_supported( @@ -288,39 +306,6 @@ pub fn check_browser_supported( .flatten() .collect(); - // let responses = glob_by_path( - // |path| { - // let mut used: Vec = Vec::new(); - // let builder = SemanticBuilder::with_file(&path).unwrap(); - // let semantic = builder.build().unwrap(); - // for node in semantic.nodes().iter() { - // for compat_handler in compat_handlers.iter() { - // if compat_handler.handle( - // semantic.source_text(), - // node, - // semantic.nodes(), - // ) { - // let ast_node = beans::AstNode::with_source_and_ast_node( - // semantic.source_text(), - // node, - // ); - - // used.push(CompatBox::new( - // ast_node, - // compat_handler.get_compat().clone(), - // path.to_str().unwrap().to_string(), - // )); - // } - // } - // } - // Some(used) - // }, - // &args, - // )? - // .into_iter() - // .flatten() - // .collect(); - Ok(responses) } @@ -356,6 +341,7 @@ mod tests { deno: None, }, source_code, + "test.ts".to_string(), ); // Assert the result diff --git a/crates/module_member_usage/Cargo.toml b/crates/module_member_usage/Cargo.toml index bfa00f7..bc134cd 100644 --- a/crates/module_member_usage/Cargo.toml +++ b/crates/module_member_usage/Cargo.toml @@ -14,6 +14,7 @@ oxc_span = { workspace = true } oxc_ast = { workspace = true } oxc_semantic = { workspace = true } oxc_allocator = { workspace = true} +oxc_parser = { workspace = true } serde = { workspace = true } parking_lot = { workspace = true } anyhow = { workspace = true } diff --git a/crates/module_member_usage/src/lib.rs b/crates/module_member_usage/src/lib.rs index f3bb4e3..a3aed94 100644 --- a/crates/module_member_usage/src/lib.rs +++ b/crates/module_member_usage/src/lib.rs @@ -1,39 +1,38 @@ use anyhow::Result; -use handler::ModuleMemberUsageHandler; -use utils::{glob_by_path, SemanticBuilder}; +use process::process; +use response::ModuleMemberUsageResponse; use utils::{glob_by_semantic, GlobArgs}; +use utils::{GlobErrorHandler, GlobSuccessHandler}; -mod handler; -mod response; -pub use response::ModuleMemberUsageResponse; +mod process; +pub mod response; pub fn check_module_member_usage( npm_name_vec: Vec, args: GlobArgs, ) -> Result> { - let responses = glob_by_path( - |path| { - let builder = SemanticBuilder::with_file(&path).unwrap(); - - let handler = match builder.build_handler() { - Ok(handler) => handler, - Err(e) => { - eprintln!("parse error: {}", e); - return None; - } - }; - - let inline_usages = - ModuleMemberUsageHandler::new(npm_name_vec.clone(), path, handler) - .handle(); - - Some(inline_usages) + let responses = glob_by_semantic( + |GlobSuccessHandler { + semantic, + relative_path, + .. + }| { + let responses = process(&semantic, &npm_name_vec); + Some(ModuleMemberUsageResponse { + file_path: relative_path.clone(), + items: responses, + errors: vec![], + }) + }, + |GlobErrorHandler { relative_path, .. }| { + Some(ModuleMemberUsageResponse { + file_path: relative_path.clone(), + items: vec![], + errors: vec![], + }) }, &args, - )? - .into_iter() - .flatten() - .collect(); + )?; Ok(responses) } diff --git a/crates/module_member_usage/src/handler.rs b/crates/module_member_usage/src/process.rs similarity index 64% rename from crates/module_member_usage/src/handler.rs rename to crates/module_member_usage/src/process.rs index 0399cbe..fa9238a 100644 --- a/crates/module_member_usage/src/handler.rs +++ b/crates/module_member_usage/src/process.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use beans::AstNode; use oxc_ast::{ ast::{ @@ -10,11 +8,10 @@ use oxc_ast::{ }; use oxc_semantic::{Reference, Semantic}; -use utils::SemanticHandler; use crate::response::JSXProps; -use super::response::ModuleMemberUsageResponse; +use super::response::ModuleMemberUsageResponseItem; static ES_NAMESPACE: &str = "ES:NAMESPACE"; static ES_DEFAULT: &str = "ES:DEFAULT"; @@ -25,39 +22,10 @@ static UNKNOWN: &str = "UNKNOWN"; static NOT_IMPLEMENTED: &str = "NOT_IMPLEMENTED"; static SPREAD_ATTRIBUTE: &str = "SPREAD_ATTRIBUTE"; -pub struct ModuleMemberUsageHandler<'a> { - npm_name_vec: Vec, - semantic_handler: SemanticHandler<'a>, - path_str: String, -} - -impl<'a> ModuleMemberUsageHandler<'a> { - pub fn new>( - npm_name_vec: Vec, - path: P, - semantic_handler: SemanticHandler<'a>, - ) -> Self { - Self { - npm_name_vec, - semantic_handler, - path_str: path.as_ref().display().to_string(), - } - } - - pub fn handle(&self) -> Vec { - process( - &self.semantic_handler.semantic, - &self.npm_name_vec, - &self.path_str, - ) - } -} - pub fn process<'a>( semantic: &'a Semantic, npm_name_vec: &Vec, - relative_path: &str, -) -> Vec { +) -> Vec { semantic .nodes() .iter() @@ -81,10 +49,9 @@ pub fn process<'a>( let specifiers = match decl.specifiers { Some(ref specs) => specs, None => { - return Some(vec![ModuleMemberUsageResponse { + return Some(vec![ModuleMemberUsageResponseItem { lib_name: source_name, member_name: SIDE_EFFECTS.to_string(), - file_path: relative_path.to_string(), ast_node, props: vec![], }]); @@ -92,17 +59,15 @@ pub fn process<'a>( }; if specifiers.is_empty() { - return Some(vec![ModuleMemberUsageResponse { + return Some(vec![ModuleMemberUsageResponseItem { lib_name: source_name, member_name: EMPTY_SPECIFIERS.to_string(), - file_path: relative_path.to_string(), ast_node, props: vec![], }]); } - let responses = - each_specifiers(semantic, &source_name, &relative_path, specifiers); + let responses = each_specifiers(semantic, &source_name, specifiers); Some(responses) }) @@ -113,9 +78,8 @@ pub fn process<'a>( fn each_specifiers<'a>( semantic: &'a Semantic, source_name: &str, - relative_path: &str, specifiers: &oxc_allocator::Vec>, -) -> Vec { +) -> Vec { let responses = specifiers .iter() .map(|spec| { @@ -160,10 +124,9 @@ fn each_specifiers<'a>( let attributes = get_jsx_props(kind); - return Some(ModuleMemberUsageResponse { + return Some(ModuleMemberUsageResponseItem { lib_name: source_name.to_string(), member_name: name.to_string(), - file_path: relative_path.to_string(), ast_node: ast_node, props: attributes, }); @@ -198,19 +161,17 @@ fn each_specifiers<'a>( private_field_expression, ) => private_field_expression.field.name.to_string(), }; - return Some(ModuleMemberUsageResponse { + return Some(ModuleMemberUsageResponseItem { lib_name: source_name.to_string(), member_name: name.to_string(), - file_path: relative_path.to_string(), ast_node: ast_node, props: vec![], }); } - return Some(ModuleMemberUsageResponse { + return Some(ModuleMemberUsageResponseItem { lib_name: source_name.to_string(), member_name: imported_name.to_string(), - file_path: relative_path.to_string(), ast_node: ast_node, props: vec![], }); @@ -361,33 +322,54 @@ fn get_jsx_props<'a>(kind: &JSXOpeningElement) -> Vec { #[cfg(test)] mod tests { - use super::ModuleMemberUsageHandler; use itertools::Itertools; - use std::{collections::HashMap, path::PathBuf}; - use utils::SemanticBuilder; + use oxc_allocator::Allocator; + use oxc_parser::Parser; + use oxc_semantic::SemanticBuilder; + use oxc_span::SourceType; + use std::collections::HashMap; + + use crate::{process::process, response::ModuleMemberUsageResponseItem}; + + fn util( + npm_name_vec: Vec, + source_code: &str, + ) -> Vec { + let allocator = Allocator::default(); + + let parser = Parser::new(&allocator, source_code, SourceType::jsx()); + + let parse = parser.parse(); + + let program = allocator.alloc(&parse.program); + + let semantic_return = SemanticBuilder::new() + .with_check_syntax_error(false) + // TODO 很多场景下是不需要开启的,只有 oxlint 下需要开启,这可能对性能会产生一定的影响 + .with_cfg(true) + .build(program); + + let result = process(&semantic_return.semantic, &npm_name_vec); + + result + } #[test] fn test_jsx_props() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( - &r#" - import {Form} from 'shineout'; - function App(){ - return ( -
- -
- ) - } - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( + let result = util( vec!["shineout".to_string()], - file_path_str, - semantic_handler.unwrap(), + &r#" + import {Form} from 'shineout'; + function App(){ + return ( +
+ +
+ ) + } + "#, ); - let result = handler.handle(); + let grouped = result .iter() .group_by(|item| item.member_name.clone()) @@ -407,145 +389,97 @@ mod tests { #[test] fn test_form_item() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( - &r#" - import {Form} from 'shineout'; - function App(){ - return ( -
- -
- ) - } - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( + let result = util( vec!["shineout".to_string()], - file_path_str, - semantic_handler.unwrap(), + &r#" + import {Form} from 'shineout'; + function App(){ + return ( +
+ +
+ ) + } + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 2); } #[test] fn test_computed_member_with_template() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["lib".to_string()], &r#" - import lib from 'lib'; + import lib from 'lib'; const name = 'method'; lib[`get${name}`](); - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["lib".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); + assert_eq!(result.len(), 1); } #[test] fn test_jsx_self_closing() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["lib".to_string()], &r#" import { Component } from 'lib'; function App() { return ; } - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["lib".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 1); } #[test] fn test_multiple_references() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["lib".to_string()], &r#" import { method } from 'lib'; method(); method(); method(); - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["lib".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 3); } #[test] fn test_empty_import_specifiers() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( - &r#" - import {} from 'react'; - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( + let result = util( vec!["react".to_string()], - file_path_str, - semantic_handler.unwrap(), + &r#" + import {} from 'react'; + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 1); } #[test] fn test_dynamic_computed_member() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( - &r#" - import lib from 'lib'; - const prop = 'method'; - const result = lib[prop](); - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( + let result = util( vec!["lib".to_string()], - file_path_str, - semantic_handler.unwrap(), + &r#" + import lib from 'lib'; + const prop = 'method'; + const result = lib[prop](); + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 1); } #[test] fn test_nested_member_expression() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["lib".to_string()], &r#" import lib from 'lib'; const result = lib.a.b.c; - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["lib".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "lib"); assert_eq!(result[0].member_name, "a"); @@ -553,22 +487,15 @@ mod tests { #[test] fn test_mixed_import_types() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["module".to_string()], &r#" import def, { named1, named2 as alias } from 'module'; def.method(); named1(); alias(); - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["module".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 3); assert_eq!(result[0].member_name, "method"); assert_eq!(result[1].member_name, "named1"); @@ -577,8 +504,8 @@ mod tests { #[test] fn test_jsx_member_expression_with_alias() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["module".to_string()], &r#" import def, { named1, named2 as alias } from 'module'; def.method(); @@ -586,15 +513,8 @@ mod tests { function App(){ return {named1} } - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["module".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); let grouped = result .into_iter() .group_by(|item| item.member_name.clone()) @@ -610,44 +530,28 @@ mod tests { #[test] fn test_jsx_nested_member_expression() { - let file_path_str = PathBuf::from("file_path_str"); - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["lib".to_string()], &r#" import * as Lib from 'lib'; function Component() { return ; } - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["lib".to_string()], - file_path_str, - semantic_handler.unwrap(), + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].member_name, "Nested"); } #[test] fn test_private_field_expression() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["a".to_string()], &r#" - import a from 'a'; + import a from 'a'; const c = a.#name; "#, ); - let semantic_handler = semantic_builder.build_handler(); - - let handler = ModuleMemberUsageHandler::new( - vec!["a".to_string()], - file_path_str, - semantic_handler.unwrap(), - ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "a"); @@ -656,22 +560,13 @@ mod tests { #[test] fn test_computed_member_expression() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["a".to_string()], &r#" - import a from 'a'; - const c = a["b"]; + import a from 'a'; + const c = a["b"]; "#, ); - let semantic_handler = semantic_builder.build_handler(); - - let handler = ModuleMemberUsageHandler::new( - vec!["a".to_string()], - file_path_str, - semantic_handler.unwrap(), - ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "a"); @@ -680,25 +575,16 @@ mod tests { #[test] fn test_import_specifier() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( - &r#" - import { useState } from 'react'; - function Component() { - const [state, setState] = useState(0); - return
{state}
; - } - "#, - ); - let semantic_handler = semantic_builder.build_handler(); - - let handler = ModuleMemberUsageHandler::new( + let result = util( vec!["react".to_string()], - file_path_str, - semantic_handler.unwrap(), + &r#" + import { useState } from 'react'; + function Component() { + const [state, setState] = useState(0); + return
{state}
; + } + "#, ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "react"); @@ -707,25 +593,15 @@ mod tests { #[test] fn test_import_default_specifier() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["react".to_string()], &r#" - import React from 'react'; - function Component() { - return Hello; - } + import React from 'react'; + function Component() { + return Hello; + } "#, ); - let semantic_handler = semantic_builder.build_handler(); - - let handler = ModuleMemberUsageHandler::new( - vec!["react".to_string()], - file_path_str, - semantic_handler.unwrap(), - ); - let result = handler.handle(); - assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "react"); assert_eq!(result[0].member_name, "Fragment"); @@ -733,24 +609,15 @@ mod tests { #[test] fn test_import_namespace_specifier() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["react".to_string()], &r#" - import * as React from 'react'; - function Component() { - return Hello; - } + import * as React from 'react'; + function Component() { + return Hello; + } "#, ); - let semantic_handler = semantic_builder.build_handler(); - - let handler = ModuleMemberUsageHandler::new( - vec!["react".to_string()], - file_path_str, - semantic_handler.unwrap(), - ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "react"); @@ -759,21 +626,12 @@ mod tests { #[test] fn test_side_effects_import() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["react".to_string()], &r#" - import 'react'; + import 'react'; "#, ); - let semantic_handler = semantic_builder.build_handler(); - - let handler = ModuleMemberUsageHandler::new( - vec!["react".to_string()], - file_path_str, - semantic_handler.unwrap(), - ); - let result = handler.handle(); assert_eq!(result.len(), 1); assert_eq!(result[0].lib_name, "react"); @@ -782,11 +640,10 @@ mod tests { #[test] fn test_multiple_imports() { - let file_path_str = PathBuf::from("file_path_str"); - - let semantic_builder = SemanticBuilder::js( + let result = util( + vec!["react".to_string(), "react-dom".to_string()], &r#" - import React, { useState } from 'react'; + import React, { useState } from 'react'; import * as ReactDOM from 'react-dom'; function Component() { const [state, setState] = useState(0); @@ -795,13 +652,6 @@ mod tests { ReactDOM.render(, document.getElementById('root')); "#, ); - let semantic_handler = semantic_builder.build_handler(); - let handler = ModuleMemberUsageHandler::new( - vec!["react".to_string(), "react-dom".to_string()], - file_path_str, - semantic_handler.unwrap(), - ); - let result = handler.handle(); assert_eq!(result.len(), 3); assert!(result diff --git a/crates/module_member_usage/src/response.rs b/crates/module_member_usage/src/response.rs index 2fc6495..889302a 100644 --- a/crates/module_member_usage/src/response.rs +++ b/crates/module_member_usage/src/response.rs @@ -5,9 +5,16 @@ use serde::Serialize; #[napi(object)] #[derive(Debug, Serialize, Clone)] pub struct ModuleMemberUsageResponse { + pub file_path: String, + pub items: Vec, + pub errors: Vec, +} + +#[napi(object)] +#[derive(Debug, Serialize, Clone)] +pub struct ModuleMemberUsageResponseItem { pub lib_name: String, pub member_name: String, - pub file_path: String, pub ast_node: AstNode, pub props: Vec, }