create-gramstax 0.0.1

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