# core/loaders/package.loader.ts

> 38 lines of code and 15 definitions.

Tree: GovLab Docs
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-loaders-package-loader-ts
Source text: https://banes-lab.com/source/docs/core/loaders/package.loader.ts.txt

Listed in [core/loaders](https://banes-lab.com/api/source/docs/core/loaders.md), after [core/loaders/manifest.loader.ts](https://banes-lab.com/source/docs/core/loaders/manifest.loader.ts.md) and before [core/loaders/plugin.loader.ts](https://banes-lab.com/source/docs/core/loaders/plugin.loader.ts.md).

## Definitions

- `memberFile` (lexical_declaration, line 31)
- `workspaceMembers` (lexical_declaration, line 35, exported)
- `expandWorkspace` (lexical_declaration, line 17)
- `declaredGlobs` (lexical_declaration, line 11)
- `membersWithoutManifest` (lexical_declaration, line 42, exported)
- `WILDCARD` (lexical_declaration, line 7)
- `SEPARATOR` (lexical_declaration, line 8)
- `WORKSPACES_KEY` (lexical_declaration, line 9)
- `manifest` (lexical_declaration, line 12)
- `globs` (lexical_declaration, line 13)
- `cut` (lexical_declaration, line 21)
- `parent` (lexical_declaration, line 22)
- `leaf` (lexical_declaration, line 23)
- `prefix` (lexical_declaration, line 24)
- `parentDir` (lexical_declaration, line 25)

## Contained in

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

## Uses

- [core/loaders/base.loader.ts](https://banes-lab.com/source/docs/core/loaders/base.loader.ts.md)

## Linked from

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

## Source

```typescript
import { MANIFEST_FILE, PACKAGE_FILE } from "#configuration/constants/document.constants";
import { readDirSafe, readJsonSafe } from "#core/loaders/base.loader";
import { existsSync } from "node:fs";
import { isRecord } from "#core/predicates/record.predicate";
import { join } from "node:path";

const WILDCARD = "*";
const SEPARATOR = "/";
const WORKSPACES_KEY = "workspaces";

const declaredGlobs = function declaredGlobs(root: string): string[] {
    const manifest = readJsonSafe(join(root, PACKAGE_FILE));
    const globs = isRecord(manifest) ? manifest[WORKSPACES_KEY] : undefined;
    return Array.isArray(globs) ? globs.filter((glob): glob is string => typeof glob === "string") : [];
};

const expandWorkspace = function expandWorkspace(root: string, pattern: string): string[] {
    if (!pattern.includes(WILDCARD)) {
        return [pattern];
    }
    const cut = pattern.lastIndexOf(SEPARATOR);
    const parent = cut === -1 ? "" : pattern.slice(0, cut);
    const leaf = pattern.slice(cut + 1);
    const prefix = leaf.slice(0, leaf.indexOf(WILDCARD));
    const parentDir = parent === "" ? root : join(root, ...parent.split(SEPARATOR));
    return readDirSafe(parentDir)
        .filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix))
        .map((entry) => (parent === "" ? entry.name : `${parent}${SEPARATOR}${entry.name}`));
};

const memberFile = function memberFile(root: string, dir: string, file: string): string {
    return join(root, ...dir.split(SEPARATOR), file);
};

export const workspaceMembers = function workspaceMembers(root: string): string[] {
    return declaredGlobs(root)
        .flatMap((pattern) => expandWorkspace(root, pattern))
        .filter((dir) => existsSync(memberFile(root, dir, PACKAGE_FILE)))
        .toSorted((left, right) => left.localeCompare(right));
};

export const membersWithoutManifest = function membersWithoutManifest(root: string): string[] {
    return workspaceMembers(root).filter((dir) => !existsSync(memberFile(root, dir, MANIFEST_FILE)));
};
```
