# tools/core/iterators/file.iterator.ts

> 77 lines of code and 22 definitions.

Tree: Coordination tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-iterators-file-iterator-ts
Source text: https://banes-lab.com/assets/sources/source.4ca88c76613f769e45e13d98e7b035b1765571abd7a297963e253b7df5f76255.generated.txt

## Definitions

- `kept` (lexical_declaration, line 28)
- `endsWithAny` (lexical_declaration, line 24)
- `readSource` (lexical_declaration, line 82, exported)
- `forwardSlashed` (lexical_declaration, line 86, exported)
- `listingOf` (lexical_declaration, line 53)
- `toPosix` (lexical_declaration, line 90, exported)
- `WalkOptions` (interface_declaration, line 4, exported)
- `EntryKind` (type_alias_declaration, line 11)
- `Listed` (interface_declaration, line 13)
- `Listing` (interface_declaration, line 19)
- `excluded` (lexical_declaration, line 29)
- `entriesOf` (lexical_declaration, line 33)
- `kindOf` (lexical_declaration, line 41)
- `info` (lexical_declaration, line 43)
- `listed` (lexical_declaration, line 54)
- `full` (lexical_declaration, line 57)
- `walk` (lexical_declaration, line 67, exported)
- `found` (lexical_declaration, line 68, exported)
- `stack` (lexical_declaration, line 69, exported)
- `directory` (lexical_declaration, line 70, exported)
- `listing` (lexical_declaration, line 73, exported)
- `rel` (lexical_declaration, line 91, exported)

## Used by

- [tools/core/transformers/reference.transformer.ts](https://banes-lab.com/source/coordination/tools/core/transformers/reference.transformer.ts.md)

## Source

```typescript
import { join, relative, sep } from "node:path";
import { readFileSync, readdirSync, statSync } from "node:fs";

export interface WalkOptions {
    readonly root: string;
    readonly ignored: readonly string[];
    readonly extensions: readonly string[];
    readonly excluded?: readonly string[];
}

type EntryKind = "directory" | "file" | "other";

interface Listed {
    readonly entry: string;
    readonly full: string;
    readonly kind: EntryKind;
}

interface Listing {
    readonly directories: readonly string[];
    readonly files: readonly string[];
}

const endsWithAny = function endsWithAny(name: string, suffixes: readonly string[]): boolean {
    return suffixes.some((suffix) => name.length > suffix.length && name.endsWith(suffix));
};

const kept = function kept(entry: string, options: WalkOptions): boolean {
    const excluded = options.excluded !== undefined && endsWithAny(entry, options.excluded);
    return !excluded && (options.extensions.length === 0 || endsWithAny(entry, options.extensions));
};

const entriesOf = function entriesOf(directory: string): string[] {
    try {
        return readdirSync(directory);
    } catch {
        return [];
    }
};

const kindOf = function kindOf(full: string): EntryKind {
    try {
        const info = statSync(full);
        if (info.isDirectory()) {
            return "directory";
        }
        return info.isFile() ? "file" : "other";
    } catch {
        return "other";
    }
};

const listingOf = function listingOf(directory: string, options: WalkOptions): Listing {
    const listed: Listed[] = entriesOf(directory)
        .filter((entry) => !options.ignored.includes(entry))
        .map((entry) => {
            const full = join(directory, entry);
            return { entry, full, kind: kindOf(full) };
        });

    return {
        directories: listed.filter((item) => item.kind === "directory").map((item) => item.full),
        files: listed.filter((item) => item.kind === "file" && kept(item.entry, options)).map((item) => item.full),
    };
};

export const walk = function walk(options: WalkOptions): string[] {
    const found: string[] = [];
    const stack: string[] = [options.root];
    let directory = stack.pop();

    while (directory !== undefined) {
        const listing = listingOf(directory, options);
        found.push(...listing.files);
        stack.push(...listing.directories);
        directory = stack.pop();
    }

    return found.toSorted((left, right) => left.localeCompare(right, "en"));
};

export const readSource = function readSource(path: string): string {
    return readFileSync(path, "utf8");
};

export const forwardSlashed = function forwardSlashed(text: string): string {
    return text.replaceAll("\\", "/");
};

export const toPosix = function toPosix(root: string, path: string): string {
    const rel = relative(root, path);
    return sep === "/" ? rel : forwardSlashed(rel);
};
```
