# core/classifiers/source.classifier.ts

> 42 lines of code and 12 definitions.

Tree: Code Parse
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/code-parse#file-code-parse-core-classifiers-source-classifier-ts
Source text: https://banes-lab.com/source/code-parse/core/classifiers/source.classifier.ts.txt

Listed in [core/classifiers](https://banes-lab.com/api/source/code-parse/core/classifiers.md).

## Definitions

- `fromShebang` (lexical_declaration, line 31)
- `detectLanguage` (lexical_declaration, line 44, exported)
- `buildExtensions` (lexical_declaration, line 9)
- `map` (lexical_declaration, line 10)
- `EXTENSIONS` (lexical_declaration, line 19)
- `extensionOf` (lexical_declaration, line 21)
- `dot` (lexical_declaration, line 22)
- `firstLine` (lexical_declaration, line 26)
- `newline` (lexical_declaration, line 27)
- `line` (lexical_declaration, line 35)
- `byExtension` (lexical_declaration, line 45, exported)
- `DETECTABLE_LANGUAGES` (lexical_declaration, line 49, exported)

## Contained in

- [core/classifiers](https://banes-lab.com/anatomy/code-parse/folder-code-parse-core-classifiers.md)

## Linked from

- [core/classifiers](https://banes-lab.com/anatomy/code-parse/folder-code-parse-core-classifiers.md)

## Source

```typescript
import {
    EXTENSION_DOT,
    SHEBANG_PREFIX,
    SHEBANG_TOKENS,
    UNDECLARED_EXTENSIONS,
} from "#configuration/constants/source.constants";
import { loadDerivedExtensions } from "#core/loaders/source.loader";

const buildExtensions = function buildExtensions(): ReadonlyMap<string, string> {
    const map = loadDerivedExtensions();
    for (const [extension, language] of UNDECLARED_EXTENSIONS) {
        if (!map.has(extension)) {
            map.set(extension, language);
        }
    }
    return map;
};

const EXTENSIONS = buildExtensions();

const extensionOf = function extensionOf(filename: string): string {
    const dot = filename.lastIndexOf(EXTENSION_DOT);
    return dot === -1 ? "" : filename.slice(dot).toLowerCase();
};

const firstLine = function firstLine(content: string): string {
    const newline = content.indexOf("\n");
    return (newline === -1 ? content : content.slice(0, newline)).toLowerCase();
};

const fromShebang = function fromShebang(content: string): string | null {
    if (!content.startsWith(SHEBANG_PREFIX)) {
        return null;
    }
    const line = firstLine(content);
    for (const [token, language] of SHEBANG_TOKENS) {
        if (line.includes(token)) {
            return language;
        }
    }
    return null;
};

export const detectLanguage = function detectLanguage(filename: string, content = ""): string | null {
    const byExtension = EXTENSIONS.get(extensionOf(filename));
    return typeof byExtension === "string" ? byExtension : fromShebang(content);
};

export const DETECTABLE_LANGUAGES: ReadonlySet<string> = new Set([...EXTENSIONS.values(), ...SHEBANG_TOKENS.values()]);
```
