# core/adapters/tool.prettier.adapter.ts

> 104 lines of code and 25 definitions.

Tree: GovLab Quality
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-adapters-tool-prettier-adapter-ts
Source text: https://banes-lab.com/source/quality/core/adapters/tool.prettier.adapter.ts.txt

Listed in [core/adapters](https://banes-lab.com/api/source/quality/core/adapters.md), after [core/adapters/tool.pmd.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.pmd.adapter.ts.md) and before [core/adapters/tool.pylint.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.pylint.adapter.ts.md).

## Definitions

- `govlabPrettierConfig` (lexical_declaration, line 31, exported)
- `prettierBin` (lexical_declaration, line 27)
- `govlabPrettierIgnore` (lexical_declaration, line 38, exported)
- `prettierRuns` (lexical_declaration, line 48)
- `prettierArgs` (lexical_declaration, line 59)
- `exitOf` (lexical_declaration, line 77)
- `SECTION` (lexical_declaration, line 23)
- `FAILED_EXIT` (lexical_declaration, line 24)
- `moduleRequire` (lexical_declaration, line 25)
- `section` (lexical_declaration, line 33, exported)
- `concerns` (lexical_declaration, line 34, exported)
- `config` (lexical_declaration, line 39, exported)
- `PrettierRun` (interface_declaration, line 43)
- `dir` (lexical_declaration, line 49)
- `exclusions` (lexical_declaration, line 50)
- `inside` (lexical_declaration, line 51)
- `outside` (lexical_declaration, line 52)
- `child` (lexical_declaration, line 79)
- `runPrettier` (lexical_declaration, line 90, exported)
- `configPath` (lexical_declaration, line 91, exported)
- `runs` (lexical_declaration, line 96, exported)
- `code` (lexical_declaration, line 97, exported)
- `prior` (lexical_declaration, line 98, exported)
- `exit` (lexical_declaration, line 99, exported)
- `failure` (lexical_declaration, line 102, exported)

## Contained in

- [core/adapters](https://banes-lab.com/anatomy/quality/folder-quality-core-adapters.md)

## Uses

- [core/emitters/tool.prettier.emitter.ts](https://banes-lab.com/source/quality/core/emitters/tool.prettier.emitter.ts.md)
- [core/lifecycle/invocation.lifecycle.ts](https://banes-lab.com/source/quality/core/lifecycle/invocation.lifecycle.ts.md)
- [core/persistence/tool.persistence.ts](https://banes-lab.com/source/quality/core/persistence/tool.persistence.ts.md)
- [core/selectors/config.selector.ts](https://banes-lab.com/source/quality/core/selectors/config.selector.ts.md)
- [core/selectors/exclusions.selector.ts](https://banes-lab.com/source/quality/core/selectors/exclusions.selector.ts.md)

## Used by

- [core/persistence/comment.persistence.ts](https://banes-lab.com/source/quality/core/persistence/comment.persistence.ts.md)

## Linked from

- [core/adapters](https://banes-lab.com/anatomy/quality/folder-quality-core-adapters.md)
- [core/emitters](https://banes-lab.com/anatomy/quality/folder-quality-core-emitters.md)
- [core/lifecycle](https://banes-lab.com/anatomy/quality/folder-quality-core-lifecycle.md)
- [core/persistence](https://banes-lab.com/anatomy/quality/folder-quality-core-persistence.md)
- [core/selectors](https://banes-lab.com/anatomy/quality/folder-quality-core-selectors.md)

## Source

```typescript
import { isRecord, stringArrayField } from "#core/selectors/record.selector";
import { toolCacheDir, writeToolFile, writeToolJson } from "#core/persistence/tool.persistence";
import { POSITION } from "#configuration/constants/tool.constants";
import type { RunResult } from "#types/finding.types";
import type { RunnerContext } from "#types/tool.types";
import { createRequire } from "node:module";
import { defineTool } from "#core/registries/tool.registry";
import { emitPrettierConfig } from "#core/emitters/tool.prettier.emitter";
import { exclusionsFrom } from "#core/converters/exclusions.converter";
import { isInsideRoot } from "#core/predicates/location.predicate";
import { loadGovlabConfig } from "#core/loaders/config.loader";
import path from "node:path";
import { prettierFailed } from "#configuration/strings/tool.strings";
import process from "node:process";
import { sectionOf } from "#core/selectors/config.selector";
import { spawn } from "node:child_process";
import { superviseChild } from "#core/lifecycle/invocation.lifecycle";
import { toolFinding } from "#core/factories/finding.factory";
import { validConcepts } from "#core/selectors/concept.selector";
import { validateConcerns } from "#core/validators/concern.validator";
import { withMasterExclude } from "#core/selectors/exclusions.selector";

const SECTION = "prettier";
const FAILED_EXIT = 1;
const moduleRequire = createRequire(import.meta.url);

const prettierBin = function prettierBin(): string {
    return path.join(path.dirname(moduleRequire.resolve("prettier/package.json")), "bin", "prettier.cjs");
};

export const govlabPrettierConfig = async (consumerRoot: string = process.cwd()): Promise<Record<string, unknown>> => {
    const config = await loadGovlabConfig(consumerRoot);
    const section = sectionOf(config, SECTION);
    const concerns = validateConcerns(config.qualityMaster?.concerns ?? {}, validConcepts());
    return emitPrettierConfig({ base: isRecord(section["base"]) ? section["base"] : undefined, concerns });
};

export const govlabPrettierIgnore = async (consumerRoot: string = process.cwd()): Promise<string[]> => {
    const config = await loadGovlabConfig(consumerRoot);
    return withMasterExclude(config, stringArrayField(sectionOf(config, SECTION), "ignore"));
};

interface PrettierRun {
    readonly ignorePath: string;
    readonly paths: readonly string[];
}

const prettierRuns = async function prettierRuns(context: RunnerContext): Promise<readonly PrettierRun[]> {
    const dir = toolCacheDir(context.root, SECTION);
    const exclusions = exclusionsFrom(await govlabPrettierIgnore(context.root), dir, context.root);
    const inside = context.paths.filter((target) => isInsideRoot(context.root, target));
    const outside = context.paths.filter((target) => !isInsideRoot(context.root, target));
    return [
        { ignorePath: writeToolFile(context.root, path.join(SECTION, "ignore"), exclusions.join("\n")), paths: inside },
        { ignorePath: writeToolFile(context.root, path.join(SECTION, "outside.ignore"), ""), paths: outside },
    ].filter((run) => run.paths.length > 0);
};

const prettierArgs = function prettierArgs(context: RunnerContext, configPath: string, run: PrettierRun): string[] {
    return [
        prettierBin(),
        "--config",
        configPath,
        "--ignore-path",
        run.ignorePath,
        "--cache",
        "--cache-location",
        path.join(toolCacheDir(context.root, SECTION), "cache"),
        "--no-error-on-unmatched-pattern",
        "--log-level",
        "log",
        context.fix ? "--write" : "--check",
        ...run.paths,
    ];
};

const exitOf = async function exitOf(root: string, args: readonly string[]): Promise<number> {
    return new Promise<number>((resolve) => {
        const child = spawn(process.execPath, args, { cwd: root, stdio: "inherit" });
        superviseChild(child);
        child.on("error", () => {
            resolve(FAILED_EXIT);
        });
        child.on("close", (exit) => {
            resolve(exit ?? FAILED_EXIT);
        });
    });
};

export const runPrettier = async function runPrettier(context: RunnerContext): Promise<RunResult> {
    const configPath = await writeToolJson(
        context.root,
        path.join(SECTION, "config.json"),
        await govlabPrettierConfig(context.root),
    );
    const runs = await prettierRuns(context);
    const code = await runs.reduce<Promise<number>>(async (previous, run) => {
        const prior = await previous;
        const exit = await exitOf(context.root, prettierArgs(context, configPath, run));
        return prior === 0 ? exit : prior;
    }, Promise.resolve(0));
    const failure = toolFinding({
        column: POSITION,
        ecosystem: context.ecosystem,
        file: "",
        line: POSITION,
        message: prettierFailed(code),
        ruleId: "prettier/format",
        tool: SECTION,
    });
    return { findings: code === 0 ? [] : [failure], fixedCount: 0, output: "" };
};

defineTool({ ecosystems: ["javascript", "typescript", "css", "json"], run: runPrettier, tool: SECTION });
```
