# test.govlab/quality/core/loaders/plugin.loader.test.ts

> 118 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-govlab-quality-core-loaders-plugin-loader-test-ts
Source text: https://banes-lab.com/source/testing/test.govlab/quality/core/loaders/plugin.loader.test.ts.txt

Listed in [test.govlab/quality/core/loaders](https://banes-lab.com/api/source/testing/test.govlab/quality/core/loaders.md), after [test.govlab/quality/core/loaders/package.loader.test.ts](https://banes-lab.com/source/testing/test.govlab/quality/core/loaders/package.loader.test.ts.md) and before [test.govlab/quality/core/loaders/rule.loader.test.ts](https://banes-lab.com/source/testing/test.govlab/quality/core/loaders/rule.loader.test.ts.md).

## Contained in

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

## Source

```typescript
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import assert from "node:assert/strict";
import { join } from "node:path";
import { loadUserPlugins } from "@govlab/quality/core/loaders/plugin.loader.ts";
import { relativePath } from "@ssot/paths";
import { test } from "vitest";
import { tmpdir } from "node:os";
import { writeVerbatim } from "@govlab/canonical-write";

const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null;

const nsRule = function nsRule(eslint: Record<string, unknown>, ns: string, rule: string): boolean {
    const plugin = eslint[ns];
    if (!isRecord(plugin) || !isRecord(plugin["rules"])) {
        throw new Error(`expected eslint plugin '${ns}' with rules`);
    }
    return plugin["rules"][rule] !== undefined;
};

const fixtureRoot = function fixtureRoot(files: Record<string, string>): string {
    const root = mkdtempSync(join(tmpdir(), "govlab-userplugins-"));
    const dir = join(root, relativePath("govlabHost.plugins"));
    mkdirSync(dir, { recursive: true });
    for (const [name, body] of Object.entries(files)) {
        writeVerbatim(join(dir, name), body);
    }
    return root;
};

test("loads custom eslint and stylelint plugin code from the host's plugins folder (code only, no enablement)", async () => {
    const root = fixtureRoot({
        "es.mjs":
            'export default { tool: "eslint", plugins: { myns: { rules: { "no-foo": { create() { return {}; } } } } } };',
        "sl.mjs": 'export default { tool: "stylelint", plugins: [{ ruleName: "myns/no-bar" }] };',
    });
    try {
        const user = await loadUserPlugins(root);
        assert.ok(Object.hasOwn(user.eslint, "myns"), "the custom eslint plugin namespace registers");
        assert.ok(nsRule(user.eslint, "myns", "no-foo"), "the custom rule code is present");
        assert.equal(user.stylelint.length, 1, "the custom stylelint plugin registers");
    } finally {
        rmSync(root, { force: true, recursive: true });
    }
});

const MALFORMED: Record<string, [string, string]> = {
    "a plugin for an unknown tool": ["other-tool.mjs", "must default-export"],
    "a plugin that throws on load": ["throws.mjs", "failed to load"],
    "a plugin with no default export": ["no-default.mjs", "has no default-exported object"],
    "a plugin with no plugins field": ["no-plugins.mjs", "must default-export"],
};

const MALFORMED_BODIES: Record<string, string> = {
    "no-default.mjs": "export const nope = 1;",
    "no-plugins.mjs": 'export default { tool: "eslint" };',
    "other-tool.mjs": 'export default { tool: "prettier", plugins: {} };',
    "throws.mjs": "throw new Error('boom');",
};

for (const [label, [file, message]] of Object.entries(MALFORMED)) {
    test(`${label} fails the load and names the file`, async () => {
        const root = fixtureRoot({
            [file]: MALFORMED_BODIES[file] ?? "",
            "ok.mjs": 'export default { tool: "eslint", plugins: { good: { rules: {} } } };',
        });
        try {
            await assert.rejects(loadUserPlugins(root), (error: unknown) => {
                assert.ok(error instanceof Error);
                assert.ok(error.message.includes(file), "the message names the file");
                assert.ok(error.message.includes(message), "the message states the defect");
                return true;
            });
        } finally {
            rmSync(root, { force: true, recursive: true });
        }
    });
}

test("a custom plugin whose namespace shadows a core namespace fails the load", async () => {
    const root = fixtureRoot({
        "own.mjs": 'export default { tool: "eslint", plugins: { mine: { rules: {} } } };',
        "shadow.mjs": 'export default { tool: "eslint", plugins: { govlab: { rules: {} } } };',
    });
    try {
        await assert.rejects(
            loadUserPlugins(root, { reservedNamespaces: new Set(["govlab", "govlab-extraction"]) }),
            (error: unknown) =>
                error instanceof Error && error.message.includes('claims the reserved namespace "govlab"'),
        );
    } finally {
        rmSync(root, { force: true, recursive: true });
    }
});

test("two files extending the same namespace: their rules merge into one plugin object", async () => {
    const root = fixtureRoot({
        "a-first.mjs": 'export default { tool: "eslint", plugins: { dup: { rules: { one: {} } } } };',
        "b-second.mjs": 'export default { tool: "eslint", plugins: { dup: { rules: { two: {} } } } };',
    });
    try {
        const user = await loadUserPlugins(root);
        assert.ok(nsRule(user.eslint, "dup", "one"), "the first file's rule registers");
        assert.ok(nsRule(user.eslint, "dup", "two"), "the second file's rule merges into the same namespace");
        assert.deepEqual(Object.keys(user.eslint), ["dup"], "a single merged plugin object per namespace");
    } finally {
        rmSync(root, { force: true, recursive: true });
    }
});

test("no plugins folder yields empty tiers (no throw)", async () => {
    const root = mkdtempSync(join(tmpdir(), "govlab-empty-"));
    try {
        const user = await loadUserPlugins(root);
        assert.deepEqual(user.eslint, {});
        assert.deepEqual(user.stylelint, []);
    } finally {
        rmSync(root, { force: true, recursive: true });
    }
});

test("global (~/.govlab) is NOT scanned unless allowGlobal is set", async () => {
    const root = fixtureRoot({ "es.mjs": 'export default { tool: "eslint", plugins: { local: { rules: {} } } };' });
    try {
        const user = await loadUserPlugins(root, { allowGlobal: false });
        assert.ok(Object.hasOwn(user.eslint, "local"));
    } finally {
        rmSync(root, { force: true, recursive: true });
    }
});
```
