create-discordjs-nextgen 0.1.0 → 0.3.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 CHANGED
@@ -11,7 +11,7 @@ npx create-discordjs-nextgen my-bot
11
11
  The CLI asks for:
12
12
 
13
13
  - Language: `JavaScript` or `TypeScript`
14
- - Plugins: `Voice`
14
+ - Plugins: `Voice`, `JSX`
15
15
  - Template: `Starter Kit`, `Basic`, `Advanced`
16
16
 
17
17
  ## Templates
@@ -49,5 +49,6 @@ Creates a fuller example structure:
49
49
  ## Notes
50
50
 
51
51
  - `Voice` adds `discordjs-nextgen-voice`
52
- - TypeScript uses `src/` and adds `ts-node`, `typescript`, `@types/node`
52
+ - `JSX` adds `discordjs-nextgen-jsx`, injects `app.use(new JSXPlugin())`, and creates one example prefix command
53
+ - TypeScript adds `ts-node`, `typescript`, `@types/node`
53
54
  - Generated code follows the current `discordjs-nextgen` API
@@ -11,62 +11,70 @@ import pc from "picocolors";
11
11
  const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = path.dirname(__filename);
13
13
  const templatesRoot = path.resolve(__dirname, "..", "templates");
14
+ const argv = process.argv.slice(2);
14
15
 
15
16
  async function main() {
16
17
  console.log("");
17
18
  p.intro(`${pc.bgBlue(pc.white(" NEXTGEN-CLI "))} ${pc.dim("Modern Discord.js Framework")}`);
18
19
 
19
- const projectName = await p.text({
20
- message: "Proje adını ne koyalım?",
20
+ const cliProjectName = argv.find((arg) => !arg.startsWith("-"));
21
+ const forceJsx = argv.includes("--jsx");
22
+
23
+ const projectName = cliProjectName ?? await p.text({
24
+ message: "Proje adini ne koyalim?",
21
25
  placeholder: "my-nextgen-bot",
22
26
  validate(value) {
23
- if (value.length === 0) return "Lütfen bir isim girin!";
24
- if (value.includes(" ")) return "Proje isminde boşluk olamaz.";
27
+ if (value.length === 0) return "Lutfen bir isim girin.";
28
+ if (value.includes(" ")) return "Proje isminde bosluk olamaz.";
25
29
  },
26
30
  });
27
31
 
28
32
  if (p.isCancel(projectName)) {
29
- p.cancel("İşlem iptal edildi.");
33
+ p.cancel("Islem iptal edildi.");
30
34
  process.exit(0);
31
35
  }
32
36
 
33
37
  const language = await p.select({
34
38
  message: "Hangi dili tercih edersiniz?",
35
39
  options: [
36
- { value: "js", label: "JavaScript", hint: "Esnek ve hızlı" },
37
- { value: "ts", label: "TypeScript", hint: "Tip güvenliği (Önerilen)" },
40
+ { value: "js", label: "JavaScript", hint: "Esnek ve hizli" },
41
+ { value: "ts", label: "TypeScript", hint: "Tip guvenligi" },
38
42
  ],
39
43
  });
40
44
 
41
45
  if (p.isCancel(language)) {
42
- p.cancel("İşlem iptal edildi.");
46
+ p.cancel("Islem iptal edildi.");
43
47
  process.exit(0);
44
48
  }
45
49
 
46
50
  const plugins = await p.multiselect({
47
- message: "Eklemek istediğiniz pluginleri seçin (Boşluk ile işaretle, Enter ile onayla):",
51
+ message: "Eklemek istediginiz pluginleri secin:",
48
52
  options: [
49
- { value: "voice", label: "Voice Support", hint: "Müzik/Ses sistemleri için" },
53
+ { value: "voice", label: "Voice Support", hint: "Muzik ve ses sistemleri" },
54
+ { value: "jsx", label: "JSX Support", hint: "discordjs-nextgen-jsx ile JSX komutlar" },
55
+ { value: "cache", label: "Cache (Sade)", hint: "MemoryAdapter kullanır" },
56
+ { value: "cache-redis", label: "Cache (with Redis)", hint: "RedisAdapter + ioredis kullanır" },
50
57
  ],
58
+ initialValues: forceJsx ? ["jsx"] : [],
51
59
  required: false,
52
60
  });
53
61
 
54
62
  if (p.isCancel(plugins)) {
55
- p.cancel("İşlem iptal edildi.");
63
+ p.cancel("Islem iptal edildi.");
56
64
  process.exit(0);
57
65
  }
58
66
 
59
67
  const template = await p.select({
60
- message: "Hangi şablonla başlamak istersiniz?",
68
+ message: "Hangi sablonla baslamak istersiniz?",
61
69
  options: [
62
- { value: "starter", label: "Starter Kit", hint: "Sadece ana dosya (Minimum)" },
63
- { value: "basic", label: "Basic", hint: "Klasör yapısı & Basit komutlar" },
64
- { value: "advanced", label: "Advanced", hint: "Tam teşekküllü profesyonel yapı" },
70
+ { value: "starter", label: "Starter Kit", hint: "Minimum kurulum" },
71
+ { value: "basic", label: "Basic", hint: "Basit komutlar ve olaylar" },
72
+ { value: "advanced", label: "Advanced", hint: "Buttons, modals, selects dahil" },
65
73
  ],
66
74
  });
67
75
 
68
76
  if (p.isCancel(template)) {
69
- p.cancel("İşlem iptal edildi.");
77
+ p.cancel("Islem iptal edildi.");
70
78
  process.exit(0);
71
79
  }
72
80
 
@@ -77,8 +85,8 @@ async function main() {
77
85
 
78
86
  try {
79
87
  if (fs.existsSync(targetDir) && (await fsp.readdir(targetDir)).length > 0) {
80
- s.stop(pc.red("Hata!"));
81
- p.log.error(`Hata: '${projectName}' klasörü zaten var ve boş değil.`);
88
+ s.stop(pc.red("Hata"));
89
+ p.log.error(`'${projectName}' klasoru zaten var ve bos degil.`);
82
90
  process.exit(1);
83
91
  }
84
92
 
@@ -88,33 +96,53 @@ async function main() {
88
96
 
89
97
  await generateProject(targetDir, { projectName, language, template, plugins });
90
98
 
91
- s.stop(pc.green("Proje başarıyla oluşturuldu!"));
99
+ s.stop(pc.green("Proje basariyla olusturuldu."));
92
100
 
93
101
  const shouldInstall = await p.confirm({
94
- message: "Bağımlılıkları şimdi kurmak ister misiniz?",
102
+ message: "Bagimliliklari simdi kurmak ister misiniz?",
95
103
  initialValue: true,
96
104
  });
97
105
 
98
106
  if (shouldInstall) {
99
107
  const installSpinner = p.spinner();
100
- installSpinner.start(pc.yellow("Paketler yükleniyor..."));
108
+ installSpinner.start(pc.yellow("Paketler yukleniyor..."));
101
109
  const success = await runInstall(targetDir);
102
110
  if (success) {
103
- installSpinner.stop(pc.green("Kurulum tamamlandı!"));
111
+ installSpinner.stop(pc.green("Kurulum tamamlandi."));
104
112
  } else {
105
- installSpinner.stop(pc.red("Kurulum başarısız oldu."));
113
+ installSpinner.stop(pc.red("Kurulum basarisiz oldu."));
106
114
  }
107
115
  }
108
116
 
109
- p.note(
110
- `cd ${projectName}\n${!shouldInstall ? "npm install\n" : ""}npm run dev`,
111
- "Sıradaki Adımlar"
112
- );
117
+ const runCommand = language === "ts" ? "npm run dev" : "npm run dev";
118
+ p.note(`cd ${projectName}\n${!shouldInstall ? "npm install\n" : ""}${runCommand}`, "Siradaki Adimlar");
113
119
 
114
- p.outro(pc.blue("İyi kodlamalar! 🚀"));
120
+ const pluginNotes = [];
121
+ if (plugins.includes("jsx")) {
122
+ pluginNotes.push(
123
+ language === "ts"
124
+ ? "JSX aktif: tsconfig.json yazildi, index dosyasina JSXPlugin eklendi, commands/prefix/hello.tsx olusturuldu."
125
+ : "JSX aktif: jsconfig.json yazildi, index dosyasina JSXPlugin eklendi, commands/prefix/hello.jsx olusturuldu."
126
+ );
127
+ }
128
+ if (plugins.includes("cache") || plugins.includes("cache-redis")) {
129
+ const isRedis = plugins.includes("cache-redis");
130
+ pluginNotes.push(
131
+ isRedis
132
+ ? "Cache (Redis) aktif: ioredis kuruldu, index dosyasina RedisAdapter eklendi."
133
+ : "Cache (Sade) aktif: index dosyasina MemoryAdapter eklendi."
134
+ );
135
+ if (template === "advanced") {
136
+ pluginNotes.push(`Advanced sablonu secildigi icin commands/prefix/money.${extension} ornek komutu eklendi.`);
137
+ }
138
+ }
139
+ if (pluginNotes.length > 0) {
140
+ p.note(pluginNotes.join("\n"), "Bilgi");
141
+ }
115
142
 
143
+ p.outro(pc.blue("Iyi kodlamalar."));
116
144
  } catch (err) {
117
- s.stop(pc.red("Bir hata oluştu!"));
145
+ s.stop(pc.red("Bir hata olustu."));
118
146
  console.error(err);
119
147
  process.exit(1);
120
148
  }
@@ -134,50 +162,97 @@ async function generateProject(targetDir, config) {
134
162
  version: "1.0.0",
135
163
  private: true,
136
164
  type: "module",
137
- scripts: config.language === "ts" ? {
138
- dev: "ts-node index.ts",
139
- start: "ts-node index.ts",
140
- build: "tsc"
141
- } : {
142
- dev: "node index.js",
143
- start: "node index.js"
144
- },
165
+ scripts: config.language === "ts"
166
+ ? {
167
+ dev: "ts-node index.ts",
168
+ start: "ts-node index.ts",
169
+ build: "tsc",
170
+ }
171
+ : {
172
+ dev: "node index.js",
173
+ start: "node index.js",
174
+ },
145
175
  dependencies: {
146
176
  "discordjs-nextgen": "latest",
147
- "dotenv": "^17.3.1"
148
- }
177
+ "dotenv": "^17.3.1",
178
+ },
149
179
  };
150
180
 
151
181
  if (config.language === "ts") {
152
182
  pkg.devDependencies = {
153
183
  "@types/node": "^20.12.0",
154
184
  "ts-node": "^10.9.2",
155
- "typescript": "^5.4.5"
185
+ "typescript": "^5.4.5",
156
186
  };
157
- await writeFile(targetDir, "tsconfig.json", JSON.stringify({
158
- compilerOptions: {
159
- target: "ESNext",
160
- module: "ESNext",
161
- moduleResolution: "node",
162
- esModuleInterop: true,
163
- strict: true,
164
- skipLibCheck: true,
165
- outDir: "dist"
166
- }
167
- }, null, 2));
168
187
  }
169
188
 
170
189
  if (config.plugins.includes("voice")) {
171
190
  pkg.dependencies["discordjs-nextgen-voice"] = "latest";
172
191
  }
173
192
 
193
+ if (config.plugins.includes("jsx")) {
194
+ pkg.dependencies["discordjs-nextgen-jsx"] = "latest";
195
+ }
196
+
197
+ if (config.plugins.includes("cache") || config.plugins.includes("cache-redis")) {
198
+ pkg.dependencies["discordjs-nextgen-cache"] = "latest";
199
+ if (config.plugins.includes("cache-redis")) {
200
+ pkg.dependencies["ioredis"] = "latest";
201
+ }
202
+ }
203
+
204
+ await writeLanguageConfig(targetDir, config.language, config.plugins.includes("jsx"));
174
205
  await writeFile(targetDir, "package.json", JSON.stringify(pkg, null, 2));
175
206
  await writeFile(targetDir, ".gitignore", "node_modules\n.env\ndist\n");
176
- await writeFile(targetDir, ".env", "TOKEN=YOUR_BOT_TOKEN_HERE");
207
+ await writeFile(targetDir, ".env", "TOKEN=YOUR_BOT_TOKEN_HERE\n");
177
208
 
178
209
  if (config.plugins.includes("voice")) {
179
210
  await injectVoicePlugin(targetDir, extension, config.template);
180
211
  }
212
+
213
+ if (config.plugins.includes("jsx")) {
214
+ await injectJSXPlugin(targetDir, config.language);
215
+ }
216
+
217
+ if (config.plugins.includes("cache") || config.plugins.includes("cache-redis")) {
218
+ const useRedis = config.plugins.includes("cache-redis");
219
+ await injectCachePlugin(targetDir, extension, useRedis, config.template, config.language);
220
+ }
221
+ }
222
+
223
+ async function writeLanguageConfig(targetDir, language, useJsx) {
224
+ if (language === "ts") {
225
+ const tsconfig = {
226
+ compilerOptions: {
227
+ target: "ESNext",
228
+ module: "ESNext",
229
+ moduleResolution: "node",
230
+ esModuleInterop: true,
231
+ strict: true,
232
+ skipLibCheck: true,
233
+ outDir: "dist",
234
+ },
235
+ include: ["**/*"],
236
+ };
237
+
238
+ if (useJsx) {
239
+ tsconfig.compilerOptions.jsx = "react-jsx";
240
+ tsconfig.compilerOptions.jsxImportSource = "discordjs-nextgen-jsx";
241
+ }
242
+
243
+ await writeFile(targetDir, "tsconfig.json", JSON.stringify(tsconfig, null, 2));
244
+ return;
245
+ }
246
+
247
+ if (useJsx) {
248
+ await writeFile(targetDir, "jsconfig.json", JSON.stringify({
249
+ compilerOptions: {
250
+ jsx: "react-jsx",
251
+ jsxImportSource: "discordjs-nextgen-jsx",
252
+ },
253
+ include: ["**/*"],
254
+ }, null, 2));
255
+ }
181
256
  }
182
257
 
183
258
  async function injectVoicePlugin(targetDir, extension, template) {
@@ -185,27 +260,89 @@ async function injectVoicePlugin(targetDir, extension, template) {
185
260
  let content = await fsp.readFile(entryPath, "utf8");
186
261
 
187
262
  const importLine = `import { VoicePlugin } from "discordjs-nextgen-voice";`;
188
- const useLine = `\napp.use(new VoicePlugin());`;
189
263
 
190
264
  if (!content.includes(importLine)) {
191
265
  content = `${importLine}\n${content}`;
192
266
  }
193
267
 
194
268
  if (!content.includes("new VoicePlugin()")) {
195
- content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n${useLine}`);
269
+ content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n\napp.use(new VoicePlugin());`);
196
270
  }
197
271
 
198
272
  await fsp.writeFile(entryPath, content, "utf8");
199
273
 
200
274
  if (template !== "starter") {
201
- const cmdCode = `export default {\n name: "join",\n description: "Sese girer.",\n run: async (ctx) => {\n await ctx.voice.join({ channelId: ctx.member.voice.channelId });\n }\n};`;
202
- await writeFile(targetDir, "commands/prefix/join." + extension, cmdCode);
275
+ const cmdCode = `export default {\n name: "join",\n description: "Sese girer.",\n run: async (ctx) => {\n await ctx.voice.join({ channelId: ctx.member.voice.channelId });\n }\n};\n`;
276
+ await writeFile(targetDir, `commands/prefix/join.${extension}`, cmdCode);
277
+ }
278
+ }
279
+
280
+ async function injectJSXPlugin(targetDir, language) {
281
+ const entryExtension = language === "ts" ? "ts" : "js";
282
+ const jsxExtension = language === "ts" ? "tsx" : "jsx";
283
+ const entryPath = path.join(targetDir, `index.${entryExtension}`);
284
+ let content = await fsp.readFile(entryPath, "utf8");
285
+
286
+ const importLine = `import { JSXPlugin } from "discordjs-nextgen-jsx";`;
287
+
288
+ if (!content.includes(importLine)) {
289
+ content = `${importLine}\n${content}`;
290
+ }
291
+
292
+ if (!content.includes("new JSXPlugin()")) {
293
+ content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n\napp.use(new JSXPlugin());`);
294
+ }
295
+
296
+ if (!content.includes(".prefix(")) {
297
+ content = content.replace(
298
+ /app\.setPresence\(/,
299
+ `app.prefix({\n folder: 'commands/prefix',\n prefix: '.',\n});\n\napp.setPresence(`
300
+ );
301
+ }
302
+
303
+ await fsp.writeFile(entryPath, content, "utf8");
304
+
305
+ const jsxPrefixCommand = language === "ts"
306
+ ? `import type { PrefixCommand } from 'discordjs-nextgen';\nimport { Container, TextDisplay } from 'discordjs-nextgen-jsx';\n\nconst hello: PrefixCommand = {\n name: 'hello',\n description: 'JSX example command',\n run: async (ctx) => {\n const card = (\n <Container accentColor={0x5865f2}>\n <TextDisplay content="Hello from JSX." />\n <TextDisplay content={\`Author: \${ctx.user.username}\`} />\n </Container>\n );\n\n await ctx.reply({\n components: [card],\n });\n },\n};\n\nexport default hello;\n`
307
+ : `import { Container, TextDisplay } from 'discordjs-nextgen-jsx';\n\nconst hello = {\n name: 'hello',\n description: 'JSX example command',\n run: async (ctx) => {\n const card = (\n <Container accentColor={0x5865f2}>\n <TextDisplay content="Hello from JSX." />\n <TextDisplay content={\`Author: \${ctx.user.username}\`} />\n </Container>\n );\n\n await ctx.reply({\n components: [card],\n });\n },\n};\n\nexport default hello;\n`;
308
+
309
+ await writeFile(targetDir, `commands/prefix/hello.${jsxExtension}`, jsxPrefixCommand);
310
+ }
311
+
312
+ async function injectCachePlugin(targetDir, extension, useRedis, template, language) {
313
+ const entryPath = path.join(targetDir, `index.${extension}`);
314
+ let content = await fsp.readFile(entryPath, "utf8");
315
+
316
+ const adapterName = useRedis ? "RedisAdapter" : "MemoryAdapter";
317
+ const importLine = `import { CachePlugin, ${adapterName} } from "discordjs-nextgen-cache";`;
318
+
319
+ if (!content.includes(importLine)) {
320
+ content = `${importLine}\n${content}`;
321
+ }
322
+
323
+ if (!content.includes("new CachePlugin")) {
324
+ const pluginCode = useRedis
325
+ ? `app.use(new CachePlugin({ adapter: new RedisAdapter() }));`
326
+ : `app.use(new CachePlugin({ adapter: new MemoryAdapter() }));`;
327
+
328
+ content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n\n${pluginCode}`);
329
+ }
330
+
331
+ await fsp.writeFile(entryPath, content, "utf8");
332
+
333
+ if (template === "advanced") {
334
+ const cmdCode = language === "ts"
335
+ ? `import type { PrefixCommand } from 'discordjs-nextgen';\n\nconst money: PrefixCommand = {\n name: 'money',\n description: 'Cache ile bakiye sistemi.',\n run: async (ctx) => {\n const userId = ctx.user.id;\n let user = await ctx.cache.user.get(userId) || { coins: 0 };\n user.coins += 100;\n await ctx.cache.user.set(userId, user);\n await ctx.reply(\`100 coin eklendi! Mevcut bakiyen: \${user.coins}\`);\n }\n};\n\nexport default money;\n`
336
+ : `const money = {\n name: 'money',\n description: 'Cache ile bakiye sistemi.',\n run: async (ctx) => {\n const userId = ctx.user.id;\n let user = await ctx.cache.user.get(userId) || { coins: 0 };\n user.coins += 100;\n await ctx.cache.user.set(userId, user);\n await ctx.reply(\`100 coin eklendi! Mevcut bakiyen: \${user.coins}\`);\n }\n};\n\nexport default money;\n`;
337
+
338
+ await writeFile(targetDir, `commands/prefix/money.${extension}`, cmdCode);
203
339
  }
204
340
  }
205
341
 
206
342
  async function copyTemplateDirectory(sourceDir, targetDir, context) {
207
343
  if (!fs.existsSync(sourceDir)) return;
208
344
  const entries = await fsp.readdir(sourceDir, { withFileTypes: true });
345
+
209
346
  for (const entry of entries) {
210
347
  const src = path.join(sourceDir, entry.name);
211
348
  const destName = entry.name.replace(/__EXT__/g, context.extension);
@@ -214,20 +351,21 @@ async function copyTemplateDirectory(sourceDir, targetDir, context) {
214
351
  if (entry.isDirectory()) {
215
352
  await fsp.mkdir(dest, { recursive: true });
216
353
  await copyTemplateDirectory(src, dest, context);
217
- } else {
218
- let content = await fsp.readFile(src, "utf8");
219
-
220
- if (context.language === "ts") {
221
- content = content.replace(/\/\/ \[JS\][\s\S]*?\/\/ \[\/JS\]/g, "");
222
- content = content.replace(/\/\/ \[TS\]/g, "").replace(/\/\/ \[\/TS\]/g, "");
223
- } else {
224
- content = content.replace(/\/\/ \[TS\][\s\S]*?\/\/ \[\/TS\]/g, "");
225
- content = content.replace(/\/\/ \[JS\]/g, "").replace(/\/\/ \[\/JS\]/g, "");
226
- }
354
+ continue;
355
+ }
227
356
 
228
- content = content.replaceAll("__EXT__", context.extension);
229
- await fsp.writeFile(dest, content, "utf8");
357
+ let content = await fsp.readFile(src, "utf8");
358
+
359
+ if (context.language === "ts") {
360
+ content = content.replace(/\/\/ \[JS\][\s\S]*?\/\/ \[\/JS\]/g, "");
361
+ content = content.replace(/\/\/ \[TS\]/g, "").replace(/\/\/ \[\/TS\]/g, "");
362
+ } else {
363
+ content = content.replace(/\/\/ \[TS\][\s\S]*?\/\/ \[\/TS\]/g, "");
364
+ content = content.replace(/\/\/ \[JS\]/g, "").replace(/\/\/ \[\/JS\]/g, "");
230
365
  }
366
+
367
+ content = content.replaceAll("__EXT__", context.extension);
368
+ await fsp.writeFile(dest, content, "utf8");
231
369
  }
232
370
  }
233
371
 
@@ -238,7 +376,12 @@ async function writeFile(baseDir, relPath, content) {
238
376
  }
239
377
 
240
378
  async function runInstall(targetDir) {
241
- const result = spawnSync("npm", ["install"], { cwd: targetDir, stdio: "inherit", shell: true });
379
+ const result = spawnSync("npm", ["install"], {
380
+ cwd: targetDir,
381
+ stdio: "inherit",
382
+ shell: true,
383
+ });
384
+
242
385
  return result.status === 0;
243
386
  }
244
387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-discordjs-nextgen",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Scaffold a Discord bot powered by discordjs-nextgen",
5
5
  "bin": {
6
6
  "create-discordjs-nextgen": "./bin/create-discordjs-nextgen.js"