create-gramstax 0.0.1 → 0.0.2
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/dist/src/index.cjs +13 -0
- package/dist/src/index.cjs.map +1 -0
- package/dist/src/index.d.cts +1 -0
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.js +12 -25
- package/dist/src/index.js.map +1 -0
- package/dist/src/templates/README.md +1 -1
- package/dist/src/templates/package.json +4 -4
- package/package.json +5 -4
- package/dist/package.json +0 -57
- package/dist/src/create-OLD.d.ts +0 -83
- package/dist/src/create-OLD.d.ts.map +0 -1
- package/dist/src/create-OLD.js +0 -676
- package/dist/src/create.d.ts +0 -34
- package/dist/src/create.d.ts.map +0 -1
- package/dist/src/create.js +0 -367
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/templates/src/env.d.ts +0 -8
- package/dist/src/utils/logger.d.ts +0 -3
- package/dist/src/utils/logger.d.ts.map +0 -1
- package/dist/src/utils/logger.js +0 -2
- package/src/create-OLD.ts +0 -783
- package/src/create.ts +0 -415
- package/src/index.ts +0 -28
- package/src/templates/.env.example +0 -17
- package/src/templates/.prettierignore +0 -1
- package/src/templates/.prettierrc +0 -23
- package/src/templates/LICENSE +0 -0
- package/src/templates/README.md +0 -121
- package/src/templates/bunfig.toml +0 -2
- package/src/templates/ecosystem.config.cjs +0 -26
- package/src/templates/package.json +0 -55
- package/src/templates/src/base/general.ts +0 -3
- package/src/templates/src/base/guard.ts +0 -3
- package/src/templates/src/base/page.ts +0 -3
- package/src/templates/src/base/repository.ts +0 -1
- package/src/templates/src/base/service.ts +0 -1
- package/src/templates/src/core/bot.ts +0 -22
- package/src/templates/src/db/index.ts +0 -3
- package/src/templates/src/env.d.ts +0 -8
- package/src/templates/src/guards/user.ts +0 -19
- package/src/templates/src/index.ts +0 -17
- package/src/templates/src/pages/general-error-input-notfound.ts +0 -34
- package/src/templates/src/pages/general-error.ts +0 -51
- package/src/templates/src/pages/help.ts +0 -56
- package/src/templates/src/pages/start.ts +0 -63
- package/src/templates/src/pages/username-notfound.ts +0 -41
- package/src/templates/src/repositories/example.ts +0 -5
- package/src/templates/src/services/example.ts +0 -4
- package/src/templates/src/templates/example.html +0 -7
- package/src/templates/src/threads/main.ts +0 -9
- package/src/templates/src/utils/log.ts +0 -3
- package/src/templates/tsconfig.json +0 -38
- package/src/utils/logger.ts +0 -3
- /package/dist/src/templates/{ecosystem.config.cjs → ecosystem.config.js} +0 -0
package/src/create-OLD.ts
DELETED
|
@@ -1,783 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs"
|
|
2
|
-
import * as path from "path"
|
|
3
|
-
import { log } from "./utils/logger"
|
|
4
|
-
import { spawn } from "child_process"
|
|
5
|
-
import enquirer from "enquirer"
|
|
6
|
-
|
|
7
|
-
type CreateOptions = {
|
|
8
|
-
yes?: boolean
|
|
9
|
-
useBun?: boolean
|
|
10
|
-
useNpm?: boolean
|
|
11
|
-
usePnpm?: boolean
|
|
12
|
-
useYarn?: boolean
|
|
13
|
-
eslint?: boolean
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
type PackageManager = `bun` | `npm` | `pnpm` | `yarn`
|
|
17
|
-
type Runtime = `bun` | `node`
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Recursively copy directory structure
|
|
21
|
-
*/
|
|
22
|
-
function copyDirectorySync(source: string, destination: string, variables: Record<string, string> = {}) {
|
|
23
|
-
// Create destination directory if it doesn't exist
|
|
24
|
-
if (!fs.existsSync(destination)) {
|
|
25
|
-
fs.mkdirSync(destination, { recursive: true })
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Read all items in source directory
|
|
29
|
-
const items = fs.readdirSync(source, { withFileTypes: true })
|
|
30
|
-
|
|
31
|
-
for (const item of items) {
|
|
32
|
-
const sourcePath = path.join(source, item.name)
|
|
33
|
-
const destPath = path.join(destination, item.name)
|
|
34
|
-
|
|
35
|
-
if (item.isDirectory()) {
|
|
36
|
-
// Recursively copy subdirectories
|
|
37
|
-
copyDirectorySync(sourcePath, destPath, variables)
|
|
38
|
-
} else if (item.isFile()) {
|
|
39
|
-
// Copy file and replace variables if needed
|
|
40
|
-
let content = fs.readFileSync(sourcePath, `utf-8`)
|
|
41
|
-
|
|
42
|
-
// Replace template variables in content
|
|
43
|
-
content = replaceTemplateVariables(content, variables)
|
|
44
|
-
|
|
45
|
-
fs.writeFileSync(destPath, content)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Replace template variables in content
|
|
52
|
-
*/
|
|
53
|
-
function replaceTemplateVariables(content: string, variables: Record<string, string>): string {
|
|
54
|
-
let result = content
|
|
55
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
56
|
-
result = result.replace(new RegExp(`{{${key}}}`, `g`), value)
|
|
57
|
-
}
|
|
58
|
-
return result
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get package manager commands
|
|
63
|
-
*/
|
|
64
|
-
function getPackageManagerCommands(packageManager: PackageManager) {
|
|
65
|
-
const commands = {
|
|
66
|
-
bun: {
|
|
67
|
-
install: `bun install`,
|
|
68
|
-
dev: `bun dev`,
|
|
69
|
-
start: `bun start`,
|
|
70
|
-
"pm2:start": `bun run pm2:start`,
|
|
71
|
-
"pm2:stop": `bun run pm2:stop`,
|
|
72
|
-
"pm2:restart": `bun run pm2:restart`,
|
|
73
|
-
"pm2:delete": `bun run pm2:delete`,
|
|
74
|
-
"pm2:logs": `bun run pm2:logs`,
|
|
75
|
-
"pm2:monit": `bun run pm2:monit`
|
|
76
|
-
},
|
|
77
|
-
npm: {
|
|
78
|
-
install: `npm install`,
|
|
79
|
-
dev: `npm run dev`,
|
|
80
|
-
start: `npm start`,
|
|
81
|
-
"pm2:start": `npm run pm2:start`,
|
|
82
|
-
"pm2:stop": `npm run pm2:stop`,
|
|
83
|
-
"pm2:restart": `npm run pm2:restart`,
|
|
84
|
-
"pm2:delete": `npm run pm2:delete`,
|
|
85
|
-
"pm2:logs": `npm run pm2:logs`,
|
|
86
|
-
"pm2:monit": `npm run pm2:monit`
|
|
87
|
-
},
|
|
88
|
-
pnpm: {
|
|
89
|
-
install: `pnpm install`,
|
|
90
|
-
dev: `pnpm dev`,
|
|
91
|
-
start: `pnpm start`,
|
|
92
|
-
"pm2:start": `pnpm pm2:start`,
|
|
93
|
-
"pm2:stop": `pnpm pm2:stop`,
|
|
94
|
-
"pm2:restart": `pnpm pm2:restart`,
|
|
95
|
-
"pm2:delete": `pnpm pm2:delete`,
|
|
96
|
-
"pm2:logs": `pnpm pm2:logs`,
|
|
97
|
-
"pm2:monit": `pnpm pm2:monit`
|
|
98
|
-
},
|
|
99
|
-
yarn: {
|
|
100
|
-
install: `yarn install`,
|
|
101
|
-
dev: `yarn dev`,
|
|
102
|
-
start: `yarn start`,
|
|
103
|
-
"pm2:start": `yarn pm2:start`,
|
|
104
|
-
"pm2:stop": `yarn pm2:stop`,
|
|
105
|
-
"pm2:restart": `yarn pm2:restart`,
|
|
106
|
-
"pm2:delete": `yarn pm2:delete`,
|
|
107
|
-
"pm2:logs": `yarn pm2:logs`,
|
|
108
|
-
"pm2:monit": `yarn pm2:monit`
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return commands[packageManager]
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export async function createProject(projectDirectory?: string, options?: CreateOptions) {
|
|
115
|
-
// Display ASCII art banner
|
|
116
|
-
printBanner()
|
|
117
|
-
|
|
118
|
-
// Get project name - if not provided, prompt or use current directory
|
|
119
|
-
let projectName = projectDirectory
|
|
120
|
-
let projectPath: string
|
|
121
|
-
|
|
122
|
-
if (!projectName) {
|
|
123
|
-
if (options?.yes) {
|
|
124
|
-
// Use current directory name like npm init
|
|
125
|
-
projectName = path.basename(process.cwd())
|
|
126
|
-
projectPath = process.cwd()
|
|
127
|
-
|
|
128
|
-
// Check if current directory is empty
|
|
129
|
-
const files = fs.readdirSync(projectPath)
|
|
130
|
-
if (files.length > 0) {
|
|
131
|
-
log.error(`Current directory is not empty!`)
|
|
132
|
-
process.exit(1)
|
|
133
|
-
}
|
|
134
|
-
} else {
|
|
135
|
-
projectName = await promptForProjectName()
|
|
136
|
-
projectPath = path.resolve(process.cwd(), projectName)
|
|
137
|
-
|
|
138
|
-
// Check if directory already exists
|
|
139
|
-
if (fs.existsSync(projectPath) && projectName != ``) {
|
|
140
|
-
log.error(`Directory "${projectName}" already exists!`)
|
|
141
|
-
process.exit(1)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (projectName != ``) {
|
|
145
|
-
// Create project directory
|
|
146
|
-
fs.mkdirSync(projectPath, { recursive: true })
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
projectPath = path.resolve(process.cwd(), projectName)
|
|
151
|
-
|
|
152
|
-
// Check if directory already exists
|
|
153
|
-
if (fs.existsSync(projectPath)) {
|
|
154
|
-
log.error(`Directory "${projectName}" already exists!`)
|
|
155
|
-
process.exit(1)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Create project directory
|
|
159
|
-
fs.mkdirSync(projectPath, { recursive: true })
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
log.info(`Creating project: ${path.basename(projectPath)}`)
|
|
163
|
-
log.info(`Location: ${projectPath}`)
|
|
164
|
-
|
|
165
|
-
// Prompt for configuration if not in --yes mode
|
|
166
|
-
let packageManager: PackageManager
|
|
167
|
-
let runtime: Runtime
|
|
168
|
-
let setupEslint: boolean
|
|
169
|
-
|
|
170
|
-
if (options?.yes) {
|
|
171
|
-
packageManager = detectPackageManager(options)
|
|
172
|
-
runtime = packageManager === `bun` ? `bun` : `node`
|
|
173
|
-
setupEslint = false
|
|
174
|
-
|
|
175
|
-
log.info(`Using defaults - Package Manager: ${packageManager}, Runtime: ${runtime}, ESLint: No`)
|
|
176
|
-
} else {
|
|
177
|
-
const config = await promptForConfiguration(options)
|
|
178
|
-
packageManager = config.packageManager
|
|
179
|
-
runtime = config.runtime
|
|
180
|
-
setupEslint = config.eslint
|
|
181
|
-
|
|
182
|
-
log.info(`Configuration - Package Manager: ${packageManager}, Runtime: ${runtime}, ESLint: ${setupEslint ? `Yes` : `No`}`)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Generate project structure
|
|
186
|
-
log.info(`Generating project structure...`)
|
|
187
|
-
await generateProjectStructure(projectPath, projectName, packageManager, runtime)
|
|
188
|
-
|
|
189
|
-
// Setup ESLint if requested
|
|
190
|
-
if (setupEslint) {
|
|
191
|
-
log.info(`Setting up ESLint...`)
|
|
192
|
-
await setupEslintConfig(projectPath, packageManager)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const commands = getPackageManagerCommands(packageManager)
|
|
196
|
-
|
|
197
|
-
log.success(`Project created successfully!`)
|
|
198
|
-
log.info(``)
|
|
199
|
-
log.info(`Next steps:`)
|
|
200
|
-
if (projectDirectory) {
|
|
201
|
-
log.info(` cd ${projectName}`)
|
|
202
|
-
}
|
|
203
|
-
log.info(` ${commands.install}`)
|
|
204
|
-
log.info(` ${commands.dev}`)
|
|
205
|
-
log.info(``)
|
|
206
|
-
log.info(`For production:`)
|
|
207
|
-
log.info(` ${commands[`pm2:start`]}`)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function printBanner() {
|
|
211
|
-
const banner = `
|
|
212
|
-
$$$$$$\\ $$\\
|
|
213
|
-
$$ __$$\\ $$ |
|
|
214
|
-
$$ / \\__| $$$$$$\\ $$$$$$\\ $$$$$$\\$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\ $$\\ $$\\
|
|
215
|
-
$$ |$$$$\\ $$ __$$\\ \\____$$\\ $$ _$$ _$$\\ $$ _____|\\_$$ _| \\____$$\\ \\$$\\ $$ |
|
|
216
|
-
$$ |\\_$$ |$$ | \\__|$$$$$$$ |$$ / $$ / $$ |\\$$$$$$\\ $$ | $$$$$$$ | \\$$$$ /
|
|
217
|
-
$$ | $$ |$$ | $$ __$$ |$$ | $$ | $$ | \\____$$\\ $$ |$$\\ $$ __$$ | $$ $$<
|
|
218
|
-
\\$$$$$$ |$$ | \\$$$$$$$ |$$ | $$ | $$ |$$$$$$$ | \\$$$$ |\\$$$$$$$ |$$ /\\$$\\
|
|
219
|
-
\\______/ \\__| \\_______|\\__| \\__| \\__|\\_______/ \\____/ \\_______|\\__/ \\__|
|
|
220
|
-
|
|
221
|
-
`
|
|
222
|
-
console.log(banner)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async function promptForProjectName(): Promise<string> {
|
|
226
|
-
const response = await enquirer.prompt<{ projectName: string }>({
|
|
227
|
-
type: `input`,
|
|
228
|
-
name: `projectName`,
|
|
229
|
-
message: `What is your project named?`,
|
|
230
|
-
initial: ``,
|
|
231
|
-
validate: (value: string) => {
|
|
232
|
-
if (value == ``) {
|
|
233
|
-
return true
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (!/^[a-z0-9-_]+$/i.test(value)) {
|
|
237
|
-
return `Project name can only contain letters, numbers, hyphens, and underscores`
|
|
238
|
-
}
|
|
239
|
-
return true
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
return response.projectName
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
async function promptForConfiguration(options?: CreateOptions): Promise<{
|
|
247
|
-
packageManager: PackageManager
|
|
248
|
-
runtime: Runtime
|
|
249
|
-
eslint: boolean
|
|
250
|
-
}> {
|
|
251
|
-
const questions: any[] = []
|
|
252
|
-
|
|
253
|
-
// Package manager selection
|
|
254
|
-
if (!options?.useBun && !options?.useNpm && !options?.usePnpm && !options?.useYarn) {
|
|
255
|
-
questions.push({
|
|
256
|
-
type: `select`,
|
|
257
|
-
name: `packageManager`,
|
|
258
|
-
message: `Which package manager would you like to use?`,
|
|
259
|
-
choices: [
|
|
260
|
-
{ name: `bun`, message: `Bun (recommended)`, value: `bun` },
|
|
261
|
-
{ name: `npm`, message: `npm`, value: `npm` },
|
|
262
|
-
{ name: `pnpm`, message: `pnpm`, value: `pnpm` },
|
|
263
|
-
{ name: `yarn`, message: `Yarn`, value: `yarn` }
|
|
264
|
-
],
|
|
265
|
-
initial: 0
|
|
266
|
-
})
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Runtime selection
|
|
270
|
-
questions.push({
|
|
271
|
-
type: `select`,
|
|
272
|
-
name: `runtime`,
|
|
273
|
-
message: `Which runtime would you like to use?`,
|
|
274
|
-
choices: [
|
|
275
|
-
{ name: `bun`, message: `Bun (recommended)`, value: `bun` },
|
|
276
|
-
{ name: `node`, message: `Node.js`, value: `node` }
|
|
277
|
-
],
|
|
278
|
-
initial: 0
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
// ESLint
|
|
282
|
-
if (options?.eslint === undefined) {
|
|
283
|
-
questions.push({
|
|
284
|
-
type: `confirm`,
|
|
285
|
-
name: `eslint`,
|
|
286
|
-
message: `Would you like to use ESLint?`,
|
|
287
|
-
initial: true
|
|
288
|
-
})
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const answers = await enquirer.prompt<{
|
|
292
|
-
packageManager?: PackageManager
|
|
293
|
-
runtime: Runtime
|
|
294
|
-
eslint?: boolean
|
|
295
|
-
}>(questions)
|
|
296
|
-
|
|
297
|
-
return {
|
|
298
|
-
packageManager: answers.packageManager || detectPackageManager(options),
|
|
299
|
-
runtime: answers.runtime,
|
|
300
|
-
eslint: answers.eslint !== undefined ? answers.eslint : options?.eslint || false
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function detectPackageManager(options?: CreateOptions): PackageManager {
|
|
305
|
-
if (options?.useBun) return `bun`
|
|
306
|
-
if (options?.useNpm) return `npm`
|
|
307
|
-
if (options?.usePnpm) return `pnpm`
|
|
308
|
-
if (options?.useYarn) return `yarn`
|
|
309
|
-
|
|
310
|
-
// Try to detect from environment
|
|
311
|
-
const userAgent = process.env.npm_config_user_agent || ``
|
|
312
|
-
if (userAgent.includes(`bun`)) return `bun`
|
|
313
|
-
if (userAgent.includes(`pnpm`)) return `pnpm`
|
|
314
|
-
if (userAgent.includes(`yarn`)) return `yarn`
|
|
315
|
-
|
|
316
|
-
// Default to bun
|
|
317
|
-
return `bun`
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
async function generateProjectStructure(projectPath: string, projectName: string, packageManager: PackageManager, runtime: Runtime) {
|
|
321
|
-
log.info(`📁 Creating project structure...`)
|
|
322
|
-
|
|
323
|
-
const templatesPath = path.join(__dirname, `templates`)
|
|
324
|
-
const commands = getPackageManagerCommands(packageManager)
|
|
325
|
-
|
|
326
|
-
// Prepare template variables
|
|
327
|
-
const variables = {
|
|
328
|
-
projectName,
|
|
329
|
-
installCmd: commands.install,
|
|
330
|
-
runDevCmd: commands.dev,
|
|
331
|
-
runStartCmd: commands.start,
|
|
332
|
-
runPm2StartCmd: commands[`pm2:start`],
|
|
333
|
-
runPm2StopCmd: commands[`pm2:stop`],
|
|
334
|
-
runPm2RestartCmd: commands[`pm2:restart`],
|
|
335
|
-
runPm2DeleteCmd: commands[`pm2:delete`],
|
|
336
|
-
runPm2LogsCmd: commands[`pm2:logs`],
|
|
337
|
-
runPm2MonitCmd: commands[`pm2:monit`]
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
log.info(`📝 Copying template files...`)
|
|
341
|
-
|
|
342
|
-
// Copy entire template structure recursively
|
|
343
|
-
copyDirectorySync(templatesPath, projectPath, variables)
|
|
344
|
-
|
|
345
|
-
// Create logs directory
|
|
346
|
-
fs.mkdirSync(path.join(projectPath, `logs`), { recursive: true })
|
|
347
|
-
fs.writeFileSync(path.join(projectPath, `logs/.gitkeep`), ``)
|
|
348
|
-
|
|
349
|
-
// Create .env.example as copy of .env
|
|
350
|
-
const envPath = path.join(projectPath, `.env`)
|
|
351
|
-
const envExamplePath = path.join(projectPath, `.env.example`)
|
|
352
|
-
if (fs.existsSync(envPath)) {
|
|
353
|
-
fs.copyFileSync(envPath, envExamplePath)
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Remove bunfig.toml if runtime is NOT bun
|
|
357
|
-
if (runtime !== `bun`) {
|
|
358
|
-
const bunfigPath = path.join(projectPath, `bunfig.toml`)
|
|
359
|
-
if (fs.existsSync(bunfigPath)) {
|
|
360
|
-
fs.unlinkSync(bunfigPath)
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
log.success(`✅ All files generated!`)
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
async function setupEslintConfig(projectPath: string, packageManager: PackageManager): Promise<void> {
|
|
368
|
-
log.info(``)
|
|
369
|
-
log.info(`🔍 Setting up ESLint...`)
|
|
370
|
-
|
|
371
|
-
return new Promise((resolve, reject) => {
|
|
372
|
-
// Use npm init @eslint/config with --yes flag for default configuration
|
|
373
|
-
const initProcess = spawn(`npm`, [`init`, `@eslint/config@latest`, `--`, `--yes`], {
|
|
374
|
-
cwd: projectPath,
|
|
375
|
-
stdio: `inherit`,
|
|
376
|
-
shell: true
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
initProcess.on(`close`, (code) => {
|
|
380
|
-
if (code === 0) {
|
|
381
|
-
log.success(`✅ ESLint configured successfully!`)
|
|
382
|
-
resolve()
|
|
383
|
-
} else {
|
|
384
|
-
log.warn(`⚠️ ESLint setup failed. You can set it up later with: npm init @eslint/config`)
|
|
385
|
-
resolve() // Don't fail the whole process
|
|
386
|
-
}
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
initProcess.on(`error`, (error) => {
|
|
390
|
-
log.warn(`⚠️ ESLint setup failed: ${error.message}`)
|
|
391
|
-
log.info(`You can set it up later with: npm init @eslint/config`)
|
|
392
|
-
resolve() // Don't fail the whole process
|
|
393
|
-
})
|
|
394
|
-
})
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
export default class {
|
|
398
|
-
public runtime: Runtime | undefined
|
|
399
|
-
public projectPath: string | undefined
|
|
400
|
-
public projectName: string | undefined
|
|
401
|
-
public packageManager: PackageManager | undefined
|
|
402
|
-
public constructor() {
|
|
403
|
-
//
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
public printBanner() {
|
|
407
|
-
const banner = `
|
|
408
|
-
$$$$$$\\ $$\\
|
|
409
|
-
$$ __$$\\ $$ |
|
|
410
|
-
$$ / \\__| $$$$$$\\ $$$$$$\\ $$$$$$\\$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\ $$\\ $$\\
|
|
411
|
-
$$ |$$$$\\ $$ __$$\\ \\____$$\\ $$ _$$ _$$\\ $$ _____|\\_$$ _| \\____$$\\ \\$$\\ $$ |
|
|
412
|
-
$$ |\\_$$ |$$ | \\__|$$$$$$$ |$$ / $$ / $$ |\\$$$$$$\\ $$ | $$$$$$$ | \\$$$$ /
|
|
413
|
-
$$ | $$ |$$ | $$ __$$ |$$ | $$ | $$ | \\____$$\\ $$ |$$\\ $$ __$$ | $$ $$<
|
|
414
|
-
\\$$$$$$ |$$ | \\$$$$$$$ |$$ | $$ | $$ |$$$$$$$ | \\$$$$ |\\$$$$$$$ |$$ /\\$$\\
|
|
415
|
-
\\______/ \\__| \\_______|\\__| \\__| \\__|\\_______/ \\____/ \\_______|\\__/ \\__|
|
|
416
|
-
|
|
417
|
-
`
|
|
418
|
-
console.log(banner)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
public detectPackageManager(options?: CreateOptions): PackageManager {
|
|
422
|
-
if (options?.useBun) return (this.packageManager = `bun`)
|
|
423
|
-
if (options?.useNpm) return (this.packageManager = `npm`)
|
|
424
|
-
if (options?.usePnpm) return (this.packageManager = `pnpm`)
|
|
425
|
-
if (options?.useYarn) return (this.packageManager = `yarn`)
|
|
426
|
-
|
|
427
|
-
// Try to detect from environment
|
|
428
|
-
const userAgent = process.env.npm_config_user_agent || ``
|
|
429
|
-
if (userAgent.includes(`bun`)) return (this.packageManager = `bun`)
|
|
430
|
-
if (userAgent.includes(`pnpm`)) return (this.packageManager = `pnpm`)
|
|
431
|
-
if (userAgent.includes(`yarn`)) return (this.packageManager = `yarn`)
|
|
432
|
-
|
|
433
|
-
// Default to bun
|
|
434
|
-
return (this.packageManager = `bun`)
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Recursively copy directory structure
|
|
439
|
-
*/
|
|
440
|
-
public copyDirectorySync(source: string, destination: string, variables: Record<string, string> = {}) {
|
|
441
|
-
// Create destination directory if it doesn't exist
|
|
442
|
-
if (!fs.existsSync(destination)) {
|
|
443
|
-
fs.mkdirSync(destination, { recursive: true })
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Read all items in source directory
|
|
447
|
-
const items = fs.readdirSync(source, { withFileTypes: true })
|
|
448
|
-
|
|
449
|
-
for (const item of items) {
|
|
450
|
-
const sourcePath = path.join(source, item.name)
|
|
451
|
-
const destPath = path.join(destination, item.name)
|
|
452
|
-
|
|
453
|
-
if (item.isDirectory()) {
|
|
454
|
-
// Recursively copy subdirectories
|
|
455
|
-
copyDirectorySync(sourcePath, destPath, variables)
|
|
456
|
-
} else if (item.isFile()) {
|
|
457
|
-
// Copy file and replace variables if needed
|
|
458
|
-
let content = fs.readFileSync(sourcePath, `utf-8`)
|
|
459
|
-
|
|
460
|
-
// Replace template variables in content
|
|
461
|
-
content = replaceTemplateVariables(content, variables)
|
|
462
|
-
|
|
463
|
-
fs.writeFileSync(destPath, content)
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Replace template variables in content
|
|
470
|
-
*/
|
|
471
|
-
public replaceTemplateVariables(content: string, variables: Record<string, string>): string {
|
|
472
|
-
let result = content
|
|
473
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
474
|
-
result = result.replace(new RegExp(`{{${key}}}`, `g`), value)
|
|
475
|
-
}
|
|
476
|
-
return result
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Get package manager commands
|
|
481
|
-
*/
|
|
482
|
-
public getPackageManagerCommands(packageManager: PackageManager) {
|
|
483
|
-
const commands = {
|
|
484
|
-
bun: {
|
|
485
|
-
install: `bun install`,
|
|
486
|
-
dev: `bun dev`,
|
|
487
|
-
start: `bun start`,
|
|
488
|
-
"pm2:start": `bun run pm2:start`,
|
|
489
|
-
"pm2:stop": `bun run pm2:stop`,
|
|
490
|
-
"pm2:restart": `bun run pm2:restart`,
|
|
491
|
-
"pm2:delete": `bun run pm2:delete`,
|
|
492
|
-
"pm2:logs": `bun run pm2:logs`,
|
|
493
|
-
"pm2:monit": `bun run pm2:monit`
|
|
494
|
-
},
|
|
495
|
-
npm: {
|
|
496
|
-
install: `npm install`,
|
|
497
|
-
dev: `npm run dev`,
|
|
498
|
-
start: `npm start`,
|
|
499
|
-
"pm2:start": `npm run pm2:start`,
|
|
500
|
-
"pm2:stop": `npm run pm2:stop`,
|
|
501
|
-
"pm2:restart": `npm run pm2:restart`,
|
|
502
|
-
"pm2:delete": `npm run pm2:delete`,
|
|
503
|
-
"pm2:logs": `npm run pm2:logs`,
|
|
504
|
-
"pm2:monit": `npm run pm2:monit`
|
|
505
|
-
},
|
|
506
|
-
pnpm: {
|
|
507
|
-
install: `pnpm install`,
|
|
508
|
-
dev: `pnpm dev`,
|
|
509
|
-
start: `pnpm start`,
|
|
510
|
-
"pm2:start": `pnpm pm2:start`,
|
|
511
|
-
"pm2:stop": `pnpm pm2:stop`,
|
|
512
|
-
"pm2:restart": `pnpm pm2:restart`,
|
|
513
|
-
"pm2:delete": `pnpm pm2:delete`,
|
|
514
|
-
"pm2:logs": `pnpm pm2:logs`,
|
|
515
|
-
"pm2:monit": `pnpm pm2:monit`
|
|
516
|
-
},
|
|
517
|
-
yarn: {
|
|
518
|
-
install: `yarn install`,
|
|
519
|
-
dev: `yarn dev`,
|
|
520
|
-
start: `yarn start`,
|
|
521
|
-
"pm2:start": `yarn pm2:start`,
|
|
522
|
-
"pm2:stop": `yarn pm2:stop`,
|
|
523
|
-
"pm2:restart": `yarn pm2:restart`,
|
|
524
|
-
"pm2:delete": `yarn pm2:delete`,
|
|
525
|
-
"pm2:logs": `yarn pm2:logs`,
|
|
526
|
-
"pm2:monit": `yarn pm2:monit`
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
return commands[packageManager]
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
public async promptForProjectName(): Promise<string> {
|
|
533
|
-
const response = await enquirer.prompt<{ projectName: string }>({
|
|
534
|
-
type: `input`,
|
|
535
|
-
name: `projectName`,
|
|
536
|
-
message: `What is your project named?`,
|
|
537
|
-
initial: ``,
|
|
538
|
-
validate: (value: string) => {
|
|
539
|
-
if (value == ``) {
|
|
540
|
-
return true
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (!/^[a-z0-9-_]+$/i.test(value)) {
|
|
544
|
-
return `Project name can only contain letters, numbers, hyphens, and underscores`
|
|
545
|
-
}
|
|
546
|
-
return true
|
|
547
|
-
}
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
return response.projectName
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
public async promptForConfiguration(options?: CreateOptions): Promise<{
|
|
554
|
-
packageManager: PackageManager
|
|
555
|
-
runtime: Runtime
|
|
556
|
-
eslint: boolean
|
|
557
|
-
}> {
|
|
558
|
-
const questions: any[] = []
|
|
559
|
-
|
|
560
|
-
// Package manager selection
|
|
561
|
-
if (!options?.useBun && !options?.useNpm && !options?.usePnpm && !options?.useYarn) {
|
|
562
|
-
questions.push({
|
|
563
|
-
type: `select`,
|
|
564
|
-
name: `packageManager`,
|
|
565
|
-
message: `Which package manager would you like to use?`,
|
|
566
|
-
choices: [
|
|
567
|
-
{ name: `bun`, message: `Bun (recommended)`, value: `bun` },
|
|
568
|
-
{ name: `npm`, message: `npm`, value: `npm` },
|
|
569
|
-
{ name: `pnpm`, message: `pnpm`, value: `pnpm` },
|
|
570
|
-
{ name: `yarn`, message: `Yarn`, value: `yarn` }
|
|
571
|
-
],
|
|
572
|
-
initial: 0
|
|
573
|
-
})
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Runtime selection
|
|
577
|
-
questions.push({
|
|
578
|
-
type: `select`,
|
|
579
|
-
name: `runtime`,
|
|
580
|
-
message: `Which runtime would you like to use?`,
|
|
581
|
-
choices: [
|
|
582
|
-
{ name: `bun`, message: `Bun (recommended)`, value: `bun` },
|
|
583
|
-
{ name: `node`, message: `Node.js`, value: `node` }
|
|
584
|
-
],
|
|
585
|
-
initial: 0
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
// ESLint
|
|
589
|
-
if (options?.eslint === undefined) {
|
|
590
|
-
questions.push({
|
|
591
|
-
type: `confirm`,
|
|
592
|
-
name: `eslint`,
|
|
593
|
-
message: `Would you like to use ESLint?`,
|
|
594
|
-
initial: true
|
|
595
|
-
})
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const answers = await enquirer.prompt<{
|
|
599
|
-
packageManager?: PackageManager
|
|
600
|
-
runtime: Runtime
|
|
601
|
-
eslint?: boolean
|
|
602
|
-
}>(questions)
|
|
603
|
-
|
|
604
|
-
return {
|
|
605
|
-
packageManager: answers.packageManager || detectPackageManager(options),
|
|
606
|
-
runtime: answers.runtime,
|
|
607
|
-
eslint: answers.eslint !== undefined ? answers.eslint : options?.eslint || false
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
public async generateProjectStructure(projectPath: string, projectName: string, packageManager: PackageManager, runtime: Runtime) {
|
|
612
|
-
log.info(`📁 Creating project structure...`)
|
|
613
|
-
|
|
614
|
-
const templatesPath = path.join(__dirname, `templates`)
|
|
615
|
-
const commands = getPackageManagerCommands(packageManager)
|
|
616
|
-
|
|
617
|
-
// Prepare template variables
|
|
618
|
-
const variables = {
|
|
619
|
-
projectName,
|
|
620
|
-
installCmd: commands.install,
|
|
621
|
-
runDevCmd: commands.dev,
|
|
622
|
-
runStartCmd: commands.start,
|
|
623
|
-
runPm2StartCmd: commands[`pm2:start`],
|
|
624
|
-
runPm2StopCmd: commands[`pm2:stop`],
|
|
625
|
-
runPm2RestartCmd: commands[`pm2:restart`],
|
|
626
|
-
runPm2DeleteCmd: commands[`pm2:delete`],
|
|
627
|
-
runPm2LogsCmd: commands[`pm2:logs`],
|
|
628
|
-
runPm2MonitCmd: commands[`pm2:monit`]
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
log.info(`📝 Copying template files...`)
|
|
632
|
-
|
|
633
|
-
// Copy entire template structure recursively
|
|
634
|
-
copyDirectorySync(templatesPath, projectPath, variables)
|
|
635
|
-
|
|
636
|
-
// Create logs directory
|
|
637
|
-
fs.mkdirSync(path.join(projectPath, `logs`), { recursive: true })
|
|
638
|
-
fs.writeFileSync(path.join(projectPath, `logs/.gitkeep`), ``)
|
|
639
|
-
|
|
640
|
-
// Create .env.example as copy of .env
|
|
641
|
-
const envPath = path.join(projectPath, `.env`)
|
|
642
|
-
const envExamplePath = path.join(projectPath, `.env.example`)
|
|
643
|
-
if (fs.existsSync(envPath)) {
|
|
644
|
-
fs.copyFileSync(envPath, envExamplePath)
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
// Remove bunfig.toml if runtime is NOT bun
|
|
648
|
-
if (runtime !== `bun`) {
|
|
649
|
-
const bunfigPath = path.join(projectPath, `bunfig.toml`)
|
|
650
|
-
if (fs.existsSync(bunfigPath)) {
|
|
651
|
-
fs.unlinkSync(bunfigPath)
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
log.success(`✅ All files generated!`)
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
public async setupEslintConfig(projectPath: string, packageManager: PackageManager): Promise<void> {
|
|
659
|
-
log.info(``)
|
|
660
|
-
log.info(`🔍 Setting up ESLint...`)
|
|
661
|
-
|
|
662
|
-
return new Promise((resolve, reject) => {
|
|
663
|
-
// Use npm init @eslint/config with --yes flag for default configuration
|
|
664
|
-
const initProcess = spawn(`npm`, [`init`, `@eslint/config@latest`, `--`, `--yes`], {
|
|
665
|
-
cwd: projectPath,
|
|
666
|
-
stdio: `inherit`,
|
|
667
|
-
shell: true
|
|
668
|
-
})
|
|
669
|
-
|
|
670
|
-
initProcess.on(`close`, (code) => {
|
|
671
|
-
if (code === 0) {
|
|
672
|
-
log.success(`✅ ESLint configured successfully!`)
|
|
673
|
-
resolve()
|
|
674
|
-
} else {
|
|
675
|
-
log.warn(`⚠️ ESLint setup failed. You can set it up later with: npm init @eslint/config`)
|
|
676
|
-
resolve() // Don't fail the whole process
|
|
677
|
-
}
|
|
678
|
-
})
|
|
679
|
-
|
|
680
|
-
initProcess.on(`error`, (error) => {
|
|
681
|
-
log.warn(`⚠️ ESLint setup failed: ${error.message}`)
|
|
682
|
-
log.info(`You can set it up later with: npm init @eslint/config`)
|
|
683
|
-
resolve() // Don't fail the whole process
|
|
684
|
-
})
|
|
685
|
-
})
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
public async createProject(projectDirectory?: string, options?: CreateOptions) {
|
|
689
|
-
// Display ASCII art banner
|
|
690
|
-
printBanner()
|
|
691
|
-
|
|
692
|
-
// Get project name - if not provided, prompt or use current directory
|
|
693
|
-
let projectName = projectDirectory
|
|
694
|
-
let projectPath: string
|
|
695
|
-
|
|
696
|
-
if (!projectName) {
|
|
697
|
-
if (options?.yes) {
|
|
698
|
-
// Use current directory name like npm init
|
|
699
|
-
projectName = path.basename(process.cwd())
|
|
700
|
-
projectPath = process.cwd()
|
|
701
|
-
|
|
702
|
-
// Check if current directory is empty
|
|
703
|
-
const files = fs.readdirSync(projectPath)
|
|
704
|
-
if (files.length > 0) {
|
|
705
|
-
log.error(`Current directory is not empty!`)
|
|
706
|
-
process.exit(1)
|
|
707
|
-
}
|
|
708
|
-
} else {
|
|
709
|
-
projectName = await promptForProjectName()
|
|
710
|
-
projectPath = path.resolve(process.cwd(), projectName)
|
|
711
|
-
|
|
712
|
-
// Check if directory already exists
|
|
713
|
-
if (fs.existsSync(projectPath) && projectName != ``) {
|
|
714
|
-
log.error(`Directory "${projectName}" already exists!`)
|
|
715
|
-
process.exit(1)
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (projectName != ``) {
|
|
719
|
-
// Create project directory
|
|
720
|
-
fs.mkdirSync(projectPath, { recursive: true })
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
} else {
|
|
724
|
-
projectPath = path.resolve(process.cwd(), projectName)
|
|
725
|
-
|
|
726
|
-
// Check if directory already exists
|
|
727
|
-
if (fs.existsSync(projectPath)) {
|
|
728
|
-
log.error(`Directory "${projectName}" already exists!`)
|
|
729
|
-
process.exit(1)
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// Create project directory
|
|
733
|
-
fs.mkdirSync(projectPath, { recursive: true })
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
log.info(`Creating project: ${path.basename(projectPath)}`)
|
|
737
|
-
log.info(`Location: ${projectPath}`)
|
|
738
|
-
|
|
739
|
-
// Prompt for configuration if not in --yes mode
|
|
740
|
-
let packageManager: PackageManager
|
|
741
|
-
let runtime: Runtime
|
|
742
|
-
let setupEslint: boolean
|
|
743
|
-
|
|
744
|
-
if (options?.yes) {
|
|
745
|
-
packageManager = this.detectPackageManager(options)
|
|
746
|
-
runtime = packageManager === `bun` ? `bun` : `node`
|
|
747
|
-
setupEslint = false
|
|
748
|
-
|
|
749
|
-
log.info(`Using defaults - Package Manager: ${packageManager}, Runtime: ${runtime}, ESLint: No`)
|
|
750
|
-
} else {
|
|
751
|
-
const config = await promptForConfiguration(options)
|
|
752
|
-
packageManager = config.packageManager
|
|
753
|
-
runtime = config.runtime
|
|
754
|
-
setupEslint = config.eslint
|
|
755
|
-
|
|
756
|
-
log.info(`Configuration - Package Manager: ${packageManager}, Runtime: ${runtime}, ESLint: ${setupEslint ? `Yes` : `No`}`)
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Generate project structure
|
|
760
|
-
log.info(`Generating project structure...`)
|
|
761
|
-
await generateProjectStructure(projectPath, projectName, packageManager, runtime)
|
|
762
|
-
|
|
763
|
-
// Setup ESLint if requested
|
|
764
|
-
if (setupEslint) {
|
|
765
|
-
log.info(`Setting up ESLint...`)
|
|
766
|
-
await setupEslintConfig(projectPath, packageManager)
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const commands = getPackageManagerCommands(packageManager)
|
|
770
|
-
|
|
771
|
-
log.success(`Project created successfully!`)
|
|
772
|
-
log.info(``)
|
|
773
|
-
log.info(`Next steps:`)
|
|
774
|
-
if (projectDirectory) {
|
|
775
|
-
log.info(` cd ${projectName}`)
|
|
776
|
-
}
|
|
777
|
-
log.info(` ${commands.install}`)
|
|
778
|
-
log.info(` ${commands.dev}`)
|
|
779
|
-
log.info(``)
|
|
780
|
-
log.info(`For production:`)
|
|
781
|
-
log.info(` ${commands[`pm2:start`]}`)
|
|
782
|
-
}
|
|
783
|
-
}
|