create-morax 1.0.4 → 1.0.14
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/index.js +928 -761
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
4
|
+
import pc18 from "picocolors";
|
|
5
5
|
|
|
6
6
|
// src/core/runner.ts
|
|
7
|
-
import
|
|
8
|
-
import { spinner as
|
|
9
|
-
import
|
|
7
|
+
import path15 from "path";
|
|
8
|
+
import { spinner as spinner11, multiselect } from "@clack/prompts";
|
|
9
|
+
import pc17 from "picocolors";
|
|
10
10
|
|
|
11
11
|
// src/core/startcli.ts
|
|
12
12
|
import gradient from "gradient-string";
|
|
@@ -55,21 +55,21 @@ function handleCancel(input) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// src/core/workspaceName.ts
|
|
58
|
-
async function promptWorkspaceName() {
|
|
58
|
+
async function promptWorkspaceName(isMonorepo = true) {
|
|
59
59
|
const nameInput = await text({
|
|
60
|
-
message: "What is the name of your new monorepo workspace?",
|
|
61
|
-
placeholder: "morax-workspace",
|
|
60
|
+
message: isMonorepo ? "What is the name of your new monorepo workspace?" : "What is the name of your new project?",
|
|
61
|
+
placeholder: isMonorepo ? "morax-workspace" : "my-app",
|
|
62
62
|
validate(value) {
|
|
63
63
|
if (value && value.includes(" "))
|
|
64
|
-
return "Workspace name cannot contain spaces!";
|
|
64
|
+
return isMonorepo ? "Workspace name cannot contain spaces!" : "Project name cannot contain spaces!";
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
handleCancel(nameInput);
|
|
68
|
-
return String(nameInput).trim() || "morax-workspace";
|
|
68
|
+
return String(nameInput).trim() || (isMonorepo ? "morax-workspace" : "my-app");
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// src/tasks/directories.ts
|
|
72
|
-
import {
|
|
72
|
+
import { spinner } from "@clack/prompts";
|
|
73
73
|
import fsPromises2 from "fs/promises";
|
|
74
74
|
import path2 from "path";
|
|
75
75
|
import pc4 from "picocolors";
|
|
@@ -162,36 +162,13 @@ async function createDirectories(directories, projectPath) {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
async function makeDirectories(name, projectPath) {
|
|
165
|
-
const directories =
|
|
166
|
-
message: "Which directories do you want to include in your workspace?",
|
|
167
|
-
options: [
|
|
168
|
-
{
|
|
169
|
-
value: "apps",
|
|
170
|
-
label: "apps/*",
|
|
171
|
-
hint: "For frontend apps and backend services"
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
value: "packages",
|
|
175
|
-
label: "packages/*",
|
|
176
|
-
hint: "For shared components, configs, and utilities"
|
|
177
|
-
}
|
|
178
|
-
],
|
|
179
|
-
required: true
|
|
180
|
-
});
|
|
181
|
-
handleCancel(directories);
|
|
165
|
+
const directories = ["apps", "packages"];
|
|
182
166
|
const s = spinner();
|
|
183
167
|
console.log("\n");
|
|
184
168
|
s.start("Generating workspace configs...");
|
|
185
169
|
try {
|
|
186
|
-
await generateWorkspaceConfig(
|
|
187
|
-
|
|
188
|
-
directories,
|
|
189
|
-
projectPath
|
|
190
|
-
);
|
|
191
|
-
await createDirectories(
|
|
192
|
-
directories,
|
|
193
|
-
projectPath
|
|
194
|
-
);
|
|
170
|
+
await generateWorkspaceConfig(name, directories, projectPath);
|
|
171
|
+
await createDirectories(directories, projectPath);
|
|
195
172
|
s.stop(pc4.green("\u2714 Success: Generated Workspace Root & Folder Structures"));
|
|
196
173
|
} catch (error) {
|
|
197
174
|
s.stop(pc4.red("\u2716 Failed: Workspace generation failed"));
|
|
@@ -203,116 +180,704 @@ Error details: ${error.message || error}`));
|
|
|
203
180
|
return directories;
|
|
204
181
|
}
|
|
205
182
|
|
|
206
|
-
// src/
|
|
207
|
-
import
|
|
183
|
+
// src/core/react-runner.ts
|
|
184
|
+
import path6 from "path";
|
|
185
|
+
import { spinner as spinner4 } from "@clack/prompts";
|
|
186
|
+
import pc8 from "picocolors";
|
|
187
|
+
|
|
188
|
+
// src/tasks/react.ts
|
|
208
189
|
import pc5 from "picocolors";
|
|
209
190
|
import fsPromises3 from "fs/promises";
|
|
210
191
|
import path3 from "path";
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
192
|
+
import { spawn } from "child_process";
|
|
193
|
+
import { confirm, spinner as spinner2, text as text2 } from "@clack/prompts";
|
|
194
|
+
function runInteractiveCommand(command, args, cwd) {
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
const fullCommand = args.length > 0 ? `${command} ${args.join(" ")}` : command;
|
|
197
|
+
const child = spawn(fullCommand, [], {
|
|
198
|
+
cwd,
|
|
199
|
+
stdio: "inherit",
|
|
200
|
+
shell: true,
|
|
201
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
202
|
+
});
|
|
203
|
+
child.on("close", (code) => {
|
|
204
|
+
if (code === 0) {
|
|
205
|
+
resolve();
|
|
206
|
+
} else {
|
|
207
|
+
reject(
|
|
208
|
+
new Error(
|
|
209
|
+
`Command "${command} ${args.join(" ")}" exited with code ${code}`
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
child.on("error", (err) => {
|
|
215
|
+
reject(err);
|
|
216
|
+
});
|
|
215
217
|
});
|
|
216
218
|
}
|
|
217
|
-
async function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
printWidth: 100
|
|
225
|
-
};
|
|
226
|
-
await fsPromises3.writeFile(
|
|
227
|
-
path3.join(projectPath, ".prettierrc"),
|
|
228
|
-
JSON.stringify(prettierrc, null, 2),
|
|
229
|
-
"utf8"
|
|
219
|
+
async function setupReact(projectPath, isMonorepo = true) {
|
|
220
|
+
const appsDir = isMonorepo ? path3.join(projectPath, "apps") : path3.dirname(projectPath);
|
|
221
|
+
console.log(pc5.magentaBright("\n\u{1F537} IMPORTANT INSTRUCTIONS:"));
|
|
222
|
+
console.log(
|
|
223
|
+
pc5.magentaBright(
|
|
224
|
+
"During the Vite interactive configuration prompts, please select:"
|
|
225
|
+
)
|
|
230
226
|
);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"web_modules/",
|
|
236
|
-
"",
|
|
237
|
-
"# Build and outputs",
|
|
238
|
-
"dist/",
|
|
239
|
-
"build/",
|
|
240
|
-
".next/",
|
|
241
|
-
"out/",
|
|
242
|
-
".turbo/",
|
|
243
|
-
"",
|
|
244
|
-
"# Configuration locks",
|
|
245
|
-
"pnpm-lock.yaml",
|
|
246
|
-
"",
|
|
247
|
-
"# Environment files",
|
|
248
|
-
".env",
|
|
249
|
-
".env.*",
|
|
250
|
-
""
|
|
251
|
-
].join("\n");
|
|
252
|
-
await fsPromises3.writeFile(
|
|
253
|
-
path3.join(projectPath, ".prettierignore"),
|
|
254
|
-
prettierignore,
|
|
255
|
-
"utf8"
|
|
227
|
+
console.log(
|
|
228
|
+
pc5.magentaBright(
|
|
229
|
+
` 1. Install & Start: Choose ${pc5.bold("No")} when asked "Install with npm/pnpm and start now?"`
|
|
230
|
+
)
|
|
256
231
|
);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
...pkg.scripts || {},
|
|
262
|
-
format: "prettier --write ."
|
|
263
|
-
};
|
|
264
|
-
await fsPromises3.writeFile(
|
|
265
|
-
rootPackagePath,
|
|
266
|
-
JSON.stringify(pkg, null, 2),
|
|
267
|
-
"utf8"
|
|
232
|
+
console.log(
|
|
233
|
+
pc5.magentaBright(
|
|
234
|
+
"This allows Morax CLI to continue automatically configuring your workspace (ESLint, Prettier, Tailwind, packages, etc.)!"
|
|
235
|
+
)
|
|
268
236
|
);
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
237
|
+
console.log(
|
|
238
|
+
pc5.cyan("\nStarting interactive Vite React application setup...")
|
|
239
|
+
);
|
|
240
|
+
let beforeDirs = [];
|
|
241
|
+
try {
|
|
242
|
+
beforeDirs = await fsPromises3.readdir(appsDir);
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
await runInteractiveCommand("pnpm", ["create", "vite@latest"], appsDir);
|
|
247
|
+
let afterDirs = [];
|
|
278
248
|
try {
|
|
279
|
-
await
|
|
280
|
-
|
|
281
|
-
} catch (error) {
|
|
282
|
-
s.stop(pc5.red("\u2716 Failed: Prettier setup failed"));
|
|
283
|
-
console.error(pc5.red(`
|
|
284
|
-
Error details: ${error.message || error}`));
|
|
285
|
-
process.exit(1);
|
|
249
|
+
afterDirs = await fsPromises3.readdir(appsDir);
|
|
250
|
+
} catch {
|
|
286
251
|
}
|
|
287
|
-
|
|
252
|
+
const newDirs = afterDirs.filter((d) => !beforeDirs.includes(d));
|
|
253
|
+
let createdDirName = "web";
|
|
254
|
+
if (newDirs.length > 0) {
|
|
255
|
+
createdDirName = newDirs[0];
|
|
256
|
+
}
|
|
257
|
+
const webDir = path3.join(appsDir, createdDirName);
|
|
258
|
+
try {
|
|
259
|
+
await fsPromises3.access(webDir);
|
|
260
|
+
} catch {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Vite React application directory "apps/${createdDirName}" was not found. Please ensure you complete the Vite installation.`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
const aliasSpinner = spinner2();
|
|
266
|
+
aliasSpinner.start("Configuring import aliases and Node types...");
|
|
267
|
+
try {
|
|
268
|
+
const pkgPath = path3.join(webDir, "package.json");
|
|
269
|
+
try {
|
|
270
|
+
const pkgRaw = await fsPromises3.readFile(pkgPath, "utf8");
|
|
271
|
+
const pkg = JSON.parse(pkgRaw);
|
|
272
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
273
|
+
pkg.devDependencies["@types/node"] = "^20.11.0";
|
|
274
|
+
await fsPromises3.writeFile(
|
|
275
|
+
pkgPath,
|
|
276
|
+
JSON.stringify(pkg, null, 2),
|
|
277
|
+
"utf8"
|
|
278
|
+
);
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
const tsconfigPaths = [
|
|
282
|
+
path3.join(webDir, "tsconfig.json"),
|
|
283
|
+
path3.join(webDir, "tsconfig.app.json")
|
|
284
|
+
];
|
|
285
|
+
for (const tsPath of tsconfigPaths) {
|
|
286
|
+
try {
|
|
287
|
+
let content = await fsPromises3.readFile(tsPath, "utf8");
|
|
288
|
+
if (content.includes('"compilerOptions"')) {
|
|
289
|
+
if (!content.includes('"paths"')) {
|
|
290
|
+
content = content.replace(
|
|
291
|
+
/("compilerOptions"\s*:\s*\{)/,
|
|
292
|
+
`$1
|
|
293
|
+
"baseUrl": ".",
|
|
294
|
+
"paths": {
|
|
295
|
+
"@/*": ["./src/*"]
|
|
296
|
+
},`
|
|
297
|
+
);
|
|
298
|
+
await fsPromises3.writeFile(tsPath, content, "utf8");
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
content = content.replace(
|
|
302
|
+
/^(\s*\{)/,
|
|
303
|
+
`$1
|
|
304
|
+
"compilerOptions": {
|
|
305
|
+
"baseUrl": ".",
|
|
306
|
+
"paths": {
|
|
307
|
+
"@/*": ["./src/*"]
|
|
308
|
+
}
|
|
309
|
+
},`
|
|
310
|
+
);
|
|
311
|
+
await fsPromises3.writeFile(tsPath, content, "utf8");
|
|
312
|
+
}
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const viteConfigPath = path3.join(webDir, "vite.config.ts");
|
|
317
|
+
try {
|
|
318
|
+
let content = await fsPromises3.readFile(viteConfigPath, "utf8");
|
|
319
|
+
if (!content.includes("import path from 'path'")) {
|
|
320
|
+
content = "import path from 'path';\n" + content;
|
|
321
|
+
}
|
|
322
|
+
if (!content.includes("resolve:")) {
|
|
323
|
+
content = content.replace(
|
|
324
|
+
/(plugins:\s*\[[^\]]*\]),?/,
|
|
325
|
+
`$1,
|
|
326
|
+
resolve: {
|
|
327
|
+
alias: {
|
|
328
|
+
"@": path.resolve(__dirname, "./src"),
|
|
329
|
+
},
|
|
330
|
+
}`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
await fsPromises3.writeFile(viteConfigPath, content, "utf8");
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
aliasSpinner.stop(
|
|
337
|
+
pc5.green("\u2714 Success: Import aliases and Node types configured")
|
|
338
|
+
);
|
|
339
|
+
} catch (err) {
|
|
340
|
+
aliasSpinner.stop(pc5.red("\u2716 Failed: Import alias configuration failed"));
|
|
341
|
+
}
|
|
342
|
+
console.log(
|
|
343
|
+
pc5.green(
|
|
344
|
+
isMonorepo ? `
|
|
345
|
+
\u2714 Success: Vite React frontend configured in apps/${createdDirName}
|
|
346
|
+
` : `
|
|
347
|
+
\u2714 Success: Vite React frontend configured at root
|
|
348
|
+
`
|
|
349
|
+
)
|
|
350
|
+
);
|
|
351
|
+
const setupTailwindPrompt = await confirm({
|
|
352
|
+
message: "Do you want to install and configure Tailwind CSS v4?",
|
|
353
|
+
initialValue: true
|
|
354
|
+
});
|
|
355
|
+
handleCancel(setupTailwindPrompt);
|
|
356
|
+
const setupShadcnPrompt = await confirm({
|
|
357
|
+
message: `Do you want to setup shadcn UI in your Vite React website (apps/${createdDirName})?`,
|
|
358
|
+
initialValue: true
|
|
359
|
+
});
|
|
360
|
+
handleCancel(setupShadcnPrompt);
|
|
361
|
+
const needsTailwind = setupTailwindPrompt || setupShadcnPrompt;
|
|
362
|
+
if (needsTailwind) {
|
|
363
|
+
const s = spinner2();
|
|
364
|
+
s.start(
|
|
365
|
+
"Installing Tailwind CSS v4, Vite integration, and Node types..."
|
|
366
|
+
);
|
|
367
|
+
try {
|
|
368
|
+
const pkgPath = path3.join(webDir, "package.json");
|
|
369
|
+
try {
|
|
370
|
+
const pkgRaw = await fsPromises3.readFile(pkgPath, "utf8");
|
|
371
|
+
const pkg = JSON.parse(pkgRaw);
|
|
372
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
373
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
374
|
+
pkg.dependencies["tailwindcss"] = "^4.0.0";
|
|
375
|
+
pkg.devDependencies["@tailwindcss/vite"] = "^4.0.0";
|
|
376
|
+
pkg.devDependencies["@types/node"] = "^20.11.0";
|
|
377
|
+
await fsPromises3.writeFile(
|
|
378
|
+
pkgPath,
|
|
379
|
+
JSON.stringify(pkg, null, 2),
|
|
380
|
+
"utf8"
|
|
381
|
+
);
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
await runCommand("pnpm install", { cwd: webDir, silent: true });
|
|
385
|
+
s.message("Configuring Vite plugins and Tailwind CSS imports...");
|
|
386
|
+
const viteConfigPath = path3.join(webDir, "vite.config.ts");
|
|
387
|
+
try {
|
|
388
|
+
let content = await fsPromises3.readFile(viteConfigPath, "utf8");
|
|
389
|
+
if (!content.includes("@tailwindcss/vite")) {
|
|
390
|
+
content = "import tailwindcss from '@tailwindcss/vite';\n" + content;
|
|
391
|
+
if (content.includes("plugins: [react()]")) {
|
|
392
|
+
content = content.replace(
|
|
393
|
+
"plugins: [react()]",
|
|
394
|
+
"plugins: [react(), tailwindcss()]"
|
|
395
|
+
);
|
|
396
|
+
} else if (content.includes("plugins: [react(),]")) {
|
|
397
|
+
content = content.replace(
|
|
398
|
+
"plugins: [react(),]",
|
|
399
|
+
"plugins: [react(), tailwindcss()]"
|
|
400
|
+
);
|
|
401
|
+
} else {
|
|
402
|
+
content = content.replace(
|
|
403
|
+
/(plugins:\s*\[\s*react\(\),?\s*)(\])/g,
|
|
404
|
+
"$1\n tailwindcss(),\n $2"
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
await fsPromises3.writeFile(viteConfigPath, content, "utf8");
|
|
408
|
+
}
|
|
409
|
+
} catch {
|
|
410
|
+
}
|
|
411
|
+
const indexCssPath = path3.join(webDir, "src", "index.css");
|
|
412
|
+
try {
|
|
413
|
+
await fsPromises3.writeFile(
|
|
414
|
+
indexCssPath,
|
|
415
|
+
'@import "tailwindcss";\n',
|
|
416
|
+
"utf8"
|
|
417
|
+
);
|
|
418
|
+
} catch {
|
|
419
|
+
}
|
|
420
|
+
s.stop(
|
|
421
|
+
pc5.green("\u2714 Success: Tailwind CSS v4 configured successfully\n")
|
|
422
|
+
);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
s.stop(pc5.red("\u2716 Failed: Tailwind CSS v4 setup failed"));
|
|
425
|
+
throw err;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (setupShadcnPrompt) {
|
|
429
|
+
const hasPreset = await confirm({
|
|
430
|
+
message: "Do you have a custom shadcn UI preset code?",
|
|
431
|
+
initialValue: false
|
|
432
|
+
});
|
|
433
|
+
handleCancel(hasPreset);
|
|
434
|
+
let presetCode = "";
|
|
435
|
+
if (hasPreset) {
|
|
436
|
+
const enteredPreset = await text2({
|
|
437
|
+
message: "Enter your custom shadcn UI preset code:",
|
|
438
|
+
placeholder: "e.g. 123456",
|
|
439
|
+
validate: (val) => {
|
|
440
|
+
if (!val || !val.trim()) return "Preset code cannot be empty";
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
handleCancel(enteredPreset);
|
|
445
|
+
presetCode = enteredPreset.trim();
|
|
446
|
+
}
|
|
447
|
+
const args = ["dlx", "shadcn@latest", "init"];
|
|
448
|
+
if (presetCode) {
|
|
449
|
+
args.push("--preset", presetCode);
|
|
450
|
+
}
|
|
451
|
+
args.push("--template", "vite");
|
|
452
|
+
console.log(
|
|
453
|
+
pc5.cyan("\nStarting interactive shadcn UI initialization...")
|
|
454
|
+
);
|
|
455
|
+
await runInteractiveCommand("pnpm", args, webDir);
|
|
456
|
+
console.log(
|
|
457
|
+
pc5.green(
|
|
458
|
+
isMonorepo ? `
|
|
459
|
+
\u2714 Success: shadcn UI successfully initialized in apps/${createdDirName}
|
|
460
|
+
` : `
|
|
461
|
+
\u2714 Success: shadcn UI successfully initialized at root
|
|
462
|
+
`
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return webDir;
|
|
467
|
+
} catch (error) {
|
|
468
|
+
console.error(pc5.red(`
|
|
469
|
+
\u2716 Failed: Vite React frontend setup failed`));
|
|
470
|
+
console.error(pc5.red(`Error details: ${error.message || error}
|
|
471
|
+
`));
|
|
472
|
+
process.exit(1);
|
|
288
473
|
}
|
|
289
474
|
}
|
|
290
475
|
|
|
291
|
-
// src/tasks/
|
|
476
|
+
// src/tasks/git.ts
|
|
292
477
|
import { confirm as confirm2, spinner as spinner3 } from "@clack/prompts";
|
|
293
478
|
import pc6 from "picocolors";
|
|
294
479
|
import fsPromises4 from "fs/promises";
|
|
295
480
|
import path4 from "path";
|
|
296
|
-
async function
|
|
481
|
+
async function promptGit() {
|
|
297
482
|
return await confirm2({
|
|
298
|
-
message: "Do you want to
|
|
483
|
+
message: "Do you want to initialize a local Git repository?",
|
|
299
484
|
initialValue: true
|
|
300
485
|
});
|
|
301
486
|
}
|
|
302
|
-
async function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
"."
|
|
487
|
+
async function gitInit(projectPath) {
|
|
488
|
+
await runCommand("git init", { cwd: projectPath, silent: true });
|
|
489
|
+
const gitignore = [
|
|
490
|
+
"# Dependency directories",
|
|
491
|
+
"node_modules/",
|
|
492
|
+
"jspm_packages/",
|
|
493
|
+
"web_modules/",
|
|
494
|
+
"",
|
|
495
|
+
"# Build and output outputs",
|
|
496
|
+
"dist/",
|
|
497
|
+
"build/",
|
|
498
|
+
".next/",
|
|
499
|
+
"out/",
|
|
500
|
+
".turbo/",
|
|
501
|
+
"",
|
|
502
|
+
"# Environments",
|
|
503
|
+
".env",
|
|
504
|
+
".env.local",
|
|
505
|
+
".env.development.local",
|
|
506
|
+
".env.test.local",
|
|
507
|
+
".env.production.local",
|
|
508
|
+
"",
|
|
509
|
+
"# Logs",
|
|
510
|
+
"npm-debug.log*",
|
|
511
|
+
"yarn-debug.log*",
|
|
512
|
+
"yarn-error.log*",
|
|
513
|
+
"pnpm-debug.log*",
|
|
514
|
+
"*.log",
|
|
515
|
+
"",
|
|
516
|
+
"# OS Metadata",
|
|
517
|
+
".DS_Store",
|
|
518
|
+
"Thumbs.db",
|
|
519
|
+
""
|
|
520
|
+
].join("\n");
|
|
521
|
+
await fsPromises4.writeFile(
|
|
522
|
+
path4.join(projectPath, ".gitignore"),
|
|
523
|
+
gitignore,
|
|
524
|
+
"utf8"
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
async function runGitSetup(projectPath) {
|
|
528
|
+
const gitPrompt = await promptGit();
|
|
529
|
+
handleCancel(gitPrompt);
|
|
530
|
+
let gitInitialized = false;
|
|
531
|
+
if (gitPrompt) {
|
|
532
|
+
const s = spinner3();
|
|
533
|
+
console.log("\n");
|
|
534
|
+
s.start("Initializing Git repository...");
|
|
535
|
+
try {
|
|
536
|
+
await gitInit(projectPath);
|
|
537
|
+
s.stop(pc6.green("\u2714 Success: Git repository initialized"));
|
|
538
|
+
gitInitialized = true;
|
|
539
|
+
} catch (error) {
|
|
540
|
+
s.stop(pc6.red("\u2716 Failed: Git initialization failed"));
|
|
541
|
+
console.error(pc6.red(`
|
|
542
|
+
Error details: ${error.message || error}`));
|
|
543
|
+
process.exit(1);
|
|
544
|
+
}
|
|
545
|
+
console.log("\n");
|
|
546
|
+
}
|
|
547
|
+
return gitInitialized;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/tasks/addreadme.ts
|
|
551
|
+
import fsPromises5 from "fs/promises";
|
|
552
|
+
import path5 from "path";
|
|
553
|
+
async function setupReadme(projectPath, workspaceName) {
|
|
554
|
+
const readmePath = path5.join(projectPath, "README.md");
|
|
555
|
+
const readmeContent = [
|
|
556
|
+
`# Morax `,
|
|
557
|
+
"",
|
|
558
|
+
"Welcome to your next-generation, high-performance monorepo workspace generated by **Morax**.",
|
|
559
|
+
"",
|
|
560
|
+
"## Workspace Structure",
|
|
561
|
+
"",
|
|
562
|
+
"- **`apps/`** \u2014 Frontend applications, backend servers, and user-facing services.",
|
|
563
|
+
"- **`packages/`** \u2014 Shared modules, TypeScript configurations, ESLint rule sets, and common utility libraries.",
|
|
564
|
+
"",
|
|
565
|
+
"## Key Commands",
|
|
566
|
+
"",
|
|
567
|
+
"Run the following commands from the root directory of your workspace:",
|
|
568
|
+
"",
|
|
569
|
+
"### Development",
|
|
570
|
+
"Start all dev servers and hot-reloading configurations concurrently:",
|
|
571
|
+
"```bash",
|
|
572
|
+
"pnpm dev",
|
|
573
|
+
"```",
|
|
574
|
+
"",
|
|
575
|
+
"### Linting",
|
|
576
|
+
"Run static analysis across all workspaces using your shared ESLint rules:",
|
|
577
|
+
"```bash",
|
|
578
|
+
"pnpm lint",
|
|
579
|
+
"```",
|
|
580
|
+
"",
|
|
581
|
+
"### Formatting",
|
|
582
|
+
"Automatically format all codebase files using Prettier:",
|
|
583
|
+
"```bash",
|
|
584
|
+
"pnpm format",
|
|
585
|
+
"```",
|
|
586
|
+
"",
|
|
587
|
+
"---",
|
|
588
|
+
"",
|
|
589
|
+
"*Generated with love by [Morax Scaffolder CLI](https://github.com/Elitedv/morax).*",
|
|
590
|
+
""
|
|
591
|
+
].join("\n");
|
|
592
|
+
await fsPromises5.writeFile(readmePath, readmeContent, "utf8");
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/core/endcli.ts
|
|
596
|
+
import pc7 from "picocolors";
|
|
597
|
+
import { outro } from "@clack/prompts";
|
|
598
|
+
function endCli(name, projectPath) {
|
|
599
|
+
outro(pc7.yellow("Morax scaffolding completed successfully!"));
|
|
600
|
+
console.log(
|
|
601
|
+
`\u26A1 ${pc7.bold("Workspace ready:")} ${pc7.yellow(name)}
|
|
602
|
+
${pc7.bold("To start developing:")}
|
|
603
|
+
1. ${pc7.cyan(`cd ${name}`)}
|
|
604
|
+
2. ${pc7.cyan("pnpm dev")}`
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/core/react-runner.ts
|
|
609
|
+
async function runReactScaffolder(name, projectPath) {
|
|
610
|
+
const finalProjectPath = await setupReact(projectPath, false);
|
|
611
|
+
await setupReadme(finalProjectPath, path6.basename(finalProjectPath));
|
|
612
|
+
await runGitSetup(finalProjectPath);
|
|
613
|
+
const installSpinner = spinner4();
|
|
614
|
+
console.log("\n");
|
|
615
|
+
installSpinner.start("Running final package installation...");
|
|
616
|
+
try {
|
|
617
|
+
await runCommand("pnpm install", { cwd: finalProjectPath, silent: true });
|
|
618
|
+
installSpinner.stop(
|
|
619
|
+
pc8.green("\u2714 Success: Dependencies configured successfully")
|
|
620
|
+
);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
installSpinner.stop(pc8.red("\u2716 Warning: Final package installation failed"));
|
|
623
|
+
console.error(pc8.red(`
|
|
624
|
+
Error details: ${error.message || error}`));
|
|
625
|
+
}
|
|
626
|
+
console.log("\n");
|
|
627
|
+
endCli(path6.basename(finalProjectPath), finalProjectPath);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/core/next-runner.ts
|
|
631
|
+
import path9 from "path";
|
|
632
|
+
import { spinner as spinner5 } from "@clack/prompts";
|
|
633
|
+
import pc11 from "picocolors";
|
|
634
|
+
|
|
635
|
+
// src/tasks/nextjs.ts
|
|
636
|
+
import { confirm as confirm3 } from "@clack/prompts";
|
|
637
|
+
import pc9 from "picocolors";
|
|
638
|
+
import fsPromises6 from "fs/promises";
|
|
639
|
+
import path7 from "path";
|
|
640
|
+
import { spawn as spawn2 } from "child_process";
|
|
641
|
+
function runInteractiveCommand2(command, args, cwd) {
|
|
642
|
+
return new Promise((resolve, reject) => {
|
|
643
|
+
const child = spawn2(command, args, {
|
|
644
|
+
cwd,
|
|
645
|
+
stdio: "inherit",
|
|
646
|
+
shell: true,
|
|
647
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
648
|
+
});
|
|
649
|
+
child.on("close", (code) => {
|
|
650
|
+
if (code === 0) {
|
|
651
|
+
resolve();
|
|
652
|
+
} else {
|
|
653
|
+
reject(
|
|
654
|
+
new Error(
|
|
655
|
+
`Command "${command} ${args.join(" ")}" exited with code ${code}`
|
|
656
|
+
)
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
child.on("error", (err) => {
|
|
661
|
+
reject(err);
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
async function setupNextjs(projectPath, isMonorepo = true) {
|
|
666
|
+
const appsDir = isMonorepo ? path7.join(projectPath, "apps") : path7.dirname(projectPath);
|
|
667
|
+
const targetName = isMonorepo ? "web" : path7.basename(projectPath);
|
|
668
|
+
const webDir = path7.join(appsDir, targetName);
|
|
669
|
+
try {
|
|
670
|
+
await fsPromises6.rm(webDir, { recursive: true, force: true });
|
|
671
|
+
} catch {
|
|
672
|
+
}
|
|
673
|
+
console.log(pc9.cyan("\nStarting interactive Next.js application setup..."));
|
|
674
|
+
await runInteractiveCommand2(
|
|
675
|
+
"npx",
|
|
676
|
+
["create-next-app@latest", targetName],
|
|
677
|
+
appsDir
|
|
678
|
+
);
|
|
679
|
+
return webDir;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/tasks/shadcn.ts
|
|
683
|
+
import { confirm as confirm4 } from "@clack/prompts";
|
|
684
|
+
import pc10 from "picocolors";
|
|
685
|
+
import fsPromises7 from "fs/promises";
|
|
686
|
+
import path8 from "path";
|
|
687
|
+
import { spawn as spawn3 } from "child_process";
|
|
688
|
+
async function promptShadcn() {
|
|
689
|
+
return await confirm4({
|
|
690
|
+
message: "Do you want to setup shadcn UI in your Next.js website (apps/web)?",
|
|
691
|
+
initialValue: true
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
function runInteractiveCommand3(command, args, cwd) {
|
|
695
|
+
return new Promise((resolve, reject) => {
|
|
696
|
+
const child = spawn3(command, args, {
|
|
697
|
+
cwd,
|
|
698
|
+
stdio: "inherit",
|
|
699
|
+
shell: true,
|
|
700
|
+
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
701
|
+
});
|
|
702
|
+
child.on("close", (code) => {
|
|
703
|
+
if (code === 0) {
|
|
704
|
+
resolve();
|
|
705
|
+
} else {
|
|
706
|
+
reject(
|
|
707
|
+
new Error(
|
|
708
|
+
`Command "${command} ${args.join(" ")}" exited with code ${code}`
|
|
709
|
+
)
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
child.on("error", (err) => {
|
|
714
|
+
reject(err);
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
async function setupShadcn(projectPath, isMonorepo = true) {
|
|
719
|
+
const webDir = isMonorepo ? path8.join(projectPath, "apps", "web") : projectPath;
|
|
720
|
+
console.log(pc10.cyan("\nStarting interactive shadcn UI initialization..."));
|
|
721
|
+
await runInteractiveCommand3(
|
|
722
|
+
"pnpm",
|
|
723
|
+
["dlx", "shadcn@latest", "init", "--template", "next"],
|
|
724
|
+
webDir
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
async function runShadcnSetup(projectPath, isMonorepo = true) {
|
|
728
|
+
const webDir = isMonorepo ? path8.join(projectPath, "apps", "web") : projectPath;
|
|
729
|
+
try {
|
|
730
|
+
await fsPromises7.access(webDir);
|
|
731
|
+
} catch {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
const shadcnPrompt = await promptShadcn();
|
|
735
|
+
handleCancel(shadcnPrompt);
|
|
736
|
+
if (shadcnPrompt) {
|
|
737
|
+
try {
|
|
738
|
+
await setupShadcn(projectPath, isMonorepo);
|
|
739
|
+
console.log(
|
|
740
|
+
pc10.green(
|
|
741
|
+
isMonorepo ? "\n\u2714 Success: shadcn UI successfully initialized in apps/web\n" : "\n\u2714 Success: shadcn UI successfully initialized at root\n"
|
|
742
|
+
)
|
|
743
|
+
);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
console.error(pc10.red(`
|
|
746
|
+
\u2716 Failed: shadcn UI setup failed`));
|
|
747
|
+
console.error(pc10.red(`Error details: ${error.message || error}
|
|
748
|
+
`));
|
|
749
|
+
process.exit(1);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/core/next-runner.ts
|
|
755
|
+
async function runNextScaffolder(name, projectPath) {
|
|
756
|
+
const finalProjectPath = await setupNextjs(projectPath, false);
|
|
757
|
+
await runShadcnSetup(finalProjectPath, false);
|
|
758
|
+
await setupReadme(finalProjectPath, path9.basename(finalProjectPath));
|
|
759
|
+
await runGitSetup(finalProjectPath);
|
|
760
|
+
const installSpinner = spinner5();
|
|
761
|
+
console.log("\n");
|
|
762
|
+
installSpinner.start("Running final package installation...");
|
|
763
|
+
try {
|
|
764
|
+
await runCommand("pnpm install", { cwd: finalProjectPath, silent: true });
|
|
765
|
+
installSpinner.stop(
|
|
766
|
+
pc11.green("\u2714 Success: Dependencies configured successfully")
|
|
767
|
+
);
|
|
768
|
+
} catch (error) {
|
|
769
|
+
installSpinner.stop(pc11.red("\u2716 Warning: Final package installation failed"));
|
|
770
|
+
console.error(pc11.red(`
|
|
771
|
+
Error details: ${error.message || error}`));
|
|
772
|
+
}
|
|
773
|
+
console.log("\n");
|
|
774
|
+
endCli(path9.basename(finalProjectPath), finalProjectPath);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/tasks/prettier.ts
|
|
778
|
+
import { confirm as confirm5, spinner as spinner6 } from "@clack/prompts";
|
|
779
|
+
import pc12 from "picocolors";
|
|
780
|
+
import fsPromises8 from "fs/promises";
|
|
781
|
+
import path10 from "path";
|
|
782
|
+
async function promptPrettier() {
|
|
783
|
+
return await confirm5({
|
|
784
|
+
message: "Do you want to setup Prettier code formatting?",
|
|
785
|
+
initialValue: true
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
async function setupPrettier(projectPath) {
|
|
789
|
+
await runCommand("pnpm add -D -E prettier -w", { cwd: projectPath });
|
|
790
|
+
const prettierrc = {
|
|
791
|
+
semi: true,
|
|
792
|
+
singleQuote: true,
|
|
793
|
+
tabWidth: 2,
|
|
794
|
+
trailingComma: "all",
|
|
795
|
+
printWidth: 100
|
|
796
|
+
};
|
|
797
|
+
await fsPromises8.writeFile(
|
|
798
|
+
path10.join(projectPath, ".prettierrc"),
|
|
799
|
+
JSON.stringify(prettierrc, null, 2),
|
|
800
|
+
"utf8"
|
|
801
|
+
);
|
|
802
|
+
const prettierignore = [
|
|
803
|
+
"# Dependencies",
|
|
804
|
+
"node_modules/",
|
|
805
|
+
"jspm_packages/",
|
|
806
|
+
"web_modules/",
|
|
807
|
+
"",
|
|
808
|
+
"# Build and outputs",
|
|
809
|
+
"dist/",
|
|
810
|
+
"build/",
|
|
811
|
+
".next/",
|
|
812
|
+
"out/",
|
|
813
|
+
".turbo/",
|
|
814
|
+
"",
|
|
815
|
+
"# Configuration locks",
|
|
816
|
+
"pnpm-lock.yaml",
|
|
817
|
+
"",
|
|
818
|
+
"# Environment files",
|
|
819
|
+
".env",
|
|
820
|
+
".env.*",
|
|
821
|
+
""
|
|
822
|
+
].join("\n");
|
|
823
|
+
await fsPromises8.writeFile(
|
|
824
|
+
path10.join(projectPath, ".prettierignore"),
|
|
825
|
+
prettierignore,
|
|
826
|
+
"utf8"
|
|
827
|
+
);
|
|
828
|
+
const rootPackagePath = path10.join(projectPath, "package.json");
|
|
829
|
+
const rootPackageContent = await fsPromises8.readFile(rootPackagePath, "utf8");
|
|
830
|
+
const pkg = JSON.parse(rootPackageContent);
|
|
831
|
+
pkg.scripts = {
|
|
832
|
+
...pkg.scripts || {},
|
|
833
|
+
format: "prettier --write ."
|
|
834
|
+
};
|
|
835
|
+
await fsPromises8.writeFile(
|
|
836
|
+
rootPackagePath,
|
|
837
|
+
JSON.stringify(pkg, null, 2),
|
|
838
|
+
"utf8"
|
|
839
|
+
);
|
|
840
|
+
await runCommand("pnpm format", { cwd: projectPath });
|
|
841
|
+
}
|
|
842
|
+
async function runPrettierSetup(projectPath) {
|
|
843
|
+
const prettierPrompt = await promptPrettier();
|
|
844
|
+
handleCancel(prettierPrompt);
|
|
845
|
+
if (prettierPrompt) {
|
|
846
|
+
const s = spinner6();
|
|
847
|
+
console.log("\n");
|
|
848
|
+
s.start("Setting up Prettier auto-formatting...");
|
|
849
|
+
try {
|
|
850
|
+
await setupPrettier(projectPath);
|
|
851
|
+
s.stop(pc12.green("\u2714 Success: Prettier formatting configured"));
|
|
852
|
+
} catch (error) {
|
|
853
|
+
s.stop(pc12.red("\u2716 Failed: Prettier setup failed"));
|
|
854
|
+
console.error(pc12.red(`
|
|
855
|
+
Error details: ${error.message || error}`));
|
|
856
|
+
process.exit(1);
|
|
857
|
+
}
|
|
858
|
+
console.log("\n");
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/tasks/eslint.ts
|
|
863
|
+
import { spinner as spinner7 } from "@clack/prompts";
|
|
864
|
+
import pc13 from "picocolors";
|
|
865
|
+
import fsPromises9 from "fs/promises";
|
|
866
|
+
import path11 from "path";
|
|
867
|
+
async function setupEslint(projectPath) {
|
|
868
|
+
const eslintDir = path11.join(projectPath, "packages", "eslint");
|
|
869
|
+
await fsPromises9.mkdir(eslintDir, { recursive: true });
|
|
870
|
+
await runCommand("pnpm init", { cwd: eslintDir, silent: true });
|
|
871
|
+
const eslintPackagePath = path11.join(eslintDir, "package.json");
|
|
872
|
+
const eslintPackageRaw = await fsPromises9.readFile(eslintPackagePath, "utf8");
|
|
873
|
+
const eslintPkg = JSON.parse(eslintPackageRaw);
|
|
874
|
+
eslintPkg.name = "@config/eslint";
|
|
875
|
+
eslintPkg.version = "1.0.0";
|
|
876
|
+
eslintPkg.private = true;
|
|
877
|
+
eslintPkg.type = "module";
|
|
878
|
+
eslintPkg.main = "eslint.config.ts";
|
|
879
|
+
eslintPkg.exports = {
|
|
880
|
+
".": "./eslint.config.ts"
|
|
316
881
|
};
|
|
317
882
|
eslintPkg.scripts = {
|
|
318
883
|
lint: "eslint ."
|
|
@@ -320,7 +885,7 @@ async function setupEslint(projectPath) {
|
|
|
320
885
|
delete eslintPkg.keywords;
|
|
321
886
|
delete eslintPkg.author;
|
|
322
887
|
delete eslintPkg.license;
|
|
323
|
-
await
|
|
888
|
+
await fsPromises9.writeFile(
|
|
324
889
|
eslintPackagePath,
|
|
325
890
|
JSON.stringify(eslintPkg, null, 2),
|
|
326
891
|
"utf8"
|
|
@@ -360,69 +925,57 @@ async function setupEslint(projectPath) {
|
|
|
360
925
|
");",
|
|
361
926
|
""
|
|
362
927
|
].join("\n");
|
|
363
|
-
await
|
|
364
|
-
|
|
928
|
+
await fsPromises9.writeFile(
|
|
929
|
+
path11.join(eslintDir, "eslint.config.ts"),
|
|
365
930
|
eslintConfigContent,
|
|
366
931
|
"utf8"
|
|
367
932
|
);
|
|
368
|
-
const rootPackagePath =
|
|
369
|
-
const rootPackageContent = await
|
|
933
|
+
const rootPackagePath = path11.join(projectPath, "package.json");
|
|
934
|
+
const rootPackageContent = await fsPromises9.readFile(rootPackagePath, "utf8");
|
|
370
935
|
const rootPkg = JSON.parse(rootPackageContent);
|
|
371
936
|
rootPkg.scripts = {
|
|
372
937
|
...rootPkg.scripts || {},
|
|
373
938
|
lint: "pnpm --filter @config/eslint lint"
|
|
374
939
|
};
|
|
375
|
-
await
|
|
940
|
+
await fsPromises9.writeFile(
|
|
376
941
|
rootPackagePath,
|
|
377
942
|
JSON.stringify(rootPkg, null, 2),
|
|
378
943
|
"utf8"
|
|
379
944
|
);
|
|
380
945
|
}
|
|
381
946
|
async function runEslintSetup(projectPath) {
|
|
382
|
-
const packagesDir =
|
|
947
|
+
const packagesDir = path11.join(projectPath, "packages");
|
|
383
948
|
try {
|
|
384
|
-
await
|
|
949
|
+
await fsPromises9.access(packagesDir);
|
|
385
950
|
} catch {
|
|
386
951
|
return;
|
|
387
952
|
}
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
s.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
pc6.green("\u2714 Success: Modular ESLint configured in packages/eslint")
|
|
398
|
-
);
|
|
399
|
-
} catch (error) {
|
|
400
|
-
s.stop(pc6.red("\u2716 Failed: ESLint setup failed"));
|
|
401
|
-
console.error(pc6.red(`
|
|
953
|
+
const s = spinner7();
|
|
954
|
+
console.log("\n");
|
|
955
|
+
s.start("Setting up modular ESLint in packages/eslint...");
|
|
956
|
+
try {
|
|
957
|
+
await setupEslint(projectPath);
|
|
958
|
+
s.stop(pc13.green("\u2714 Success: Modular ESLint configured in packages/eslint"));
|
|
959
|
+
} catch (error) {
|
|
960
|
+
s.stop(pc13.red("\u2716 Failed: ESLint setup failed"));
|
|
961
|
+
console.error(pc13.red(`
|
|
402
962
|
Error details: ${error.message || error}`));
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// src/tasks/typescript.ts
|
|
410
|
-
import { confirm as confirm3, spinner as spinner4 } from "@clack/prompts";
|
|
411
|
-
import pc7 from "picocolors";
|
|
412
|
-
import fsPromises5 from "fs/promises";
|
|
413
|
-
import path5 from "path";
|
|
414
|
-
async function promptTypescript() {
|
|
415
|
-
return await confirm3({
|
|
416
|
-
message: "Do you want to setup TypeScript config in packages/typescript?",
|
|
417
|
-
initialValue: true
|
|
418
|
-
});
|
|
963
|
+
process.exit(1);
|
|
964
|
+
}
|
|
965
|
+
console.log("\n");
|
|
419
966
|
}
|
|
967
|
+
|
|
968
|
+
// src/tasks/typescript.ts
|
|
969
|
+
import { spinner as spinner8 } from "@clack/prompts";
|
|
970
|
+
import pc14 from "picocolors";
|
|
971
|
+
import fsPromises10 from "fs/promises";
|
|
972
|
+
import path12 from "path";
|
|
420
973
|
async function setupTypescript(projectPath) {
|
|
421
|
-
const tsDir =
|
|
422
|
-
await
|
|
974
|
+
const tsDir = path12.join(projectPath, "packages", "typescript");
|
|
975
|
+
await fsPromises10.mkdir(tsDir, { recursive: true });
|
|
423
976
|
await runCommand("pnpm init", { cwd: tsDir, silent: true });
|
|
424
|
-
const tsPackagePath =
|
|
425
|
-
const tsPackageRaw = await
|
|
977
|
+
const tsPackagePath = path12.join(tsDir, "package.json");
|
|
978
|
+
const tsPackageRaw = await fsPromises10.readFile(tsPackagePath, "utf8");
|
|
426
979
|
const tsPkg = JSON.parse(tsPackageRaw);
|
|
427
980
|
tsPkg.name = "@config/typescript";
|
|
428
981
|
tsPkg.version = "1.0.0";
|
|
@@ -436,7 +989,7 @@ async function setupTypescript(projectPath) {
|
|
|
436
989
|
delete tsPkg.license;
|
|
437
990
|
delete tsPkg.main;
|
|
438
991
|
delete tsPkg.scripts;
|
|
439
|
-
await
|
|
992
|
+
await fsPromises10.writeFile(
|
|
440
993
|
tsPackagePath,
|
|
441
994
|
JSON.stringify(tsPkg, null, 2),
|
|
442
995
|
"utf8"
|
|
@@ -448,58 +1001,54 @@ async function setupTypescript(projectPath) {
|
|
|
448
1001
|
await runCommand("npx tsc --init", { cwd: tsDir });
|
|
449
1002
|
}
|
|
450
1003
|
async function runTypescriptSetup(projectPath) {
|
|
451
|
-
const packagesDir =
|
|
1004
|
+
const packagesDir = path12.join(projectPath, "packages");
|
|
452
1005
|
try {
|
|
453
|
-
await
|
|
1006
|
+
await fsPromises10.access(packagesDir);
|
|
454
1007
|
} catch {
|
|
455
1008
|
return;
|
|
456
1009
|
}
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
s.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
} catch (error) {
|
|
471
|
-
s.stop(pc7.red("\u2716 Failed: TypeScript config setup failed"));
|
|
472
|
-
console.error(pc7.red(`
|
|
1010
|
+
const s = spinner8();
|
|
1011
|
+
console.log("\n");
|
|
1012
|
+
s.start("Setting up modular TypeScript config in packages/typescript...");
|
|
1013
|
+
try {
|
|
1014
|
+
await setupTypescript(projectPath);
|
|
1015
|
+
s.stop(
|
|
1016
|
+
pc14.green(
|
|
1017
|
+
"\u2714 Success: Modular TypeScript config configured in packages/typescript"
|
|
1018
|
+
)
|
|
1019
|
+
);
|
|
1020
|
+
} catch (error) {
|
|
1021
|
+
s.stop(pc14.red("\u2716 Failed: TypeScript config setup failed"));
|
|
1022
|
+
console.error(pc14.red(`
|
|
473
1023
|
Error details: ${error.message || error}`));
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
console.log("\n");
|
|
1024
|
+
process.exit(1);
|
|
477
1025
|
}
|
|
1026
|
+
console.log("\n");
|
|
478
1027
|
}
|
|
479
1028
|
|
|
480
1029
|
// src/tasks/server.ts
|
|
481
|
-
import { confirm as
|
|
482
|
-
import
|
|
483
|
-
import
|
|
484
|
-
import
|
|
1030
|
+
import { confirm as confirm6, spinner as spinner9 } from "@clack/prompts";
|
|
1031
|
+
import pc15 from "picocolors";
|
|
1032
|
+
import fsPromises11 from "fs/promises";
|
|
1033
|
+
import path13 from "path";
|
|
485
1034
|
async function promptServer() {
|
|
486
|
-
return await
|
|
487
|
-
message: "Do you want to setup a
|
|
1035
|
+
return await confirm6({
|
|
1036
|
+
message: "Do you want to setup a Express backend in apps/server?",
|
|
488
1037
|
initialValue: true
|
|
489
1038
|
});
|
|
490
1039
|
}
|
|
491
1040
|
async function setupServer(projectPath) {
|
|
492
|
-
const serverDir =
|
|
493
|
-
const srcDir =
|
|
494
|
-
await
|
|
1041
|
+
const serverDir = path13.join(projectPath, "apps", "server");
|
|
1042
|
+
const srcDir = path13.join(serverDir, "src");
|
|
1043
|
+
await fsPromises11.mkdir(srcDir, { recursive: true });
|
|
495
1044
|
await runCommand("pnpm init", { cwd: serverDir, silent: true });
|
|
496
|
-
const serverPackagePath =
|
|
497
|
-
const serverPackageRaw = await
|
|
1045
|
+
const serverPackagePath = path13.join(serverDir, "package.json");
|
|
1046
|
+
const serverPackageRaw = await fsPromises11.readFile(serverPackagePath, "utf8");
|
|
498
1047
|
const serverPkg = JSON.parse(serverPackageRaw);
|
|
499
|
-
const eslintDir =
|
|
1048
|
+
const eslintDir = path13.join(projectPath, "packages", "eslint");
|
|
500
1049
|
let hasEslint = false;
|
|
501
1050
|
try {
|
|
502
|
-
await
|
|
1051
|
+
await fsPromises11.access(eslintDir);
|
|
503
1052
|
hasEslint = true;
|
|
504
1053
|
} catch {
|
|
505
1054
|
}
|
|
@@ -523,7 +1072,7 @@ async function setupServer(projectPath) {
|
|
|
523
1072
|
delete serverPkg.keywords;
|
|
524
1073
|
delete serverPkg.author;
|
|
525
1074
|
delete serverPkg.license;
|
|
526
|
-
await
|
|
1075
|
+
await fsPromises11.writeFile(
|
|
527
1076
|
serverPackagePath,
|
|
528
1077
|
JSON.stringify(serverPkg, null, 2),
|
|
529
1078
|
"utf8"
|
|
@@ -536,8 +1085,8 @@ async function setupServer(projectPath) {
|
|
|
536
1085
|
},
|
|
537
1086
|
include: ["src/**/*"]
|
|
538
1087
|
};
|
|
539
|
-
await
|
|
540
|
-
|
|
1088
|
+
await fsPromises11.writeFile(
|
|
1089
|
+
path13.join(serverDir, "tsconfig.json"),
|
|
541
1090
|
JSON.stringify(tsconfigContent, null, 2),
|
|
542
1091
|
"utf8"
|
|
543
1092
|
);
|
|
@@ -553,8 +1102,8 @@ async function setupServer(projectPath) {
|
|
|
553
1102
|
"];",
|
|
554
1103
|
""
|
|
555
1104
|
].join("\n");
|
|
556
|
-
await
|
|
557
|
-
|
|
1105
|
+
await fsPromises11.writeFile(
|
|
1106
|
+
path13.join(serverDir, "eslint.config.ts"),
|
|
558
1107
|
eslintConfigContent,
|
|
559
1108
|
"utf8"
|
|
560
1109
|
);
|
|
@@ -589,586 +1138,115 @@ async function setupServer(projectPath) {
|
|
|
589
1138
|
"});",
|
|
590
1139
|
""
|
|
591
1140
|
].join("\n");
|
|
592
|
-
await
|
|
593
|
-
|
|
1141
|
+
await fsPromises11.writeFile(
|
|
1142
|
+
path13.join(srcDir, "index.ts"),
|
|
594
1143
|
indexTsContent,
|
|
595
1144
|
"utf8"
|
|
596
1145
|
);
|
|
597
1146
|
}
|
|
598
1147
|
async function runServerSetup(projectPath) {
|
|
599
|
-
const appsDir =
|
|
1148
|
+
const appsDir = path13.join(projectPath, "apps");
|
|
600
1149
|
try {
|
|
601
|
-
await
|
|
1150
|
+
await fsPromises11.access(appsDir);
|
|
602
1151
|
} catch {
|
|
603
1152
|
return;
|
|
604
1153
|
}
|
|
605
1154
|
const serverPrompt = await promptServer();
|
|
606
1155
|
handleCancel(serverPrompt);
|
|
607
1156
|
if (serverPrompt) {
|
|
608
|
-
const s =
|
|
1157
|
+
const s = spinner9();
|
|
609
1158
|
console.log("\n");
|
|
610
1159
|
s.start("Setting up modular Express backend in apps/server...");
|
|
611
1160
|
try {
|
|
612
1161
|
await setupServer(projectPath);
|
|
613
1162
|
s.stop(
|
|
614
|
-
|
|
615
|
-
"\u2714 Success: Modular Express backend configured in apps/server"
|
|
616
|
-
)
|
|
617
|
-
);
|
|
618
|
-
} catch (error) {
|
|
619
|
-
s.stop(pc8.red("\u2716 Failed: Express backend setup failed"));
|
|
620
|
-
console.error(pc8.red(`
|
|
621
|
-
Error details: ${error.message || error}`));
|
|
622
|
-
process.exit(1);
|
|
623
|
-
}
|
|
624
|
-
console.log("\n");
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// src/tasks/git.ts
|
|
629
|
-
import { confirm as confirm5, spinner as spinner6 } from "@clack/prompts";
|
|
630
|
-
import pc9 from "picocolors";
|
|
631
|
-
import fsPromises7 from "fs/promises";
|
|
632
|
-
import path7 from "path";
|
|
633
|
-
async function promptGit() {
|
|
634
|
-
return await confirm5({
|
|
635
|
-
message: "Do you want to initialize a local Git repository?",
|
|
636
|
-
initialValue: true
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
async function gitInit(projectPath) {
|
|
640
|
-
await runCommand("git init", { cwd: projectPath, silent: true });
|
|
641
|
-
const gitignore = [
|
|
642
|
-
"# Dependency directories",
|
|
643
|
-
"node_modules/",
|
|
644
|
-
"jspm_packages/",
|
|
645
|
-
"web_modules/",
|
|
646
|
-
"",
|
|
647
|
-
"# Build and output outputs",
|
|
648
|
-
"dist/",
|
|
649
|
-
"build/",
|
|
650
|
-
".next/",
|
|
651
|
-
"out/",
|
|
652
|
-
".turbo/",
|
|
653
|
-
"",
|
|
654
|
-
"# Environments",
|
|
655
|
-
".env",
|
|
656
|
-
".env.local",
|
|
657
|
-
".env.development.local",
|
|
658
|
-
".env.test.local",
|
|
659
|
-
".env.production.local",
|
|
660
|
-
"",
|
|
661
|
-
"# Logs",
|
|
662
|
-
"npm-debug.log*",
|
|
663
|
-
"yarn-debug.log*",
|
|
664
|
-
"yarn-error.log*",
|
|
665
|
-
"pnpm-debug.log*",
|
|
666
|
-
"*.log",
|
|
667
|
-
"",
|
|
668
|
-
"# OS Metadata",
|
|
669
|
-
".DS_Store",
|
|
670
|
-
"Thumbs.db",
|
|
671
|
-
""
|
|
672
|
-
].join("\n");
|
|
673
|
-
await fsPromises7.writeFile(
|
|
674
|
-
path7.join(projectPath, ".gitignore"),
|
|
675
|
-
gitignore,
|
|
676
|
-
"utf8"
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
async function runGitSetup(projectPath) {
|
|
680
|
-
const gitPrompt = await promptGit();
|
|
681
|
-
handleCancel(gitPrompt);
|
|
682
|
-
let gitInitialized = false;
|
|
683
|
-
if (gitPrompt) {
|
|
684
|
-
const s = spinner6();
|
|
685
|
-
console.log("\n");
|
|
686
|
-
s.start("Initializing Git repository...");
|
|
687
|
-
try {
|
|
688
|
-
await gitInit(projectPath);
|
|
689
|
-
s.stop(pc9.green("\u2714 Success: Git repository initialized"));
|
|
690
|
-
gitInitialized = true;
|
|
691
|
-
} catch (error) {
|
|
692
|
-
s.stop(pc9.red("\u2716 Failed: Git initialization failed"));
|
|
693
|
-
console.error(pc9.red(`
|
|
694
|
-
Error details: ${error.message || error}`));
|
|
695
|
-
process.exit(1);
|
|
696
|
-
}
|
|
697
|
-
console.log("\n");
|
|
698
|
-
}
|
|
699
|
-
return gitInitialized;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// src/tasks/husky.ts
|
|
703
|
-
import { confirm as confirm6, spinner as spinner7 } from "@clack/prompts";
|
|
704
|
-
import pc10 from "picocolors";
|
|
705
|
-
import fsPromises8 from "fs/promises";
|
|
706
|
-
import path8 from "path";
|
|
707
|
-
async function promptHusky() {
|
|
708
|
-
return await confirm6({
|
|
709
|
-
message: "Do you want to setup Husky pre-commit hooks?",
|
|
710
|
-
initialValue: true
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
async function setupHusky(projectPath) {
|
|
714
|
-
await runCommand("pnpm add -D -E husky -w", { cwd: projectPath });
|
|
715
|
-
await runCommand("pnpm exec husky init", { cwd: projectPath });
|
|
716
|
-
const preCommitPath = path8.join(projectPath, ".husky", "pre-commit");
|
|
717
|
-
let hookContent = "";
|
|
718
|
-
try {
|
|
719
|
-
hookContent = await fsPromises8.readFile(preCommitPath, "utf8");
|
|
720
|
-
} catch {
|
|
721
|
-
hookContent = "pnpm test";
|
|
722
|
-
}
|
|
723
|
-
hookContent = hookContent.replace("pnpm test", "pnpm format");
|
|
724
|
-
if (!hookContent.includes("git add .")) {
|
|
725
|
-
hookContent = hookContent.trim() + "\ngit add .\n";
|
|
726
|
-
}
|
|
727
|
-
await fsPromises8.writeFile(preCommitPath, hookContent, "utf8");
|
|
728
|
-
try {
|
|
729
|
-
await fsPromises8.chmod(preCommitPath, 493);
|
|
730
|
-
} catch {
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
async function runHuskySetup(projectPath, gitInitialized) {
|
|
734
|
-
if (!gitInitialized) return;
|
|
735
|
-
const huskyPrompt = await promptHusky();
|
|
736
|
-
handleCancel(huskyPrompt);
|
|
737
|
-
if (huskyPrompt) {
|
|
738
|
-
const s = spinner7();
|
|
739
|
-
console.log("\n");
|
|
740
|
-
s.start("Setting up Husky hooks...");
|
|
741
|
-
try {
|
|
742
|
-
await setupHusky(projectPath);
|
|
743
|
-
s.stop(pc10.green("\u2714 Success: Husky pre-commit hooks configured"));
|
|
744
|
-
} catch (error) {
|
|
745
|
-
s.stop(pc10.red("\u2716 Failed: Husky setup failed"));
|
|
746
|
-
console.error(pc10.red(`
|
|
747
|
-
Error details: ${error.message || error}`));
|
|
748
|
-
process.exit(1);
|
|
749
|
-
}
|
|
750
|
-
console.log("\n");
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// src/core/endcli.ts
|
|
755
|
-
import pc11 from "picocolors";
|
|
756
|
-
import { outro } from "@clack/prompts";
|
|
757
|
-
import boxen2 from "boxen";
|
|
758
|
-
function endCli(name, projectPath) {
|
|
759
|
-
outro(pc11.yellow("Morax scaffolding completed successfully!"));
|
|
760
|
-
console.log(
|
|
761
|
-
boxen2(
|
|
762
|
-
`\u26A1 ${pc11.bold("Workspace ready:")} ${pc11.yellow(name)}
|
|
763
|
-
Location: ${pc11.cyan(projectPath)}
|
|
764
|
-
|
|
765
|
-
${pc11.bold("To start developing:")}
|
|
766
|
-
1. ${pc11.cyan(`cd ${name}`)}
|
|
767
|
-
2. ${pc11.cyan("pnpm dev")}
|
|
768
|
-
|
|
769
|
-
\u{1F4A1} Code formatting & git orchestration is fully active.`,
|
|
770
|
-
{
|
|
771
|
-
padding: 0,
|
|
772
|
-
margin: 0,
|
|
773
|
-
borderStyle: "round",
|
|
774
|
-
borderColor: "yellow",
|
|
775
|
-
title: pc11.black(pc11.bold(" Setup Complete ")),
|
|
776
|
-
titleAlignment: "center"
|
|
777
|
-
}
|
|
778
|
-
)
|
|
779
|
-
);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// src/tasks/addreadme.ts
|
|
783
|
-
import fsPromises9 from "fs/promises";
|
|
784
|
-
import path9 from "path";
|
|
785
|
-
async function setupReadme(projectPath, workspaceName) {
|
|
786
|
-
const readmePath = path9.join(projectPath, "README.md");
|
|
787
|
-
const readmeContent = [
|
|
788
|
-
`# Morax `,
|
|
789
|
-
"",
|
|
790
|
-
"Welcome to your next-generation, high-performance monorepo workspace generated by **Morax**.",
|
|
791
|
-
"",
|
|
792
|
-
"## Workspace Structure",
|
|
793
|
-
"",
|
|
794
|
-
"- **`apps/`** \u2014 Frontend applications, backend servers, and user-facing services.",
|
|
795
|
-
"- **`packages/`** \u2014 Shared modules, TypeScript configurations, ESLint rule sets, and common utility libraries.",
|
|
796
|
-
"",
|
|
797
|
-
"## Key Commands",
|
|
798
|
-
"",
|
|
799
|
-
"Run the following commands from the root directory of your workspace:",
|
|
800
|
-
"",
|
|
801
|
-
"### Development",
|
|
802
|
-
"Start all dev servers and hot-reloading configurations concurrently:",
|
|
803
|
-
"```bash",
|
|
804
|
-
"pnpm dev",
|
|
805
|
-
"```",
|
|
806
|
-
"",
|
|
807
|
-
"### Linting",
|
|
808
|
-
"Run static analysis across all workspaces using your shared ESLint rules:",
|
|
809
|
-
"```bash",
|
|
810
|
-
"pnpm lint",
|
|
811
|
-
"```",
|
|
812
|
-
"",
|
|
813
|
-
"### Formatting",
|
|
814
|
-
"Automatically format all codebase files using Prettier:",
|
|
815
|
-
"```bash",
|
|
816
|
-
"pnpm format",
|
|
817
|
-
"```",
|
|
818
|
-
"",
|
|
819
|
-
"---",
|
|
820
|
-
"",
|
|
821
|
-
"*Generated with love by [Morax Scaffolder CLI](https://github.com/Elitedv/morax).*",
|
|
822
|
-
""
|
|
823
|
-
].join("\n");
|
|
824
|
-
await fsPromises9.writeFile(readmePath, readmeContent, "utf8");
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// src/core/web.ts
|
|
828
|
-
import { select } from "@clack/prompts";
|
|
829
|
-
|
|
830
|
-
// src/tasks/shadcn.ts
|
|
831
|
-
import { confirm as confirm7 } from "@clack/prompts";
|
|
832
|
-
import pc12 from "picocolors";
|
|
833
|
-
import fsPromises10 from "fs/promises";
|
|
834
|
-
import path10 from "path";
|
|
835
|
-
import { spawn } from "child_process";
|
|
836
|
-
async function promptShadcn() {
|
|
837
|
-
return await confirm7({
|
|
838
|
-
message: "Do you want to setup shadcn UI in your Next.js website (apps/web)?",
|
|
839
|
-
initialValue: true
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
function runInteractiveCommand(command, args, cwd) {
|
|
843
|
-
return new Promise((resolve, reject) => {
|
|
844
|
-
const child = spawn(command, args, {
|
|
845
|
-
cwd,
|
|
846
|
-
stdio: "inherit",
|
|
847
|
-
shell: true,
|
|
848
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
849
|
-
});
|
|
850
|
-
child.on("close", (code) => {
|
|
851
|
-
if (code === 0) {
|
|
852
|
-
resolve();
|
|
853
|
-
} else {
|
|
854
|
-
reject(new Error(`Command "${command} ${args.join(" ")}" exited with code ${code}`));
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
child.on("error", (err) => {
|
|
858
|
-
reject(err);
|
|
859
|
-
});
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
async function setupShadcn(projectPath) {
|
|
863
|
-
const webDir = path10.join(projectPath, "apps", "web");
|
|
864
|
-
console.log(pc12.cyan("\nStarting interactive shadcn UI initialization..."));
|
|
865
|
-
await runInteractiveCommand("pnpm", ["dlx", "shadcn@latest", "init", "--template", "next"], webDir);
|
|
866
|
-
}
|
|
867
|
-
async function runShadcnSetup(projectPath) {
|
|
868
|
-
const webDir = path10.join(projectPath, "apps", "web");
|
|
869
|
-
try {
|
|
870
|
-
await fsPromises10.access(webDir);
|
|
871
|
-
} catch {
|
|
872
|
-
return;
|
|
873
|
-
}
|
|
874
|
-
const shadcnPrompt = await promptShadcn();
|
|
875
|
-
handleCancel(shadcnPrompt);
|
|
876
|
-
if (shadcnPrompt) {
|
|
877
|
-
try {
|
|
878
|
-
await setupShadcn(projectPath);
|
|
879
|
-
console.log(pc12.green("\n\u2714 Success: shadcn UI successfully initialized in apps/web\n"));
|
|
1163
|
+
pc15.green(
|
|
1164
|
+
"\u2714 Success: Modular Express backend configured in apps/server"
|
|
1165
|
+
)
|
|
1166
|
+
);
|
|
880
1167
|
} catch (error) {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
`));
|
|
1168
|
+
s.stop(pc15.red("\u2716 Failed: Express backend setup failed"));
|
|
1169
|
+
console.error(pc15.red(`
|
|
1170
|
+
Error details: ${error.message || error}`));
|
|
885
1171
|
process.exit(1);
|
|
886
1172
|
}
|
|
1173
|
+
console.log("\n");
|
|
887
1174
|
}
|
|
888
1175
|
}
|
|
889
1176
|
|
|
890
|
-
// src/tasks/
|
|
891
|
-
import { confirm as
|
|
892
|
-
import
|
|
893
|
-
import
|
|
894
|
-
import
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
cwd,
|
|
900
|
-
stdio: "inherit",
|
|
901
|
-
shell: true,
|
|
902
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
903
|
-
});
|
|
904
|
-
child.on("close", (code) => {
|
|
905
|
-
if (code === 0) {
|
|
906
|
-
resolve();
|
|
907
|
-
} else {
|
|
908
|
-
reject(
|
|
909
|
-
new Error(
|
|
910
|
-
`Command "${command} ${args.join(" ")}" exited with code ${code}`
|
|
911
|
-
)
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
});
|
|
915
|
-
child.on("error", (err) => {
|
|
916
|
-
reject(err);
|
|
917
|
-
});
|
|
1177
|
+
// src/tasks/husky.ts
|
|
1178
|
+
import { confirm as confirm7, spinner as spinner10 } from "@clack/prompts";
|
|
1179
|
+
import pc16 from "picocolors";
|
|
1180
|
+
import fsPromises12 from "fs/promises";
|
|
1181
|
+
import path14 from "path";
|
|
1182
|
+
async function promptHusky() {
|
|
1183
|
+
return await confirm7({
|
|
1184
|
+
message: "Do you want to setup Husky pre-commit hooks?",
|
|
1185
|
+
initialValue: true
|
|
918
1186
|
});
|
|
919
1187
|
}
|
|
920
|
-
async function
|
|
921
|
-
|
|
922
|
-
|
|
1188
|
+
async function setupHusky(projectPath) {
|
|
1189
|
+
await runCommand("pnpm add -D -E husky -w", { cwd: projectPath });
|
|
1190
|
+
await runCommand("pnpm exec husky init", { cwd: projectPath });
|
|
1191
|
+
const preCommitPath = path14.join(projectPath, ".husky", "pre-commit");
|
|
1192
|
+
let hookContent = "";
|
|
923
1193
|
try {
|
|
924
|
-
await
|
|
1194
|
+
hookContent = await fsPromises12.readFile(preCommitPath, "utf8");
|
|
925
1195
|
} catch {
|
|
1196
|
+
hookContent = "pnpm test";
|
|
926
1197
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
"
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// src/tasks/react.ts
|
|
936
|
-
import pc14 from "picocolors";
|
|
937
|
-
import fsPromises12 from "fs/promises";
|
|
938
|
-
import path12 from "path";
|
|
939
|
-
import { spawn as spawn3 } from "child_process";
|
|
940
|
-
import { confirm as confirm9, spinner as spinner8, text as text2 } from "@clack/prompts";
|
|
941
|
-
function runInteractiveCommand3(command, args, cwd) {
|
|
942
|
-
return new Promise((resolve, reject) => {
|
|
943
|
-
const child = spawn3(command, args, {
|
|
944
|
-
cwd,
|
|
945
|
-
stdio: "inherit",
|
|
946
|
-
shell: true,
|
|
947
|
-
env: { ...process.env, NODE_NO_WARNINGS: "1" }
|
|
948
|
-
});
|
|
949
|
-
child.on("close", (code) => {
|
|
950
|
-
if (code === 0) {
|
|
951
|
-
resolve();
|
|
952
|
-
} else {
|
|
953
|
-
reject(
|
|
954
|
-
new Error(
|
|
955
|
-
`Command "${command} ${args.join(" ")}" exited with code ${code}`
|
|
956
|
-
)
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
|
-
});
|
|
960
|
-
child.on("error", (err) => {
|
|
961
|
-
reject(err);
|
|
962
|
-
});
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
async function setupReact(projectPath) {
|
|
966
|
-
const appsDir = path12.join(projectPath, "apps");
|
|
967
|
-
console.log(pc14.magentaBright("\n\u{1F537} IMPORTANT INSTRUCTIONS:"));
|
|
968
|
-
console.log(pc14.magentaBright("During the Vite interactive configuration prompts, please select:"));
|
|
969
|
-
console.log(pc14.magentaBright(` 1. Install & Start: Choose ${pc14.bold("No")} when asked "Install with npm/pnpm and start now?"`));
|
|
970
|
-
console.log(pc14.magentaBright("This allows Morax CLI to continue automatically configuring your workspace (ESLint, Prettier, Tailwind, packages, etc.)!"));
|
|
971
|
-
console.log(pc14.cyan("\nStarting interactive Vite React application setup..."));
|
|
972
|
-
let beforeDirs = [];
|
|
1198
|
+
hookContent = hookContent.replace("pnpm test", "pnpm format");
|
|
1199
|
+
if (!hookContent.includes("git add .")) {
|
|
1200
|
+
hookContent = hookContent.trim() + "\ngit add .\n";
|
|
1201
|
+
}
|
|
1202
|
+
await fsPromises12.writeFile(preCommitPath, hookContent, "utf8");
|
|
973
1203
|
try {
|
|
974
|
-
|
|
1204
|
+
await fsPromises12.chmod(preCommitPath, 493);
|
|
975
1205
|
} catch {
|
|
976
1206
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
} catch {
|
|
987
|
-
}
|
|
988
|
-
const newDirs = afterDirs.filter((d) => !beforeDirs.includes(d));
|
|
989
|
-
let createdDirName = "web";
|
|
990
|
-
if (newDirs.length > 0) {
|
|
991
|
-
createdDirName = newDirs[0];
|
|
992
|
-
}
|
|
993
|
-
const webDir = path12.join(appsDir, createdDirName);
|
|
1207
|
+
}
|
|
1208
|
+
async function runHuskySetup(projectPath, gitInitialized) {
|
|
1209
|
+
if (!gitInitialized) return;
|
|
1210
|
+
const huskyPrompt = await promptHusky();
|
|
1211
|
+
handleCancel(huskyPrompt);
|
|
1212
|
+
if (huskyPrompt) {
|
|
1213
|
+
const s = spinner10();
|
|
1214
|
+
console.log("\n");
|
|
1215
|
+
s.start("Setting up Husky hooks...");
|
|
994
1216
|
try {
|
|
995
|
-
await
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
`)
|
|
1003
|
-
);
|
|
1004
|
-
const setupTailwindPrompt = await confirm9({
|
|
1005
|
-
message: "Do you want to install and configure Tailwind CSS v4?",
|
|
1006
|
-
initialValue: true
|
|
1007
|
-
});
|
|
1008
|
-
handleCancel(setupTailwindPrompt);
|
|
1009
|
-
const setupShadcnPrompt = await confirm9({
|
|
1010
|
-
message: `Do you want to setup shadcn UI in your Vite React website (apps/${createdDirName})?`,
|
|
1011
|
-
initialValue: true
|
|
1012
|
-
});
|
|
1013
|
-
handleCancel(setupShadcnPrompt);
|
|
1014
|
-
const needsTailwind = setupTailwindPrompt || setupShadcnPrompt;
|
|
1015
|
-
if (needsTailwind) {
|
|
1016
|
-
const s = spinner8();
|
|
1017
|
-
s.start("Installing Tailwind CSS v4, Vite integration, and Node types...");
|
|
1018
|
-
try {
|
|
1019
|
-
const pkgPath = path12.join(webDir, "package.json");
|
|
1020
|
-
try {
|
|
1021
|
-
const pkgRaw = await fsPromises12.readFile(pkgPath, "utf8");
|
|
1022
|
-
const pkg = JSON.parse(pkgRaw);
|
|
1023
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
1024
|
-
pkg.devDependencies = pkg.devDependencies || {};
|
|
1025
|
-
pkg.dependencies["tailwindcss"] = "^4.0.0";
|
|
1026
|
-
pkg.devDependencies["@tailwindcss/vite"] = "^4.0.0";
|
|
1027
|
-
pkg.devDependencies["@types/node"] = "^20.11.0";
|
|
1028
|
-
await fsPromises12.writeFile(pkgPath, JSON.stringify(pkg, null, 2), "utf8");
|
|
1029
|
-
} catch {
|
|
1030
|
-
}
|
|
1031
|
-
await runCommand("pnpm install", { cwd: webDir, silent: true });
|
|
1032
|
-
s.message("Configuring Vite plugins and Tailwind CSS imports...");
|
|
1033
|
-
const viteConfigPath = path12.join(webDir, "vite.config.ts");
|
|
1034
|
-
try {
|
|
1035
|
-
let content = await fsPromises12.readFile(viteConfigPath, "utf8");
|
|
1036
|
-
if (!content.includes("@tailwindcss/vite")) {
|
|
1037
|
-
content = "import tailwindcss from '@tailwindcss/vite';\n" + content;
|
|
1038
|
-
if (content.includes("plugins: [react()]")) {
|
|
1039
|
-
content = content.replace("plugins: [react()]", "plugins: [react(), tailwindcss()]");
|
|
1040
|
-
} else if (content.includes("plugins: [react(),]")) {
|
|
1041
|
-
content = content.replace("plugins: [react(),]", "plugins: [react(), tailwindcss()]");
|
|
1042
|
-
} else {
|
|
1043
|
-
content = content.replace(/(plugins:\s*\[\s*react\(\),?\s*)(\])/g, "$1\n tailwindcss(),\n $2");
|
|
1044
|
-
}
|
|
1045
|
-
await fsPromises12.writeFile(viteConfigPath, content, "utf8");
|
|
1046
|
-
}
|
|
1047
|
-
} catch {
|
|
1048
|
-
}
|
|
1049
|
-
const indexCssPath = path12.join(webDir, "src", "index.css");
|
|
1050
|
-
try {
|
|
1051
|
-
await fsPromises12.writeFile(indexCssPath, '@import "tailwindcss";\n', "utf8");
|
|
1052
|
-
} catch {
|
|
1053
|
-
}
|
|
1054
|
-
s.stop(pc14.green("\u2714 Success: Tailwind CSS v4 configured successfully\n"));
|
|
1055
|
-
} catch (err) {
|
|
1056
|
-
s.stop(pc14.red("\u2716 Failed: Tailwind CSS v4 setup failed"));
|
|
1057
|
-
throw err;
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
if (setupShadcnPrompt) {
|
|
1061
|
-
const s = spinner8();
|
|
1062
|
-
s.start("Configuring path aliases for shadcn UI preflight checks...");
|
|
1063
|
-
try {
|
|
1064
|
-
const tsconfigPaths = [
|
|
1065
|
-
path12.join(webDir, "tsconfig.json"),
|
|
1066
|
-
path12.join(webDir, "tsconfig.app.json")
|
|
1067
|
-
];
|
|
1068
|
-
for (const tsPath of tsconfigPaths) {
|
|
1069
|
-
try {
|
|
1070
|
-
let content = await fsPromises12.readFile(tsPath, "utf8");
|
|
1071
|
-
if (content.includes('"compilerOptions"')) {
|
|
1072
|
-
if (!content.includes('"paths"')) {
|
|
1073
|
-
content = content.replace(
|
|
1074
|
-
/("compilerOptions"\s*:\s*\{)/,
|
|
1075
|
-
`$1
|
|
1076
|
-
"baseUrl": ".",
|
|
1077
|
-
"paths": {
|
|
1078
|
-
"@/*": ["./src/*"]
|
|
1079
|
-
},`
|
|
1080
|
-
);
|
|
1081
|
-
await fsPromises12.writeFile(tsPath, content, "utf8");
|
|
1082
|
-
}
|
|
1083
|
-
} else {
|
|
1084
|
-
content = content.replace(
|
|
1085
|
-
/^(\s*\{)/,
|
|
1086
|
-
`$1
|
|
1087
|
-
"compilerOptions": {
|
|
1088
|
-
"baseUrl": ".",
|
|
1089
|
-
"paths": {
|
|
1090
|
-
"@/*": ["./src/*"]
|
|
1091
|
-
}
|
|
1092
|
-
},`
|
|
1093
|
-
);
|
|
1094
|
-
await fsPromises12.writeFile(tsPath, content, "utf8");
|
|
1095
|
-
}
|
|
1096
|
-
} catch {
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
const viteConfigPath = path12.join(webDir, "vite.config.ts");
|
|
1100
|
-
try {
|
|
1101
|
-
let content = await fsPromises12.readFile(viteConfigPath, "utf8");
|
|
1102
|
-
if (!content.includes("import path from 'path'")) {
|
|
1103
|
-
content = "import path from 'path';\n" + content;
|
|
1104
|
-
}
|
|
1105
|
-
if (!content.includes("resolve:")) {
|
|
1106
|
-
content = content.replace(
|
|
1107
|
-
/(plugins:\s*\[[^\]]*\]),?/,
|
|
1108
|
-
`$1,
|
|
1109
|
-
resolve: {
|
|
1110
|
-
alias: {
|
|
1111
|
-
"@": path.resolve(__dirname, "./src"),
|
|
1112
|
-
},
|
|
1113
|
-
}`
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
await fsPromises12.writeFile(viteConfigPath, content, "utf8");
|
|
1117
|
-
} catch {
|
|
1118
|
-
}
|
|
1119
|
-
s.stop(pc14.green("\u2714 Success: Path aliases successfully configured\n"));
|
|
1120
|
-
} catch (err) {
|
|
1121
|
-
s.stop(pc14.red("\u2716 Failed: Path alias configuration failed"));
|
|
1122
|
-
throw err;
|
|
1123
|
-
}
|
|
1124
|
-
const hasPreset = await confirm9({
|
|
1125
|
-
message: "Do you have a custom shadcn UI preset code?",
|
|
1126
|
-
initialValue: false
|
|
1127
|
-
});
|
|
1128
|
-
handleCancel(hasPreset);
|
|
1129
|
-
let presetCode = "";
|
|
1130
|
-
if (hasPreset) {
|
|
1131
|
-
const enteredPreset = await text2({
|
|
1132
|
-
message: "Enter your custom shadcn UI preset code:",
|
|
1133
|
-
placeholder: "e.g. 123456",
|
|
1134
|
-
validate: (val) => {
|
|
1135
|
-
if (!val || !val.trim()) return "Preset code cannot be empty";
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
});
|
|
1139
|
-
handleCancel(enteredPreset);
|
|
1140
|
-
presetCode = enteredPreset.trim();
|
|
1141
|
-
}
|
|
1142
|
-
const args = ["dlx", "shadcn@latest", "init"];
|
|
1143
|
-
if (presetCode) {
|
|
1144
|
-
args.push("--preset", presetCode);
|
|
1145
|
-
}
|
|
1146
|
-
args.push("--template", "vite");
|
|
1147
|
-
console.log(pc14.cyan("\nStarting interactive shadcn UI initialization..."));
|
|
1148
|
-
await runInteractiveCommand3("pnpm", args, webDir);
|
|
1149
|
-
console.log(
|
|
1150
|
-
pc14.green(`
|
|
1151
|
-
\u2714 Success: shadcn UI successfully initialized in apps/${createdDirName}
|
|
1152
|
-
`)
|
|
1153
|
-
);
|
|
1217
|
+
await setupHusky(projectPath);
|
|
1218
|
+
s.stop(pc16.green("\u2714 Success: Husky pre-commit hooks configured"));
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
s.stop(pc16.red("\u2716 Failed: Husky setup failed"));
|
|
1221
|
+
console.error(pc16.red(`
|
|
1222
|
+
Error details: ${error.message || error}`));
|
|
1223
|
+
process.exit(1);
|
|
1154
1224
|
}
|
|
1155
|
-
|
|
1156
|
-
console.error(pc14.red(`
|
|
1157
|
-
\u2716 Failed: Vite React frontend setup failed`));
|
|
1158
|
-
console.error(pc14.red(`Error details: ${error.message || error}
|
|
1159
|
-
`));
|
|
1160
|
-
process.exit(1);
|
|
1225
|
+
console.log("\n");
|
|
1161
1226
|
}
|
|
1162
1227
|
}
|
|
1163
1228
|
|
|
1164
1229
|
// src/core/web.ts
|
|
1230
|
+
import { select } from "@clack/prompts";
|
|
1165
1231
|
async function addweb(projectPath) {
|
|
1166
1232
|
const framework = await select({
|
|
1167
1233
|
message: "Which frontend website setup would you like to include in apps/web?",
|
|
1168
1234
|
options: [
|
|
1169
|
-
{
|
|
1170
|
-
|
|
1171
|
-
|
|
1235
|
+
{
|
|
1236
|
+
value: "nextjs",
|
|
1237
|
+
label: "Next.js",
|
|
1238
|
+
hint: "The React Framework for the Web"
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
value: "react",
|
|
1242
|
+
label: "React (Vite)",
|
|
1243
|
+
hint: "Vite React Starter Template"
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
value: "skip",
|
|
1247
|
+
label: "Skip",
|
|
1248
|
+
hint: "Skip setting up a frontend website"
|
|
1249
|
+
}
|
|
1172
1250
|
],
|
|
1173
1251
|
initialValue: "nextjs"
|
|
1174
1252
|
});
|
|
@@ -1182,44 +1260,133 @@ async function addweb(projectPath) {
|
|
|
1182
1260
|
}
|
|
1183
1261
|
|
|
1184
1262
|
// src/core/runner.ts
|
|
1185
|
-
async function runWorkspaceScaffolder() {
|
|
1263
|
+
async function runWorkspaceScaffolder(options = {}) {
|
|
1186
1264
|
startCli();
|
|
1187
|
-
const
|
|
1188
|
-
const
|
|
1265
|
+
const isMonorepo = !options.react && !options.next;
|
|
1266
|
+
const name = await promptWorkspaceName(isMonorepo);
|
|
1267
|
+
const projectPath = path15.join(process.cwd(), name);
|
|
1268
|
+
if (options.react) {
|
|
1269
|
+
await runReactScaffolder(name, projectPath);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
if (options.next) {
|
|
1273
|
+
await runNextScaffolder(name, projectPath);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1189
1276
|
await makeDirectories(name, projectPath);
|
|
1190
1277
|
await setupReadme(projectPath, name);
|
|
1191
1278
|
const gitInitialized = await runGitSetup(projectPath);
|
|
1192
|
-
await
|
|
1193
|
-
await
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1279
|
+
const selectedApps = await askServerorFrontendInclue();
|
|
1280
|
+
const selectedTools = await askWhichToolToInclude(
|
|
1281
|
+
selectedApps.includes("server")
|
|
1282
|
+
);
|
|
1283
|
+
if (selectedTools.includes("prettier")) {
|
|
1284
|
+
await runPrettierSetup(projectPath);
|
|
1285
|
+
}
|
|
1286
|
+
if (selectedTools.includes("husky")) {
|
|
1287
|
+
await runHuskySetup(projectPath, gitInitialized);
|
|
1288
|
+
}
|
|
1289
|
+
if (selectedTools.includes("eslint")) {
|
|
1290
|
+
await runEslintSetup(projectPath);
|
|
1291
|
+
}
|
|
1292
|
+
if (selectedTools.includes("typescript")) {
|
|
1293
|
+
await runTypescriptSetup(projectPath);
|
|
1294
|
+
}
|
|
1295
|
+
if (selectedApps.includes("server")) {
|
|
1296
|
+
await runServerSetup(projectPath);
|
|
1297
|
+
}
|
|
1298
|
+
if (selectedApps.includes("web")) {
|
|
1299
|
+
await addweb(projectPath);
|
|
1300
|
+
}
|
|
1301
|
+
const s = spinner11();
|
|
1199
1302
|
console.log("\n");
|
|
1200
1303
|
s.start("Running final workspace package installation...");
|
|
1201
1304
|
try {
|
|
1202
1305
|
await runCommand("pnpm install", { cwd: projectPath, silent: true });
|
|
1203
1306
|
s.stop(
|
|
1204
|
-
|
|
1307
|
+
pc17.green(
|
|
1205
1308
|
"\u2714 Success: Workspace dependencies and symlinks configured successfully"
|
|
1206
1309
|
)
|
|
1207
1310
|
);
|
|
1208
1311
|
} catch (error) {
|
|
1209
|
-
s.stop(
|
|
1210
|
-
console.error(
|
|
1312
|
+
s.stop(pc17.red("\u2716 Warning: Final workspace installation failed"));
|
|
1313
|
+
console.error(pc17.red(`
|
|
1211
1314
|
Error details: ${error.message || error}`));
|
|
1212
1315
|
}
|
|
1213
1316
|
console.log("\n");
|
|
1214
1317
|
endCli(name, projectPath);
|
|
1215
1318
|
}
|
|
1319
|
+
async function askWhichToolToInclude(hasServer) {
|
|
1320
|
+
const options = [
|
|
1321
|
+
{
|
|
1322
|
+
value: "prettier",
|
|
1323
|
+
label: "Prettier",
|
|
1324
|
+
hint: "Code formatter rules & auto-formatting script"
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
value: "husky",
|
|
1328
|
+
label: "Husky",
|
|
1329
|
+
hint: "Git pre-commit hooks setup (requires Git)"
|
|
1330
|
+
}
|
|
1331
|
+
];
|
|
1332
|
+
if (!hasServer) {
|
|
1333
|
+
options.splice(
|
|
1334
|
+
1,
|
|
1335
|
+
0,
|
|
1336
|
+
{
|
|
1337
|
+
value: "eslint",
|
|
1338
|
+
label: "ESLint",
|
|
1339
|
+
hint: "Modern Flat Config linter configurations"
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
value: "typescript",
|
|
1343
|
+
label: "TypeScript",
|
|
1344
|
+
hint: "Extendable shared TypeScript configurations"
|
|
1345
|
+
}
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
const tools = await multiselect({
|
|
1349
|
+
message: hasServer ? "ESLint & TypeScript are required for the Express backend. Select additional configurations/tools:" : "Which configurations/linter tools would you like to set up?",
|
|
1350
|
+
options,
|
|
1351
|
+
required: false
|
|
1352
|
+
});
|
|
1353
|
+
handleCancel(tools);
|
|
1354
|
+
const selectedTools = tools;
|
|
1355
|
+
if (hasServer) {
|
|
1356
|
+
if (!selectedTools.includes("eslint")) selectedTools.push("eslint");
|
|
1357
|
+
if (!selectedTools.includes("typescript")) selectedTools.push("typescript");
|
|
1358
|
+
}
|
|
1359
|
+
return selectedTools;
|
|
1360
|
+
}
|
|
1361
|
+
async function askServerorFrontendInclue() {
|
|
1362
|
+
const apps = await multiselect({
|
|
1363
|
+
message: "Which backend/frontend applications would you like to set up?",
|
|
1364
|
+
options: [
|
|
1365
|
+
{
|
|
1366
|
+
value: "web",
|
|
1367
|
+
label: "Frontend Web App",
|
|
1368
|
+
hint: "Scaffold Next.js or React (Vite) frontend in apps/web"
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
value: "server",
|
|
1372
|
+
label: "Backend Server",
|
|
1373
|
+
hint: "Scaffold Express backend in apps/server"
|
|
1374
|
+
}
|
|
1375
|
+
],
|
|
1376
|
+
required: false
|
|
1377
|
+
});
|
|
1378
|
+
handleCancel(apps);
|
|
1379
|
+
return apps;
|
|
1380
|
+
}
|
|
1216
1381
|
|
|
1217
1382
|
// src/index.ts
|
|
1218
1383
|
async function main() {
|
|
1219
|
-
|
|
1384
|
+
const isReactOnly = process.argv.includes("--react") || process.argv.includes("-r");
|
|
1385
|
+
const isNextOnly = process.argv.includes("--nextjs") || process.argv.includes("--next") || process.argv.includes("-n");
|
|
1386
|
+
await runWorkspaceScaffolder({ react: isReactOnly, next: isNextOnly });
|
|
1220
1387
|
}
|
|
1221
1388
|
main().catch((err) => {
|
|
1222
|
-
console.error(
|
|
1389
|
+
console.error(pc18.red("Fatal Error during execution:"), err);
|
|
1223
1390
|
process.exit(1);
|
|
1224
1391
|
});
|
|
1225
1392
|
//# sourceMappingURL=index.js.map
|