import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { test } from "vitest"; import fs from "node:fs/promises"; import path from "node:path"; import { getConfig } from "../src/utils/get-config"; interface TmpDirFixture { tmpdir: string; } async function createTempDir() { const tmpdir = path.join(process.cwd(), "test", "getConfig_test-"); return await fs.mkdtemp(tmpdir); } export const tmpdirTest = test.extend({ tmpdir: async ({}, use) => { const directory = await createTempDir(); await use(directory); await fs.rm(directory, { recursive: true }); }, }); let tmpDir = "."; describe("getConfig", async () => { beforeEach(async () => { const tmp = path.join(process.cwd(), "getConfig_test-"); tmpDir = await fs.mkdtemp(tmp); }); afterEach(async () => { await fs.rm(tmpDir, { recursive: true }); }); it("should resolve resolver type alias", async () => { const authPath = path.join(tmpDir, "server", "auth"); const dbPath = path.join(tmpDir, "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy tsconfig.json await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { /* Path Aliases */ "baseUrl": ".", "paths": { "@server/*": ["./server/*"] } } }`, ); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "@server/db/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); //create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should resolve direct alias", async () => { const authPath = path.join(tmpDir, "server", "auth"); const dbPath = path.join(tmpDir, "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy tsconfig.json await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { /* Path Aliases */ "baseUrl": ".", "paths": { "prismaDbClient": ["./server/db/db"] } } }`, ); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "prismaDbClient"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); //create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should resolve resolver type alias with relative path", async () => { const authPath = path.join(tmpDir, "test", "server", "auth"); const dbPath = path.join(tmpDir, "test", "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy tsconfig.json await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { /* Path Aliases */ "baseUrl": "./test", "paths": { "@server/*": ["./server/*"] } } }`, ); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "@server/db/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); //create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "test/server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should resolve direct alias with relative path", async () => { const authPath = path.join(tmpDir, "test", "server", "auth"); const dbPath = path.join(tmpDir, "test", "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy tsconfig.json await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { /* Path Aliases */ "baseUrl": "./test", "paths": { "prismaDbClient": ["./server/db/db"] } } }`, ); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "prismaDbClient"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); //create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "test/server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should resolve with relative import", async () => { const authPath = path.join(tmpDir, "test", "server", "auth"); const dbPath = path.join(tmpDir, "test", "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy tsconfig.json await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { /* Path Aliases */ "baseUrl": "./test", "paths": { "prismaDbClient": ["./server/db/db"] } } }`, ); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "../db/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); //create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "test/server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should error with invalid alias", async () => { const authPath = path.join(tmpDir, "server", "auth"); const dbPath = path.join(tmpDir, "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy tsconfig.json await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { /* Path Aliases */ "baseUrl": ".", "paths": { "@server/*": ["./PathIsInvalid/*"] } } }`, ); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "@server/db/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); //create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const consoleErrorSpy = vi .spyOn(console, "error") .mockImplementation(() => {}); await expect(() => getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts" }), ).rejects.toThrowError(); expect(consoleErrorSpy).toHaveBeenCalledWith( expect.stringContaining("Couldn't read your auth config."), expect.objectContaining({ code: "MODULE_NOT_FOUND", }), ); }); it("should resolve js config", async () => { const authPath = path.join(tmpDir, "server", "auth"); const dbPath = path.join(tmpDir, "server", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); //create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.js"), `import { betterAuth } from "better-auth"; export const auth = betterAuth({ emailAndPassword: { enabled: true, } })`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.js", }); expect(config).toMatchObject({ emailAndPassword: { enabled: true }, }); }); it("should resolve path aliases from referenced tsconfig files", async () => { const authPath = path.join(tmpDir, "apps", "web", "server", "auth"); const dbPath = path.join(tmpDir, "packages", "shared", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(dbPath, { recursive: true }); // Create root tsconfig.json with references await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "references": [ { "path": "./apps/web" }, { "path": "./packages/shared" } ] }`, ); // Create web app tsconfig.json with aliases await fs.writeFile( path.join(tmpDir, "apps", "web", "tsconfig.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@web/*": ["./server/*"] } } }`, ); // Create shared package tsconfig.json with aliases await fs.writeFile( path.join(tmpDir, "packages", "shared", "tsconfig.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@shared/*": ["./db/*"] } } }`, ); // Create dummy auth.ts using both aliases await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "@shared/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); // Create dummy db.ts await fs.writeFile( path.join(dbPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "apps/web/server/auth/auth.ts", }); expect(config).toMatchObject({ emailAndPassword: { enabled: true }, database: expect.objectContaining({ // This proves the @shared/db alias was resolved correctly }), }); }); it("should handle missing referenced tsconfig files gracefully", async () => { const authPath = path.join(tmpDir, "server", "auth"); await fs.mkdir(authPath, { recursive: true }); // Create root tsconfig.json with reference to non-existent file await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@server/*": ["./server/*"] } }, "references": [ { "path": "./non-existent" } ] }`, ); // Create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; export const auth = betterAuth({ emailAndPassword: { enabled: true, } })`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should handle circular references in tsconfig files", async () => { const authPath = path.join(tmpDir, "server", "auth"); const appPath = path.join(tmpDir, "app"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(appPath, { recursive: true }); // Create root tsconfig.json that references app tsconfig await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@root/*": ["./server/*"] } }, "references": [ { "path": "./app" } ] }`, ); // Create app tsconfig.json that references back to root await fs.writeFile( path.join(tmpDir, "app", "tsconfig.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@app/*": ["./src/*"] } }, "references": [ { "path": ".." } ] }`, ); // Create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; export const auth = betterAuth({ emailAndPassword: { enabled: true, } })`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should resolve direct tsconfig file references", async () => { const authPath = path.join(tmpDir, "server", "auth"); const sharedPath = path.join(tmpDir, "shared", "db"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(sharedPath, { recursive: true }); // Create root tsconfig.json with direct file references await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.shared.json" } ] }`, ); // Create tsconfig.app.json with app-specific aliases await fs.writeFile( path.join(tmpDir, "tsconfig.app.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@app/*": ["./server/*"] } } }`, ); // Create tsconfig.shared.json with shared aliases await fs.writeFile( path.join(tmpDir, "tsconfig.shared.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@shared/*": ["./shared/*"] } } }`, ); // Create dummy auth.ts using both aliases await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; import {prismaAdapter} from "better-auth/adapters/prisma"; import {db} from "@shared/db/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true, } })`, ); // Create dummy db.ts await fs.writeFile( path.join(sharedPath, "db.ts"), `class PrismaClient { constructor() {} } export const db = new PrismaClient()`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should handle mixed directory and file references", async () => { const authPath = path.join(tmpDir, "apps", "web", "server", "auth"); const utilsPath = path.join(tmpDir, "packages", "utils"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(utilsPath, { recursive: true }); // Create root tsconfig.json with mixed references await fs.writeFile( path.join(tmpDir, "tsconfig.json"), `{ "references": [ { "path": "./apps/web" }, { "path": "./tsconfig.utils.json" } ] }`, ); // Create web app directory-based tsconfig await fs.writeFile( path.join(tmpDir, "apps", "web", "tsconfig.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@web/*": ["./server/*"] } } }`, ); // Create utils file-based tsconfig await fs.writeFile( path.join(tmpDir, "tsconfig.utils.json"), `{ "compilerOptions": { "baseUrl": ".", "paths": { "@utils/*": ["./packages/utils/*"] } } }`, ); // Create dummy auth.ts await fs.writeFile( path.join(authPath, "auth.ts"), `import {betterAuth} from "better-auth"; export const auth = betterAuth({ emailAndPassword: { enabled: true, } })`, ); // Create dummy utils file await fs.writeFile( path.join(utilsPath, "index.ts"), `export const utils = {}`, ); const config = await getConfig({ cwd: tmpDir, configPath: "apps/web/server/auth/auth.ts", }); expect(config).not.toBe(null); }); it("should resolve SvelteKit $lib/server imports correctly", async () => { const authPath = path.join(tmpDir, "src"); const libServerPath = path.join(tmpDir, "src", "lib", "server"); await fs.mkdir(authPath, { recursive: true }); await fs.mkdir(libServerPath, { recursive: true }); await fs.writeFile( path.join(tmpDir, "package.json"), JSON.stringify({ name: "test-sveltekit", devDependencies: { "@sveltejs/kit": "^2.0.0", }, }), ); await fs.writeFile( path.join(libServerPath, "database.ts"), `export const db = { // Mock database client connect: () => console.log('Connected to database') };`, ); await fs.writeFile( path.join(authPath, "auth.ts"), `import { betterAuth } from "better-auth"; import { db } from "$lib/server/database"; export const auth = betterAuth({ emailAndPassword: { enabled: true, } })`, ); const config = await getConfig({ cwd: tmpDir, configPath: "src/auth.ts", }); expect(config).not.toBe(null); expect(config).toMatchObject({ emailAndPassword: { enabled: true }, }); }); it("should resolve export default auth", async () => { const authPath = path.join(tmpDir, "server", "auth"); await fs.mkdir(authPath, { recursive: true }); await fs.writeFile( path.join(authPath, "auth.ts"), `import { betterAuth } from "better-auth"; const auth = betterAuth({ emailAndPassword: { enabled: true, }, socialProviders: { github: { clientId: "test-id", clientSecret: "test-secret" } } }); export default auth;`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); expect(config).toMatchObject({ emailAndPassword: { enabled: true }, socialProviders: { github: { clientId: "test-id", clientSecret: "test-secret", }, }, }); }); it("should resolve export default auth with named export", async () => { const authPath = path.join(tmpDir, "server", "auth"); await fs.mkdir(authPath, { recursive: true }); await fs.writeFile( path.join(authPath, "auth.ts"), `import { betterAuth } from "better-auth"; const auth = betterAuth({ emailAndPassword: { enabled: true, }, socialProviders: { github: { clientId: "test-id", clientSecret: "test-secret" } } }); export { auth }; export default auth;`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); expect(config).toMatchObject({ emailAndPassword: { enabled: true }, socialProviders: { github: { clientId: "test-id", clientSecret: "test-secret", }, }, }); }); it("should resolve export default with inline betterAuth call", async () => { const authPath = path.join(tmpDir, "server", "auth"); await fs.mkdir(authPath, { recursive: true }); await fs.writeFile( path.join(authPath, "auth.ts"), `import { betterAuth } from "better-auth"; export default betterAuth({ emailAndPassword: { enabled: true, }, trustedOrigins: ["http://localhost:3000"] });`, ); const config = await getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts", }); expect(config).not.toBe(null); expect(config).toMatchObject({ emailAndPassword: { enabled: true }, trustedOrigins: ["http://localhost:3000"], }); }); });