create-sprint 0.0.2 ā 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/dist/cli.js +24 -0
- package/dist/generators.js +373 -0
- package/dist/index.js +201 -0
- package/dist/validators.js +25 -0
- package/package.json +37 -36
- package/src/cli.ts +27 -0
- package/src/generators.ts +396 -0
- package/src/index.ts +252 -0
- package/src/validators.ts +32 -0
- package/bin/cli.js +0 -253
- package/bin/cli.ts +0 -282
package/dist/cli.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
import { runCLI } from "./index";
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
6
|
+
console.log("\nš Sprint - Quickly API Framework\n");
|
|
7
|
+
console.log("Usage: npx create-sprint [options]");
|
|
8
|
+
console.log("\nOptions:");
|
|
9
|
+
console.log(" --ts, --typescript Create TypeScript project");
|
|
10
|
+
console.log(" --js, --javascript Create JavaScript project");
|
|
11
|
+
console.log(" --name <name> Project name (use '.' for current directory)");
|
|
12
|
+
console.log(" --no-install Skip automatic dependency installation");
|
|
13
|
+
console.log(" --telemetry <type> Telemetry: none, sentry, glitchtip, discord");
|
|
14
|
+
console.log(" --docker Add Docker support");
|
|
15
|
+
console.log(" --yes, -y Skip all prompts (use defaults)");
|
|
16
|
+
console.log(" --help, -h Show this help message");
|
|
17
|
+
console.log("\nExamples:");
|
|
18
|
+
console.log(" npx create-sprint Interactive mode");
|
|
19
|
+
console.log(" npx create-sprint -y Create TypeScript project with defaults");
|
|
20
|
+
console.log(" npx create-sprint --ts --name my-api");
|
|
21
|
+
console.log(" npx create-sprint --js --name . --telemetry sentry --docker\n");
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
runCLI(args).catch(console.error);
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
export function getTypeScriptPackageJson(name, telemetry) {
|
|
2
|
+
const deps = {
|
|
3
|
+
"sprint-es": "^0.0.24",
|
|
4
|
+
dotenv: "^17.0.0",
|
|
5
|
+
};
|
|
6
|
+
const devDeps = {
|
|
7
|
+
"@types/node": "^22.0.0",
|
|
8
|
+
"tsx": "^4.19.0",
|
|
9
|
+
typescript: "^5.6.0",
|
|
10
|
+
vite: "^6.0.0",
|
|
11
|
+
};
|
|
12
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
13
|
+
deps["@sentry/node"] = "^8.0.0";
|
|
14
|
+
}
|
|
15
|
+
else if (telemetry === "discord") {
|
|
16
|
+
deps["axios"] = "^1.6.0";
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
name: name === "." ? "sprint-app" : name,
|
|
20
|
+
version: "0.0.1",
|
|
21
|
+
description: "Sprint API",
|
|
22
|
+
main: "dist/index.js",
|
|
23
|
+
scripts: {
|
|
24
|
+
build: "vite build",
|
|
25
|
+
start: "NODE_ENV=production node dist/index.js",
|
|
26
|
+
dev: "NODE_ENV=development tsx src/index.ts",
|
|
27
|
+
},
|
|
28
|
+
dependencies: deps,
|
|
29
|
+
devDependencies: devDeps,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function getJavaScriptPackageJson(name, telemetry) {
|
|
33
|
+
const deps = {
|
|
34
|
+
"sprint-es": "^0.0.24",
|
|
35
|
+
dotenv: "^17.0.0",
|
|
36
|
+
};
|
|
37
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
38
|
+
deps["@sentry/node"] = "^8.0.0";
|
|
39
|
+
}
|
|
40
|
+
else if (telemetry === "discord") {
|
|
41
|
+
deps["axios"] = "^1.6.0";
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
name: name === "." ? "sprint-app" : name,
|
|
45
|
+
version: "0.0.1",
|
|
46
|
+
description: "Sprint API",
|
|
47
|
+
main: "src/index.js",
|
|
48
|
+
type: "module",
|
|
49
|
+
scripts: {
|
|
50
|
+
start: "NODE_ENV=production node src/index.js",
|
|
51
|
+
dev: "NODE_ENV=development node --watch src/index.js",
|
|
52
|
+
},
|
|
53
|
+
dependencies: deps,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function getTsConfig() {
|
|
57
|
+
return JSON.stringify({
|
|
58
|
+
compilerOptions: {
|
|
59
|
+
target: "ES2020",
|
|
60
|
+
module: "ESNext",
|
|
61
|
+
moduleResolution: "bundler",
|
|
62
|
+
lib: ["ES2020"],
|
|
63
|
+
outDir: "./dist",
|
|
64
|
+
rootDir: "./src",
|
|
65
|
+
strict: true,
|
|
66
|
+
esModuleInterop: true,
|
|
67
|
+
skipLibCheck: true,
|
|
68
|
+
forceConsistentCasingInFileNames: true,
|
|
69
|
+
resolveJsonModule: true,
|
|
70
|
+
declaration: true,
|
|
71
|
+
declarationMap: true,
|
|
72
|
+
sourceMap: true,
|
|
73
|
+
tabWidth: 4,
|
|
74
|
+
},
|
|
75
|
+
include: ["src/**/*"],
|
|
76
|
+
exclude: ["node_modules", "dist"],
|
|
77
|
+
}, null, 2);
|
|
78
|
+
}
|
|
79
|
+
export function getViteConfig() {
|
|
80
|
+
return `import { defineConfig } from "vite";
|
|
81
|
+
import { resolve } from "path";
|
|
82
|
+
|
|
83
|
+
export default defineConfig({
|
|
84
|
+
build: {
|
|
85
|
+
lib: {
|
|
86
|
+
entry: resolve(__dirname, "src/index.ts"),
|
|
87
|
+
formats: ["es"],
|
|
88
|
+
fileName: "index",
|
|
89
|
+
},
|
|
90
|
+
outDir: "dist",
|
|
91
|
+
rollupOptions: {
|
|
92
|
+
external: ["sprint-es", "express", "cors", "morgan", "serve-favicon", "dotenv"],
|
|
93
|
+
},
|
|
94
|
+
target: "ES2020",
|
|
95
|
+
},
|
|
96
|
+
resolve: {
|
|
97
|
+
alias: {
|
|
98
|
+
"@": resolve(__dirname, "src"),
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
export function getMainFile(language) {
|
|
105
|
+
if (language === "typescript") {
|
|
106
|
+
return `import Sprint from "sprint-es";
|
|
107
|
+
import { config } from "./sprint.config";
|
|
108
|
+
import homeRouter from "./routes/home";
|
|
109
|
+
|
|
110
|
+
const app = new Sprint(config);
|
|
111
|
+
|
|
112
|
+
app.use(homeRouter);
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
return `import Sprint from "sprint-es";
|
|
116
|
+
import { config } from "./sprint.config.js";
|
|
117
|
+
import homeRouter from "./routes/home.js";
|
|
118
|
+
|
|
119
|
+
const app = new Sprint(config);
|
|
120
|
+
|
|
121
|
+
app.use(homeRouter);
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
export function getHomeRoute(language) {
|
|
125
|
+
if (language === "typescript") {
|
|
126
|
+
return `import { Router } from "sprint-es";
|
|
127
|
+
|
|
128
|
+
const router = Router();
|
|
129
|
+
|
|
130
|
+
router.get("/", (req, res) => {
|
|
131
|
+
res.json({
|
|
132
|
+
message: "Hello World",
|
|
133
|
+
status: "ok"
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
export default router;
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
return `import { Router } from "sprint-es";
|
|
141
|
+
|
|
142
|
+
const router = Router();
|
|
143
|
+
|
|
144
|
+
router.get("/", (req, res) => {
|
|
145
|
+
res.json({
|
|
146
|
+
message: "Hello World",
|
|
147
|
+
status: "ok"
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
export default router;
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
export function getDockerfile(language) {
|
|
155
|
+
if (language === "typescript") {
|
|
156
|
+
return `FROM node:20-alpine
|
|
157
|
+
|
|
158
|
+
WORKDIR /app
|
|
159
|
+
|
|
160
|
+
COPY package*.json ./
|
|
161
|
+
|
|
162
|
+
RUN npm ci
|
|
163
|
+
|
|
164
|
+
COPY . .
|
|
165
|
+
|
|
166
|
+
RUN npm run build
|
|
167
|
+
|
|
168
|
+
EXPOSE 3000
|
|
169
|
+
|
|
170
|
+
CMD ["npm", "start"]
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
return `FROM node:20-alpine
|
|
174
|
+
|
|
175
|
+
WORKDIR /app
|
|
176
|
+
|
|
177
|
+
COPY package*.json ./
|
|
178
|
+
|
|
179
|
+
RUN npm ci
|
|
180
|
+
|
|
181
|
+
COPY . .
|
|
182
|
+
|
|
183
|
+
EXPOSE 3000
|
|
184
|
+
|
|
185
|
+
CMD ["npm", "start"]
|
|
186
|
+
`;
|
|
187
|
+
}
|
|
188
|
+
export function getDockerCompose(language) {
|
|
189
|
+
return `version: "3.8"
|
|
190
|
+
|
|
191
|
+
services:
|
|
192
|
+
app:
|
|
193
|
+
build: .
|
|
194
|
+
ports:
|
|
195
|
+
- "3000:3000"
|
|
196
|
+
environment:
|
|
197
|
+
- NODE_ENV=production
|
|
198
|
+
- PORT=3000
|
|
199
|
+
restart: unless-stopped
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
202
|
+
export function getGitignore() {
|
|
203
|
+
return `# Dependencies
|
|
204
|
+
node_modules/
|
|
205
|
+
npm-debug.log*
|
|
206
|
+
yarn-debug.log*
|
|
207
|
+
yarn-error.log*
|
|
208
|
+
|
|
209
|
+
# Build
|
|
210
|
+
dist/
|
|
211
|
+
build/
|
|
212
|
+
*.tsbuildinfo
|
|
213
|
+
|
|
214
|
+
# Environment
|
|
215
|
+
.env.development
|
|
216
|
+
.env.production
|
|
217
|
+
.env.local
|
|
218
|
+
.env.*.local
|
|
219
|
+
|
|
220
|
+
# IDE
|
|
221
|
+
.vscode/
|
|
222
|
+
.idea/
|
|
223
|
+
*.swp
|
|
224
|
+
*.swo
|
|
225
|
+
*~
|
|
226
|
+
|
|
227
|
+
# OS
|
|
228
|
+
.DS_Store
|
|
229
|
+
Thumbs.db
|
|
230
|
+
|
|
231
|
+
# Logs
|
|
232
|
+
logs/
|
|
233
|
+
*.log
|
|
234
|
+
|
|
235
|
+
# Test
|
|
236
|
+
coverage/
|
|
237
|
+
|
|
238
|
+
# Temporary
|
|
239
|
+
tmp/
|
|
240
|
+
temp/
|
|
241
|
+
`;
|
|
242
|
+
}
|
|
243
|
+
export function getDockerIgnore() {
|
|
244
|
+
return `node_modules
|
|
245
|
+
npm-debug.log
|
|
246
|
+
.env
|
|
247
|
+
.env.*
|
|
248
|
+
.git
|
|
249
|
+
.gitignore
|
|
250
|
+
README.md
|
|
251
|
+
dist
|
|
252
|
+
build
|
|
253
|
+
coverage
|
|
254
|
+
.vscode
|
|
255
|
+
.idea
|
|
256
|
+
*.log
|
|
257
|
+
tmp
|
|
258
|
+
temp
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
261
|
+
export function getSprintConfigFile(language, telemetry) {
|
|
262
|
+
if (language === "typescript") {
|
|
263
|
+
let config = `import type { SprintOptions } from "sprint-es";
|
|
264
|
+
|
|
265
|
+
export const config: SprintOptions = {
|
|
266
|
+
port: process.env.PORT ? parseInt(process.env.PORT) : 3000
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
`;
|
|
270
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
271
|
+
config += `import { initTelemetry } from "sprint-es/telemetry";
|
|
272
|
+
|
|
273
|
+
initTelemetry({
|
|
274
|
+
provider: "${telemetry}",
|
|
275
|
+
dsn: process.env.SENTRY_DSN || "",
|
|
276
|
+
environment: process.env.NODE_ENV || "development"
|
|
277
|
+
});
|
|
278
|
+
`;
|
|
279
|
+
}
|
|
280
|
+
else if (telemetry === "discord") {
|
|
281
|
+
config += `import { initTelemetry } from "sprint-es/telemetry";
|
|
282
|
+
|
|
283
|
+
initTelemetry({
|
|
284
|
+
provider: "discord",
|
|
285
|
+
webhookUrl: process.env.DISCORD_WEBHOOK_URL || ""
|
|
286
|
+
});
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
return config;
|
|
290
|
+
}
|
|
291
|
+
let config = `export const config = {
|
|
292
|
+
port: process.env.PORT ? parseInt(process.env.PORT) : 3000
|
|
293
|
+
};
|
|
294
|
+
`;
|
|
295
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
296
|
+
config += `
|
|
297
|
+
import { initTelemetry } from "sprint-es/telemetry";
|
|
298
|
+
|
|
299
|
+
initTelemetry({
|
|
300
|
+
provider: "${telemetry}",
|
|
301
|
+
dsn: process.env.SENTRY_DSN || "",
|
|
302
|
+
environment: process.env.NODE_ENV || "development"
|
|
303
|
+
});
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
else if (telemetry === "discord") {
|
|
307
|
+
config += `
|
|
308
|
+
import { initTelemetry } from "sprint-es/telemetry";
|
|
309
|
+
|
|
310
|
+
initTelemetry({
|
|
311
|
+
provider: "discord",
|
|
312
|
+
webhookUrl: process.env.DISCORD_WEBHOOK_URL || ""
|
|
313
|
+
});
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
return config;
|
|
317
|
+
}
|
|
318
|
+
export function getEnvExample(telemetry) {
|
|
319
|
+
let env = `PORT=3000
|
|
320
|
+
|
|
321
|
+
# Development: npm run dev (NODE_ENV=development)
|
|
322
|
+
# Production: npm start (NODE_ENV=production)
|
|
323
|
+
`;
|
|
324
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
325
|
+
env += `
|
|
326
|
+
# Sentry / GlitchTip (use GlitchTip DSN for self-hosted)
|
|
327
|
+
SENTRY_DSN=
|
|
328
|
+
`;
|
|
329
|
+
}
|
|
330
|
+
else if (telemetry === "discord") {
|
|
331
|
+
env += `
|
|
332
|
+
# Discord Webhook URL for error notifications
|
|
333
|
+
DISCORD_WEBHOOK_URL=
|
|
334
|
+
`;
|
|
335
|
+
}
|
|
336
|
+
return env;
|
|
337
|
+
}
|
|
338
|
+
export function getEnvDevelopment(telemetry) {
|
|
339
|
+
let env = `NODE_ENV=development
|
|
340
|
+
PORT=3000
|
|
341
|
+
`;
|
|
342
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
343
|
+
env += `
|
|
344
|
+
# Sentry / GlitchTip
|
|
345
|
+
SENTRY_DSN=
|
|
346
|
+
`;
|
|
347
|
+
}
|
|
348
|
+
else if (telemetry === "discord") {
|
|
349
|
+
env += `
|
|
350
|
+
# Discord Webhook URL
|
|
351
|
+
DISCORD_WEBHOOK_URL=
|
|
352
|
+
`;
|
|
353
|
+
}
|
|
354
|
+
return env;
|
|
355
|
+
}
|
|
356
|
+
export function getEnvProduction(telemetry) {
|
|
357
|
+
let env = `NODE_ENV=production
|
|
358
|
+
PORT=3000
|
|
359
|
+
`;
|
|
360
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
361
|
+
env += `
|
|
362
|
+
# Sentry / GlitchTip
|
|
363
|
+
SENTRY_DSN=
|
|
364
|
+
`;
|
|
365
|
+
}
|
|
366
|
+
else if (telemetry === "discord") {
|
|
367
|
+
env += `
|
|
368
|
+
# Discord Webhook URL
|
|
369
|
+
DISCORD_WEBHOOK_URL=
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
return env;
|
|
373
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { input, select, confirm } from "@inquirer/prompts";
|
|
6
|
+
import { validateProjectName } from "./validators";
|
|
7
|
+
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvExample, getEnvDevelopment, getEnvProduction } from "./generators";
|
|
8
|
+
export async function runCLI(args) {
|
|
9
|
+
const options = parseArgs(args);
|
|
10
|
+
console.log("\nš Welcome to Sprint - Quickly API Framework\n");
|
|
11
|
+
let projectName = options.projectName;
|
|
12
|
+
let language = options.language;
|
|
13
|
+
const telemetry = options.telemetry;
|
|
14
|
+
const useDocker = options.docker;
|
|
15
|
+
if (!projectName) {
|
|
16
|
+
projectName = await getProjectName();
|
|
17
|
+
}
|
|
18
|
+
const error = validateProjectName(projectName);
|
|
19
|
+
if (error) {
|
|
20
|
+
console.error(`\nā Error: ${error}\n`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
if (!language) {
|
|
24
|
+
language = await selectLanguage();
|
|
25
|
+
}
|
|
26
|
+
console.log(`\nā
Creating Sprint project: ${projectName === "." ? "current directory" : projectName} with ${language === "typescript" ? "TypeScript" : "JavaScript"}\n`);
|
|
27
|
+
await createProject(projectName, language, telemetry, useDocker);
|
|
28
|
+
console.log("\nā
Project created successfully!");
|
|
29
|
+
let installDeps = true;
|
|
30
|
+
if (options.skipInstall) {
|
|
31
|
+
installDeps = false;
|
|
32
|
+
}
|
|
33
|
+
else if (!options.skipPrompts) {
|
|
34
|
+
installDeps = await confirm({
|
|
35
|
+
message: "Do you want to install dependencies now?",
|
|
36
|
+
default: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (installDeps) {
|
|
40
|
+
console.log("\nš¦ Installing dependencies...\n");
|
|
41
|
+
const targetDir = projectName === "." ? process.cwd() : join(process.cwd(), projectName);
|
|
42
|
+
try {
|
|
43
|
+
execSync("npm install", { cwd: targetDir, stdio: "inherit" });
|
|
44
|
+
console.log("\nā
Dependencies installed successfully!");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
console.error("\nā Error installing dependencies. Please run 'npm install' manually.");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log("\nš¦ Next steps:");
|
|
51
|
+
const cdCmd = projectName === "." ? "" : `cd ${projectName} && `;
|
|
52
|
+
if (!installDeps) {
|
|
53
|
+
console.log(` ${cdCmd}npm install`);
|
|
54
|
+
}
|
|
55
|
+
console.log(` ${cdCmd}npm run dev`);
|
|
56
|
+
console.log("\n");
|
|
57
|
+
}
|
|
58
|
+
function parseArgs(args) {
|
|
59
|
+
const options = {};
|
|
60
|
+
const hasTs = args.includes("--ts") || args.includes("--typescript");
|
|
61
|
+
const hasJs = args.includes("--js") || args.includes("--javascript");
|
|
62
|
+
const hasName = args.indexOf("--name");
|
|
63
|
+
const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
|
|
64
|
+
if (args.includes("--yes") || args.includes("-y")) {
|
|
65
|
+
options.skipPrompts = true;
|
|
66
|
+
options.language = "typescript";
|
|
67
|
+
}
|
|
68
|
+
else if (hasTs) {
|
|
69
|
+
options.language = "typescript";
|
|
70
|
+
}
|
|
71
|
+
else if (hasJs) {
|
|
72
|
+
options.language = "javascript";
|
|
73
|
+
}
|
|
74
|
+
if (hasName !== -1 && args[hasName + 1]) {
|
|
75
|
+
options.projectName = args[hasName + 1];
|
|
76
|
+
}
|
|
77
|
+
if (args.includes("--current")) {
|
|
78
|
+
options.projectName = ".";
|
|
79
|
+
}
|
|
80
|
+
if (args.includes("--docker")) {
|
|
81
|
+
options.docker = true;
|
|
82
|
+
}
|
|
83
|
+
if (args.includes("--no-install")) {
|
|
84
|
+
options.skipInstall = true;
|
|
85
|
+
}
|
|
86
|
+
if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
|
|
87
|
+
options.telemetry = telemetryArg;
|
|
88
|
+
}
|
|
89
|
+
return options;
|
|
90
|
+
}
|
|
91
|
+
async function getProjectName() {
|
|
92
|
+
const name = await input({
|
|
93
|
+
message: "Enter project name:",
|
|
94
|
+
validate: (value) => {
|
|
95
|
+
return validateProjectName(value) || true;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
return name;
|
|
99
|
+
}
|
|
100
|
+
async function selectLanguage() {
|
|
101
|
+
const language = await select({
|
|
102
|
+
message: "Select your preferred language:",
|
|
103
|
+
choices: [
|
|
104
|
+
{
|
|
105
|
+
name: "TypeScript",
|
|
106
|
+
value: "typescript",
|
|
107
|
+
description: "Recommended - Type safety and better developer experience",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "JavaScript",
|
|
111
|
+
value: "javascript",
|
|
112
|
+
description: "Vanilla JavaScript for simpler projects",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
return language;
|
|
117
|
+
}
|
|
118
|
+
async function selectTelemetry() {
|
|
119
|
+
const telemetry = await select({
|
|
120
|
+
message: "Select error tracking/telemetry solution:",
|
|
121
|
+
choices: [
|
|
122
|
+
{
|
|
123
|
+
name: "None",
|
|
124
|
+
value: "none",
|
|
125
|
+
description: "No error tracking integration",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "Sentry",
|
|
129
|
+
value: "sentry",
|
|
130
|
+
description: "Full-featured error tracking (free tier available)",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "GlitchTip",
|
|
134
|
+
value: "glitchtip",
|
|
135
|
+
description: "Simple error tracking, can be self-hosted",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "Discord Webhook",
|
|
139
|
+
value: "discord",
|
|
140
|
+
description: "Send error notifications to Discord channel",
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
return telemetry;
|
|
145
|
+
}
|
|
146
|
+
async function createProject(projectName, language, telemetryArg, useDockerArg) {
|
|
147
|
+
const isCurrentDir = projectName === ".";
|
|
148
|
+
const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
|
|
149
|
+
if (!isCurrentDir && existsSync(targetDir)) {
|
|
150
|
+
console.error(`Error: Directory ${projectName} already exists`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
if (!isCurrentDir) {
|
|
154
|
+
await mkdir(targetDir, { recursive: true });
|
|
155
|
+
}
|
|
156
|
+
let telemetry = telemetryArg || "none";
|
|
157
|
+
if (!telemetryArg) {
|
|
158
|
+
telemetry = await selectTelemetry();
|
|
159
|
+
}
|
|
160
|
+
let useDocker = useDockerArg || false;
|
|
161
|
+
if (!useDockerArg) {
|
|
162
|
+
useDocker = await confirm({
|
|
163
|
+
message: "Do you want to add Docker support?",
|
|
164
|
+
default: false,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
let pkgJson;
|
|
168
|
+
if (language === "typescript") {
|
|
169
|
+
pkgJson = getTypeScriptPackageJson(projectName, telemetry);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
pkgJson = getJavaScriptPackageJson(projectName, telemetry);
|
|
173
|
+
}
|
|
174
|
+
await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
175
|
+
if (language === "typescript") {
|
|
176
|
+
await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
|
|
177
|
+
await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
|
|
178
|
+
await writeFile(join(targetDir, "sprint.config.ts"), getSprintConfigFile(language, telemetry));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
await writeFile(join(targetDir, "sprint.config.js"), getSprintConfigFile(language, telemetry));
|
|
182
|
+
}
|
|
183
|
+
const srcDir = join(targetDir, "src");
|
|
184
|
+
await mkdir(srcDir, { recursive: true });
|
|
185
|
+
await mkdir(join(srcDir, "middlewares"), { recursive: true });
|
|
186
|
+
await mkdir(join(srcDir, "routes"), { recursive: true });
|
|
187
|
+
await mkdir(join(srcDir, "controllers"), { recursive: true });
|
|
188
|
+
await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
|
|
189
|
+
await writeFile(join(srcDir, "controllers", ".gitkeep"), "");
|
|
190
|
+
await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getMainFile(language));
|
|
191
|
+
await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
|
|
192
|
+
await writeFile(join(targetDir, ".env.example"), getEnvExample(telemetry));
|
|
193
|
+
await writeFile(join(targetDir, ".env.development"), getEnvDevelopment(telemetry));
|
|
194
|
+
await writeFile(join(targetDir, ".env.production"), getEnvProduction(telemetry));
|
|
195
|
+
await writeFile(join(targetDir, ".gitignore"), getGitignore());
|
|
196
|
+
if (useDocker) {
|
|
197
|
+
await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
|
|
198
|
+
await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
|
|
199
|
+
await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function validateProjectName(name) {
|
|
2
|
+
if (!name.trim())
|
|
3
|
+
return "Please enter a project name";
|
|
4
|
+
const n = name.trim();
|
|
5
|
+
if (n !== n.toLowerCase()) {
|
|
6
|
+
return "Project name must be lowercase";
|
|
7
|
+
}
|
|
8
|
+
if (n.length > 214) {
|
|
9
|
+
return "Project name must be less than 214 characters";
|
|
10
|
+
}
|
|
11
|
+
if (n.startsWith("-") || n.startsWith(".")) {
|
|
12
|
+
return "Project name cannot start with - or .";
|
|
13
|
+
}
|
|
14
|
+
if (/[~!@#$%^&*(){}[\]<>?:]/.test(n)) {
|
|
15
|
+
return "Project name cannot contain special characters (only letters, numbers, - and _)";
|
|
16
|
+
}
|
|
17
|
+
if (n !== encodeURIComponent(n)) {
|
|
18
|
+
return "Project name must be URL-safe";
|
|
19
|
+
}
|
|
20
|
+
const reserved = ["node_modules", "favicon.ico"];
|
|
21
|
+
if (reserved.includes(n.toLowerCase())) {
|
|
22
|
+
return `Cannot use "${n}" as project name`;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
}
|
|
2
|
+
"name": "create-sprint",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Create a new Sprint API project",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-sprint": "./dist/cli.js",
|
|
8
|
+
"sprint": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*",
|
|
12
|
+
"src/**/*"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && node -e \"const fs=require('fs');const p='dist/cli.js';const c=fs.readFileSync(p,'utf8');fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c)\"",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
17
|
+
"start": "node dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"sprint",
|
|
21
|
+
"api",
|
|
22
|
+
"framework",
|
|
23
|
+
"init",
|
|
24
|
+
"create"
|
|
25
|
+
],
|
|
26
|
+
"author": "TPEOficial LLC",
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@inquirer/prompts": "^7.10.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsx": "^4.19.0",
|
|
33
|
+
"typescript": "^5.9.3"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|