add mongoose orm to the stack builder (#191)

This commit is contained in:
José Alberto Gómez García
2025-05-02 15:23:04 +02:00
committed by GitHub
parent 946f3eb421
commit 437cf9a45a
15 changed files with 524 additions and 273 deletions

View File

@@ -0,0 +1,5 @@
---
"create-better-t-stack": minor
---
add mongoose orm support to the stack builder

View File

@@ -40,6 +40,8 @@ export const dependencyVersionMap = {
"@prisma/client": "^6.7.0",
prisma: "^6.7.0",
mongoose: "^8.14.0",
"vite-plugin-pwa": "^0.21.2",
"@vite-pwa/assets-generator": "^0.2.6",

View File

@@ -62,6 +62,12 @@ export async function setupDatabase(config: ProjectConfig): Promise<void> {
projectDir: serverDir,
});
}
} else if (orm === "mongoose") {
await addPackageDependency({
dependencies: ["mongoose"],
devDependencies: [],
projectDir: serverDir,
});
}
if (database === "sqlite" && dbSetup === "turso") {

View File

@@ -367,6 +367,11 @@ export async function setupAuthTemplate(
PKG_ROOT,
`templates/auth/server/db/prisma/${db}`,
);
} else if (orm === "mongoose") {
authDbSrc = path.join(
PKG_ROOT,
`templates/auth/server/db/mongoose/${db}`,
)
}
if (authDbSrc && (await fs.pathExists(authDbSrc))) {
await processAndCopyFiles("**/*", authDbSrc, serverAppDir, context);

View File

@@ -60,7 +60,7 @@ async function main() {
.option("orm", {
type: "string",
describe: "ORM type",
choices: ["drizzle", "prisma", "none"],
choices: ["drizzle", "prisma", "mongoose", "none"],
})
.option("auth", {
type: "boolean",
@@ -446,78 +446,82 @@ function processAndValidateFlags(
config.dbSetup = "none";
}
if (effectiveDatabase === "mongodb" && effectiveOrm === "drizzle") {
if (effectiveDatabase === "mongodb" && effectiveOrm === "drizzle") {
consola.fatal(
"Drizzle ORM is not compatible with MongoDB. Please use --orm prisma or --orm mongoose.",
);
process.exit(1);
}
if (
effectiveOrm === "mongoose" &&
effectiveDatabase &&
effectiveDatabase !== "mongodb"
) {
consola.fatal(
`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
if (config.dbSetup && config.dbSetup !== "none") {
const dbSetup = config.dbSetup;
if (!effectiveDatabase || effectiveDatabase === "none") {
consola.fatal(
"MongoDB is only available with Prisma. Cannot use --database mongodb with --orm drizzle",
`Database setup '--db-setup ${dbSetup}' requires a database. Cannot use when database is 'none'.`,
);
process.exit(1);
}
if (config.dbSetup && config.dbSetup !== "none") {
const dbSetup = config.dbSetup;
if (dbSetup === "turso") {
if (effectiveDatabase && effectiveDatabase !== "sqlite") {
consola.fatal(
`Turso setup requires SQLite. Cannot use --db-setup turso with --database ${effectiveDatabase}`,
);
process.exit(1);
}
if (effectiveOrm === "prisma") {
consola.fatal(
"Turso setup is not compatible with Prisma. Cannot use --db-setup turso with --orm prisma",
);
process.exit(1);
}
config.database = "sqlite";
config.orm = "drizzle";
} else if (dbSetup === "prisma-postgres") {
if (effectiveDatabase && effectiveDatabase !== "postgres") {
consola.fatal(
`Prisma PostgreSQL setup requires PostgreSQL. Cannot use --db-setup prisma-postgres with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
if (
effectiveOrm &&
effectiveOrm !== "prisma" &&
effectiveOrm !== "none"
) {
consola.fatal(
`Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with --orm ${effectiveOrm}.`,
);
process.exit(1);
}
config.database = "postgres";
config.orm = "prisma";
} else if (dbSetup === "mongodb-atlas") {
if (effectiveDatabase && effectiveDatabase !== "mongodb") {
consola.fatal(
`MongoDB Atlas setup requires MongoDB. Cannot use --db-setup mongodb-atlas with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
if (
effectiveOrm &&
effectiveOrm !== "prisma" &&
effectiveOrm !== "none"
) {
consola.fatal(
`MongoDB Atlas setup requires Prisma ORM. Cannot use --db-setup mongodb-atlas with --orm ${effectiveOrm}.`,
);
process.exit(1);
}
config.database = "mongodb";
config.orm = "prisma";
} else if (dbSetup === "neon") {
if (effectiveDatabase && effectiveDatabase !== "postgres") {
consola.fatal(
`Neon PostgreSQL setup requires PostgreSQL. Cannot use --db-setup neon with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
config.database = "postgres";
if (dbSetup === "turso") {
if (effectiveDatabase && effectiveDatabase !== "sqlite") {
consola.fatal(
`Turso setup requires SQLite. Cannot use --db-setup turso with --database ${effectiveDatabase}`,
);
process.exit(1);
}
if (effectiveOrm !== "drizzle") {
consola.fatal(
`Turso setup requires Drizzle ORM. Cannot use --db-setup turso with --orm ${effectiveOrm ?? "none"}.`,
);
process.exit(1);
}
} else if (dbSetup === "prisma-postgres") {
if (effectiveDatabase !== "postgres") {
consola.fatal(
`Prisma PostgreSQL setup requires PostgreSQL. Cannot use --db-setup prisma-postgres with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
if (effectiveOrm !== "prisma") {
consola.fatal(
`Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with --orm ${effectiveOrm}.`,
);
process.exit(1);
}
} else if (dbSetup === "mongodb-atlas") {
if (effectiveDatabase !== "mongodb") {
consola.fatal(
`MongoDB Atlas setup requires MongoDB. Cannot use --db-setup mongodb-atlas with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
if (effectiveOrm !== "prisma" && effectiveOrm !== "mongoose") {
consola.fatal(
`MongoDB Atlas setup requires Prisma or Mongoose ORM. Cannot use --db-setup mongodb-atlas with --orm ${effectiveOrm}.`,
);
process.exit(1);
}
} else if (dbSetup === "neon") {
if (effectiveDatabase !== "postgres") {
consola.fatal(
`Neon PostgreSQL setup requires PostgreSQL. Cannot use --db-setup neon with --database ${effectiveDatabase}.`,
);
process.exit(1);
}
}
}
const includesNuxt = effectiveFrontend?.includes("nuxt");
const includesSvelte = effectiveFrontend?.includes("svelte");
@@ -576,13 +580,13 @@ function processAndValidateFlags(
process.exit(1);
}
if (config.addons.includes("husky") && !config.addons.includes("biome")) {
consola.warn(
"Husky addon is recommended to be used with Biome for lint-staged configuration.",
);
}
config.addons = [...new Set(config.addons)];
if (config.addons.includes("husky") && !config.addons.includes("biome")) {
consola.warn(
"Husky addon is recommended to be used with Biome for lint-staged configuration.",
);
}
config.addons = [...new Set(config.addons)];
}
const onlyNativeFrontend =
effectiveFrontend &&

View File

@@ -3,6 +3,24 @@ import pc from "picocolors";
import { DEFAULT_CONFIG } from "../constants";
import type { ProjectBackend, ProjectDatabase, ProjectOrm } from "../types";
const ormOptions = {
prisma: {
value: "prisma" as const,
label: "Prisma",
hint: "Powerful, feature-rich ORM",
},
mongoose: {
value: "mongoose" as const,
label: "Mongoose",
hint: "Elegant object modeling tool",
},
drizzle: {
value: "drizzle" as const,
label: "Drizzle",
hint: "Lightweight and performant TypeScript ORM",
},
};
export async function getORMChoice(
orm: ProjectOrm | undefined,
hasDatabase: boolean,
@@ -16,26 +34,16 @@ export async function getORMChoice(
if (!hasDatabase) return "none";
if (orm !== undefined) return orm;
if (database === "mongodb") {
log.info("Only Prisma is supported with MongoDB.");
return "prisma";
}
const options = [
...(database === "mongodb"
? [ormOptions.prisma, ormOptions.mongoose]
: [ormOptions.drizzle, ormOptions.prisma]),
];
const response = await select<ProjectOrm>({
message: "Select ORM",
options: [
{
value: "drizzle",
label: "Drizzle",
hint: "lightweight and performant TypeScript ORM",
},
{
value: "prisma",
label: "Prisma",
hint: "Powerful, feature-rich ORM",
},
],
initialValue: DEFAULT_CONFIG.orm,
options,
initialValue: database === "mongodb" ? "prisma" : DEFAULT_CONFIG.orm,
});
if (isCancel(response)) {

View File

@@ -4,7 +4,7 @@ export type ProjectDatabase =
| "mongodb"
| "mysql"
| "none";
export type ProjectOrm = "drizzle" | "prisma" | "none";
export type ProjectOrm = "drizzle" | "prisma" | "mongoose" | "none";
export type ProjectPackageManager = "npm" | "pnpm" | "bun";
export type ProjectAddons =
| "pwa"

View File

@@ -58,6 +58,31 @@ export const auth = betterAuth({
});
{{/if}}
{{#if (eq orm "mongoose")}}
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
{{#if (includes frontend "native")}}
import { expo } from "@better-auth/expo";
{{/if}}
import { client } from "../db";
export const auth = betterAuth({
database: mongodbAdapter(client.db()),
trustedOrigins: [
process.env.CORS_ORIGIN || "",{{#if (includes frontend "native")}}
"my-better-t-app://",{{/if}}
],
emailAndPassword: {
enabled: true,
}
{{~#if (includes frontend "native")}}
,
plugins: [expo()]
{{/if~}}
});
{{/if}}
{{#if (eq orm "none")}}
import { betterAuth } from "better-auth";
{{#if (includes frontend "native")}}

View File

@@ -0,0 +1,68 @@
import mongoose from 'mongoose';
const { Schema, model } = mongoose;
const userSchema = new Schema(
{
_id: { type: String },
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
emailVerified: { type: Boolean, required: true },
image: { type: String },
createdAt: { type: Date, required: true },
updatedAt: { type: Date, required: true },
},
{ collection: 'user' }
);
const sessionSchema = new Schema(
{
_id: { type: String },
expiresAt: { type: Date, required: true },
token: { type: String, required: true, unique: true },
createdAt: { type: Date, required: true },
updatedAt: { type: Date, required: true },
ipAddress: { type: String },
userAgent: { type: String },
userId: { type: String, ref: 'User', required: true },
},
{ collection: 'session' }
);
const accountSchema = new Schema(
{
_id: { type: String },
accountId: { type: String, required: true },
providerId: { type: String, required: true },
userId: { type: String, ref: 'User', required: true },
accessToken: { type: String },
refreshToken: { type: String },
idToken: { type: String },
accessTokenExpiresAt: { type: Date },
refreshTokenExpiresAt: { type: Date },
scope: { type: String },
password: { type: String },
createdAt: { type: Date, required: true },
updatedAt: { type: Date, required: true },
},
{ collection: 'account' }
);
const verificationSchema = new Schema(
{
_id: { type: String },
identifier: { type: String, required: true },
value: { type: String, required: true },
expiresAt: { type: Date, required: true },
createdAt: { type: Date },
updatedAt: { type: Date },
},
{ collection: 'verification' }
);
const User = model('User', userSchema);
const Session = model('Session', sessionSchema);
const Account = model('Account', accountSchema);
const Verification = model('Verification', verificationSchema);
export { User, Session, Account, Verification };

View File

@@ -0,0 +1,6 @@
import mongoose from 'mongoose';
await mongoose.connect(process.env.DATABASE_URL || "");
const client = mongoose.connection.getClient();
export { client };

View File

@@ -0,0 +1,66 @@
{{#if (eq api "orpc")}}
import { z } from "zod";
import { publicProcedure } from "../lib/orpc";
import { Todo } from "../db/models/todo.model";
export const todoRouter = {
getAll: publicProcedure.handler(async () => {
return await Todo.find().lean();
}),
create: publicProcedure
.input(z.object({ text: z.string().min(1) }))
.handler(async ({ input }) => {
const newTodo = await Todo.create({ text: input.text });
return newTodo.toObject();
}),
toggle: publicProcedure
.input(z.object({ id: z.string(), completed: z.boolean() }))
.handler(async ({ input }) => {
await Todo.updateOne({ id: input.id }, { completed: input.completed });
return { success: true };
}),
delete: publicProcedure
.input(z.object({ id: z.string() }))
.handler(async ({ input }) => {
await Todo.deleteOne({ id: input.id });
return { success: true };
}),
};
{{/if}}
{{#if (eq api "trpc")}}
import { z } from "zod";
import { router, publicProcedure } from "../lib/trpc";
import { Todo } from "../db/models/todo.model";
export const todoRouter = router({
getAll: publicProcedure.query(async () => {
return await Todo.find().lean();
}),
create: publicProcedure
.input(z.object({ text: z.string().min(1) }))
.mutation(async ({ input }) => {
const newTodo = await Todo.create({ text: input.text });
return newTodo.toObject();
}),
toggle: publicProcedure
.input(z.object({ id: z.string(), completed: z.boolean() }))
.mutation(async ({ input }) => {
await Todo.updateOne({ id: input.id }, { completed: input.completed });
return { success: true };
}),
delete: publicProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ input }) => {
await Todo.deleteOne({ id: input.id });
return { success: true };
}),
});
{{/if}}

View File

@@ -0,0 +1,24 @@
import mongoose from 'mongoose';
const { Schema, model } = mongoose;
const todoSchema = new Schema({
id: {
type: mongoose.Schema.Types.ObjectId,
auto: true,
},
text: {
type: String,
required: true,
},
completed: {
type: Boolean,
default: false,
},
}, {
collection: 'todo'
});
const Todo = model('Todo', todoSchema);
export { Todo };

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -224,137 +224,158 @@ const analyzeStackCompatibility = (stack: StackState): CompatibilityResult => {
changed = true;
}
if (nextStack.database === "none") {
if (nextStack.orm !== "none") {
notes.database.notes.push(
"Database 'None' selected: ORM will be set to 'None'.",
);
notes.orm.notes.push(
"ORM requires a database. It will be set to 'None'.",
);
notes.database.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "none";
changed = true;
}
if (nextStack.auth === "true") {
notes.database.notes.push(
"Database 'None' selected: Auth will be disabled.",
);
notes.auth.notes.push(
"Authentication requires a database. It will be disabled.",
);
notes.database.hasIssue = true;
notes.auth.hasIssue = true;
nextStack.auth = "false";
changed = true;
}
if (nextStack.dbSetup !== "none") {
notes.database.notes.push(
"Database 'None' selected: DB Setup will be set to 'Basic'.",
);
notes.dbSetup.notes.push(
"DB Setup requires a database. It will be set to 'Basic Setup'.",
);
notes.database.hasIssue = true;
notes.dbSetup.hasIssue = true;
nextStack.dbSetup = "none";
changed = true;
}
} else if (nextStack.database === "mongodb") {
if (nextStack.orm !== "prisma" && nextStack.orm !== "none") {
notes.database.notes.push(
"MongoDB requires Prisma ORM. It will be selected.",
);
notes.orm.notes.push(
"MongoDB requires Prisma ORM. It will be selected.",
);
notes.database.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
if (nextStack.database === "none") {
if (nextStack.orm !== "none") {
notes.database.notes.push(
"Database 'None' selected: ORM will be set to 'None'.",
);
notes.orm.notes.push(
"ORM requires a database. It will be set to 'None'.",
);
notes.database.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "none";
changed = true;
}
if (nextStack.auth === "true") {
notes.database.notes.push(
"Database 'None' selected: Auth will be disabled.",
);
notes.auth.notes.push(
"Authentication requires a database. It will be disabled.",
);
notes.database.hasIssue = true;
notes.auth.hasIssue = true;
nextStack.auth = "false";
changed = true;
}
if (nextStack.dbSetup !== "none") {
notes.database.notes.push(
"Database 'None' selected: DB Setup will be set to 'Basic'.",
);
notes.dbSetup.notes.push(
"DB Setup requires a database. It will be set to 'Basic Setup'.",
);
notes.database.hasIssue = true;
notes.dbSetup.hasIssue = true;
nextStack.dbSetup = "none";
changed = true;
}
} else if (nextStack.database === "mongodb") {
if (nextStack.orm !== "prisma" && nextStack.orm !== "mongoose") {
notes.database.notes.push(
"MongoDB requires Prisma or Mongoose ORM. Prisma will be selected.",
);
notes.orm.notes.push("MongoDB requires Prisma or Mongoose ORM. Prisma will be selected.");
notes.database.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
} else {
if (nextStack.orm === "mongoose") {
notes.database.notes.push(
"Relational databases are not compatible with Mongoose ORM",
);
notes.orm.notes.push("Relational databases are not compatible with Mongoose ORM");
notes.database.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
if (nextStack.dbSetup === "mongodb-atlas") {
notes.database.notes.push(
"Relational databases are not compatible with MongoDB Atlas setup. DB Setup will be reset.",
);
notes.dbSetup.notes.push(
"MongoDB Atlas setup requires MongoDB. It will be reset to 'Basic Setup'.",
);
notes.database.hasIssue = true;
notes.dbSetup.hasIssue = true;
nextStack.dbSetup = "none";
changed = true;
}
}
if (nextStack.dbSetup === "turso") {
if (nextStack.database !== "sqlite") {
notes.dbSetup.notes.push("Turso requires SQLite. It will be selected.");
notes.database.notes.push(
"Turso DB setup requires SQLite. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "sqlite";
changed = true;
}
if (nextStack.orm !== "drizzle") {
notes.dbSetup.notes.push(
"Turso requires Drizzle ORM. It will be selected.",
);
notes.orm.notes.push(
"Turso DB setup requires Drizzle ORM. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "drizzle";
changed = true;
}
} else if (nextStack.dbSetup === "prisma-postgres") {
if (nextStack.database !== "postgres") {
notes.dbSetup.notes.push("Requires PostgreSQL. It will be selected.");
notes.database.notes.push(
"Prisma PostgreSQL setup requires PostgreSQL. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "postgres";
changed = true;
}
if (nextStack.orm !== "prisma") {
notes.dbSetup.notes.push("Requires Prisma ORM. It will be selected.");
notes.orm.notes.push(
"Prisma PostgreSQL setup requires Prisma ORM. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
} else if (nextStack.dbSetup === "mongodb-atlas") {
if (nextStack.database !== "mongodb") {
notes.dbSetup.notes.push("Requires MongoDB. It will be selected.");
notes.database.notes.push(
"MongoDB Atlas setup requires MongoDB. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "mongodb";
changed = true;
}
if (nextStack.orm !== "prisma") {
notes.dbSetup.notes.push("Requires Prisma ORM. It will be selected.");
notes.orm.notes.push(
"MongoDB Atlas setup requires Prisma ORM. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
} else if (nextStack.dbSetup === "neon") {
if (nextStack.database !== "postgres") {
notes.dbSetup.notes.push(
"Neon requires PostgreSQL. It will be selected.",
);
notes.database.notes.push(
"Neon DB setup requires PostgreSQL. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "postgres";
changed = true;
}
if (nextStack.dbSetup === "turso") {
if (nextStack.database !== "sqlite") {
notes.dbSetup.notes.push("Turso requires SQLite. It will be selected.");
notes.database.notes.push(
"Turso DB setup requires SQLite. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "sqlite";
changed = true;
}
if (nextStack.orm !== "drizzle") {
notes.dbSetup.notes.push(
"Turso requires Drizzle ORM. It will be selected.",
);
notes.orm.notes.push(
"Turso DB setup requires Drizzle ORM. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "drizzle";
changed = true;
}
} else if (nextStack.dbSetup === "prisma-postgres") {
if (nextStack.database !== "postgres") {
notes.dbSetup.notes.push("Requires PostgreSQL. It will be selected.");
notes.database.notes.push(
"Prisma PostgreSQL setup requires PostgreSQL. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "postgres";
changed = true;
}
if (nextStack.orm !== "prisma") {
notes.dbSetup.notes.push("Requires Prisma ORM. It will be selected.");
notes.orm.notes.push(
"Prisma PostgreSQL setup requires Prisma ORM. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
} else if (nextStack.dbSetup === "mongodb-atlas") {
if (nextStack.database !== "mongodb") {
notes.dbSetup.notes.push("Requires MongoDB. It will be selected.");
notes.database.notes.push(
"MongoDB Atlas setup requires MongoDB. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "mongodb";
changed = true;
}
if (nextStack.orm !== "prisma" && nextStack.orm !== "mongoose") {
notes.dbSetup.notes.push("Requires Prisma or Mongoose ORM. Prisma will be selected.");
notes.orm.notes.push(
"MongoDB Atlas setup requires Prisma or Mongoose ORM. Prisma will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.orm.hasIssue = true;
nextStack.orm = "prisma";
changed = true;
}
} else if (nextStack.dbSetup === "neon") {
if (nextStack.database !== "postgres") {
notes.dbSetup.notes.push(
"Neon requires PostgreSQL. It will be selected.",
);
notes.database.notes.push(
"Neon DB setup requires PostgreSQL. It will be selected.",
);
notes.dbSetup.hasIssue = true;
notes.database.hasIssue = true;
nextStack.database = "postgres";
changed = true;
}
}
const isNuxt = nextStack.frontend.includes("nuxt");
const isSvelte = nextStack.frontend.includes("svelte");
@@ -450,51 +471,48 @@ const analyzeStackCompatibility = (stack: StackState): CompatibilityResult => {
}
}
const uniqueIncompatibleExamples = [...new Set(incompatibleExamples)];
if (uniqueIncompatibleExamples.length > 0) {
if (isNativeOnly) {
} else {
if (
!isWeb &&
(uniqueIncompatibleExamples.includes("todo") ||
uniqueIncompatibleExamples.includes("ai"))
) {
notes.frontend.notes.push(
"Examples require a web frontend. Incompatible examples will be removed.",
);
notes.examples.notes.push(
"Requires a web frontend. Incompatible examples will be removed.",
);
notes.frontend.hasIssue = true;
notes.examples.hasIssue = true;
}
if (
nextStack.database === "none" &&
uniqueIncompatibleExamples.includes("todo")
) {
notes.database.notes.push(
"Todo example requires a database. It will be removed.",
);
notes.examples.notes.push(
"Todo example requires a database. It will be removed.",
);
notes.database.hasIssue = true;
notes.examples.hasIssue = true;
}
if (
nextStack.backend === "elysia" &&
uniqueIncompatibleExamples.includes("ai")
) {
notes.backend.notes.push(
"AI example is not compatible with Elysia. It will be removed.",
);
notes.examples.notes.push(
"AI example is not compatible with Elysia. It will be removed.",
);
notes.backend.hasIssue = true;
notes.examples.hasIssue = true;
}
}
const uniqueIncompatibleExamples = [...new Set(incompatibleExamples)];
if (uniqueIncompatibleExamples.length > 0) {
if (
!isWeb &&
(uniqueIncompatibleExamples.includes("todo") ||
uniqueIncompatibleExamples.includes("ai"))
) {
notes.frontend.notes.push(
"Examples require a web frontend. Incompatible examples will be removed.",
);
notes.examples.notes.push(
"Requires a web frontend. Incompatible examples will be removed.",
);
notes.frontend.hasIssue = true;
notes.examples.hasIssue = true;
}
if (
nextStack.database === "none" &&
uniqueIncompatibleExamples.includes("todo")
) {
notes.database.notes.push(
"Todo example requires a database. It will be removed.",
);
notes.examples.notes.push(
"Todo example requires a database. It will be removed.",
);
notes.database.hasIssue = true;
notes.examples.hasIssue = true;
}
if (
nextStack.backend === "elysia" &&
uniqueIncompatibleExamples.includes("ai")
) {
notes.backendFramework.notes.push(
"AI example is not compatible with Elysia. It will be removed.",
);
notes.examples.notes.push(
"AI example is not compatible with Elysia. It will be removed.",
);
notes.backendFramework.hasIssue = true;
notes.examples.hasIssue = true;
}
const originalExamplesLength = nextStack.examples.length;
nextStack.examples = nextStack.examples.filter(
@@ -748,9 +766,10 @@ const StackArchitect = () => {
if (
currentStack.database === "mongodb" &&
techId !== "prisma" &&
techId !== "mongoose" &&
techId !== "none"
)
reason = "MongoDB requires the Prisma ORM.";
reason = "MongoDB requires the Prisma or Mongoose ORM.";
if (
currentStack.dbSetup === "turso" &&
techId !== "drizzle" &&
@@ -766,21 +785,23 @@ const StackArchitect = () => {
if (
currentStack.dbSetup === "mongodb-atlas" &&
techId !== "prisma" &&
techId !== "mongoose" &&
techId !== "none"
)
reason = "MongoDB Atlas setup requires Prisma ORM.";
reason = "MongoDB Atlas setup requires Prisma or Mongoose ORM.";
if (techId === "none") {
if (currentStack.database === "mongodb")
reason = "MongoDB requires Prisma ORM.";
reason = "MongoDB requires Prisma or Mongoose ORM.";
if (currentStack.dbSetup === "turso")
reason = "Turso DB setup requires Drizzle ORM.";
if (
currentStack.dbSetup === "prisma-postgres" ||
currentStack.dbSetup === "mongodb-atlas"
)
if (currentStack.dbSetup === "prisma-postgres")
reason = "This DB setup requires Prisma ORM.";
}
if (techId === "mongoose" && (currentStack.database !== "mongodb")) {
reason = "Mongoose ORM is not compatible with relational databases.";
}
}
if (catKey === "dbSetup" && techId !== "none") {
@@ -809,8 +830,8 @@ const StackArchitect = () => {
currentStack.database !== "none"
)
reason = "Requires MongoDB database.";
if (currentStack.orm !== "prisma" && currentStack.orm !== "none")
reason = "Requires Prisma ORM.";
if (currentStack.orm !== "prisma" && currentStack.orm !== "mongoose" && currentStack.orm !== "none")
reason = "Requires Prisma or Mongoose ORM.";
} else if (techId === "neon") {
if (
currentStack.database !== "postgres" &&
@@ -1051,7 +1072,7 @@ const StackArchitect = () => {
});
};
const copyToClipboard = () => {
const copyToClipboard = () => {
navigator.clipboard.writeText(command);
setCopied(true);
setTimeout(() => setCopied(false), 2000);

View File

@@ -192,6 +192,13 @@ export const TECH_OPTIONS = {
icon: "/icon/prisma.svg",
color: "from-purple-400 to-purple-600",
},
{
id: "mongoose",
name: "Mongoose",
description: "Elegant object modeling tool",
icon: "/icon/mongoose.svg",
color: "from-blue-400 to-blue-600",
},
{
id: "none",
name: "No ORM",