create-sprint 0.0.8 → 0.0.34

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.
@@ -1,7 +1,7 @@
1
1
  export function getTypeScriptPackageJson(name, telemetry) {
2
2
  const deps = {
3
- "sprint-es": "^0.0.28",
4
- "sprint": "^0.0.1",
3
+ "sprint-es": "^0.0.35",
4
+ "node-cron": "^3.0.3",
5
5
  dotenv: "^17.0.0",
6
6
  };
7
7
  const devDeps = {
@@ -21,9 +21,9 @@ export function getTypeScriptPackageJson(name, telemetry) {
21
21
  description: "Sprint API",
22
22
  main: "dist/index.js",
23
23
  scripts: {
24
- build: "sprint build",
25
- start: "sprint start",
26
- dev: "sprint dev",
24
+ build: "sprint-es build",
25
+ start: "sprint-es start",
26
+ dev: "sprint-es dev",
27
27
  },
28
28
  dependencies: deps,
29
29
  devDependencies: devDeps,
@@ -31,8 +31,8 @@ export function getTypeScriptPackageJson(name, telemetry) {
31
31
  }
32
32
  export function getJavaScriptPackageJson(name, telemetry) {
33
33
  const deps = {
34
- "sprint-es": "^0.0.28",
35
- "sprint": "^0.0.1",
34
+ "sprint-es": "^0.0.35",
35
+ "node-cron": "^3.0.3",
36
36
  dotenv: "^17.0.0",
37
37
  };
38
38
  if (telemetry === "sentry" || telemetry === "glitchtip") {
@@ -48,9 +48,9 @@ export function getJavaScriptPackageJson(name, telemetry) {
48
48
  main: "src/index.js",
49
49
  type: "module",
50
50
  scripts: {
51
- build: "sprint build",
52
- start: "sprint start",
53
- dev: "sprint dev",
51
+ build: "sprint-es build",
52
+ start: "sprint-es start",
53
+ dev: "sprint-es dev",
54
54
  },
55
55
  dependencies: deps,
56
56
  };
@@ -235,22 +235,12 @@ export function getHomeSchema(language) {
235
235
  if (language === "typescript") {
236
236
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
237
237
 
238
- export const homeSchema = defineRouteSchema({
239
- input: z.object({
240
- message: z.string(),
241
- status: z.string()
242
- })
243
- });
238
+ export const homeSchema = defineRouteSchema({});
244
239
  `;
245
240
  }
246
241
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
247
242
 
248
- export const homeSchema = defineRouteSchema({
249
- input: z.object({
250
- message: z.string(),
251
- status: z.string()
252
- })
253
- });
243
+ export const homeSchema = defineRouteSchema({});
254
244
  `;
255
245
  }
256
246
  export function getAdminSchema(language) {
@@ -258,16 +248,11 @@ export function getAdminSchema(language) {
258
248
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
259
249
 
260
250
  export const adminSchema = defineRouteSchema({
261
- params: z.object({
251
+ queryParams: z.object({
262
252
  id: z.string().uuid()
263
253
  }),
264
254
  body: z.object({
265
255
  name: z.string().min(1),
266
- email: z.email().optional()
267
- }),
268
- input: z.object({
269
- id: z.string().uuid(),
270
- name: z.string(),
271
256
  email: z.string().email().optional()
272
257
  })
273
258
  });
@@ -276,16 +261,11 @@ export const adminSchema = defineRouteSchema({
276
261
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
277
262
 
278
263
  export const adminSchema = defineRouteSchema({
279
- params: z.object({
264
+ queryParams: z.object({
280
265
  id: z.string().uuid()
281
266
  }),
282
267
  body: z.object({
283
268
  name: z.string().min(1),
284
- email: z.email().optional()
285
- }),
286
- input: z.object({
287
- id: z.string().uuid(),
288
- name: z.string(),
289
269
  email: z.string().email().optional()
290
270
  })
291
271
  });
@@ -566,3 +546,27 @@ DISCORD_WEBHOOK_URL=
566
546
  }
567
547
  return env;
568
548
  }
549
+ export function getExampleCronJob(language) {
550
+ if (language === "typescript") {
551
+ return `import { defineCronJob } from "sprint-es/cronjobs";
552
+
553
+ export default defineCronJob({
554
+ name: "daily-task",
555
+ cronExpression: "0 21 * * *",
556
+ handler: () => {
557
+ console.log("Hello World from cronjob!");
558
+ }
559
+ });
560
+ `;
561
+ }
562
+ return `import { defineCronJob } from "sprint-es/cronjobs";
563
+
564
+ export default defineCronJob({
565
+ name: "daily-task",
566
+ cronExpression: "0 21 * * *",
567
+ handler: () => {
568
+ console.log("Hello World from cronjob!");
569
+ }
570
+ });
571
+ `;
572
+ }
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { mkdir, writeFile } from "fs/promises";
4
4
  import { join } from "path";
5
5
  import { input, select, confirm } from "@inquirer/prompts";
6
6
  import { validateProjectName } from "./validators.js";
7
- import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction } from "./generators.js";
7
+ import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob } from "./generators.js";
8
8
  export async function runCLI(args) {
9
9
  const options = parseArgs(args);
10
10
  console.log("\n🚀 Welcome to Sprint - Quickly API Framework\n");
@@ -61,46 +61,39 @@ function parseArgs(args) {
61
61
  const hasJs = args.includes("--js") || args.includes("--javascript");
62
62
  const hasName = args.indexOf("--name");
63
63
  const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
64
- if (args.includes("--yes") || args.includes("-y")) {
64
+ if (args.includes("--yes") || args.includes("-y"))
65
65
  options.skipPrompts = true;
66
- }
67
66
  if (!options.skipPrompts) {
68
- if (hasTs) {
67
+ if (hasTs)
69
68
  options.language = "typescript";
70
- }
71
- else if (hasJs) {
69
+ else if (hasJs)
72
70
  options.language = "javascript";
73
- }
74
71
  }
75
- else {
72
+ else
76
73
  options.language = "typescript";
77
- }
78
- if (hasName !== -1 && args[hasName + 1]) {
74
+ if (hasName !== -1 && args[hasName + 1])
79
75
  options.projectName = args[hasName + 1];
80
- }
81
- if (args.includes("--current")) {
76
+ if (args.includes("--current"))
82
77
  options.projectName = ".";
83
- }
84
- if (args.includes("--docker")) {
78
+ if (args.includes("--docker"))
85
79
  options.docker = true;
86
- }
87
- if (args.includes("--no-install")) {
80
+ if (args.includes("--no-install"))
88
81
  options.skipInstall = true;
89
- }
90
- if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
82
+ if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg))
91
83
  options.telemetry = telemetryArg;
92
- }
93
84
  return options;
94
85
  }
86
+ ;
95
87
  async function getProjectName() {
96
88
  const name = await input({
97
89
  message: "Enter project name:",
98
90
  validate: (value) => {
99
91
  return validateProjectName(value) || true;
100
- },
92
+ }
101
93
  });
102
94
  return name;
103
95
  }
96
+ ;
104
97
  async function selectLanguage() {
105
98
  const language = await select({
106
99
  message: "Select your preferred language:",
@@ -114,11 +107,12 @@ async function selectLanguage() {
114
107
  name: "JavaScript",
115
108
  value: "javascript",
116
109
  description: "Vanilla JavaScript for simpler projects",
117
- },
118
- ],
110
+ }
111
+ ]
119
112
  });
120
113
  return language;
121
114
  }
115
+ ;
122
116
  async function selectTelemetry() {
123
117
  const telemetry = await select({
124
118
  message: "Select error tracking/telemetry solution:",
@@ -142,11 +136,12 @@ async function selectTelemetry() {
142
136
  name: "Discord Webhook",
143
137
  value: "discord",
144
138
  description: "Send error notifications to Discord channel",
145
- },
146
- ],
139
+ }
140
+ ]
147
141
  });
148
142
  return telemetry;
149
143
  }
144
+ ;
150
145
  async function createProject(projectName, language, telemetryArg, useDockerArg) {
151
146
  const isCurrentDir = projectName === ".";
152
147
  const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
@@ -154,13 +149,11 @@ async function createProject(projectName, language, telemetryArg, useDockerArg)
154
149
  console.error(`Error: Directory ${projectName} already exists`);
155
150
  process.exit(1);
156
151
  }
157
- if (!isCurrentDir) {
152
+ if (!isCurrentDir)
158
153
  await mkdir(targetDir, { recursive: true });
159
- }
160
154
  let telemetry = telemetryArg || "none";
161
- if (!telemetryArg) {
155
+ if (!telemetryArg)
162
156
  telemetry = await selectTelemetry();
163
- }
164
157
  let useDocker = useDockerArg || false;
165
158
  if (!useDockerArg) {
166
159
  useDocker = await confirm({
@@ -169,27 +162,25 @@ async function createProject(projectName, language, telemetryArg, useDockerArg)
169
162
  });
170
163
  }
171
164
  let pkgJson;
172
- if (language === "typescript") {
165
+ if (language === "typescript")
173
166
  pkgJson = getTypeScriptPackageJson(projectName, telemetry);
174
- }
175
- else {
167
+ else
176
168
  pkgJson = getJavaScriptPackageJson(projectName, telemetry);
177
- }
178
169
  await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
179
170
  if (language === "typescript") {
180
171
  await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
181
172
  await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
182
173
  await writeFile(join(targetDir, "sprint.config.ts"), getSprintConfigFile(language, telemetry));
183
174
  }
184
- else {
175
+ else
185
176
  await writeFile(join(targetDir, "sprint.config.js"), getSprintConfigFile(language, telemetry));
186
- }
187
177
  const srcDir = join(targetDir, "src");
188
178
  await mkdir(srcDir, { recursive: true });
189
179
  await mkdir(join(srcDir, "middlewares"), { recursive: true });
190
180
  await mkdir(join(srcDir, "routes"), { recursive: true });
191
181
  await mkdir(join(srcDir, "controllers"), { recursive: true });
192
182
  await mkdir(join(srcDir, "schemas"), { recursive: true });
183
+ await mkdir(join(srcDir, "cronjobs"), { recursive: true });
193
184
  await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
194
185
  await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getMainFile(language));
195
186
  await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
@@ -199,6 +190,7 @@ async function createProject(projectName, language, telemetryArg, useDockerArg)
199
190
  await writeFile(join(srcDir, "middlewares", "auth." + (language === "typescript" ? "ts" : "js")), getAuthMiddleware(language));
200
191
  await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
201
192
  await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
193
+ await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
202
194
  await writeFile(join(targetDir, ".env.development.example"), getEnvDevelopment(telemetry));
203
195
  await writeFile(join(targetDir, ".env.production.example"), getEnvProduction(telemetry));
204
196
  await writeFile(join(targetDir, ".env.development"), "");
@@ -210,3 +202,4 @@ async function createProject(projectName, language, telemetryArg, useDockerArg)
210
202
  await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
211
203
  }
212
204
  }
205
+ ;
@@ -2,24 +2,19 @@ export function validateProjectName(name) {
2
2
  if (!name.trim())
3
3
  return "Please enter a project name";
4
4
  const n = name.trim();
5
- if (n !== n.toLowerCase()) {
5
+ if (n !== n.toLowerCase())
6
6
  return "Project name must be lowercase";
7
- }
8
- if (n.length > 214) {
7
+ if (n.length > 214)
9
8
  return "Project name must be less than 214 characters";
10
- }
11
- if (n.startsWith("-") || n.startsWith(".")) {
9
+ if (n.startsWith("-") || n.startsWith("."))
12
10
  return "Project name cannot start with - or .";
13
- }
14
- if (/[~!@#$%^&*(){}[\]<>?:]/.test(n)) {
11
+ if (/[~!@#$%^&*(){}[\]<>?:]/.test(n))
15
12
  return "Project name cannot contain special characters (only letters, numbers, - and _)";
16
- }
17
- if (n !== encodeURIComponent(n)) {
13
+ if (n !== encodeURIComponent(n))
18
14
  return "Project name must be URL-safe";
19
- }
20
15
  const reserved = ["node_modules", "favicon.ico"];
21
- if (reserved.includes(n.toLowerCase())) {
16
+ if (reserved.includes(n.toLowerCase()))
22
17
  return `Cannot use "${n}" as project name`;
23
- }
24
18
  return null;
25
19
  }
20
+ ;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sprint",
3
- "version": "0.0.8",
3
+ "version": "0.0.34",
4
4
  "description": "Create a new Sprint API project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "build": "tsc && node -e \"const fs=require('fs');const p='dist/cli.js';let c=fs.readFileSync(p,'utf8');if(!c.startsWith('#!')){fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c)}\"",
16
- "prepublishOnly": "npm run build",
16
+ "prepublishOnly": "npm version patch && npm run build",
17
17
  "start": "node dist/cli.js"
18
18
  },
19
19
  "keywords": [
@@ -29,10 +29,11 @@
29
29
  "@inquirer/prompts": "^7.10.1"
30
30
  },
31
31
  "devDependencies": {
32
+ "@types/node": "^25.3.3",
32
33
  "tsx": "^4.19.0",
33
34
  "typescript": "^5.9.3"
34
35
  },
35
36
  "engines": {
36
37
  "node": ">=18.0.0"
37
38
  }
38
- }
39
+ }
package/src/cli.ts CHANGED
@@ -23,4 +23,4 @@ if (args.includes("--help") || args.includes("-h")) {
23
23
  process.exit(0);
24
24
  }
25
25
 
26
- runCLI(args).catch(console.error);
26
+ runCLI(args).catch(console.error);
package/src/generators.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export function getTypeScriptPackageJson(name: string, telemetry: string) {
2
2
  const deps: Record<string, string> = {
3
- "sprint-es": "^0.0.28",
4
- "sprint": "^0.0.1",
3
+ "sprint-es": "^0.0.35",
4
+ "node-cron": "^3.0.3",
5
5
  dotenv: "^17.0.0",
6
6
  };
7
7
 
@@ -23,9 +23,9 @@ export function getTypeScriptPackageJson(name: string, telemetry: string) {
23
23
  description: "Sprint API",
24
24
  main: "dist/index.js",
25
25
  scripts: {
26
- build: "sprint build",
27
- start: "sprint start",
28
- dev: "sprint dev",
26
+ build: "sprint-es build",
27
+ start: "sprint-es start",
28
+ dev: "sprint-es dev",
29
29
  },
30
30
  dependencies: deps,
31
31
  devDependencies: devDeps,
@@ -34,8 +34,8 @@ export function getTypeScriptPackageJson(name: string, telemetry: string) {
34
34
 
35
35
  export function getJavaScriptPackageJson(name: string, telemetry: string) {
36
36
  const deps: Record<string, string> = {
37
- "sprint-es": "^0.0.28",
38
- "sprint": "^0.0.1",
37
+ "sprint-es": "^0.0.35",
38
+ "node-cron": "^3.0.3",
39
39
  dotenv: "^17.0.0",
40
40
  };
41
41
 
@@ -52,9 +52,9 @@ export function getJavaScriptPackageJson(name: string, telemetry: string) {
52
52
  main: "src/index.js",
53
53
  type: "module",
54
54
  scripts: {
55
- build: "sprint build",
56
- start: "sprint start",
57
- dev: "sprint dev",
55
+ build: "sprint-es build",
56
+ start: "sprint-es start",
57
+ dev: "sprint-es dev",
58
58
  },
59
59
  dependencies: deps,
60
60
  };
@@ -248,22 +248,12 @@ export function getHomeSchema(language: string) {
248
248
  if (language === "typescript") {
249
249
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
250
250
 
251
- export const homeSchema = defineRouteSchema({
252
- input: z.object({
253
- message: z.string(),
254
- status: z.string()
255
- })
256
- });
251
+ export const homeSchema = defineRouteSchema({});
257
252
  `;
258
253
  }
259
254
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
260
255
 
261
- export const homeSchema = defineRouteSchema({
262
- input: z.object({
263
- message: z.string(),
264
- status: z.string()
265
- })
266
- });
256
+ export const homeSchema = defineRouteSchema({});
267
257
  `;
268
258
  }
269
259
 
@@ -272,16 +262,11 @@ export function getAdminSchema(language: string) {
272
262
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
273
263
 
274
264
  export const adminSchema = defineRouteSchema({
275
- params: z.object({
265
+ queryParams: z.object({
276
266
  id: z.string().uuid()
277
267
  }),
278
268
  body: z.object({
279
269
  name: z.string().min(1),
280
- email: z.email().optional()
281
- }),
282
- input: z.object({
283
- id: z.string().uuid(),
284
- name: z.string(),
285
270
  email: z.string().email().optional()
286
271
  })
287
272
  });
@@ -290,16 +275,11 @@ export const adminSchema = defineRouteSchema({
290
275
  return `import { z, defineRouteSchema } from "sprint-es/schemas";
291
276
 
292
277
  export const adminSchema = defineRouteSchema({
293
- params: z.object({
278
+ queryParams: z.object({
294
279
  id: z.string().uuid()
295
280
  }),
296
281
  body: z.object({
297
282
  name: z.string().min(1),
298
- email: z.email().optional()
299
- }),
300
- input: z.object({
301
- id: z.string().uuid(),
302
- name: z.string(),
303
283
  email: z.string().email().optional()
304
284
  })
305
285
  });
@@ -595,3 +575,28 @@ DISCORD_WEBHOOK_URL=
595
575
 
596
576
  return env;
597
577
  }
578
+
579
+ export function getExampleCronJob(language: string) {
580
+ if (language === "typescript") {
581
+ return `import { defineCronJob } from "sprint-es/cronjobs";
582
+
583
+ export default defineCronJob({
584
+ name: "daily-task",
585
+ cronExpression: "0 21 * * *",
586
+ handler: () => {
587
+ console.log("Hello World from cronjob!");
588
+ }
589
+ });
590
+ `;
591
+ }
592
+ return `import { defineCronJob } from "sprint-es/cronjobs";
593
+
594
+ export default defineCronJob({
595
+ name: "daily-task",
596
+ cronExpression: "0 21 * * *",
597
+ handler: () => {
598
+ console.log("Hello World from cronjob!");
599
+ }
600
+ });
601
+ `;
602
+ }
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@ import { mkdir, writeFile } from "fs/promises";
4
4
  import { join } from "path";
5
5
  import { input, select, confirm } from "@inquirer/prompts";
6
6
  import { validateProjectName } from "./validators.js";
7
- import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction } from "./generators.js";
7
+ import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getViteConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob } from "./generators.js";
8
8
 
9
9
  export interface CLIOptions {
10
10
  projectName?: string;
@@ -83,53 +83,36 @@ function parseArgs(args: string[]): CLIOptions {
83
83
  const hasName = args.indexOf("--name");
84
84
  const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
85
85
 
86
- if (args.includes("--yes") || args.includes("-y")) {
87
- options.skipPrompts = true;
88
- }
86
+ if (args.includes("--yes") || args.includes("-y")) options.skipPrompts = true;
89
87
 
90
88
  if (!options.skipPrompts) {
91
- if (hasTs) {
92
- options.language = "typescript";
93
- } else if (hasJs) {
94
- options.language = "javascript";
95
- }
96
- } else {
97
- options.language = "typescript";
98
- }
89
+ if (hasTs) options.language = "typescript";
90
+ else if (hasJs) options.language = "javascript";
91
+ } else options.language = "typescript";
92
+
93
+ if (hasName !== -1 && args[hasName + 1]) options.projectName = args[hasName + 1];
99
94
 
100
- if (hasName !== -1 && args[hasName + 1]) {
101
- options.projectName = args[hasName + 1];
102
- }
103
-
104
- if (args.includes("--current")) {
105
- options.projectName = ".";
106
- }
95
+ if (args.includes("--current")) options.projectName = ".";
107
96
 
108
- if (args.includes("--docker")) {
109
- options.docker = true;
110
- }
111
-
112
- if (args.includes("--no-install")) {
113
- options.skipInstall = true;
114
- }
115
-
116
- if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
117
- options.telemetry = telemetryArg as CLIOptions["telemetry"];
118
- }
97
+ if (args.includes("--docker")) options.docker = true;
98
+
99
+ if (args.includes("--no-install")) options.skipInstall = true;
119
100
 
101
+ if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) options.telemetry = telemetryArg as CLIOptions["telemetry"];
102
+
120
103
  return options;
121
- }
104
+ };
122
105
 
123
106
  async function getProjectName(): Promise<string> {
124
107
  const name = await input({
125
108
  message: "Enter project name:",
126
109
  validate: (value) => {
127
110
  return validateProjectName(value) || true;
128
- },
111
+ }
129
112
  });
130
113
 
131
114
  return name;
132
- }
115
+ };
133
116
 
134
117
  async function selectLanguage(): Promise<"typescript" | "javascript"> {
135
118
  const language = await select({
@@ -144,12 +127,12 @@ async function selectLanguage(): Promise<"typescript" | "javascript"> {
144
127
  name: "JavaScript",
145
128
  value: "javascript",
146
129
  description: "Vanilla JavaScript for simpler projects",
147
- },
148
- ],
130
+ }
131
+ ]
149
132
  });
150
133
 
151
134
  return language as "typescript" | "javascript";
152
- }
135
+ };
153
136
 
154
137
  async function selectTelemetry(): Promise<"none" | "sentry" | "glitchtip" | "discord"> {
155
138
  const telemetry = await select({
@@ -174,12 +157,12 @@ async function selectTelemetry(): Promise<"none" | "sentry" | "glitchtip" | "dis
174
157
  name: "Discord Webhook",
175
158
  value: "discord",
176
159
  description: "Send error notifications to Discord channel",
177
- },
178
- ],
160
+ }
161
+ ]
179
162
  });
180
163
 
181
164
  return telemetry as "none" | "sentry" | "glitchtip" | "discord";
182
- }
165
+ };
183
166
 
184
167
  async function createProject(
185
168
  projectName: string,
@@ -195,14 +178,10 @@ async function createProject(
195
178
  process.exit(1);
196
179
  }
197
180
 
198
- if (!isCurrentDir) {
199
- await mkdir(targetDir, { recursive: true });
200
- }
181
+ if (!isCurrentDir) await mkdir(targetDir, { recursive: true });
201
182
 
202
183
  let telemetry = telemetryArg || "none";
203
- if (!telemetryArg) {
204
- telemetry = await selectTelemetry();
205
- }
184
+ if (!telemetryArg) telemetry = await selectTelemetry();
206
185
 
207
186
  let useDocker = useDockerArg || false;
208
187
  if (!useDockerArg) {
@@ -213,22 +192,17 @@ async function createProject(
213
192
  }
214
193
 
215
194
  let pkgJson;
216
- if (language === "typescript") {
217
- pkgJson = getTypeScriptPackageJson(projectName, telemetry);
218
- } else {
219
- pkgJson = getJavaScriptPackageJson(projectName, telemetry);
220
- }
221
-
195
+ if (language === "typescript") pkgJson = getTypeScriptPackageJson(projectName, telemetry);
196
+ else pkgJson = getJavaScriptPackageJson(projectName, telemetry);
197
+
222
198
  await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
223
199
 
224
200
  if (language === "typescript") {
225
201
  await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
226
202
  await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
227
203
  await writeFile(join(targetDir, "sprint.config.ts"), getSprintConfigFile(language, telemetry));
228
- } else {
229
- await writeFile(join(targetDir, "sprint.config.js"), getSprintConfigFile(language, telemetry));
230
- }
231
-
204
+ } else await writeFile(join(targetDir, "sprint.config.js"), getSprintConfigFile(language, telemetry));
205
+
232
206
  const srcDir = join(targetDir, "src");
233
207
  await mkdir(srcDir, { recursive: true });
234
208
 
@@ -236,6 +210,7 @@ async function createProject(
236
210
  await mkdir(join(srcDir, "routes"), { recursive: true });
237
211
  await mkdir(join(srcDir, "controllers"), { recursive: true });
238
212
  await mkdir(join(srcDir, "schemas"), { recursive: true });
213
+ await mkdir(join(srcDir, "cronjobs"), { recursive: true });
239
214
 
240
215
  await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
241
216
 
@@ -252,6 +227,8 @@ async function createProject(
252
227
  await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
253
228
  await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
254
229
 
230
+ await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
231
+
255
232
  await writeFile(join(targetDir, ".env.development.example"), getEnvDevelopment(telemetry));
256
233
  await writeFile(join(targetDir, ".env.production.example"), getEnvProduction(telemetry));
257
234
 
@@ -265,4 +242,4 @@ async function createProject(
265
242
  await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
266
243
  await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
267
244
  }
268
- }
245
+ };
package/src/validators.ts CHANGED
@@ -3,30 +3,19 @@ export function validateProjectName(name: string): string | null {
3
3
 
4
4
  const n = name.trim();
5
5
 
6
- if (n !== n.toLowerCase()) {
7
- return "Project name must be lowercase";
8
- }
6
+ if (n !== n.toLowerCase()) return "Project name must be lowercase";
9
7
 
10
- if (n.length > 214) {
11
- return "Project name must be less than 214 characters";
12
- }
8
+ if (n.length > 214) return "Project name must be less than 214 characters";
13
9
 
14
- if (n.startsWith("-") || n.startsWith(".")) {
15
- return "Project name cannot start with - or .";
16
- }
10
+ if (n.startsWith("-") || n.startsWith(".")) return "Project name cannot start with - or .";
17
11
 
18
- if (/[~!@#$%^&*(){}[\]<>?:]/.test(n)) {
19
- return "Project name cannot contain special characters (only letters, numbers, - and _)";
20
- }
21
-
22
- if (n !== encodeURIComponent(n)) {
23
- return "Project name must be URL-safe";
24
- }
12
+ if (/[~!@#$%^&*(){}[\]<>?:]/.test(n)) return "Project name cannot contain special characters (only letters, numbers, - and _)";
13
+
14
+ if (n !== encodeURIComponent(n)) return "Project name must be URL-safe";
15
+
25
16
 
26
17
  const reserved = ["node_modules", "favicon.ico"];
27
- if (reserved.includes(n.toLowerCase())) {
28
- return `Cannot use "${n}" as project name`;
29
- }
30
-
18
+ if (reserved.includes(n.toLowerCase())) return `Cannot use "${n}" as project name`;
19
+
31
20
  return null;
32
- }
21
+ };