diff --git a/crates/api_server/src/main.rs b/crates/api_server/src/main.rs index 925249d..704f678 100644 --- a/crates/api_server/src/main.rs +++ b/crates/api_server/src/main.rs @@ -3,7 +3,7 @@ use actix_web::{error, get, web, App, HttpServer, Result}; use dt_core::{ graph::used_by_graph::UsedByGraph, portable::Portable, - tracker::{DependencyTracker, ModuleSymbol, TraceTarget}, + tracker::{DependencyTracker, TraceTarget}, }; use serde::Serialize; use std::{ @@ -19,11 +19,16 @@ struct AppState { used_by_graph: UsedByGraph, } +#[derive(Serialize, Clone)] +struct Step { + module_path: String, + symbol_name: String, +} + #[derive(Serialize)] struct SearchResponse { project_root: String, - i18n_to_symbol: HashMap>, - trace_result: HashMap>>>, + trace_result: HashMap>>>>, } #[get("/search/{search}")] @@ -35,41 +40,83 @@ async fn search( // TODO: deal with search mode - match data.i18n_to_symbol.get(&search) { - None => Err(error::ErrorNotFound(format!("{} not found", search))), - Some(ts) => { - let mut dependency_tracker = DependencyTracker::new(&data.used_by_graph, true); - let mut trace_result: HashMap>>> = - HashMap::new(); - for (module_path, symbols) in ts { - trace_result.insert(module_path.to_owned(), HashMap::new()); + let mut dependency_tracker = DependencyTracker::new(&data.used_by_graph, true); + let matched_i18n_keys = vec![&search]; + let mut trace_result = HashMap::new(); + for i18n_key in matched_i18n_keys { + let mut route_to_paths = HashMap::new(); + if let Some(i18n_key_usage) = data.i18n_to_symbol.get(i18n_key) { + for (module_path, symbols) in i18n_key_usage { for symbol in symbols { let full_paths = dependency_tracker .trace((module_path.clone(), TraceTarget::LocalVar(symbol.clone()))) .unwrap(); - trace_result - .entry(module_path.to_owned()) - .and_modify(|symbol_table| { - symbol_table.insert(symbol.to_owned(), full_paths); - }); + // traverse each path and check if any symbol is used in some routes + for mut full_path in full_paths { + full_path.reverse(); + for (i, (step_module_path, step_trace_target)) in + full_path.iter().enumerate() + { + match step_trace_target { + TraceTarget::LocalVar(step_symbol_name) => { + if let Some(symbol_to_routes) = + data.symbol_to_route.get(step_module_path) + { + if let Some(routes) = symbol_to_routes.get(step_symbol_name) + { + let dependency_from_target_to_route: Vec = + full_path[0..i] + .iter() + .map(|(path, target)| Step { + module_path: path.clone(), + symbol_name: target.to_string(), + }) + .collect(); + for route in routes.iter() { + if !route_to_paths.contains_key(route) { + route_to_paths + .insert(route.clone(), HashMap::new()); + } + if !route_to_paths + .get(route) + .unwrap() + .contains_key(symbol) + { + route_to_paths + .get_mut(route) + .unwrap() + .insert(symbol.to_string(), vec![]); + } + route_to_paths + .get_mut(route) + .unwrap() + .get_mut(symbol) + .unwrap() + .push(dependency_from_target_to_route.clone()); + } + } + } + } + _ => (), + } + } + } } } - - Ok(web::Json(SearchResponse { - project_root: data.project_root.to_owned(), - i18n_to_symbol: ts.to_owned(), - trace_result, - })) } + trace_result.insert(i18n_key.to_string(), route_to_paths); } + + Ok(web::Json(SearchResponse { + project_root: data.project_root.to_owned(), + trace_result, + })) } #[actix_web::main] async fn main() -> std::io::Result<()> { // TODO: get portable path from args - // let mut file = File::open("")?; - let mut file = - File::open("/Users/linweitang/rust/js-symbol-dependency-tracker/outputs/sample.json")?; + let mut file = File::open("")?; let mut exported = String::new(); file.read_to_string(&mut exported)?; let portable = Portable::import(&exported).unwrap(); diff --git a/web/src/search/api-mock.ts b/web/src/search/api-mock.ts index 1d22320..7b96690 100644 --- a/web/src/search/api-mock.ts +++ b/web/src/search/api-mock.ts @@ -2,9 +2,9 @@ import { SearchResult } from "./api"; const mockedTraceResult = { "i18n.key.pikachu": { - "/account": [ + "/account": { // paths from Avatar to /account - [ + Avatar: [ // path 1 [ { @@ -56,14 +56,18 @@ const mockedTraceResult = { }, ], ], - ], + }, }, "i18n.key.pikapi": { - "/home": [ + "/home": { // paths from Header to /home - [ + Header: [ // path 1 [ + { + module_path: "module/path/can/be/super/long/too/bad/Header.tsx", + symbol_name: "Header", + }, { module_path: "module/path/can/be/super/long/too/bad/Layout.tsx", symbol_name: "Layout", @@ -72,18 +76,18 @@ const mockedTraceResult = { module_path: "module/path/can/be/super/long/too/bad/Home.tsx", symbol_name: "Home", }, - { - module_path: "module/path/can/be/super/long/too/bad/Header.tsx", - symbol_name: "Header", - }, ], ], - ], - "/account": [ + }, + "/account": { // paths from Header to /account - [ + Header: [ // path 1 [ + { + module_path: "module/path/can/be/super/long/too/bad/Header.tsx", + symbol_name: "Header", + }, { module_path: "module/path/can/be/super/long/too/bad/UserProfileHeader.tsx", @@ -98,13 +102,9 @@ const mockedTraceResult = { module_path: "module/path/can/be/super/long/too/bad/Account.tsx", symbol_name: "Account", }, - { - module_path: "module/path/can/be/super/long/too/bad/Header.tsx", - symbol_name: "Header", - }, ], ], - ], + }, }, }; diff --git a/web/src/search/components/TreeView.tsx b/web/src/search/components/TreeView.tsx index 9f33a22..d25c719 100644 --- a/web/src/search/components/TreeView.tsx +++ b/web/src/search/components/TreeView.tsx @@ -99,12 +99,12 @@ export const TreeView = React.memo(function TreeView({ itemId={`${i18nKey} - ${url}`} label={url} > - {traceTargets.map((traceTarget, i) => ( + {Object.entries(traceTargets).map(([traceTarget, paths]) => ( ))} diff --git a/web/src/search/shared/type.ts b/web/src/search/shared/type.ts index 415d379..f9ec709 100644 --- a/web/src/search/shared/type.ts +++ b/web/src/search/shared/type.ts @@ -6,9 +6,11 @@ export type ModuleSymbol = { symbol_name: string; }; export type TracePath = ModuleSymbol[]; -export type TraceTarget = TracePath[]; // Trace result might have multiple i18n keys, each i18n key might be // used in multiple module symbols, and each module symbol might be // used in multiple urls through different paths. -export type TraceResult = Record>; +export type TraceResult = Record< + I18nKey, + Record> +>; diff --git a/web/tsconfig.app.tsbuildinfo b/web/tsconfig.app.tsbuildinfo new file mode 100644 index 0000000..7a8f522 --- /dev/null +++ b/web/tsconfig.app.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/search/api-mock.ts","./src/search/api.ts","./src/search/index.tsx","./src/search/components/searchinput.tsx","./src/search/components/treeview.tsx","./src/search/shared/type.ts"],"version":"5.6.2"} \ No newline at end of file diff --git a/web/tsconfig.node.tsbuildinfo b/web/tsconfig.node.tsbuildinfo new file mode 100644 index 0000000..98ef2f9 --- /dev/null +++ b/web/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./vite.config.ts"],"version":"5.6.2"} \ No newline at end of file