# core/fragment/eslint/no-duplicate-fragment.eslint.rule.ts

> 62 lines of code and 14 definitions.

Tree: GovLab Quality
Language: typescript
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-fragment-eslint-no-duplicate-fragment-eslint-rule-ts
Source text: https://banes-lab.com/source/quality/core/fragment/eslint/no-duplicate-fragment.eslint.rule.ts.txt

Listed in [core/fragment/eslint](https://banes-lab.com/api/source/quality/core/fragment/eslint.md), after [core/fragment/eslint/fragment-single-concern.eslint.rule.ts](https://banes-lab.com/source/quality/core/fragment/eslint/fragment-single-concern.eslint.rule.ts.md) and before [core/fragment/eslint/no-hardcoded-context-token.eslint.rule.ts](https://banes-lab.com/source/quality/core/fragment/eslint/no-hardcoded-context-token.eslint.rule.ts.md).

## Definitions

- `normalize` (lexical_declaration, line 7)
- `fragmentBody` (lexical_declaration, line 23)
- `WHITESPACE` (lexical_declaration, line 5)
- `out` (lexical_declaration, line 8)
- `FragmentBody` (interface_declaration, line 17)
- `object` (lexical_declaration, line 27)
- `body` (lexical_declaration, line 28)
- `create` (method_definition, line 40, exported)
- `{ sourceCode }` (lexical_declaration, line 41, exported)
- `seen` (lexical_declaration, line 42, exported)
- `onCall` (lexical_declaration, line 44, exported)
- `found` (lexical_declaration, line 45, exported)
- `other` (lexical_declaration, line 49, exported)
- `handlers` (lexical_declaration, line 56, exported)

## Contained in

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

## Uses

- [core/predicates/context.fragment.predicate.ts](https://banes-lab.com/source/quality/core/predicates/context.fragment.predicate.ts.md)
- [core/selectors/context.fragment.selector.ts](https://banes-lab.com/source/quality/core/selectors/context.fragment.selector.ts.md)

## Linked from

- [core/fragment/eslint](https://banes-lab.com/anatomy/quality/folder-quality-core-fragment-eslint.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 type { Rule, SourceCode } from "eslint";
import { fragmentObject, property, stringValue } from "#core/selectors/context.fragment.selector";
import { isDefineFragmentCall, isRuleNode } from "#core/predicates/context.fragment.predicate";

const WHITESPACE = new Set([" ", "\t", "\n", "\r"]);

const normalize = function normalize(text: string): string {
    let out = "";
    for (const ch of text) {
        if (!WHITESPACE.has(ch)) {
            out += ch;
        }
    }
    return out;
};

interface FragmentBody {
    body: Rule.Node;
    normalized: string;
    id: string;
}

const fragmentBody = function fragmentBody(node: Rule.Node, sourceCode: SourceCode): FragmentBody | null {
    if (!isDefineFragmentCall(node)) {
        return null;
    }
    const object = fragmentObject(node);
    const body = object === null ? null : property(object, "body");
    if (object === null || body === null || !isRuleNode(body)) {
        return null;
    }
    return {
        body,
        id: stringValue(property(object, "id")) ?? "(unnamed)",
        normalized: normalize(sourceCode.getText(body)),
    };
};

export default {
    create(context) {
        const { sourceCode } = context;
        const seen = new Map<string, string>();

        const onCall = function onCall(node: Rule.Node): void {
            const found = fragmentBody(node, sourceCode);
            if (found === null) {
                return;
            }
            const other = seen.get(found.normalized);
            if (typeof other === "string") {
                context.report({ data: { id: found.id, other }, messageId: "duplicate", node: found.body });
            } else {
                seen.set(found.normalized, found.id);
            }
        };
        const handlers: [string, (node: Rule.Node) => void][] = [["CallExpression", onCall]];
        return Object.fromEntries(handlers);
    },

    meta: {
        docs: { description: "Disallow two context fragments with identical (whitespace-normalized) bodies." },
        messages: {
            duplicate:
                "Fragment '{{id}}' has a body identical to '{{other}}'. Collapse the duplication into one fragment (share a param or gate one fragment on a condition). [no_duplicate_fragment]",
        },
        schema: [],
        type: "problem",
    },
} satisfies Rule.RuleModule;
```
