create-better-t-stack 3.7.3-canary.8e4d5716 → 3.7.3-canary.98ba1e7a
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/dist/cli.d.ts +1 -0
- package/dist/cli.js +8 -0
- package/dist/index.d.ts +347 -0
- package/dist/index.js +4 -0
- package/dist/src-yXf02Wox.js +7077 -0
- package/package.json +11 -7
- package/src/cli.ts +0 -3
- package/src/constants.ts +0 -188
- package/src/helpers/addons/addons-setup.ts +0 -226
- package/src/helpers/addons/examples-setup.ts +0 -104
- package/src/helpers/addons/fumadocs-setup.ts +0 -103
- package/src/helpers/addons/ruler-setup.ts +0 -139
- package/src/helpers/addons/starlight-setup.ts +0 -51
- package/src/helpers/addons/tauri-setup.ts +0 -96
- package/src/helpers/addons/ultracite-setup.ts +0 -232
- package/src/helpers/addons/vite-pwa-setup.ts +0 -59
- package/src/helpers/core/add-addons.ts +0 -85
- package/src/helpers/core/add-deployment.ts +0 -102
- package/src/helpers/core/api-setup.ts +0 -280
- package/src/helpers/core/auth-setup.ts +0 -203
- package/src/helpers/core/backend-setup.ts +0 -73
- package/src/helpers/core/command-handlers.ts +0 -354
- package/src/helpers/core/convex-codegen.ts +0 -14
- package/src/helpers/core/create-project.ts +0 -133
- package/src/helpers/core/create-readme.ts +0 -687
- package/src/helpers/core/db-setup.ts +0 -184
- package/src/helpers/core/detect-project-config.ts +0 -41
- package/src/helpers/core/env-setup.ts +0 -449
- package/src/helpers/core/git.ts +0 -31
- package/src/helpers/core/install-dependencies.ts +0 -32
- package/src/helpers/core/payments-setup.ts +0 -48
- package/src/helpers/core/post-installation.ts +0 -383
- package/src/helpers/core/project-config.ts +0 -246
- package/src/helpers/core/runtime-setup.ts +0 -76
- package/src/helpers/core/template-manager.ts +0 -917
- package/src/helpers/core/workspace-setup.ts +0 -184
- package/src/helpers/database-providers/d1-setup.ts +0 -28
- package/src/helpers/database-providers/docker-compose-setup.ts +0 -50
- package/src/helpers/database-providers/mongodb-atlas-setup.ts +0 -186
- package/src/helpers/database-providers/neon-setup.ts +0 -243
- package/src/helpers/database-providers/planetscale-setup.ts +0 -78
- package/src/helpers/database-providers/prisma-postgres-setup.ts +0 -196
- package/src/helpers/database-providers/supabase-setup.ts +0 -218
- package/src/helpers/database-providers/turso-setup.ts +0 -309
- package/src/helpers/deployment/alchemy/alchemy-combined-setup.ts +0 -80
- package/src/helpers/deployment/alchemy/alchemy-next-setup.ts +0 -51
- package/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts +0 -104
- package/src/helpers/deployment/alchemy/alchemy-react-router-setup.ts +0 -32
- package/src/helpers/deployment/alchemy/alchemy-solid-setup.ts +0 -32
- package/src/helpers/deployment/alchemy/alchemy-svelte-setup.ts +0 -98
- package/src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts +0 -33
- package/src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts +0 -98
- package/src/helpers/deployment/alchemy/env-dts-setup.ts +0 -76
- package/src/helpers/deployment/alchemy/index.ts +0 -7
- package/src/helpers/deployment/server-deploy-setup.ts +0 -55
- package/src/helpers/deployment/web-deploy-setup.ts +0 -58
- package/src/index.ts +0 -253
- package/src/prompts/addons.ts +0 -178
- package/src/prompts/api.ts +0 -49
- package/src/prompts/auth.ts +0 -84
- package/src/prompts/backend.ts +0 -83
- package/src/prompts/config-prompts.ts +0 -138
- package/src/prompts/database-setup.ts +0 -112
- package/src/prompts/database.ts +0 -57
- package/src/prompts/examples.ts +0 -64
- package/src/prompts/frontend.ts +0 -118
- package/src/prompts/git.ts +0 -16
- package/src/prompts/install.ts +0 -16
- package/src/prompts/orm.ts +0 -53
- package/src/prompts/package-manager.ts +0 -32
- package/src/prompts/payments.ts +0 -50
- package/src/prompts/project-name.ts +0 -86
- package/src/prompts/runtime.ts +0 -47
- package/src/prompts/server-deploy.ts +0 -91
- package/src/prompts/web-deploy.ts +0 -107
- package/src/types.ts +0 -2
- package/src/utils/add-package-deps.ts +0 -57
- package/src/utils/analytics.ts +0 -39
- package/src/utils/better-auth-plugin-setup.ts +0 -71
- package/src/utils/biome-formatter.ts +0 -82
- package/src/utils/bts-config.ts +0 -122
- package/src/utils/command-exists.ts +0 -16
- package/src/utils/compatibility-rules.ts +0 -319
- package/src/utils/compatibility.ts +0 -11
- package/src/utils/config-processing.ts +0 -130
- package/src/utils/config-validation.ts +0 -470
- package/src/utils/display-config.ts +0 -96
- package/src/utils/docker-utils.ts +0 -70
- package/src/utils/errors.ts +0 -32
- package/src/utils/generate-reproducible-command.ts +0 -53
- package/src/utils/get-latest-cli-version.ts +0 -11
- package/src/utils/get-package-manager.ts +0 -13
- package/src/utils/open-url.ts +0 -25
- package/src/utils/package-runner.ts +0 -23
- package/src/utils/project-directory.ts +0 -102
- package/src/utils/project-name-validation.ts +0 -43
- package/src/utils/render-title.ts +0 -48
- package/src/utils/setup-catalogs.ts +0 -192
- package/src/utils/sponsors.ts +0 -101
- package/src/utils/telemetry.ts +0 -19
- package/src/utils/template-processor.ts +0 -64
- package/src/utils/templates.ts +0 -94
- package/src/utils/ts-morph.ts +0 -26
- package/src/validation.ts +0 -117
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { autocompleteMultiselect, isCancel, log, spinner } from "@clack/prompts";
|
|
3
|
-
import { execa } from "execa";
|
|
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
|
-
export async function setupRuler(config: ProjectConfig) {
|
|
11
|
-
const { packageManager, projectDir } = config;
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
log.info("Setting up Ruler...");
|
|
15
|
-
|
|
16
|
-
const rulerDir = path.join(projectDir, ".ruler");
|
|
17
|
-
|
|
18
|
-
if (!(await fs.pathExists(rulerDir))) {
|
|
19
|
-
log.error(
|
|
20
|
-
pc.red(
|
|
21
|
-
"Ruler template directory not found. Please ensure ruler addon is properly installed.",
|
|
22
|
-
),
|
|
23
|
-
);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const EDITORS = {
|
|
28
|
-
amp: { label: "AMP" },
|
|
29
|
-
copilot: { label: "GitHub Copilot" },
|
|
30
|
-
claude: { label: "Claude Code" },
|
|
31
|
-
codex: { label: "OpenAI Codex CLI" },
|
|
32
|
-
cursor: { label: "Cursor" },
|
|
33
|
-
windsurf: { label: "Windsurf" },
|
|
34
|
-
cline: { label: "Cline" },
|
|
35
|
-
aider: { label: "Aider" },
|
|
36
|
-
firebase: { label: "Firebase Studio" },
|
|
37
|
-
"gemini-cli": { label: "Gemini CLI" },
|
|
38
|
-
junie: { label: "Junie" },
|
|
39
|
-
kilocode: { label: "Kilo Code" },
|
|
40
|
-
opencode: { label: "OpenCode" },
|
|
41
|
-
crush: { label: "Crush" },
|
|
42
|
-
zed: { label: "Zed" },
|
|
43
|
-
qwen: { label: "Qwen" },
|
|
44
|
-
amazonqcli: { label: "Amazon Q CLI" },
|
|
45
|
-
augmentcode: { label: "AugmentCode" },
|
|
46
|
-
firebender: { label: "Firebender" },
|
|
47
|
-
goose: { label: "Goose" },
|
|
48
|
-
jules: { label: "Jules" },
|
|
49
|
-
kiro: { label: "Kiro" },
|
|
50
|
-
openhands: { label: "Open Hands" },
|
|
51
|
-
roo: { label: "RooCode" },
|
|
52
|
-
trae: { label: "Trae AI" },
|
|
53
|
-
warp: { label: "Warp" },
|
|
54
|
-
} as const;
|
|
55
|
-
|
|
56
|
-
const selectedEditors = await autocompleteMultiselect({
|
|
57
|
-
message: "Select AI assistants for Ruler",
|
|
58
|
-
options: Object.entries(EDITORS).map(([key, v]) => ({
|
|
59
|
-
value: key,
|
|
60
|
-
label: v.label,
|
|
61
|
-
})),
|
|
62
|
-
required: false,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (isCancel(selectedEditors)) return exitCancelled("Operation cancelled");
|
|
66
|
-
|
|
67
|
-
if (selectedEditors.length === 0) {
|
|
68
|
-
log.info("No AI assistants selected. To apply rules later, run:");
|
|
69
|
-
log.info(
|
|
70
|
-
pc.cyan(
|
|
71
|
-
`${getPackageExecutionCommand(packageManager, "@intellectronica/ruler@latest apply --local-only")}`,
|
|
72
|
-
),
|
|
73
|
-
);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const configFile = path.join(rulerDir, "ruler.toml");
|
|
78
|
-
const currentConfig = await fs.readFile(configFile, "utf-8");
|
|
79
|
-
|
|
80
|
-
let updatedConfig = currentConfig;
|
|
81
|
-
|
|
82
|
-
const defaultAgentsLine = `default_agents = [${selectedEditors.map((editor) => `"${editor}"`).join(", ")}]`;
|
|
83
|
-
updatedConfig = updatedConfig.replace(/default_agents = \[\]/, defaultAgentsLine);
|
|
84
|
-
|
|
85
|
-
await fs.writeFile(configFile, updatedConfig);
|
|
86
|
-
|
|
87
|
-
await addRulerScriptToPackageJson(projectDir, packageManager);
|
|
88
|
-
|
|
89
|
-
const s = spinner();
|
|
90
|
-
s.start("Applying rules with Ruler...");
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const rulerApplyCmd = getPackageExecutionCommand(
|
|
94
|
-
packageManager,
|
|
95
|
-
`@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`,
|
|
96
|
-
);
|
|
97
|
-
await execa(rulerApplyCmd, {
|
|
98
|
-
cwd: projectDir,
|
|
99
|
-
env: { CI: "true" },
|
|
100
|
-
shell: true,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
s.stop("Applied rules with Ruler");
|
|
104
|
-
} catch {
|
|
105
|
-
s.stop(pc.red("Failed to apply rules"));
|
|
106
|
-
}
|
|
107
|
-
} catch (error) {
|
|
108
|
-
log.error(pc.red("Failed to set up Ruler"));
|
|
109
|
-
if (error instanceof Error) {
|
|
110
|
-
console.error(pc.red(error.message));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function addRulerScriptToPackageJson(
|
|
116
|
-
projectDir: string,
|
|
117
|
-
packageManager: ProjectConfig["packageManager"],
|
|
118
|
-
) {
|
|
119
|
-
const rootPackageJsonPath = path.join(projectDir, "package.json");
|
|
120
|
-
|
|
121
|
-
if (!(await fs.pathExists(rootPackageJsonPath))) {
|
|
122
|
-
log.warn("Root package.json not found, skipping ruler:apply script addition");
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const packageJson = await fs.readJson(rootPackageJsonPath);
|
|
127
|
-
|
|
128
|
-
if (!packageJson.scripts) {
|
|
129
|
-
packageJson.scripts = {};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const rulerApplyCommand = getPackageExecutionCommand(
|
|
133
|
-
packageManager,
|
|
134
|
-
"@intellectronica/ruler@latest apply --local-only",
|
|
135
|
-
);
|
|
136
|
-
packageJson.scripts["ruler:apply"] = rulerApplyCommand;
|
|
137
|
-
|
|
138
|
-
await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 });
|
|
139
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { spinner } from "@clack/prompts";
|
|
3
|
-
import consola from "consola";
|
|
4
|
-
import { execa } from "execa";
|
|
5
|
-
import fs from "fs-extra";
|
|
6
|
-
import pc from "picocolors";
|
|
7
|
-
import type { ProjectConfig } from "../../types";
|
|
8
|
-
import { getPackageExecutionCommand } from "../../utils/package-runner";
|
|
9
|
-
|
|
10
|
-
export async function setupStarlight(config: ProjectConfig) {
|
|
11
|
-
const { packageManager, projectDir } = config;
|
|
12
|
-
const s = spinner();
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
s.start("Setting up Starlight docs...");
|
|
16
|
-
|
|
17
|
-
const starlightArgs = [
|
|
18
|
-
"docs",
|
|
19
|
-
"--template",
|
|
20
|
-
"starlight",
|
|
21
|
-
"--no-install",
|
|
22
|
-
"--add",
|
|
23
|
-
"tailwind",
|
|
24
|
-
"--no-git",
|
|
25
|
-
"--skip-houston",
|
|
26
|
-
];
|
|
27
|
-
const starlightArgsString = starlightArgs.join(" ");
|
|
28
|
-
|
|
29
|
-
const commandWithArgs = `create-astro@latest ${starlightArgsString}`;
|
|
30
|
-
|
|
31
|
-
const starlightInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
32
|
-
|
|
33
|
-
const appsDir = path.join(projectDir, "apps");
|
|
34
|
-
await fs.ensureDir(appsDir);
|
|
35
|
-
|
|
36
|
-
await execa(starlightInitCommand, {
|
|
37
|
-
cwd: appsDir,
|
|
38
|
-
env: {
|
|
39
|
-
CI: "true",
|
|
40
|
-
},
|
|
41
|
-
shell: true,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
s.stop("Starlight docs setup successfully!");
|
|
45
|
-
} catch (error) {
|
|
46
|
-
s.stop(pc.red("Failed to set up Starlight docs"));
|
|
47
|
-
if (error instanceof Error) {
|
|
48
|
-
consola.error(pc.red(error.message));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { spinner } from "@clack/prompts";
|
|
3
|
-
import { consola } from "consola";
|
|
4
|
-
import { execa } from "execa";
|
|
5
|
-
import fs from "fs-extra";
|
|
6
|
-
import pc from "picocolors";
|
|
7
|
-
import type { ProjectConfig } from "../../types";
|
|
8
|
-
import { addPackageDependency } from "../../utils/add-package-deps";
|
|
9
|
-
import { getPackageExecutionCommand } from "../../utils/package-runner";
|
|
10
|
-
|
|
11
|
-
export async function setupTauri(config: ProjectConfig) {
|
|
12
|
-
const { packageManager, frontend, projectDir } = config;
|
|
13
|
-
const s = spinner();
|
|
14
|
-
const clientPackageDir = path.join(projectDir, "apps/web");
|
|
15
|
-
|
|
16
|
-
if (!(await fs.pathExists(clientPackageDir))) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
s.start("Setting up Tauri desktop app support...");
|
|
22
|
-
|
|
23
|
-
await addPackageDependency({
|
|
24
|
-
devDependencies: ["@tauri-apps/cli"],
|
|
25
|
-
projectDir: clientPackageDir,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const clientPackageJsonPath = path.join(clientPackageDir, "package.json");
|
|
29
|
-
if (await fs.pathExists(clientPackageJsonPath)) {
|
|
30
|
-
const packageJson = await fs.readJson(clientPackageJsonPath);
|
|
31
|
-
|
|
32
|
-
packageJson.scripts = {
|
|
33
|
-
...packageJson.scripts,
|
|
34
|
-
tauri: "tauri",
|
|
35
|
-
"desktop:dev": "tauri dev",
|
|
36
|
-
"desktop:build": "tauri build",
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
await fs.writeJson(clientPackageJsonPath, packageJson, { spaces: 2 });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const _hasTanstackRouter = frontend.includes("tanstack-router");
|
|
43
|
-
const hasReactRouter = frontend.includes("react-router");
|
|
44
|
-
const hasNuxt = frontend.includes("nuxt");
|
|
45
|
-
const hasSvelte = frontend.includes("svelte");
|
|
46
|
-
const _hasSolid = frontend.includes("solid");
|
|
47
|
-
const hasNext = frontend.includes("next");
|
|
48
|
-
|
|
49
|
-
const devUrl =
|
|
50
|
-
hasReactRouter || hasSvelte
|
|
51
|
-
? "http://localhost:5173"
|
|
52
|
-
: hasNext
|
|
53
|
-
? "http://localhost:3001"
|
|
54
|
-
: "http://localhost:3001";
|
|
55
|
-
|
|
56
|
-
const frontendDist = hasNuxt
|
|
57
|
-
? "../.output/public"
|
|
58
|
-
: hasSvelte
|
|
59
|
-
? "../build"
|
|
60
|
-
: hasNext
|
|
61
|
-
? "../.next"
|
|
62
|
-
: hasReactRouter
|
|
63
|
-
? "../build/client"
|
|
64
|
-
: "../dist";
|
|
65
|
-
|
|
66
|
-
const tauriArgs = [
|
|
67
|
-
"init",
|
|
68
|
-
`--app-name=${path.basename(projectDir)}`,
|
|
69
|
-
`--window-title=${path.basename(projectDir)}`,
|
|
70
|
-
`--frontend-dist=${frontendDist}`,
|
|
71
|
-
`--dev-url=${devUrl}`,
|
|
72
|
-
`--before-dev-command="${packageManager} run dev"`,
|
|
73
|
-
`--before-build-command="${packageManager} run build"`,
|
|
74
|
-
];
|
|
75
|
-
const tauriArgsString = tauriArgs.join(" ");
|
|
76
|
-
|
|
77
|
-
const commandWithArgs = `@tauri-apps/cli@latest ${tauriArgsString}`;
|
|
78
|
-
|
|
79
|
-
const tauriInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
80
|
-
|
|
81
|
-
await execa(tauriInitCommand, {
|
|
82
|
-
cwd: clientPackageDir,
|
|
83
|
-
env: {
|
|
84
|
-
CI: "true",
|
|
85
|
-
},
|
|
86
|
-
shell: true,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
s.stop("Tauri desktop app support configured successfully!");
|
|
90
|
-
} catch (error) {
|
|
91
|
-
s.stop(pc.red("Failed to set up Tauri"));
|
|
92
|
-
if (error instanceof Error) {
|
|
93
|
-
consola.error(pc.red(error.message));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import { autocompleteMultiselect, group, log, multiselect, spinner } from "@clack/prompts";
|
|
2
|
-
import { execa } from "execa";
|
|
3
|
-
import pc from "picocolors";
|
|
4
|
-
import type { ProjectConfig } from "../../types";
|
|
5
|
-
import { addPackageDependency } from "../../utils/add-package-deps";
|
|
6
|
-
import { exitCancelled } from "../../utils/errors";
|
|
7
|
-
import { getPackageExecutionCommand } from "../../utils/package-runner";
|
|
8
|
-
import { setupBiome } from "./addons-setup";
|
|
9
|
-
|
|
10
|
-
type UltraciteEditor = "vscode" | "zed";
|
|
11
|
-
type UltraciteAgent =
|
|
12
|
-
| "vscode-copilot"
|
|
13
|
-
| "cursor"
|
|
14
|
-
| "windsurf"
|
|
15
|
-
| "zed"
|
|
16
|
-
| "claude"
|
|
17
|
-
| "codex"
|
|
18
|
-
| "kiro"
|
|
19
|
-
| "cline"
|
|
20
|
-
| "amp"
|
|
21
|
-
| "aider"
|
|
22
|
-
| "firebase-studio"
|
|
23
|
-
| "open-hands"
|
|
24
|
-
| "gemini-cli"
|
|
25
|
-
| "junie"
|
|
26
|
-
| "augmentcode"
|
|
27
|
-
| "kilo-code"
|
|
28
|
-
| "goose"
|
|
29
|
-
| "roo-code";
|
|
30
|
-
|
|
31
|
-
type UltraciteHook = "cursor" | "claude";
|
|
32
|
-
|
|
33
|
-
const EDITORS = {
|
|
34
|
-
vscode: {
|
|
35
|
-
label: "VSCode / Cursor / Windsurf",
|
|
36
|
-
},
|
|
37
|
-
zed: {
|
|
38
|
-
label: "Zed",
|
|
39
|
-
},
|
|
40
|
-
} as const;
|
|
41
|
-
|
|
42
|
-
const AGENTS = {
|
|
43
|
-
"vscode-copilot": {
|
|
44
|
-
label: "VS Code Copilot",
|
|
45
|
-
},
|
|
46
|
-
cursor: {
|
|
47
|
-
label: "Cursor",
|
|
48
|
-
},
|
|
49
|
-
windsurf: {
|
|
50
|
-
label: "Windsurf",
|
|
51
|
-
},
|
|
52
|
-
zed: {
|
|
53
|
-
label: "Zed",
|
|
54
|
-
},
|
|
55
|
-
claude: {
|
|
56
|
-
label: "Claude",
|
|
57
|
-
},
|
|
58
|
-
codex: {
|
|
59
|
-
label: "Codex",
|
|
60
|
-
},
|
|
61
|
-
kiro: {
|
|
62
|
-
label: "Kiro",
|
|
63
|
-
},
|
|
64
|
-
cline: {
|
|
65
|
-
label: "Cline",
|
|
66
|
-
},
|
|
67
|
-
amp: {
|
|
68
|
-
label: "Amp",
|
|
69
|
-
},
|
|
70
|
-
aider: {
|
|
71
|
-
label: "Aider",
|
|
72
|
-
},
|
|
73
|
-
"firebase-studio": {
|
|
74
|
-
label: "Firebase Studio",
|
|
75
|
-
},
|
|
76
|
-
"open-hands": {
|
|
77
|
-
label: "Open Hands",
|
|
78
|
-
},
|
|
79
|
-
"gemini-cli": {
|
|
80
|
-
label: "Gemini CLI",
|
|
81
|
-
},
|
|
82
|
-
junie: {
|
|
83
|
-
label: "Junie",
|
|
84
|
-
},
|
|
85
|
-
augmentcode: {
|
|
86
|
-
label: "AugmentCode",
|
|
87
|
-
},
|
|
88
|
-
"kilo-code": {
|
|
89
|
-
label: "Kilo Code",
|
|
90
|
-
},
|
|
91
|
-
goose: {
|
|
92
|
-
label: "Goose",
|
|
93
|
-
},
|
|
94
|
-
"roo-code": {
|
|
95
|
-
label: "Roo Code",
|
|
96
|
-
},
|
|
97
|
-
} as const;
|
|
98
|
-
|
|
99
|
-
const HOOKS = {
|
|
100
|
-
cursor: {
|
|
101
|
-
label: "Cursor",
|
|
102
|
-
},
|
|
103
|
-
claude: {
|
|
104
|
-
label: "Claude",
|
|
105
|
-
},
|
|
106
|
-
} as const;
|
|
107
|
-
|
|
108
|
-
function getFrameworksFromFrontend(frontend: string[]): string[] {
|
|
109
|
-
const frameworkMap: Record<string, string> = {
|
|
110
|
-
"tanstack-router": "react",
|
|
111
|
-
"react-router": "react",
|
|
112
|
-
"tanstack-start": "react",
|
|
113
|
-
next: "next",
|
|
114
|
-
nuxt: "vue",
|
|
115
|
-
"native-bare": "react",
|
|
116
|
-
"native-uniwind": "react",
|
|
117
|
-
"native-unistyles": "react",
|
|
118
|
-
svelte: "svelte",
|
|
119
|
-
solid: "solid",
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const frameworks = new Set<string>();
|
|
123
|
-
|
|
124
|
-
for (const f of frontend) {
|
|
125
|
-
if (f !== "none" && frameworkMap[f]) {
|
|
126
|
-
frameworks.add(frameworkMap[f]);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return Array.from(frameworks);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export async function setupUltracite(config: ProjectConfig, hasHusky: boolean) {
|
|
134
|
-
const { packageManager, projectDir, frontend } = config;
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
log.info("Setting up Ultracite...");
|
|
138
|
-
|
|
139
|
-
await setupBiome(projectDir);
|
|
140
|
-
|
|
141
|
-
const result = await group(
|
|
142
|
-
{
|
|
143
|
-
editors: () =>
|
|
144
|
-
multiselect<UltraciteEditor>({
|
|
145
|
-
message: "Choose editors",
|
|
146
|
-
options: Object.entries(EDITORS).map(([key, editor]) => ({
|
|
147
|
-
value: key as UltraciteEditor,
|
|
148
|
-
label: editor.label,
|
|
149
|
-
})),
|
|
150
|
-
required: true,
|
|
151
|
-
}),
|
|
152
|
-
agents: () =>
|
|
153
|
-
autocompleteMultiselect<UltraciteAgent>({
|
|
154
|
-
message: "Choose agents",
|
|
155
|
-
options: Object.entries(AGENTS).map(([key, agent]) => ({
|
|
156
|
-
value: key as UltraciteAgent,
|
|
157
|
-
label: agent.label,
|
|
158
|
-
})),
|
|
159
|
-
required: true,
|
|
160
|
-
}),
|
|
161
|
-
hooks: () =>
|
|
162
|
-
autocompleteMultiselect<UltraciteHook>({
|
|
163
|
-
message: "Choose hooks",
|
|
164
|
-
options: Object.entries(HOOKS).map(([key, hook]) => ({
|
|
165
|
-
value: key as UltraciteHook,
|
|
166
|
-
label: hook.label,
|
|
167
|
-
})),
|
|
168
|
-
}),
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
onCancel: () => {
|
|
172
|
-
exitCancelled("Operation cancelled");
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
const editors = result.editors as UltraciteEditor[];
|
|
178
|
-
const agents = result.agents as UltraciteAgent[];
|
|
179
|
-
const hooks = result.hooks as UltraciteHook[];
|
|
180
|
-
const frameworks = getFrameworksFromFrontend(frontend);
|
|
181
|
-
|
|
182
|
-
const ultraciteArgs = ["init", "--pm", packageManager];
|
|
183
|
-
|
|
184
|
-
if (frameworks.length > 0) {
|
|
185
|
-
ultraciteArgs.push("--frameworks", ...frameworks);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (editors.length > 0) {
|
|
189
|
-
ultraciteArgs.push("--editors", ...editors);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (agents.length > 0) {
|
|
193
|
-
ultraciteArgs.push("--agents", ...agents);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (hooks.length > 0) {
|
|
197
|
-
ultraciteArgs.push("--hooks", ...hooks);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (hasHusky) {
|
|
201
|
-
ultraciteArgs.push("--integrations", "husky", "lint-staged");
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const ultraciteArgsString = ultraciteArgs.join(" ");
|
|
205
|
-
const commandWithArgs = `ultracite@latest ${ultraciteArgsString} --skip-install`;
|
|
206
|
-
|
|
207
|
-
const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
|
|
208
|
-
|
|
209
|
-
const s = spinner();
|
|
210
|
-
s.start("Running Ultracite init command...");
|
|
211
|
-
|
|
212
|
-
await execa(ultraciteInitCommand, {
|
|
213
|
-
cwd: projectDir,
|
|
214
|
-
env: { CI: "true" },
|
|
215
|
-
shell: true,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
if (hasHusky) {
|
|
219
|
-
await addPackageDependency({
|
|
220
|
-
devDependencies: ["husky", "lint-staged"],
|
|
221
|
-
projectDir,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
s.stop("Ultracite setup successfully!");
|
|
226
|
-
} catch (error) {
|
|
227
|
-
log.error(pc.red("Failed to set up Ultracite"));
|
|
228
|
-
if (error instanceof Error) {
|
|
229
|
-
console.error(pc.red(error.message));
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { type CallExpression, Node, type ObjectLiteralExpression, SyntaxKind } from "ts-morph";
|
|
2
|
-
import { ensureArrayProperty, tsProject } from "../../utils/ts-morph";
|
|
3
|
-
|
|
4
|
-
export async function addPwaToViteConfig(viteConfigPath: string, projectName: string) {
|
|
5
|
-
const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
|
|
6
|
-
if (!sourceFile) {
|
|
7
|
-
throw new Error("vite config not found");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const hasImport = sourceFile
|
|
11
|
-
.getImportDeclarations()
|
|
12
|
-
.some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa");
|
|
13
|
-
|
|
14
|
-
if (!hasImport) {
|
|
15
|
-
sourceFile.insertImportDeclaration(0, {
|
|
16
|
-
namedImports: ["VitePWA"],
|
|
17
|
-
moduleSpecifier: "vite-plugin-pwa",
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
|
|
22
|
-
const expression = expr.getExpression();
|
|
23
|
-
return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
if (!defineCall) {
|
|
27
|
-
throw new Error("Could not find defineConfig call in vite config");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const callExpr = defineCall as CallExpression;
|
|
31
|
-
const configObject = callExpr.getArguments()[0] as ObjectLiteralExpression | undefined;
|
|
32
|
-
if (!configObject) {
|
|
33
|
-
throw new Error("defineConfig argument is not an object literal");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const pluginsArray = ensureArrayProperty(configObject, "plugins");
|
|
37
|
-
|
|
38
|
-
const alreadyPresent = pluginsArray
|
|
39
|
-
.getElements()
|
|
40
|
-
.some((el) => el.getText().startsWith("VitePWA("));
|
|
41
|
-
|
|
42
|
-
if (!alreadyPresent) {
|
|
43
|
-
pluginsArray.addElement(
|
|
44
|
-
`VitePWA({
|
|
45
|
-
registerType: "autoUpdate",
|
|
46
|
-
manifest: {
|
|
47
|
-
name: "${projectName}",
|
|
48
|
-
short_name: "${projectName}",
|
|
49
|
-
description: "${projectName} - PWA Application",
|
|
50
|
-
theme_color: "#0c0c0c",
|
|
51
|
-
},
|
|
52
|
-
pwaAssets: { disabled: false, config: true },
|
|
53
|
-
devOptions: { enabled: true },
|
|
54
|
-
})`,
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
await tsProject.save();
|
|
59
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
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
|
-
}
|