# rules/eslint/closure-no-undeclared-type-layer.eslint.rule.ts

> 40 lines of code and 7 definitions.

Tree: Governance tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/governance#file-governance-rules-eslint-closure-no-undeclared-type-layer-eslint-rule-ts
Source text: https://banes-lab.com/source/governance/rules/eslint/closure-no-undeclared-type-layer.eslint.rule.ts.txt

Listed in [rules/eslint](https://banes-lab.com/api/source/governance/rules/eslint.md), after [rules/eslint/closure-no-type-layer-drift.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-type-layer-drift.eslint.rule.ts.md) and before [rules/eslint/closure-no-unreachable-file.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/closure-no-unreachable-file.eslint.rule.ts.md).

## Definitions

- `isTypesFile` (lexical_declaration, line 12)
- `create` (method_definition, line 18, exported)
- `TYPES_CONCERN` (lexical_declaration, line 9)
- `TYPES_ROOT` (lexical_declaration, line 10)
- `{ filename }` (lexical_declaration, line 19, exported)
- `rel` (lexical_declaration, line 20, exported)
- `program` (method_definition, line 25, exported)

## Uses

- [shared/factories/listener.factory.ts](https://banes-lab.com/source/governance/shared/factories/listener.factory.ts.md)
- [shared/matchers/filename.matcher.ts](https://banes-lab.com/source/governance/shared/matchers/filename.matcher.ts.md)

## Records this file checks

- [Layered Architecture](https://banes-lab.com/records/architecture/layered-architecture.md)

## Source

```typescript
import { GOVERNED_ROOT, basenameOf } from "../../shared/resolvers/anchor.resolver.ts";
import type { LocalRule, RuleContext, RuleListener } from "../../types/rule.types.ts";
import { TYPE_LAYERS, relativeFromMember } from "../../shared/manifests/layer.manifest.ts";
import { containerPath } from "../../shared/resolvers/container.resolver.ts";
import { defineCheck } from "@govlab/context/check";
import { hasConcern } from "../../shared/matchers/filename.matcher.ts";
import { listener } from "../../shared/factories/listener.factory.ts";

const TYPES_CONCERN = "types";
const TYPES_ROOT = containerPath(TYPES_CONCERN, GOVERNED_ROOT);

const isTypesFile = function isTypesFile(filepath: string): boolean {
    const rel = relativeFromMember(filepath);
    return rel.startsWith(TYPES_ROOT) && hasConcern(basenameOf(rel), TYPES_CONCERN);
};

export default {
    create(context: RuleContext): RuleListener {
        const { filename } = context;
        const rel = relativeFromMember(filename);
        if (!isTypesFile(filename) || TYPE_LAYERS.has(rel)) {
            return {};
        }
        return listener({
            program(_view, node) {
                context.report({ data: { path: rel }, messageId: "undeclaredTypeLayer", node });
            },
        });
    },
    meta: {
        docs: {
            checks: defineCheck({ detects: [], enforces: ["architecture:layered-architecture"] }),
            description:
                "Every types file in the bucket is classified in the layer manifest as `platform` or `product`. The bucket is flat and its path carries no tier, so nothing else can decide the classification — leaving it undeclared makes the platform-to-product import rule unable to judge that file at all, and it fails open rather than reporting.",
        },
        messages: {
            undeclaredTypeLayer:
                "Types file `{{path}}` has no tier classification. Add an entry to TYPE_LAYERS in `.govlab/shared/manifests/layer.manifest.ts` — `platform` for an application-agnostic contract any tier may import, `product` for one tied to this application's specifics. Unclassified means the tier boundary cannot reason about the file at all.",
        },
        schema: [],
        type: "problem",
    },
} satisfies LocalRule;
```
