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

> 62 lines of code and 20 definitions.

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

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

## Definitions

- `settleArtifact` (lexical_declaration, line 49)
- `govlabJscpdConfig` (lexical_declaration, line 30, exported)
- `runJscpd` (lexical_declaration, line 58)
- `SECTION` (lexical_declaration, line 19)
- `CONFIG_FILE` (lexical_declaration, line 20)
- `REPORTER_FILE` (lexical_declaration, line 21)
- `DEFAULT_THRESHOLD` (lexical_declaration, line 22)
- `DEFAULT_MIN_TOKENS` (lexical_declaration, line 23)
- `DEFAULT_MIN_LINES` (lexical_declaration, line 24)
- `DEFAULT_FORMATS` (lexical_declaration, line 25)
- `REPORTERS` (lexical_declaration, line 26)
- `moduleRequire` (lexical_declaration, line 28)
- `config` (lexical_declaration, line 31, exported)
- `section` (lexical_declaration, line 32, exported)
- `jscpdBin` (lexical_declaration, line 45)
- `declared` (lexical_declaration, line 50)
- `written` (lexical_declaration, line 51)
- `configFile` (lexical_declaration, line 59)
- `args` (lexical_declaration, line 60)
- `result` (lexical_declaration, line 61)

## Contained in

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

## Uses

- [core/factories/finding.factory.ts](https://banes-lab.com/source/quality/core/factories/finding.factory.ts.md)
- [core/parsers/tool.jscpd.parser.ts](https://banes-lab.com/source/quality/core/parsers/tool.jscpd.parser.ts.md)
- [core/predicates/failure.predicate.ts](https://banes-lab.com/source/quality/core/predicates/failure.predicate.ts.md)
- [core/selectors/exclusions.selector.ts](https://banes-lab.com/source/quality/core/selectors/exclusions.selector.ts.md)
- [core/selectors/record.selector.ts](https://banes-lab.com/source/quality/core/selectors/record.selector.ts.md)

## Linked from

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

## Source

```typescript
import { existsSync, renameSync, rmSync } from "node:fs";
import { findingsResult, signalDeathResult } from "#core/factories/finding.factory";
import { numberField, stringArrayField, stringArrayFieldOr, stringField } from "#core/selectors/record.selector";
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 { loadGovlabConfig } from "#core/loaders/config.loader";
import { parseJscpdOutput } from "#core/parsers/tool.jscpd.parser";
import path from "node:path";
import process from "node:process";
import { relativePath } from "@ssot/paths";
import { sectionOf } from "#core/selectors/config.selector";
import { signalKilled } from "#core/predicates/failure.predicate";
import { spawnTool } from "#core/adapters/invocation.adapter";
import { withMasterExclude } from "#core/selectors/exclusions.selector";
import { writeToolJson } from "#core/persistence/tool.persistence";

const SECTION = "jscpd";
const CONFIG_FILE = "jscpd.json";
const REPORTER_FILE = "jscpd-report.json";
const DEFAULT_THRESHOLD = 0;
const DEFAULT_MIN_TOKENS = 50;
const DEFAULT_MIN_LINES = 5;
const DEFAULT_FORMATS = ["typescript", "javascript", "css", "html"];
const REPORTERS = ["json"];

const moduleRequire = createRequire(import.meta.url);

export const govlabJscpdConfig = async (consumerRoot: string = process.cwd()): Promise<Record<string, unknown>> => {
    const config = await loadGovlabConfig(consumerRoot);
    const section = sectionOf(config, SECTION);
    return {
        absolute: section["absolute"] !== false,
        format: stringArrayFieldOr(section, "format", DEFAULT_FORMATS),
        ignore: withMasterExclude(config, stringArrayField(section, "ignore")),
        minLines: numberField(section, "minLines", DEFAULT_MIN_LINES),
        minTokens: numberField(section, "minTokens", DEFAULT_MIN_TOKENS),
        output: stringField(section, "output", relativePath("govlabHost.reports")),
        reporters: REPORTERS,
        threshold: numberField(section, "threshold", DEFAULT_THRESHOLD),
    };
};

const jscpdBin = function jscpdBin(): string {
    return path.resolve(path.dirname(moduleRequire.resolve("jscpd/package.json")), "run-jscpd.js");
};

const settleArtifact = function settleArtifact(root: string): void {
    const declared = path.join(root, relativePath("govlabHost.reports.duplication"));
    const written = path.join(path.dirname(declared), REPORTER_FILE);
    if (existsSync(written)) {
        rmSync(declared, { force: true });
        renameSync(written, declared);
    }
};

const runJscpd = async function runJscpd(context: RunnerContext): Promise<RunResult> {
    const configFile = await writeToolJson(context.root, CONFIG_FILE, await govlabJscpdConfig(context.root));
    const args = [jscpdBin(), "--config", configFile, "--reporters", "json", "--silent", ...context.paths];
    const result = spawnTool(process.execPath, args, { cwd: context.root, encoding: "utf8", shell: false });
    settleArtifact(context.root);
    if (signalKilled(result.status)) {
        return signalDeathResult({ ecosystem: context.ecosystem, root: context.root, tool: SECTION }, result.signal);
    }
    return findingsResult(parseJscpdOutput(result.stdout, context.ecosystem));
};

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