# core/normalizers/link.normalizer.ts

> 42 lines of code and 15 definitions.

Tree: Build tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build#file-build-core-normalizers-link-normalizer-ts
Source text: https://banes-lab.com/assets/sources/source.aa99ce530267d676f99eb32fe1db8d4bb8d317b1f41c5abdf41450d499b7eb8f.generated.txt

## Definitions

- `relinked` (lexical_declaration, line 34, exported)
- `relinkLine` (lexical_declaration, line 18)
- `targetOf` (lexical_declaration, line 9)
- `ROOT_LINK` (lexical_declaration, line 7)
- `hash` (lexical_declaration, line 10)
- `path` (lexical_declaration, line 11)
- `fragment` (lexical_declaration, line 12)
- `[page = "", tab = null]` (lexical_declaration, line 13)
- `file` (lexical_declaration, line 14)
- `out` (lexical_declaration, line 19)
- `index` (lexical_declaration, line 20)
- `open` (lexical_declaration, line 22)
- `close` (lexical_declaration, line 23)
- `start` (lexical_declaration, line 27)
- `fenced` (lexical_declaration, line 35, exported)

## Used by

- [core/renderers/chapter.renderer.ts](https://banes-lab.com/source/build/core/renderers/chapter.renderer.ts.md)
- [core/renderers/wiki.renderer.ts](https://banes-lab.com/source/build/core/renderers/wiki.renderer.ts.md)

## Source

```typescript
import { FENCE_MARK, FOLDER_SEPARATOR, LINK_CLOSE, LINK_MIDDLE } from "#configuration/constants/chapter.constants";
import { ANCHOR_MARK } from "#configuration/constants/readme.constants";
import { LINE_END } from "#configuration/constants/inventory.constants";
import type { LinkResolver } from "#types/chapter.types";
import { SITE_URL } from "@banes-lab/web/core/assets/link.assets.ts";

const ROOT_LINK = LINK_MIDDLE + FOLDER_SEPARATOR;

const targetOf = function targetOf(target: string, resolve: LinkResolver): string {
    const hash = target.indexOf(ANCHOR_MARK);
    const path = hash === -1 ? target : target.slice(0, hash);
    const fragment = hash === -1 ? "" : target.slice(hash);
    const [page = "", tab = null] = path.split(FOLDER_SEPARATOR).filter((segment) => segment.length > 0);
    const file = resolve(page, tab);
    return file === null ? SITE_URL + target : file + fragment;
};

const relinkLine = function relinkLine(line: string, resolve: LinkResolver): string {
    let out = "";
    let index = 0;
    while (index < line.length) {
        const open = line.indexOf(ROOT_LINK, index);
        const close = open === -1 ? -1 : line.indexOf(LINK_CLOSE, open);
        if (close === -1) {
            return out + line.slice(index);
        }
        const start = open + LINK_MIDDLE.length;
        out += line.slice(index, start) + targetOf(line.slice(start, close), resolve);
        index = close;
    }
    return out;
};

export const relinked = function relinked(markdown: string, resolve: LinkResolver): string {
    let fenced = false;
    return markdown
        .split(LINE_END)
        .map((line) => {
            if (line.startsWith(FENCE_MARK)) {
                fenced = !fenced;
                return line;
            }
            return fenced ? line : relinkLine(line, resolve);
        })
        .join(LINE_END);
};
```
