# rules/eslint/no-inline-style-write.eslint.rule.ts

> 79 lines of code and 16 definitions.

Tree: Governance tree
Language: typescript
Canonical: https://banes-lab.com/anatomy/governance#file-governance-rules-eslint-no-inline-style-write-eslint-rule-ts
Source text: https://banes-lab.com/source/governance/rules/eslint/no-inline-style-write.eslint.rule.ts.txt

Listed in [rules/eslint](https://banes-lab.com/api/source/governance/rules/eslint.md), after [rules/eslint/no-inline-icon-options.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-inline-icon-options.eslint.rule.ts.md) and before [rules/eslint/no-linear-scan-lookup.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/no-linear-scan-lookup.eslint.rule.ts.md).

## Definitions

- `isPresentationAccessor` (lexical_declaration, line 20)
- `isDeclarationCall` (lexical_declaration, line 28)
- `isPresentationTarget` (lexical_declaration, line 24)
- `isPresentationAttributeCall` (lexical_declaration, line 36)
- `create` (method_definition, line 50, exported)
- `assignmentExpression` (method_definition, line 55, exported)
- `callExpression` (method_definition, line 60, exported)
- `PRESENTATION_ACCESSORS` (lexical_declaration, line 9)
- `DECLARATION_METHODS` (lexical_declaration, line 10)
- `ATTRIBUTE_METHODS` (lexical_declaration, line 11)
- `PRESENTATION_ATTRIBUTE` (lexical_declaration, line 12)
- `ATTRIBUTE_NAME_INDEX` (lexical_declaration, line 13)
- `DECLARATION_MODULES` (lexical_declaration, line 18)
- `callee` (lexical_declaration, line 37)
- `method` (lexical_declaration, line 41)
- `index` (lexical_declaration, line 45)

## Uses

- [shared/allowlists/factory.allowlist.ts](https://banes-lab.com/source/governance/shared/allowlists/factory.allowlist.ts.md)
- [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)

## Records this file checks

- [Secure by Default](https://banes-lab.com/records/architecture/secure-by-default.md)

## Source

```typescript
import type { LocalRule, RuleContext, RuleListener } from "../../types/rule.types.ts";
import { MODULE_OPTIONS_SCHEMA, declaredModules } from "../../shared/allowlists/factory.allowlist.ts";
import { argumentAt, isType, literalString, nameOf, nodeAt } from "../../shared/selectors/syntax.selector.ts";
import type { AstNode } from "../../types/syntax.types.ts";
import { basenameOf } from "../../shared/resolvers/anchor.resolver.ts";
import { defineCheck } from "@govlab/context/check";
import { listener } from "../../shared/factories/listener.factory.ts";

const PRESENTATION_ACCESSORS = new Set(["style", "attributeStyleMap"]);
const DECLARATION_METHODS = new Set(["setProperty", "removeProperty", "set", "append", "delete", "clear"]);
const ATTRIBUTE_METHODS = new Set(["setAttribute", "setAttributeNS"]);
const PRESENTATION_ATTRIBUTE = "style";
const ATTRIBUTE_NAME_INDEX = new Map([
    ["setAttribute", 0],
    ["setAttributeNS", 1],
]);

const DECLARATION_MODULES: ReadonlySet<string> = new Set<string>();

const isPresentationAccessor = function isPresentationAccessor(node: AstNode | null): boolean {
    return isType(node, "MemberExpression") && PRESENTATION_ACCESSORS.has(nameOf(nodeAt(node, "property")));
};

const isPresentationTarget = function isPresentationTarget(node: AstNode | null): boolean {
    return isPresentationAccessor(node) || isPresentationAccessor(nodeAt(node, "object"));
};

const isDeclarationCall = function isDeclarationCall(node: AstNode): boolean {
    const callee = nodeAt(node, "callee");
    if (!isType(callee, "MemberExpression") || !isPresentationAccessor(nodeAt(callee, "object"))) {
        return false;
    }
    return DECLARATION_METHODS.has(nameOf(nodeAt(callee, "property")));
};

const isPresentationAttributeCall = function isPresentationAttributeCall(node: AstNode): boolean {
    const callee = nodeAt(node, "callee");
    if (!isType(callee, "MemberExpression")) {
        return false;
    }
    const method = nameOf(nodeAt(callee, "property"));
    if (!ATTRIBUTE_METHODS.has(method)) {
        return false;
    }
    const index = ATTRIBUTE_NAME_INDEX.get(method) ?? 0;
    return literalString(argumentAt(node, index)) === PRESENTATION_ATTRIBUTE;
};

export default {
    create(context: RuleContext): RuleListener {
        if (declaredModules(context, DECLARATION_MODULES).has(basenameOf(context.filename))) {
            return {};
        }
        return listener({
            assignmentExpression(view, node) {
                if (isPresentationTarget(nodeAt(view, "left"))) {
                    context.report({ messageId: "presentationWrite", node });
                }
            },
            callExpression(view, node) {
                if (isDeclarationCall(view)) {
                    context.report({ messageId: "presentationWrite", node });
                    return;
                }
                if (isPresentationAttributeCall(view)) {
                    context.report({ messageId: "presentationAttribute", node });
                }
            },
        });
    },
    meta: {
        docs: {
            checks: defineCheck({ detects: [], enforces: ["architecture:secure-by-default"] }),
            description:
                "A presentation value written onto a node itself is refused by a deny-by-default content policy that admits stylesheets only, so the value silently never applies. Declarations belong in a stylesheet the policy admits; which module owns runtime declarations is DATA in the module registry, never a filename written into this rule.",
        },
        messages: {
            presentationAttribute:
                "Setting a node's presentation attribute is refused by a deny-by-default content policy, so the value never applies. Declare the value through the module that owns runtime declarations.",
            presentationWrite:
                "Writing a presentation value onto a node is refused by a deny-by-default content policy that admits stylesheets only, so the value never applies. Declare the value through the module that owns runtime declarations.",
        },
        schema: MODULE_OPTIONS_SCHEMA,
        type: "problem",
    },
} satisfies LocalRule;
```
