# domain/converters/search.fragment.converter.ts

> 68 lines of code and 12 definitions.

Tree: Site tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-domain-converters-search-fragment-converter-ts
Source text: https://banes-lab.com/assets/sources/source.ecd6bda7046b11291d0477800ff215ede0f15282ac5763667b981891d990d67b.generated.txt

## Definitions

- `proseWords` (lexical_declaration, line 18)
- `narrowSubsection` (lexical_declaration, line 35, exported)
- `narrowSection` (lexical_declaration, line 54, exported)
- `heldWords` (lexical_declaration, line 6)
- `wordsIn` (lexical_declaration, line 8)
- `held` (lexical_declaration, line 9)
- `words` (lexical_declaration, line 13)
- `narrowGlossaries` (lexical_declaration, line 28)
- `entries` (lexical_declaration, line 30)
- `subsections` (lexical_declaration, line 58, exported)
- `narrowed` (lexical_declaration, line 59, exported)
- `blocks` (lexical_declaration, line 62, exported)

## Uses

- [core/matchers/search.matcher.ts](https://banes-lab.com/source/tree/core/matchers/search.matcher.ts.md)

## Source

```typescript
import type { Section, Subsection } from "#types/document.types";
import { isGlossary, textsOf } from "#core/analyzers/search.analyzer";
import { markupWords, matchesAll } from "#core/matchers/search.matcher";
import type { Block } from "#types/block.types";

const heldWords = new WeakMap<object, readonly string[]>();

const wordsIn = function wordsIn(value: object): readonly string[] {
    const held = heldWords.get(value);
    if (held !== undefined) {
        return held;
    }
    const words = textsOf(value).flatMap(markupWords);
    heldWords.set(value, words);
    return words;
};

const proseWords = function proseWords(
    parts: readonly (string | undefined)[],
    blocks: readonly Block[],
): readonly string[] {
    return [
        ...parts.flatMap((part) => (part === undefined ? [] : markupWords(part))),
        ...blocks.filter((block) => !isGlossary(block)).flatMap(wordsIn),
    ];
};

const narrowGlossaries = function narrowGlossaries(terms: readonly string[], blocks: readonly Block[]): Block[] {
    return blocks.filter(isGlossary).flatMap((block) => {
        const entries = block.entries.filter((entry) => matchesAll(terms, wordsIn(entry)));
        return entries.length === 0 ? [] : [{ ...block, entries }];
    });
};

export const narrowSubsection = function narrowSubsection(
    terms: readonly string[],
    sub: Subsection,
): Subsection | null {
    if (matchesAll(terms, proseWords([sub.title, sub.content], sub.blocks ?? []))) {
        return sub;
    }
    const blocks = narrowGlossaries(terms, sub.blocks ?? []);
    if (blocks.length === 0) {
        return null;
    }
    return {
        blocks,
        title: sub.title,
        ...(sub.id === undefined ? {} : { id: sub.id }),
        ...(sub.layout === undefined ? {} : { layout: sub.layout }),
    };
};

export const narrowSection = function narrowSection(terms: readonly string[], section: Section): Section | null {
    if (matchesAll(terms, proseWords([section.title, section.intro], section.blocks ?? []))) {
        return section;
    }
    const subsections = section.subsections.flatMap((sub) => {
        const narrowed = narrowSubsection(terms, sub);
        return narrowed === null ? [] : [narrowed];
    });
    const blocks = narrowGlossaries(terms, section.blocks ?? []);
    if (subsections.length === 0 && blocks.length === 0) {
        return null;
    }
    return {
        icon: section.icon,
        id: section.id,
        subsections,
        title: section.title,
        ...(blocks.length === 0 ? {} : { blocks }),
        ...(section.layout === undefined ? {} : { layout: section.layout }),
    };
};
```
