# core/fragment/eslint/no-hardcoded-context-token.eslint.rule.ts

> 62 lines of code and 12 definitions.

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

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

## Definitions

- `scan` (method_definition, line 23)
- `onEnter` (method_definition, line 31)
- `onExit` (method_definition, line 37)
- `ContextTokenVisitor` (class_declaration, line 5)
- `fragmentDepth` (public_field_definition, line 6)
- `context` (public_field_definition, line 7)
- `constructor` (method_definition, line 9)
- `listeners` (method_definition, line 13)
- `handlers` (lexical_declaration, line 14)
- `onLiteral` (method_definition, line 43)
- `onTemplate` (method_definition, line 49)
- `create` (method_definition, line 57, exported)

## Contained in

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

## Uses

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

## Linked from

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

## Source

```typescript
import type { Rule } from "eslint";
import { biasTokensIn } from "#core/matchers/context.matcher";
import { isDefineFragmentCall } from "#core/predicates/context.fragment.predicate";

class ContextTokenVisitor {
    private fragmentDepth = 0;
    private readonly context: Rule.RuleContext;

    public constructor(context: Rule.RuleContext) {
        this.context = context;
    }

    public listeners(): Rule.RuleListener {
        const handlers: [string, (node: Rule.Node) => void][] = [
            ["CallExpression", this.onEnter.bind(this)],
            ["CallExpression:exit", this.onExit.bind(this)],
            ["Literal", this.onLiteral.bind(this)],
            ["TemplateElement", this.onTemplate.bind(this)],
        ];
        return Object.fromEntries(handlers);
    }

    private scan(node: Rule.Node, text: string): void {
        if (this.fragmentDepth > 0) {
            for (const token of biasTokensIn(text)) {
                this.context.report({ data: { token }, messageId: "token", node });
            }
        }
    }

    private onEnter(node: Rule.Node): void {
        if (isDefineFragmentCall(node)) {
            this.fragmentDepth += 1;
        }
    }

    private onExit(node: Rule.Node): void {
        if (isDefineFragmentCall(node)) {
            this.fragmentDepth -= 1;
        }
    }

    private onLiteral(node: Rule.Node): void {
        if (node.type === "Literal" && typeof node.value === "string") {
            this.scan(node, node.value);
        }
    }

    private onTemplate(node: Rule.Node): void {
        if (node.type === "TemplateElement") {
            this.scan(node, node.value.cooked ?? node.value.raw);
        }
    }
}

export default {
    create(context: Rule.RuleContext): Rule.RuleListener {
        return new ContextTokenVisitor(context).listeners();
    },

    meta: {
        docs: {
            description:
                "Disallow hardcoded stack/tool/product tokens in a context fragment body — source them from a param.",
        },
        messages: {
            token: "Fragment body hardcodes the stack/tool token '{{token}}'. Route it through a param derived from the tool registry / config, not a literal. [no_hardcoded_context_token]",
        },
        schema: [],
        type: "problem",
    },
} satisfies Rule.RuleModule;
```
