mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 22:09:16 +01:00
refactor: 🚚 Organize code into sub-packages, instead of a single large package
This commit is contained in:
parent
79742f47dc
commit
a6d3ebbeef
366 changed files with 942 additions and 833 deletions
|
|
@ -1,221 +0,0 @@
|
|||
/* import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
jest,
|
||||
mock,
|
||||
test,
|
||||
} from "bun:test";
|
||||
import { ZodError, type ZodTypeAny, z } from "zod";
|
||||
import { Plugin } from "~/packages/plugin-kit";
|
||||
import { type Manifest, manifestSchema } from "~/packages/plugin-kit/schema";
|
||||
import { PluginLoader } from "./loader.ts";
|
||||
|
||||
const mockReaddir = jest.fn();
|
||||
const mockGetLogger = jest.fn(() => ({
|
||||
fatal: jest.fn(),
|
||||
}));
|
||||
const mockParseJSON5 = jest.fn();
|
||||
const mockParseJSONC = jest.fn();
|
||||
const mockFromZodError = jest.fn();
|
||||
|
||||
mock.module("node:fs/promises", () => ({
|
||||
readdir: mockReaddir,
|
||||
}));
|
||||
|
||||
mock.module("@logtape/logtape", () => ({
|
||||
getLogger: mockGetLogger,
|
||||
}));
|
||||
|
||||
mock.module("confbox", () => ({
|
||||
parseJSON5: mockParseJSON5,
|
||||
parseJSONC: mockParseJSONC,
|
||||
}));
|
||||
|
||||
mock.module("zod-validation-error", () => ({
|
||||
fromZodError: mockFromZodError,
|
||||
}));
|
||||
|
||||
describe("PluginLoader", () => {
|
||||
let pluginLoader: PluginLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
pluginLoader = new PluginLoader();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test("getDirectories should return directories", async () => {
|
||||
mockReaddir.mockResolvedValue([
|
||||
{ name: "dir1", isDirectory: (): true => true },
|
||||
{ name: "file1", isDirectory: (): false => false },
|
||||
{ name: "dir2", isDirectory: (): true => true },
|
||||
]);
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const directories = await PluginLoader["getDirectories"]("/some/path");
|
||||
expect(directories).toEqual(["dir1", "dir2"]);
|
||||
});
|
||||
|
||||
test("findManifestFile should return manifest file if found", async () => {
|
||||
mockReaddir.mockResolvedValue(["manifest.json", "otherfile.txt"]);
|
||||
|
||||
const manifestFile =
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
await PluginLoader["findManifestFile"]("/some/path");
|
||||
expect(manifestFile).toBe("manifest.json");
|
||||
});
|
||||
|
||||
test("hasEntrypoint should return true if entrypoint file is found", async () => {
|
||||
mockReaddir.mockResolvedValue(["index.ts", "otherfile.txt"]);
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const hasEntrypoint = await PluginLoader["hasEntrypoint"]("/some/path");
|
||||
expect(hasEntrypoint).toBe(true);
|
||||
});
|
||||
|
||||
test("parseManifestFile should parse JSON manifest", async () => {
|
||||
const manifestContent = { name: "test-plugin" };
|
||||
Bun.file = jest.fn().mockReturnValue({
|
||||
text: (): Promise<string> =>
|
||||
Promise.resolve(JSON.stringify(manifestContent)),
|
||||
});
|
||||
|
||||
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
||||
const manifest = await pluginLoader["parseManifestFile"](
|
||||
"/some/path/manifest.json",
|
||||
"manifest.json",
|
||||
);
|
||||
expect(manifest).toEqual(manifestContent);
|
||||
});
|
||||
|
||||
test("findPlugins should return plugin directories with valid manifest and entrypoint", async () => {
|
||||
mockReaddir
|
||||
.mockResolvedValueOnce([
|
||||
{ name: "plugin1", isDirectory: (): true => true },
|
||||
{ name: "plugin2", isDirectory: (): true => true },
|
||||
])
|
||||
.mockResolvedValue(["manifest.json", "index.ts"]);
|
||||
|
||||
const plugins = await PluginLoader.findPlugins("/some/path");
|
||||
expect(plugins).toEqual(["plugin1", "plugin2"]);
|
||||
});
|
||||
|
||||
test("parseManifest should parse and validate manifest", async () => {
|
||||
const manifestContent: Manifest = {
|
||||
name: "test-plugin",
|
||||
version: "1.1.0",
|
||||
description: "Doobaee",
|
||||
};
|
||||
mockReaddir.mockResolvedValue(["manifest.json"]);
|
||||
Bun.file = jest.fn().mockReturnValue({
|
||||
text: (): Promise<string> =>
|
||||
Promise.resolve(JSON.stringify(manifestContent)),
|
||||
});
|
||||
manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({
|
||||
success: true,
|
||||
data: manifestContent,
|
||||
});
|
||||
|
||||
const manifest = await pluginLoader.parseManifest(
|
||||
"/some/path",
|
||||
"plugin1",
|
||||
);
|
||||
expect(manifest).toEqual(manifestContent);
|
||||
});
|
||||
|
||||
test("parseManifest should throw error if manifest is missing", async () => {
|
||||
mockReaddir.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
pluginLoader.parseManifest("/some/path", "plugin1"),
|
||||
).rejects.toThrow("Plugin plugin1 is missing a manifest file");
|
||||
});
|
||||
|
||||
test("parseManifest should throw error if manifest is invalid", async () => {
|
||||
// @ts-expect-error trying to cause a type error here
|
||||
const manifestContent: Manifest = {
|
||||
name: "test-plugin",
|
||||
version: "1.1.0",
|
||||
};
|
||||
mockReaddir.mockResolvedValue(["manifest.json"]);
|
||||
Bun.file = jest.fn().mockReturnValue({
|
||||
text: (): Promise<string> =>
|
||||
Promise.resolve(JSON.stringify(manifestContent)),
|
||||
});
|
||||
manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({
|
||||
success: false,
|
||||
error: new ZodError([]),
|
||||
});
|
||||
|
||||
await expect(
|
||||
pluginLoader.parseManifest("/some/path", "plugin1"),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("loadPlugin should load and return a Plugin instance", async () => {
|
||||
const mockPlugin = new Plugin(z.object({}));
|
||||
mock.module("/some/path/index.ts", () => ({
|
||||
default: mockPlugin,
|
||||
}));
|
||||
|
||||
const plugin = await pluginLoader.loadPlugin("/some/path", "index.ts");
|
||||
expect(plugin).toBeInstanceOf(Plugin);
|
||||
});
|
||||
|
||||
test("loadPlugin should throw error if default export is not a Plugin", async () => {
|
||||
mock.module("/some/path/index.ts", () => ({
|
||||
default: "cheese",
|
||||
}));
|
||||
|
||||
await expect(
|
||||
pluginLoader.loadPlugin("/some/path", "index.ts"),
|
||||
).rejects.toThrow("Entrypoint is not a Plugin");
|
||||
});
|
||||
|
||||
test("loadPlugins should load all plugins in a directory", async () => {
|
||||
const manifestContent: Manifest = {
|
||||
name: "test-plugin",
|
||||
version: "1.1.0",
|
||||
description: "Doobaee",
|
||||
};
|
||||
const mockPlugin = new Plugin(z.object({}));
|
||||
|
||||
mockReaddir
|
||||
.mockResolvedValueOnce([
|
||||
{ name: "plugin1", isDirectory: (): true => true },
|
||||
{ name: "plugin2", isDirectory: (): true => true },
|
||||
])
|
||||
.mockResolvedValue(["manifest.json", "index.ts"]);
|
||||
Bun.file = jest.fn().mockReturnValue({
|
||||
text: (): Promise<string> =>
|
||||
Promise.resolve(JSON.stringify(manifestContent)),
|
||||
});
|
||||
manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({
|
||||
success: true,
|
||||
data: manifestContent,
|
||||
});
|
||||
mock.module("/some/path/plugin1/index", () => ({
|
||||
default: mockPlugin,
|
||||
}));
|
||||
mock.module("/some/path/plugin2/index", () => ({
|
||||
default: mockPlugin,
|
||||
}));
|
||||
|
||||
const plugins = await pluginLoader.loadPlugins("/some/path", true);
|
||||
expect(plugins).toEqual([
|
||||
{
|
||||
manifest: manifestContent,
|
||||
plugin: mockPlugin as unknown as Plugin<ZodTypeAny>,
|
||||
},
|
||||
{
|
||||
manifest: manifestContent,
|
||||
plugin: mockPlugin as unknown as Plugin<ZodTypeAny>,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
import { readdir } from "node:fs/promises";
|
||||
import { getLogger, type Logger } from "@logtape/logtape";
|
||||
import { type Manifest, manifestSchema, Plugin } from "@versia/kit";
|
||||
import { config } from "@versia-server/config";
|
||||
import { file, sleep } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { parseJSON5, parseJSONC } from "confbox";
|
||||
import type { Hono } from "hono";
|
||||
import type { ZodTypeAny } from "zod";
|
||||
import { fromZodError, type ValidationError } from "zod-validation-error";
|
||||
import { config } from "~/config.ts";
|
||||
import { Plugin } from "~/packages/plugin-kit/plugin";
|
||||
import { type Manifest, manifestSchema } from "~/packages/plugin-kit/schema";
|
||||
import type { HonoEnv } from "~/types/api";
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue