create-better-t-stack 3.10.0 → 3.11.0-pr749.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-better-t-stack +98 -0
- package/package.json +40 -30
- package/src/api.ts +203 -0
- package/src/cli.ts +185 -0
- package/src/constants.ts +270 -0
- package/src/helpers/addons/addons-setup.ts +201 -0
- package/src/helpers/addons/examples-setup.ts +137 -0
- package/src/helpers/addons/fumadocs-setup.ts +99 -0
- package/src/helpers/addons/oxlint-setup.ts +36 -0
- package/src/helpers/addons/ruler-setup.ts +135 -0
- package/src/helpers/addons/starlight-setup.ts +45 -0
- package/src/helpers/addons/tauri-setup.ts +90 -0
- package/src/helpers/addons/tui-setup.ts +64 -0
- package/src/helpers/addons/ultracite-setup.ts +228 -0
- package/src/helpers/addons/vite-pwa-setup.ts +59 -0
- package/src/helpers/addons/wxt-setup.ts +86 -0
- package/src/helpers/core/add-addons.ts +85 -0
- package/src/helpers/core/add-deployment.ts +102 -0
- package/src/helpers/core/api-setup.ts +280 -0
- package/src/helpers/core/auth-setup.ts +203 -0
- package/src/helpers/core/backend-setup.ts +69 -0
- package/src/helpers/core/command-handlers.ts +354 -0
- package/src/helpers/core/convex-codegen.ts +14 -0
- package/src/helpers/core/create-project.ts +134 -0
- package/src/helpers/core/create-readme.ts +694 -0
- package/src/helpers/core/db-setup.ts +184 -0
- package/src/helpers/core/detect-project-config.ts +41 -0
- package/src/helpers/core/env-setup.ts +481 -0
- package/src/helpers/core/git.ts +23 -0
- package/src/helpers/core/install-dependencies.ts +29 -0
- package/src/helpers/core/payments-setup.ts +48 -0
- package/src/helpers/core/post-installation.ts +403 -0
- package/src/helpers/core/project-config.ts +250 -0
- package/src/helpers/core/runtime-setup.ts +76 -0
- package/src/helpers/core/template-manager.ts +917 -0
- package/src/helpers/core/workspace-setup.ts +184 -0
- package/src/helpers/database-providers/d1-setup.ts +28 -0
- package/src/helpers/database-providers/docker-compose-setup.ts +50 -0
- package/src/helpers/database-providers/mongodb-atlas-setup.ts +182 -0
- package/src/helpers/database-providers/neon-setup.ts +240 -0
- package/src/helpers/database-providers/planetscale-setup.ts +78 -0
- package/src/helpers/database-providers/prisma-postgres-setup.ts +193 -0
- package/src/helpers/database-providers/supabase-setup.ts +196 -0
- package/src/helpers/database-providers/turso-setup.ts +309 -0
- package/src/helpers/deployment/alchemy/alchemy-combined-setup.ts +80 -0
- package/src/helpers/deployment/alchemy/alchemy-next-setup.ts +52 -0
- package/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts +105 -0
- package/src/helpers/deployment/alchemy/alchemy-react-router-setup.ts +33 -0
- package/src/helpers/deployment/alchemy/alchemy-solid-setup.ts +33 -0
- package/src/helpers/deployment/alchemy/alchemy-svelte-setup.ts +99 -0
- package/src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts +34 -0
- package/src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts +99 -0
- package/src/helpers/deployment/alchemy/env-dts-setup.ts +76 -0
- package/src/helpers/deployment/alchemy/index.ts +7 -0
- package/src/helpers/deployment/server-deploy-setup.ts +55 -0
- package/src/helpers/deployment/web-deploy-setup.ts +58 -0
- package/src/index.ts +51 -0
- package/src/prompts/addons.ts +200 -0
- package/src/prompts/api.ts +49 -0
- package/src/prompts/auth.ts +84 -0
- package/src/prompts/backend.ts +83 -0
- package/src/prompts/config-prompts.ts +138 -0
- package/src/prompts/database-setup.ts +112 -0
- package/src/prompts/database.ts +57 -0
- package/src/prompts/examples.ts +60 -0
- package/src/prompts/frontend.ts +118 -0
- package/src/prompts/git.ts +16 -0
- package/src/prompts/install.ts +16 -0
- package/src/prompts/orm.ts +53 -0
- package/src/prompts/package-manager.ts +32 -0
- package/src/prompts/payments.ts +50 -0
- package/src/prompts/project-name.ts +86 -0
- package/src/prompts/runtime.ts +47 -0
- package/src/prompts/server-deploy.ts +91 -0
- package/src/prompts/web-deploy.ts +107 -0
- package/src/tui/app.tsx +1062 -0
- package/src/types.ts +70 -0
- package/src/utils/add-package-deps.ts +57 -0
- package/src/utils/analytics.ts +39 -0
- package/src/utils/better-auth-plugin-setup.ts +71 -0
- package/src/utils/bts-config.ts +122 -0
- package/src/utils/command-exists.ts +16 -0
- package/src/utils/compatibility-rules.ts +337 -0
- package/src/utils/compatibility.ts +11 -0
- package/src/utils/config-processing.ts +130 -0
- package/src/utils/config-validation.ts +470 -0
- package/src/utils/display-config.ts +96 -0
- package/src/utils/docker-utils.ts +70 -0
- package/src/utils/errors.ts +30 -0
- package/src/utils/file-formatter.ts +11 -0
- package/src/utils/generate-reproducible-command.ts +53 -0
- package/src/utils/get-latest-cli-version.ts +27 -0
- package/src/utils/get-package-manager.ts +13 -0
- package/src/utils/open-url.ts +18 -0
- package/src/utils/package-runner.ts +23 -0
- package/src/utils/project-directory.ts +102 -0
- package/src/utils/project-name-validation.ts +43 -0
- package/src/utils/render-title.ts +48 -0
- package/src/utils/setup-catalogs.ts +192 -0
- package/src/utils/sponsors.ts +101 -0
- package/src/utils/telemetry.ts +19 -0
- package/src/utils/template-processor.ts +64 -0
- package/src/utils/templates.ts +94 -0
- package/src/utils/ts-morph.ts +26 -0
- package/src/validation.ts +117 -0
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +1 -1
- package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +17 -0
- package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +9 -0
- package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +67 -0
- package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +301 -3
- package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +296 -10
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +180 -1
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +172 -9
- package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +156 -6
- package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +156 -4
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +159 -6
- package/templates/frontend/react/web-base/src/index.css.hbs +1 -1
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +0 -8
- package/dist/index.d.mts +0 -347
- package/dist/index.mjs +0 -4
- package/dist/src-QkFdHtZE.mjs +0 -7072
- package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +0 -7
- package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +0 -22
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { isCancel, log, select, spinner } from "@clack/prompts";
|
|
3
|
+
import { $ } from "bun";
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import type { ProjectConfig } from "../../types";
|
|
7
|
+
import { exitCancelled } from "../../utils/errors";
|
|
8
|
+
import { getPackageExecutionCommand } from "../../utils/package-runner";
|
|
9
|
+
|
|
10
|
+
type WxtTemplate = "vanilla" | "vue" | "react" | "solid" | "svelte";
|
|
11
|
+
|
|
12
|
+
const TEMPLATES = {
|
|
13
|
+
vanilla: {
|
|
14
|
+
label: "Vanilla",
|
|
15
|
+
hint: "Vanilla JavaScript template",
|
|
16
|
+
},
|
|
17
|
+
vue: {
|
|
18
|
+
label: "Vue",
|
|
19
|
+
hint: "Vue.js template",
|
|
20
|
+
},
|
|
21
|
+
react: {
|
|
22
|
+
label: "React",
|
|
23
|
+
hint: "React template",
|
|
24
|
+
},
|
|
25
|
+
solid: {
|
|
26
|
+
label: "Solid",
|
|
27
|
+
hint: "SolidJS template",
|
|
28
|
+
},
|
|
29
|
+
svelte: {
|
|
30
|
+
label: "Svelte",
|
|
31
|
+
hint: "Svelte template",
|
|
32
|
+
},
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
export async function setupWxt(config: ProjectConfig) {
|
|
36
|
+
const { packageManager, projectDir } = config;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
log.info("Setting up WXT...");
|
|
40
|
+
|
|
41
|
+
const template = await select<WxtTemplate>({
|
|
42
|
+
message: "Choose a template",
|
|
43
|
+
options: Object.entries(TEMPLATES).map(([key, template]) => ({
|
|
44
|
+
value: key as WxtTemplate,
|
|
45
|
+
label: template.label,
|
|
46
|
+
hint: template.hint,
|
|
47
|
+
})),
|
|
48
|
+
initialValue: "react",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (isCancel(template)) return exitCancelled("Operation cancelled");
|
|
52
|
+
|
|
53
|
+
const commandWithArgs = `wxt@latest init extension --template ${template} --pm ${packageManager}`;
|
|
54
|
+
|
|
55
|
+
const wxtInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
56
|
+
|
|
57
|
+
const appsDir = path.join(projectDir, "apps");
|
|
58
|
+
await fs.ensureDir(appsDir);
|
|
59
|
+
|
|
60
|
+
const s = spinner();
|
|
61
|
+
s.start("Running WXT init command...");
|
|
62
|
+
|
|
63
|
+
await $`${{ raw: wxtInitCommand }}`.cwd(appsDir).env({ CI: "true" });
|
|
64
|
+
|
|
65
|
+
const extensionDir = path.join(projectDir, "apps", "extension");
|
|
66
|
+
const packageJsonPath = path.join(extensionDir, "package.json");
|
|
67
|
+
|
|
68
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
69
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
70
|
+
packageJson.name = "extension";
|
|
71
|
+
|
|
72
|
+
if (packageJson.scripts?.dev) {
|
|
73
|
+
packageJson.scripts.dev = `${packageJson.scripts.dev} --port 5555`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
s.stop("WXT setup complete!");
|
|
80
|
+
} catch (error) {
|
|
81
|
+
log.error(pc.red("Failed to set up WXT"));
|
|
82
|
+
if (error instanceof Error) {
|
|
83
|
+
console.error(pc.red(error.message));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { log } from "@clack/prompts";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import type { AddInput, Addons, ProjectConfig } from "../../types";
|
|
5
|
+
import { updateBtsConfig } from "../../utils/bts-config";
|
|
6
|
+
import { validateAddonCompatibility } from "../../utils/compatibility-rules";
|
|
7
|
+
import { exitWithError } from "../../utils/errors";
|
|
8
|
+
import { setupAddons } from "../addons/addons-setup";
|
|
9
|
+
import { detectProjectConfig, isBetterTStackProject } from "./detect-project-config";
|
|
10
|
+
import { installDependencies } from "./install-dependencies";
|
|
11
|
+
import { setupAddonsTemplate } from "./template-manager";
|
|
12
|
+
|
|
13
|
+
export async function addAddonsToProject(
|
|
14
|
+
input: AddInput & { addons: Addons[]; suppressInstallMessage?: boolean },
|
|
15
|
+
) {
|
|
16
|
+
try {
|
|
17
|
+
const projectDir = input.projectDir || process.cwd();
|
|
18
|
+
|
|
19
|
+
const isBetterTStack = await isBetterTStackProject(projectDir);
|
|
20
|
+
if (!isBetterTStack) {
|
|
21
|
+
exitWithError(
|
|
22
|
+
"This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const detectedConfig = await detectProjectConfig(projectDir);
|
|
27
|
+
if (!detectedConfig) {
|
|
28
|
+
exitWithError(
|
|
29
|
+
"Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const config: ProjectConfig = {
|
|
34
|
+
projectName: detectedConfig.projectName || path.basename(projectDir),
|
|
35
|
+
projectDir,
|
|
36
|
+
relativePath: ".",
|
|
37
|
+
database: detectedConfig.database || "none",
|
|
38
|
+
orm: detectedConfig.orm || "none",
|
|
39
|
+
backend: detectedConfig.backend || "none",
|
|
40
|
+
runtime: detectedConfig.runtime || "none",
|
|
41
|
+
frontend: detectedConfig.frontend || [],
|
|
42
|
+
addons: input.addons,
|
|
43
|
+
examples: detectedConfig.examples || [],
|
|
44
|
+
auth: detectedConfig.auth || "none",
|
|
45
|
+
payments: detectedConfig.payments || "none",
|
|
46
|
+
git: false,
|
|
47
|
+
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
48
|
+
install: input.install || false,
|
|
49
|
+
dbSetup: detectedConfig.dbSetup || "none",
|
|
50
|
+
api: detectedConfig.api || "none",
|
|
51
|
+
webDeploy: detectedConfig.webDeploy || "none",
|
|
52
|
+
serverDeploy: detectedConfig.serverDeploy || "none",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
for (const addon of input.addons) {
|
|
56
|
+
const { isCompatible, reason } = validateAddonCompatibility(addon, config.frontend);
|
|
57
|
+
if (!isCompatible) {
|
|
58
|
+
exitWithError(
|
|
59
|
+
reason || `${addon} addon is not compatible with current frontend configuration`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await setupAddonsTemplate(projectDir, config);
|
|
65
|
+
await setupAddons(config, true);
|
|
66
|
+
|
|
67
|
+
const currentAddons = detectedConfig.addons || [];
|
|
68
|
+
const mergedAddons = [...new Set([...currentAddons, ...input.addons])];
|
|
69
|
+
await updateBtsConfig(projectDir, { addons: mergedAddons });
|
|
70
|
+
|
|
71
|
+
if (config.install) {
|
|
72
|
+
await installDependencies({
|
|
73
|
+
projectDir,
|
|
74
|
+
packageManager: config.packageManager,
|
|
75
|
+
});
|
|
76
|
+
} else if (!input.suppressInstallMessage) {
|
|
77
|
+
log.info(
|
|
78
|
+
pc.yellow(`Run ${pc.bold(`${config.packageManager} install`)} to install dependencies`),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
exitWithError(`Error adding addons: ${message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { log } from "@clack/prompts";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import type { AddInput, ProjectConfig, ServerDeploy, WebDeploy } from "../../types";
|
|
5
|
+
import { updateBtsConfig } from "../../utils/bts-config";
|
|
6
|
+
import { exitWithError } from "../../utils/errors";
|
|
7
|
+
import { setupServerDeploy } from "../deployment/server-deploy-setup";
|
|
8
|
+
import { setupWebDeploy } from "../deployment/web-deploy-setup";
|
|
9
|
+
import { detectProjectConfig, isBetterTStackProject } from "./detect-project-config";
|
|
10
|
+
import { installDependencies } from "./install-dependencies";
|
|
11
|
+
import { setupDeploymentTemplates } from "./template-manager";
|
|
12
|
+
|
|
13
|
+
export async function addDeploymentToProject(
|
|
14
|
+
input: AddInput & {
|
|
15
|
+
webDeploy?: WebDeploy;
|
|
16
|
+
serverDeploy?: ServerDeploy;
|
|
17
|
+
suppressInstallMessage?: boolean;
|
|
18
|
+
},
|
|
19
|
+
) {
|
|
20
|
+
try {
|
|
21
|
+
const projectDir = input.projectDir || process.cwd();
|
|
22
|
+
|
|
23
|
+
const isBetterTStack = await isBetterTStackProject(projectDir);
|
|
24
|
+
if (!isBetterTStack) {
|
|
25
|
+
exitWithError(
|
|
26
|
+
"This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const detectedConfig = await detectProjectConfig(projectDir);
|
|
31
|
+
if (!detectedConfig) {
|
|
32
|
+
exitWithError(
|
|
33
|
+
"Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (input.webDeploy && detectedConfig.webDeploy === input.webDeploy) {
|
|
38
|
+
exitWithError(`${input.webDeploy} web deployment is already configured for this project.`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (input.serverDeploy && detectedConfig.serverDeploy === input.serverDeploy) {
|
|
42
|
+
exitWithError(
|
|
43
|
+
`${input.serverDeploy} server deployment is already configured for this project.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const config: ProjectConfig = {
|
|
48
|
+
projectName: detectedConfig.projectName || path.basename(projectDir),
|
|
49
|
+
projectDir,
|
|
50
|
+
relativePath: ".",
|
|
51
|
+
database: detectedConfig.database || "none",
|
|
52
|
+
orm: detectedConfig.orm || "none",
|
|
53
|
+
backend: detectedConfig.backend || "none",
|
|
54
|
+
runtime: detectedConfig.runtime || "none",
|
|
55
|
+
frontend: detectedConfig.frontend || [],
|
|
56
|
+
addons: detectedConfig.addons || [],
|
|
57
|
+
examples: detectedConfig.examples || [],
|
|
58
|
+
auth: detectedConfig.auth || "none",
|
|
59
|
+
payments: detectedConfig.payments || "none",
|
|
60
|
+
git: false,
|
|
61
|
+
packageManager: input.packageManager || detectedConfig.packageManager || "npm",
|
|
62
|
+
install: input.install || false,
|
|
63
|
+
dbSetup: detectedConfig.dbSetup || "none",
|
|
64
|
+
api: detectedConfig.api || "none",
|
|
65
|
+
webDeploy: input.webDeploy || detectedConfig.webDeploy || "none",
|
|
66
|
+
serverDeploy: input.serverDeploy || detectedConfig.serverDeploy || "none",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (input.webDeploy && input.webDeploy !== "none") {
|
|
70
|
+
log.info(
|
|
71
|
+
pc.green(`Adding ${input.webDeploy} web deployment to ${config.frontend.join("/")}`),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (input.serverDeploy && input.serverDeploy !== "none") {
|
|
76
|
+
log.info(pc.green(`Adding ${input.serverDeploy} server deployment`));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await setupDeploymentTemplates(projectDir, config);
|
|
80
|
+
await setupWebDeploy(config);
|
|
81
|
+
await setupServerDeploy(config);
|
|
82
|
+
|
|
83
|
+
await updateBtsConfig(projectDir, {
|
|
84
|
+
webDeploy: input.webDeploy || config.webDeploy,
|
|
85
|
+
serverDeploy: input.serverDeploy || config.serverDeploy,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (config.install) {
|
|
89
|
+
await installDependencies({
|
|
90
|
+
projectDir,
|
|
91
|
+
packageManager: config.packageManager,
|
|
92
|
+
});
|
|
93
|
+
} else if (!input.suppressInstallMessage) {
|
|
94
|
+
log.info(
|
|
95
|
+
pc.yellow(`Run ${pc.bold(`${config.packageManager} install`)} to install dependencies`),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
exitWithError(`Error adding deployment: ${message}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import type { AvailableDependencies } from "../../constants";
|
|
4
|
+
import type { API, Backend, Frontend, ProjectConfig } from "../../types";
|
|
5
|
+
import { addPackageDependency } from "../../utils/add-package-deps";
|
|
6
|
+
|
|
7
|
+
function getFrontendType(frontend: Frontend[]): {
|
|
8
|
+
hasReactWeb: boolean;
|
|
9
|
+
hasNuxtWeb: boolean;
|
|
10
|
+
hasSvelteWeb: boolean;
|
|
11
|
+
hasSolidWeb: boolean;
|
|
12
|
+
hasNative: boolean;
|
|
13
|
+
} {
|
|
14
|
+
const reactBasedFrontends = ["tanstack-router", "react-router", "tanstack-start", "next"];
|
|
15
|
+
const nativeFrontends = ["native-bare", "native-uniwind", "native-unistyles"];
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
hasReactWeb: frontend.some((f) => reactBasedFrontends.includes(f)),
|
|
19
|
+
hasNuxtWeb: frontend.includes("nuxt"),
|
|
20
|
+
hasSvelteWeb: frontend.includes("svelte"),
|
|
21
|
+
hasSolidWeb: frontend.includes("solid"),
|
|
22
|
+
hasNative: frontend.some((f) => nativeFrontends.includes(f)),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getApiDependencies(
|
|
27
|
+
api: API,
|
|
28
|
+
frontendType: ReturnType<typeof getFrontendType>,
|
|
29
|
+
backend: Backend,
|
|
30
|
+
) {
|
|
31
|
+
const deps: Record<string, { dependencies: string[]; devDependencies?: string[] }> = {};
|
|
32
|
+
|
|
33
|
+
if (api === "orpc") {
|
|
34
|
+
deps.server = {
|
|
35
|
+
dependencies: ["@orpc/server", "@orpc/client", "@orpc/openapi", "@orpc/zod"],
|
|
36
|
+
};
|
|
37
|
+
} else if (api === "trpc") {
|
|
38
|
+
deps.server = { dependencies: ["@trpc/server", "@trpc/client"] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (backend !== "self" && backend !== "convex" && backend !== "none") {
|
|
42
|
+
if (!deps.server) {
|
|
43
|
+
deps.server = { dependencies: [] };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (backend === "hono") {
|
|
47
|
+
deps.server.dependencies.push("hono");
|
|
48
|
+
} else if (backend === "elysia") {
|
|
49
|
+
deps.server.dependencies.push("elysia");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (frontendType.hasReactWeb) {
|
|
54
|
+
if (api === "orpc") {
|
|
55
|
+
deps.web = {
|
|
56
|
+
dependencies: ["@orpc/tanstack-query", "@orpc/client", "@orpc/server"],
|
|
57
|
+
};
|
|
58
|
+
} else if (api === "trpc") {
|
|
59
|
+
deps.web = {
|
|
60
|
+
dependencies: ["@trpc/tanstack-react-query", "@trpc/client", "@trpc/server"],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
} else if (frontendType.hasNuxtWeb && api === "orpc") {
|
|
64
|
+
deps.web = {
|
|
65
|
+
dependencies: ["@tanstack/vue-query", "@orpc/tanstack-query", "@orpc/client", "@orpc/server"],
|
|
66
|
+
devDependencies: ["@tanstack/vue-query-devtools"],
|
|
67
|
+
};
|
|
68
|
+
} else if (frontendType.hasSvelteWeb && api === "orpc") {
|
|
69
|
+
deps.web = {
|
|
70
|
+
dependencies: [
|
|
71
|
+
"@orpc/tanstack-query",
|
|
72
|
+
"@orpc/client",
|
|
73
|
+
"@orpc/server",
|
|
74
|
+
"@tanstack/svelte-query",
|
|
75
|
+
],
|
|
76
|
+
devDependencies: ["@tanstack/svelte-query-devtools"],
|
|
77
|
+
};
|
|
78
|
+
} else if (frontendType.hasSolidWeb && api === "orpc") {
|
|
79
|
+
deps.web = {
|
|
80
|
+
dependencies: [
|
|
81
|
+
"@orpc/tanstack-query",
|
|
82
|
+
"@orpc/client",
|
|
83
|
+
"@orpc/server",
|
|
84
|
+
"@tanstack/solid-query",
|
|
85
|
+
],
|
|
86
|
+
devDependencies: ["@tanstack/solid-query-devtools", "@tanstack/solid-router-devtools"],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (api === "trpc") {
|
|
91
|
+
deps.native = {
|
|
92
|
+
dependencies: ["@trpc/tanstack-react-query", "@trpc/client", "@trpc/server"],
|
|
93
|
+
};
|
|
94
|
+
} else if (api === "orpc") {
|
|
95
|
+
deps.native = { dependencies: ["@orpc/tanstack-query", "@orpc/client"] };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return deps;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getQueryDependencies(frontend: Frontend[]) {
|
|
102
|
+
const reactBasedFrontends: Frontend[] = [
|
|
103
|
+
"react-router",
|
|
104
|
+
"tanstack-router",
|
|
105
|
+
"tanstack-start",
|
|
106
|
+
"next",
|
|
107
|
+
"native-bare",
|
|
108
|
+
"native-uniwind",
|
|
109
|
+
"native-unistyles",
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const deps: Record<string, { dependencies: string[]; devDependencies?: string[] }> = {};
|
|
113
|
+
|
|
114
|
+
const needsReactQuery = frontend.some((f) => reactBasedFrontends.includes(f));
|
|
115
|
+
if (needsReactQuery) {
|
|
116
|
+
const hasReactWeb = frontend.some(
|
|
117
|
+
(f) =>
|
|
118
|
+
f !== "native-bare" &&
|
|
119
|
+
f !== "native-uniwind" &&
|
|
120
|
+
f !== "native-unistyles" &&
|
|
121
|
+
reactBasedFrontends.includes(f),
|
|
122
|
+
);
|
|
123
|
+
const hasNative =
|
|
124
|
+
frontend.includes("native-bare") ||
|
|
125
|
+
frontend.includes("native-uniwind") ||
|
|
126
|
+
frontend.includes("native-unistyles");
|
|
127
|
+
|
|
128
|
+
if (hasReactWeb) {
|
|
129
|
+
deps.web = {
|
|
130
|
+
dependencies: ["@tanstack/react-query"],
|
|
131
|
+
devDependencies: ["@tanstack/react-query-devtools"],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (hasNative) {
|
|
135
|
+
deps.native = { dependencies: ["@tanstack/react-query"] };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (frontend.includes("solid")) {
|
|
140
|
+
deps.web = {
|
|
141
|
+
dependencies: ["@tanstack/solid-query"],
|
|
142
|
+
devDependencies: ["@tanstack/solid-query-devtools", "@tanstack/solid-router-devtools"],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return deps;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getConvexDependencies(frontend: Frontend[]) {
|
|
150
|
+
const deps: Record<string, { dependencies: string[] }> = {
|
|
151
|
+
web: { dependencies: ["convex"] },
|
|
152
|
+
native: { dependencies: ["convex"] },
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (frontend.includes("tanstack-start")) {
|
|
156
|
+
deps.web.dependencies.push("@convex-dev/react-query");
|
|
157
|
+
}
|
|
158
|
+
if (frontend.includes("svelte")) {
|
|
159
|
+
deps.web.dependencies.push("convex-svelte");
|
|
160
|
+
}
|
|
161
|
+
if (frontend.includes("nuxt")) {
|
|
162
|
+
deps.web.dependencies.push("convex-nuxt", "convex-vue");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return deps;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function setupApi(config: ProjectConfig) {
|
|
169
|
+
const { api, frontend, backend, projectDir } = config;
|
|
170
|
+
const isConvex = backend === "convex";
|
|
171
|
+
|
|
172
|
+
const webDir = path.join(projectDir, "apps/web");
|
|
173
|
+
const nativeDir = path.join(projectDir, "apps/native");
|
|
174
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
175
|
+
|
|
176
|
+
const webDirExists = await fs.pathExists(webDir);
|
|
177
|
+
const nativeDirExists = await fs.pathExists(nativeDir);
|
|
178
|
+
const _serverDirExists = await fs.pathExists(serverDir);
|
|
179
|
+
|
|
180
|
+
const frontendType = getFrontendType(frontend);
|
|
181
|
+
|
|
182
|
+
if (!isConvex && api !== "none") {
|
|
183
|
+
const apiDeps = getApiDependencies(api, frontendType, backend);
|
|
184
|
+
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
185
|
+
|
|
186
|
+
if (apiDeps.server) {
|
|
187
|
+
await addPackageDependency({
|
|
188
|
+
dependencies: apiDeps.server.dependencies as AvailableDependencies[],
|
|
189
|
+
projectDir: apiPackageDir,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (backend === "self" && webDirExists) {
|
|
193
|
+
await addPackageDependency({
|
|
194
|
+
dependencies: apiDeps.server.dependencies as AvailableDependencies[],
|
|
195
|
+
projectDir: webDir,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (backend === "self") {
|
|
200
|
+
const frameworkDeps: AvailableDependencies[] = [];
|
|
201
|
+
if (frontend.includes("next")) {
|
|
202
|
+
frameworkDeps.push("next");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (frameworkDeps.length > 0) {
|
|
206
|
+
await addPackageDependency({
|
|
207
|
+
dependencies: frameworkDeps,
|
|
208
|
+
projectDir: apiPackageDir,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (config.auth === "better-auth" && (backend === "express" || backend === "fastify")) {
|
|
215
|
+
await addPackageDependency({
|
|
216
|
+
dependencies: ["better-auth"],
|
|
217
|
+
projectDir: apiPackageDir,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (backend === "express") {
|
|
222
|
+
await addPackageDependency({
|
|
223
|
+
devDependencies: ["@types/express"],
|
|
224
|
+
projectDir: apiPackageDir,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (webDirExists && apiDeps.web) {
|
|
229
|
+
await addPackageDependency({
|
|
230
|
+
dependencies: apiDeps.web.dependencies as AvailableDependencies[],
|
|
231
|
+
devDependencies: apiDeps.web.devDependencies as AvailableDependencies[],
|
|
232
|
+
projectDir: webDir,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (nativeDirExists && apiDeps.native) {
|
|
237
|
+
await addPackageDependency({
|
|
238
|
+
dependencies: apiDeps.native.dependencies as AvailableDependencies[],
|
|
239
|
+
projectDir: nativeDir,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!isConvex) {
|
|
245
|
+
const queryDeps = getQueryDependencies(frontend);
|
|
246
|
+
|
|
247
|
+
if (webDirExists && queryDeps.web) {
|
|
248
|
+
await addPackageDependency({
|
|
249
|
+
dependencies: queryDeps.web.dependencies as AvailableDependencies[],
|
|
250
|
+
devDependencies: queryDeps.web.devDependencies as AvailableDependencies[],
|
|
251
|
+
projectDir: webDir,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (nativeDirExists && queryDeps.native) {
|
|
256
|
+
await addPackageDependency({
|
|
257
|
+
dependencies: queryDeps.native.dependencies as AvailableDependencies[],
|
|
258
|
+
projectDir: nativeDir,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (isConvex) {
|
|
264
|
+
const convexDeps = getConvexDependencies(frontend);
|
|
265
|
+
|
|
266
|
+
if (webDirExists) {
|
|
267
|
+
await addPackageDependency({
|
|
268
|
+
dependencies: convexDeps.web.dependencies as AvailableDependencies[],
|
|
269
|
+
projectDir: webDir,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (nativeDirExists) {
|
|
274
|
+
await addPackageDependency({
|
|
275
|
+
dependencies: convexDeps.native.dependencies as AvailableDependencies[],
|
|
276
|
+
projectDir: nativeDir,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|