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

rules/eslint/scoped-resource-release.eslint.rule.ts is a file in GovLab Extension Host. 70 lines of code and 9 definitions.

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;