# core/validators/element.validator.ts

> 39 lines of code and 8 definitions.

Tree: Bane's Lab Build Scripts
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build-scripts#file-build-scripts-core-validators-element-validator-ts
Source text: https://banes-lab.com/source/build-scripts/core/validators/element.validator.ts.txt

Listed in [core/validators](https://banes-lab.com/api/source/build-scripts/core/validators.md), after [core/validators/definition.validator.ts](https://banes-lab.com/source/build-scripts/core/validators/definition.validator.ts.md) and before [core/validators/figure.validator.ts](https://banes-lab.com/source/build-scripts/core/validators/figure.validator.ts.md).

## Definitions

- `findingOf` (lexical_declaration, line 26)
- `accessibleName` (lexical_declaration, line 16)
- `checkLinkNames` (lexical_declaration, line 38, exported)
- `SPACE` (lexical_declaration, line 7)
- `imageNames` (lexical_declaration, line 9)
- `candidates` (lexical_declaration, line 17)
- `href` (lexical_declaration, line 27)
- `text` (lexical_declaration, line 28)

## Contained in

- [core/validators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-validators.md)

## Uses

- [configuration/strings/element.strings.ts](https://banes-lab.com/source/build-scripts/configuration/strings/element.strings.ts.md)
- [core/adapters/document.adapter.ts](https://banes-lab.com/source/build-scripts/core/adapters/document.adapter.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-configuration-strings.md)
- [core/adapters](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-adapters.md)
- [core/validators](https://banes-lab.com/anatomy/build-scripts/folder-build-scripts-core-validators.md)

## Source

```typescript
import { ALT_ATTRIBUTE, ARIA_LABEL_ATTRIBUTE, HREF_ATTRIBUTE, TITLE_ATTRIBUTE } from "#core/ids/element.ids";
import { genericLink, namelessLink } from "#configuration/strings/element.strings";
import type { Finding } from "#types/validation.types";
import { isGenericLabel } from "@banes-lab/web/core/predicates/link.predicate.ts";
import { readDocument } from "#core/adapters/document.adapter";

const SPACE = " ";

const imageNames = function imageNames(anchor: Element): string {
    return [...anchor.querySelectorAll("img")]
        .map((image) => image.getAttribute(ALT_ATTRIBUTE)?.trim() ?? "")
        .filter((alt) => alt.length > 0)
        .join(SPACE);
};

const accessibleName = function accessibleName(anchor: Element): string {
    const candidates = [
        anchor.getAttribute(ARIA_LABEL_ATTRIBUTE) ?? "",
        anchor.textContent,
        imageNames(anchor),
        anchor.getAttribute(TITLE_ATTRIBUTE) ?? "",
    ];
    return candidates.map((candidate) => candidate.trim()).find((name) => name.length > 0) ?? "";
};

const findingOf = function findingOf(file: string, anchor: Element): Finding | null {
    const href = anchor.getAttribute(HREF_ATTRIBUTE) ?? "";
    const text = anchor.textContent.trim();
    if (accessibleName(anchor).length === 0) {
        return { file, message: namelessLink(href) };
    }
    if (isGenericLabel(text)) {
        return { file, message: genericLink(href, text) };
    }
    return null;
};

export const checkLinkNames = function checkLinkNames(file: string, html: string): Finding[] {
    return readDocument(html, (document) =>
        [...document.querySelectorAll("a[href]")]
            .map((anchor) => findingOf(file, anchor))
            .filter((finding): finding is Finding => finding !== null),
    );
};
```
