# core/resolvers/package.resolver.ts

> 43 lines of code and 12 definitions.

Tree: GovLab Stats
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/stats#file-stats-core-resolvers-package-resolver-ts
Source text: https://banes-lab.com/source/stats/core/resolvers/package.resolver.ts.txt

Listed in [core/resolvers](https://banes-lab.com/api/source/stats/core/resolvers.md), after [core/resolvers/folder.resolver.ts](https://banes-lab.com/source/stats/core/resolvers/folder.resolver.ts.md).

## Definitions

- `ownerOf` (lexical_declaration, line 46, exported)
- `matchesSegment` (lexical_declaration, line 8)
- `workspaceMembers` (lexical_declaration, line 33, exported)
- `expand` (lexical_declaration, line 16)
- `memberIndex` (lexical_declaration, line 40, exported)
- `holdsManifest` (lexical_declaration, line 29)
- `[head = "", tail = ""]` (lexical_declaration, line 12)
- `rest]` (lexical_declaration, line 17)
- `manifest` (lexical_declaration, line 34, exported)
- `patterns` (lexical_declaration, line 35, exported)
- `candidates` (lexical_declaration, line 36, exported)
- `norm` (lexical_declaration, line 47, exported)

## Contained in

- [core/resolvers](https://banes-lab.com/anatomy/stats/folder-stats-core-resolvers.md)

## Uses

- [core/loaders/folder.loader.ts](https://banes-lab.com/source/stats/core/loaders/folder.loader.ts.md)
- [core/selectors/source.selector.ts](https://banes-lab.com/source/stats/core/selectors/source.selector.ts.md)

## Used by

- [core/analyzers/source.analyzer.ts](https://banes-lab.com/source/stats/core/analyzers/source.analyzer.ts.md)
- [core/analyzers/taxonomy.analyzer.ts](https://banes-lab.com/source/stats/core/analyzers/taxonomy.analyzer.ts.md)

## Linked from

- [core/analyzers](https://banes-lab.com/anatomy/stats/folder-stats-core-analyzers.md)
- [core/loaders](https://banes-lab.com/anatomy/stats/folder-stats-core-loaders.md)
- [core/resolvers](https://banes-lab.com/anatomy/stats/folder-stats-core-resolvers.md)
- [core/selectors](https://banes-lab.com/anatomy/stats/folder-stats-core-selectors.md)

## Source

```typescript
import { PACKAGE_MANIFEST, WILDCARD, WORKSPACES_KEY } from "#configuration/constants/package.constants";
import { arrayField, stringsIn } from "#core/selectors/field.selector";
import path from "node:path";
import { posixOf } from "#core/selectors/source.selector";
import { readJson } from "#core/loaders/data.loader";
import { readdirSafe } from "#core/loaders/folder.loader";

const matchesSegment = function matchesSegment(pattern: string, name: string): boolean {
    if (!pattern.includes(WILDCARD)) {
        return pattern === name;
    }
    const [head = "", tail = ""] = pattern.split(WILDCARD);
    return name.startsWith(head) && name.endsWith(tail) && name.length >= head.length + tail.length;
};

const expand = function expand(absDir: string, segments: readonly string[]): string[] {
    const [head, ...rest] = segments;
    if (head === undefined) {
        return [absDir];
    }
    if (!head.includes(WILDCARD)) {
        return expand(path.join(absDir, head), rest);
    }
    return readdirSafe(absDir)
        .filter((entry) => entry.isDirectory() && matchesSegment(head, entry.name))
        .flatMap((entry) => expand(path.join(absDir, entry.name), rest));
};

const holdsManifest = function holdsManifest(abs: string): boolean {
    return readdirSafe(abs).some((entry) => entry.isFile() && entry.name === PACKAGE_MANIFEST);
};

export const workspaceMembers = function workspaceMembers(root: string): string[] {
    const manifest = readJson(path.join(root, PACKAGE_MANIFEST));
    const patterns = stringsIn(arrayField(manifest, WORKSPACES_KEY));
    const candidates = patterns.flatMap((pattern) => expand(root, pattern.split("/")));
    return [...new Set(candidates.filter(holdsManifest))].toSorted((a, b) => a.localeCompare(b));
};

export const memberIndex = function memberIndex(root: string): string[] {
    return workspaceMembers(root)
        .map((abs) => posixOf(path.relative(root, abs)))
        .toSorted((a, b) => b.length - a.length);
};

export const ownerOf = function ownerOf(members: readonly string[], relPath: string): string | null {
    const norm = posixOf(relPath);
    return members.find((member) => norm === member || norm.startsWith(`${member}/`)) ?? null;
};
```
