fallow-cli 2.100.0

CLI for fallow, Rust-native codebase intelligence for TypeScript and JavaScript
Documentation
#![allow(
    clippy::unwrap_used,
    clippy::expect_used,
    reason = "integration tests keep fixture setup concise"
)]

#[path = "common/mod.rs"]
mod common;

use common::{parse_json, run_fallow_in_root};
use tempfile::tempdir;

fn write_project(root: &std::path::Path) {
    std::fs::create_dir_all(root.join("src")).unwrap();
    std::fs::write(
        root.join("package.json"),
        r#"{"name":"inspect-fixture","type":"module"}"#,
    )
    .unwrap();
    std::fs::write(root.join("tsconfig.json"), r#"{"include":["src"]}"#).unwrap();
    std::fs::write(root.join(".fallowrc.json"), r#"{"entry":["src/index.ts"]}"#).unwrap();
    std::fs::write(
        root.join("src/index.ts"),
        "import { fetchUser } from './api';\nexport const boot = () => fetchUser('1');\n",
    )
    .unwrap();
    std::fs::write(
        root.join("src/api.ts"),
        "export const fetchUser = (id: string) => ({ id });\n",
    )
    .unwrap();
}

#[test]
fn inspect_file_outputs_typed_evidence_bundle() {
    let dir = tempdir().unwrap();
    write_project(dir.path());

    let output = run_fallow_in_root(
        "inspect",
        dir.path(),
        &["--file", "src/api.ts", "--format", "json", "--quiet"],
    );
    assert_eq!(output.code, 0, "inspect should exit 0: {}", output.stderr);

    let json = parse_json(&output);
    assert_eq!(json["kind"].as_str(), Some("inspect_target"));
    assert_eq!(json["target"]["type"].as_str(), Some("file"));
    assert_eq!(json["target"]["file"].as_str(), Some("src/api.ts"));
    assert_eq!(json["identity"]["file"].as_str(), Some("src/api.ts"));
    assert_eq!(
        json["evidence"]["trace_file"]["status"].as_str(),
        Some("ok")
    );
    assert_eq!(
        json["evidence"]["dead_code"]["scope"].as_str(),
        Some("file")
    );
    assert_eq!(
        json["evidence"]["duplication"]["scope"].as_str(),
        Some("project_filtered_to_file")
    );
}

#[test]
fn inspect_file_accepts_absolute_path_inside_root() {
    let dir = tempdir().unwrap();
    write_project(dir.path());

    let file = dir.path().join("src/api.ts");
    let output = run_fallow_in_root(
        "inspect",
        dir.path(),
        &[
            "--file",
            file.to_str().unwrap(),
            "--format",
            "json",
            "--quiet",
        ],
    );
    assert_eq!(output.code, 0, "inspect should exit 0: {}", output.stderr);

    let json = parse_json(&output);
    assert_eq!(json["target"]["file"].as_str(), Some("src/api.ts"));
    assert_eq!(json["identity"]["file"].as_str(), Some("src/api.ts"));
}

#[test]
fn inspect_symbol_outputs_trace_export_section() {
    let dir = tempdir().unwrap();
    write_project(dir.path());

    let output = run_fallow_in_root(
        "inspect",
        dir.path(),
        &[
            "--symbol",
            "src/api.ts:fetchUser",
            "--format",
            "json",
            "--quiet",
        ],
    );
    assert_eq!(output.code, 0, "inspect should exit 0: {}", output.stderr);

    let json = parse_json(&output);
    assert_eq!(json["kind"].as_str(), Some("inspect_target"));
    assert_eq!(json["target"]["type"].as_str(), Some("symbol"));
    assert_eq!(json["target"]["export_name"].as_str(), Some("fetchUser"));
    assert_eq!(json["identity"]["export_name"].as_str(), Some("fetchUser"));
    assert_eq!(
        json["evidence"]["trace_export"]["status"].as_str(),
        Some("ok")
    );
    assert!(
        json["warnings"]
            .as_array()
            .is_some_and(|items| !items.is_empty())
    );
}

#[test]
fn inspect_human_output_includes_evidence_summary() {
    let dir = tempdir().unwrap();
    write_project(dir.path());

    let output = run_fallow_in_root("inspect", dir.path(), &["--file", "src/api.ts"]);
    assert_eq!(output.code, 0, "inspect should exit 0: {}", output.stderr);

    assert!(output.stdout.contains("Evidence"));
    assert!(output.stdout.contains("trace_file: ok [file]"));
    assert!(
        output
            .stdout
            .contains("duplication: ok [project filtered to file]")
    );
}