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
7 changes: 4 additions & 3 deletions crates/codescythe/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ pub fn analyze_path(
config: &CodescytheConfig,
options: AnalysisOptions,
) -> Result<Analysis> {
let cwd = cwd
.canonicalize()
.with_context(|| format!("failed to resolve {}", cwd.display()))?;
let cwd = normalize_path(cwd);
if !cwd.exists() {
anyhow::bail!("analysis root does not exist: {}", cwd.display());
}
let project_files = discover_project_files(&cwd, config)?;
let entry_files = discover_entry_files(&cwd, config, &project_files)?;
let entry_set = entry_files.iter().cloned().collect::<HashSet<_>>();
Expand Down
14 changes: 14 additions & 0 deletions crates/codescythe_cli/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load("@rules_rs//rs:rust_binary.bzl", "rust_binary")
load("@rules_rs//rs:rust_test.bzl", "rust_test")
load(
":release_binary.bzl",
"release_binary_darwin_arm64",
Expand All @@ -20,6 +21,19 @@ rust_binary(
],
)

rust_test(
name = "codescythe_cli_test",
srcs = ["main.rs"],
crate_name = "codescythe_cli_test",
edition = "2024",
deps = [
"//crates/codescythe",
"@crates//:anyhow",
"@crates//:clap",
"@crates//:serde_json",
],
)

release_binary_linux_amd64(
name = "release_binary_linux_amd64",
srcs = [":codescythe"],
Expand Down
52 changes: 48 additions & 4 deletions crates/codescythe_cli/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::{path::PathBuf, process::ExitCode};
use std::{
env,
path::{Path, PathBuf},
process::ExitCode,
};

use anyhow::Result;
use clap::Parser;
Expand All @@ -9,8 +13,8 @@ struct Args {
#[arg(short, long)]
config: Option<PathBuf>,

#[arg(short = 'C', long, default_value = ".")]
directory: PathBuf,
#[arg(short = 'C', long)]
directory: Option<PathBuf>,

#[arg(long)]
fix: bool,
Expand Down Expand Up @@ -40,8 +44,8 @@ fn main() -> ExitCode {

fn run() -> Result<bool> {
let args = Args::parse();
let cwd = args.directory.canonicalize()?;
let config = args.config.as_deref();
let cwd = analysis_root(args.directory.as_deref(), config)?;

if args.fix {
let result = codescythe::run_and_fix(&cwd, config)?;
Expand Down Expand Up @@ -119,3 +123,43 @@ fn print_text_report(analysis: &codescythe::Analysis) {
}
}
}

fn analysis_root(directory: Option<&Path>, config: Option<&Path>) -> Result<PathBuf> {
if let Some(directory) = directory {
return Ok(directory.to_path_buf());
}

if let Some(parent) = config
.and_then(Path::parent)
.filter(|parent| !parent.as_os_str().is_empty())
{
return Ok(parent.to_path_buf());
}

Ok(env::current_dir()?)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn derives_analysis_root_from_config_parent_without_directory() {
let root = Path::new("/tmp/runfiles/_main");
let config = root.join("codescythe.json");

let analysis_root = analysis_root(None, Some(&config)).unwrap();

assert_eq!(analysis_root, root);
}

#[test]
fn explicit_directory_overrides_config_parent() {
let directory = Path::new("/tmp/runfiles/_main");
let config = Path::new("/tmp/runfiles/_main/pplx/frontend/codescythe.json");

let analysis_root = analysis_root(Some(directory), Some(config)).unwrap();

assert_eq!(analysis_root, directory);
}
}
13 changes: 9 additions & 4 deletions crates/codescythe_napi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ pub struct RunOptions {
#[napi]
pub fn analyze(options: Option<RunOptions>) -> napi::Result<String> {
let options = options.unwrap_or_default();
let cwd = cwd(options.cwd)?;
let config = options.config.as_deref().map(PathBuf::from);
let cwd = cwd(options.cwd, config.as_deref())?;
let analysis = codescythe::run(&cwd, config.as_deref()).map_err(to_napi_error)?;
serde_json::to_string(&analysis).map_err(to_napi_error)
}

#[napi]
pub fn fix(options: Option<RunOptions>) -> napi::Result<String> {
let options = options.unwrap_or_default();
let cwd = cwd(options.cwd)?;
let config = options.config.as_deref().map(PathBuf::from);
let cwd = cwd(options.cwd, config.as_deref())?;
let result = codescythe::run_and_fix(&cwd, config.as_deref()).map_err(to_napi_error)?;
serde_json::to_string(&result).map_err(to_napi_error)
}
Expand All @@ -38,10 +38,15 @@ impl Default for RunOptions {
}
}

fn cwd(value: Option<String>) -> napi::Result<PathBuf> {
fn cwd(value: Option<String>, config: Option<&std::path::Path>) -> napi::Result<PathBuf> {
match value {
Some(path) => Ok(PathBuf::from(path)),
None => std::env::current_dir().map_err(to_napi_error),
None => config
.and_then(std::path::Path::parent)
.filter(|parent| !parent.as_os_str().is_empty())
.map(PathBuf::from)
.map(Ok)
.unwrap_or_else(|| std::env::current_dir().map_err(to_napi_error)),
}
}

Expand Down
33 changes: 31 additions & 2 deletions packages/codescythe/npm_smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ type Analysis = {
};

type NativeBinding = {
analyze(options: {cwd: string}): string;
analyze(options: {config?: string; cwd?: string}): string;
};

type Codescythe = {
analyze(options: {cwd: string}): Analysis;
analyze(options: {config?: string; cwd?: string}): Analysis;
};

const repoRoot = process.cwd();
Expand Down Expand Up @@ -53,6 +53,12 @@ describe('@perplexity/codescythe npm package', () => {
assertFixtureAnalysis(analysis);
});

it('uses the config parent as the cwd when cwd is omitted', () => {
const codescythe = smokeRequire('@perplexity/codescythe') as Codescythe;
const analysis = codescythe.analyze({config: path.join(fixture, 'codescythe.json')});
assertFixtureAnalysis(analysis);
});

it('runs the public package bin', () => {
const binResult = childProcess.spawnSync(
process.execPath,
Expand All @@ -75,6 +81,29 @@ describe('@perplexity/codescythe npm package', () => {
assert.equal(binResult.status, 1, binResult.stderr || binResult.stdout);
assertFixtureAnalysis(JSON.parse(binResult.stdout) as Analysis);
});

it('runs the public package bin from the config parent', () => {
const binResult = childProcess.spawnSync(
process.execPath,
[
'--experimental-transform-types',
path.join(mainPackageDir, 'bin/codescythe.ts'),
'--json',
'--config',
path.join(fixture, 'codescythe.json'),
],
{
encoding: 'utf8',
env: {
...process.env,
NODE_PATH: nodeModules,
},
},
);

assert.equal(binResult.status, 1, binResult.stderr || binResult.stdout);
assertFixtureAnalysis(JSON.parse(binResult.stdout) as Analysis);
});
});

function packageDirFromEnv(name: string): string {
Expand Down
Loading