# shared/analyzers/generator.analyzer.ts

> 81 lines of code and 21 definitions.

Tree: Governance tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-analyzers-generator-analyzer-ts
Source text: https://banes-lab.com/source/governance/shared/analyzers/generator.analyzer.ts.txt

Listed in [shared/analyzers](https://banes-lab.com/api/source/governance/shared/analyzers.md), after [shared/analyzers/document.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/document.analyzer.ts.md) and before [shared/analyzers/liveness.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/liveness.analyzer.ts.md).

## Definitions

- `literalChildren` (lexical_declaration, line 8)
- `collectLiterals` (lexical_declaration, line 30, exported)
- `rootsAtEphemeralDir` (lexical_declaration, line 37, exported)
- `ownLiterals` (lexical_declaration, line 22)
- `targetIsUnmarkedGenerated` (lexical_declaration, line 79, exported)
- `targetLiterals` (lexical_declaration, line 67)
- `EPHEMERAL_ROOT_FNS` (lexical_declaration, line 5)
- `CLIMB_LIMIT` (lexical_declaration, line 6)
- `literal` (lexical_declaration, line 23)
- `quasis` (lexical_declaration, line 24)
- `extensionOf` (lexical_declaration, line 55)
- `dot` (lexical_declaration, line 56)
- `prettierExtLiterals` (lexical_declaration, line 60)
- `ext` (lexical_declaration, line 62)
- `own` (lexical_declaration, line 68)
- `name` (lexical_declaration, line 69)
- `init` (lexical_declaration, line 70, exported)
- `basenameOf` (lexical_declaration, line 74)
- `slash` (lexical_declaration, line 75)
- `named` (lexical_declaration, line 83, exported)
- `base` (lexical_declaration, line 88, exported)

## Uses

- [shared/analyzers/sink.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/sink.analyzer.ts.md)

## Used by

- [rules/eslint/no-raw-file-write.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-raw-file-write.eslint.rule.ts.md)

## Source

```typescript
import { GENERATED_MARKER, MARKER_EXEMPT_BASENAMES, PRETTIER_EXTENSIONS } from "../manifests/generator.manifest.ts";
import type { InitResolver, WriteNode } from "../../types/generator.types.ts";
import { calledName, identName } from "./sink.analyzer.ts";

const EPHEMERAL_ROOT_FNS = new Set(["tmpdir", "mkdtemp", "mkdtempSync"]);
const CLIMB_LIMIT = 16;

const literalChildren = function literalChildren(node: WriteNode): (WriteNode | undefined)[] {
    return [
        node.callee,
        node.object,
        node.property,
        node.left,
        node.right,
        node.argument,
        ...(node.arguments ?? []),
        ...(node.elements ?? []),
        ...(node.expressions ?? []),
    ];
};

const ownLiterals = function ownLiterals(node: WriteNode): string[] {
    const literal = node.type === "Literal" && typeof node.value === "string" ? [node.value] : [];
    const quasis = (node.quasis ?? []).flatMap((quasi) =>
        typeof quasi.value?.cooked === "string" ? [quasi.value.cooked] : [],
    );
    return [...literal, ...quasis];
};

export const collectLiterals = function collectLiterals(node: WriteNode | undefined, depth: number): string[] {
    if (!node || depth > CLIMB_LIMIT) {
        return [];
    }
    return [...ownLiterals(node), ...literalChildren(node).flatMap((child) => collectLiterals(child, depth + 1))];
};

export const rootsAtEphemeralDir = function rootsAtEphemeralDir(
    resolve: InitResolver,
    node: WriteNode | undefined,
    depth: number,
): boolean {
    if (!node || depth > CLIMB_LIMIT) {
        return false;
    }
    if (node.type === "CallExpression" && EPHEMERAL_ROOT_FNS.has(calledName(node) ?? "")) {
        return true;
    }
    if (node.type === "Identifier") {
        const init = resolve(identName(node) ?? "");
        return init !== null && rootsAtEphemeralDir(resolve, init, depth + 1);
    }
    return literalChildren(node).some((child) => rootsAtEphemeralDir(resolve, child, depth + 1));
};

const extensionOf = function extensionOf(text: string): string | null {
    const dot = text.lastIndexOf(".");
    return dot === -1 ? null : text.slice(dot + 1).toLowerCase();
};

const prettierExtLiterals = function prettierExtLiterals(strings: string[]): string[] {
    return strings.filter((text) => {
        const ext = extensionOf(text);
        return ext !== null && PRETTIER_EXTENSIONS.has(ext);
    });
};

const targetLiterals = function targetLiterals(resolve: InitResolver, target: WriteNode): string[] {
    const own = collectLiterals(target, 0);
    const name = identName(target);
    const init = name === null ? null : resolve(name);
    return init === null ? own : [...own, ...collectLiterals(init, 0)];
};

const basenameOf = function basenameOf(text: string): string {
    const slash = Math.max(text.lastIndexOf("/"), text.lastIndexOf("\\"));
    return slash === -1 ? text : text.slice(slash + 1);
};

export const targetIsUnmarkedGenerated = function targetIsUnmarkedGenerated(
    resolve: InitResolver,
    target: WriteNode,
): boolean {
    const named = prettierExtLiterals(targetLiterals(resolve, target));
    if (named.length === 0) {
        return false;
    }
    return named.every((text) => {
        const base = basenameOf(text).toLowerCase();
        return !base.includes(GENERATED_MARKER) && !MARKER_EXEMPT_BASENAMES.has(base);
    });
};
```
