create-sprint 0.0.3 → 0.0.5

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/bin/cli.ts DELETED
@@ -1,700 +0,0 @@
1
- import { select, input, confirm } from "@inquirer/prompts";
2
- import { mkdir, writeFile } from "fs/promises";
3
- import { existsSync } from "fs";
4
- import { join } from "path";
5
- import { execSync } from "child_process";
6
-
7
- const args = process.argv.slice(2);
8
-
9
- const hasHelp = args.includes("--help") || args.includes("-h");
10
- const hasTs = args.includes("--ts") || args.includes("--typescript");
11
- const hasJs = args.includes("--js") || args.includes("--javascript");
12
- const hasName = args.indexOf("--name");
13
- const projectNameArg = hasName !== -1 ? args[hasName + 1] : null;
14
- const useCurrentDirArg = args.includes("--current");
15
- const skipInstallArg = args.includes("--no-install");
16
- const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
17
- const useDockerArg = args.includes("--docker");
18
-
19
- if (hasHelp) {
20
- console.log("\nšŸš€ Sprint - Quickly API Framework\n");
21
- console.log("Usage: sprint [options]");
22
- console.log("\nOptions:");
23
- console.log(" --ts, --typescript Create TypeScript project");
24
- console.log(" --js, --javascript Create JavaScript project");
25
- console.log(" --name <name> Project name (use '.' for current directory)");
26
- console.log(" --no-install Skip automatic dependency installation");
27
- console.log(" --telemetry <type> Telemetry: none, sentry, glitchtip, discord");
28
- console.log(" --docker Add Docker support");
29
- console.log(" --help, -h Show this help message");
30
- console.log("\nExamples:");
31
- console.log(" sprint Interactive mode");
32
- console.log(" sprint --ts Create TypeScript project");
33
- console.log(" sprint --ts --name my-api");
34
- console.log(" sprint --js --name . --telemetry sentry --docker\n");
35
- process.exit(0);
36
- }
37
-
38
- async function main() {
39
- console.log("\nšŸš€ Welcome to Sprint - Quickly API Framework\n");
40
-
41
- let projectName;
42
- let language;
43
-
44
- if (projectNameArg) {
45
- projectName = projectNameArg;
46
- } else if (useCurrentDirArg) {
47
- projectName = ".";
48
- } else {
49
- projectName = await getProjectName();
50
- }
51
-
52
- if (hasTs) {
53
- language = "typescript";
54
- } else if (hasJs) {
55
- language = "javascript";
56
- } else {
57
- language = await selectLanguage();
58
- }
59
-
60
- console.log(`\nāœ… Creating Sprint project: ${projectName === "." ? "current directory" : projectName} with ${language === "typescript" ? "TypeScript" : "JavaScript"}\n`);
61
-
62
- await createProject(projectName, language);
63
-
64
- console.log("\nāœ… Project created successfully!");
65
-
66
- let installDeps = true;
67
- if (!skipInstallArg) {
68
- installDeps = await confirm({
69
- message: "Do you want to install dependencies now?",
70
- default: true,
71
- });
72
- }
73
-
74
- if (installDeps) {
75
- console.log("\nšŸ“¦ Installing dependencies...\n");
76
- const targetDir = projectName === "." ? process.cwd() : join(process.cwd(), projectName);
77
- try {
78
- execSync("npm install", { cwd: targetDir, stdio: "inherit" });
79
- console.log("\nāœ… Dependencies installed successfully!");
80
- } catch (error) {
81
- console.error("\nāŒ Error installing dependencies. Please run 'npm install' manually.");
82
- }
83
- }
84
-
85
- console.log("\nšŸ“¦ Next steps:");
86
- const cdCmd = projectName === "." ? "" : `cd ${projectName} && `;
87
- if (!installDeps) {
88
- console.log(` ${cdCmd}npm install`);
89
- }
90
- console.log(` ${cdCmd}npm run dev`);
91
- console.log("\n");
92
- }
93
-
94
- async function getProjectName() {
95
- const name = await input({
96
- message: "Enter project name:",
97
- validate: (value) => {
98
- if (!value.trim()) return "Please enter a project name";
99
- return true;
100
- },
101
- });
102
-
103
- return name;
104
- }
105
-
106
- async function selectLanguage() {
107
- const language = await select({
108
- message: "Select your preferred language:",
109
- choices: [
110
- {
111
- name: "TypeScript",
112
- value: "typescript",
113
- description: "Recommended - Type safety and better developer experience",
114
- },
115
- {
116
- name: "JavaScript",
117
- value: "javascript",
118
- description: "Vanilla JavaScript for simpler projects",
119
- },
120
- ],
121
- });
122
-
123
- return language;
124
- }
125
-
126
- async function selectTelemetry() {
127
- const telemetry = await select({
128
- message: "Select error tracking/telemetry solution:",
129
- choices: [
130
- {
131
- name: "None",
132
- value: "none",
133
- description: "No error tracking integration",
134
- },
135
- {
136
- name: "Sentry",
137
- value: "sentry",
138
- description: "Full-featured error tracking (free tier available)",
139
- },
140
- {
141
- name: "GlitchTip",
142
- value: "glitchtip",
143
- description: "Simple error tracking, can be self-hosted",
144
- },
145
- {
146
- name: "Discord Webhook",
147
- value: "discord",
148
- description: "Send error notifications to Discord channel",
149
- },
150
- ],
151
- });
152
-
153
- return telemetry;
154
- }
155
-
156
- async function createProject(projectName, language) {
157
- const isCurrentDir = projectName === ".";
158
- const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
159
-
160
- if (!isCurrentDir && existsSync(targetDir)) {
161
- console.error(`Error: Directory ${projectName} already exists`);
162
- process.exit(1);
163
- }
164
-
165
- if (!isCurrentDir) {
166
- await mkdir(targetDir, { recursive: true });
167
- }
168
-
169
- let telemetry = "none";
170
- if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
171
- telemetry = telemetryArg;
172
- } else if (!hasTs && !hasJs) {
173
- telemetry = await selectTelemetry();
174
- }
175
-
176
- let useDocker = useDockerArg;
177
- if (!useDocker && !hasTs && !hasJs) {
178
- useDocker = await confirm({
179
- message: "Do you want to add Docker support?",
180
- default: false,
181
- });
182
- }
183
-
184
- let pkgJson;
185
- if (language === "typescript") {
186
- pkgJson = getTypeScriptPackageJson(projectName, telemetry);
187
- } else {
188
- pkgJson = getJavaScriptPackageJson(projectName, telemetry);
189
- }
190
-
191
- await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
192
-
193
- if (language === "typescript") {
194
- await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
195
- await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
196
- }
197
-
198
- const srcDir = join(targetDir, "src");
199
- await mkdir(srcDir, { recursive: true });
200
-
201
- await mkdir(join(srcDir, "middlewares"), { recursive: true });
202
- await mkdir(join(srcDir, "routes"), { recursive: true });
203
- await mkdir(join(srcDir, "controllers"), { recursive: true });
204
-
205
- await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
206
- await writeFile(join(srcDir, "controllers", ".gitkeep"), "");
207
-
208
- await writeFile(join(srcDir, "index." + (language === "typescript" ? "ts" : "js")), getMainFile(language, telemetry));
209
-
210
- await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
211
-
212
- if (language === "typescript") {
213
- await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getAppFile(language, telemetry));
214
- }
215
-
216
- await writeFile(join(targetDir, ".env.example"), getEnvExample(telemetry));
217
- await writeFile(join(targetDir, ".env"), "");
218
-
219
- if (telemetry !== "none") {
220
- await writeFile(join(targetDir, "sprint.config.js"), getSprintConfig(telemetry));
221
- }
222
-
223
- await writeFile(join(targetDir, ".gitignore"), getGitignore(language));
224
-
225
- if (useDocker) {
226
- await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
227
- await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
228
- await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
229
- }
230
- }
231
-
232
- function getTypeScriptPackageJson(name, telemetry) {
233
- const deps = {
234
- "sprint-es": "^0.0.24",
235
- dotenv: "^17.0.0",
236
- };
237
-
238
- const devDeps = {
239
- "@types/node": "^22.0.0",
240
- "tsx": "^4.19.0",
241
- typescript: "^5.6.0",
242
- vite: "^6.0.0",
243
- };
244
-
245
- if (telemetry === "sentry") {
246
- deps["@sentry/node"] = "^8.0.0";
247
- } else if (telemetry === "glitchtip") {
248
- deps["@sentry/node"] = "^8.0.0";
249
- } else if (telemetry === "discord") {
250
- deps["axios"] = "^1.6.0";
251
- }
252
-
253
- return {
254
- name: name === "." ? "sprint-app" : name,
255
- version: "0.0.1",
256
- description: "Sprint API",
257
- main: "dist/index.js",
258
- scripts: {
259
- build: "vite build",
260
- start: "node dist/index.js --prod",
261
- dev: "vite --dev",
262
- },
263
- dependencies: deps,
264
- devDependencies: devDeps,
265
- };
266
- }
267
-
268
- function getJavaScriptPackageJson(name, telemetry) {
269
- const deps = {
270
- "sprint-es": "^0.0.24",
271
- dotenv: "^17.0.0",
272
- };
273
-
274
- if (telemetry === "sentry") {
275
- deps["@sentry/node"] = "^8.0.0";
276
- } else if (telemetry === "glitchtip") {
277
- deps["@sentry/node"] = "^8.0.0";
278
- } else if (telemetry === "discord") {
279
- deps["axios"] = "^1.6.0";
280
- }
281
-
282
- return {
283
- name: name === "." ? "sprint-app" : name,
284
- version: "0.0.1",
285
- description: "Sprint API",
286
- main: "src/index.js",
287
- type: "module",
288
- scripts: {
289
- start: "node src/index.js --prod",
290
- dev: "node --watch src/index.js --dev",
291
- },
292
- dependencies: deps,
293
- };
294
- }
295
-
296
- function getTsConfig() {
297
- return JSON.stringify({
298
- compilerOptions: {
299
- target: "ES2020",
300
- module: "ESNext",
301
- moduleResolution: "bundler",
302
- lib: ["ES2020"],
303
- outDir: "./dist",
304
- rootDir: "./src",
305
- strict: true,
306
- esModuleInterop: true,
307
- skipLibCheck: true,
308
- forceConsistentCasingInFileNames: true,
309
- resolveJsonModule: true,
310
- declaration: true,
311
- declarationMap: true,
312
- sourceMap: true,
313
- tabWidth: 4,
314
- },
315
- include: ["src/**/*"],
316
- exclude: ["node_modules", "dist"],
317
- }, null, 2);
318
- }
319
-
320
- function getViteConfig() {
321
- return `import { defineConfig } from "vite";
322
- import { resolve } from "path";
323
-
324
- export default defineConfig({
325
- build: {
326
- lib: {
327
- entry: resolve(__dirname, "src/index.ts"),
328
- formats: ["es"],
329
- fileName: "index",
330
- },
331
- outDir: "dist",
332
- rollupOptions: {
333
- external: ["sprint-es", "express", "cors", "morgan", "serve-favicon", "dotenv"],
334
- },
335
- target: "ES2020",
336
- },
337
- resolve: {
338
- alias: {
339
- "@": resolve(__dirname, "src"),
340
- },
341
- },
342
- });
343
- `;
344
- }
345
-
346
- function getMainFile(language, telemetry) {
347
- const hasTelemetry = telemetry !== "none";
348
-
349
- if (language === "typescript") {
350
- let code = `import Sprint from "sprint-es";
351
- import dotenv from "dotenv";
352
- import homeRouter from "./routes/home";
353
-
354
- dotenv.config();
355
-
356
- `;
357
-
358
- if (hasTelemetry) {
359
- code += getTelemetryImport(language, telemetry);
360
- }
361
-
362
- code += `const app = new Sprint({
363
- port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
364
- favicon: process.env.FAVICON || undefined,
365
- bodyParser: {
366
- limit: "10mb"
367
- }
368
- });
369
-
370
- app.use(homeRouter);
371
-
372
- app.listen();
373
- `;
374
- return code;
375
- }
376
-
377
- let code = `import Sprint from "sprint-es";
378
- import dotenv from "dotenv";
379
- import homeRouter from "./routes/home";
380
-
381
- dotenv.config();
382
-
383
- `;
384
-
385
- if (hasTelemetry) {
386
- code += getTelemetryImport(language, telemetry);
387
- }
388
-
389
- code += `const app = new Sprint({
390
- port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
391
- favicon: process.env.FAVICON || undefined,
392
- bodyParser: {
393
- limit: "10mb"
394
- }
395
- });
396
-
397
- app.use(homeRouter);
398
-
399
- app.listen();
400
- `;
401
- return code;
402
- }
403
-
404
- function getTelemetryImport(language, telemetry) {
405
- if (telemetry === "sentry" || telemetry === "glitchtip") {
406
- return `import * as Sentry from "@sentry/node";
407
-
408
- Sentry.init({
409
- dsn: process.env.SENTRY_DSN,
410
- integrations: [
411
- Sentry.httpIntegration(),
412
- ],
413
- });
414
-
415
- `;
416
- } else if (telemetry === "discord") {
417
- return `import axios from "axios";
418
-
419
- const discordWebhook = process.env.DISCORD_WEBHOOK_URL;
420
-
421
- async function sendDiscordError(error, req) {
422
- if (!discordWebhook) return;
423
-
424
- const embed = {
425
- title: "🚨 Error in Sprint API",
426
- description: \`\${error.message}\`,
427
- color: 16711680,
428
- fields: [
429
- {
430
- name: "Method",
431
- value: req.method,
432
- inline: true,
433
- },
434
- {
435
- name: "URL",
436
- value: req.url,
437
- inline: true,
438
- },
439
- {
440
- name: "Stack",
441
- value: error.stack?.split("\\n").slice(0, 5).join("\\n") || "No stack trace",
442
- },
443
- ],
444
- timestamp: new Date().toISOString(),
445
- };
446
-
447
- try {
448
- await axios.post(discordWebhook, {
449
- embeds: [embed],
450
- });
451
- } catch (e) {
452
- console.error("Failed to send Discord webhook:", e.message);
453
- }
454
- }
455
-
456
- `;
457
- }
458
- return "";
459
- }
460
-
461
- function getAppFile(language, telemetry) {
462
- if (language === "typescript") {
463
- return `import type { SprintOptions } from "sprint-es";
464
-
465
- export const options: SprintOptions = {
466
- port: 3000,
467
- bodyParser: {
468
- limit: "10mb"
469
- }
470
- };
471
- `;
472
- }
473
- return "";
474
- }
475
-
476
- function getEnvExample(telemetry) {
477
- let env = `PORT=3000
478
- FAVICON=./public/favicon.ico
479
- NODE_ENV=development
480
- `;
481
-
482
- if (telemetry === "sentry" || telemetry === "glitchtip") {
483
- env += `
484
- # Sentry / GlitchTip (use GlitchTip DSN for self-hosted)
485
- SENTRY_DSN=
486
- `;
487
- } else if (telemetry === "discord") {
488
- env += `
489
- # Discord Webhook URL for error notifications
490
- DISCORD_WEBHOOK_URL=
491
- `;
492
- }
493
-
494
- return env;
495
- }
496
-
497
- function getSprintConfig(telemetry) {
498
- if (telemetry === "discord") {
499
- return `export default {
500
- errorHandler: async (error, req, res) => {
501
- console.error(error);
502
-
503
- if (process.env.DISCORD_WEBHOOK_URL && res.socket.server) {
504
- const axios = await import("axios");
505
- const embed = {
506
- title: "🚨 Error in Sprint API",
507
- description: error.message,
508
- color: 16711680,
509
- fields: [
510
- {
511
- name: "Method",
512
- value: req.method,
513
- inline: true,
514
- },
515
- {
516
- name: "URL",
517
- value: req.url,
518
- inline: true,
519
- },
520
- {
521
- name: "Stack",
522
- value: error.stack?.split("\\n").slice(0, 5).join("\\n") || "No stack",
523
- },
524
- ],
525
- timestamp: new Date().toISOString(),
526
- };
527
-
528
- try {
529
- await axios.default.post(process.env.DISCORD_WEBHOOK_URL, {
530
- embeds: [embed],
531
- });
532
- } catch (e) {
533
- console.error("Discord webhook error:", e.message);
534
- }
535
- }
536
-
537
- res.status(500).json({ error: "Internal server error" });
538
- }
539
- };
540
- `;
541
- }
542
- return "";
543
- }
544
-
545
- function getHomeRoute(language) {
546
- if (language === "typescript") {
547
- return `import { Router } from "sprint-es";
548
-
549
- const router = Router();
550
-
551
- router.get("/", (req, res) => {
552
- res.json({
553
- message: "Hello World",
554
- status: "ok"
555
- });
556
- });
557
-
558
- export default router;
559
- `;
560
- }
561
- return `import { Router } from "sprint-es";
562
-
563
- const router = Router();
564
-
565
- router.get("/", (req, res) => {
566
- res.json({
567
- message: "Hello World",
568
- status: "ok"
569
- });
570
- });
571
-
572
- export default router;
573
- `;
574
- }
575
-
576
- function getDockerfile(language) {
577
- if (language === "typescript") {
578
- return `FROM node:20-alpine
579
-
580
- WORKDIR /app
581
-
582
- COPY package*.json ./
583
-
584
- RUN npm install
585
-
586
- COPY . .
587
-
588
- RUN npm run build
589
-
590
- EXPOSE 3000
591
-
592
- CMD ["npm", "start"]
593
- `;
594
- }
595
- return `FROM node:20-alpine
596
-
597
- WORKDIR /app
598
-
599
- COPY package*.json ./
600
-
601
- RUN npm install
602
-
603
- COPY . .
604
-
605
- EXPOSE 3000
606
-
607
- CMD ["npm", "start"]
608
- `;
609
- }
610
-
611
- function getDockerCompose(language) {
612
- if (language === "typescript") {
613
- return `version: "3.8"
614
-
615
- services:
616
- app:
617
- build: .
618
- ports:
619
- - "3000:3000"
620
- environment:
621
- - NODE_ENV=production
622
- - PORT=3000
623
- restart: unless-stopped
624
- `;
625
- }
626
- return `version: "3.8"
627
-
628
- services:
629
- app:
630
- build: .
631
- ports:
632
- - "3000:3000"
633
- environment:
634
- - NODE_ENV=production
635
- - PORT=3000
636
- restart: unless-stopped
637
- `;
638
- }
639
-
640
- function getGitignore(language) {
641
- return `# Dependencies
642
- node_modules/
643
- npm-debug.log*
644
- yarn-debug.log*
645
- yarn-error.log*
646
-
647
- # Build
648
- dist/
649
- build/
650
- *.tsbuildinfo
651
-
652
- # Environment
653
- .env
654
- .env.local
655
- .env.*.local
656
-
657
- # IDE
658
- .vscode/
659
- .idea/
660
- *.swp
661
- *.swo
662
- *~
663
-
664
- # OS
665
- .DS_Store
666
- Thumbs.db
667
-
668
- # Logs
669
- logs/
670
- *.log
671
-
672
- # Test
673
- coverage/
674
-
675
- # Temporary
676
- tmp/
677
- temp/
678
- `;
679
- }
680
-
681
- function getDockerIgnore() {
682
- return `node_modules
683
- npm-debug.log
684
- .env
685
- .env.local
686
- .git
687
- .gitignore
688
- README.md
689
- dist
690
- build
691
- coverage
692
- .vscode
693
- .idea
694
- *.log
695
- tmp
696
- temp
697
- `;
698
- }
699
-
700
- main().catch(console.error);