Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,11 @@ Text output is optimized for terminal inspection, including the resolved
selector kinds, match counts, and a reachability summary that helps explain
no-path results. JSON output includes the same diagnostics, stable file/export
nodes, and typed import or re-export edges. Pass `--include-unresolved` when
you also need unresolved imports discovered while building the source graph.
Mermaid output renders the same query graph as a `flowchart LR` diagram, and
SVG output renders that Mermaid source with the pure-Rust
`mermaid-rs-renderer` crate.
you also need every unresolved import discovered while building the source
graph, or `--include-unresolved=related` to limit diagnostics to unresolved
imports from the query endpoints and returned path graph. Mermaid output renders
the same query graph as a `flowchart LR` diagram, and SVG output renders that
Mermaid source with the pure-Rust `mermaid-rs-renderer` crate.

`somepath` uses breadth-first search with visited nodes, while `allpaths`
intersects forward reachability from the source with reverse reachability from
Expand Down
42 changes: 42 additions & 0 deletions crates/codescythe_cli/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,11 @@ fn cli_query_includes_unresolved_imports_when_requested() {
)
.unwrap();
fs::write(fixture.join("src/module.ts"), "export const used = 1;\n").unwrap();
fs::write(
fixture.join("src/unrelated.ts"),
"import { unrelated } from '#generated/unrelated';\nconsole.log(unrelated);\n",
)
.unwrap();

let default_output = Command::new(&cli)
.args([
Expand Down Expand Up @@ -445,6 +450,43 @@ fn cli_query_includes_unresolved_imports_when_requested() {
included_query["unresolved"][0]["specifier"],
"#generated/missing"
);
assert_eq!(
included_query["unresolved"][1]["specifier"],
"#generated/unrelated"
);

let related_output = Command::new(&cli)
.args([
"query",
"somepath",
"-C",
path_arg(&fixture),
"--json",
"--include-unresolved=related",
"src/main.ts",
"src/module.ts:used",
])
.output()
.expect("failed to run codescythe query with related unresolved imports");

assert!(
related_output.status.success(),
"{}",
output_text(&related_output)
);
let related_query: Value =
serde_json::from_slice(&related_output.stdout).expect("query stdout should be JSON");
assert_eq!(
related_query["unresolved"][0]["specifier"],
"#generated/missing"
);
assert_eq!(
related_query["unresolved"]
.as_array()
.expect("unresolved should be an array")
.len(),
1
);

fs::remove_dir_all(&fixture).unwrap();
}
Expand Down
56 changes: 51 additions & 5 deletions crates/codescythe_cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,15 @@ struct QueryPathArgs {
#[arg(long)]
json: bool,

#[arg(long, help = "Include unresolved import diagnostics in JSON output")]
include_unresolved: bool,
#[arg(
long,
value_enum,
num_args = 0..=1,
require_equals = true,
default_missing_value = "all",
help = "Include unresolved import diagnostics in JSON output: all or related"
)]
include_unresolved: Option<QueryUnresolvedScope>,

#[arg(long, value_enum, default_value_t = QueryOutputFormat::Text)]
output: QueryOutputFormat,
Expand All @@ -102,6 +109,12 @@ enum QueryOutputFormat {
Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
enum QueryUnresolvedScope {
All,
Related,
}

fn main() -> ExitCode {
match run() {
Ok(has_issues) => {
Expand Down Expand Up @@ -250,9 +263,7 @@ fn run_query_command(
} else {
args.output
};
if !args.include_unresolved {
result.unresolved.clear();
}
filter_query_unresolved(&mut result, args.include_unresolved);
match output {
QueryOutputFormat::Json => {
let started = start_profile_timer();
Expand All @@ -272,6 +283,41 @@ fn run_query_command(
Ok(false)
}

fn filter_query_unresolved(
result: &mut codescythe::QueryResult,
include_unresolved: Option<QueryUnresolvedScope>,
) {
match include_unresolved {
Some(QueryUnresolvedScope::All) => {}
Some(QueryUnresolvedScope::Related) => {
let related_paths = query_related_paths(result);
result
.unresolved
.retain(|unresolved| related_paths.contains(&unresolved.importer));
}
None => result.unresolved.clear(),
}
}

fn query_related_paths(result: &codescythe::QueryResult) -> std::collections::BTreeSet<String> {
let mut paths = result
.source_nodes
.iter()
.chain(&result.target_nodes)
.map(|node| node.path.clone())
.collect::<std::collections::BTreeSet<_>>();

if let Some(graph) = &result.graph {
paths.extend(graph.nodes.iter().map(|node| node.path.clone()));
return paths;
}

for path in &result.paths {
paths.extend(path.nodes.iter().map(|node| node.path.clone()));
}
paths
}

#[cfg(feature = "profiling")]
struct CliProfileTimer(Option<Instant>);

Expand Down
12 changes: 11 additions & 1 deletion docs/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,14 @@ npx codescythe query allpaths \\
h('code', null, 'mermaid-rs-renderer'),
'.',
),
h(
'p',
null,
h('code', null, '--include-unresolved'),
' adds every unresolved import discovered while building the graph. Use ',
h('code', null, '--include-unresolved=related'),
' when you only want unresolved imports from the query endpoints and returned path graph files.',
),
h(CodeBlock, null, `From selector: src/main.ts (file selector, 1 matched node)
To selector: src/module.ts:used (export selector, 1 matched node)
src/main.ts
Expand Down Expand Up @@ -1175,7 +1183,9 @@ npx codescythe --json --explain-export src/constants.ts:oldFlag`),
null,
'Pass ',
h('code', null, '--include-unresolved'),
' to include the full unresolved import diagnostics array in JSON output.',
' to include the full unresolved import diagnostics array in JSON output. Use ',
h('code', null, '--include-unresolved=related'),
' to limit that array to imports from files that are part of the query endpoints or returned path graph.',
),
),
h(
Expand Down