# rules/eslint/scoped-resource-release.eslint.rule.ts

> 70 lines of code and 9 definitions.

Tree: GovLab Extension Host
Language: typescript
Canonical: https://banes-lab.com/anatomy/governance#file-governance-rules-eslint-scoped-resource-release-eslint-rule-ts
Source text: https://banes-lab.com/source/governance/rules/eslint/scoped-resource-release.eslint.rule.ts.txt

Listed in [rules/eslint](https://banes-lab.com/api/source/governance/rules/eslint.md), after [rules/eslint/retired-collection-id.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/retired-collection-id.eslint.rule.ts.md) and before [rules/eslint/strings-composition.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-composition.eslint.rule.ts.md).

## Definitions

- `importDeclaration` (method_definition, line 27, exported)
- `program` (method_definition, line 40, exported)
- `create` (method_definition, line 22, exported)
- `Acquired` (interface_declaration, line 15)
- `names` (lexical_declaration, line 23, exported)
- `acquired` (lexical_declaration, line 24, exported)
- `source` (lexical_declaration, line 28, exported)
- `acquirer` (lexical_declaration, line 33, exported)
- `release` (lexical_declaration, line 34, exported)

## Contained in

- [rules/eslint](https://banes-lab.com/anatomy/governance/folder-governance-rules-eslint.md)

## Uses

- [shared/factories/listener.factory.ts](https://banes-lab.com/source/governance/shared/factories/listener.factory.ts.md)
- [shared/selectors/syntax.selector.ts](https://banes-lab.com/source/governance/shared/selectors/syntax.selector.ts.md)

## Detector for

- [Resource Waste/Saturation](https://banes-lab.com/records/lexicon/resource-waste-saturation.md)

## Linked from

- [shared/factories](https://banes-lab.com/anatomy/governance/folder-governance-shared-factories.md)
- [shared/selectors](https://banes-lab.com/anatomy/governance/folder-governance-shared-selectors.md)

## Source

```typescript
import type { LocalRule, RuleContext, RuleListener, RuleNode } from "../../types/rule.types.ts";
import { RESOURCE_MARK, SCOPED_RESOURCES } from "../../shared/manifests/invariant.manifest.ts";
import {
    declaredAtTop,
    importedNamesOf,
    isType,
    literalString,
    nameOf,
    nodeAt,
    nodesAt,
} from "../../shared/selectors/syntax.selector.ts";
import { defineCheck } from "@govlab/context/check";
import { listener } from "../../shared/factories/listener.factory.ts";

interface Acquired {
    readonly acquirer: string;
    readonly node: RuleNode;
    readonly release: string;
}

export default {
    create(context: RuleContext): RuleListener {
        const names = new Set<string>();
        const acquired: Acquired[] = [];
        return listener(
            {
                importDeclaration(view, raw) {
                    const source = literalString(nodeAt(view, "source")) ?? "";
                    for (const specifier of nodesAt(view, "specifiers")) {
                        if (!isType(specifier, "ImportSpecifier")) {
                            continue;
                        }
                        const acquirer = nameOf(nodeAt(specifier, "imported"));
                        const release = SCOPED_RESOURCES.get(source + RESOURCE_MARK + acquirer);
                        if (release !== undefined) {
                            acquired.push({ acquirer, node: raw, release });
                        }
                    }
                },
                program(view) {
                    for (const name of [...declaredAtTop(view), ...importedNamesOf(view)]) {
                        names.add(name);
                    }
                },
            },
            () => {
                for (const entry of acquired) {
                    if (!names.has(entry.release)) {
                        context.report({
                            data: { acquirer: entry.acquirer, release: entry.release },
                            messageId: "unreleased",
                            node: entry.node,
                        });
                    }
                }
            },
        );
    },
    meta: {
        docs: {
            checks: defineCheck({ detects: ["lexicon:resource-waste-saturation"], enforces: [] }),
            description:
                "A resource whose release takes more than its own close is acquired only beside its one release function. The resource and its release are data in the scoped resources registry. A file that acquires one without importing or declaring the release leaves what the close keeps reachable in memory.",
        },
        messages: {
            unreleased:
                "'{{acquirer}}' acquires a resource whose close keeps its contents reachable. Release it through '{{release}}', imported or declared in this file.",
        },
        schema: [],
        type: "problem",
    },
} satisfies LocalRule;
```
