# How project-scanner Identifies Programming Languages and Frameworks in Understand-Anything

> Discover how Egonex-AI/Understand-Anything's project-scanner identifies programming languages and frameworks by analyzing file extensions and import maps. Get clear insights now.

- Repository: [Egonex/Understand-Anything](https://github.com/Egonex-AI/Understand-Anything)
- Tags: how-to-guide
- Published: 2026-06-23

---

**The project-scanner determines file types through deterministic filename and extension lookups, then delegates framework classification to the graph-builder which analyzes import maps against a registry of known frameworks.**

The `project-scanner` in the Egonex-AI/Understand-Anything repository employs a fast, rule-based pipeline to classify source files. By separating language detection from framework inference, the tool maintains high performance while achieving accurate technology stack identification. This article examines the two-stage process that enables the scanner to identify programming languages and frameworks without LLM-based analysis.

## Stage 1: Language Identification via Filename and Extension

The `detectLanguage()` function exported from `understand-anything-plugin/skills/understand/scan-project.mjs` (lines 21-48) performs language identification through a cascading lookup strategy designed for speed and determinism.

### Filename-Based Detection

First, the function checks the file name against `LANGUAGE_BY_FILENAME`, a lookup table mapping well-known no-extension conventions to their respective language identifiers. This table includes entries like `Dockerfile`, `Makefile`, and various dot-file patterns.

For dot-files such as `.env.local` or `.env.production`, the helper `dotfileKey()` extracts the leading `.env` segment to normalize all variants to the same `config` category. This ensures that `.env`, `.env.local`, and `.env.production` all resolve to the same language ID.

### Extension-Based Detection

If no special filename matches, the function lower-cases the file extension and queries the `LANGUAGE_BY_EXT` table (defined at lines 5-94 of the same file). This comprehensive lookup covers standard development extensions:

- `.ts` and `.tsx` → `typescript`
- `.py` → `python`
- `.go` → `go`
- `.rb` → `ruby`
- `.html` → `html`
- `.env` → `config`

For unknown extensions, the leading dot is stripped and the remainder returned as the language ID (e.g., `data.weirdext` becomes `weirdext`). Files lacking both special names and extensions return `'unknown'`.

## Stage 2: Framework Inference via Import-Map Analysis

The scanner intentionally **does not** analyze file contents for frameworks. Instead, framework detection occurs later in the pipeline through the **graph-builder** located in [`packages/core/src/analyzer/graph-builder.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/analyzer/graph-builder.ts).

### The Framework Registry

The framework registry at [`packages/core/src/languages/framework-registry.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/languages/framework-registry.ts) maintains bidirectional mappings between language IDs and framework descriptors. Known frameworks including **Express**, **Django**, **FastAPI**, and **Gin** are registered with their associated language IDs.

Individual framework definition files—such as [`packages/core/src/languages/frameworks/express.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/languages/frameworks/express.ts)—contain predicates that recognize framework-specific entry points. For example, the Express detector identifies `require('express')` or `import ... from 'express'` statements within JavaScript files.

### Graph-Builder Integration

When processing the project graph, the builder invokes `detectLanguage()` to obtain the language ID, then cross-references this with the project's import map generated by the `extract-import-map.mjs` skill. If a file's language is `javascript` and the import map contains Express dependencies, the builder labels it as an **Express** framework file. This separation of concerns keeps the initial scan fast while allowing sophisticated framework detection through AST analysis.

## Practical Implementation Examples

The following examples demonstrate direct usage of the scanner's detection utilities and their integration with the framework classification system:

```javascript
// Direct usage of detection helpers (as tested in test_scan_project.test.mjs)
import { detectLanguage, detectCategory } from './scan-project.mjs';

console.log(detectLanguage('src/server.ts'));      // → "typescript"
console.log(detectLanguage('Dockerfile.dev'));    // → "dockerfile"
console.log(detectLanguage('.env.production'));  // → "config"
console.log(detectLanguage('unknown.xyz'));       // → "xyz"

```

```typescript
// Framework assignment in the graph builder (simplified from graph-builder.ts)
import { detectLanguage } from '../../skills/understand/scan-project.mjs';
import { FRAMEWORK_REGISTRY } from '../languages/framework-registry';

function assignFramework(filePath: string, importMap: Set<string>) {
  const lang = detectLanguage(filePath);
  for (const fw of FRAMEWORK_REGISTRY[lang] ?? []) {
    if (fw.matchesImportMap(importMap)) return fw.name;   // e.g., "Express"
  }
  return null;                                           // no framework detected
}

```

## Summary

- **Language detection** relies on `detectLanguage()` in `scan-project.mjs`, which uses lookup tables (`LANGUAGE_BY_FILENAME` and `LANGUAGE_BY_EXT`) to classify files by name and extension.
- **Dot-file normalization** is handled by `dotfileKey()` to group variants like `.env.local` under the `config` category.
- **Framework inference** is performed separately by the graph-builder using the framework registry and import-map analysis rather than file content heuristics.
- **Key files** include the scanner script (`understand-anything-plugin/skills/understand/scan-project.mjs`), the framework registry ([`packages/core/src/languages/framework-registry.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/languages/framework-registry.ts)), and the graph-builder ([`packages/core/src/analyzer/graph-builder.ts`](https://github.com/Egonex-AI/Understand-Anything/blob/main/packages/core/src/analyzer/graph-builder.ts)).

## Frequently Asked Questions

### How does project-scanner handle files without extensions?

Files without extensions are checked against `LANGUAGE_BY_FILENAME` for well-known names like `Dockerfile` or `Makefile`. If no match exists, the function returns `'unknown'`.

### Can the scanner detect frameworks directly from source code content?

No. The scanner only identifies programming languages. Framework detection occurs later in the pipeline when the graph-builder analyzes import statements against the framework registry.

### Where are the language lookup tables defined?

The `LANGUAGE_BY_FILENAME` and `LANGUAGE_BY_EXT` tables are defined at lines 5-94 of `understand-anything-plugin/skills/understand/scan-project.mjs`.

### How are environment configuration files like `.env.local` handled?

The `dotfileKey()` helper extracts the base name (e.g., `.env`) from dot-files, causing all `.env.*` variants to map to the `config` language ID in the lookup table.