mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-20 20:26:55 -05:00
fix(db): use static zod lookup in toZodSchema so locales tree-shake (#10866)
Co-authored-by: Taesu <166604494+bytaesu@users.noreply.github.com> Co-authored-by: Taesu <bytaesu@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"better-auth": patch
|
||||
---
|
||||
|
||||
The organization plugin no longer pulls every zod locale into server bundles.
|
||||
@@ -68,6 +68,51 @@ it("build minimal without unexpected imports", async () => {
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://github.com/better-auth/better-auth/issues/10865
|
||||
*/
|
||||
it("build organization plugin without unused zod locales", async () => {
|
||||
const esbuildDir = join(fixturesDir, "esbuild");
|
||||
const buildProcess = spawn(
|
||||
"pnpm",
|
||||
[
|
||||
"exec",
|
||||
"esbuild",
|
||||
"src/organization.ts",
|
||||
"--bundle",
|
||||
"--outfile=dist/organization.js",
|
||||
],
|
||||
{
|
||||
cwd: esbuildDir,
|
||||
stdio: ["ignore", "inherit", "inherit"],
|
||||
},
|
||||
);
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
buildProcess.once("error", reject);
|
||||
buildProcess.once("close", (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(
|
||||
new Error(`esbuild organization build failed with code ${code}`),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
const outputFile = join(esbuildDir, "dist", "organization.js");
|
||||
const outputContent = await readFile(outputFile, "utf-8");
|
||||
const locales = outputContent.match(/zod\/v4\/locales\/[\w-]+\.js/g) ?? [];
|
||||
const unusedLocales = locales.filter(
|
||||
(locale) =>
|
||||
locale !== "zod/v4/locales/en.js" && locale !== "zod/v4/locales/index.js",
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
unusedLocales,
|
||||
[],
|
||||
"Built output should not contain unused zod locales",
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://github.com/better-auth/better-auth/issues/10366
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { betterAuth } from "better-auth/minimal";
|
||||
import { organization } from "better-auth/plugins";
|
||||
|
||||
export const auth = betterAuth({ plugins: [organization()] });
|
||||
@@ -33,6 +33,24 @@ describe("toZodSchema", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("scalar field types", () => {
|
||||
it("should map each scalar type to its zod schema", () => {
|
||||
const schema = toZodSchema({
|
||||
fields: {
|
||||
name: { type: "string" },
|
||||
age: { type: "number" },
|
||||
active: { type: "boolean" },
|
||||
createdAt: { type: "date" },
|
||||
},
|
||||
isClientSide: true,
|
||||
});
|
||||
|
||||
const valid = { name: "a", age: 1, active: true, createdAt: new Date() };
|
||||
expect(schema.parse(valid)).toEqual(valid);
|
||||
expect(schema.safeParse({ ...valid, age: "1" }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("required: false field nullability", () => {
|
||||
it("should accept null, undefined, and a value for an optional field", () => {
|
||||
const schema = toZodSchema({
|
||||
|
||||
@@ -2,6 +2,17 @@ import type { DBFieldAttribute } from "@better-auth/core/db";
|
||||
import type { ZodType } from "zod";
|
||||
import * as z from "zod";
|
||||
|
||||
/**
|
||||
* Static lookup keeps `zod` tree-shakeable. `z[field.type]()` forces bundlers
|
||||
* to retain every `zod` export, including all locales.
|
||||
*/
|
||||
const scalarSchemas = {
|
||||
string: z.string,
|
||||
number: z.number,
|
||||
boolean: z.boolean,
|
||||
date: z.date,
|
||||
};
|
||||
|
||||
export function toZodSchema<
|
||||
Fields extends Record<string, DBFieldAttribute | never>,
|
||||
IsClientSide extends boolean,
|
||||
@@ -32,7 +43,7 @@ export function toZodSchema<
|
||||
} else if (Array.isArray(field.type)) {
|
||||
schema = z.any();
|
||||
} else {
|
||||
schema = z[field.type]();
|
||||
schema = scalarSchemas[field.type]();
|
||||
}
|
||||
|
||||
if (field?.required === false) {
|
||||
|
||||
Reference in New Issue
Block a user