mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
cli: organize file structure
This commit is contained in:
138
apps/cli/src/helpers/setup/addons-setup.ts
Normal file
138
apps/cli/src/helpers/setup/addons-setup.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import type { Frontend } from "../../types";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
import { setupStarlight } from "./starlight-setup";
|
||||
import { setupTauri } from "./tauri-setup";
|
||||
|
||||
import type { ProjectConfig } from "../../types";
|
||||
|
||||
export async function setupAddons(config: ProjectConfig) {
|
||||
const { addons, frontend, projectDir } = config;
|
||||
const hasReactWebFrontend =
|
||||
frontend.includes("react-router") ||
|
||||
frontend.includes("tanstack-router") ||
|
||||
frontend.includes("next");
|
||||
const hasNuxtFrontend = frontend.includes("nuxt");
|
||||
const hasSvelteFrontend = frontend.includes("svelte");
|
||||
const hasSolidFrontend = frontend.includes("solid");
|
||||
const hasNextFrontend = frontend.includes("next");
|
||||
|
||||
if (addons.includes("turborepo")) {
|
||||
await addPackageDependency({
|
||||
devDependencies: ["turbo"],
|
||||
projectDir,
|
||||
});
|
||||
}
|
||||
|
||||
if (addons.includes("pwa") && (hasReactWebFrontend || hasSolidFrontend)) {
|
||||
await setupPwa(projectDir, frontend);
|
||||
}
|
||||
if (
|
||||
addons.includes("tauri") &&
|
||||
(hasReactWebFrontend ||
|
||||
hasNuxtFrontend ||
|
||||
hasSvelteFrontend ||
|
||||
hasSolidFrontend ||
|
||||
hasNextFrontend)
|
||||
) {
|
||||
await setupTauri(config);
|
||||
}
|
||||
if (addons.includes("biome")) {
|
||||
await setupBiome(projectDir);
|
||||
}
|
||||
if (addons.includes("husky")) {
|
||||
await setupHusky(projectDir);
|
||||
}
|
||||
if (addons.includes("starlight")) {
|
||||
await setupStarlight(config);
|
||||
}
|
||||
}
|
||||
|
||||
function getWebAppDir(projectDir: string, frontends: Frontend[]): string {
|
||||
if (
|
||||
frontends.some((f) =>
|
||||
["react-router", "tanstack-router", "nuxt", "svelte", "solid"].includes(
|
||||
f,
|
||||
),
|
||||
)
|
||||
) {
|
||||
return path.join(projectDir, "apps/web");
|
||||
}
|
||||
return path.join(projectDir, "apps/web");
|
||||
}
|
||||
|
||||
async function setupBiome(projectDir: string) {
|
||||
await addPackageDependency({
|
||||
devDependencies: ["@biomejs/biome"],
|
||||
projectDir,
|
||||
});
|
||||
|
||||
const packageJsonPath = path.join(projectDir, "package.json");
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
check: "biome check --write .",
|
||||
};
|
||||
|
||||
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
||||
}
|
||||
}
|
||||
|
||||
async function setupHusky(projectDir: string) {
|
||||
await addPackageDependency({
|
||||
devDependencies: ["husky", "lint-staged"],
|
||||
projectDir,
|
||||
});
|
||||
|
||||
const packageJsonPath = path.join(projectDir, "package.json");
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
prepare: "husky",
|
||||
};
|
||||
|
||||
packageJson["lint-staged"] = {
|
||||
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
|
||||
"biome check --write .",
|
||||
],
|
||||
};
|
||||
|
||||
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
||||
}
|
||||
}
|
||||
|
||||
async function setupPwa(projectDir: string, frontends: Frontend[]) {
|
||||
const isCompatibleFrontend = frontends.some((f) =>
|
||||
["react-router", "tanstack-router", "solid"].includes(f),
|
||||
);
|
||||
if (!isCompatibleFrontend) return;
|
||||
|
||||
const clientPackageDir = getWebAppDir(projectDir, frontends);
|
||||
|
||||
if (!(await fs.pathExists(clientPackageDir))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await addPackageDependency({
|
||||
dependencies: ["vite-plugin-pwa"],
|
||||
devDependencies: ["@vite-pwa/assets-generator"],
|
||||
projectDir: clientPackageDir,
|
||||
});
|
||||
|
||||
const clientPackageJsonPath = path.join(clientPackageDir, "package.json");
|
||||
if (await fs.pathExists(clientPackageJsonPath)) {
|
||||
const packageJson = await fs.readJson(clientPackageJsonPath);
|
||||
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
"generate-pwa-assets": "pwa-assets-generator",
|
||||
};
|
||||
|
||||
await fs.writeJson(clientPackageJsonPath, packageJson, { spaces: 2 });
|
||||
}
|
||||
}
|
||||
278
apps/cli/src/helpers/setup/api-setup.ts
Normal file
278
apps/cli/src/helpers/setup/api-setup.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import type { AvailableDependencies } from "../../constants";
|
||||
import type { Frontend, ProjectConfig } from "../../types";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
|
||||
export async function setupApi(config: ProjectConfig): Promise<void> {
|
||||
const { api, projectName, frontend, backend, packageManager, projectDir } =
|
||||
config;
|
||||
const isConvex = backend === "convex";
|
||||
const webDir = path.join(projectDir, "apps/web");
|
||||
const nativeDir = path.join(projectDir, "apps/native");
|
||||
const webDirExists = await fs.pathExists(webDir);
|
||||
const nativeDirExists = await fs.pathExists(nativeDir);
|
||||
|
||||
const hasReactWeb = frontend.some((f) =>
|
||||
["tanstack-router", "react-router", "tanstack-start", "next"].includes(f),
|
||||
);
|
||||
const hasNuxtWeb = frontend.includes("nuxt");
|
||||
const hasSvelteWeb = frontend.includes("svelte");
|
||||
const hasSolidWeb = frontend.includes("solid");
|
||||
|
||||
if (!isConvex && api !== "none") {
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
const serverDirExists = await fs.pathExists(serverDir);
|
||||
|
||||
if (serverDirExists) {
|
||||
if (api === "orpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@orpc/server", "@orpc/client"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
} else if (api === "trpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@trpc/server", "@trpc/client"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
if (config.backend === "hono") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@hono/trpc-server"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
} else if (config.backend === "elysia") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@elysiajs/trpc"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (webDirExists) {
|
||||
if (hasReactWeb) {
|
||||
if (api === "orpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@orpc/react-query", "@orpc/client", "@orpc/server"],
|
||||
projectDir: webDir,
|
||||
});
|
||||
} else if (api === "trpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: [
|
||||
"@trpc/tanstack-react-query",
|
||||
"@trpc/client",
|
||||
"@trpc/server",
|
||||
],
|
||||
projectDir: webDir,
|
||||
});
|
||||
}
|
||||
} else if (hasNuxtWeb) {
|
||||
if (api === "orpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@orpc/vue-query", "@orpc/client", "@orpc/server"],
|
||||
projectDir: webDir,
|
||||
});
|
||||
}
|
||||
} else if (hasSvelteWeb) {
|
||||
if (api === "orpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: [
|
||||
"@orpc/svelte-query",
|
||||
"@orpc/client",
|
||||
"@orpc/server",
|
||||
"@tanstack/svelte-query",
|
||||
],
|
||||
projectDir: webDir,
|
||||
});
|
||||
}
|
||||
} else if (hasSolidWeb) {
|
||||
if (api === "orpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: [
|
||||
"@orpc/solid-query",
|
||||
"@orpc/client",
|
||||
"@orpc/server",
|
||||
"@tanstack/solid-query",
|
||||
],
|
||||
projectDir: webDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeDirExists) {
|
||||
if (api === "trpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: [
|
||||
"@trpc/tanstack-react-query",
|
||||
"@trpc/client",
|
||||
"@trpc/server",
|
||||
],
|
||||
projectDir: nativeDir,
|
||||
});
|
||||
} else if (api === "orpc") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@orpc/react-query", "@orpc/client", "@orpc/server"],
|
||||
projectDir: nativeDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reactBasedFrontends: Frontend[] = [
|
||||
"react-router",
|
||||
"tanstack-router",
|
||||
"tanstack-start",
|
||||
"next",
|
||||
"native-nativewind",
|
||||
"native-unistyles",
|
||||
];
|
||||
const needsSolidQuery = frontend.includes("solid");
|
||||
const needsReactQuery = frontend.some((f) => reactBasedFrontends.includes(f));
|
||||
|
||||
if (needsReactQuery && !isConvex) {
|
||||
const reactQueryDeps: AvailableDependencies[] = ["@tanstack/react-query"];
|
||||
const reactQueryDevDeps: AvailableDependencies[] = [
|
||||
"@tanstack/react-query-devtools",
|
||||
];
|
||||
|
||||
const hasReactWeb = frontend.some(
|
||||
(f) =>
|
||||
f !== "native-nativewind" &&
|
||||
f !== "native-unistyles" &&
|
||||
reactBasedFrontends.includes(f),
|
||||
);
|
||||
const hasNative =
|
||||
frontend.includes("native-nativewind") ||
|
||||
frontend.includes("native-unistyles");
|
||||
|
||||
if (hasReactWeb && webDirExists) {
|
||||
const webPkgJsonPath = path.join(webDir, "package.json");
|
||||
if (await fs.pathExists(webPkgJsonPath)) {
|
||||
try {
|
||||
await addPackageDependency({
|
||||
dependencies: reactQueryDeps,
|
||||
devDependencies: reactQueryDevDeps,
|
||||
projectDir: webDir,
|
||||
});
|
||||
} catch (_error) {}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNative && nativeDirExists) {
|
||||
const nativePkgJsonPath = path.join(nativeDir, "package.json");
|
||||
if (await fs.pathExists(nativePkgJsonPath)) {
|
||||
try {
|
||||
await addPackageDependency({
|
||||
dependencies: reactQueryDeps,
|
||||
projectDir: nativeDir,
|
||||
});
|
||||
} catch (_error) {}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsSolidQuery && !isConvex) {
|
||||
const solidQueryDeps: AvailableDependencies[] = ["@tanstack/solid-query"];
|
||||
const solidQueryDevDeps: AvailableDependencies[] = [
|
||||
"@tanstack/solid-query-devtools",
|
||||
];
|
||||
|
||||
if (webDirExists) {
|
||||
const webPkgJsonPath = path.join(webDir, "package.json");
|
||||
if (await fs.pathExists(webPkgJsonPath)) {
|
||||
try {
|
||||
await addPackageDependency({
|
||||
dependencies: solidQueryDeps,
|
||||
devDependencies: solidQueryDevDeps,
|
||||
projectDir: webDir,
|
||||
});
|
||||
} catch (_error) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isConvex) {
|
||||
if (webDirExists) {
|
||||
const webPkgJsonPath = path.join(webDir, "package.json");
|
||||
if (await fs.pathExists(webPkgJsonPath)) {
|
||||
try {
|
||||
const webDepsToAdd: AvailableDependencies[] = ["convex"];
|
||||
if (frontend.includes("tanstack-start")) {
|
||||
webDepsToAdd.push("@convex-dev/react-query");
|
||||
}
|
||||
if (hasSvelteWeb) {
|
||||
webDepsToAdd.push("convex-svelte");
|
||||
}
|
||||
|
||||
await addPackageDependency({
|
||||
dependencies: webDepsToAdd,
|
||||
projectDir: webDir,
|
||||
});
|
||||
} catch (_error) {}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeDirExists) {
|
||||
const nativePkgJsonPath = path.join(nativeDir, "package.json");
|
||||
if (await fs.pathExists(nativePkgJsonPath)) {
|
||||
try {
|
||||
await addPackageDependency({
|
||||
dependencies: ["convex"],
|
||||
projectDir: nativeDir,
|
||||
});
|
||||
} catch (_error) {}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
const backendPackageName = `@${projectName}/backend`;
|
||||
const backendWorkspaceVersion =
|
||||
packageManager === "npm" ? "*" : "workspace:*";
|
||||
const addWorkspaceDepManually = async (
|
||||
pkgJsonPath: string,
|
||||
depName: string,
|
||||
depVersion: string,
|
||||
) => {
|
||||
try {
|
||||
const pkgJson = await fs.readJson(pkgJsonPath);
|
||||
if (!pkgJson.dependencies) {
|
||||
pkgJson.dependencies = {};
|
||||
}
|
||||
if (pkgJson.dependencies[depName] !== depVersion) {
|
||||
pkgJson.dependencies[depName] = depVersion;
|
||||
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
||||
} else {
|
||||
}
|
||||
} catch (_error) {}
|
||||
};
|
||||
|
||||
if (webDirExists) {
|
||||
const webPkgJsonPath = path.join(webDir, "package.json");
|
||||
if (await fs.pathExists(webPkgJsonPath)) {
|
||||
await addWorkspaceDepManually(
|
||||
webPkgJsonPath,
|
||||
backendPackageName,
|
||||
backendWorkspaceVersion,
|
||||
);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeDirExists) {
|
||||
const nativePkgJsonPath = path.join(nativeDir, "package.json");
|
||||
if (await fs.pathExists(nativePkgJsonPath)) {
|
||||
await addWorkspaceDepManually(
|
||||
nativePkgJsonPath,
|
||||
backendPackageName,
|
||||
backendWorkspaceVersion,
|
||||
);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
apps/cli/src/helpers/setup/auth-setup.ts
Normal file
81
apps/cli/src/helpers/setup/auth-setup.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import path from "node:path";
|
||||
import consola from "consola";
|
||||
import fs from "fs-extra";
|
||||
import pc from "picocolors";
|
||||
import type { ProjectConfig } from "../../types";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
|
||||
export async function setupAuth(config: ProjectConfig): Promise<void> {
|
||||
const { auth, frontend, backend, projectDir } = config;
|
||||
if (backend === "convex" || !auth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
const clientDir = path.join(projectDir, "apps/web");
|
||||
const nativeDir = path.join(projectDir, "apps/native");
|
||||
|
||||
const clientDirExists = await fs.pathExists(clientDir);
|
||||
const nativeDirExists = await fs.pathExists(nativeDir);
|
||||
const serverDirExists = await fs.pathExists(serverDir);
|
||||
|
||||
try {
|
||||
if (serverDirExists) {
|
||||
await addPackageDependency({
|
||||
dependencies: ["better-auth"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
|
||||
const hasWebFrontend = frontend.some((f) =>
|
||||
[
|
||||
"react-router",
|
||||
"tanstack-router",
|
||||
"tanstack-start",
|
||||
"next",
|
||||
"nuxt",
|
||||
"svelte",
|
||||
].includes(f),
|
||||
);
|
||||
|
||||
if (hasWebFrontend && clientDirExists) {
|
||||
await addPackageDependency({
|
||||
dependencies: ["better-auth"],
|
||||
projectDir: clientDir,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(frontend.includes("native-nativewind") ||
|
||||
frontend.includes("native-unistyles")) &&
|
||||
nativeDirExists
|
||||
) {
|
||||
await addPackageDependency({
|
||||
dependencies: ["better-auth", "@better-auth/expo"],
|
||||
projectDir: nativeDir,
|
||||
});
|
||||
if (serverDirExists) {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@better-auth/expo"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
consola.error(pc.red("Failed to configure authentication dependencies"));
|
||||
if (error instanceof Error) {
|
||||
consola.error(pc.red(error.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function generateAuthSecret(length = 32): string {
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
67
apps/cli/src/helpers/setup/backend-setup.ts
Normal file
67
apps/cli/src/helpers/setup/backend-setup.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import path from "node:path";
|
||||
import type { AvailableDependencies } from "../../constants";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
|
||||
import type { ProjectConfig } from "../../types";
|
||||
|
||||
export async function setupBackendDependencies(
|
||||
config: ProjectConfig,
|
||||
): Promise<void> {
|
||||
const { backend, runtime, api, projectDir } = config;
|
||||
|
||||
if (backend === "convex") {
|
||||
return;
|
||||
}
|
||||
|
||||
const framework = backend;
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
|
||||
const dependencies: AvailableDependencies[] = [];
|
||||
const devDependencies: AvailableDependencies[] = [];
|
||||
|
||||
if (framework === "hono") {
|
||||
dependencies.push("hono");
|
||||
if (api === "trpc") {
|
||||
dependencies.push("@hono/trpc-server");
|
||||
}
|
||||
|
||||
if (runtime === "node") {
|
||||
dependencies.push("@hono/node-server");
|
||||
devDependencies.push("tsx", "@types/node");
|
||||
}
|
||||
} else if (framework === "elysia") {
|
||||
dependencies.push("elysia", "@elysiajs/cors");
|
||||
if (api === "trpc") {
|
||||
dependencies.push("@elysiajs/trpc");
|
||||
}
|
||||
if (runtime === "node") {
|
||||
dependencies.push("@elysiajs/node");
|
||||
devDependencies.push("tsx", "@types/node");
|
||||
}
|
||||
} else if (framework === "express") {
|
||||
dependencies.push("express", "cors");
|
||||
devDependencies.push("@types/express", "@types/cors");
|
||||
|
||||
if (runtime === "node") {
|
||||
devDependencies.push("tsx", "@types/node");
|
||||
}
|
||||
} else if (framework === "fastify") {
|
||||
dependencies.push("fastify", "@fastify/cors");
|
||||
|
||||
if (runtime === "node") {
|
||||
devDependencies.push("tsx", "@types/node");
|
||||
}
|
||||
}
|
||||
|
||||
if (runtime === "bun") {
|
||||
devDependencies.push("@types/bun");
|
||||
}
|
||||
|
||||
if (dependencies.length > 0 || devDependencies.length > 0) {
|
||||
await addPackageDependency({
|
||||
dependencies,
|
||||
devDependencies,
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
91
apps/cli/src/helpers/setup/db-setup.ts
Normal file
91
apps/cli/src/helpers/setup/db-setup.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import path from "node:path";
|
||||
import { spinner } from "@clack/prompts";
|
||||
import consola from "consola";
|
||||
import fs from "fs-extra";
|
||||
import pc from "picocolors";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
import { setupMongoDBAtlas } from "../database-providers/mongodb-atlas-setup";
|
||||
import { setupPrismaPostgres } from "../database-providers/prisma-postgres-setup";
|
||||
import { setupSupabase } from "../database-providers/supabase-setup";
|
||||
import { setupTurso } from "../database-providers/turso-setup";
|
||||
|
||||
import { setupNeonPostgres } from "../database-providers/neon-setup";
|
||||
|
||||
import type { ProjectConfig } from "../../types";
|
||||
|
||||
export async function setupDatabase(config: ProjectConfig): Promise<void> {
|
||||
const { database, orm, dbSetup, backend, projectDir } = config;
|
||||
|
||||
if (backend === "convex" || database === "none") {
|
||||
if (backend !== "convex") {
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
const serverDbDir = path.join(serverDir, "src/db");
|
||||
if (await fs.pathExists(serverDbDir)) {
|
||||
await fs.remove(serverDbDir);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const s = spinner();
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
|
||||
if (!(await fs.pathExists(serverDir))) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (orm === "prisma") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@prisma/client"],
|
||||
devDependencies: ["prisma"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
} else if (orm === "drizzle") {
|
||||
if (database === "sqlite") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["drizzle-orm", "@libsql/client"],
|
||||
devDependencies: ["drizzle-kit"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
} else if (database === "postgres") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["drizzle-orm", "pg"],
|
||||
devDependencies: ["drizzle-kit", "@types/pg"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
} else if (database === "mysql") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["drizzle-orm", "mysql2"],
|
||||
devDependencies: ["drizzle-kit"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
} else if (orm === "mongoose") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["mongoose"],
|
||||
devDependencies: [],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
|
||||
if (database === "sqlite" && dbSetup === "turso") {
|
||||
await setupTurso(config);
|
||||
} else if (database === "postgres") {
|
||||
if (orm === "prisma" && dbSetup === "prisma-postgres") {
|
||||
await setupPrismaPostgres(config);
|
||||
} else if (dbSetup === "neon") {
|
||||
await setupNeonPostgres(config);
|
||||
} else if (dbSetup === "supabase") {
|
||||
await setupSupabase(config);
|
||||
}
|
||||
} else if (database === "mongodb" && dbSetup === "mongodb-atlas") {
|
||||
await setupMongoDBAtlas(config);
|
||||
}
|
||||
} catch (error) {
|
||||
s.stop(pc.red("Failed to set up database"));
|
||||
if (error instanceof Error) {
|
||||
consola.error(pc.red(error.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
55
apps/cli/src/helpers/setup/examples-setup.ts
Normal file
55
apps/cli/src/helpers/setup/examples-setup.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import type { AvailableDependencies } from "../../constants";
|
||||
import type { ProjectConfig } from "../../types";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
|
||||
export async function setupExamples(config: ProjectConfig): Promise<void> {
|
||||
const { examples, frontend, backend, projectDir } = config;
|
||||
|
||||
if (
|
||||
backend === "convex" ||
|
||||
!examples ||
|
||||
examples.length === 0 ||
|
||||
examples[0] === "none"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (examples.includes("ai")) {
|
||||
const clientDir = path.join(projectDir, "apps/web");
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
const clientDirExists = await fs.pathExists(clientDir);
|
||||
const serverDirExists = await fs.pathExists(serverDir);
|
||||
|
||||
const hasNuxt = frontend.includes("nuxt");
|
||||
const hasSvelte = frontend.includes("svelte");
|
||||
const hasReact =
|
||||
frontend.includes("react-router") ||
|
||||
frontend.includes("tanstack-router") ||
|
||||
frontend.includes("next") ||
|
||||
frontend.includes("tanstack-start");
|
||||
|
||||
if (clientDirExists) {
|
||||
const dependencies: AvailableDependencies[] = ["ai"];
|
||||
if (hasNuxt) {
|
||||
dependencies.push("@ai-sdk/vue");
|
||||
} else if (hasSvelte) {
|
||||
dependencies.push("@ai-sdk/svelte");
|
||||
} else if (hasReact) {
|
||||
dependencies.push("@ai-sdk/react");
|
||||
}
|
||||
await addPackageDependency({
|
||||
dependencies,
|
||||
projectDir: clientDir,
|
||||
});
|
||||
}
|
||||
|
||||
if (serverDirExists && backend !== "none") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["ai", "@ai-sdk/google"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
82
apps/cli/src/helpers/setup/runtime-setup.ts
Normal file
82
apps/cli/src/helpers/setup/runtime-setup.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import path from "node:path";
|
||||
import fs from "fs-extra";
|
||||
import type { Backend, ProjectConfig } from "../../types";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
|
||||
export async function setupRuntime(config: ProjectConfig): Promise<void> {
|
||||
const { runtime, backend, projectDir } = config;
|
||||
|
||||
if (backend === "convex" || backend === "next" || runtime === "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverDir = path.join(projectDir, "apps/server");
|
||||
|
||||
if (!(await fs.pathExists(serverDir))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (runtime === "bun") {
|
||||
await setupBunRuntime(serverDir, backend);
|
||||
} else if (runtime === "node") {
|
||||
await setupNodeRuntime(serverDir, backend);
|
||||
}
|
||||
}
|
||||
|
||||
async function setupBunRuntime(
|
||||
serverDir: string,
|
||||
_backend: Backend,
|
||||
): Promise<void> {
|
||||
const packageJsonPath = path.join(serverDir, "package.json");
|
||||
if (!(await fs.pathExists(packageJsonPath))) return;
|
||||
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
dev: "bun run --hot src/index.ts",
|
||||
start: "bun run dist/src/index.js",
|
||||
};
|
||||
|
||||
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
||||
|
||||
await addPackageDependency({
|
||||
devDependencies: ["@types/bun"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
|
||||
async function setupNodeRuntime(
|
||||
serverDir: string,
|
||||
backend: Backend,
|
||||
): Promise<void> {
|
||||
const packageJsonPath = path.join(serverDir, "package.json");
|
||||
if (!(await fs.pathExists(packageJsonPath))) return;
|
||||
|
||||
const packageJson = await fs.readJson(packageJsonPath);
|
||||
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
dev: "tsx watch src/index.ts",
|
||||
start: "node dist/src/index.js",
|
||||
};
|
||||
|
||||
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
||||
|
||||
await addPackageDependency({
|
||||
devDependencies: ["tsx", "@types/node"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
|
||||
if (backend === "hono") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@hono/node-server"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
} else if (backend === "elysia") {
|
||||
await addPackageDependency({
|
||||
dependencies: ["@elysiajs/node"],
|
||||
projectDir: serverDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
50
apps/cli/src/helpers/setup/starlight-setup.ts
Normal file
50
apps/cli/src/helpers/setup/starlight-setup.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import path from "node:path";
|
||||
import { spinner } from "@clack/prompts";
|
||||
import consola from "consola";
|
||||
import { execa } from "execa";
|
||||
import pc from "picocolors";
|
||||
import type { ProjectConfig } from "../../types";
|
||||
import { getPackageExecutionCommand } from "../../utils/get-package-execution-command";
|
||||
|
||||
export async function setupStarlight(config: ProjectConfig): Promise<void> {
|
||||
const { packageManager, projectDir } = config;
|
||||
const s = spinner();
|
||||
|
||||
try {
|
||||
s.start("Setting up Starlight docs...");
|
||||
|
||||
const starlightArgs = [
|
||||
"docs",
|
||||
"--template",
|
||||
"starlight",
|
||||
"--no-install",
|
||||
"--add",
|
||||
"tailwind",
|
||||
"--no-git",
|
||||
"--skip-houston",
|
||||
];
|
||||
const starlightArgsString = starlightArgs.join(" ");
|
||||
|
||||
const commandWithArgs = `create-astro@latest ${starlightArgsString}`;
|
||||
|
||||
const starlightInitCommand = getPackageExecutionCommand(
|
||||
packageManager,
|
||||
commandWithArgs,
|
||||
);
|
||||
|
||||
await execa(starlightInitCommand, {
|
||||
cwd: path.join(projectDir, "apps"),
|
||||
env: {
|
||||
CI: "true",
|
||||
},
|
||||
shell: true,
|
||||
});
|
||||
|
||||
s.stop("Starlight docs setup successfully!");
|
||||
} catch (error) {
|
||||
s.stop(pc.red("Failed to set up Starlight docs"));
|
||||
if (error instanceof Error) {
|
||||
consola.error(pc.red(error.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
100
apps/cli/src/helpers/setup/tauri-setup.ts
Normal file
100
apps/cli/src/helpers/setup/tauri-setup.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import path from "node:path";
|
||||
import { spinner } from "@clack/prompts";
|
||||
import { consola } from "consola";
|
||||
import { execa } from "execa";
|
||||
import fs from "fs-extra";
|
||||
import pc from "picocolors";
|
||||
import { addPackageDependency } from "../../utils/add-package-deps";
|
||||
import { getPackageExecutionCommand } from "../../utils/get-package-execution-command";
|
||||
|
||||
import type { ProjectConfig } from "../../types";
|
||||
|
||||
export async function setupTauri(config: ProjectConfig): Promise<void> {
|
||||
const { packageManager, frontend, projectDir } = config;
|
||||
const s = spinner();
|
||||
const clientPackageDir = path.join(projectDir, "apps/web");
|
||||
|
||||
if (!(await fs.pathExists(clientPackageDir))) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
s.start("Setting up Tauri desktop app support...");
|
||||
|
||||
await addPackageDependency({
|
||||
devDependencies: ["@tauri-apps/cli"],
|
||||
projectDir: clientPackageDir,
|
||||
});
|
||||
|
||||
const clientPackageJsonPath = path.join(clientPackageDir, "package.json");
|
||||
if (await fs.pathExists(clientPackageJsonPath)) {
|
||||
const packageJson = await fs.readJson(clientPackageJsonPath);
|
||||
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
tauri: "tauri",
|
||||
"desktop:dev": "tauri dev",
|
||||
"desktop:build": "tauri build",
|
||||
};
|
||||
|
||||
await fs.writeJson(clientPackageJsonPath, packageJson, { spaces: 2 });
|
||||
}
|
||||
|
||||
const _hasTanstackRouter = frontend.includes("tanstack-router");
|
||||
const hasReactRouter = frontend.includes("react-router");
|
||||
const hasNuxt = frontend.includes("nuxt");
|
||||
const hasSvelte = frontend.includes("svelte");
|
||||
const _hasSolid = frontend.includes("solid");
|
||||
const hasNext = frontend.includes("next");
|
||||
|
||||
const devUrl =
|
||||
hasReactRouter || hasSvelte
|
||||
? "http://localhost:5173"
|
||||
: hasNext
|
||||
? "http://localhost:3001"
|
||||
: "http://localhost:3001";
|
||||
|
||||
const frontendDist = hasNuxt
|
||||
? "../.output/public"
|
||||
: hasSvelte
|
||||
? "../build"
|
||||
: hasNext
|
||||
? "../.next"
|
||||
: hasReactRouter
|
||||
? "../build/client"
|
||||
: "../dist";
|
||||
|
||||
const tauriArgs = [
|
||||
"init",
|
||||
`--app-name=${path.basename(projectDir)}`,
|
||||
`--window-title=${path.basename(projectDir)}`,
|
||||
`--frontend-dist=${frontendDist}`,
|
||||
`--dev-url=${devUrl}`,
|
||||
`--before-dev-command=\"${packageManager} run dev\"`,
|
||||
`--before-build-command=\"${packageManager} run build\"`,
|
||||
];
|
||||
const tauriArgsString = tauriArgs.join(" ");
|
||||
|
||||
const commandWithArgs = `@tauri-apps/cli@latest ${tauriArgsString}`;
|
||||
|
||||
const tauriInitCommand = getPackageExecutionCommand(
|
||||
packageManager,
|
||||
commandWithArgs,
|
||||
);
|
||||
|
||||
await execa(tauriInitCommand, {
|
||||
cwd: clientPackageDir,
|
||||
env: {
|
||||
CI: "true",
|
||||
},
|
||||
shell: true,
|
||||
});
|
||||
|
||||
s.stop("Tauri desktop app support configured successfully!");
|
||||
} catch (error) {
|
||||
s.stop(pc.red("Failed to set up Tauri"));
|
||||
if (error instanceof Error) {
|
||||
consola.error(pc.red(error.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user