@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 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
+ }