brewcli 1.0.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/.prettierignore +9 -0
- package/.prettierrc +25 -0
- package/LICENSE +21 -0
- package/Readme.md +157 -0
- package/dist/app.d.ts +8 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +188 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/ErrorDisplay.d.ts +7 -0
- package/dist/components/ErrorDisplay.d.ts.map +1 -0
- package/dist/components/ErrorDisplay.js +8 -0
- package/dist/components/ErrorDisplay.js.map +1 -0
- package/dist/components/NamePrompt.d.ts +7 -0
- package/dist/components/NamePrompt.d.ts.map +1 -0
- package/dist/components/NamePrompt.js +19 -0
- package/dist/components/NamePrompt.js.map +1 -0
- package/dist/components/SelectionList.d.ts +13 -0
- package/dist/components/SelectionList.d.ts.map +1 -0
- package/dist/components/SelectionList.js +9 -0
- package/dist/components/SelectionList.js.map +1 -0
- package/dist/components/Spinner.d.ts +7 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +9 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/SuccessBox.d.ts +8 -0
- package/dist/components/SuccessBox.d.ts.map +1 -0
- package/dist/components/SuccessBox.js +10 -0
- package/dist/components/SuccessBox.js.map +1 -0
- package/dist/components/Welcome.d.ts +4 -0
- package/dist/components/Welcome.d.ts.map +1 -0
- package/dist/components/Welcome.js +10 -0
- package/dist/components/Welcome.js.map +1 -0
- package/dist/templates/node/javascript/angular/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/.env.example +5 -0
- package/dist/templates/node/javascript/express/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/Readme.md +11 -0
- package/dist/templates/node/javascript/express/package.json +18 -0
- package/dist/templates/node/javascript/express/src/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/src/app.js +23 -0
- package/dist/templates/node/javascript/express/src/constants.js +1 -0
- package/dist/templates/node/javascript/express/src/controllers/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/src/db/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/src/db/index.js +14 -0
- package/dist/templates/node/javascript/express/src/index.js +17 -0
- package/dist/templates/node/javascript/express/src/middlewares/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/src/models/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/src/routes/.gitkeep +0 -0
- package/dist/templates/node/javascript/express/src/utils/.gitkeep +0 -0
- package/dist/templates/node/javascript/fastify/.env.example +5 -0
- package/dist/templates/node/javascript/fastify/.gitkeep +0 -0
- package/dist/templates/node/javascript/fastify/package.json +20 -0
- package/dist/templates/node/javascript/fastify/src/index.js +28 -0
- package/dist/templates/node/javascript/nestjs/.gitkeep +0 -0
- package/dist/templates/node/javascript/nextjs/.gitkeep +0 -0
- package/dist/templates/node/javascript/reactjs/.gitkeep +0 -0
- package/dist/templates/node/javascript/vuejs/.gitkeep +0 -0
- package/dist/templates/node/typescript/angular/.gitkeep +0 -0
- package/dist/templates/node/typescript/express/.env.example +5 -0
- package/dist/templates/node/typescript/express/.gitkeep +0 -0
- package/dist/templates/node/typescript/express/package.json +25 -0
- package/dist/templates/node/typescript/express/src/app.ts +17 -0
- package/dist/templates/node/typescript/express/src/constants.ts +1 -0
- package/dist/templates/node/typescript/express/src/db/index.ts +14 -0
- package/dist/templates/node/typescript/express/src/index.ts +17 -0
- package/dist/templates/node/typescript/express/tsconfig.json +15 -0
- package/dist/templates/node/typescript/fastify/.env.example +5 -0
- package/dist/templates/node/typescript/fastify/.gitkeep +0 -0
- package/dist/templates/node/typescript/fastify/package.json +20 -0
- package/dist/templates/node/typescript/fastify/tsconfig.json +15 -0
- package/dist/templates/node/typescript/nestjs/.gitkeep +0 -0
- package/dist/templates/node/typescript/nextjs/.gitkeep +0 -0
- package/dist/templates/node/typescript/reactjs/.gitkeep +0 -0
- package/dist/templates/node/typescript/vuejs/.gitkeep +0 -0
- package/package.json +66 -0
- package/src/app.tsx +274 -0
- package/src/cli.tsx +32 -0
- package/src/components/ErrorDisplay.tsx +21 -0
- package/src/components/NamePrompt.tsx +67 -0
- package/src/components/SelectionList.tsx +64 -0
- package/src/components/Spinner.tsx +24 -0
- package/src/components/SuccessBox.tsx +79 -0
- package/src/components/Welcome.tsx +30 -0
- package/src/templates/node/javascript/angular/.gitkeep +0 -0
- package/src/templates/node/javascript/express/.env.example +5 -0
- package/src/templates/node/javascript/express/.gitkeep +0 -0
- package/src/templates/node/javascript/express/Readme.md +11 -0
- package/src/templates/node/javascript/express/package.json +18 -0
- package/src/templates/node/javascript/express/src/.gitkeep +0 -0
- package/src/templates/node/javascript/express/src/app.js +23 -0
- package/src/templates/node/javascript/express/src/constants.js +1 -0
- package/src/templates/node/javascript/express/src/controllers/.gitkeep +0 -0
- package/src/templates/node/javascript/express/src/db/.gitkeep +0 -0
- package/src/templates/node/javascript/express/src/db/index.js +14 -0
- package/src/templates/node/javascript/express/src/index.js +17 -0
- package/src/templates/node/javascript/express/src/middlewares/.gitkeep +0 -0
- package/src/templates/node/javascript/express/src/models/.gitkeep +0 -0
- package/src/templates/node/javascript/express/src/routes/.gitkeep +0 -0
- package/src/templates/node/javascript/express/src/utils/.gitkeep +0 -0
- package/src/templates/node/javascript/fastify/.env.example +5 -0
- package/src/templates/node/javascript/fastify/.gitkeep +0 -0
- package/src/templates/node/javascript/fastify/package.json +20 -0
- package/src/templates/node/javascript/fastify/src/index.js +28 -0
- package/src/templates/node/javascript/nestjs/.gitkeep +0 -0
- package/src/templates/node/javascript/nextjs/.gitkeep +0 -0
- package/src/templates/node/javascript/reactjs/.gitkeep +0 -0
- package/src/templates/node/javascript/vuejs/.gitkeep +0 -0
- package/src/templates/node/typescript/angular/.gitkeep +0 -0
- package/src/templates/node/typescript/express/.env.example +5 -0
- package/src/templates/node/typescript/express/.gitkeep +0 -0
- package/src/templates/node/typescript/express/package.json +25 -0
- package/src/templates/node/typescript/express/src/app.ts +17 -0
- package/src/templates/node/typescript/express/src/constants.ts +1 -0
- package/src/templates/node/typescript/express/src/db/index.ts +14 -0
- package/src/templates/node/typescript/express/src/index.ts +17 -0
- package/src/templates/node/typescript/express/tsconfig.json +15 -0
- package/src/templates/node/typescript/fastify/.env.example +5 -0
- package/src/templates/node/typescript/fastify/.gitkeep +0 -0
- package/src/templates/node/typescript/fastify/package.json +20 -0
- package/src/templates/node/typescript/fastify/tsconfig.json +15 -0
- package/src/templates/node/typescript/nestjs/.gitkeep +0 -0
- package/src/templates/node/typescript/nextjs/.gitkeep +0 -0
- package/src/templates/node/typescript/reactjs/.gitkeep +0 -0
- package/src/templates/node/typescript/vuejs/.gitkeep +0 -0
- package/tsconfig.json +46 -0
package/src/app.tsx
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Box, useApp, Text } from "ink";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import Welcome from "./components/Welcome.js";
|
|
7
|
+
import NamePrompt from "./components/NamePrompt.js";
|
|
8
|
+
import Spinner from "./components/Spinner.js";
|
|
9
|
+
import SuccessBox from "./components/SuccessBox.js";
|
|
10
|
+
import ErrorDisplay from "./components/ErrorDisplay.js";
|
|
11
|
+
import SelectionList from "./components/SelectionList.js";
|
|
12
|
+
import type { SelectionItem } from "./components/SelectionList.js";
|
|
13
|
+
|
|
14
|
+
interface AppProps {
|
|
15
|
+
initialProjectName?: string;
|
|
16
|
+
templatePath: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type Phase =
|
|
20
|
+
| "PLATFORM"
|
|
21
|
+
| "LANGUAGE"
|
|
22
|
+
| "FRAMEWORK"
|
|
23
|
+
| "NAME"
|
|
24
|
+
| "COMING_SOON"
|
|
25
|
+
| "BREWING"
|
|
26
|
+
| "SUCCESS"
|
|
27
|
+
| "ERROR";
|
|
28
|
+
|
|
29
|
+
const App: React.FC<AppProps> = ({ initialProjectName, templatePath }) => {
|
|
30
|
+
const { exit } = useApp();
|
|
31
|
+
const [projectName, setProjectName] = useState<string | undefined>(
|
|
32
|
+
initialProjectName,
|
|
33
|
+
);
|
|
34
|
+
const [phase, setPhase] = useState<Phase>("PLATFORM");
|
|
35
|
+
|
|
36
|
+
const [platform, setPlatform] = useState<string>("");
|
|
37
|
+
const [language, setLanguage] = useState<string>("");
|
|
38
|
+
const [framework, setFramework] = useState<string>("");
|
|
39
|
+
|
|
40
|
+
const [statusMessage, setStatusMessage] = useState("Brewing your project...");
|
|
41
|
+
const [errorMessage, setErrorMessage] = useState("");
|
|
42
|
+
const [targetPath, setTargetPath] = useState("");
|
|
43
|
+
|
|
44
|
+
const platformItems = [
|
|
45
|
+
{ label: "Node.js", value: "node" },
|
|
46
|
+
{ label: "Other Framework", value: "other" },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const languageItems = [
|
|
50
|
+
{ label: "JavaScript", value: "javascript" },
|
|
51
|
+
{ label: "TypeScript", value: "typescript" },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const frameworkItems = [
|
|
55
|
+
{ label: "Express", value: "express" },
|
|
56
|
+
{ label: "Fastify", value: "fastify" },
|
|
57
|
+
{ label: "NestJS", value: "nestjs" },
|
|
58
|
+
{ label: "ReactJS", value: "reactjs" },
|
|
59
|
+
...(language === "typescript"
|
|
60
|
+
? [{ label: "React Native CLI", value: "reactNativeCli" }]
|
|
61
|
+
: []),
|
|
62
|
+
{ label: "React Native Expo", value: "reactNativeExpo" },
|
|
63
|
+
{ label: "Angular", value: "angular" },
|
|
64
|
+
{ label: "Vuejs", value: "vuejs" },
|
|
65
|
+
{ label: "NextJS", value: "nextjs" },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const handlePlatformSelect = (item: SelectionItem) => {
|
|
69
|
+
if (item.value === "other") {
|
|
70
|
+
setPhase("COMING_SOON");
|
|
71
|
+
} else {
|
|
72
|
+
setPlatform(item.value);
|
|
73
|
+
setPhase("LANGUAGE");
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const handleLanguageSelect = (item: SelectionItem) => {
|
|
78
|
+
setLanguage(item.value);
|
|
79
|
+
setPhase("FRAMEWORK");
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleFrameworkSelect = (item: SelectionItem) => {
|
|
83
|
+
setFramework(item.value);
|
|
84
|
+
if (projectName) {
|
|
85
|
+
setPhase("BREWING");
|
|
86
|
+
} else {
|
|
87
|
+
setPhase("NAME");
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const startBrewing = async (name: string) => {
|
|
92
|
+
setProjectName(name);
|
|
93
|
+
setPhase("BREWING");
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (
|
|
98
|
+
phase === "BREWING" &&
|
|
99
|
+
projectName &&
|
|
100
|
+
platform &&
|
|
101
|
+
language &&
|
|
102
|
+
framework
|
|
103
|
+
) {
|
|
104
|
+
brew();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (phase === "SUCCESS") exit();
|
|
108
|
+
}, [phase]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (phase === "COMING_SOON") {
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
exit();
|
|
114
|
+
}, 3000);
|
|
115
|
+
}
|
|
116
|
+
}, [phase]);
|
|
117
|
+
|
|
118
|
+
const brew = async () => {
|
|
119
|
+
if (!projectName || !platform || !language || !framework) return;
|
|
120
|
+
|
|
121
|
+
const target = path.join(process.cwd(), projectName);
|
|
122
|
+
setTargetPath(target);
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
if (fs.existsSync(target)) {
|
|
126
|
+
throw new Error(`Folder "${projectName}" already exists!`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// --- Scaffolding Logic ---
|
|
130
|
+
const isTS = language === "typescript";
|
|
131
|
+
|
|
132
|
+
// Logic based on framework type
|
|
133
|
+
if (framework === "express" || framework === "fastify") {
|
|
134
|
+
// TEMPLATE-BASED SCAFFOLDING
|
|
135
|
+
setStatusMessage(`Brewing ${framework} app (${language})...`);
|
|
136
|
+
const finalTemplatePath = path.join(
|
|
137
|
+
templatePath,
|
|
138
|
+
platform,
|
|
139
|
+
language,
|
|
140
|
+
framework,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
await fs.ensureDir(target);
|
|
144
|
+
if (fs.existsSync(finalTemplatePath)) {
|
|
145
|
+
await fs.copy(finalTemplatePath, target);
|
|
146
|
+
} else {
|
|
147
|
+
// Minimal fallback
|
|
148
|
+
await fs.ensureDir(path.join(target, "src"));
|
|
149
|
+
await fs.writeJson(path.join(target, "package.json"), {
|
|
150
|
+
name: projectName,
|
|
151
|
+
version: "1.0.0",
|
|
152
|
+
type: "module",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// COMMAND-BASED SCAFFOLDING
|
|
157
|
+
setStatusMessage(
|
|
158
|
+
`Invoking official ${framework} cli (might take a minute)...`,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
let cmd = "";
|
|
162
|
+
switch (framework) {
|
|
163
|
+
case "nextjs":
|
|
164
|
+
cmd = `npx create-next-app@latest ${projectName} --${language} --eslint --tailwind --app --src-dir --import-alias "@/*" --use-npm --skip-install --yes`;
|
|
165
|
+
break;
|
|
166
|
+
case "reactjs":
|
|
167
|
+
cmd = `npm create vite@latest ${projectName} -- --template react${isTS ? "-ts" : ""} --no-interactive`;
|
|
168
|
+
break;
|
|
169
|
+
case "reactNativeCli":
|
|
170
|
+
cmd = `npx @react-native-community/cli@latest init ${projectName} --skip-install --install-pods false`;
|
|
171
|
+
break;
|
|
172
|
+
case "reactNativeExpo":
|
|
173
|
+
cmd = `npx create-expo-app@latest ${projectName} --template ${isTS ? "blank-typescript" : "blank"} --no-install`;
|
|
174
|
+
break;
|
|
175
|
+
case "vuejs":
|
|
176
|
+
cmd = `npm create vite@latest ${projectName} -- --template vue${isTS ? "-ts" : ""} --no-interactive`;
|
|
177
|
+
break;
|
|
178
|
+
case "angular":
|
|
179
|
+
cmd = `npx -y @angular/cli@latest new ${projectName} --defaults --skip-install --skip-git`;
|
|
180
|
+
break;
|
|
181
|
+
case "nestjs":
|
|
182
|
+
cmd = `npx -y @nestjs/cli@latest new ${projectName} --package-manager npm --language ${isTS ? "TS" : "JS"} --skip-install --skip-git`;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (cmd) {
|
|
187
|
+
try {
|
|
188
|
+
execSync(cmd, { stdio: ["ignore", "ignore", "pipe"] });
|
|
189
|
+
} catch (err: any) {
|
|
190
|
+
const stderr = err.stderr?.toString().trim();
|
|
191
|
+
throw new Error(
|
|
192
|
+
stderr
|
|
193
|
+
? `${framework} scaffolder failed:\n${stderr}`
|
|
194
|
+
: `${framework} scaffolder failed (exit ${err.status ?? "?"})`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!fs.existsSync(target)) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`${framework} scaffolder reported success but did not create "${projectName}".`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// --- Final Post-Scaffolding Customization ---
|
|
207
|
+
setStatusMessage("Finishing touches...");
|
|
208
|
+
|
|
209
|
+
const pkgPath = path.join(target, "package.json");
|
|
210
|
+
if (fs.existsSync(pkgPath)) {
|
|
211
|
+
const pkg = await fs.readJson(pkgPath);
|
|
212
|
+
pkg.name = projectName;
|
|
213
|
+
pkg.description = `Project scaffolded using brew-cli (${framework} / ${language})`;
|
|
214
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Standardize Git
|
|
218
|
+
try {
|
|
219
|
+
// Sometimes tools create it even if we ask not to, or don't.
|
|
220
|
+
// We ensure it's there and freshly initialized if it's missing.
|
|
221
|
+
if (!fs.existsSync(path.join(target, ".git"))) {
|
|
222
|
+
execSync("git init", { cwd: target, stdio: "ignore" });
|
|
223
|
+
}
|
|
224
|
+
} catch (e) {}
|
|
225
|
+
|
|
226
|
+
setPhase("SUCCESS");
|
|
227
|
+
} catch (error: any) {
|
|
228
|
+
setErrorMessage(error.message || "An unknown error occurred");
|
|
229
|
+
setPhase("ERROR");
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<Box flexDirection="column" padding={1}>
|
|
235
|
+
<Welcome />
|
|
236
|
+
{phase === "PLATFORM" && (
|
|
237
|
+
<SelectionList
|
|
238
|
+
title="Choose your platform:"
|
|
239
|
+
items={platformItems}
|
|
240
|
+
onSelect={handlePlatformSelect}
|
|
241
|
+
/>
|
|
242
|
+
)}
|
|
243
|
+
{phase === "LANGUAGE" && (
|
|
244
|
+
<SelectionList
|
|
245
|
+
title={`Select language for ${platform}:`}
|
|
246
|
+
items={languageItems}
|
|
247
|
+
onSelect={handleLanguageSelect}
|
|
248
|
+
/>
|
|
249
|
+
)}
|
|
250
|
+
{phase === "FRAMEWORK" && (
|
|
251
|
+
<SelectionList
|
|
252
|
+
title={`Which framework or library do you want to use?`}
|
|
253
|
+
items={frameworkItems}
|
|
254
|
+
onSelect={handleFrameworkSelect}
|
|
255
|
+
/>
|
|
256
|
+
)}
|
|
257
|
+
{phase === "NAME" && <NamePrompt onSubmit={startBrewing} />}
|
|
258
|
+
{phase === "COMING_SOON" && (
|
|
259
|
+
<Box marginTop={1}>
|
|
260
|
+
<Text color="yellow" bold>
|
|
261
|
+
More technologies will be available in the near future!
|
|
262
|
+
</Text>
|
|
263
|
+
</Box>
|
|
264
|
+
)}
|
|
265
|
+
{phase === "BREWING" && <Spinner label={statusMessage} />}
|
|
266
|
+
{phase === "SUCCESS" && projectName && (
|
|
267
|
+
<SuccessBox projectName={projectName} targetPath={targetPath} />
|
|
268
|
+
)}
|
|
269
|
+
{phase === "ERROR" && <ErrorDisplay message={errorMessage} />}
|
|
270
|
+
</Box>
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export default App;
|
package/src/cli.tsx
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { render } from "ink";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import App from "./app.js";
|
|
8
|
+
|
|
9
|
+
// --- ESM __dirname Fix ---
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name("create-brew-app")
|
|
17
|
+
.description("Brew a fresh Node.js backend with Express and MongoDB")
|
|
18
|
+
.version("1.1.0")
|
|
19
|
+
.argument("[project-name]", "Name of the project")
|
|
20
|
+
.action((projectName) => {
|
|
21
|
+
const templatePath = path.join(__dirname, "templates");
|
|
22
|
+
|
|
23
|
+
// We don't clear the screen anymore as Ink handles its own rendering
|
|
24
|
+
// but if the user really wants it we could do it before render.
|
|
25
|
+
// console.clear();
|
|
26
|
+
|
|
27
|
+
render(
|
|
28
|
+
<App initialProjectName={projectName} templatePath={templatePath} />,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Box } from "ink";
|
|
3
|
+
|
|
4
|
+
interface ErrorDisplayProps {
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const ErrorDisplay: React.FC<ErrorDisplayProps> = ({ message }) => {
|
|
9
|
+
return (
|
|
10
|
+
<Box marginTop={1}>
|
|
11
|
+
<Text color="red" bold>
|
|
12
|
+
The brew spilled (Error)!
|
|
13
|
+
</Text>
|
|
14
|
+
<Box marginLeft={1}>
|
|
15
|
+
<Text color="red">{message}</Text>
|
|
16
|
+
</Box>
|
|
17
|
+
</Box>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default ErrorDisplay;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Box } from "ink";
|
|
3
|
+
import TextInput from "ink-text-input";
|
|
4
|
+
|
|
5
|
+
interface NamePromptProps {
|
|
6
|
+
onSubmit: (name: string) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const NamePrompt: React.FC<NamePromptProps> = ({ onSubmit }) => {
|
|
10
|
+
const [name, setName] = React.useState("");
|
|
11
|
+
const [error, setError] = React.useState<string | null>(null);
|
|
12
|
+
|
|
13
|
+
const handleSubmit = (value: string) => {
|
|
14
|
+
if (/^([a-z\-\_\d])+$/.test(value)) {
|
|
15
|
+
onSubmit(value);
|
|
16
|
+
} else {
|
|
17
|
+
setError("Project name may only include letters, numbers, and dashes.");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Box flexDirection="column" marginTop={1} marginLeft={2}>
|
|
23
|
+
<Box marginBottom={1}>
|
|
24
|
+
<Text bold color="yellow">
|
|
25
|
+
➜{" "}
|
|
26
|
+
</Text>
|
|
27
|
+
<Text bold color="white">
|
|
28
|
+
What is your project name?
|
|
29
|
+
</Text>
|
|
30
|
+
</Box>
|
|
31
|
+
|
|
32
|
+
<Box
|
|
33
|
+
borderStyle="round"
|
|
34
|
+
borderColor="cyan"
|
|
35
|
+
paddingLeft={1}
|
|
36
|
+
paddingRight={2}
|
|
37
|
+
width={40}
|
|
38
|
+
>
|
|
39
|
+
<Text color="yellow" bold>
|
|
40
|
+
»{" "}
|
|
41
|
+
</Text>
|
|
42
|
+
<TextInput
|
|
43
|
+
value={name}
|
|
44
|
+
onChange={setName}
|
|
45
|
+
onSubmit={handleSubmit}
|
|
46
|
+
placeholder="my-cool-project"
|
|
47
|
+
/>
|
|
48
|
+
</Box>
|
|
49
|
+
|
|
50
|
+
{error && (
|
|
51
|
+
<Box marginTop={1}>
|
|
52
|
+
<Text color="red" bold>
|
|
53
|
+
✖ {error}
|
|
54
|
+
</Text>
|
|
55
|
+
</Box>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
<Box marginTop={1}>
|
|
59
|
+
<Text dimColor italic>
|
|
60
|
+
Enter your project identifier and press Enter
|
|
61
|
+
</Text>
|
|
62
|
+
</Box>
|
|
63
|
+
</Box>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default NamePrompt;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import SelectInput from "ink-select-input";
|
|
4
|
+
|
|
5
|
+
export interface SelectionItem {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface SelectionListProps {
|
|
11
|
+
title: string;
|
|
12
|
+
items: SelectionItem[];
|
|
13
|
+
onSelect: (item: SelectionItem) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SelectionList: React.FC<SelectionListProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
items,
|
|
19
|
+
onSelect,
|
|
20
|
+
}) => {
|
|
21
|
+
return (
|
|
22
|
+
<Box flexDirection="column" marginTop={1} marginLeft={2}>
|
|
23
|
+
<Box marginBottom={1}>
|
|
24
|
+
<Text bold color="yellow">
|
|
25
|
+
➜{" "}
|
|
26
|
+
</Text>
|
|
27
|
+
<Text bold color="white">
|
|
28
|
+
{`${title}`}
|
|
29
|
+
</Text>
|
|
30
|
+
</Box>
|
|
31
|
+
|
|
32
|
+
<Box
|
|
33
|
+
borderStyle="round"
|
|
34
|
+
borderColor="cyan"
|
|
35
|
+
paddingLeft={1}
|
|
36
|
+
paddingRight={2}
|
|
37
|
+
flexDirection="column"
|
|
38
|
+
>
|
|
39
|
+
<SelectInput
|
|
40
|
+
items={items}
|
|
41
|
+
onSelect={onSelect}
|
|
42
|
+
indicatorComponent={({ isSelected }) => (
|
|
43
|
+
<Box marginRight={1}>
|
|
44
|
+
<Text color="blue">{isSelected ? "❯" : " "}</Text>
|
|
45
|
+
</Box>
|
|
46
|
+
)}
|
|
47
|
+
itemComponent={({ isSelected, label }) => (
|
|
48
|
+
<Text color={isSelected ? "blue" : "white"} bold={!!isSelected}>
|
|
49
|
+
{label}
|
|
50
|
+
</Text>
|
|
51
|
+
)}
|
|
52
|
+
/>
|
|
53
|
+
</Box>
|
|
54
|
+
|
|
55
|
+
<Box marginTop={1}>
|
|
56
|
+
<Text dimColor italic>
|
|
57
|
+
Use arrow keys to navigate · Enter to select
|
|
58
|
+
</Text>
|
|
59
|
+
</Box>
|
|
60
|
+
</Box>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default SelectionList;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Box } from "ink";
|
|
3
|
+
import SpinnerLib from "ink-spinner";
|
|
4
|
+
|
|
5
|
+
interface SpinnerProps {
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const Spinner: React.FC<SpinnerProps> = ({ label }) => {
|
|
10
|
+
return (
|
|
11
|
+
<Box marginTop={1} marginLeft={2}>
|
|
12
|
+
<Box marginRight={1}>
|
|
13
|
+
<Text color="yellow">
|
|
14
|
+
<SpinnerLib type="dots" />
|
|
15
|
+
</Text>
|
|
16
|
+
</Box>
|
|
17
|
+
<Text bold color="white">
|
|
18
|
+
{label}
|
|
19
|
+
</Text>
|
|
20
|
+
</Box>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default Spinner;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Box } from "ink";
|
|
3
|
+
import Gradient from "ink-gradient";
|
|
4
|
+
import BigText from "ink-big-text";
|
|
5
|
+
|
|
6
|
+
interface SuccessBoxProps {
|
|
7
|
+
projectName: string;
|
|
8
|
+
targetPath: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const SuccessBox: React.FC<SuccessBoxProps> = ({ projectName, targetPath }) => {
|
|
12
|
+
return (
|
|
13
|
+
<Box flexDirection="column" marginLeft={2}>
|
|
14
|
+
<Gradient colors={["#f7971e", "#ffd200", "#00c6ff", "#0072ff"]}>
|
|
15
|
+
<BigText text="CHEERS" font="block" />
|
|
16
|
+
</Gradient>
|
|
17
|
+
|
|
18
|
+
<Text color="green" bold>
|
|
19
|
+
✨ Brewing complete! Your project is served hot.
|
|
20
|
+
</Text>
|
|
21
|
+
|
|
22
|
+
<Box
|
|
23
|
+
borderStyle="double"
|
|
24
|
+
borderColor="yellow"
|
|
25
|
+
padding={1}
|
|
26
|
+
flexDirection="column"
|
|
27
|
+
marginTop={1}
|
|
28
|
+
width={60}
|
|
29
|
+
>
|
|
30
|
+
<Box justifyContent="center" marginBottom={1}>
|
|
31
|
+
<Text bold color="cyan">
|
|
32
|
+
🍺 create-brew-app
|
|
33
|
+
</Text>
|
|
34
|
+
</Box>
|
|
35
|
+
|
|
36
|
+
<Box marginBottom={1}>
|
|
37
|
+
<Text bold>Project: </Text>
|
|
38
|
+
<Text color="yellow">{projectName}</Text>
|
|
39
|
+
</Box>
|
|
40
|
+
|
|
41
|
+
<Box marginBottom={1}>
|
|
42
|
+
<Text bold>Path: </Text>
|
|
43
|
+
<Text color="blue">{targetPath}</Text>
|
|
44
|
+
</Box>
|
|
45
|
+
|
|
46
|
+
<Box flexDirection="column" marginTop={1}>
|
|
47
|
+
<Text bold color="magenta">
|
|
48
|
+
🚀 NEXT STEPS:
|
|
49
|
+
</Text>
|
|
50
|
+
<Box marginTop={1} flexDirection="column">
|
|
51
|
+
<Box>
|
|
52
|
+
<Text color="cyan"> 1. </Text>
|
|
53
|
+
<Text>cd </Text>
|
|
54
|
+
<Text color="yellow" bold>
|
|
55
|
+
{projectName}
|
|
56
|
+
</Text>
|
|
57
|
+
</Box>
|
|
58
|
+
<Box>
|
|
59
|
+
<Text color="cyan"> 2. </Text>
|
|
60
|
+
<Text color="white">npm install</Text>
|
|
61
|
+
</Box>
|
|
62
|
+
<Box>
|
|
63
|
+
<Text color="cyan"> 3. </Text>
|
|
64
|
+
<Text color="white">npm run dev</Text>
|
|
65
|
+
</Box>
|
|
66
|
+
</Box>
|
|
67
|
+
</Box>
|
|
68
|
+
|
|
69
|
+
<Box marginTop={2} justifyContent="center">
|
|
70
|
+
<Text italic dimColor>
|
|
71
|
+
Happy Coding! May your brew never turn cold.
|
|
72
|
+
</Text>
|
|
73
|
+
</Box>
|
|
74
|
+
</Box>
|
|
75
|
+
</Box>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default SuccessBox;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text, Box } from "ink";
|
|
3
|
+
import Gradient from "ink-gradient";
|
|
4
|
+
import BigText from "ink-big-text";
|
|
5
|
+
|
|
6
|
+
const Welcome: React.FC = () => {
|
|
7
|
+
return (
|
|
8
|
+
<Box flexDirection="column" padding={1}>
|
|
9
|
+
<Box>
|
|
10
|
+
<Gradient colors={["#f7971e", "#ffd200", "#00c6ff", "#0072ff"]}>
|
|
11
|
+
<BigText text="BREW CLI" font="block" />
|
|
12
|
+
</Gradient>
|
|
13
|
+
</Box>
|
|
14
|
+
|
|
15
|
+
<Box>
|
|
16
|
+
<Text color="cyanBright" italic>
|
|
17
|
+
⚡ The Ultimate Project Scaffolder for 2026
|
|
18
|
+
</Text>
|
|
19
|
+
</Box>
|
|
20
|
+
|
|
21
|
+
<Box marginY={1}>
|
|
22
|
+
<Text dimColor>
|
|
23
|
+
─────────────────────────────────────────────────────────────
|
|
24
|
+
</Text>
|
|
25
|
+
</Box>
|
|
26
|
+
</Box>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default Welcome;
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# 🚀 Brewed Backend
|
|
2
|
+
|
|
3
|
+
Welcome to your fresh Node.js backend! This project was scaffolded using **Brew CLI**. You have a modular, ESM-ready structure waiting for your logic.
|
|
4
|
+
|
|
5
|
+
## 🛠️ Getting Started
|
|
6
|
+
|
|
7
|
+
### 1. Install Dependencies
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "temp-name",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node src/index.js",
|
|
8
|
+
"dev": "nodemon src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"express": "^4.18.2",
|
|
12
|
+
"mongoose": "^8.0.0",
|
|
13
|
+
"dotenv": "^16.3.1"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"nodemon": "^3.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import cookieParser from "cookie-parser";
|
|
4
|
+
|
|
5
|
+
const app = express();
|
|
6
|
+
|
|
7
|
+
app.use(cors({
|
|
8
|
+
origin: process.env.CORS_ORIGIN,
|
|
9
|
+
credentials: true
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
app.use(express.json({limit: "16kb"}));
|
|
13
|
+
app.use(express.urlencoded({extended: true, limit: "16kb"}));
|
|
14
|
+
app.use(express.static("public"));
|
|
15
|
+
app.use(cookieParser());
|
|
16
|
+
|
|
17
|
+
// Import routes
|
|
18
|
+
// import userRouter from './routes/user.routes.js'
|
|
19
|
+
|
|
20
|
+
// Routes declaration
|
|
21
|
+
// app.use("/api/v1/users", userRouter)
|
|
22
|
+
|
|
23
|
+
export { app };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DB_NAME = "brewdb";
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
import { DB_NAME } from "../constants.js";
|
|
3
|
+
|
|
4
|
+
const connectDB = async () => {
|
|
5
|
+
try {
|
|
6
|
+
const connectionInstance = await mongoose.connect(`${process.env.MONGODB_URI}/${DB_NAME}`);
|
|
7
|
+
console.log(`\n MongoDB connected !! DB HOST: ${connectionInstance.connection.host}`);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.log("MONGODB connection FAILED ", error);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default connectDB;
|