@rellcodes16/devkit 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/README.md +128 -0
- package/bin/devkit.js +55 -0
- package/package.json +42 -0
- package/src/commands/createNode.js +219 -0
- package/src/commands/createReact.js +124 -0
- package/src/templates/node/index.js +666 -0
- package/src/templates/react/index.js +172 -0
- package/src/utils.js +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# devkit
|
|
2
|
+
|
|
3
|
+
Personal project scaffolding CLI. Spin up a full Node.js or React + Vite project with your standard stack in one command.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/rellcodes16/devkit.git
|
|
9
|
+
cd devkit
|
|
10
|
+
bash install.sh
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or manually:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm link
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Node.js + Express
|
|
24
|
+
devkit node <appname>
|
|
25
|
+
|
|
26
|
+
# React + Vite + Tailwind
|
|
27
|
+
devkit react <appname>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Options
|
|
31
|
+
|
|
32
|
+
### `devkit node <appname>`
|
|
33
|
+
|
|
34
|
+
| Flag | Description |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `--mongo` | Add Mongoose (MongoDB) setup |
|
|
37
|
+
| `--prisma` | Add Prisma ORM + schema setup |
|
|
38
|
+
| `--turso` | Add Turso (LibSQL) client |
|
|
39
|
+
| `--pg` | Add postgres.js (PostgreSQL) setup |
|
|
40
|
+
| `--supabase` | Add Supabase JS client |
|
|
41
|
+
| `--drizzle` | Add Drizzle ORM (must be paired with `--turso` or `--pg`) |
|
|
42
|
+
| `--redis` | Add Redis (ioredis) setup |
|
|
43
|
+
| `--cloudinary` | Add Cloudinary + Multer setup |
|
|
44
|
+
| `--socket` | Add Socket.IO |
|
|
45
|
+
| `--github` | Create and push to a GitHub repo (requires `gh` CLI installed + logged in) |
|
|
46
|
+
| `--no-git` | Skip git init |
|
|
47
|
+
|
|
48
|
+
> Only one primary database flag (`--mongo`, `--prisma`, `--turso`, `--pg`, `--supabase`) can be used at a time. `--redis` can be paired with any of them.
|
|
49
|
+
|
|
50
|
+
### `devkit react <appname>`
|
|
51
|
+
|
|
52
|
+
| Flag | Description |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `--router` | Add React Router DOM |
|
|
55
|
+
| `--axios` | Add Axios |
|
|
56
|
+
| `--github` | Create and push to a GitHub repo (requires `gh` CLI installed + logged in) |
|
|
57
|
+
| `--no-git` | Skip git init |
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
devkit node my-api # Express + standard middleware, no DB
|
|
63
|
+
devkit node my-api --mongo # + Mongoose (MongoDB)
|
|
64
|
+
devkit node my-api --pg --drizzle # + PostgreSQL with Drizzle ORM
|
|
65
|
+
devkit node my-api --turso --drizzle # + Turso (LibSQL) with Drizzle ORM
|
|
66
|
+
devkit node my-api --supabase --redis # + Supabase + Redis cache
|
|
67
|
+
devkit node my-api --mongo --cloudinary # + Mongoose + Cloudinary uploads
|
|
68
|
+
devkit node my-api --mongo --github # + Mongoose, then create & push GitHub repo
|
|
69
|
+
|
|
70
|
+
devkit react my-app # React + Vite + Tailwind
|
|
71
|
+
devkit react my-app --router # + React Router DOM
|
|
72
|
+
devkit react my-app --router --axios # + React Router + Axios
|
|
73
|
+
devkit react my-app --axios --github # + Axios, then create & push GitHub repo
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## GitHub repo creation
|
|
77
|
+
|
|
78
|
+
The `--github` flag uses the [GitHub CLI](https://cli.github.com) to create a repo and push your initial commit automatically. It requires:
|
|
79
|
+
|
|
80
|
+
1. The `gh` CLI installed (`winget install --id GitHub.cli` on Windows)
|
|
81
|
+
2. Being logged in once via `gh auth login`
|
|
82
|
+
|
|
83
|
+
Without `--github`, devkit only runs `git init` locally — no remote repo is created.
|
|
84
|
+
|
|
85
|
+
## What gets scaffolded
|
|
86
|
+
|
|
87
|
+
### Node project
|
|
88
|
+
```
|
|
89
|
+
<appname>/
|
|
90
|
+
src/
|
|
91
|
+
routes/ # Express routers
|
|
92
|
+
controllers/ # Handler logic
|
|
93
|
+
middleware/ # errorHandler.js, notFound.js, asyncHandler.js
|
|
94
|
+
config/ # DB config, redis.js, cloudinary.js (depending on flags)
|
|
95
|
+
models/ # Mongoose models (if --mongo)
|
|
96
|
+
index.js # Entry point with all middleware wired up
|
|
97
|
+
prisma/ # (if --prisma)
|
|
98
|
+
schema.prisma
|
|
99
|
+
drizzle/ # (if --drizzle)
|
|
100
|
+
.env + .env.example
|
|
101
|
+
.gitignore
|
|
102
|
+
package.json # nodemon dev script included
|
|
103
|
+
README.md
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### React project
|
|
107
|
+
```
|
|
108
|
+
<appname>/
|
|
109
|
+
src/
|
|
110
|
+
components/
|
|
111
|
+
pages/
|
|
112
|
+
hooks/
|
|
113
|
+
utils/
|
|
114
|
+
api.js # Pre-configured axios instance (if --axios)
|
|
115
|
+
assets/
|
|
116
|
+
App.jsx # Clean starting point (with Router if --router)
|
|
117
|
+
index.css # Tailwind directives + base styles
|
|
118
|
+
vite.config.js
|
|
119
|
+
.env + .env.example # VITE_API_URL pre-set
|
|
120
|
+
.gitignore
|
|
121
|
+
README.md
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Uninstall
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm unlink -g devkit
|
|
128
|
+
```
|
package/bin/devkit.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from "commander";
|
|
3
|
+
import { createNodeProject } from "../src/commands/createNode.js";
|
|
4
|
+
import { createReactProject } from "../src/commands/createReact.js";
|
|
5
|
+
import kleur from "kleur";
|
|
6
|
+
|
|
7
|
+
const VERSION = "1.0.0";
|
|
8
|
+
|
|
9
|
+
console.log(kleur.bold().cyan(`\n devkit CLI tool v${VERSION} \n`));
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name("devkit")
|
|
13
|
+
.description("Spin up Node or React projects instantly with your standard stack")
|
|
14
|
+
.version(VERSION);
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.command("node <appname>")
|
|
18
|
+
.description("Scaffold a Node.js + Express project")
|
|
19
|
+
.option("--no-git", "skip git init")
|
|
20
|
+
.option("--github", "create and push to a GitHub repo (requires gh CLI + login)")
|
|
21
|
+
.option("--mongo", "include Mongoose (MongoDB)")
|
|
22
|
+
.option("--prisma", "include Prisma ORM setup")
|
|
23
|
+
.option("--turso", "include Turso (LibSQL) client")
|
|
24
|
+
.option("--pg", "include postgres.js (PostgreSQL)")
|
|
25
|
+
.option("--supabase", "include Supabase JS client")
|
|
26
|
+
.option("--redis", "include Redis (ioredis)")
|
|
27
|
+
.option("--drizzle", "include Drizzle ORM (pair with --turso or --pg)")
|
|
28
|
+
.option("--cloudinary", "include Cloudinary + Multer")
|
|
29
|
+
.option("--socket", "include Socket.IO")
|
|
30
|
+
.action((appname, options) => {
|
|
31
|
+
createNodeProject(appname, options);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command("react <appname>")
|
|
36
|
+
.description("Scaffold a React + Vite project")
|
|
37
|
+
.option("--no-git", "skip git init")
|
|
38
|
+
.option("--github", "create and push to a GitHub repo (requires gh CLI + login)")
|
|
39
|
+
.option("--router", "include React Router DOM")
|
|
40
|
+
.option("--axios", "include Axios for HTTP requests")
|
|
41
|
+
.action((appname, options) => {
|
|
42
|
+
createReactProject(appname, options);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
program.on("command:*", () => {
|
|
46
|
+
console.error(kleur.red(` Unknown command: ${program.args.join(" ")}\n`));
|
|
47
|
+
console.log(` Run ${kleur.cyan("devkit --help")} to see available commands.\n`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
program.parse(process.argv);
|
|
52
|
+
|
|
53
|
+
if (!process.argv.slice(2).length) {
|
|
54
|
+
program.outputHelp();
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rellcodes16/devkit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Personal project scaffolding CLI — spin up Node or React projects instantly",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"devkit": "./bin/devkit.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node bin/devkit.js --help"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cli",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"express",
|
|
20
|
+
"react",
|
|
21
|
+
"vite",
|
|
22
|
+
"tailwind",
|
|
23
|
+
"boilerplate"
|
|
24
|
+
],
|
|
25
|
+
"author": "rellcodes16",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/rellcodes16/devkit"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/rellcodes16/devkit#readme",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^12.0.0",
|
|
34
|
+
"execa": "^8.0.1",
|
|
35
|
+
"fs-extra": "^11.2.0",
|
|
36
|
+
"kleur": "^4.1.5",
|
|
37
|
+
"ora": "^8.0.1"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import {
|
|
5
|
+
printStep,
|
|
6
|
+
printError,
|
|
7
|
+
printDivider,
|
|
8
|
+
runCommand,
|
|
9
|
+
printDoneMessage,
|
|
10
|
+
} from "../utils.js";
|
|
11
|
+
import {
|
|
12
|
+
nodePackageJson,
|
|
13
|
+
nodeIndexJs,
|
|
14
|
+
nodeEnv,
|
|
15
|
+
nodeGitignore,
|
|
16
|
+
nodeReadme,
|
|
17
|
+
nodeMongoConfig,
|
|
18
|
+
nodePrismaConfig,
|
|
19
|
+
nodePrismaSchema,
|
|
20
|
+
nodeTursoConfig,
|
|
21
|
+
nodeTursoWithDrizzleConfig,
|
|
22
|
+
nodeTursoSchema,
|
|
23
|
+
nodeDrizzleConfig,
|
|
24
|
+
nodePgConfig,
|
|
25
|
+
nodePgWithDrizzleConfig,
|
|
26
|
+
nodePgSchema,
|
|
27
|
+
nodeSupabaseConfig,
|
|
28
|
+
nodeRedisConfig,
|
|
29
|
+
nodeCloudinaryConfig,
|
|
30
|
+
nodeErrorHandler,
|
|
31
|
+
nodeNotFound,
|
|
32
|
+
nodeAsyncHandler,
|
|
33
|
+
nodeRoutes,
|
|
34
|
+
nodeUserModel,
|
|
35
|
+
} from "../templates/node/index.js";
|
|
36
|
+
|
|
37
|
+
export async function createNodeProject(appname, options) {
|
|
38
|
+
const targetDir = path.resolve(process.cwd(), appname);
|
|
39
|
+
|
|
40
|
+
if (fs.existsSync(targetDir)) {
|
|
41
|
+
printError(`Folder "${appname}" already exists. Choose a different name.`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Only one primary DB allowed
|
|
46
|
+
const dbFlags = [options.mongo, options.prisma, options.turso, options.pg, options.supabase].filter(Boolean);
|
|
47
|
+
if (dbFlags.length > 1) {
|
|
48
|
+
printError(
|
|
49
|
+
"Please choose only one database option: --mongo, --prisma, --turso, --pg, or --supabase.\n" +
|
|
50
|
+
" Redis (--redis) can be paired with any of the above."
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// pair --drizzle with only --turso or --pg
|
|
56
|
+
if (options.drizzle && !options.turso && !options.pg) {
|
|
57
|
+
printError("--drizzle must be paired with --turso or --pg.");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(kleur.bold(` Setting up Node.js project: ${kleur.cyan(appname)}\n`));
|
|
62
|
+
printDivider();
|
|
63
|
+
|
|
64
|
+
printStep("Creating project structure...");
|
|
65
|
+
|
|
66
|
+
const dirs = [
|
|
67
|
+
"src/routes",
|
|
68
|
+
"src/controllers",
|
|
69
|
+
"src/middleware",
|
|
70
|
+
"src/config",
|
|
71
|
+
"public",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
if (options.mongo) dirs.push("src/models");
|
|
75
|
+
if (options.drizzle) dirs.push("drizzle");
|
|
76
|
+
|
|
77
|
+
for (const dir of dirs) {
|
|
78
|
+
fs.mkdirSync(path.join(targetDir, dir), { recursive: true });
|
|
79
|
+
if (!["public", "drizzle"].includes(dir)) {
|
|
80
|
+
fs.writeFileSync(path.join(targetDir, dir, ".gitkeep"), "");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(path.join(targetDir, "public/favicon.ico"), "");
|
|
85
|
+
|
|
86
|
+
printStep("Writing base files...");
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(path.join(targetDir, "package.json"), nodePackageJson(appname, options));
|
|
89
|
+
fs.writeFileSync(path.join(targetDir, "src/index.js"), nodeIndexJs(options));
|
|
90
|
+
fs.writeFileSync(path.join(targetDir, ".env"), nodeEnv(options));
|
|
91
|
+
fs.writeFileSync(path.join(targetDir, ".env.example"), nodeEnv(options));
|
|
92
|
+
fs.writeFileSync(path.join(targetDir, ".gitignore"), nodeGitignore());
|
|
93
|
+
fs.writeFileSync(path.join(targetDir, "README.md"), nodeReadme(appname, options));
|
|
94
|
+
|
|
95
|
+
fs.writeFileSync(path.join(targetDir, "src/routes/index.js"), nodeRoutes());
|
|
96
|
+
fs.writeFileSync(path.join(targetDir, "src/middleware/errorHandler.js"), nodeErrorHandler());
|
|
97
|
+
fs.writeFileSync(path.join(targetDir, "src/middleware/notFound.js"), nodeNotFound());
|
|
98
|
+
fs.writeFileSync(path.join(targetDir, "src/middleware/asyncHandler.js"), nodeAsyncHandler());
|
|
99
|
+
|
|
100
|
+
if (options.mongo) {
|
|
101
|
+
printStep("Adding Mongoose setup...");
|
|
102
|
+
fs.writeFileSync(path.join(targetDir, "src/config/db.js"), nodeMongoConfig());
|
|
103
|
+
fs.writeFileSync(
|
|
104
|
+
path.join(targetDir, "src/models/User.js"),
|
|
105
|
+
nodeUserModel(options.cloudinary)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options.prisma) {
|
|
110
|
+
printStep("Adding Prisma setup...");
|
|
111
|
+
fs.mkdirSync(path.join(targetDir, "prisma"), { recursive: true });
|
|
112
|
+
fs.writeFileSync(path.join(targetDir, "prisma/schema.prisma"), nodePrismaSchema());
|
|
113
|
+
fs.writeFileSync(path.join(targetDir, "src/config/db.js"), nodePrismaConfig());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (options.turso) {
|
|
117
|
+
printStep(options.drizzle ? "Adding Turso + Drizzle setup..." : "Adding Turso setup...");
|
|
118
|
+
fs.writeFileSync(
|
|
119
|
+
path.join(targetDir, "src/config/db.js"),
|
|
120
|
+
options.drizzle ? nodeTursoWithDrizzleConfig() : nodeTursoConfig()
|
|
121
|
+
);
|
|
122
|
+
if (options.drizzle) {
|
|
123
|
+
fs.writeFileSync(path.join(targetDir, "src/config/schema.js"), nodeTursoSchema());
|
|
124
|
+
fs.writeFileSync(path.join(targetDir, "drizzle.config.js"), nodeDrizzleConfig(options));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (options.pg) {
|
|
129
|
+
printStep(options.drizzle ? "Adding PostgreSQL + Drizzle setup..." : "Adding PostgreSQL setup...");
|
|
130
|
+
fs.writeFileSync(
|
|
131
|
+
path.join(targetDir, "src/config/db.js"),
|
|
132
|
+
options.drizzle ? nodePgWithDrizzleConfig() : nodePgConfig()
|
|
133
|
+
);
|
|
134
|
+
if (options.drizzle) {
|
|
135
|
+
fs.writeFileSync(path.join(targetDir, "src/config/schema.js"), nodePgSchema());
|
|
136
|
+
fs.writeFileSync(path.join(targetDir, "drizzle.config.js"), nodeDrizzleConfig(options));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (options.supabase) {
|
|
141
|
+
printStep("Adding Supabase setup...");
|
|
142
|
+
fs.writeFileSync(path.join(targetDir, "src/config/db.js"), nodeSupabaseConfig());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (options.redis) {
|
|
146
|
+
printStep("Adding Redis setup...");
|
|
147
|
+
fs.writeFileSync(path.join(targetDir, "src/config/redis.js"), nodeRedisConfig());
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if (options.cloudinary) {
|
|
152
|
+
printStep("Adding Cloudinary + Multer setup...");
|
|
153
|
+
fs.writeFileSync(path.join(targetDir, "src/config/cloudinary.js"), nodeCloudinaryConfig());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
printDivider();
|
|
157
|
+
|
|
158
|
+
const coreDeps = [
|
|
159
|
+
"express",
|
|
160
|
+
"cors",
|
|
161
|
+
"dotenv",
|
|
162
|
+
"morgan",
|
|
163
|
+
"helmet",
|
|
164
|
+
"cookie-parser",
|
|
165
|
+
"express-rate-limit",
|
|
166
|
+
"express-mongo-sanitize",
|
|
167
|
+
"xss-clean",
|
|
168
|
+
"serve-favicon",
|
|
169
|
+
"validator",
|
|
170
|
+
"uuid",
|
|
171
|
+
"http-errors",
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const devDeps = ["nodemon"];
|
|
175
|
+
|
|
176
|
+
if (options.mongo) coreDeps.push("mongoose");
|
|
177
|
+
if (options.prisma) coreDeps.push("@prisma/client");
|
|
178
|
+
if (options.turso) coreDeps.push("@libsql/client");
|
|
179
|
+
if (options.pg) coreDeps.push("postgres");
|
|
180
|
+
if (options.supabase) coreDeps.push("@supabase/supabase-js");
|
|
181
|
+
if (options.redis) coreDeps.push("ioredis");
|
|
182
|
+
if (options.drizzle) coreDeps.push("drizzle-orm");
|
|
183
|
+
if (options.socket) coreDeps.push("socket.io");
|
|
184
|
+
|
|
185
|
+
if (options.cloudinary) {
|
|
186
|
+
coreDeps.push("cloudinary", "multer", "multer-storage-cloudinary");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (options.prisma) devDeps.push("prisma");
|
|
190
|
+
if (options.drizzle) devDeps.push("drizzle-kit");
|
|
191
|
+
|
|
192
|
+
await runCommand("npm", ["install", ...coreDeps], targetDir, `Installing: ${coreDeps.join(", ")}`);
|
|
193
|
+
await runCommand("npm", ["install", "-D", ...devDeps], targetDir, `Installing dev deps: ${devDeps.join(", ")}`);
|
|
194
|
+
|
|
195
|
+
if (options.prisma) {
|
|
196
|
+
await runCommand("npx", ["prisma", "generate"], targetDir, "Generating Prisma client...").catch(() => {
|
|
197
|
+
// Non-fatal — DATABASE_URL not set yet
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (options.git !== false) {
|
|
202
|
+
printDivider();
|
|
203
|
+
await runCommand("git", ["init"], targetDir, "Initialising git repository...");
|
|
204
|
+
await runCommand("git", ["add", "."], targetDir, "Staging files...");
|
|
205
|
+
await runCommand("git", ["commit", "-m", "chore: initial scaffold via devkit"], targetDir, "Initial commit...");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (options.github) {
|
|
209
|
+
await runCommand("gh", ["repo", "create", appname, "--public", "--source=.", "--push"], targetDir, "Creating GitHub repo...");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const extraLines = ["cp .env.example .env # then fill in your secrets"];
|
|
213
|
+
if (options.prisma) extraLines.push("npx prisma migrate dev # after setting DATABASE_URL");
|
|
214
|
+
if (options.drizzle) extraLines.push("npx drizzle-kit push # after setting DB credentials");
|
|
215
|
+
if (options.redis) extraLines.push("# Make sure Redis is running: redis-server");
|
|
216
|
+
|
|
217
|
+
printDivider();
|
|
218
|
+
printDoneMessage(appname, "npm run dev", extraLines);
|
|
219
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import kleur from "kleur";
|
|
4
|
+
import {
|
|
5
|
+
printStep,
|
|
6
|
+
printError,
|
|
7
|
+
printDivider,
|
|
8
|
+
runCommand,
|
|
9
|
+
printDoneMessage,
|
|
10
|
+
} from "../utils.js";
|
|
11
|
+
import {
|
|
12
|
+
reactAppJsx,
|
|
13
|
+
reactIndexCss,
|
|
14
|
+
reactViteConfig,
|
|
15
|
+
reactGitignore,
|
|
16
|
+
reactReadme,
|
|
17
|
+
reactEnv,
|
|
18
|
+
reactApiUtil,
|
|
19
|
+
} from "../templates/react/index.js";
|
|
20
|
+
|
|
21
|
+
export async function createReactProject(appname, options) {
|
|
22
|
+
const targetDir = path.resolve(process.cwd(), appname);
|
|
23
|
+
|
|
24
|
+
if (fs.existsSync(targetDir)) {
|
|
25
|
+
printError(`Folder "${appname}" already exists. Choose a different name.`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(
|
|
30
|
+
kleur.bold(` Setting up React + Vite project: ${kleur.cyan(appname)}\n`)
|
|
31
|
+
);
|
|
32
|
+
printDivider();
|
|
33
|
+
|
|
34
|
+
printStep("Creating Vite + React app...");
|
|
35
|
+
await runCommand(
|
|
36
|
+
"npm",
|
|
37
|
+
["create", "vite@latest", appname, "--", "--template", "react"],
|
|
38
|
+
process.cwd(),
|
|
39
|
+
"Running create-vite..."
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
printStep("Writing project files...");
|
|
43
|
+
|
|
44
|
+
const extraDirs = [
|
|
45
|
+
"src/components",
|
|
46
|
+
"src/pages",
|
|
47
|
+
"src/hooks",
|
|
48
|
+
"src/utils",
|
|
49
|
+
"src/assets",
|
|
50
|
+
];
|
|
51
|
+
for (const dir of extraDirs) {
|
|
52
|
+
fs.mkdirSync(path.join(targetDir, dir), { recursive: true });
|
|
53
|
+
fs.writeFileSync(path.join(targetDir, dir, ".gitkeep"), "");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fs.writeFileSync(path.join(targetDir, "vite.config.js"), reactViteConfig());
|
|
57
|
+
|
|
58
|
+
fs.writeFileSync(
|
|
59
|
+
path.join(targetDir, "src/App.jsx"),
|
|
60
|
+
reactAppJsx(appname, options)
|
|
61
|
+
);
|
|
62
|
+
fs.writeFileSync(path.join(targetDir, "src/index.css"), reactIndexCss());
|
|
63
|
+
|
|
64
|
+
if (options.axios) {
|
|
65
|
+
fs.writeFileSync(
|
|
66
|
+
path.join(targetDir, "src/utils/api.js"),
|
|
67
|
+
reactApiUtil()
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fs.writeFileSync(path.join(targetDir, ".env"), reactEnv());
|
|
72
|
+
fs.writeFileSync(path.join(targetDir, ".env.example"), reactEnv());
|
|
73
|
+
fs.writeFileSync(path.join(targetDir, ".gitignore"), reactGitignore());
|
|
74
|
+
fs.writeFileSync(
|
|
75
|
+
path.join(targetDir, "README.md"),
|
|
76
|
+
reactReadme(appname, options)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
printDivider();
|
|
80
|
+
|
|
81
|
+
const coreDeps = [
|
|
82
|
+
"lucide-react",
|
|
83
|
+
"clsx",
|
|
84
|
+
"react-hot-toast",
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const devDeps = ["tailwindcss", "@tailwindcss/vite"];
|
|
88
|
+
|
|
89
|
+
if (options.router) coreDeps.push("react-router-dom");
|
|
90
|
+
if (options.axios) coreDeps.push("axios");
|
|
91
|
+
|
|
92
|
+
await runCommand("npm", ["install"], targetDir, "Installing base deps...");
|
|
93
|
+
await runCommand(
|
|
94
|
+
"npm",
|
|
95
|
+
["install", ...coreDeps],
|
|
96
|
+
targetDir,
|
|
97
|
+
`Installing: ${coreDeps.join(", ")}`
|
|
98
|
+
);
|
|
99
|
+
await runCommand(
|
|
100
|
+
"npm",
|
|
101
|
+
["install", "--save-dev", ...devDeps],
|
|
102
|
+
targetDir,
|
|
103
|
+
`Installing dev deps: ${devDeps.join(", ")}`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (options.git !== false) {
|
|
107
|
+
printDivider();
|
|
108
|
+
await runCommand("git", ["init"], targetDir, "Initialising git repository...");
|
|
109
|
+
await runCommand("git", ["add", "."], targetDir, "Staging files...");
|
|
110
|
+
await runCommand(
|
|
111
|
+
"git",
|
|
112
|
+
["commit", "-m", "chore: initial scaffold via devkit"],
|
|
113
|
+
targetDir,
|
|
114
|
+
"Initial commit..."
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (options.github) {
|
|
119
|
+
await runCommand("gh", ["repo", "create", appname, "--public", "--source=.", "--push"], targetDir, "Creating GitHub repo...");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
printDivider();
|
|
123
|
+
printDoneMessage(appname, "npm run dev");
|
|
124
|
+
}
|