@dawitworku/projectcli 0.1.1 → 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/package.json +1 -1
- package/src/add.js +156 -171
- package/src/detect.js +19 -1
- package/src/index.js +81 -45
- package/src/libraries.js +79 -1
- package/src/pm.js +32 -0
- package/src/registry.js +399 -1
package/package.json
CHANGED
package/src/add.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const fs = require("node:fs");
|
|
2
2
|
const path = require("node:path");
|
|
3
|
+
const chalk = require("chalk");
|
|
4
|
+
const Fuse = require("fuse.js");
|
|
5
|
+
const inquirerImport = require("inquirer");
|
|
6
|
+
const inquirer = inquirerImport.default ?? inquirerImport;
|
|
3
7
|
|
|
4
8
|
const { detectLanguage, detectPackageManager } = require("./detect");
|
|
5
9
|
const { getCatalog } = require("./libraries");
|
|
@@ -21,18 +25,22 @@ function parseAddArgs(argv) {
|
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
function printStepsPreview(steps) {
|
|
24
|
-
console.log("\nPlanned actions:");
|
|
28
|
+
console.log(chalk.bold.cyan("\nPlanned actions:"));
|
|
25
29
|
for (const step of steps) {
|
|
26
30
|
const type = step.type || "command";
|
|
27
31
|
if (type === "command") {
|
|
28
32
|
const where = step.cwdFromProjectRoot ? "(in project)" : "(here)";
|
|
29
|
-
console.log(
|
|
33
|
+
console.log(
|
|
34
|
+
chalk.gray("- ") +
|
|
35
|
+
chalk.green(`${step.program} ${step.args.join(" ")}`) +
|
|
36
|
+
chalk.dim(` ${where}`)
|
|
37
|
+
);
|
|
30
38
|
} else if (type === "mkdir") {
|
|
31
|
-
console.log(
|
|
39
|
+
console.log(chalk.gray("- ") + chalk.yellow(`mkdir -p ${step.path}`));
|
|
32
40
|
} else if (type === "writeFile") {
|
|
33
|
-
console.log(
|
|
41
|
+
console.log(chalk.gray("- ") + chalk.yellow(`write ${step.path}`));
|
|
34
42
|
} else {
|
|
35
|
-
console.log(`- ${type}`);
|
|
43
|
+
console.log(chalk.gray(`- ${type}`));
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
console.log("");
|
|
@@ -42,7 +50,7 @@ async function runAdd({ prompt, argv }) {
|
|
|
42
50
|
const flags = parseAddArgs(argv);
|
|
43
51
|
const cwd = process.cwd();
|
|
44
52
|
const language = detectLanguage(cwd);
|
|
45
|
-
const pm = detectPackageManager(cwd);
|
|
53
|
+
const pm = detectPackageManager(cwd); // Now returns pip, poetry, cargo, go, etc.
|
|
46
54
|
|
|
47
55
|
if (language === "Unknown") {
|
|
48
56
|
throw new Error(
|
|
@@ -50,10 +58,8 @@ async function runAdd({ prompt, argv }) {
|
|
|
50
58
|
);
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
console.log(`\nDetected project: ${language}`);
|
|
54
|
-
|
|
55
|
-
console.log(`Detected package manager: ${pm}`);
|
|
56
|
-
}
|
|
61
|
+
console.log(chalk.bold(`\nDetected project: ${chalk.blue(language)}`));
|
|
62
|
+
console.log(chalk.dim(`Detected package manager: ${pm}`));
|
|
57
63
|
|
|
58
64
|
const catalog = getCatalog(language);
|
|
59
65
|
const categories = Object.keys(catalog);
|
|
@@ -61,201 +67,180 @@ async function runAdd({ prompt, argv }) {
|
|
|
61
67
|
throw new Error(`No library catalog configured for: ${language}`);
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
// Flatten catalog for fuzzy search
|
|
71
|
+
const allItems = [];
|
|
72
|
+
for (const cat of categories) {
|
|
73
|
+
for (const item of catalog[cat]) {
|
|
74
|
+
allItems.push({
|
|
75
|
+
...item,
|
|
76
|
+
category: cat,
|
|
77
|
+
displayName: `${cat}: ${item.label}`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const fuse = new Fuse(allItems, {
|
|
83
|
+
keys: ["label", "category"],
|
|
84
|
+
threshold: 0.4,
|
|
85
|
+
});
|
|
86
|
+
|
|
64
87
|
const BACK = "__back__";
|
|
65
88
|
const EXIT = "__exit__";
|
|
66
89
|
|
|
67
|
-
let
|
|
68
|
-
let selectedLabels = null;
|
|
90
|
+
let selectedItems = [];
|
|
69
91
|
|
|
70
92
|
while (true) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
})),
|
|
77
|
-
new (require("inquirer").Separator)(),
|
|
78
|
-
{ name: "Exit", value: EXIT },
|
|
79
|
-
];
|
|
93
|
+
// If we have selected items, show them
|
|
94
|
+
if (selectedItems.length > 0) {
|
|
95
|
+
console.log(chalk.green(`\nSelected:`));
|
|
96
|
+
selectedItems.forEach((i) => console.log(` - ${i.displayName}`));
|
|
97
|
+
}
|
|
80
98
|
|
|
81
|
-
|
|
99
|
+
const choices = [
|
|
100
|
+
{ name: "Done (Install selected)", value: "DONE" },
|
|
101
|
+
{ name: "Search libraries (Fuzzy)", value: "SEARCH" },
|
|
102
|
+
{ name: "Browse by Category", value: "BROWSE" },
|
|
103
|
+
new inquirer.Separator(),
|
|
104
|
+
{ name: "Exit", value: EXIT },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const { action } = await prompt([
|
|
108
|
+
{
|
|
109
|
+
type: "list",
|
|
110
|
+
name: "action",
|
|
111
|
+
message: "What do you want to do?",
|
|
112
|
+
choices,
|
|
113
|
+
pageSize: 10,
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
if (action === EXIT) return;
|
|
118
|
+
|
|
119
|
+
if (action === "SEARCH") {
|
|
120
|
+
// Fuzzy search
|
|
121
|
+
const { item } = await prompt([
|
|
82
122
|
{
|
|
83
|
-
type: "
|
|
84
|
-
name: "
|
|
85
|
-
message: "
|
|
86
|
-
|
|
87
|
-
|
|
123
|
+
type: "autocomplete",
|
|
124
|
+
name: "item",
|
|
125
|
+
message: "Search library:",
|
|
126
|
+
source: (answersSoFar, input) => {
|
|
127
|
+
if (!input)
|
|
128
|
+
return Promise.resolve(
|
|
129
|
+
allItems.map((i) => ({ name: i.displayName, value: i }))
|
|
130
|
+
);
|
|
131
|
+
return Promise.resolve(
|
|
132
|
+
fuse
|
|
133
|
+
.search(input)
|
|
134
|
+
.map((r) => ({ name: r.item.displayName, value: r.item }))
|
|
135
|
+
);
|
|
136
|
+
},
|
|
88
137
|
},
|
|
89
138
|
]);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
139
|
+
if (item) {
|
|
140
|
+
if (!selectedItems.find((i) => i.label === item.label)) {
|
|
141
|
+
selectedItems.push(item);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
93
144
|
continue;
|
|
94
145
|
}
|
|
95
146
|
|
|
96
|
-
if (
|
|
97
|
-
const
|
|
98
|
-
const { picks } = await prompt([
|
|
147
|
+
if (action === "BROWSE") {
|
|
148
|
+
const { category } = await prompt([
|
|
99
149
|
{
|
|
100
|
-
type: "
|
|
101
|
-
name: "
|
|
102
|
-
message: "
|
|
103
|
-
choices:
|
|
104
|
-
validate: (v) =>
|
|
105
|
-
v && v.length ? true : "Pick at least one library.",
|
|
106
|
-
pageSize: 14,
|
|
150
|
+
type: "list",
|
|
151
|
+
name: "category",
|
|
152
|
+
message: "Category:",
|
|
153
|
+
choices: [...categories, { name: "Back", value: BACK }],
|
|
107
154
|
},
|
|
108
155
|
]);
|
|
156
|
+
if (category === BACK) continue;
|
|
109
157
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const { next } = await prompt([
|
|
158
|
+
const items = catalog[category];
|
|
159
|
+
const { picked } = await prompt([
|
|
113
160
|
{
|
|
114
|
-
type: "
|
|
115
|
-
name: "
|
|
116
|
-
message: "
|
|
117
|
-
choices:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
pageSize: 8,
|
|
161
|
+
type: "checkbox",
|
|
162
|
+
name: "picked",
|
|
163
|
+
message: "Select libraries:",
|
|
164
|
+
choices: items.map((i) => ({
|
|
165
|
+
name: i.label,
|
|
166
|
+
value: i,
|
|
167
|
+
checked: !!selectedItems.find((s) => s.label === i.label),
|
|
168
|
+
})),
|
|
123
169
|
},
|
|
124
170
|
]);
|
|
125
171
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
selectedCategory = null;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// continue
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const items = catalog[selectedCategory] || [];
|
|
137
|
-
const chosen = items.filter((i) => selectedLabels.includes(i.label));
|
|
138
|
-
|
|
139
|
-
const packages = uniq(chosen.flatMap((i) => i.packages || []));
|
|
140
|
-
const packagesDev = uniq(chosen.flatMap((i) => i.packagesDev || []));
|
|
141
|
-
const posts = uniq(chosen.map((i) => i.post).filter(Boolean));
|
|
142
|
-
|
|
143
|
-
const steps = [];
|
|
144
|
-
|
|
145
|
-
if (language === "JavaScript/TypeScript") {
|
|
146
|
-
if (!fs.existsSync(path.join(cwd, "package.json"))) {
|
|
147
|
-
throw new Error(
|
|
148
|
-
"No package.json found; run this inside a JS/TS project."
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (packages.length) {
|
|
153
|
-
const cmd = pmAddCommand(pm, packages, { dev: false });
|
|
154
|
-
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
155
|
-
}
|
|
156
|
-
if (packagesDev.length) {
|
|
157
|
-
const cmd = pmAddCommand(pm, packagesDev, { dev: true });
|
|
158
|
-
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Optional post-steps
|
|
162
|
-
for (const post of posts) {
|
|
163
|
-
if (post === "tailwind-init") {
|
|
164
|
-
const cmd = pmExecCommand(pm, "tailwindcss", ["init", "-p"]);
|
|
165
|
-
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
166
|
-
}
|
|
167
|
-
if (post === "playwright-install") {
|
|
168
|
-
const cmd = pmExecCommand(pm, "playwright", ["install"]);
|
|
169
|
-
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
170
|
-
}
|
|
171
|
-
if (post === "shadcn-init") {
|
|
172
|
-
// shadcn/ui uses a CLI; keep it interactive.
|
|
173
|
-
const cmd = pmExecCommand(pm, "shadcn@latest", ["init"]);
|
|
174
|
-
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
172
|
+
for (const p of picked) {
|
|
173
|
+
if (!selectedItems.find((i) => i.label === p.label)) {
|
|
174
|
+
selectedItems.push(p);
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
console.log("
|
|
180
|
+
if (action === "DONE") {
|
|
181
|
+
if (selectedItems.length === 0) {
|
|
182
|
+
console.log(chalk.yellow("No libraries selected. Exiting."));
|
|
181
183
|
return;
|
|
182
184
|
}
|
|
183
|
-
|
|
184
|
-
if (!flags.yes) {
|
|
185
|
-
const { ok } = await prompt([
|
|
186
|
-
{
|
|
187
|
-
type: "confirm",
|
|
188
|
-
name: "ok",
|
|
189
|
-
message: "Install selected libraries now?",
|
|
190
|
-
default: true,
|
|
191
|
-
},
|
|
192
|
-
]);
|
|
193
|
-
if (!ok) {
|
|
194
|
-
selectedLabels = null;
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
await runSteps(steps, { projectRoot: cwd });
|
|
200
|
-
console.log("\nDone. Libraries added.");
|
|
201
|
-
return;
|
|
185
|
+
break;
|
|
202
186
|
}
|
|
187
|
+
}
|
|
203
188
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type: "list",
|
|
209
|
-
name: "mode",
|
|
210
|
-
message: "How to add Python packages?",
|
|
211
|
-
choices: [
|
|
212
|
-
{
|
|
213
|
-
name: "Append to requirements.txt (no install)",
|
|
214
|
-
value: "requirements",
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: "pip install now (and append requirements.txt)",
|
|
218
|
-
value: "pip",
|
|
219
|
-
},
|
|
220
|
-
],
|
|
221
|
-
default: "requirements",
|
|
222
|
-
},
|
|
223
|
-
]);
|
|
189
|
+
// Install phase
|
|
190
|
+
const packages = uniq(selectedItems.flatMap((i) => i.packages || []));
|
|
191
|
+
const packagesDev = uniq(selectedItems.flatMap((i) => i.packagesDev || []));
|
|
192
|
+
const posts = uniq(selectedItems.map((i) => i.post).filter(Boolean));
|
|
224
193
|
|
|
225
|
-
|
|
226
|
-
const toAppend = all.map((p) => `${p}\n`).join("");
|
|
194
|
+
const steps = [];
|
|
227
195
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
console.log(`- append requirements.txt: ${all.join(", ")}`);
|
|
234
|
-
console.log("\nDry run: nothing executed.");
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
196
|
+
// Generic handling based on 'pm'
|
|
197
|
+
if (packages.length) {
|
|
198
|
+
const cmd = pmAddCommand(pm, packages, { dev: false });
|
|
199
|
+
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
200
|
+
}
|
|
237
201
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
type: "command",
|
|
243
|
-
program: "python",
|
|
244
|
-
args: ["-m", "pip", "install", ...all],
|
|
245
|
-
cwdFromProjectRoot: true,
|
|
246
|
-
},
|
|
247
|
-
],
|
|
248
|
-
{ projectRoot: cwd }
|
|
249
|
-
);
|
|
250
|
-
}
|
|
202
|
+
if (packagesDev.length) {
|
|
203
|
+
const cmd = pmAddCommand(pm, packagesDev, { dev: true });
|
|
204
|
+
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
205
|
+
}
|
|
251
206
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
207
|
+
// Post install steps
|
|
208
|
+
for (const post of posts) {
|
|
209
|
+
if (post === "tailwind-init") {
|
|
210
|
+
const cmd = pmExecCommand(pm, "tailwindcss", ["init", "-p"]);
|
|
211
|
+
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
212
|
+
}
|
|
213
|
+
if (post === "playwright-install") {
|
|
214
|
+
const cmd = pmExecCommand(pm, "playwright", ["install"]);
|
|
215
|
+
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
255
216
|
}
|
|
217
|
+
if (post === "shadcn-init") {
|
|
218
|
+
const cmd = pmExecCommand(pm, "shadcn@latest", ["init"]);
|
|
219
|
+
steps.push({ type: "command", ...cmd, cwdFromProjectRoot: true });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
256
222
|
|
|
257
|
-
|
|
223
|
+
if (flags.dryRun) {
|
|
224
|
+
printStepsPreview(steps);
|
|
225
|
+
console.log("Dry run: nothing executed.");
|
|
226
|
+
return;
|
|
258
227
|
}
|
|
228
|
+
|
|
229
|
+
if (!flags.yes) {
|
|
230
|
+
printStepsPreview(steps);
|
|
231
|
+
const { ok } = await prompt([
|
|
232
|
+
{
|
|
233
|
+
type: "confirm",
|
|
234
|
+
name: "ok",
|
|
235
|
+
message: "Proceed with installation?",
|
|
236
|
+
default: true,
|
|
237
|
+
},
|
|
238
|
+
]);
|
|
239
|
+
if (!ok) return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await runSteps(steps, { projectRoot: cwd });
|
|
243
|
+
console.log(chalk.green("\nDone. Libraries added."));
|
|
259
244
|
}
|
|
260
245
|
|
|
261
246
|
module.exports = {
|
package/src/detect.js
CHANGED
|
@@ -6,11 +6,29 @@ function exists(cwd, rel) {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function detectPackageManager(cwd) {
|
|
9
|
+
// JS/TS
|
|
9
10
|
if (exists(cwd, "pnpm-lock.yaml")) return "pnpm";
|
|
10
11
|
if (exists(cwd, "yarn.lock")) return "yarn";
|
|
11
12
|
if (exists(cwd, "bun.lockb") || exists(cwd, "bun.lock")) return "bun";
|
|
12
13
|
if (exists(cwd, "package-lock.json")) return "npm";
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
// Python
|
|
16
|
+
if (exists(cwd, "poetry.lock")) return "poetry";
|
|
17
|
+
if (exists(cwd, "requirements.txt")) return "pip";
|
|
18
|
+
if (exists(cwd, "pyproject.toml")) return "pip"; // Default to pip/standard if no specific lock file
|
|
19
|
+
|
|
20
|
+
// Rust
|
|
21
|
+
if (exists(cwd, "Cargo.toml")) return "cargo";
|
|
22
|
+
|
|
23
|
+
// Go
|
|
24
|
+
if (exists(cwd, "go.mod")) return "go";
|
|
25
|
+
|
|
26
|
+
// PHP
|
|
27
|
+
if (exists(cwd, "composer.json")) return "composer";
|
|
28
|
+
|
|
29
|
+
// fallback for JS
|
|
30
|
+
if (exists(cwd, "package.json")) return "npm";
|
|
31
|
+
|
|
14
32
|
return "npm";
|
|
15
33
|
}
|
|
16
34
|
|
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 =
|
|
@@ -211,18 +215,22 @@ function withBack(choices) {
|
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
function printStepsPreview(steps) {
|
|
214
|
-
console.log("\nPlanned actions:");
|
|
218
|
+
console.log(chalk.bold.cyan("\nPlanned actions:"));
|
|
215
219
|
for (const step of steps) {
|
|
216
220
|
const type = step.type || "command";
|
|
217
221
|
if (type === "command") {
|
|
218
222
|
const where = step.cwdFromProjectRoot ? "(in project)" : "(here)";
|
|
219
|
-
console.log(
|
|
223
|
+
console.log(
|
|
224
|
+
chalk.gray("- ") +
|
|
225
|
+
chalk.green(`${step.program} ${step.args.join(" ")}`) +
|
|
226
|
+
chalk.dim(` ${where}`)
|
|
227
|
+
);
|
|
220
228
|
} else if (type === "mkdir") {
|
|
221
|
-
console.log(
|
|
229
|
+
console.log(chalk.gray("- ") + chalk.yellow(`mkdir -p ${step.path}`));
|
|
222
230
|
} else if (type === "writeFile") {
|
|
223
|
-
console.log(
|
|
231
|
+
console.log(chalk.gray("- ") + chalk.yellow(`write ${step.path}`));
|
|
224
232
|
} else {
|
|
225
|
-
console.log(`- ${type}`);
|
|
233
|
+
console.log(chalk.gray(`- ${type}`));
|
|
226
234
|
}
|
|
227
235
|
}
|
|
228
236
|
console.log("");
|
|
@@ -267,11 +275,22 @@ async function main(options = {}) {
|
|
|
267
275
|
return;
|
|
268
276
|
}
|
|
269
277
|
|
|
270
|
-
console
|
|
271
|
-
console.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
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
|
+
);
|
|
275
294
|
|
|
276
295
|
const languages = getLanguages();
|
|
277
296
|
if (languages.length === 0) {
|
|
@@ -319,15 +338,32 @@ async function main(options = {}) {
|
|
|
319
338
|
return { name: `${lang} (${count})`, value: lang, short: lang };
|
|
320
339
|
});
|
|
321
340
|
|
|
322
|
-
const
|
|
323
|
-
{
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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]);
|
|
331
367
|
|
|
332
368
|
state.language = language;
|
|
333
369
|
state.framework = undefined;
|
|
@@ -348,32 +384,32 @@ async function main(options = {}) {
|
|
|
348
384
|
return { name: `${fw}${note}`, value: fw, short: fw };
|
|
349
385
|
});
|
|
350
386
|
|
|
351
|
-
const frameworkQuestion =
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
+
};
|
|
377
413
|
|
|
378
414
|
const answer = await prompt([frameworkQuestion]);
|
|
379
415
|
if (answer.framework === BACK) {
|
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] };
|
package/src/registry.js
CHANGED
|
@@ -94,6 +94,36 @@ const REGISTRY = {
|
|
|
94
94
|
},
|
|
95
95
|
notes: "Vite + Svelte (JS)",
|
|
96
96
|
},
|
|
97
|
+
"Vite (Preact)": {
|
|
98
|
+
id: "js.vite.preact",
|
|
99
|
+
label: "Vite (Preact)",
|
|
100
|
+
commands: (ctx) => {
|
|
101
|
+
const projectName = getProjectName(ctx);
|
|
102
|
+
const pm = getPackageManager(ctx);
|
|
103
|
+
return [
|
|
104
|
+
pmExec(pm, "create-vite@latest", [
|
|
105
|
+
projectName,
|
|
106
|
+
"--template",
|
|
107
|
+
"preact",
|
|
108
|
+
]),
|
|
109
|
+
pmInstall(pm, { cwdFromProjectRoot: true }),
|
|
110
|
+
];
|
|
111
|
+
},
|
|
112
|
+
notes: "Vite + Preact (JS)",
|
|
113
|
+
},
|
|
114
|
+
"Vite (Lit)": {
|
|
115
|
+
id: "js.vite.lit",
|
|
116
|
+
label: "Vite (Lit)",
|
|
117
|
+
commands: (ctx) => {
|
|
118
|
+
const projectName = getProjectName(ctx);
|
|
119
|
+
const pm = getPackageManager(ctx);
|
|
120
|
+
return [
|
|
121
|
+
pmExec(pm, "create-vite@latest", [projectName, "--template", "lit"]),
|
|
122
|
+
pmInstall(pm, { cwdFromProjectRoot: true }),
|
|
123
|
+
];
|
|
124
|
+
},
|
|
125
|
+
notes: "Vite + Lit (JS)",
|
|
126
|
+
},
|
|
97
127
|
"Next.js": {
|
|
98
128
|
id: "js.nextjs",
|
|
99
129
|
label: "Next.js",
|
|
@@ -110,7 +140,6 @@ const REGISTRY = {
|
|
|
110
140
|
commands: (ctx) => {
|
|
111
141
|
const projectName = getProjectName(ctx);
|
|
112
142
|
const pm = getPackageManager(ctx);
|
|
113
|
-
// Astro docs recommend npm/pnpm/yarn create astro@latest; using the underlying package works too.
|
|
114
143
|
return [pmExec(pm, "create-astro@latest", [projectName])];
|
|
115
144
|
},
|
|
116
145
|
notes: "Astro site (wizard)",
|
|
@@ -148,6 +177,63 @@ const REGISTRY = {
|
|
|
148
177
|
},
|
|
149
178
|
notes: "Nuxt via nuxi init",
|
|
150
179
|
},
|
|
180
|
+
"Express (Generator)": {
|
|
181
|
+
id: "js.express",
|
|
182
|
+
label: "Express",
|
|
183
|
+
commands: (ctx) => {
|
|
184
|
+
const projectName = getProjectName(ctx);
|
|
185
|
+
const pm = getPackageManager(ctx);
|
|
186
|
+
return [
|
|
187
|
+
pmExec(pm, "express-generator", [projectName]),
|
|
188
|
+
pmInstall(pm, { cwdFromProjectRoot: true }),
|
|
189
|
+
];
|
|
190
|
+
},
|
|
191
|
+
notes: "Scaffolds basic Express app",
|
|
192
|
+
},
|
|
193
|
+
"Fastify (CLI)": {
|
|
194
|
+
id: "js.fastify",
|
|
195
|
+
label: "Fastify",
|
|
196
|
+
commands: (ctx) => {
|
|
197
|
+
const projectName = getProjectName(ctx);
|
|
198
|
+
const pm = getPackageManager(ctx);
|
|
199
|
+
return [
|
|
200
|
+
pmExec(pm, "fastify-cli", ["generate", projectName]),
|
|
201
|
+
pmInstall(pm, { cwdFromProjectRoot: true }),
|
|
202
|
+
];
|
|
203
|
+
},
|
|
204
|
+
notes: "Scaffolds Fastify app",
|
|
205
|
+
},
|
|
206
|
+
"React Native (Expo)": {
|
|
207
|
+
id: "js.expo",
|
|
208
|
+
label: "React Native (Expo)",
|
|
209
|
+
commands: (ctx) => {
|
|
210
|
+
const projectName = getProjectName(ctx);
|
|
211
|
+
const pm = getPackageManager(ctx);
|
|
212
|
+
// create-expo-app
|
|
213
|
+
return [pmExec(pm, "create-expo-app@latest", [projectName])];
|
|
214
|
+
},
|
|
215
|
+
notes: "Expo for React Native",
|
|
216
|
+
},
|
|
217
|
+
"Electron (Forge)": {
|
|
218
|
+
id: "js.electron",
|
|
219
|
+
label: "Electron (Forge)",
|
|
220
|
+
commands: (ctx) => {
|
|
221
|
+
const projectName = getProjectName(ctx);
|
|
222
|
+
const pm = getPackageManager(ctx);
|
|
223
|
+
return [pmExec(pm, "create-electron-app@latest", [projectName])];
|
|
224
|
+
},
|
|
225
|
+
notes: "Electron Forge template",
|
|
226
|
+
},
|
|
227
|
+
"Qwik City": {
|
|
228
|
+
id: "js.qwik",
|
|
229
|
+
label: "Qwik City",
|
|
230
|
+
commands: (ctx) => {
|
|
231
|
+
const projectName = getProjectName(ctx);
|
|
232
|
+
const pm = getPackageManager(ctx);
|
|
233
|
+
return [pmExec(pm, "create-qwik@latest", ["basic", projectName])];
|
|
234
|
+
},
|
|
235
|
+
notes: "Qwik framework",
|
|
236
|
+
},
|
|
151
237
|
"Node.js (basic)": {
|
|
152
238
|
id: "js.node.basic",
|
|
153
239
|
label: "Node.js (basic)",
|
|
@@ -388,6 +474,29 @@ const REGISTRY = {
|
|
|
388
474
|
],
|
|
389
475
|
notes: "Writes files only (no pip install).",
|
|
390
476
|
},
|
|
477
|
+
Pyramid: {
|
|
478
|
+
id: "py.pyramid",
|
|
479
|
+
label: "Pyramid",
|
|
480
|
+
commands: (ctx) => {
|
|
481
|
+
const projectName = getProjectName(ctx);
|
|
482
|
+
return [
|
|
483
|
+
// Pyramid cookiecutter is common, but basic scaffold:
|
|
484
|
+
{ type: "mkdir", path: "." },
|
|
485
|
+
{
|
|
486
|
+
type: "writeFile",
|
|
487
|
+
path: "requirements.txt",
|
|
488
|
+
content: "pyramid\nwaitress\n",
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
type: "writeFile",
|
|
492
|
+
path: "app.py",
|
|
493
|
+
content:
|
|
494
|
+
"from wsgiref.simple_server import make_server\nfrom pyramid.config import Configurator\nfrom pyramid.response import Response\n\ndef hello_world(request):\n return Response('Hello World!')\n\nif __name__ == '__main__':\n with Configurator() as config:\n config.add_route('hello', '/')\n config.add_view(hello_world, route_name='hello')\n app = config.make_wsgi_app()\n server = make_server('0.0.0.0', 6543, app)\n server.serve_forever()\n",
|
|
495
|
+
},
|
|
496
|
+
];
|
|
497
|
+
},
|
|
498
|
+
notes: "Pyramid basic app",
|
|
499
|
+
},
|
|
391
500
|
},
|
|
392
501
|
|
|
393
502
|
Rust: {
|
|
@@ -419,6 +528,35 @@ const REGISTRY = {
|
|
|
419
528
|
},
|
|
420
529
|
notes: "Uses cargo new --lib",
|
|
421
530
|
},
|
|
531
|
+
"Tauri (App)": {
|
|
532
|
+
id: "rs.tauri",
|
|
533
|
+
label: "Tauri (App)",
|
|
534
|
+
commands: (ctx) => {
|
|
535
|
+
const projectName = getProjectName(ctx);
|
|
536
|
+
const pm = getPackageManager(ctx); // usually npm for tauri frontend
|
|
537
|
+
// Using create-tauri-app via pmExec
|
|
538
|
+
// cargo create-tauri-app is also an option but node way is common if JS frontend
|
|
539
|
+
return [pmExec(pm, "create-tauri-app@latest", [projectName])];
|
|
540
|
+
},
|
|
541
|
+
notes: "Cross-platform desktop app",
|
|
542
|
+
},
|
|
543
|
+
"Leptos (CSR)": {
|
|
544
|
+
id: "rs.leptos",
|
|
545
|
+
label: "Leptos (CSR)",
|
|
546
|
+
commands: (ctx) => {
|
|
547
|
+
const projectName = getProjectName(ctx);
|
|
548
|
+
return [
|
|
549
|
+
{
|
|
550
|
+
program: "cargo",
|
|
551
|
+
args: ["init", "--name", projectName],
|
|
552
|
+
cwdFromProjectRoot: true,
|
|
553
|
+
},
|
|
554
|
+
// In reality, one would use cargo-leptos or a template.
|
|
555
|
+
// Just a stub for now.
|
|
556
|
+
];
|
|
557
|
+
},
|
|
558
|
+
notes: "Init basic Rust project (Leptos needs template usually)",
|
|
559
|
+
},
|
|
422
560
|
},
|
|
423
561
|
|
|
424
562
|
Go: {
|
|
@@ -459,6 +597,92 @@ const REGISTRY = {
|
|
|
459
597
|
},
|
|
460
598
|
notes: "Writes main.go + runs go mod init",
|
|
461
599
|
},
|
|
600
|
+
"Buffalo (Web)": {
|
|
601
|
+
id: "go.buffalo",
|
|
602
|
+
label: "Buffalo (Web)",
|
|
603
|
+
commands: (ctx) => {
|
|
604
|
+
const projectName = getProjectName(ctx);
|
|
605
|
+
return [
|
|
606
|
+
{
|
|
607
|
+
program: "buffalo",
|
|
608
|
+
args: ["new", projectName],
|
|
609
|
+
},
|
|
610
|
+
];
|
|
611
|
+
},
|
|
612
|
+
notes: "Requires 'buffalo' CLI installed",
|
|
613
|
+
},
|
|
614
|
+
"Fiber (Skeleton)": {
|
|
615
|
+
id: "go.fiber",
|
|
616
|
+
label: "Fiber (Skeleton)",
|
|
617
|
+
commands: (_ctx) => [
|
|
618
|
+
{ type: "mkdir", path: "." },
|
|
619
|
+
{
|
|
620
|
+
type: "writeFile",
|
|
621
|
+
path: "main.go",
|
|
622
|
+
content: `package main
|
|
623
|
+
|
|
624
|
+
import "github.com/gofiber/fiber/v2"
|
|
625
|
+
|
|
626
|
+
func main() {
|
|
627
|
+
app := fiber.New()
|
|
628
|
+
|
|
629
|
+
app.Get("/", func(c *fiber.Ctx) error {
|
|
630
|
+
return c.SendString("Hello, World!")
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
app.Listen(":3000")
|
|
634
|
+
}
|
|
635
|
+
`,
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
program: "go",
|
|
639
|
+
args: ["mod", "init", "fiber-app"],
|
|
640
|
+
cwdFromProjectRoot: true,
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
program: "go",
|
|
644
|
+
args: ["get", "github.com/gofiber/fiber/v2"],
|
|
645
|
+
cwdFromProjectRoot: true,
|
|
646
|
+
},
|
|
647
|
+
],
|
|
648
|
+
notes: "Basic Fiber setup",
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
|
|
652
|
+
"C++": {
|
|
653
|
+
"CMake (Basic)": {
|
|
654
|
+
id: "cpp.cmake",
|
|
655
|
+
label: "CMake (Basic)",
|
|
656
|
+
commands: (ctx) => {
|
|
657
|
+
const projectName = getProjectName(ctx);
|
|
658
|
+
return [
|
|
659
|
+
{ type: "mkdir", path: "src" },
|
|
660
|
+
{
|
|
661
|
+
type: "writeFile",
|
|
662
|
+
path: "CMakeLists.txt",
|
|
663
|
+
content: `cmake_minimum_required(VERSION 3.10)
|
|
664
|
+
project(${projectName})
|
|
665
|
+
|
|
666
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
667
|
+
|
|
668
|
+
add_executable(${projectName} src/main.cpp)
|
|
669
|
+
`,
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
type: "writeFile",
|
|
673
|
+
path: "src/main.cpp",
|
|
674
|
+
content: `#include <iostream>
|
|
675
|
+
|
|
676
|
+
int main() {
|
|
677
|
+
std::cout << "Hello, World!" << std::endl;
|
|
678
|
+
return 0;
|
|
679
|
+
}
|
|
680
|
+
`,
|
|
681
|
+
},
|
|
682
|
+
];
|
|
683
|
+
},
|
|
684
|
+
notes: "Standard CMake structure",
|
|
685
|
+
},
|
|
462
686
|
},
|
|
463
687
|
|
|
464
688
|
"C#": {
|
|
@@ -491,6 +715,180 @@ const REGISTRY = {
|
|
|
491
715
|
notes: "Requires dotnet SDK",
|
|
492
716
|
},
|
|
493
717
|
},
|
|
718
|
+
|
|
719
|
+
Ruby: {
|
|
720
|
+
Rails: {
|
|
721
|
+
id: "rb.rails",
|
|
722
|
+
label: "Rails",
|
|
723
|
+
commands: (ctx) => {
|
|
724
|
+
const projectName = getProjectName(ctx);
|
|
725
|
+
return [
|
|
726
|
+
{
|
|
727
|
+
program: "rails",
|
|
728
|
+
args: ["new", projectName],
|
|
729
|
+
},
|
|
730
|
+
];
|
|
731
|
+
},
|
|
732
|
+
notes: "Requires rails gem installed",
|
|
733
|
+
},
|
|
734
|
+
"Bundler (gem)": {
|
|
735
|
+
id: "rb.bundler",
|
|
736
|
+
label: "Bundler (new gem)",
|
|
737
|
+
commands: (ctx) => {
|
|
738
|
+
const projectName = getProjectName(ctx);
|
|
739
|
+
return [
|
|
740
|
+
{
|
|
741
|
+
program: "bundle",
|
|
742
|
+
args: ["gem", projectName],
|
|
743
|
+
},
|
|
744
|
+
];
|
|
745
|
+
},
|
|
746
|
+
notes: "Creates a new gem with bundler",
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
|
|
750
|
+
PHP: {
|
|
751
|
+
Laravel: {
|
|
752
|
+
id: "php.laravel",
|
|
753
|
+
label: "Laravel",
|
|
754
|
+
commands: (ctx) => {
|
|
755
|
+
const projectName = getProjectName(ctx);
|
|
756
|
+
return [
|
|
757
|
+
{
|
|
758
|
+
program: "composer",
|
|
759
|
+
args: ["create-project", "laravel/laravel", projectName],
|
|
760
|
+
},
|
|
761
|
+
];
|
|
762
|
+
},
|
|
763
|
+
notes: "Uses composer create-project",
|
|
764
|
+
},
|
|
765
|
+
Symfony: {
|
|
766
|
+
id: "php.symfony",
|
|
767
|
+
label: "Symfony",
|
|
768
|
+
commands: (ctx) => {
|
|
769
|
+
const projectName = getProjectName(ctx);
|
|
770
|
+
return [
|
|
771
|
+
{
|
|
772
|
+
program: "composer",
|
|
773
|
+
args: ["create-project", "symfony/skeleton", projectName],
|
|
774
|
+
},
|
|
775
|
+
];
|
|
776
|
+
},
|
|
777
|
+
notes: "Uses composer create-project",
|
|
778
|
+
},
|
|
779
|
+
},
|
|
780
|
+
|
|
781
|
+
"Java/Kotlin": {
|
|
782
|
+
"Spring Boot (Maven)": {
|
|
783
|
+
id: "java.springboot.maven",
|
|
784
|
+
label: "Spring Boot (Maven)",
|
|
785
|
+
commands: (ctx) => {
|
|
786
|
+
// Simple archetype or just hints. Spring Init is complex.
|
|
787
|
+
// Let's use maven quickstart for now as basic.
|
|
788
|
+
const projectName = getProjectName(ctx);
|
|
789
|
+
return [
|
|
790
|
+
{
|
|
791
|
+
program: "mvn",
|
|
792
|
+
args: [
|
|
793
|
+
"archetype:generate",
|
|
794
|
+
"-DgroupId=com.example",
|
|
795
|
+
`-DartifactId=${projectName}`,
|
|
796
|
+
"-DarchetypeArtifactId=maven-archetype-quickstart",
|
|
797
|
+
"-DinteractiveMode=false",
|
|
798
|
+
],
|
|
799
|
+
},
|
|
800
|
+
];
|
|
801
|
+
},
|
|
802
|
+
notes: "Basic Maven Quickstart",
|
|
803
|
+
},
|
|
804
|
+
"Gradle (Basic)": {
|
|
805
|
+
id: "java.gradle.basic",
|
|
806
|
+
label: "Gradle (Basic)",
|
|
807
|
+
commands: (ctx) => {
|
|
808
|
+
const projectName = getProjectName(ctx);
|
|
809
|
+
return [
|
|
810
|
+
{ type: "mkdir", path: projectName },
|
|
811
|
+
{
|
|
812
|
+
program: "gradle",
|
|
813
|
+
args: [
|
|
814
|
+
"init",
|
|
815
|
+
"--type",
|
|
816
|
+
"basic",
|
|
817
|
+
"--dsl",
|
|
818
|
+
"kotlin",
|
|
819
|
+
"--project-name",
|
|
820
|
+
projectName,
|
|
821
|
+
],
|
|
822
|
+
cwd: projectName, // This property needs to be supported in runSteps, handled via chdir logic usually?
|
|
823
|
+
// current runSteps implementation supports cwdFromProjectRoot but not arbitrary cwd change easily maybe?
|
|
824
|
+
// runSteps implementation check:
|
|
825
|
+
// It uses `cwdFromProjectRoot` or defaults to current process.cwd().
|
|
826
|
+
// Better to just run gradle init inside the folder.
|
|
827
|
+
},
|
|
828
|
+
];
|
|
829
|
+
},
|
|
830
|
+
notes: "Gradle init",
|
|
831
|
+
},
|
|
832
|
+
Quarkus: {
|
|
833
|
+
id: "java.quarkus",
|
|
834
|
+
label: "Quarkus",
|
|
835
|
+
commands: (ctx) => {
|
|
836
|
+
const projectName = getProjectName(ctx);
|
|
837
|
+
return [
|
|
838
|
+
{
|
|
839
|
+
program: "mvn",
|
|
840
|
+
args: [
|
|
841
|
+
"io.quarkus.platform:quarkus-maven-plugin:create",
|
|
842
|
+
`-DprojectGroupId=org.acme`,
|
|
843
|
+
`-DprojectArtifactId=${projectName}`,
|
|
844
|
+
],
|
|
845
|
+
},
|
|
846
|
+
];
|
|
847
|
+
},
|
|
848
|
+
notes: "Quarkus app (Maven)",
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
|
|
852
|
+
Others: {
|
|
853
|
+
"HTML/CSS": {
|
|
854
|
+
id: "other.html",
|
|
855
|
+
label: "HTML/CSS (Vanilla)",
|
|
856
|
+
commands: (ctx) => {
|
|
857
|
+
return [
|
|
858
|
+
{ type: "mkdir", path: "." },
|
|
859
|
+
{
|
|
860
|
+
type: "writeFile",
|
|
861
|
+
path: "index.html",
|
|
862
|
+
content:
|
|
863
|
+
'<!DOCTYPE html>\\n<html lang="en">\\n<head>\\n <meta charset="UTF-8">\\n <title>My App</title>\\n</head>\\n<body>\\n <h1>Hello World</h1>\\n</body>\\n</html>',
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
type: "writeFile",
|
|
867
|
+
path: "style.css",
|
|
868
|
+
content: "body { font-family: sans-serif; }",
|
|
869
|
+
},
|
|
870
|
+
];
|
|
871
|
+
},
|
|
872
|
+
notes: "Just static files",
|
|
873
|
+
},
|
|
874
|
+
},
|
|
875
|
+
|
|
876
|
+
Dart: {
|
|
877
|
+
"Console Application": {
|
|
878
|
+
id: "dart.console",
|
|
879
|
+
label: "Console Application",
|
|
880
|
+
commands: (ctx) => {
|
|
881
|
+
const projectName = getProjectName(ctx);
|
|
882
|
+
return [
|
|
883
|
+
{
|
|
884
|
+
program: "dart",
|
|
885
|
+
args: ["create", "-t", "console", projectName],
|
|
886
|
+
},
|
|
887
|
+
];
|
|
888
|
+
},
|
|
889
|
+
notes: "Requires Dart SDK",
|
|
890
|
+
},
|
|
891
|
+
},
|
|
494
892
|
};
|
|
495
893
|
|
|
496
894
|
function getLanguages() {
|