<div align="center">
<h1>project-detect</h1>
<p><strong>Zero-config project type detection for 29 ecosystems.</strong></p>
<p>
<a href="https://crates.io/crates/project-detect"><img alt="crates.io" src="https://img.shields.io/crates/v/project-detect?logo=rust&logoColor=white&color=orange"></a>
<a href="https://docs.rs/project-detect"><img alt="docs.rs" src="https://img.shields.io/docsrs/project-detect"></a>
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
</p>
</div>
---
Point it at a directory and it tells you what kind of project lives there. No config files, no setup — just file detection.
```rust
use project_detect::{detect, ProjectKind};
if let Some(kind) = detect(".") {
println!("{} project ({})", kind.label(), kind.detected_file());
// "Rust project (Cargo.toml)"
}
```
## Features
- **29 ecosystems** detected out of the box
- **Priority-ordered** — language-specific files win over generic build systems
- **Ecosystem metadata** — Node.js package manager, Flutter vs Dart, Stack vs Cabal, etc.
- **Directory walking** — `detect_walk` searches up the directory tree for monorepo support
- **Artifact directories** — know what to clean for every ecosystem
- **Zero dependencies** beyond `serde_json` (for package.json parsing)
## Supported Ecosystems
| 1 | `Cargo.toml` | Rust | `"Rust"` |
| 2 | `go.mod` | Go | `"Go"` |
| 3 | `mix.exs` | Elixir | `"Elixir"` |
| 4 | `pyproject.toml` / `setup.py` | Python | `"Python"` |
| 5 | `package.json` | Node.js | `"Node.js"` |
| 6 | `build.gradle` | Gradle (Java/Kotlin) | `"Gradle"` |
| 7 | `pom.xml` | Maven (Java) | `"Maven"` |
| 8 | `build.sbt` | Scala | `"Scala"` |
| 9 | `Gemfile` | Ruby | `"Ruby"` |
| 10 | `Package.swift` | Swift | `"Swift"` |
| 11 | `build.zig` | Zig | `"Zig"` |
| 12 | `*.csproj` / `*.sln` | .NET | `".NET"` |
| 13 | `composer.json` | PHP | `"PHP"` |
| 14 | `pubspec.yaml` | Dart / Flutter | `"Dart"` / `"Flutter"` |
| 15 | `stack.yaml` / `*.cabal` | Haskell | `"Haskell"` |
| 16 | `project.clj` / `deps.edn` | Clojure | `"Clojure"` |
| 17 | `rebar.config` | Erlang | `"Erlang"` |
| 18 | `dune-project` | OCaml | `"OCaml"` |
| 19 | `cpanfile` / `Makefile.PL` | Perl | `"Perl"` |
| 20 | `Project.toml` | Julia | `"Julia"` |
| 21 | `*.nimble` | Nim | `"Nim"` |
| 22 | `shard.yml` | Crystal | `"Crystal"` |
| 23 | `v.mod` | V | `"V"` |
| 24 | `gleam.toml` | Gleam | `"Gleam"` |
| 25 | `*.rockspec` | Lua | `"Lua"` |
| 26 | `MODULE.bazel` / `WORKSPACE` | Bazel | `"Bazel"` |
| 27 | `meson.build` | Meson | `"Meson"` |
| 28 | `CMakeLists.txt` | CMake | `"CMake"` |
| 29 | `Makefile` | Make | `"Make"` |
## Usage
### Basic detection
```rust
use project_detect::{detect, ProjectKind};
let kind = detect("/path/to/project").unwrap();
println!("{}", kind.label()); // "Rust"
println!("{}", kind.detected_file()); // "Cargo.toml"
```
### Walk up the directory tree
```rust
use project_detect::detect_walk;
// Running from src/lib/ inside a Cargo workspace?
// detect_walk finds the Cargo.toml in the parent.
if let Some((kind, project_dir)) = detect_walk(".") {
println!("Found {} at {}", kind.label(), project_dir.display());
}
```
### Ecosystem metadata
```rust
use project_detect::{detect, ProjectKind, NodePM};
match detect(".") {
Some(ProjectKind::Node { manager: NodePM::Pnpm }) => println!("pnpm project"),
Some(ProjectKind::Dart { flutter: true }) => println!("Flutter app"),
Some(ProjectKind::Haskell { stack: true }) => println!("Stack project"),
Some(ProjectKind::Haskell { stack: false }) => println!("Cabal project"),
Some(kind) => println!("{}", kind.label()),
None => println!("No project detected"),
}
```
### Clean artifacts
```rust
use project_detect::detect;
if let Some(kind) = detect(".") {
for dir in kind.artifact_dirs() {
println!("Can remove: {dir}/");
}
// Rust: ["target"]
// Node.js: ["node_modules", ".next", ".nuxt", ".turbo"]
// Haskell (Stack): [".stack-work"]
}
```
### Node.js helpers
```rust
use project_detect::{node_has_script, node_has_bin, detect_node_workspace};
use std::path::Path;
let dir = Path::new(".");
if node_has_script(dir, "build") { /* has a build script */ }
if node_has_bin(dir) { /* has a bin field */ }
if let Some(packages) = detect_node_workspace(dir) {
for pkg in packages {
println!("{}: {}", pkg.name, pkg.dev_script);
}
}
```
## License
MIT