project-detect 0.1.2

Zero-config project type detection — identify any project's language, build system, and toolchain from directory contents
Documentation

Point it at a directory and it tells you what kind of project lives there. No config files, no setup — just file detection.

use project_detect::{detect, ProjectKind};

if let Some(kind) = detect(".") {
    println!("{} project ({})", kind.label(), kind.detected_file());
    // "Rust project (Cargo.toml)"
}

Features

  • 32 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 walkingdetect_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

Priority File Ecosystem Label
1 Cargo.toml Rust "Rust"
2 go.mod Go "Go"
3 mix.exs Elixir "Elixir"
4 pyproject.toml / setup.py / setup.cfg Python "Python"
5 package.json Node.js "Node.js"
6 build.gradle(.kts) or pom.xml + Kotlin sources Kotlin "Kotlin"
7 build.gradle / build.gradle.kts Gradle "Gradle"
8 pom.xml Maven "Maven"
9 build.sbt Scala "Scala"
10 Gemfile Ruby "Ruby"
11 Package.swift Swift "Swift"
12 *.xcworkspace / *.xcodeproj Xcode (Objective-C / Objective-C++) "Xcode"
13 build.zig Zig "Zig"
14 *.csproj / *.sln .NET ".NET"
15 composer.json PHP "PHP"
16 pubspec.yaml Dart / Flutter "Dart" / "Flutter"
17 stack.yaml / *.cabal Haskell "Haskell"
18 project.clj / deps.edn Clojure "Clojure"
19 rebar.config Erlang "Erlang"
20 dune-project OCaml "OCaml"
21 cpanfile / Makefile.PL Perl "Perl"
22 Project.toml Julia "Julia"
23 DESCRIPTION / renv.lock R "R"
24 *.nimble Nim "Nim"
25 shard.yml Crystal "Crystal"
26 v.mod V "V"
27 gleam.toml Gleam "Gleam"
28 *.rockspec Lua "Lua"
29 MODULE.bazel / WORKSPACE Bazel "Bazel"
30 meson.build Meson "Meson"
31 CMakeLists.txt CMake "CMake"
32 Makefile Make "Make"

Usage

Basic detection

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

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

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

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

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