# core/loaders/package.loader.ts

> 55 lines of code and 17 definitions.

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

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

## Definitions

- `workspaceDirs` (lexical_declaration, line 35, exported)
- `expandWorkspace` (lexical_declaration, line 17)
- `workspacePatterns` (lexical_declaration, line 30)
- `dependentWorkspaces` (lexical_declaration, line 41, exported)
- `packageNameIndex` (lexical_declaration, line 53, exported)
- `PACKAGE_MANIFEST` (lexical_declaration, line 6)
- `nameOfPackage` (lexical_declaration, line 8)
- `manifest` (lexical_declaration, line 9)
- `star` (lexical_declaration, line 18)
- `cut` (lexical_declaration, line 22)
- `base` (lexical_declaration, line 23)
- `prefix` (lexical_declaration, line 24)
- `declared` (lexical_declaration, line 31)
- `own` (lexical_declaration, line 43, exported)
- `dependencies` (lexical_declaration, line 48, exported)
- `byName` (lexical_declaration, line 54, exported)
- `name` (lexical_declaration, line 56, exported)

## Contained in

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

## Uses

- [core/converters/filename.converter.ts](https://banes-lab.com/source/quality/core/converters/filename.converter.ts.md)
- [core/loaders/source.loader.ts](https://banes-lab.com/source/quality/core/loaders/source.loader.ts.md)

## Used by

- [core/adapters/tool.knip.adapter.ts](https://banes-lab.com/source/quality/core/adapters/tool.knip.adapter.ts.md)

## Linked from

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

## Source

```typescript
import { readJsonField, safeReaddir } from "#core/loaders/source.loader";
import { forwardSlashed } from "#core/converters/filename.converter";
import fs from "node:fs";
import path from "node:path";

const PACKAGE_MANIFEST = "package.json";

const nameOfPackage = function nameOfPackage(dir: string): string {
    const manifest = path.join(dir, PACKAGE_MANIFEST);
    if (!fs.existsSync(manifest)) {
        return "";
    }
    const name = readJsonField(manifest, "name");
    return typeof name === "string" ? name : "";
};

const expandWorkspace = function expandWorkspace(rootAbs: string, pattern: string): string[] {
    const star = pattern.indexOf("*");
    if (star === -1) {
        return [forwardSlashed(path.join(rootAbs, pattern))];
    }
    const cut = pattern.lastIndexOf("/", star);
    const base = path.join(rootAbs, pattern.slice(0, cut));
    const prefix = pattern.slice(cut + 1, star);
    return safeReaddir(base)
        .filter((entry) => entry.isDirectory() && entry.name.startsWith(prefix))
        .map((entry) => forwardSlashed(path.join(base, entry.name)));
};

const workspacePatterns = function workspacePatterns(rootAbs: string): string[] {
    const declared = readJsonField(path.join(rootAbs, PACKAGE_MANIFEST), "workspaces");
    return Array.isArray(declared) ? declared.filter((value): value is string => typeof value === "string") : [];
};

export const workspaceDirs = function workspaceDirs(rootAbs: string): string[] {
    return workspacePatterns(rootAbs)
        .flatMap((pattern) => expandWorkspace(rootAbs, pattern))
        .filter((dir) => fs.existsSync(path.join(dir, PACKAGE_MANIFEST)));
};

export const dependentWorkspaces = function dependentWorkspaces(rootAbs: string, packageDir: string): string[] {
    const name = nameOfPackage(packageDir);
    const own = forwardSlashed(path.resolve(packageDir));
    return workspaceDirs(rootAbs).filter((dir) => {
        if (name.length === 0 || forwardSlashed(path.resolve(dir)) === own) {
            return false;
        }
        const dependencies = readJsonField(path.join(dir, PACKAGE_MANIFEST), "dependencies");
        return typeof dependencies === "object" && dependencies !== null && name in dependencies;
    });
};

export const packageNameIndex = function packageNameIndex(rootAbs: string): Map<string, string> {
    const byName = new Map<string, string>();
    for (const dir of workspaceDirs(rootAbs)) {
        const name = nameOfPackage(dir);
        if (name.length > 0) {
            byName.set(name, dir);
        }
    }
    return byName;
};
```
