export async function generateAuthConfig()

in packages/cli/src/generators/auth-config.ts [38:590]


export async function generateAuthConfig({
	format,
	current_user_config,
	spinner,
	plugins,
	database,
}: {
	format: Format;
	current_user_config: string;
	spinner: ReturnType<typeof clackSpinner>;
	plugins: SupportedPlugin[];
	database: SupportedDatabases | null;
}): Promise<{
	generatedCode: string;
	dependencies: string[];
	envs: string[];
}> {
	let _start_of_plugins_common_index = {
		START_OF_PLUGINS: {
			type: "regex",
			regex: /betterAuth\([\w\W]*plugins:[\W]*\[()/m,
			getIndex: ({ matchIndex, match }) => {
				return matchIndex + match[0].length;
			},
		} satisfies CommonIndexConfig<{}>,
	};
	const common_indexes = {
		START_OF_PLUGINS:
			_start_of_plugins_common_index.START_OF_PLUGINS satisfies CommonIndexConfig<{}>,
		END_OF_PLUGINS: {
			type: "manual",
			getIndex: ({ content, additionalFields }) => {
				const closingBracketIndex = findClosingBracket(
					content,
					additionalFields.start_of_plugins,
					"[",
					"]",
				);
				return closingBracketIndex;
			},
		} satisfies CommonIndexConfig<{ start_of_plugins: number }>,
		START_OF_BETTERAUTH: {
			type: "regex",
			regex: /betterAuth\({()/m,
			getIndex: ({ matchIndex }) => {
				return matchIndex + "betterAuth({".length;
			},
		} satisfies CommonIndexConfig<{}>,
	};

	const config_generation = {
		add_plugin: async (opts: {
			direction_in_plugins_array: "append" | "prepend";
			pluginFunctionName: string;
			pluginContents: string;
			config: string;
		}): Promise<{ code: string; dependencies: string[]; envs: string[] }> => {
			let start_of_plugins = getGroupInfo(
				opts.config,
				common_indexes.START_OF_PLUGINS,
				{},
			);

			// console.log(`start of plugins:`, start_of_plugins);

			if (!start_of_plugins) {
				throw new Error(
					"Couldn't find start of your plugins array in your auth config file.",
				);
			}
			let end_of_plugins = getGroupInfo(
				opts.config,
				common_indexes.END_OF_PLUGINS,
				{ start_of_plugins: start_of_plugins.index },
			);

			// console.log(`end of plugins:`, end_of_plugins);

			if (!end_of_plugins) {
				throw new Error(
					"Couldn't find end of your plugins array in your auth config file.",
				);
			}
			// console.log(
			// 	"slice:\n",
			// 	opts.config.slice(start_of_plugins.index, end_of_plugins.index),
			// );
			let new_content: string;

			if (opts.direction_in_plugins_array === "prepend") {
				new_content = insertContent({
					line: start_of_plugins.line,
					character: start_of_plugins.character,
					content: opts.config,
					insert_content: `${opts.pluginFunctionName}(${opts.pluginContents}),`,
				});
			} else {
				let has_found_comma = false;
				const str = opts.config
					.slice(start_of_plugins.index, end_of_plugins.index)
					.split("")
					.reverse();
				for (let index = 0; index < str.length; index++) {
					const char = str[index];
					if (char === ",") {
						has_found_comma = true;
					}
					if (char === ")") {
						break;
					}
				}

				new_content = insertContent({
					line: end_of_plugins.line,
					character: end_of_plugins.character,
					content: opts.config,
					insert_content: `${!has_found_comma ? "," : ""}${
						opts.pluginFunctionName
					}(${opts.pluginContents})`,
				});
			}

			// console.log(`new_content`, new_content);
			try {
				new_content = await format(new_content);
			} catch (error) {
				console.error(error);
				throw new Error(
					`Failed to generate new auth config during plugin addition phase.`,
				);
			}
			return { code: await new_content, dependencies: [], envs: [] };
		},
		add_import: async (opts: {
			imports: Import[];
			config: string;
		}): Promise<{ code: string; dependencies: string[]; envs: string[] }> => {
			let importString = "";
			for (const import_ of opts.imports) {
				if (Array.isArray(import_.variables)) {
					importString += `import { ${import_.variables
						.map(
							(x) =>
								`${x.asType ? "type " : ""}${x.name}${
									x.as ? ` as ${x.as}` : ""
								}`,
						)
						.join(", ")} } from "${import_.path}";\n`;
				} else {
					importString += `import ${import_.variables.asType ? "type " : ""}${
						import_.variables.name
					}${import_.variables.as ? ` as ${import_.variables.as}` : ""} from "${
						import_.path
					}";\n`;
				}
			}
			try {
				let new_content = format(importString + opts.config);
				return { code: await new_content, dependencies: [], envs: [] };
			} catch (error) {
				console.error(error);
				throw new Error(
					`Failed to generate new auth config during import addition phase.`,
				);
			}
		},
		add_database: async (opts: {
			database: SupportedDatabases;
			config: string;
		}): Promise<{ code: string; dependencies: string[]; envs: string[] }> => {
			const required_envs: string[] = [];
			const required_deps: string[] = [];
			let database_code_str: string = "";

			async function add_db({
				db_code,
				dependencies,
				envs,
				imports,
				code_before_betterAuth,
			}: {
				imports: Import[];
				db_code: string;
				envs: string[];
				dependencies: string[];
				/**
				 * Any code you want to put before the betterAuth export
				 */
				code_before_betterAuth?: string;
			}) {
				if (code_before_betterAuth) {
					let start_of_betterauth = getGroupInfo(
						opts.config,
						common_indexes.START_OF_BETTERAUTH,
						{},
					);
					if (!start_of_betterauth) {
						throw new Error("Couldn't find start of betterAuth() function.");
					}
					opts.config = insertContent({
						line: start_of_betterauth.line - 1,
						character: 0,
						content: opts.config,
						insert_content: `\n${code_before_betterAuth}\n`,
					});
				}

				const code_gen = await config_generation.add_import({
					config: opts.config,
					imports: imports,
				});
				opts.config = code_gen.code;
				database_code_str = db_code;
				required_envs.push(...envs, ...code_gen.envs);
				required_deps.push(...dependencies, ...code_gen.dependencies);
			}

			if (opts.database === "sqlite") {
				await add_db({
					db_code: `new Database(process.env.DATABASE_URL || "database.sqlite")`,
					dependencies: ["better-sqlite3"],
					envs: ["DATABASE_URL"],
					imports: [
						{
							path: "better-sqlite3",
							variables: {
								asType: false,
								name: "Database",
							},
						},
					],
				});
			} else if (opts.database === "postgres") {
				await add_db({
					db_code: `new Pool({\nconnectionString: process.env.DATABASE_URL || "postgresql://postgres:password@localhost:5432/database"\n})`,
					dependencies: ["pg"],
					envs: ["DATABASE_URL"],
					imports: [
						{
							path: "pg",
							variables: [
								{
									asType: false,
									name: "Pool",
								},
							],
						},
					],
				});
			} else if (opts.database === "mysql") {
				await add_db({
					db_code: `createPool(process.env.DATABASE_URL!)`,
					dependencies: ["mysql2"],
					envs: ["DATABASE_URL"],
					imports: [
						{
							path: "mysql2/promise",
							variables: [
								{
									asType: false,
									name: "createPool",
								},
							],
						},
					],
				});
			} else if (opts.database === "mssql") {
				const dialectCode = `new MssqlDialect({
						tarn: {
							...Tarn,
							options: {
							min: 0,
							max: 10,
							},
						},
						tedious: {
							...Tedious,
							connectionFactory: () => new Tedious.Connection({
							authentication: {
								options: {
								password: 'password',
								userName: 'username',
								},
								type: 'default',
							},
							options: {
								database: 'some_db',
								port: 1433,
								trustServerCertificate: true,
							},
							server: 'localhost',
							}),
						},
					})`;
				await add_db({
					code_before_betterAuth: dialectCode,
					db_code: `dialect`,
					dependencies: ["tedious", "tarn", "kysely"],
					envs: ["DATABASE_URL"],
					imports: [
						{
							path: "tedious",
							variables: {
								name: "*",
								as: "Tedious",
							},
						},
						{
							path: "tarn",
							variables: {
								name: "*",
								as: "Tarn",
							},
						},
						{
							path: "kysely",
							variables: [
								{
									name: "MssqlDialect",
								},
							],
						},
					],
				});
			} else if (
				opts.database === "drizzle:mysql" ||
				opts.database === "drizzle:sqlite" ||
				opts.database === "drizzle:pg"
			) {
				await add_db({
					db_code: `drizzleAdapter(db, {\nprovider: "${opts.database.replace(
						"drizzle:",
						"",
					)}",\n})`,
					dependencies: [""],
					envs: [],
					imports: [
						{
							path: "better-auth/adapters/drizzle",
							variables: [
								{
									name: "drizzleAdapter",
								},
							],
						},
						{
							path: "./database.ts",
							variables: [
								{
									name: "db",
								},
							],
						},
					],
				});
			} else if (
				opts.database === "prisma:mysql" ||
				opts.database === "prisma:sqlite" ||
				opts.database === "prisma:postgresql"
			) {
				await add_db({
					db_code: `prismaAdapter(client, {\nprovider: "${opts.database.replace(
						"prisma:",
						"",
					)}",\n})`,
					dependencies: [`@prisma/client`],
					envs: [],
					code_before_betterAuth: "const client = new PrismaClient();",
					imports: [
						{
							path: "better-auth/adapters/prisma",
							variables: [
								{
									name: "prismaAdapter",
								},
							],
						},
						{
							path: "@prisma/client",
							variables: [
								{
									name: "PrismaClient",
								},
							],
						},
					],
				});
			} else if (opts.database === "mongodb") {
				await add_db({
					db_code: `mongodbAdapter(db)`,
					dependencies: ["mongodb"],
					envs: [`DATABASE_URL`],
					code_before_betterAuth: [
						`const client = new MongoClient(process.env.DATABASE_URL || "mongodb://localhost:27017/database");`,
						`const db = client.db();`,
					].join("\n"),
					imports: [
						{
							path: "better-auth/adapters/mongo",
							variables: [
								{
									name: "mongodbAdapter",
								},
							],
						},
						{
							path: "mongodb",
							variables: [
								{
									name: "MongoClient",
								},
							],
						},
					],
				});
			}

			let start_of_betterauth = getGroupInfo(
				opts.config,
				common_indexes.START_OF_BETTERAUTH,
				{},
			);
			if (!start_of_betterauth) {
				throw new Error("Couldn't find start of betterAuth() function.");
			}
			let new_content: string;
			new_content = insertContent({
				line: start_of_betterauth.line,
				character: start_of_betterauth.character,
				content: opts.config,
				insert_content: `database: ${database_code_str},`,
			});

			try {
				new_content = await format(new_content);
				return {
					code: new_content,
					dependencies: required_deps,
					envs: required_envs,
				};
			} catch (error) {
				console.error(error);
				throw new Error(
					`Failed to generate new auth config during database addition phase.`,
				);
			}
		},
	};

	let new_user_config: string = await format(current_user_config);
	let total_dependencies: string[] = [];
	let total_envs: string[] = [];

	if (plugins.length !== 0) {
		const imports: {
			path: string;
			variables: {
				asType: boolean;
				name: string;
			}[];
		}[] = [];
		for await (const plugin of plugins) {
			const existingIndex = imports.findIndex((x) => x.path === plugin.path);
			if (existingIndex !== -1) {
				imports[existingIndex]!.variables.push({
					name: plugin.name,
					asType: false,
				});
			} else {
				imports.push({
					path: plugin.path,
					variables: [
						{
							name: plugin.name,
							asType: false,
						},
					],
				});
			}
		}
		if (imports.length !== 0) {
			const { code, envs, dependencies } = await config_generation.add_import({
				config: new_user_config,
				imports: imports,
			});
			total_dependencies.push(...dependencies);
			total_envs.push(...envs);
			new_user_config = code;
		}
	}

	for await (const plugin of plugins) {
		try {
			// console.log(`--------- UPDATE: ${plugin} ---------`);
			let pluginContents = "";
			if (plugin.id === "magic-link") {
				pluginContents = `{\nsendMagicLink({ email, token, url }, request) {\n// Send email with magic link\n},\n}`;
			} else if (plugin.id === "email-otp") {
				pluginContents = `{\nasync sendVerificationOTP({ email, otp, type }, request) {\n// Send email with OTP\n},\n}`;
			} else if (plugin.id === "generic-oauth") {
				pluginContents = `{\nconfig: [],\n}`;
			} else if (plugin.id === "oidc") {
				pluginContents = `{\nloginPage: "/sign-in",\n}`;
			}
			const { code, dependencies, envs } = await config_generation.add_plugin({
				config: new_user_config,
				direction_in_plugins_array:
					plugin.id === "next-cookies" ? "append" : "prepend",
				pluginFunctionName: plugin.name,
				pluginContents: pluginContents,
			});
			new_user_config = code;
			total_envs.push(...envs);
			total_dependencies.push(...dependencies);
			// console.log(new_user_config);
			// console.log(`--------- UPDATE END ---------`);
		} catch (error: any) {
			spinner.stop(
				`Something went wrong while generating/updating your new auth config file.`,
				1,
			);
			logger.error(error.message);
			process.exit(1);
		}
	}

	if (database) {
		try {
			const { code, dependencies, envs } = await config_generation.add_database(
				{
					config: new_user_config,
					database: database,
				},
			);
			new_user_config = code;
			total_dependencies.push(...dependencies);
			total_envs.push(...envs);
		} catch (error: any) {
			spinner.stop(
				`Something went wrong while generating/updating your new auth config file.`,
				1,
			);
			logger.error(error.message);
			process.exit(1);
		}
	}

	return {
		generatedCode: new_user_config,
		dependencies: total_dependencies,
		envs: total_envs,
	};
}