create-discordjs-nextgen 0.1.0 → 0.2.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,68 @@ 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" },
50
55
  ],
56
+ initialValues: forceJsx ? ["jsx"] : [],
51
57
  required: false,
52
58
  });
53
59
 
54
60
  if (p.isCancel(plugins)) {
55
- p.cancel("İşlem iptal edildi.");
61
+ p.cancel("Islem iptal edildi.");
56
62
  process.exit(0);
57
63
  }
58
64
 
59
65
  const template = await p.select({
60
- message: "Hangi şablonla başlamak istersiniz?",
66
+ message: "Hangi sablonla baslamak istersiniz?",
61
67
  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ı" },
68
+ { value: "starter", label: "Starter Kit", hint: "Minimum kurulum" },
69
+ { value: "basic", label: "Basic", hint: "Basit komutlar ve olaylar" },
70
+ { value: "advanced", label: "Advanced", hint: "Buttons, modals, selects dahil" },
65
71
  ],
66
72
  });
67
73
 
68
74
  if (p.isCancel(template)) {
69
- p.cancel("İşlem iptal edildi.");
75
+ p.cancel("Islem iptal edildi.");
70
76
  process.exit(0);
71
77
  }
72
78
 
@@ -77,8 +83,8 @@ async function main() {
77
83
 
78
84
  try {
79
85
  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.`);
86
+ s.stop(pc.red("Hata"));
87
+ p.log.error(`'${projectName}' klasoru zaten var ve bos degil.`);
82
88
  process.exit(1);
83
89
  }
84
90
 
@@ -88,33 +94,42 @@ async function main() {
88
94
 
89
95
  await generateProject(targetDir, { projectName, language, template, plugins });
90
96
 
91
- s.stop(pc.green("Proje başarıyla oluşturuldu!"));
97
+ s.stop(pc.green("Proje basariyla olusturuldu."));
92
98
 
93
99
  const shouldInstall = await p.confirm({
94
- message: "Bağımlılıkları şimdi kurmak ister misiniz?",
100
+ message: "Bagimliliklari simdi kurmak ister misiniz?",
95
101
  initialValue: true,
96
102
  });
97
103
 
98
104
  if (shouldInstall) {
99
105
  const installSpinner = p.spinner();
100
- installSpinner.start(pc.yellow("Paketler yükleniyor..."));
106
+ installSpinner.start(pc.yellow("Paketler yukleniyor..."));
101
107
  const success = await runInstall(targetDir);
102
108
  if (success) {
103
- installSpinner.stop(pc.green("Kurulum tamamlandı!"));
109
+ installSpinner.stop(pc.green("Kurulum tamamlandi."));
104
110
  } else {
105
- installSpinner.stop(pc.red("Kurulum başarısız oldu."));
111
+ installSpinner.stop(pc.red("Kurulum basarisiz oldu."));
106
112
  }
107
113
  }
108
114
 
109
- p.note(
110
- `cd ${projectName}\n${!shouldInstall ? "npm install\n" : ""}npm run dev`,
111
- "Sıradaki Adımlar"
112
- );
115
+ const runCommand = language === "ts" ? "npm run dev" : "npm run dev";
116
+ p.note(`cd ${projectName}\n${!shouldInstall ? "npm install\n" : ""}${runCommand}`, "Siradaki Adimlar");
113
117
 
114
- p.outro(pc.blue("İyi kodlamalar! 🚀"));
118
+ const pluginNotes = [];
119
+ if (plugins.includes("jsx")) {
120
+ pluginNotes.push(
121
+ language === "ts"
122
+ ? "JSX aktif: tsconfig.json yazildi, index dosyasina JSXPlugin eklendi, commands/prefix/hello.tsx olusturuldu."
123
+ : "JSX aktif: jsconfig.json yazildi, index dosyasina JSXPlugin eklendi, commands/prefix/hello.jsx olusturuldu."
124
+ );
125
+ }
126
+ if (pluginNotes.length > 0) {
127
+ p.note(pluginNotes.join("\n"), "Bilgi");
128
+ }
115
129
 
130
+ p.outro(pc.blue("Iyi kodlamalar."));
116
131
  } catch (err) {
117
- s.stop(pc.red("Bir hata oluştu!"));
132
+ s.stop(pc.red("Bir hata olustu."));
118
133
  console.error(err);
119
134
  process.exit(1);
120
135
  }
@@ -134,50 +149,85 @@ async function generateProject(targetDir, config) {
134
149
  version: "1.0.0",
135
150
  private: true,
136
151
  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
- },
152
+ scripts: config.language === "ts"
153
+ ? {
154
+ dev: "ts-node index.ts",
155
+ start: "ts-node index.ts",
156
+ build: "tsc",
157
+ }
158
+ : {
159
+ dev: "node index.js",
160
+ start: "node index.js",
161
+ },
145
162
  dependencies: {
146
163
  "discordjs-nextgen": "latest",
147
- "dotenv": "^17.3.1"
148
- }
164
+ "dotenv": "^17.3.1",
165
+ },
149
166
  };
150
167
 
151
168
  if (config.language === "ts") {
152
169
  pkg.devDependencies = {
153
170
  "@types/node": "^20.12.0",
154
171
  "ts-node": "^10.9.2",
155
- "typescript": "^5.4.5"
172
+ "typescript": "^5.4.5",
156
173
  };
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
174
  }
169
175
 
170
176
  if (config.plugins.includes("voice")) {
171
177
  pkg.dependencies["discordjs-nextgen-voice"] = "latest";
172
178
  }
173
179
 
180
+ if (config.plugins.includes("jsx")) {
181
+ pkg.dependencies["discordjs-nextgen-jsx"] = "latest";
182
+ }
183
+
184
+ await writeLanguageConfig(targetDir, config.language, config.plugins.includes("jsx"));
174
185
  await writeFile(targetDir, "package.json", JSON.stringify(pkg, null, 2));
175
186
  await writeFile(targetDir, ".gitignore", "node_modules\n.env\ndist\n");
176
- await writeFile(targetDir, ".env", "TOKEN=YOUR_BOT_TOKEN_HERE");
187
+ await writeFile(targetDir, ".env", "TOKEN=YOUR_BOT_TOKEN_HERE\n");
177
188
 
178
189
  if (config.plugins.includes("voice")) {
179
190
  await injectVoicePlugin(targetDir, extension, config.template);
180
191
  }
192
+
193
+ if (config.plugins.includes("jsx")) {
194
+ await injectJSXPlugin(targetDir, config.language);
195
+ }
196
+ }
197
+
198
+ async function writeLanguageConfig(targetDir, language, useJsx) {
199
+ if (language === "ts") {
200
+ const tsconfig = {
201
+ compilerOptions: {
202
+ target: "ESNext",
203
+ module: "ESNext",
204
+ moduleResolution: "node",
205
+ esModuleInterop: true,
206
+ strict: true,
207
+ skipLibCheck: true,
208
+ outDir: "dist",
209
+ },
210
+ include: ["**/*"],
211
+ };
212
+
213
+ if (useJsx) {
214
+ tsconfig.compilerOptions.jsx = "react-jsx";
215
+ tsconfig.compilerOptions.jsxImportSource = "discordjs-nextgen-jsx";
216
+ }
217
+
218
+ await writeFile(targetDir, "tsconfig.json", JSON.stringify(tsconfig, null, 2));
219
+ return;
220
+ }
221
+
222
+ if (useJsx) {
223
+ await writeFile(targetDir, "jsconfig.json", JSON.stringify({
224
+ compilerOptions: {
225
+ jsx: "react-jsx",
226
+ jsxImportSource: "discordjs-nextgen-jsx",
227
+ },
228
+ include: ["**/*"],
229
+ }, null, 2));
230
+ }
181
231
  }
182
232
 
183
233
  async function injectVoicePlugin(targetDir, extension, template) {
@@ -185,27 +235,59 @@ async function injectVoicePlugin(targetDir, extension, template) {
185
235
  let content = await fsp.readFile(entryPath, "utf8");
186
236
 
187
237
  const importLine = `import { VoicePlugin } from "discordjs-nextgen-voice";`;
188
- const useLine = `\napp.use(new VoicePlugin());`;
189
238
 
190
239
  if (!content.includes(importLine)) {
191
240
  content = `${importLine}\n${content}`;
192
241
  }
193
242
 
194
243
  if (!content.includes("new VoicePlugin()")) {
195
- content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n${useLine}`);
244
+ content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n\napp.use(new VoicePlugin());`);
196
245
  }
197
246
 
198
247
  await fsp.writeFile(entryPath, content, "utf8");
199
248
 
200
249
  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);
250
+ 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`;
251
+ await writeFile(targetDir, `commands/prefix/join.${extension}`, cmdCode);
252
+ }
253
+ }
254
+
255
+ async function injectJSXPlugin(targetDir, language) {
256
+ const entryExtension = language === "ts" ? "ts" : "js";
257
+ const jsxExtension = language === "ts" ? "tsx" : "jsx";
258
+ const entryPath = path.join(targetDir, `index.${entryExtension}`);
259
+ let content = await fsp.readFile(entryPath, "utf8");
260
+
261
+ const importLine = `import { JSXPlugin } from "discordjs-nextgen-jsx";`;
262
+
263
+ if (!content.includes(importLine)) {
264
+ content = `${importLine}\n${content}`;
265
+ }
266
+
267
+ if (!content.includes("new JSXPlugin()")) {
268
+ content = content.replace(/const app = new App\((\{[\s\S]*?\}|)\);/, (match) => `${match}\n\napp.use(new JSXPlugin());`);
269
+ }
270
+
271
+ if (!content.includes(".prefix(")) {
272
+ content = content.replace(
273
+ /app\.setPresence\(/,
274
+ `app.prefix({\n folder: 'commands/prefix',\n prefix: '.',\n});\n\napp.setPresence(`
275
+ );
203
276
  }
277
+
278
+ await fsp.writeFile(entryPath, content, "utf8");
279
+
280
+ const jsxPrefixCommand = language === "ts"
281
+ ? `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`
282
+ : `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`;
283
+
284
+ await writeFile(targetDir, `commands/prefix/hello.${jsxExtension}`, jsxPrefixCommand);
204
285
  }
205
286
 
206
287
  async function copyTemplateDirectory(sourceDir, targetDir, context) {
207
288
  if (!fs.existsSync(sourceDir)) return;
208
289
  const entries = await fsp.readdir(sourceDir, { withFileTypes: true });
290
+
209
291
  for (const entry of entries) {
210
292
  const src = path.join(sourceDir, entry.name);
211
293
  const destName = entry.name.replace(/__EXT__/g, context.extension);
@@ -214,20 +296,21 @@ async function copyTemplateDirectory(sourceDir, targetDir, context) {
214
296
  if (entry.isDirectory()) {
215
297
  await fsp.mkdir(dest, { recursive: true });
216
298
  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
- }
299
+ continue;
300
+ }
301
+
302
+ let content = await fsp.readFile(src, "utf8");
227
303
 
228
- content = content.replaceAll("__EXT__", context.extension);
229
- await fsp.writeFile(dest, content, "utf8");
304
+ if (context.language === "ts") {
305
+ content = content.replace(/\/\/ \[JS\][\s\S]*?\/\/ \[\/JS\]/g, "");
306
+ content = content.replace(/\/\/ \[TS\]/g, "").replace(/\/\/ \[\/TS\]/g, "");
307
+ } else {
308
+ content = content.replace(/\/\/ \[TS\][\s\S]*?\/\/ \[\/TS\]/g, "");
309
+ content = content.replace(/\/\/ \[JS\]/g, "").replace(/\/\/ \[\/JS\]/g, "");
230
310
  }
311
+
312
+ content = content.replaceAll("__EXT__", context.extension);
313
+ await fsp.writeFile(dest, content, "utf8");
231
314
  }
232
315
  }
233
316
 
@@ -238,7 +321,12 @@ async function writeFile(baseDir, relPath, content) {
238
321
  }
239
322
 
240
323
  async function runInstall(targetDir) {
241
- const result = spawnSync("npm", ["install"], { cwd: targetDir, stdio: "inherit", shell: true });
324
+ const result = spawnSync("npm", ["install"], {
325
+ cwd: targetDir,
326
+ stdio: "inherit",
327
+ shell: true,
328
+ });
329
+
242
330
  return result.status === 0;
243
331
  }
244
332
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-discordjs-nextgen",
3
- "version": "0.1.0",
3
+ "version": "0.2.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"