create-buntok 0.1.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/package.json +24 -0
- package/src/index.ts +185 -0
- package/templates/.dockerignore +34 -0
- package/templates/.env.example +6 -0
- package/templates/.vscode/settings.json +15 -0
- package/templates/Dockerfile +36 -0
- package/templates/biome.json +28 -0
- package/templates/docker-compose.yml +15 -0
- package/templates/drizzle.config.ts +10 -0
- package/templates/package.json +26 -0
- package/templates/public/favicon.ico +0 -0
- package/templates/src/db/index.ts +9 -0
- package/templates/src/index.ts +11 -0
- package/templates/tsconfig.json +18 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-buntok",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool to create new Buntok projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-buntok": "./src/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"buntok",
|
|
15
|
+
"bun",
|
|
16
|
+
"framework",
|
|
17
|
+
"cli",
|
|
18
|
+
"scaffold"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/bun": "latest"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
statSync,
|
|
10
|
+
unlinkSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
import { createInterface } from "node:readline";
|
|
15
|
+
|
|
16
|
+
const TEMPLATES_DIR = join(import.meta.dir, "..", "templates");
|
|
17
|
+
|
|
18
|
+
function printBanner() {
|
|
19
|
+
console.log(`
|
|
20
|
+
\x1b[36m
|
|
21
|
+
██████╗ ██╗ ██╗███╗ ██╗████████╗ ██████╗ ██╗ ██╗
|
|
22
|
+
██╔══██╗██║ ██║████╗ ██║╚══██╔══╝██╔═══██╗██║ ██╔╝
|
|
23
|
+
██████╔╝██║ ██║██╔██╗ ██║ ██║ ██║ ██║█████╔╝
|
|
24
|
+
██╔══██╗██║ ██║██║╚██╗██║ ██║ ██║ ██║██╔═██╗
|
|
25
|
+
██████╔╝╚██████╔╝██║ ╚████║ ██║ ╚██████╔╝██║ ██╗
|
|
26
|
+
╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
|
|
27
|
+
\x1b[0m`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function printUsage() {
|
|
31
|
+
console.log("Usage: bunx create-buntok <project-name>\n");
|
|
32
|
+
console.log("Example:");
|
|
33
|
+
console.log(" bunx create-buntok my-api\n");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateProjectName(name: string): boolean {
|
|
37
|
+
if (!name || name.length === 0) {
|
|
38
|
+
console.error("\x1b[31mError: Project name is required\x1b[0m");
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
43
|
+
console.error(
|
|
44
|
+
"\x1b[31mError: Project name can only contain letters, numbers, hyphens, and underscores\x1b[0m",
|
|
45
|
+
);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function copyDirSync(src: string, dest: string) {
|
|
53
|
+
mkdirSync(dest, { recursive: true });
|
|
54
|
+
const entries = readdirSync(src);
|
|
55
|
+
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const srcPath = join(src, entry);
|
|
58
|
+
const destPath = join(dest, entry);
|
|
59
|
+
|
|
60
|
+
if (statSync(srcPath).isDirectory()) {
|
|
61
|
+
copyDirSync(srcPath, destPath);
|
|
62
|
+
} else {
|
|
63
|
+
cpSync(srcPath, destPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function replaceTemplate(content: string, projectName: string): string {
|
|
69
|
+
return content.replace(/{PROJECT_NAME}/g, projectName);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function askQuestion(question: string): Promise<boolean> {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
const rl = createInterface({
|
|
75
|
+
input: process.stdin,
|
|
76
|
+
output: process.stdout,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
rl.question(question, (answer) => {
|
|
80
|
+
rl.close();
|
|
81
|
+
const normalized = answer.trim().toLowerCase();
|
|
82
|
+
resolve(normalized === "y" || normalized === "yes" || normalized === "");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function main() {
|
|
88
|
+
printBanner();
|
|
89
|
+
|
|
90
|
+
const args = process.argv.slice(2);
|
|
91
|
+
const projectName = args[0];
|
|
92
|
+
|
|
93
|
+
if (!projectName) {
|
|
94
|
+
printUsage();
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!validateProjectName(projectName)) {
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const projectPath = resolve(process.cwd(), projectName);
|
|
103
|
+
|
|
104
|
+
if (existsSync(projectPath)) {
|
|
105
|
+
console.error(
|
|
106
|
+
`\x1b[31mError: Directory "${projectName}" already exists\x1b[0m`,
|
|
107
|
+
);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(`\x1b[36mCreating Buntok project: ${projectName}\x1b[0m\n`);
|
|
112
|
+
|
|
113
|
+
// Ask about Docker support
|
|
114
|
+
const useDocker = await askQuestion(
|
|
115
|
+
"\x1b[36mDo you want to include Docker support? (Y/n): \x1b[0m",
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Create project directory
|
|
119
|
+
mkdirSync(projectPath, { recursive: true });
|
|
120
|
+
|
|
121
|
+
// Copy templates
|
|
122
|
+
console.log("\n\x1b[90m Copying template files...\x1b[0m");
|
|
123
|
+
copyDirSync(TEMPLATES_DIR, projectPath);
|
|
124
|
+
|
|
125
|
+
// Handle Docker files based on user choice
|
|
126
|
+
const dockerFiles = ["Dockerfile", ".dockerignore", "docker-compose.yml"];
|
|
127
|
+
if (!useDocker) {
|
|
128
|
+
// Remove Docker files if user declined
|
|
129
|
+
for (const file of dockerFiles) {
|
|
130
|
+
const filePath = join(projectPath, file);
|
|
131
|
+
if (existsSync(filePath)) {
|
|
132
|
+
unlinkSync(filePath);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log("\x1b[90m Skipped Docker files\x1b[0m");
|
|
136
|
+
} else {
|
|
137
|
+
console.log("\x1b[90m Included Docker support\x1b[0m");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Process template files (replace placeholders)
|
|
141
|
+
const packageJsonPath = join(projectPath, "package.json");
|
|
142
|
+
if (existsSync(packageJsonPath)) {
|
|
143
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
144
|
+
writeFileSync(packageJsonPath, replaceTemplate(content, projectName));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const tsConfigPath = join(projectPath, "tsconfig.json");
|
|
148
|
+
if (existsSync(tsConfigPath)) {
|
|
149
|
+
const content = readFileSync(tsConfigPath, "utf-8");
|
|
150
|
+
writeFileSync(tsConfigPath, replaceTemplate(content, projectName));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Run bun install
|
|
154
|
+
console.log("\n\x1b[90m Installing dependencies...\x1b[0m\n");
|
|
155
|
+
|
|
156
|
+
const proc = Bun.spawnSync(["bun", "install"], {
|
|
157
|
+
cwd: projectPath,
|
|
158
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (proc.exitCode !== 0) {
|
|
162
|
+
console.error("\x1b[31mFailed to install dependencies\x1b[0m");
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Success message
|
|
167
|
+
console.log(`
|
|
168
|
+
\x1b[32m✓ Project "${projectName}" created successfully!\x1b[0m
|
|
169
|
+
|
|
170
|
+
\x1b[36mGetting started:\x1b[0m
|
|
171
|
+
cd ${projectName}
|
|
172
|
+
bun run dev
|
|
173
|
+
|
|
174
|
+
\x1b[36mCode generation:\x1b[0m
|
|
175
|
+
bunx buntok create <entity> # Generate all (schema, repo, service, controller)
|
|
176
|
+
bunx buntok create <entity> --schema # Generate only schema
|
|
177
|
+
${useDocker ? `
|
|
178
|
+
\x1b[36mDocker:\x1b[0m
|
|
179
|
+
docker compose up --build
|
|
180
|
+
` : ""}
|
|
181
|
+
\x1b[90mHappy coding with Buntok! ⚡\x1b[0m
|
|
182
|
+
`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
main();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# dependencies (bun install)
|
|
2
|
+
node_modules
|
|
3
|
+
|
|
4
|
+
# output
|
|
5
|
+
out
|
|
6
|
+
dist
|
|
7
|
+
*.tgz
|
|
8
|
+
|
|
9
|
+
# code coverage
|
|
10
|
+
coverage
|
|
11
|
+
*.lcov
|
|
12
|
+
|
|
13
|
+
# logs
|
|
14
|
+
logs
|
|
15
|
+
*.log
|
|
16
|
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
17
|
+
|
|
18
|
+
# dotenv environment variable files
|
|
19
|
+
.env
|
|
20
|
+
.env.development.local
|
|
21
|
+
.env.test.local
|
|
22
|
+
.env.production.local
|
|
23
|
+
.env.local
|
|
24
|
+
|
|
25
|
+
# caches
|
|
26
|
+
.eslintcache
|
|
27
|
+
.cache
|
|
28
|
+
*.tsbuildinfo
|
|
29
|
+
|
|
30
|
+
# IntelliJ based IDEs
|
|
31
|
+
.idea
|
|
32
|
+
|
|
33
|
+
# Finder (MacOS) folder config
|
|
34
|
+
.DS_Store
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.formatOnSave": true,
|
|
3
|
+
"editor.defaultFormatter": "biomejs.biome",
|
|
4
|
+
"editor.tabSize": 2,
|
|
5
|
+
"editor.insertSpaces": false,
|
|
6
|
+
"[typescript]": {
|
|
7
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
8
|
+
},
|
|
9
|
+
"[javascript]": {
|
|
10
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
11
|
+
},
|
|
12
|
+
"[json]": {
|
|
13
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Builder
|
|
2
|
+
FROM oven/bun:alpine AS builder
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Copy konfigurasi package dan install dependency
|
|
6
|
+
COPY package.json bun.lockb* ./
|
|
7
|
+
RUN bun install --frozen-lockfile
|
|
8
|
+
|
|
9
|
+
# Copy seluruh source code
|
|
10
|
+
COPY . .
|
|
11
|
+
|
|
12
|
+
# Production Runner
|
|
13
|
+
FROM oven/bun:alpine
|
|
14
|
+
WORKDIR /app
|
|
15
|
+
|
|
16
|
+
# Copy dependency yang sudah di-install dan source code dari builder
|
|
17
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
18
|
+
COPY --from=builder /app/src ./src
|
|
19
|
+
COPY --from=builder /app/package.json ./
|
|
20
|
+
|
|
21
|
+
# Siapkan folder logs
|
|
22
|
+
RUN mkdir -p /app/logs && chown -R bun:bun /app/logs
|
|
23
|
+
|
|
24
|
+
# Gunakan user non-root untuk keamanan
|
|
25
|
+
USER bun
|
|
26
|
+
|
|
27
|
+
# Expose default port
|
|
28
|
+
EXPOSE 1212
|
|
29
|
+
|
|
30
|
+
# Environment variables
|
|
31
|
+
ENV NODE_ENV=production
|
|
32
|
+
ENV PORT=1212
|
|
33
|
+
ENV LOG_DIR=/app/logs
|
|
34
|
+
|
|
35
|
+
# Jalankan framework
|
|
36
|
+
CMD ["bun", "run", "src/index.ts"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.3/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false
|
|
10
|
+
},
|
|
11
|
+
"formatter": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"indentStyle": "tab",
|
|
14
|
+
"indentWidth": 2,
|
|
15
|
+
"lineWidth": 80
|
|
16
|
+
},
|
|
17
|
+
"linter": {
|
|
18
|
+
"enabled": true,
|
|
19
|
+
"rules": {
|
|
20
|
+
"preset": "recommended"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"javascript": {
|
|
24
|
+
"formatter": {
|
|
25
|
+
"quoteStyle": "double"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{PROJECT_NAME}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "bun --watch src/index.ts",
|
|
7
|
+
"start": "bun run src/index.ts",
|
|
8
|
+
"db:push": "bunx drizzle-kit push",
|
|
9
|
+
"db:generate": "bunx drizzle-kit generate",
|
|
10
|
+
"db:migrate": "bunx drizzle-kit migrate",
|
|
11
|
+
"db:studio": "bunx drizzle-kit studio",
|
|
12
|
+
"check": "bunx @biomejs/biome check --write .",
|
|
13
|
+
"format": "bunx @biomejs/biome format --write .",
|
|
14
|
+
"lint": "bunx @biomejs/biome lint ."
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"buntok": "latest",
|
|
18
|
+
"drizzle-orm": "^0.38.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@biomejs/biome": "2.5.3",
|
|
22
|
+
"@types/bun": "latest",
|
|
23
|
+
"drizzle-kit": "^0.30.0",
|
|
24
|
+
"typescript": "^5"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { drizzle } from "drizzle-orm/bun-sql";
|
|
2
|
+
import * as schema from "./schemas";
|
|
3
|
+
|
|
4
|
+
// TODO: Update DATABASE_URL in .env file
|
|
5
|
+
// Example: DATABASE_URL=postgresql://user:password@localhost:5432/mydb
|
|
6
|
+
|
|
7
|
+
const connection = Bun.env.DATABASE_URL || "postgresql://localhost:5432/buntok";
|
|
8
|
+
|
|
9
|
+
export const db = drizzle(connection, { schema });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "Preserve",
|
|
6
|
+
"moduleDetection": "force",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"types": ["bun"],
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"skipLibCheck": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"]
|
|
18
|
+
}
|