create-sprint 0.0.1 ā 0.0.3
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.js +650 -0
- package/bin/cli.ts +639 -221
- package/package.json +35 -32
package/bin/cli.ts
CHANGED
|
@@ -1,281 +1,699 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { join } from 'path';
|
|
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";
|
|
7
6
|
|
|
8
7
|
const args = process.argv.slice(2);
|
|
9
|
-
const initIndex = args.indexOf('init');
|
|
10
|
-
const isInitCommand = initIndex !== -1;
|
|
11
|
-
|
|
12
|
-
if (!isInitCommand) {
|
|
13
|
-
console.log('\nš Sprint - Quickly API Framework\n');
|
|
14
|
-
console.log('Usage: sprint init [options]');
|
|
15
|
-
console.log('\nOptions:');
|
|
16
|
-
console.log(' --ts, --typescript Create TypeScript project');
|
|
17
|
-
console.log(' --js, --javascript Create JavaScript project');
|
|
18
|
-
console.log(' --name <name> Project name');
|
|
19
|
-
console.log(' --current Create in current directory');
|
|
20
|
-
console.log('\nExamples:');
|
|
21
|
-
console.log(' sprint init Interactive mode');
|
|
22
|
-
console.log(' sprint init --ts Create TypeScript project');
|
|
23
|
-
console.log(' sprint init --ts --name my-api');
|
|
24
|
-
console.log(' sprint init --js --current\n');
|
|
25
|
-
process.exit(0);
|
|
26
|
-
}
|
|
27
8
|
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
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");
|
|
31
13
|
const projectNameArg = hasName !== -1 ? args[hasName + 1] : null;
|
|
32
|
-
const useCurrentDirArg = args.includes(
|
|
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
|
+
}
|
|
33
37
|
|
|
34
38
|
async function main() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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");
|
|
66
92
|
}
|
|
67
93
|
|
|
68
|
-
async function getProjectName()
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
name: 'New directory',
|
|
78
|
-
value: 'new',
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
if (useCurrentDir === 'current') {
|
|
84
|
-
return '.';
|
|
85
|
-
}
|
|
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
|
+
});
|
|
86
102
|
|
|
87
|
-
|
|
88
|
-
message: 'Enter project name:',
|
|
89
|
-
validate: (value: string) => {
|
|
90
|
-
if (!value.trim()) return 'Please enter a project name';
|
|
91
|
-
return true;
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
return name;
|
|
103
|
+
return name;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
|
-
async function selectLanguage()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
}
|
|
114
125
|
|
|
115
|
-
|
|
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;
|
|
116
154
|
}
|
|
117
155
|
|
|
118
|
-
async function createProject(projectName
|
|
119
|
-
|
|
120
|
-
|
|
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
|
+
}
|
|
121
183
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
184
|
+
let pkgJson;
|
|
185
|
+
if (language === "typescript") {
|
|
186
|
+
pkgJson = getTypeScriptPackageJson(projectName, telemetry);
|
|
187
|
+
} else {
|
|
188
|
+
pkgJson = getJavaScriptPackageJson(projectName, telemetry);
|
|
189
|
+
}
|
|
126
190
|
|
|
127
|
-
|
|
128
|
-
await mkdir(targetDir, { recursive: true });
|
|
129
|
-
}
|
|
191
|
+
await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
130
192
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
193
|
+
if (language === "typescript") {
|
|
194
|
+
await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
|
|
195
|
+
await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
|
|
196
|
+
}
|
|
134
197
|
|
|
135
|
-
|
|
198
|
+
const srcDir = join(targetDir, "src");
|
|
199
|
+
await mkdir(srcDir, { recursive: true });
|
|
136
200
|
|
|
137
|
-
|
|
138
|
-
await
|
|
139
|
-
|
|
201
|
+
await mkdir(join(srcDir, "middlewares"), { recursive: true });
|
|
202
|
+
await mkdir(join(srcDir, "routes"), { recursive: true });
|
|
203
|
+
await mkdir(join(srcDir, "controllers"), { recursive: true });
|
|
140
204
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
await writeFile(join(srcDir, 'index.' + (language === 'typescript' ? 'ts' : 'js')), getMainFile(language));
|
|
145
|
-
|
|
146
|
-
if (language === 'typescript') {
|
|
147
|
-
await writeFile(join(srcDir, 'app.' + (language === 'typescript' ? 'ts' : 'js')), getAppFile(language));
|
|
148
|
-
}
|
|
205
|
+
await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
|
|
206
|
+
await writeFile(join(srcDir, "controllers", ".gitkeep"), "");
|
|
149
207
|
|
|
150
|
-
|
|
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
|
+
}
|
|
151
230
|
}
|
|
152
231
|
|
|
153
|
-
function getTypeScriptPackageJson(name
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
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
|
+
};
|
|
174
266
|
}
|
|
175
267
|
|
|
176
|
-
function getJavaScriptPackageJson(name
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
};
|
|
192
294
|
}
|
|
193
295
|
|
|
194
296
|
function getTsConfig() {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
+
},
|
|
211
341
|
},
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}, null, 2);
|
|
342
|
+
});
|
|
343
|
+
`;
|
|
215
344
|
}
|
|
216
345
|
|
|
217
|
-
function getMainFile(language
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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";
|
|
221
353
|
|
|
222
354
|
dotenv.config();
|
|
223
355
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
});
|
|
356
|
+
`;
|
|
357
|
+
|
|
358
|
+
if (hasTelemetry) {
|
|
359
|
+
code += getTelemetryImport(language, telemetry);
|
|
360
|
+
}
|
|
231
361
|
|
|
232
|
-
app
|
|
233
|
-
|
|
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
|
+
}
|
|
234
368
|
});
|
|
235
369
|
|
|
370
|
+
app.use(homeRouter);
|
|
371
|
+
|
|
236
372
|
app.listen();
|
|
237
373
|
`;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
374
|
+
return code;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
let code = `import Sprint from "sprint-es";
|
|
378
|
+
import dotenv from "dotenv";
|
|
379
|
+
import homeRouter from "./routes/home";
|
|
241
380
|
|
|
242
381
|
dotenv.config();
|
|
243
382
|
|
|
244
|
-
|
|
245
|
-
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
|
246
|
-
favicon: process.env.FAVICON || undefined,
|
|
247
|
-
bodyParser: {
|
|
248
|
-
limit: '10mb'
|
|
249
|
-
}
|
|
250
|
-
});
|
|
383
|
+
`;
|
|
251
384
|
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
}
|
|
254
395
|
});
|
|
255
396
|
|
|
397
|
+
app.use(homeRouter);
|
|
398
|
+
|
|
256
399
|
app.listen();
|
|
257
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 "";
|
|
258
459
|
}
|
|
259
460
|
|
|
260
|
-
function getAppFile(language
|
|
261
|
-
|
|
262
|
-
|
|
461
|
+
function getAppFile(language, telemetry) {
|
|
462
|
+
if (language === "typescript") {
|
|
463
|
+
return `import type { SprintOptions } from "sprint-es";
|
|
263
464
|
|
|
264
465
|
export const options: SprintOptions = {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
466
|
+
port: 3000,
|
|
467
|
+
bodyParser: {
|
|
468
|
+
limit: "10mb"
|
|
469
|
+
}
|
|
269
470
|
};
|
|
270
471
|
`;
|
|
271
|
-
|
|
272
|
-
|
|
472
|
+
}
|
|
473
|
+
return "";
|
|
273
474
|
}
|
|
274
475
|
|
|
275
|
-
function getEnvExample() {
|
|
276
|
-
|
|
476
|
+
function getEnvExample(telemetry) {
|
|
477
|
+
let env = `PORT=3000
|
|
277
478
|
FAVICON=./public/favicon.ico
|
|
278
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
|
|
279
697
|
`;
|
|
280
698
|
}
|
|
281
699
|
|