@dawitworku/projectcli 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/add.js +156 -171
- package/src/detect.js +19 -1
- package/src/index.js +171 -48
- package/src/libraries.js +79 -1
- package/src/pm.js +32 -0
- package/src/registry.js +399 -1
package/src/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const path = require("node:path");
|
|
2
2
|
const fs = require("node:fs");
|
|
3
3
|
const os = require("node:os");
|
|
4
|
+
const chalk = require("chalk");
|
|
5
|
+
const boxen = require("boxen");
|
|
6
|
+
const figlet = require("figlet");
|
|
7
|
+
const gradient = require("gradient-string");
|
|
4
8
|
const inquirerImport = require("inquirer");
|
|
5
9
|
const inquirer = inquirerImport.default ?? inquirerImport;
|
|
6
10
|
const prompt =
|
|
@@ -25,6 +29,75 @@ const { getLanguages, getFrameworks, getGenerator } = require("./registry");
|
|
|
25
29
|
const { runSteps } = require("./run");
|
|
26
30
|
const { runAdd } = require("./add");
|
|
27
31
|
|
|
32
|
+
const RUST_KEYWORDS = new Set(
|
|
33
|
+
[
|
|
34
|
+
// Strict + reserved keywords (covers the common Cargo failure cases)
|
|
35
|
+
"as",
|
|
36
|
+
"break",
|
|
37
|
+
"const",
|
|
38
|
+
"continue",
|
|
39
|
+
"crate",
|
|
40
|
+
"else",
|
|
41
|
+
"enum",
|
|
42
|
+
"extern",
|
|
43
|
+
"false",
|
|
44
|
+
"fn",
|
|
45
|
+
"for",
|
|
46
|
+
"if",
|
|
47
|
+
"impl",
|
|
48
|
+
"in",
|
|
49
|
+
"let",
|
|
50
|
+
"loop",
|
|
51
|
+
"match",
|
|
52
|
+
"mod",
|
|
53
|
+
"move",
|
|
54
|
+
"mut",
|
|
55
|
+
"pub",
|
|
56
|
+
"ref",
|
|
57
|
+
"return",
|
|
58
|
+
"self",
|
|
59
|
+
"Self",
|
|
60
|
+
"static",
|
|
61
|
+
"struct",
|
|
62
|
+
"super",
|
|
63
|
+
"trait",
|
|
64
|
+
"true",
|
|
65
|
+
"type",
|
|
66
|
+
"unsafe",
|
|
67
|
+
"use",
|
|
68
|
+
"where",
|
|
69
|
+
"while",
|
|
70
|
+
"async",
|
|
71
|
+
"await",
|
|
72
|
+
"dyn",
|
|
73
|
+
"union",
|
|
74
|
+
// Reserved (historical / future)
|
|
75
|
+
"abstract",
|
|
76
|
+
"become",
|
|
77
|
+
"box",
|
|
78
|
+
"do",
|
|
79
|
+
"final",
|
|
80
|
+
"macro",
|
|
81
|
+
"override",
|
|
82
|
+
"priv",
|
|
83
|
+
"try",
|
|
84
|
+
"typeof",
|
|
85
|
+
"unsized",
|
|
86
|
+
"virtual",
|
|
87
|
+
"yield",
|
|
88
|
+
].map(String)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
function validateProjectNameForSelection({ language, framework }, name) {
|
|
92
|
+
if (language === "Rust" && typeof framework === "string") {
|
|
93
|
+
const usesCargo = framework.toLowerCase().includes("cargo");
|
|
94
|
+
if (usesCargo && RUST_KEYWORDS.has(name)) {
|
|
95
|
+
return `That name is a Rust keyword (${name}). Pick a different name.`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
28
101
|
function isSafeProjectName(name) {
|
|
29
102
|
// Avoid path traversal / empty names; keep permissive.
|
|
30
103
|
if (!name) return false;
|
|
@@ -142,18 +215,22 @@ function withBack(choices) {
|
|
|
142
215
|
}
|
|
143
216
|
|
|
144
217
|
function printStepsPreview(steps) {
|
|
145
|
-
console.log("\nPlanned actions:");
|
|
218
|
+
console.log(chalk.bold.cyan("\nPlanned actions:"));
|
|
146
219
|
for (const step of steps) {
|
|
147
220
|
const type = step.type || "command";
|
|
148
221
|
if (type === "command") {
|
|
149
222
|
const where = step.cwdFromProjectRoot ? "(in project)" : "(here)";
|
|
150
|
-
console.log(
|
|
223
|
+
console.log(
|
|
224
|
+
chalk.gray("- ") +
|
|
225
|
+
chalk.green(`${step.program} ${step.args.join(" ")}`) +
|
|
226
|
+
chalk.dim(` ${where}`)
|
|
227
|
+
);
|
|
151
228
|
} else if (type === "mkdir") {
|
|
152
|
-
console.log(
|
|
229
|
+
console.log(chalk.gray("- ") + chalk.yellow(`mkdir -p ${step.path}`));
|
|
153
230
|
} else if (type === "writeFile") {
|
|
154
|
-
console.log(
|
|
231
|
+
console.log(chalk.gray("- ") + chalk.yellow(`write ${step.path}`));
|
|
155
232
|
} else {
|
|
156
|
-
console.log(`- ${type}`);
|
|
233
|
+
console.log(chalk.gray(`- ${type}`));
|
|
157
234
|
}
|
|
158
235
|
}
|
|
159
236
|
console.log("");
|
|
@@ -198,11 +275,22 @@ async function main(options = {}) {
|
|
|
198
275
|
return;
|
|
199
276
|
}
|
|
200
277
|
|
|
201
|
-
console
|
|
202
|
-
console.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
console.log(
|
|
278
|
+
// Clear console for a fresh start
|
|
279
|
+
console.clear();
|
|
280
|
+
|
|
281
|
+
const title = figlet.textSync(" PROJECT CLI", { font: "Slant" });
|
|
282
|
+
console.log(gradient.pastel.multiline(title));
|
|
283
|
+
|
|
284
|
+
const subtitle = " The Ultimate Interactive Project Generator ";
|
|
285
|
+
console.log(gradient.vice(subtitle));
|
|
286
|
+
console.log(chalk.dim(" v" + readPackageVersion()));
|
|
287
|
+
console.log("\n");
|
|
288
|
+
|
|
289
|
+
console.log(
|
|
290
|
+
chalk.cyan.bold(" ? ") +
|
|
291
|
+
chalk.bold("Select a Language") +
|
|
292
|
+
chalk.dim(" (Type to search)")
|
|
293
|
+
);
|
|
206
294
|
|
|
207
295
|
const languages = getLanguages();
|
|
208
296
|
if (languages.length === 0) {
|
|
@@ -250,15 +338,32 @@ async function main(options = {}) {
|
|
|
250
338
|
return { name: `${lang} (${count})`, value: lang, short: lang };
|
|
251
339
|
});
|
|
252
340
|
|
|
253
|
-
const
|
|
254
|
-
{
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
341
|
+
const languageQuestion = hasAutocomplete
|
|
342
|
+
? {
|
|
343
|
+
type: "autocomplete",
|
|
344
|
+
name: "language",
|
|
345
|
+
message: "Language (type to search):",
|
|
346
|
+
pageSize: 12,
|
|
347
|
+
source: async (_answersSoFar, input) => {
|
|
348
|
+
const q = String(input || "")
|
|
349
|
+
.toLowerCase()
|
|
350
|
+
.trim();
|
|
351
|
+
if (!q) return languageChoices;
|
|
352
|
+
// Fuzzy/Simple filter
|
|
353
|
+
return languageChoices.filter((c) =>
|
|
354
|
+
String(c.name).toLowerCase().includes(q)
|
|
355
|
+
);
|
|
356
|
+
},
|
|
357
|
+
}
|
|
358
|
+
: {
|
|
359
|
+
type: "list",
|
|
360
|
+
name: "language",
|
|
361
|
+
message: "Language:",
|
|
362
|
+
choices: languageChoices,
|
|
363
|
+
pageSize: 12,
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const { language } = await prompt([languageQuestion]);
|
|
262
367
|
|
|
263
368
|
state.language = language;
|
|
264
369
|
state.framework = undefined;
|
|
@@ -279,32 +384,32 @@ async function main(options = {}) {
|
|
|
279
384
|
return { name: `${fw}${note}`, value: fw, short: fw };
|
|
280
385
|
});
|
|
281
386
|
|
|
282
|
-
const frameworkQuestion =
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
387
|
+
const frameworkQuestion = hasAutocomplete
|
|
388
|
+
? {
|
|
389
|
+
type: "autocomplete",
|
|
390
|
+
name: "framework",
|
|
391
|
+
message: "Framework (type to search):",
|
|
392
|
+
pageSize: 12,
|
|
393
|
+
source: async (_answersSoFar, input) => {
|
|
394
|
+
const q = String(input || "")
|
|
395
|
+
.toLowerCase()
|
|
396
|
+
.trim();
|
|
397
|
+
const backOption = { name: "← Back", value: BACK };
|
|
398
|
+
if (!q) return [backOption, ...frameworkChoices];
|
|
399
|
+
|
|
400
|
+
const filtered = frameworkChoices.filter((c) =>
|
|
401
|
+
String(c.name).toLowerCase().includes(q)
|
|
402
|
+
);
|
|
403
|
+
return [backOption, ...filtered];
|
|
404
|
+
},
|
|
405
|
+
}
|
|
406
|
+
: {
|
|
407
|
+
type: "list",
|
|
408
|
+
name: "framework",
|
|
409
|
+
message: "Framework:",
|
|
410
|
+
choices: withBack(frameworkChoices),
|
|
411
|
+
pageSize: 12,
|
|
412
|
+
};
|
|
308
413
|
|
|
309
414
|
const answer = await prompt([frameworkQuestion]);
|
|
310
415
|
if (answer.framework === BACK) {
|
|
@@ -378,7 +483,10 @@ async function main(options = {}) {
|
|
|
378
483
|
if (fs.existsSync(target)) {
|
|
379
484
|
return "That folder already exists. Pick a different name.";
|
|
380
485
|
}
|
|
381
|
-
return
|
|
486
|
+
return validateProjectNameForSelection(
|
|
487
|
+
{ language: state.language, framework: state.framework },
|
|
488
|
+
v
|
|
489
|
+
);
|
|
382
490
|
},
|
|
383
491
|
},
|
|
384
492
|
]);
|
|
@@ -452,6 +560,7 @@ async function main(options = {}) {
|
|
|
452
560
|
|
|
453
561
|
if (action === "cancel") return;
|
|
454
562
|
if (action === "back") {
|
|
563
|
+
state.name = undefined;
|
|
455
564
|
step = "name";
|
|
456
565
|
continue;
|
|
457
566
|
}
|
|
@@ -471,6 +580,11 @@ async function main(options = {}) {
|
|
|
471
580
|
const message = err && err.message ? err.message : String(err);
|
|
472
581
|
console.error(`\nError: ${message}`);
|
|
473
582
|
|
|
583
|
+
const looksLikeNameIssue =
|
|
584
|
+
/cannot be used as a package name|Rust keyword|keyword|Cargo\.toml/i.test(
|
|
585
|
+
message
|
|
586
|
+
);
|
|
587
|
+
|
|
474
588
|
if (args.yes) {
|
|
475
589
|
throw err;
|
|
476
590
|
}
|
|
@@ -481,22 +595,31 @@ async function main(options = {}) {
|
|
|
481
595
|
name: "next",
|
|
482
596
|
message: "What next?",
|
|
483
597
|
choices: [
|
|
484
|
-
{ name: "Try again", value: "retry" },
|
|
598
|
+
{ name: "Try again (same settings)", value: "retry" },
|
|
599
|
+
{ name: "Change project name", value: "name" },
|
|
485
600
|
{ name: "← Back", value: "back" },
|
|
486
601
|
{ name: "Cancel", value: "cancel" },
|
|
487
602
|
],
|
|
488
603
|
pageSize: 6,
|
|
604
|
+
default: looksLikeNameIssue ? "name" : "retry",
|
|
489
605
|
},
|
|
490
606
|
]);
|
|
491
607
|
|
|
492
608
|
if (next === "cancel") return;
|
|
609
|
+
if (next === "name") {
|
|
610
|
+
state.name = undefined;
|
|
611
|
+
step = "name";
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
493
614
|
if (next === "back") {
|
|
615
|
+
state.name = undefined;
|
|
494
616
|
step = "name";
|
|
495
617
|
continue;
|
|
496
618
|
}
|
|
497
619
|
|
|
498
620
|
// retry
|
|
499
|
-
step = "confirm";
|
|
621
|
+
step = looksLikeNameIssue ? "name" : "confirm";
|
|
622
|
+
if (step === "name") state.name = undefined;
|
|
500
623
|
continue;
|
|
501
624
|
}
|
|
502
625
|
|
package/src/libraries.js
CHANGED
|
@@ -27,16 +27,21 @@ const JS_TS = {
|
|
|
27
27
|
"UX / Animation": [
|
|
28
28
|
{ label: "Framer Motion", packages: ["framer-motion"] },
|
|
29
29
|
{ label: "Lottie", packages: ["lottie-react"] },
|
|
30
|
+
{ label: "GSAP", packages: ["gsap"] },
|
|
31
|
+
{ label: "Three.js", packages: ["three"] },
|
|
30
32
|
],
|
|
31
33
|
"Forms / Validation": [
|
|
32
34
|
{ label: "React Hook Form", packages: ["react-hook-form"] },
|
|
33
35
|
{ label: "Zod", packages: ["zod"] },
|
|
34
36
|
{ label: "Yup", packages: ["yup"] },
|
|
37
|
+
{ label: "Valibot", packages: ["valibot"] },
|
|
35
38
|
],
|
|
36
39
|
"Data / State": [
|
|
37
40
|
{ label: "TanStack Query", packages: ["@tanstack/react-query"] },
|
|
38
41
|
{ label: "Zustand", packages: ["zustand"] },
|
|
39
42
|
{ label: "Redux Toolkit", packages: ["@reduxjs/toolkit", "react-redux"] },
|
|
43
|
+
{ label: "Jotai", packages: ["jotai"] },
|
|
44
|
+
{ label: "Recoil", packages: ["recoil"] },
|
|
40
45
|
],
|
|
41
46
|
Testing: [
|
|
42
47
|
{ label: "Vitest", packagesDev: ["vitest"] },
|
|
@@ -45,6 +50,16 @@ const JS_TS = {
|
|
|
45
50
|
packagesDev: ["@playwright/test"],
|
|
46
51
|
post: "playwright-install",
|
|
47
52
|
},
|
|
53
|
+
{ label: "Jest", packagesDev: ["jest", "ts-jest", "@types/jest"] },
|
|
54
|
+
{ label: "Cypress", packagesDev: ["cypress"] },
|
|
55
|
+
],
|
|
56
|
+
"Backend / API": [
|
|
57
|
+
{ label: "Axios", packages: ["axios"] },
|
|
58
|
+
{
|
|
59
|
+
label: "TRPC",
|
|
60
|
+
packages: ["@trpc/client", "@trpc/server", "@trpc/react-query"],
|
|
61
|
+
},
|
|
62
|
+
{ label: "Socket.io Client", packages: ["socket.io-client"] },
|
|
48
63
|
],
|
|
49
64
|
};
|
|
50
65
|
|
|
@@ -53,17 +68,80 @@ const PY = {
|
|
|
53
68
|
{ label: "Rich", packages: ["rich"] },
|
|
54
69
|
{ label: "Typer (CLI)", packages: ["typer"] },
|
|
55
70
|
{ label: "Textual", packages: ["textual"] },
|
|
71
|
+
{ label: "Flet", packages: ["flet"] },
|
|
56
72
|
],
|
|
57
73
|
Web: [
|
|
58
|
-
{ label: "FastAPI", packages: ["fastapi", "uvicorn"] },
|
|
74
|
+
{ label: "FastAPI", packages: ["fastapi", "uvicorn[standard]"] },
|
|
59
75
|
{ label: "Flask", packages: ["flask"] },
|
|
60
76
|
{ label: "Django", packages: ["django"] },
|
|
77
|
+
{ label: "Starlette", packages: ["starlette"] },
|
|
78
|
+
{ label: "Litestar", packages: ["litestar"] },
|
|
79
|
+
],
|
|
80
|
+
"Data Science": [
|
|
81
|
+
{ label: "Pandas", packages: ["pandas"] },
|
|
82
|
+
{ label: "NumPy", packages: ["numpy"] },
|
|
83
|
+
{ label: "Matplotlib", packages: ["matplotlib"] },
|
|
84
|
+
{ label: "Scikit-learn", packages: ["scikit-learn"] },
|
|
85
|
+
],
|
|
86
|
+
Testing: [
|
|
87
|
+
{ label: "Pytest", packages: ["pytest"] },
|
|
88
|
+
{ label: "Unittest", packages: [] }, // builtin
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const RUST = {
|
|
93
|
+
Web: [
|
|
94
|
+
{ label: "Axum", packages: ["axum", "tokio"] },
|
|
95
|
+
{ label: "Actix-web", packages: ["actix-web"] },
|
|
96
|
+
{ label: "Rocket", packages: ["rocket"] },
|
|
97
|
+
{ label: "Yew", packages: ["yew"] },
|
|
98
|
+
{ label: "Leptos", packages: ["leptos"] },
|
|
99
|
+
],
|
|
100
|
+
"CLI / TUI": [
|
|
101
|
+
{ label: "Clap", packages: ["clap"] },
|
|
102
|
+
{ label: "Ratatui", packages: ["ratatui"] },
|
|
103
|
+
{ label: "Dialoguer", packages: ["dialoguer"] },
|
|
104
|
+
{ label: "Inquire", packages: ["inquire"] },
|
|
105
|
+
],
|
|
106
|
+
"Async / Runtime": [{ label: "Tokio", packages: ["tokio"] }],
|
|
107
|
+
Serialization: [{ label: "Serde", packages: ["serde", "serde_json"] }],
|
|
108
|
+
"ORM / DB": [
|
|
109
|
+
{ label: "Diesel", packages: ["diesel"] },
|
|
110
|
+
{ label: "SQLx", packages: ["sqlx"] },
|
|
111
|
+
{ label: "SeaORM", packages: ["sea-orm"] },
|
|
112
|
+
],
|
|
113
|
+
Utilities: [
|
|
114
|
+
{ label: "Anyhow", packages: ["anyhow"] },
|
|
115
|
+
{ label: "Thiserror", packages: ["thiserror"] },
|
|
116
|
+
{ label: "Log", packages: ["log"] },
|
|
117
|
+
{ label: "Env_logger", packages: ["env_logger"] },
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const GO = {
|
|
122
|
+
Web: [
|
|
123
|
+
{ label: "Gin", packages: ["github.com/gin-gonic/gin"] },
|
|
124
|
+
{ label: "Echo", packages: ["github.com/labstack/echo/v4"] },
|
|
125
|
+
{ label: "Fiber", packages: ["github.com/gofiber/fiber/v2"] },
|
|
126
|
+
{ label: "Chi", packages: ["github.com/go-chi/chi/v5"] },
|
|
127
|
+
],
|
|
128
|
+
CLI: [
|
|
129
|
+
{ label: "Cobra", packages: ["github.com/spf13/cobra"] },
|
|
130
|
+
{ label: "Viper", packages: ["github.com/spf13/viper"] },
|
|
131
|
+
{ label: "Bubbletea", packages: ["github.com/charmbracelet/bubbletea"] },
|
|
132
|
+
],
|
|
133
|
+
"ORM / DB": [
|
|
134
|
+
{ label: "GORM", packages: ["gorm.io/gorm"] },
|
|
135
|
+
{ label: "Ent", packages: ["entgo.io/ent"] },
|
|
136
|
+
{ label: "Sqlc", packages: ["github.com/kyleconroy/sqlc/cmd/sqlc"] },
|
|
61
137
|
],
|
|
62
138
|
};
|
|
63
139
|
|
|
64
140
|
function getCatalog(language) {
|
|
65
141
|
if (language === "JavaScript/TypeScript") return JS_TS;
|
|
66
142
|
if (language === "Python") return PY;
|
|
143
|
+
if (language === "Rust") return RUST;
|
|
144
|
+
if (language === "Go") return GO;
|
|
67
145
|
return {};
|
|
68
146
|
}
|
|
69
147
|
|
package/src/pm.js
CHANGED
|
@@ -2,6 +2,11 @@ function pmInstallCommand(pm) {
|
|
|
2
2
|
if (pm === "pnpm") return { program: "pnpm", args: ["install"] };
|
|
3
3
|
if (pm === "yarn") return { program: "yarn", args: ["install"] };
|
|
4
4
|
if (pm === "bun") return { program: "bun", args: ["install"] };
|
|
5
|
+
if (pm === "poetry") return { program: "poetry", args: ["install"] };
|
|
6
|
+
if (pm === "pip")
|
|
7
|
+
return { program: "pip", args: ["install", "-r", "requirements.txt"] };
|
|
8
|
+
if (pm === "cargo") return { program: "cargo", args: ["build"] };
|
|
9
|
+
if (pm === "go") return { program: "go", args: ["mod", "tidy"] };
|
|
5
10
|
return { program: "npm", args: ["install"] };
|
|
6
11
|
}
|
|
7
12
|
|
|
@@ -23,6 +28,28 @@ function pmAddCommand(pm, packages, { dev = false } = {}) {
|
|
|
23
28
|
return { program: "bun", args: ["add", ...(dev ? ["-d"] : []), ...pkgs] };
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
// Python
|
|
32
|
+
if (pm === "pip") {
|
|
33
|
+
return { program: "pip", args: ["install", ...pkgs] };
|
|
34
|
+
}
|
|
35
|
+
if (pm === "poetry") {
|
|
36
|
+
return {
|
|
37
|
+
program: "poetry",
|
|
38
|
+
args: ["add", ...(dev ? ["--group", "dev"] : []), ...pkgs],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Rust
|
|
43
|
+
if (pm === "cargo") {
|
|
44
|
+
return { program: "cargo", args: ["add", ...pkgs] };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Go
|
|
48
|
+
if (pm === "go") {
|
|
49
|
+
// go get matches 'add' somewhat, but typically go get <pkg>
|
|
50
|
+
return { program: "go", args: ["get", ...pkgs] };
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
return { program: "npm", args: ["install", ...(dev ? ["-D"] : []), ...pkgs] };
|
|
27
54
|
}
|
|
28
55
|
|
|
@@ -32,6 +59,11 @@ function pmExecCommand(pm, pkg, pkgArgs) {
|
|
|
32
59
|
if (pm === "pnpm") return { program: "pnpm", args: ["dlx", pkg, ...args] };
|
|
33
60
|
if (pm === "yarn") return { program: "yarn", args: ["dlx", pkg, ...args] };
|
|
34
61
|
if (pm === "bun") return { program: "bunx", args: [pkg, ...args] };
|
|
62
|
+
if (pm === "mvn")
|
|
63
|
+
return { program: "mvn", args: ["archetype:generate", ...args] };
|
|
64
|
+
if (pm === "gradle") return { program: "gradle", args: ["init", ...args] };
|
|
65
|
+
if (pm === "composer")
|
|
66
|
+
return { program: "composer", args: ["create-project", pkg, ...args] };
|
|
35
67
|
|
|
36
68
|
// npm default
|
|
37
69
|
return { program: "npx", args: ["--yes", pkg, ...args] };
|