jmdev-stack-cli 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/index.js +155 -0
- package/package.json +31 -0
package/index.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require("commander");
|
|
4
|
+
const inquirer = require("inquirer");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const ora = require("ora");
|
|
7
|
+
const { execa } = require("execa"); // destructure the function
|
|
8
|
+
const fs = require("fs-extra");
|
|
9
|
+
require("dotenv").config();
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name("jmdev-stack-cli")
|
|
15
|
+
.description("Fullstack CLI generator for Next.js + Drizzle + Neon + Chakra UI")
|
|
16
|
+
.version("1.0.0");
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command("create [projectName]")
|
|
20
|
+
.option("-t, --typescript", "Use TypeScript")
|
|
21
|
+
.action(async (projectName, options) => {
|
|
22
|
+
try {
|
|
23
|
+
// š„ STEP 1: Ask missing inputs
|
|
24
|
+
if (!projectName) {
|
|
25
|
+
const answer = await inquirer.prompt([
|
|
26
|
+
{
|
|
27
|
+
type: "input",
|
|
28
|
+
name: "name",
|
|
29
|
+
message: "Project name:",
|
|
30
|
+
default: "my-app",
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
projectName = answer.name;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let useTS = options.typescript;
|
|
37
|
+
|
|
38
|
+
if (!useTS) {
|
|
39
|
+
const answer = await inquirer.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: "confirm",
|
|
42
|
+
name: "typescript",
|
|
43
|
+
message: "Use TypeScript?",
|
|
44
|
+
default: true,
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
useTS = answer.typescript;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log(chalk.cyan(`\nCreating ${projectName}...\n`));
|
|
51
|
+
|
|
52
|
+
// š„ STEP 2: Create Next.js app
|
|
53
|
+
const spinner = ora("Creating Next.js app...").start();
|
|
54
|
+
|
|
55
|
+
const args = ["create-next-app@latest", projectName, "--yes"];
|
|
56
|
+
if (useTS) args.push("--ts");
|
|
57
|
+
|
|
58
|
+
await execa("npx", args, { stdio: "inherit" });
|
|
59
|
+
|
|
60
|
+
spinner.succeed("Next.js created!");
|
|
61
|
+
|
|
62
|
+
process.chdir(projectName);
|
|
63
|
+
|
|
64
|
+
// š„ STEP 3: Install dependencies
|
|
65
|
+
spinner.start("Installing dependencies...");
|
|
66
|
+
|
|
67
|
+
await execa("npm", [
|
|
68
|
+
"install",
|
|
69
|
+
"drizzle-orm",
|
|
70
|
+
"@neondatabase/serverless",
|
|
71
|
+
"dotenv",
|
|
72
|
+
"@chakra-ui/react",
|
|
73
|
+
"@emotion/react",
|
|
74
|
+
"@emotion/styled",
|
|
75
|
+
"framer-motion"
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
await execa("npm", [
|
|
79
|
+
"install",
|
|
80
|
+
"-D",
|
|
81
|
+
"drizzle-kit",
|
|
82
|
+
"tsx"
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
spinner.succeed("Dependencies installed!");
|
|
86
|
+
|
|
87
|
+
// š„ STEP 4: Create project structure
|
|
88
|
+
spinner.start("Setting up project files...");
|
|
89
|
+
|
|
90
|
+
await fs.ensureDir("config");
|
|
91
|
+
|
|
92
|
+
// .env
|
|
93
|
+
await fs.writeFile(".env", "DATABASE_URL=\n");
|
|
94
|
+
|
|
95
|
+
// db.ts
|
|
96
|
+
await fs.writeFile(
|
|
97
|
+
"config/db.ts",
|
|
98
|
+
`import { neon } from '@neondatabase/serverless';
|
|
99
|
+
import { drizzle } from 'drizzle-orm/neon-http';
|
|
100
|
+
|
|
101
|
+
const sql = neon(process.env.DATABASE_URL!);
|
|
102
|
+
export const db = drizzle({ client: sql });
|
|
103
|
+
`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// schema.ts
|
|
107
|
+
await fs.writeFile(
|
|
108
|
+
"config/schema.ts",
|
|
109
|
+
`import { pgTable, serial, text } from "drizzle-orm/pg-core";
|
|
110
|
+
|
|
111
|
+
export const users = pgTable("users", {
|
|
112
|
+
id: serial("id").primaryKey(),
|
|
113
|
+
name: text("name"),
|
|
114
|
+
});
|
|
115
|
+
`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// drizzle.config.ts
|
|
119
|
+
await fs.writeFile(
|
|
120
|
+
"drizzle.config.ts",
|
|
121
|
+
`import 'dotenv/config';
|
|
122
|
+
import { defineConfig } from 'drizzle-kit';
|
|
123
|
+
|
|
124
|
+
export default defineConfig({
|
|
125
|
+
schema: './config/schema.ts',
|
|
126
|
+
dialect: 'postgresql',
|
|
127
|
+
dbCredentials: {
|
|
128
|
+
url: process.env.DATABASE_URL!,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
`
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
spinner.succeed("Project setup complete!");
|
|
135
|
+
|
|
136
|
+
// š„ STEP 5: Validate .env
|
|
137
|
+
const envContent = await fs.readFile(".env", "utf-8");
|
|
138
|
+
|
|
139
|
+
if (!envContent.includes("DATABASE_URL=")) {
|
|
140
|
+
console.log(chalk.red("Missing DATABASE_URL in .env"));
|
|
141
|
+
} else {
|
|
142
|
+
console.log(chalk.yellow("\nā ļø Add your Neon DATABASE_URL in .env\n"));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// š DONE
|
|
146
|
+
console.log(chalk.green("\nā
All set!\n"));
|
|
147
|
+
console.log(`cd ${projectName}`);
|
|
148
|
+
console.log("npm run dev\n");
|
|
149
|
+
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error(chalk.red("Error:"), error.message);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jmdev-stack-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fullstack CLI generator (Next.js + Drizzle + Neon + Chakra UI)",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jmdev-stack-cli": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cli",
|
|
14
|
+
"nextjs",
|
|
15
|
+
"drizzle",
|
|
16
|
+
"neon",
|
|
17
|
+
"chakra"
|
|
18
|
+
],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"type": "commonjs",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"chalk": "^4.1.2",
|
|
24
|
+
"commander": "^14.0.3",
|
|
25
|
+
"dotenv": "^17.4.0",
|
|
26
|
+
"execa": "^8.0.1",
|
|
27
|
+
"fs-extra": "^11.3.4",
|
|
28
|
+
"inquirer": "^8.2.7",
|
|
29
|
+
"ora": "^5.4.1"
|
|
30
|
+
}
|
|
31
|
+
}
|